mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-06 16:33:00 +00:00
Compare commits
50 Commits
v1.20.1-1.
...
feature/po
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94e7d2d03b | ||
|
|
c271ed7c7f | ||
|
|
d6a246c122 | ||
|
|
0bef3ee0d8 | ||
|
|
bb04df7086 | ||
|
|
a70baf0d74 | ||
|
|
86e2f92493 | ||
|
|
e9aceca1de | ||
|
|
3042950507 | ||
|
|
f7a6aac657 | ||
|
|
782564e6ab | ||
|
|
6b8ba8b80b | ||
|
|
52b76d8886 | ||
|
|
ba36c69583 | ||
|
|
370e5f92a0 | ||
|
|
36d05e4774 | ||
|
|
89d1be17c9 | ||
|
|
0069591af9 | ||
|
|
c36c8605bf | ||
|
|
3c72a00d46 | ||
|
|
58aefc8df8 | ||
|
|
97ddfc2794 | ||
|
|
6e4ec86586 | ||
|
|
d24984c1d5 | ||
|
|
8080dcdd9e | ||
|
|
d7cea55e2a | ||
|
|
9b2f974a81 | ||
|
|
43770fa9bd | ||
|
|
80c7a54ad4 | ||
|
|
e57b6fede2 | ||
|
|
34a2fd039f | ||
|
|
3299d0e72a | ||
|
|
b89e2615db | ||
|
|
cdcd82679c | ||
|
|
cdfa866760 | ||
|
|
aa8078ddeb | ||
|
|
7e53c19d74 | ||
|
|
b7a8432cfb | ||
|
|
356c8e8aeb | ||
|
|
ed283155f7 | ||
|
|
87dfad026e | ||
|
|
bb97c465d9 | ||
|
|
9484315d37 | ||
|
|
be59f1a875 | ||
|
|
bfb28b4710 | ||
|
|
216f0adb3c | ||
|
|
77af4bc213 | ||
|
|
5abab982c7 | ||
|
|
764e1aa332 | ||
|
|
c47718b09d |
10
.github/workflows/main-ci.yml
vendored
10
.github/workflows/main-ci.yml
vendored
@@ -30,8 +30,16 @@ jobs:
|
||||
- name: ⚒️ Build
|
||||
run: ./gradlew assemble || ./gradlew assemble
|
||||
|
||||
- name: Cache pre-commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
|
||||
- name: 💡 Lint
|
||||
uses: pre-commit/action@v3.0.0
|
||||
run: |
|
||||
pipx install pre-commit
|
||||
pre-commit run --show-diff-on-failure --color=always
|
||||
|
||||
- name: 🧪 Run tests
|
||||
run: ./gradlew test validateMixinNames checkChangelog
|
||||
|
||||
@@ -12,6 +12,7 @@ If you've any other questions, [just ask the community][community] or [open an i
|
||||
|
||||
## Table of Contents
|
||||
- [Reporting issues](#reporting-issues)
|
||||
- [Translations](#translations)
|
||||
- [Setting up a development environment](#setting-up-a-development-environment)
|
||||
- [Developing CC: Tweaked](#developing-cc-tweaked)
|
||||
- [Writing documentation](#writing-documentation)
|
||||
@@ -20,6 +21,10 @@ If you've any other questions, [just ask the community][community] or [open an i
|
||||
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
|
||||
use the issue templates - they provide a useful hint on what information to provide.
|
||||
|
||||
## Translations
|
||||
Translations are managed through [CrowdIn], an online interface for managing language strings. Translations may either
|
||||
be contributed there, or directly via a pull request.
|
||||
|
||||
## Setting up a development environment
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||
|
||||
@@ -102,3 +107,4 @@ about how you can build on that until you've covered everything!
|
||||
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
|
||||
[node]: https://nodejs.org/en/ "Node.js"
|
||||
[architecture]: projects/ARCHITECTURE.md
|
||||
[Crowdin]: https://crowdin.com/project/cc-tweaked/
|
||||
|
||||
@@ -100,7 +100,7 @@ SPDX-License-Identifier = "CC0-1.0"
|
||||
path = ".github/**"
|
||||
|
||||
[[annotations]]
|
||||
path = ["gradle/wrapper/**", "gradlew", "gradlew.bat"]
|
||||
path = ["gradle/wrapper/**"]
|
||||
SPDX-FileCopyrightText = "Gradle Inc"
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ fun getNextVersion(version: String): String {
|
||||
val lastIndex = mainVersion.lastIndexOf('.')
|
||||
if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
|
||||
val lastVersion = try {
|
||||
version.substring(lastIndex + 1).toInt()
|
||||
mainVersion.substring(lastIndex + 1).toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
|
||||
}
|
||||
|
||||
27
crowdin.yml
Normal file
27
crowdin.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
files:
|
||||
- source: projects/common/src/generated/resources/assets/computercraft/lang/en_us.json
|
||||
translation: /projects/common/src/main/resources/assets/computercraft/lang/%locale_with_underscore%.json
|
||||
languages_mapping:
|
||||
locale_with_underscore:
|
||||
cs: cs_cs # Czech
|
||||
da: da_dk # Danish
|
||||
de: de_de # German
|
||||
es-ES: es_es # Spanish
|
||||
fr: fr_fr # French
|
||||
it: it_it # Italian
|
||||
ja: ja_jp # Japanese
|
||||
ko: ko_kr # Korean
|
||||
nb: nb_no # Norwegian Bokmal
|
||||
nl: nl_nl # Dutch
|
||||
pl: pl_pl # Polish
|
||||
pt-BR: pt_br # Portuguese, Brazilian
|
||||
ru: ru_ru # Russian
|
||||
sv-SE: sv_se # Sweedish
|
||||
tok: tok # Toki Pona
|
||||
tr: tr_tr # Turkish
|
||||
uk: uk_ua # Ukraine
|
||||
vi: vi_vn # Vietnamese
|
||||
zh-CN: zh_cn # Chinese Simplified
|
||||
@@ -81,7 +81,7 @@ compatibility for these newer versions.
|
||||
| `string.dump` strip argument | ✔ | |
|
||||
| `string.pack`/`string.unpack`/`string.packsize` | ✔ | |
|
||||
| `table.move` | ✔ | |
|
||||
| `math.atan2` -> `math.atan` | ❌ | |
|
||||
| `math.atan2` -> `math.atan` | 🔶 | `math.atan` supports its two argument form. |
|
||||
| Removed `math.frexp`, `math.ldexp`, `math.pow`, `math.cosh`, `math.sinh`, `math.tanh` | ❌ | |
|
||||
| `math.maxinteger`/`math.mininteger` | ❌ | |
|
||||
| `math.tointeger` | ❌ | |
|
||||
|
||||
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.112.0
|
||||
modVersion=1.113.1
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.20.1
|
||||
|
||||
@@ -26,7 +26,7 @@ slf4j = "2.0.1"
|
||||
asm = "9.6"
|
||||
autoService = "1.1.1"
|
||||
checkerFramework = "3.42.0"
|
||||
cobalt = "0.9.3"
|
||||
cobalt = "0.9.4"
|
||||
commonsCli = "1.6.0"
|
||||
jetbrainsAnnotations = "24.1.0"
|
||||
jsr305 = "3.0.2"
|
||||
@@ -71,6 +71,7 @@ librarian = "1.+"
|
||||
lwjgl = "3.3.3"
|
||||
minotaur = "2.+"
|
||||
nullAway = "0.10.25"
|
||||
shadow = "8.3.1"
|
||||
spotless = "6.23.3"
|
||||
taskTree = "2.1.1"
|
||||
teavm = "0.11.0-SQUID.1"
|
||||
@@ -94,9 +95,10 @@ jzlib = { module = "com.jcraft:jzlib", version.ref = "jzlib" }
|
||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" }
|
||||
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
|
||||
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
|
||||
netty-proxy = { module = "io.netty:netty-handler-proxy", version.ref = "netty" }
|
||||
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
|
||||
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
|
||||
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
|
||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
@@ -175,6 +177,7 @@ githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "g
|
||||
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
|
||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
5
gradlew
vendored
5
gradlew
vendored
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -84,7 +86,8 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* An item detail provider for {@link ItemStack}'s whose {@link Item} has a specific type.
|
||||
* An item detail provider for {@link ItemStack}s whose {@link Item} has a specific type.
|
||||
*
|
||||
* @param <T> The type the stack's item must have.
|
||||
*/
|
||||
@@ -22,7 +22,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
||||
private final @Nullable String namespace;
|
||||
|
||||
/**
|
||||
* Create a new item detail provider. Meta will be inserted into a new sub-map named as per {@code namespace}.
|
||||
* Create a new item detail provider. Details will be inserted into a new sub-map named as per {@code namespace}.
|
||||
*
|
||||
* @param itemType The type the stack's item must have.
|
||||
* @param namespace The namespace to use for this provider.
|
||||
@@ -34,7 +34,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new item detail provider. Meta will be inserted directly into the results.
|
||||
* Create a new item detail provider. Details will be inserted directly into the results.
|
||||
*
|
||||
* @param itemType The type the stack's item must have.
|
||||
*/
|
||||
@@ -53,21 +53,18 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
||||
* @param stack The item stack to provide details for.
|
||||
* @param item The item to provide details for.
|
||||
*/
|
||||
public abstract void provideDetails(
|
||||
Map<? super String, Object> data, ItemStack stack, T item
|
||||
);
|
||||
public abstract void provideDetails(Map<? super String, Object> data, ItemStack stack, T item);
|
||||
|
||||
@Override
|
||||
public void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
||||
public final void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
||||
var item = stack.getItem();
|
||||
if (!itemType.isInstance(item)) return;
|
||||
|
||||
// If `namespace` is specified, insert into a new data map instead of the existing one.
|
||||
Map<? super String, Object> child = namespace == null ? data : new HashMap<>();
|
||||
|
||||
provideDetails(child, stack, itemType.cast(item));
|
||||
|
||||
if (namespace != null) {
|
||||
if (namespace == null) {
|
||||
provideDetails(data, stack, itemType.cast(item));
|
||||
} else {
|
||||
Map<? super String, Object> child = new HashMap<>();
|
||||
provideDetails(child, stack, itemType.cast(item));
|
||||
data.put(namespace, child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public interface DetailRegistry<T> {
|
||||
* @param provider The detail provider to register.
|
||||
* @see DetailProvider
|
||||
*/
|
||||
void addProvider(DetailProvider<T> provider);
|
||||
void addProvider(DetailProvider<? super T> provider);
|
||||
|
||||
/**
|
||||
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
/**
|
||||
* An enum representing the two sides of the turtle that a turtle turtle might reside.
|
||||
* An enum representing the two sides of the turtle that a turtle upgrade might reside.
|
||||
*/
|
||||
public enum TurtleSide {
|
||||
/**
|
||||
|
||||
@@ -38,9 +38,9 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
|
||||
implementation(project(":core"))
|
||||
implementation(commonClasses(project(":common-api")))
|
||||
clientImplementation(clientClasses(project(":common-api")))
|
||||
api(project(":core"))
|
||||
api(commonClasses(project(":common-api")))
|
||||
clientApi(clientClasses(project(":common-api")))
|
||||
|
||||
compileOnly(libs.bundles.externalMods.common)
|
||||
compileOnly(variantOf(libs.create.forge) { classifier("slim") }) { isTransitive = false }
|
||||
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
||||
@@ -73,6 +74,7 @@ public final class ClientRegistry {
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.LECTERN.get(), CustomLecternRenderer::new);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,15 +6,22 @@ package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ContainerListener;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
|
||||
|
||||
@@ -23,40 +30,75 @@ import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMA
|
||||
*
|
||||
* @see dan200.computercraft.client.render.PrintoutRenderer
|
||||
*/
|
||||
public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
private final boolean book;
|
||||
private final int pages;
|
||||
private final TextBuffer[] text;
|
||||
private final TextBuffer[] colours;
|
||||
private int page;
|
||||
public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu> implements ContainerListener {
|
||||
private PrintoutInfo printout = PrintoutInfo.DEFAULT;
|
||||
private int page = 0;
|
||||
|
||||
public PrintoutScreen(HeldItemMenu container, Inventory player, Component title) {
|
||||
public PrintoutScreen(PrintoutMenu container, Inventory player, Component title) {
|
||||
super(container, player, title);
|
||||
|
||||
imageHeight = Y_SIZE;
|
||||
}
|
||||
|
||||
var text = PrintoutItem.getText(container.getStack());
|
||||
this.text = new TextBuffer[text.length];
|
||||
for (var i = 0; i < this.text.length; i++) this.text[i] = new TextBuffer(text[i]);
|
||||
private void setPrintout(ItemStack stack) {
|
||||
var text = PrintoutItem.getText(stack);
|
||||
var textBuffers = new TextBuffer[text.length];
|
||||
for (var i = 0; i < textBuffers.length; i++) textBuffers[i] = new TextBuffer(text[i]);
|
||||
|
||||
var colours = PrintoutItem.getColours(container.getStack());
|
||||
this.colours = new TextBuffer[colours.length];
|
||||
for (var i = 0; i < this.colours.length; i++) this.colours[i] = new TextBuffer(colours[i]);
|
||||
var colours = PrintoutItem.getColours(stack);
|
||||
var colourBuffers = new TextBuffer[colours.length];
|
||||
for (var i = 0; i < colours.length; i++) colourBuffers[i] = new TextBuffer(colours[i]);
|
||||
|
||||
page = 0;
|
||||
pages = Math.max(this.text.length / PrintoutItem.LINES_PER_PAGE, 1);
|
||||
book = ((PrintoutItem) container.getStack().getItem()).getType() == PrintoutItem.Type.BOOK;
|
||||
var pages = Math.max(text.length / PrintoutItem.LINES_PER_PAGE, 1);
|
||||
var book = stack.is(ModRegistry.Items.PRINTED_BOOK.get());
|
||||
|
||||
printout = new PrintoutInfo(pages, book, textBuffers, colourBuffers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
menu.addSlotListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
menu.removeSlotListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void slotChanged(AbstractContainerMenu menu, int slot, ItemStack stack) {
|
||||
if (slot == 0) setPrintout(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataChanged(AbstractContainerMenu menu, int slot, int data) {
|
||||
if (slot == PrintoutMenu.DATA_CURRENT_PAGE) page = data;
|
||||
}
|
||||
|
||||
private void setPage(int page) {
|
||||
this.page = page;
|
||||
|
||||
var gameMode = Objects.requireNonNull(Objects.requireNonNull(minecraft).gameMode);
|
||||
gameMode.handleInventoryButtonClick(menu.containerId, PrintoutMenu.PAGE_BUTTON_OFFSET + page);
|
||||
}
|
||||
|
||||
private void previousPage() {
|
||||
if (page > 0) setPage(page - 1);
|
||||
}
|
||||
|
||||
private void nextPage() {
|
||||
if (page < printout.pages() - 1) setPage(page + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, int scancode, int modifiers) {
|
||||
if (key == GLFW.GLFW_KEY_RIGHT) {
|
||||
if (page < pages - 1) page++;
|
||||
nextPage();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == GLFW.GLFW_KEY_LEFT) {
|
||||
if (page > 0) page--;
|
||||
previousPage();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,13 +110,13 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
if (super.mouseScrolled(x, y, delta)) return true;
|
||||
if (delta < 0) {
|
||||
// Scroll up goes to the next page
|
||||
if (page < pages - 1) page++;
|
||||
nextPage();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (delta > 0) {
|
||||
// Scroll down goes to the previous page
|
||||
if (page > 0) page--;
|
||||
previousPage();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -85,8 +127,9 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||
// Draw the printout
|
||||
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
|
||||
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
|
||||
|
||||
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, printout.pages(), printout.book(), FULL_BRIGHT_LIGHTMAP);
|
||||
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, printout.text(), printout.colour());
|
||||
renderer.endBatch();
|
||||
}
|
||||
|
||||
@@ -105,4 +148,18 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
||||
// Skip rendering labels.
|
||||
}
|
||||
|
||||
record PrintoutInfo(int pages, boolean book, TextBuffer[] text, TextBuffer[] colour) {
|
||||
public static final PrintoutInfo DEFAULT;
|
||||
|
||||
static {
|
||||
var textLines = new TextBuffer[PrintoutItem.LINES_PER_PAGE];
|
||||
Arrays.fill(textLines, new TextBuffer(" ".repeat(PrintoutItem.LINE_MAX_LENGTH)));
|
||||
|
||||
var colourLines = new TextBuffer[PrintoutItem.LINES_PER_PAGE];
|
||||
Arrays.fill(colourLines, new TextBuffer("f".repeat(PrintoutItem.LINE_MAX_LENGTH)));
|
||||
|
||||
DEFAULT = new PrintoutInfo(1, false, textLines, colourLines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.model;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
import net.minecraft.client.model.geom.builders.CubeListBuilder;
|
||||
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A model for {@linkplain PrintoutItem printouts} placed on a lectern.
|
||||
* <p>
|
||||
* This provides two models, {@linkplain #renderPages(PoseStack, VertexConsumer, int, int, int) one for a variable
|
||||
* number of pages}, and {@linkplain #renderBook(PoseStack, VertexConsumer, int, int) one for books}.
|
||||
*
|
||||
* @see CustomLecternRenderer
|
||||
*/
|
||||
public class LecternPrintoutModel {
|
||||
public static final ResourceLocation TEXTURE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/printout");
|
||||
public static final Material MATERIAL = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE);
|
||||
|
||||
private static final int TEXTURE_WIDTH = 32;
|
||||
private static final int TEXTURE_HEIGHT = 32;
|
||||
|
||||
private static final String PAGE_1 = "page_1";
|
||||
private static final String PAGE_2 = "page_2";
|
||||
private static final String PAGE_3 = "page_3";
|
||||
private static final List<String> PAGES = List.of(PAGE_1, PAGE_2, PAGE_3);
|
||||
|
||||
private final ModelPart pagesRoot;
|
||||
private final ModelPart bookRoot;
|
||||
private final ModelPart[] pages;
|
||||
|
||||
public LecternPrintoutModel() {
|
||||
pagesRoot = buildPages();
|
||||
bookRoot = buildBook();
|
||||
pages = PAGES.stream().map(pagesRoot::getChild).toArray(ModelPart[]::new);
|
||||
}
|
||||
|
||||
private static ModelPart buildPages() {
|
||||
var mesh = new MeshDefinition();
|
||||
var parts = mesh.getRoot();
|
||||
parts.addOrReplaceChild(
|
||||
PAGE_1,
|
||||
CubeListBuilder.create().texOffs(0, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||
PartPose.ZERO
|
||||
);
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
PAGE_2,
|
||||
CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.125f, 0, 1.5f, (float) Math.PI * (1f / 16), 0, 0)
|
||||
);
|
||||
parts.addOrReplaceChild(
|
||||
PAGE_3,
|
||||
CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.25f, 0, -1.5f, (float) -Math.PI * (2f / 16), 0, 0)
|
||||
);
|
||||
|
||||
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
private static ModelPart buildBook() {
|
||||
var mesh = new MeshDefinition();
|
||||
var parts = mesh.getRoot();
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
"spine",
|
||||
CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
|
||||
PartPose.ZERO
|
||||
);
|
||||
|
||||
var angle = (float) Math.toRadians(5);
|
||||
parts.addOrReplaceChild(
|
||||
"left",
|
||||
CubeListBuilder.create()
|
||||
.texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
|
||||
.texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
|
||||
);
|
||||
|
||||
parts.addOrReplaceChild(
|
||||
"right",
|
||||
CubeListBuilder.create()
|
||||
.texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
|
||||
.texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
|
||||
PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
|
||||
);
|
||||
|
||||
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
public void renderBook(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay) {
|
||||
bookRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
public void renderPages(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int pageCount) {
|
||||
if (pageCount > pages.length) pageCount = pages.length;
|
||||
var i = 0;
|
||||
for (; i < pageCount; i++) pages[i].visible = true;
|
||||
for (; i < pages.length; i++) pages[i].visible = false;
|
||||
|
||||
pagesRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
@@ -56,4 +56,8 @@ public final class ClientPocketComputers {
|
||||
var id = PocketComputerItem.getInstanceID(stack);
|
||||
return id == null ? null : instances.get(id);
|
||||
}
|
||||
|
||||
static @Nullable PocketComputerData get(UUID id) {
|
||||
return instances.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.pocket;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.SpriteRenderer;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||
import dan200.computercraft.shared.pocket.items.PocketTooltipComponent;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
|
||||
/**
|
||||
* Renders the pocket computer's terminal in the item's tooltip.
|
||||
* <p>
|
||||
* The rendered terminal is downscaled by a factor of {@link #SCALE}.
|
||||
*/
|
||||
public class PocketClientTooltipComponent implements ClientTooltipComponent {
|
||||
private static final float SCALE = 0.5f;
|
||||
|
||||
private final UUID id;
|
||||
private final ComputerFamily family;
|
||||
|
||||
public PocketClientTooltipComponent(PocketTooltipComponent component) {
|
||||
this.id = component.id();
|
||||
this.family = component.family();
|
||||
}
|
||||
|
||||
private @Nullable PocketComputerData computer() {
|
||||
return ClientPocketComputers.get(id);
|
||||
}
|
||||
|
||||
private @Nullable NetworkedTerminal terminal() {
|
||||
var computer = computer();
|
||||
return computer == null ? null : computer.getTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
var terminal = terminal();
|
||||
if (terminal == null) return 0;
|
||||
|
||||
return (int) Math.ceil(
|
||||
(terminal.getHeight() * FixedWidthFontRenderer.FONT_HEIGHT + ComputerBorderRenderer.BORDER * 2 + ComputerBorderRenderer.MARGIN * 2) * SCALE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(Font font) {
|
||||
var terminal = terminal();
|
||||
if (terminal == null) return 0;
|
||||
|
||||
return (int) Math.ceil(
|
||||
(terminal.getWidth() * FixedWidthFontRenderer.FONT_WIDTH + ComputerBorderRenderer.BORDER * 2 + ComputerBorderRenderer.MARGIN * 2) * SCALE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderImage(Font font, int x, int y, GuiGraphics guiGraphics) {
|
||||
var terminal = terminal();
|
||||
if (terminal == null) return;
|
||||
|
||||
var pose = guiGraphics.pose();
|
||||
pose.pushPose();
|
||||
pose.translate(x, y, 0);
|
||||
pose.scale(SCALE, SCALE, 1);
|
||||
|
||||
|
||||
render(pose, guiGraphics.bufferSource(), terminal);
|
||||
|
||||
pose.popPose();
|
||||
}
|
||||
|
||||
private void render(PoseStack stack, MultiBufferSource buffers, Terminal terminal) {
|
||||
var width = terminal.getWidth() * FONT_WIDTH + MARGIN * 2;
|
||||
var height = terminal.getHeight() * FONT_HEIGHT + MARGIN * 2;
|
||||
|
||||
var renderer = SpriteRenderer.createForGui(stack.last().pose(), buffers.getBuffer(RenderTypes.GUI_SPRITES));
|
||||
ComputerBorderRenderer.render(renderer, GuiSprites.getComputerTextures(family), BORDER, BORDER, width, height, false);
|
||||
|
||||
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(stack, buffers.getBuffer(RenderTypes.TERMINAL));
|
||||
FixedWidthFontRenderer.drawTerminal(quadEmitter, BORDER + MARGIN, BORDER + MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.client.renderer.blockentity.LecternRenderer;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
|
||||
/**
|
||||
* A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}.
|
||||
* <p>
|
||||
* This largely follows {@link LecternRenderer}, but with support for multiple types of item.
|
||||
*/
|
||||
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
|
||||
private final LecternPrintoutModel printoutModel;
|
||||
|
||||
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
|
||||
printoutModel = new LecternPrintoutModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(0.5f, 1.0625f, 0.5f);
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(-lectern.getBlockState().getValue(LecternBlock.FACING).getClockWise().toYRot()));
|
||||
poseStack.mulPose(Axis.ZP.rotationDegrees(67.5f));
|
||||
poseStack.translate(0, -0.125f, 0);
|
||||
|
||||
var item = lectern.getItem();
|
||||
if (item.getItem() instanceof PrintoutItem printout) {
|
||||
var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
|
||||
if (printout.getType() == PrintoutItem.Type.BOOK) {
|
||||
printoutModel.renderBook(poseStack, vertexConsumer, packedLight, packedOverlay);
|
||||
} else {
|
||||
printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutItem.getPageCount(item));
|
||||
}
|
||||
}
|
||||
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import dan200.computercraft.shared.util.ARGB32;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -92,16 +93,11 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
}
|
||||
|
||||
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
|
||||
var r = (byte) ((colour >>> 16) & 0xFF);
|
||||
var g = (byte) ((colour >>> 8) & 0xFF);
|
||||
var b = (byte) (colour & 0xFF);
|
||||
var c = new byte[]{ r, g, b, (byte) 255 };
|
||||
|
||||
var buffer = render.getBuffer(RenderTypes.TERMINAL);
|
||||
FixedWidthFontRenderer.drawQuad(
|
||||
FixedWidthFontRenderer.toVertexConsumer(transform, buffer),
|
||||
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
|
||||
c, RenderTypes.FULL_BRIGHT_LIGHTMAP
|
||||
ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,12 @@ public class SpriteRenderer {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static SpriteRenderer createForGui(Matrix4f transform, VertexConsumer builder) {
|
||||
return new SpriteRenderer(transform, builder, 0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255);
|
||||
}
|
||||
|
||||
public static SpriteRenderer createForGui(GuiGraphics graphics, RenderType renderType) {
|
||||
return new SpriteRenderer(
|
||||
graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType),
|
||||
0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255
|
||||
);
|
||||
return createForGui(graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,9 +13,11 @@ import dan200.computercraft.core.terminal.Palette;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.util.ARGB32;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*;
|
||||
import static org.lwjgl.system.MemoryUtil.*;
|
||||
@@ -38,10 +40,12 @@ import static org.lwjgl.system.MemoryUtil.*;
|
||||
* {@link FixedWidthFontRenderer}.
|
||||
*/
|
||||
public final class DirectFixedWidthFontRenderer {
|
||||
private static final boolean IS_LITTLE_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
|
||||
|
||||
private DirectFixedWidthFontRenderer() {
|
||||
}
|
||||
|
||||
private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour) {
|
||||
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour) {
|
||||
// Short circuit to avoid the common case - the texture should be blank here after all.
|
||||
if (index == '\0' || index == ' ') return;
|
||||
|
||||
@@ -158,8 +162,8 @@ public final class DirectFixedWidthFontRenderer {
|
||||
return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2;
|
||||
}
|
||||
|
||||
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
|
||||
buffer.quad(x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
|
||||
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
|
||||
buffer.quad(x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
|
||||
}
|
||||
|
||||
public interface QuadEmitter {
|
||||
@@ -167,7 +171,7 @@ public final class DirectFixedWidthFontRenderer {
|
||||
|
||||
ByteBuffer buffer();
|
||||
|
||||
void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2);
|
||||
void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2);
|
||||
}
|
||||
|
||||
public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter {
|
||||
@@ -177,12 +181,12 @@ public final class DirectFixedWidthFontRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
|
||||
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
|
||||
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
|
||||
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
|
||||
}
|
||||
}
|
||||
|
||||
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
|
||||
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
|
||||
// Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the
|
||||
// underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write.
|
||||
// This provides significant performance gains, at the cost of well, using Unsafe.
|
||||
@@ -196,16 +200,15 @@ public final class DirectFixedWidthFontRenderer {
|
||||
if (position < 0 || 112 > buffer.limit() - position) throw new IndexOutOfBoundsException();
|
||||
// Require the pointer to be aligned to a 32-bit boundary.
|
||||
if ((addr & 3) != 0) throw new IllegalStateException("Memory is not aligned");
|
||||
// Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances.
|
||||
if (rgba.length != 4) throw new IllegalStateException();
|
||||
|
||||
// Pack colour so it is equivalent to rgba:BBBB.
|
||||
var colourAbgr = ARGB32.toABGR32(colour);
|
||||
var nativeColour = IS_LITTLE_ENDIAN ? colourAbgr : Integer.reverseBytes(colourAbgr);
|
||||
|
||||
memPutFloat(addr + 0, x1);
|
||||
memPutFloat(addr + 4, y1);
|
||||
memPutFloat(addr + 8, z);
|
||||
memPutByte(addr + 12, rgba[0]);
|
||||
memPutByte(addr + 13, rgba[1]);
|
||||
memPutByte(addr + 14, rgba[2]);
|
||||
memPutByte(addr + 15, (byte) 255);
|
||||
memPutInt(addr + 12, nativeColour);
|
||||
memPutFloat(addr + 16, u1);
|
||||
memPutFloat(addr + 20, v1);
|
||||
memPutShort(addr + 24, (short) 0xF0);
|
||||
@@ -214,10 +217,7 @@ public final class DirectFixedWidthFontRenderer {
|
||||
memPutFloat(addr + 28, x1);
|
||||
memPutFloat(addr + 32, y2);
|
||||
memPutFloat(addr + 36, z);
|
||||
memPutByte(addr + 40, rgba[0]);
|
||||
memPutByte(addr + 41, rgba[1]);
|
||||
memPutByte(addr + 42, rgba[2]);
|
||||
memPutByte(addr + 43, (byte) 255);
|
||||
memPutInt(addr + 40, nativeColour);
|
||||
memPutFloat(addr + 44, u1);
|
||||
memPutFloat(addr + 48, v2);
|
||||
memPutShort(addr + 52, (short) 0xF0);
|
||||
@@ -226,10 +226,7 @@ public final class DirectFixedWidthFontRenderer {
|
||||
memPutFloat(addr + 56, x2);
|
||||
memPutFloat(addr + 60, y2);
|
||||
memPutFloat(addr + 64, z);
|
||||
memPutByte(addr + 68, rgba[0]);
|
||||
memPutByte(addr + 69, rgba[1]);
|
||||
memPutByte(addr + 70, rgba[2]);
|
||||
memPutByte(addr + 71, (byte) 255);
|
||||
memPutInt(addr + 68, nativeColour);
|
||||
memPutFloat(addr + 72, u2);
|
||||
memPutFloat(addr + 76, v2);
|
||||
memPutShort(addr + 80, (short) 0xF0);
|
||||
@@ -238,10 +235,7 @@ public final class DirectFixedWidthFontRenderer {
|
||||
memPutFloat(addr + 84, x2);
|
||||
memPutFloat(addr + 88, y1);
|
||||
memPutFloat(addr + 92, z);
|
||||
memPutByte(addr + 96, rgba[0]);
|
||||
memPutByte(addr + 97, rgba[1]);
|
||||
memPutByte(addr + 98, rgba[2]);
|
||||
memPutByte(addr + 99, (byte) 255);
|
||||
memPutInt(addr + 96, nativeColour);
|
||||
memPutFloat(addr + 100, u2);
|
||||
memPutFloat(addr + 104, v1);
|
||||
memPutShort(addr + 108, (short) 0xF0);
|
||||
|
||||
@@ -12,6 +12,7 @@ import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.FastColor;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
@@ -41,7 +42,7 @@ public final class FixedWidthFontRenderer {
|
||||
static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
|
||||
static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
|
||||
|
||||
private static final byte[] BLACK = new byte[]{ byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), (byte) 255 };
|
||||
private static final int BLACK = FastColor.ARGB32.color(255, byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()));
|
||||
private static final float Z_OFFSET = 1e-3f;
|
||||
|
||||
private FixedWidthFontRenderer() {
|
||||
@@ -59,7 +60,7 @@ public final class FixedWidthFontRenderer {
|
||||
return 15 - Terminal.getColour(c, def);
|
||||
}
|
||||
|
||||
private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour, int light) {
|
||||
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour, int light) {
|
||||
// Short circuit to avoid the common case - the texture should be blank here after all.
|
||||
if (index == '\0' || index == ' ') return;
|
||||
|
||||
@@ -75,7 +76,7 @@ public final class FixedWidthFontRenderer {
|
||||
);
|
||||
}
|
||||
|
||||
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light) {
|
||||
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, int colour, int light) {
|
||||
quad(emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light);
|
||||
}
|
||||
|
||||
@@ -216,10 +217,10 @@ public final class FixedWidthFontRenderer {
|
||||
return new QuadEmitter(transform.last().pose(), consumer);
|
||||
}
|
||||
|
||||
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light) {
|
||||
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2, int light) {
|
||||
var poseMatrix = c.poseMatrix();
|
||||
var consumer = c.consumer();
|
||||
byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
|
||||
int r = FastColor.ARGB32.red(colour), g = FastColor.ARGB32.green(colour), b = FastColor.ARGB32.blue(colour), a = FastColor.ARGB32.alpha(colour);
|
||||
|
||||
consumer.vertex(poseMatrix, x1, y1, z).color(r, g, b, a).uv(u1, v1).uv2(light).endVertex();
|
||||
consumer.vertex(poseMatrix, x1, y2, z).color(r, g, b, a).uv(u1, v2).uv2(light).endVertex();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.data.client;
|
||||
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||
import dan200.computercraft.data.DataProviders;
|
||||
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
|
||||
@@ -30,7 +31,8 @@ public final class ClientDataProviders {
|
||||
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())
|
||||
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty()),
|
||||
new SingleFile(LecternPrintoutModel.TEXTURE, Optional.empty())
|
||||
));
|
||||
out.accept(GuiSprites.SPRITE_SHEET, Stream.of(
|
||||
// Buttons
|
||||
|
||||
8
projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
generated
Normal file
8
projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"variants": {
|
||||
"facing=east": {"model": "minecraft:block/lectern", "y": 90},
|
||||
"facing=north": {"model": "minecraft:block/lectern", "y": 0},
|
||||
"facing=south": {"model": "minecraft:block/lectern", "y": 180},
|
||||
"facing=west": {"model": "minecraft:block/lectern", "y": 270}
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,6 @@
|
||||
"commands.computercraft.generic.additional_rows": "%d additional rows…",
|
||||
"commands.computercraft.generic.exception": "Unhandled exception (%s)",
|
||||
"commands.computercraft.generic.no": "N",
|
||||
"commands.computercraft.generic.no_position": "<no pos>",
|
||||
"commands.computercraft.generic.position": "%s, %s, %s",
|
||||
"commands.computercraft.generic.yes": "Y",
|
||||
"commands.computercraft.help.desc": "Displays this help message",
|
||||
"commands.computercraft.help.no_children": "%s has no sub-commands",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"sources": [
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"},
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"}
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"},
|
||||
{"type": "minecraft:single", "resource": "computercraft:entity/printout"}
|
||||
]
|
||||
}
|
||||
|
||||
12
projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/lectern.json
generated
Normal file
12
projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/lectern.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "minecraft:block",
|
||||
"pools": [
|
||||
{
|
||||
"bonus_rolls": 0.0,
|
||||
"conditions": [{"condition": "minecraft:survives_explosion"}],
|
||||
"entries": [{"type": "minecraft:item", "name": "minecraft:lectern"}],
|
||||
"rolls": 1.0
|
||||
}
|
||||
],
|
||||
"random_sequence": "computercraft:blocks/lectern"
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import net.minecraft.data.models.BlockModelGenerators;
|
||||
import net.minecraft.data.models.blockstates.*;
|
||||
import net.minecraft.data.models.model.*;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
@@ -100,6 +101,11 @@ class BlockModelProvider {
|
||||
registerTurtleUpgrade(generators, "block/turtle_speaker", "block/turtle_speaker_face");
|
||||
registerTurtleModem(generators, "block/turtle_modem_normal", "block/wireless_modem_normal_face");
|
||||
registerTurtleModem(generators, "block/turtle_modem_advanced", "block/wireless_modem_advanced_face");
|
||||
|
||||
generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(
|
||||
ModRegistry.Blocks.LECTERN.get(),
|
||||
Variant.variant().with(VariantProperties.MODEL, ModelLocationUtils.getModelLocation(Blocks.LECTERN))
|
||||
).with(createHorizontalFacingDispatch()));
|
||||
}
|
||||
|
||||
private static void registerDiskDrive(BlockModelGenerators generators) {
|
||||
|
||||
@@ -160,8 +160,6 @@ public final class LanguageProvider implements DataProvider {
|
||||
add("commands.computercraft.queue.synopsis", "Send a computer_command event to a command computer");
|
||||
add("commands.computercraft.queue.desc", "Send a computer_command event to a command computer, passing through the additional arguments. This is mostly designed for map makers, acting as a more computer-friendly version of /trigger. Any player can run the command, which would most likely be done through a text component's click event.");
|
||||
|
||||
add("commands.computercraft.generic.no_position", "<no pos>");
|
||||
add("commands.computercraft.generic.position", "%s, %s, %s");
|
||||
add("commands.computercraft.generic.yes", "Y");
|
||||
add("commands.computercraft.generic.no", "N");
|
||||
add("commands.computercraft.generic.exception", "Unhandled exception (%s)");
|
||||
@@ -284,7 +282,9 @@ public final class LanguageProvider implements DataProvider {
|
||||
return Stream.of(
|
||||
RegistryWrappers.BLOCKS.stream()
|
||||
.filter(x -> RegistryWrappers.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(Block::getDescriptionId),
|
||||
.map(Block::getDescriptionId)
|
||||
// Exclude blocks that just reuse vanilla translations, such as the lectern.
|
||||
.filter(x -> !x.startsWith("block.minecraft.")),
|
||||
RegistryWrappers.ITEMS.stream()
|
||||
.filter(x -> RegistryWrappers.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||
.map(Item::getDescriptionId),
|
||||
|
||||
@@ -15,6 +15,7 @@ import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
||||
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
|
||||
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.storage.loot.LootPool;
|
||||
import net.minecraft.world.level.storage.loot.LootTable;
|
||||
@@ -57,6 +58,8 @@ class LootTableProvider {
|
||||
computerDrop(add, ModRegistry.Blocks.TURTLE_NORMAL);
|
||||
computerDrop(add, ModRegistry.Blocks.TURTLE_ADVANCED);
|
||||
|
||||
blockDrop(add, ModRegistry.Blocks.LECTERN, LootItem.lootTableItem(Items.LECTERN), ExplosionCondition.survivesExplosion());
|
||||
|
||||
add.accept(ModRegistry.Blocks.CABLE.get().getLootTable(), LootTable
|
||||
.lootTable()
|
||||
.withPool(LootPool.lootPool()
|
||||
|
||||
@@ -102,8 +102,14 @@ class TagProvider {
|
||||
ModRegistry.Items.MONITOR_ADVANCED.get()
|
||||
);
|
||||
|
||||
// Allow printed books to be placed in bookshelves.
|
||||
tags.tag(ItemTags.BOOKSHELF_BOOKS).add(ModRegistry.Items.PRINTED_BOOK.get());
|
||||
|
||||
// Allow any printout to be placed on lecterns. See also PrintoutItem and CustomLecternBlock.
|
||||
tags.tag(ItemTags.LECTERN_BOOKS).add(
|
||||
ModRegistry.Items.PRINTED_PAGE.get(), ModRegistry.Items.PRINTED_PAGES.get(), ModRegistry.Items.PRINTED_BOOK.get()
|
||||
);
|
||||
|
||||
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
|
||||
.add(Items.GLASS_BOTTLE)
|
||||
.addTag(ItemTags.BOATS);
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.*;
|
||||
* @param <T> The type of object that this registry provides details for.
|
||||
*/
|
||||
public class DetailRegistryImpl<T> implements DetailRegistry<T> {
|
||||
private final Collection<DetailProvider<T>> providers = new ArrayList<>();
|
||||
private final Collection<DetailProvider<? super T>> providers = new ArrayList<>();
|
||||
private final DetailProvider<T> basic;
|
||||
|
||||
public DetailRegistryImpl(DetailProvider<T> basic) {
|
||||
@@ -24,7 +24,7 @@ public class DetailRegistryImpl<T> implements DetailRegistry<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addProvider(DetailProvider<T> provider) {
|
||||
public synchronized void addProvider(DetailProvider<? super T> provider) {
|
||||
Objects.requireNonNull(provider, "provider cannot be null");
|
||||
if (!providers.contains(provider)) providers.add(provider);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import dan200.computercraft.shared.command.arguments.TrackingFieldArgumentType;
|
||||
import dan200.computercraft.shared.common.ClearColourRecipe;
|
||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.computer.apis.CommandAPI;
|
||||
import dan200.computercraft.shared.computer.blocks.CommandComputerBlock;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||
@@ -41,6 +40,9 @@ import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
||||
import dan200.computercraft.shared.details.BlockDetails;
|
||||
import dan200.computercraft.shared.details.ItemDetails;
|
||||
import dan200.computercraft.shared.integration.PermissionRegistry;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlock;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||
import dan200.computercraft.shared.media.items.DiskItem;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import dan200.computercraft.shared.media.items.RecordMedia;
|
||||
@@ -49,7 +51,6 @@ import dan200.computercraft.shared.media.recipes.DiskRecipe;
|
||||
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.network.container.ContainerData;
|
||||
import dan200.computercraft.shared.network.container.HeldItemContainerData;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlock;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveMenu;
|
||||
@@ -101,10 +102,12 @@ import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||
|
||||
@@ -172,6 +175,10 @@ public final class ModRegistry {
|
||||
public static final RegistryEntry<WiredModemFullBlock> WIRED_MODEM_FULL = REGISTRY.register("wired_modem_full",
|
||||
() -> new WiredModemFullBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||
|
||||
public static final RegistryEntry<CustomLecternBlock> LECTERN = REGISTRY.register("lectern", () -> new CustomLecternBlock(
|
||||
BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.5F).sound(SoundType.WOOD).ignitedByLava()
|
||||
));
|
||||
}
|
||||
|
||||
public static class BlockEntities {
|
||||
@@ -213,6 +220,8 @@ public final class ModRegistry {
|
||||
ofBlock(Blocks.WIRELESS_MODEM_NORMAL, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_NORMAL.get(), p, s, false));
|
||||
public static final RegistryEntry<BlockEntityType<WirelessModemBlockEntity>> WIRELESS_MODEM_ADVANCED =
|
||||
ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_ADVANCED.get(), p, s, true));
|
||||
|
||||
public static final RegistryEntry<BlockEntityType<CustomLecternBlockEntity>> LECTERN = ofBlock(Blocks.LECTERN, CustomLecternBlockEntity::new);
|
||||
}
|
||||
|
||||
public static final class Items {
|
||||
@@ -309,11 +318,8 @@ public final class ModRegistry {
|
||||
public static final RegistryEntry<MenuType<PrinterMenu>> PRINTER = REGISTRY.register("printer",
|
||||
() -> new MenuType<>(PrinterMenu::new, FeatureFlags.VANILLA_SET));
|
||||
|
||||
public static final RegistryEntry<MenuType<HeldItemMenu>> PRINTOUT = REGISTRY.register("printout",
|
||||
() -> ContainerData.toType(
|
||||
HeldItemContainerData::new,
|
||||
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.getHand())
|
||||
));
|
||||
public static final RegistryEntry<MenuType<PrintoutMenu>> PRINTOUT = REGISTRY.register("printout",
|
||||
() -> new MenuType<>((i, c) -> PrintoutMenu.createRemote(i), FeatureFlags.VANILLA_SET));
|
||||
}
|
||||
|
||||
static class ArgumentTypes {
|
||||
|
||||
@@ -40,9 +40,8 @@ public final class ChatHelpers {
|
||||
return component;
|
||||
}
|
||||
|
||||
public static MutableComponent position(@Nullable BlockPos pos) {
|
||||
if (pos == null) return Component.translatable("commands.computercraft.generic.no_position");
|
||||
return Component.translatable("commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ());
|
||||
public static MutableComponent position(BlockPos pos) {
|
||||
return Component.literal(pos.toShortString());
|
||||
}
|
||||
|
||||
public static MutableComponent bool(boolean value) {
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TableBuilder {
|
||||
/**
|
||||
* Get the number of columns for this table.
|
||||
* <p>
|
||||
* This will be the same as {@link #getHeaders()}'s length if it is is non-{@code null},
|
||||
* This will be the same as {@link #getHeaders()}'s length if it is non-{@code null},
|
||||
* otherwise the length of the first column.
|
||||
*
|
||||
* @return The number of columns.
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
package dan200.computercraft.shared.common;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class HeldItemMenu extends AbstractContainerMenu {
|
||||
private final ItemStack stack;
|
||||
private final InteractionHand hand;
|
||||
|
||||
public HeldItemMenu(MenuType<? extends HeldItemMenu> type, int id, Player player, InteractionHand hand) {
|
||||
super(type, id);
|
||||
|
||||
this.hand = hand;
|
||||
stack = player.getItemInHand(hand).copy();
|
||||
}
|
||||
|
||||
public ItemStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int slot) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
if (!player.isAlive()) return false;
|
||||
|
||||
var stack = player.getItemInHand(hand);
|
||||
return stack == this.stack || !stack.isEmpty() && !this.stack.isEmpty() && stack.getItem() == this.stack.getItem();
|
||||
}
|
||||
|
||||
public static class Factory implements MenuProvider {
|
||||
private final MenuType<HeldItemMenu> type;
|
||||
private final Component name;
|
||||
private final InteractionHand hand;
|
||||
|
||||
public Factory(MenuType<HeldItemMenu> type, ItemStack stack, InteractionHand hand) {
|
||||
this.type = type;
|
||||
name = stack.getHoverName();
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) {
|
||||
return new HeldItemMenu(type, id, player, hand);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,9 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.storage.loot.LootParams;
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntity> extends HorizontalDirectionalBlock implements IBundledRedstoneBlock, EntityBlock {
|
||||
private static final ResourceLocation DROP = new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer");
|
||||
@@ -110,9 +110,19 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
return super.getCloneItemStack(world, pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public List<ItemStack> getDrops(BlockState state, LootParams.Builder params) {
|
||||
if (params.getOptionalParameter(LootContextParams.BLOCK_ENTITY) instanceof AbstractComputerBlockEntity computer) {
|
||||
params = params.withDynamicDrop(DROP, out -> out.accept(getItem(computer)));
|
||||
}
|
||||
|
||||
return super.getDrops(state, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity tile, ItemStack tool) {
|
||||
// Don't drop blocks here - see onBlockHarvested.
|
||||
// Don't drop blocks here - see playerWillDestroy.
|
||||
player.awardStat(Stats.BLOCK_MINED.get(this));
|
||||
player.causeFoodExhaustion(0.005F);
|
||||
}
|
||||
@@ -120,25 +130,11 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
@Override
|
||||
public void playerWillDestroy(Level world, BlockPos pos, BlockState state, Player player) {
|
||||
super.playerWillDestroy(world, pos, state, player);
|
||||
if (!(world instanceof ServerLevel serverWorld)) return;
|
||||
if (!(world instanceof ServerLevel serverLevel)) return;
|
||||
|
||||
// We drop the item here instead of doing it in the harvest method, as we should
|
||||
// drop computers for creative players too.
|
||||
|
||||
var tile = world.getBlockEntity(pos);
|
||||
if (tile instanceof AbstractComputerBlockEntity computer) {
|
||||
var context = new LootParams.Builder(serverWorld)
|
||||
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
|
||||
.withParameter(LootContextParams.TOOL, player.getMainHandItem())
|
||||
.withParameter(LootContextParams.THIS_ENTITY, player)
|
||||
.withParameter(LootContextParams.BLOCK_ENTITY, tile)
|
||||
.withDynamicDrop(DROP, out -> out.accept(getItem(computer)));
|
||||
for (var item : state.getDrops(context)) {
|
||||
popResource(world, pos, item);
|
||||
}
|
||||
|
||||
state.spawnAfterBreak(serverWorld, pos, player.getMainHandItem(), true);
|
||||
}
|
||||
dropResources(state, serverLevel, pos, world.getBlockEntity(pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
fresh = false;
|
||||
computerID = computer.getID();
|
||||
|
||||
// If the on state has changed, mark as as dirty.
|
||||
// If the on state has changed, mark as dirty.
|
||||
var newOn = computer.isOn();
|
||||
if (on != newOn) {
|
||||
on = newOn;
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
|
||||
package dan200.computercraft.shared.container;
|
||||
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A basic implementation of {@link Container} which operates on a {@linkplain #getContents() list of stacks}.
|
||||
*/
|
||||
public interface BasicContainer extends Container {
|
||||
NonNullList<ItemStack> getContents();
|
||||
List<ItemStack> getContents();
|
||||
|
||||
@Override
|
||||
default int getContainerSize() {
|
||||
|
||||
@@ -50,12 +50,12 @@ public class ItemDetails {
|
||||
if (tag != null && tag.contains("display", Tag.TAG_COMPOUND)) {
|
||||
var displayTag = tag.getCompound("display");
|
||||
if (displayTag.contains("Lore", Tag.TAG_LIST)) {
|
||||
var loreTag = displayTag.getList("Lore", Tag.TAG_STRING);
|
||||
data.put("lore", loreTag.stream()
|
||||
var lore = displayTag.getList("Lore", Tag.TAG_STRING).stream()
|
||||
.map(ItemDetails::parseTextComponent)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Component::getString)
|
||||
.toList());
|
||||
.toList();
|
||||
if (!lore.isEmpty()) data.put("lore", lore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.lectern;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
|
||||
/**
|
||||
* Extends {@link LecternBlock} with support for {@linkplain PrintoutItem printouts}.
|
||||
* <p>
|
||||
* Unlike the vanilla lectern, this block is never empty. If the book is removed from the lectern, it converts back to
|
||||
* its vanilla version (see {@link #clearLectern(Level, BlockPos, BlockState)}).
|
||||
*
|
||||
* @see PrintoutItem#useOn(UseOnContext) Placing books into a lectern.
|
||||
*/
|
||||
public class CustomLecternBlock extends LecternBlock {
|
||||
public CustomLecternBlock(Properties properties) {
|
||||
super(properties);
|
||||
registerDefaultState(defaultBlockState().setValue(HAS_BOOK, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a vanilla lectern with a custom one.
|
||||
*
|
||||
* @param level The current level.
|
||||
* @param pos The position of the lectern.
|
||||
* @param blockState The current state of the lectern.
|
||||
* @param item The item to place in the custom lectern.
|
||||
*/
|
||||
public static void replaceLectern(Level level, BlockPos pos, BlockState blockState, ItemStack item) {
|
||||
level.setBlockAndUpdate(pos, ModRegistry.Blocks.LECTERN.get().defaultBlockState()
|
||||
.setValue(HAS_BOOK, true)
|
||||
.setValue(FACING, blockState.getValue(FACING))
|
||||
.setValue(POWERED, blockState.getValue(POWERED)));
|
||||
|
||||
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity be) be.setItem(item.split(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a custom lectern and replace it with an empty vanilla one.
|
||||
*
|
||||
* @param level The current level.
|
||||
* @param pos The position of the lectern.
|
||||
* @param blockState The current state of the lectern.
|
||||
*/
|
||||
static void clearLectern(Level level, BlockPos pos, BlockState blockState) {
|
||||
level.setBlockAndUpdate(pos, Blocks.LECTERN.defaultBlockState()
|
||||
.setValue(HAS_BOOK, false)
|
||||
.setValue(FACING, blockState.getValue(FACING))
|
||||
.setValue(POWERED, blockState.getValue(POWERED)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ItemStack getCloneItemStack(BlockGetter level, BlockPos pos, BlockState state) {
|
||||
return new ItemStack(Items.LECTERN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
// If we've no lectern, remove it.
|
||||
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern && lectern.getItem().isEmpty()) {
|
||||
clearLectern(level, pos, state);
|
||||
return;
|
||||
}
|
||||
|
||||
super.tick(state, level, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||
if (state.is(newState.getBlock())) return;
|
||||
|
||||
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
|
||||
dropItem(level, pos, state, lectern.getItem().copy());
|
||||
}
|
||||
|
||||
super.onRemove(state, level, pos, newState, isMoving);
|
||||
}
|
||||
|
||||
private static void dropItem(Level level, BlockPos pos, BlockState state, ItemStack stack) {
|
||||
if (stack.isEmpty()) return;
|
||||
|
||||
var direction = state.getValue(FACING);
|
||||
var dx = 0.25 * direction.getStepX();
|
||||
var dz = 0.25 * direction.getStepZ();
|
||||
var entity = new ItemEntity(level, pos.getX() + 0.5 + dx, pos.getY() + 1, pos.getZ() + 0.5 + dz, stack);
|
||||
entity.setDefaultPickUpDelay();
|
||||
level.addFreshEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescriptionId() {
|
||||
return Blocks.LECTERN.getDescriptionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomLecternBlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new CustomLecternBlockEntity(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) {
|
||||
return level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern ? lectern.getRedstoneSignal() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||
if (!level.isClientSide && level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
|
||||
if (player.isSecondaryUseActive()) {
|
||||
// When shift+clicked with an empty hand, drop the item and replace with the normal lectern.
|
||||
clearLectern(level, pos, state);
|
||||
} else {
|
||||
// Otherwise open the screen.
|
||||
player.openMenu(lectern);
|
||||
}
|
||||
|
||||
player.awardStat(Stats.INTERACT_WITH_LECTERN);
|
||||
}
|
||||
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.lectern;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.container.BasicContainer;
|
||||
import dan200.computercraft.shared.container.SingleContainerData;
|
||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ContainerData;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The block entity for our {@link CustomLecternBlock}.
|
||||
*
|
||||
* @see LecternBlockEntity
|
||||
*/
|
||||
public final class CustomLecternBlockEntity extends BlockEntity implements MenuProvider {
|
||||
private static final String NBT_ITEM = "Item";
|
||||
private static final String NBT_PAGE = "Page";
|
||||
|
||||
private ItemStack item = ItemStack.EMPTY;
|
||||
private int page, pageCount;
|
||||
|
||||
public CustomLecternBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(ModRegistry.BlockEntities.LECTERN.get(), pos, blockState);
|
||||
}
|
||||
|
||||
public ItemStack getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
void setItem(ItemStack item) {
|
||||
this.item = item;
|
||||
itemChanged();
|
||||
BlockEntityHelpers.updateBlock(this);
|
||||
}
|
||||
|
||||
int getRedstoneSignal() {
|
||||
if (item.getItem() instanceof PrintoutItem) {
|
||||
var progress = pageCount > 1 ? (float) page / (pageCount - 1) : 1F;
|
||||
return Mth.floor(progress * 14f) + 1;
|
||||
}
|
||||
|
||||
return 15;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the item has changed. This sets up the state for the new item.
|
||||
*/
|
||||
private void itemChanged() {
|
||||
if (item.getItem() instanceof PrintoutItem) {
|
||||
pageCount = PrintoutItem.getPageCount(item);
|
||||
page = Mth.clamp(page, 0, pageCount - 1);
|
||||
} else {
|
||||
pageCount = page = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current page, emitting a redstone pulse if needed.
|
||||
*
|
||||
* @param page The new page.
|
||||
*/
|
||||
private void setPage(int page) {
|
||||
if (this.page == page) return;
|
||||
|
||||
this.page = page;
|
||||
setChanged();
|
||||
if (getLevel() != null) LecternBlock.signalPageChange(getLevel(), getBlockPos(), getBlockState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(CompoundTag tag) {
|
||||
super.load(tag);
|
||||
|
||||
item = tag.contains(NBT_ITEM, Tag.TAG_COMPOUND) ? ItemStack.of(tag.getCompound(NBT_ITEM)) : ItemStack.EMPTY;
|
||||
page = tag.getInt(NBT_PAGE);
|
||||
itemChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveAdditional(CompoundTag tag) {
|
||||
super.saveAdditional(tag);
|
||||
|
||||
if (!item.isEmpty()) tag.put(NBT_ITEM, item.save(new CompoundTag()));
|
||||
if (item.getItem() instanceof PrintoutItem) tag.putInt(NBT_PAGE, page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
||||
return ClientboundBlockEntityDataPacket.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getUpdateTag() {
|
||||
var tag = super.getUpdateTag();
|
||||
tag.put(NBT_ITEM, item.save(new CompoundTag()));
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) {
|
||||
var item = getItem();
|
||||
if (item.getItem() instanceof PrintoutItem) {
|
||||
return new PrintoutMenu(
|
||||
containerId, new LecternContainer(), 0,
|
||||
p -> Container.stillValidBlockEntity(this, player, Container.DEFAULT_DISTANCE_LIMIT),
|
||||
new PrintoutContainerData()
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
return getItem().getDisplayName();
|
||||
}
|
||||
|
||||
/**
|
||||
* A read-only container storing the lectern's contents.
|
||||
*/
|
||||
private final class LecternContainer implements BasicContainer {
|
||||
private final List<ItemStack> itemView = new AbstractList<>() {
|
||||
@Override
|
||||
public ItemStack get(int index) {
|
||||
if (index != 0) throw new IndexOutOfBoundsException("Inventory only has one slot");
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return itemView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
// Should never happen, so a no-op.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return !isRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ContainerData} for a {@link PrintoutMenu}. This provides a read/write view of the current page.
|
||||
*/
|
||||
private final class PrintoutContainerData implements SingleContainerData {
|
||||
@Override
|
||||
public int get() {
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int index, int value) {
|
||||
if (index == 0) setPage(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.media;
|
||||
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.container.InvisibleSlot;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.*;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The menus for {@linkplain PrintoutItem printouts}.
|
||||
* <p>
|
||||
* This is a somewhat similar design to {@link LecternMenu}, which is used to read written books.
|
||||
* <p>
|
||||
* This holds a single slot (containing the printout), and a single data slot ({@linkplain #DATA_CURRENT_PAGE holding
|
||||
* the current page}). The page is set by the client by sending a {@linkplain #clickMenuButton(Player, int) button
|
||||
* press} with an index of {@link #PAGE_BUTTON_OFFSET} plus the current page.
|
||||
* <p>
|
||||
* The client-side screen uses {@linkplain ContainerListener container listeners} to subscribe to item and page changes.
|
||||
* However, listeners aren't fired on the client, so we copy {@link LecternMenu}'s hack and call
|
||||
* {@link #broadcastChanges()} whenever an item or data value are changed.
|
||||
*/
|
||||
public class PrintoutMenu extends AbstractContainerMenu {
|
||||
public static final int DATA_CURRENT_PAGE = 0;
|
||||
private static final int DATA_SIZE = 1;
|
||||
|
||||
public static final int PAGE_BUTTON_OFFSET = 100;
|
||||
|
||||
private final Predicate<Player> valid;
|
||||
private final ContainerData currentPage;
|
||||
|
||||
public PrintoutMenu(
|
||||
int containerId, Container container, int slotIdx, Predicate<Player> valid, ContainerData currentPage
|
||||
) {
|
||||
super(ModRegistry.Menus.PRINTOUT.get(), containerId);
|
||||
this.valid = valid;
|
||||
this.currentPage = currentPage;
|
||||
|
||||
addSlot(new InvisibleSlot(container, slotIdx) {
|
||||
@Override
|
||||
public void setChanged() {
|
||||
super.setChanged();
|
||||
slotsChanged(container); // Trigger listeners on the client.
|
||||
}
|
||||
});
|
||||
addDataSlots(currentPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link PrintoutMenu} for use a remote (client).
|
||||
*
|
||||
* @param containerId The current container id.
|
||||
* @return The constructed container.
|
||||
*/
|
||||
public static PrintoutMenu createRemote(int containerId) {
|
||||
return new PrintoutMenu(containerId, new SimpleContainer(1), 0, p -> true, new SimpleContainerData(DATA_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link PrintoutMenu} for the printout in the current player's hand.
|
||||
*
|
||||
* @param containerId The current container id.
|
||||
* @param player The player to open the container.
|
||||
* @param hand The hand containing the item.
|
||||
* @return The constructed container.
|
||||
*/
|
||||
public static PrintoutMenu createInHand(int containerId, Player player, InteractionHand hand) {
|
||||
var currentStack = player.getItemInHand(hand);
|
||||
var currentItem = currentStack.getItem();
|
||||
|
||||
var slot = switch (hand) {
|
||||
case MAIN_HAND -> player.getInventory().selected;
|
||||
case OFF_HAND -> Inventory.SLOT_OFFHAND;
|
||||
};
|
||||
return new PrintoutMenu(
|
||||
containerId, player.getInventory(), slot,
|
||||
p -> player.getItemInHand(hand).getItem() == currentItem, new SimpleContainerData(DATA_SIZE)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return valid.test(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clickMenuButton(Player player, int id) {
|
||||
if (id >= PAGE_BUTTON_OFFSET) {
|
||||
var page = Mth.clamp(id - PAGE_BUTTON_OFFSET, 0, PrintoutItem.getPageCount(getPrintout()) - 1);
|
||||
setData(DATA_CURRENT_PAGE, page);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.clickMenuButton(player, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current printout.
|
||||
*
|
||||
* @return The current printout.
|
||||
*/
|
||||
public ItemStack getPrintout() {
|
||||
return getSlot(0).getItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current page.
|
||||
*
|
||||
* @return The current page.
|
||||
*/
|
||||
public int getPage() {
|
||||
return currentPage.get(DATA_CURRENT_PAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(int id, int data) {
|
||||
super.setData(id, data);
|
||||
broadcastChanges(); // Trigger listeners on the client.
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,23 @@
|
||||
|
||||
package dan200.computercraft.shared.media.items;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.network.container.HeldItemContainerData;
|
||||
import dan200.computercraft.shared.lectern.CustomLecternBlock;
|
||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LecternBlock;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
@@ -50,12 +55,30 @@ public class PrintoutItem extends Item {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||
if (!world.isClientSide) {
|
||||
new HeldItemContainerData(hand)
|
||||
.open(player, new HeldItemMenu.Factory(ModRegistry.Menus.PRINTOUT.get(), player.getItemInHand(hand), hand));
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
var level = context.getLevel();
|
||||
var blockPos = context.getClickedPos();
|
||||
var blockState = level.getBlockState(blockPos);
|
||||
if (blockState.is(Blocks.LECTERN) && !blockState.getValue(LecternBlock.HAS_BOOK)) {
|
||||
// If we have an empty lectern, place our book into it.
|
||||
if (!level.isClientSide) {
|
||||
CustomLecternBlock.replaceLectern(level, blockPos, blockState, context.getItemInHand());
|
||||
}
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
} else {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), player.getItemInHand(hand));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||
var stack = player.getItemInHand(hand);
|
||||
if (!world.isClientSide) {
|
||||
var title = getTitle(stack);
|
||||
var displayTitle = Strings.isNullOrEmpty(title) ? stack.getDisplayName() : Component.literal(title);
|
||||
player.openMenu(new SimpleMenuProvider((id, playerInventory, p) -> PrintoutMenu.createInHand(id, p, hand), displayTitle));
|
||||
}
|
||||
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), stack);
|
||||
}
|
||||
|
||||
private ItemStack createFromTitleAndText(@Nullable String title, @Nullable String[] text, @Nullable String[] colours) {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.network.container;
|
||||
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
|
||||
/**
|
||||
* Opens a printout GUI based on the currently held item.
|
||||
*
|
||||
* @see HeldItemMenu
|
||||
* @see PrintoutItem
|
||||
*/
|
||||
public class HeldItemContainerData implements ContainerData {
|
||||
private final InteractionHand hand;
|
||||
|
||||
public HeldItemContainerData(InteractionHand hand) {
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
public HeldItemContainerData(FriendlyByteBuf buffer) {
|
||||
hand = buffer.readEnum(InteractionHand.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(FriendlyByteBuf buf) {
|
||||
buf.writeEnum(hand);
|
||||
}
|
||||
|
||||
public InteractionHand getHand() {
|
||||
return hand;
|
||||
}
|
||||
}
|
||||
@@ -96,11 +96,13 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
||||
|
||||
@Override
|
||||
public void clearRemoved() {
|
||||
super.clearRemoved();
|
||||
updateMedia();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoved() {
|
||||
super.setRemoved();
|
||||
if (recordPlaying) stopRecord();
|
||||
}
|
||||
|
||||
|
||||
@@ -70,10 +70,10 @@ public abstract class AbstractFluidMethods<T> implements GenericPeripheral {
|
||||
) throws LuaException;
|
||||
|
||||
/**
|
||||
* Move a fluid from a connected fluid container into this oneone.
|
||||
* Move a fluid from a connected fluid container into this one.
|
||||
* <p>
|
||||
* This allows you to pull fluid in the current fluid container from another container <em>on the same wired
|
||||
* network</em>. Both containers must attached to wired modems which are connected via a cable.
|
||||
* network</em>. Both containers must be attached to wired modems which are connected via a cable.
|
||||
*
|
||||
* @param to Container to move fluid to.
|
||||
* @param computer The current computer.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.peripheral.generic.methods;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package dan200.computercraft.shared.peripheral.monitor;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.api.peripheral.AttachedComputerSet;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
@@ -25,9 +26,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MonitorBlockEntity extends BlockEntity {
|
||||
@@ -53,7 +51,7 @@ public class MonitorBlockEntity extends BlockEntity {
|
||||
private @Nullable ClientMonitor clientMonitor;
|
||||
|
||||
private @Nullable MonitorPeripheral peripheral;
|
||||
private final Set<IComputerAccess> computers = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
private final AttachedComputerSet computers = new AttachedComputerSet();
|
||||
|
||||
private boolean needsUpdate = false;
|
||||
private boolean needsValidating = false;
|
||||
@@ -487,7 +485,7 @@ public class MonitorBlockEntity extends BlockEntity {
|
||||
var monitor = getLoadedMonitor(x, y).getMonitor();
|
||||
if (monitor == null) continue;
|
||||
|
||||
for (var computer : monitor.computers) fun.accept(computer);
|
||||
computers.forEach(fun);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import com.google.errorprone.annotations.concurrent.GuardedBy;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.lua.LuaTable;
|
||||
import dan200.computercraft.api.peripheral.AttachedComputerSet;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
@@ -33,7 +33,10 @@ import net.minecraft.world.item.RecordItem;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
|
||||
|
||||
@@ -60,7 +63,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
public static final int SAMPLE_RATE = 48000;
|
||||
|
||||
private final UUID source = UUID.randomUUID();
|
||||
private final @GuardedBy("computers") Set<IComputerAccess> computers = new HashSet<>();
|
||||
private final AttachedComputerSet computers = new AttachedComputerSet();
|
||||
|
||||
private long clock = 0;
|
||||
private long lastPositionTime;
|
||||
@@ -140,11 +143,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
syncedPosition(position);
|
||||
|
||||
// And notify computers that we have space for more audio.
|
||||
synchronized (computers) {
|
||||
for (var computer : computers) {
|
||||
computer.queueEvent("speaker_audio_empty", computer.getAttachmentName());
|
||||
}
|
||||
}
|
||||
computers.forEach(c -> c.queueEvent("speaker_audio_empty", c.getAttachmentName()));
|
||||
}
|
||||
|
||||
// Push position updates to any speakers which have ever played a note,
|
||||
@@ -353,16 +352,12 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
|
||||
@Override
|
||||
public void attach(IComputerAccess computer) {
|
||||
synchronized (computers) {
|
||||
computers.add(computer);
|
||||
}
|
||||
computers.add(computer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach(IComputerAccess computer) {
|
||||
synchronized (computers) {
|
||||
computers.remove(computer);
|
||||
}
|
||||
computers.remove(computer);
|
||||
}
|
||||
|
||||
static double clampVolume(double volume) {
|
||||
|
||||
@@ -30,6 +30,12 @@ import java.util.Objects;
|
||||
* print("On something else")
|
||||
* end
|
||||
* }</pre>
|
||||
* <p>
|
||||
* ## Recipes
|
||||
* <div class="recipe-container">
|
||||
* <mc-recipe recipe="computercraft:pocket_computer_normal"></mc-recipe>
|
||||
* <mc-recipe recipe="computercraft:pocket_computer_advanced"></mc-recipe>
|
||||
* </div>
|
||||
*
|
||||
* @cc.module pocket
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,6 @@ import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -39,6 +38,7 @@ import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.tooltip.TooltipComponent;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
@@ -47,6 +47,7 @@ import net.minecraft.world.level.Level;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PocketComputerItem extends Item implements IComputerItem, IMedia, IColouredItem {
|
||||
@@ -128,12 +129,16 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick(ItemStack stack, Level world, Entity entity, int slotNum, boolean selected) {
|
||||
public void inventoryTick(ItemStack stack, Level world, Entity entity, int compartmentSlot, boolean selected) {
|
||||
// This (in vanilla at least) is only called for players. Don't bother to handle other entities.
|
||||
if (world.isClientSide || !(entity instanceof ServerPlayer player)) return;
|
||||
|
||||
// Find the actual slot the item exists in, aborting if it can't be found.
|
||||
var slot = InventoryUtil.getInventorySlotFromCompartment(player, compartmentSlot, stack);
|
||||
if (slot < 0) return;
|
||||
|
||||
// If we're in the inventory, create a computer and keep it alive.
|
||||
var holder = new PocketHolder.PlayerHolder(player, slotNum);
|
||||
var holder = new PocketHolder.PlayerHolder(player, slot);
|
||||
var brain = getOrCreateBrain((ServerLevel) world, holder, stack);
|
||||
brain.computer().keepAlive();
|
||||
|
||||
@@ -192,6 +197,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TooltipComponent> getTooltipImage(ItemStack stack) {
|
||||
var id = getInstanceID(stack);
|
||||
return id == null ? Optional.empty() : Optional.of(new PocketTooltipComponent(id, family));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag flag) {
|
||||
@@ -346,8 +356,4 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
compound.put(NBT_UPGRADE_INFO, upgrade.data().copy());
|
||||
}
|
||||
}
|
||||
|
||||
public static CompoundTag getUpgradeInfo(ItemStack stack) {
|
||||
return stack.getOrCreateTagElement(NBT_UPGRADE_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.pocket.items;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import net.minecraft.world.inventory.tooltip.TooltipComponent;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A tooltip computer describing a pocket computer.
|
||||
* <p>
|
||||
* This has no behaviour on its own. When rendering, this is converted to an equivalent client-side component,
|
||||
* that renders the computer's terminal.
|
||||
*
|
||||
* @param id The instance ID of this pocket computer.
|
||||
* @param family The family of this pocket computer.
|
||||
* @see PocketComputerItem#getTooltipImage(ItemStack)
|
||||
* @see dan200.computercraft.client.pocket.PocketClientTooltipComponent
|
||||
*/
|
||||
public record PocketTooltipComponent(UUID id, ComputerFamily family) implements TooltipComponent {
|
||||
}
|
||||
@@ -47,18 +47,24 @@ import java.util.Optional;
|
||||
* <p>
|
||||
* ## Turtle upgrades
|
||||
* While a normal turtle can move about the world and place blocks, its functionality is limited. Thankfully, turtles
|
||||
* can be upgraded with *tools* and [peripherals][`peripheral`]. Turtles have two upgrade slots, one on the left and right
|
||||
* sides. Upgrades can be equipped by crafting a turtle with the upgrade, or calling the [`turtle.equipLeft`]/[`turtle.equipRight`]
|
||||
* functions.
|
||||
* can be upgraded with upgrades. Turtles have two upgrade slots, one on the left and right sides. Upgrades can be
|
||||
* equipped by crafting a turtle with the upgrade, or calling the [`turtle.equipLeft`]/[`turtle.equipRight`] functions.
|
||||
* <p>
|
||||
* Turtle tools allow you to break blocks ([`turtle.dig`]) and attack entities ([`turtle.attack`]). Some tools are more
|
||||
* suitable to a task than others. For instance, a diamond pickaxe can break every block, while a sword does more
|
||||
* damage. Other tools have more niche use-cases, for instance hoes can til dirt.
|
||||
* By default, any diamond tool may be used as an upgrade (though more may be added with [datapacks]). The diamond
|
||||
* pickaxe may be used to break blocks (with [`turtle.dig`]), while the sword can attack entities ([`turtle.attack`]).
|
||||
* Other tools have more niche use-cases, for instance hoes can til dirt.
|
||||
* <p>
|
||||
* Peripherals (such as the [wireless modem][`modem`] or [`speaker`]) can also be equipped as upgrades. These are then
|
||||
* accessible by accessing the `"left"` or `"right"` peripheral.
|
||||
* Some peripherals (namely [speakers][`speaker`] and Ender and Wireless [modems][`modem`]) can also be equipped as
|
||||
* upgrades. These are then accessible by accessing the `"left"` or `"right"` peripheral.
|
||||
* <p>
|
||||
* ## Recipes
|
||||
* <div class="recipe-container">
|
||||
* <mc-recipe recipe="computercraft:turtle_normal"></mc-recipe>
|
||||
* <mc-recipe recipe="computercraft:turtle_advanced"></mc-recipe>
|
||||
* </div>
|
||||
* <p>
|
||||
* [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics"
|
||||
* [datapacks]: https://datapacks.madefor.cc ""
|
||||
*
|
||||
* @cc.module turtle
|
||||
* @cc.since 1.3
|
||||
@@ -345,7 +351,7 @@ public class TurtleAPI implements ILuaAPI {
|
||||
* For instance, if a slot contains 13 blocks of dirt, it has room for another 51.
|
||||
*
|
||||
* @param slot The slot we wish to check. Defaults to the {@link #select selected slot}.
|
||||
* @return The space left in in this slot.
|
||||
* @return The space left in this slot.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
*/
|
||||
@LuaFunction
|
||||
|
||||
@@ -125,11 +125,15 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
|
||||
public final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||
if (state.is(newState.getBlock())) return;
|
||||
|
||||
if (!level.isClientSide && level.getBlockEntity(pos) instanceof TurtleBlockEntity turtle && !turtle.hasMoved()) {
|
||||
Containers.dropContents(level, pos, turtle);
|
||||
}
|
||||
// Most blocks drop items and then remove the BE. However, if a turtle is consuming drops right now, that can
|
||||
// lead to loops where it tries to insert an item back into the inventory. To prevent this, take a reference to
|
||||
// the turtle BE now, remove it, and then drop the items.
|
||||
var turtle = !level.isClientSide && level.getBlockEntity(pos) instanceof TurtleBlockEntity t && !t.hasMoved()
|
||||
? t : null;
|
||||
|
||||
super.onRemove(state, level, pos, newState, isMoving);
|
||||
|
||||
if (turtle != null) Containers.dropContents(level, pos, turtle);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import net.minecraft.util.FastColor;
|
||||
|
||||
/**
|
||||
* Utilities for working with 32-bit ARGB colours.
|
||||
*
|
||||
* @see FastColor.ARGB32
|
||||
*/
|
||||
public final class ARGB32 {
|
||||
private ARGB32() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alpha channel to be fully opaque.
|
||||
*
|
||||
* @param colour The colour to make opaque.
|
||||
* @return The fully-opaque colour
|
||||
*/
|
||||
public static int opaque(int colour) {
|
||||
return 0xFF000000 | colour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an ARGB32 colour to a {@linkplain FastColor.ABGR32 ABGR32} one.
|
||||
*
|
||||
* @param colour The colour to convert.
|
||||
* @return The converted colour.
|
||||
*/
|
||||
public static int toABGR32(int colour) {
|
||||
// Swap B and R components, converting ARGB32 to ABGR32.
|
||||
return colour & 0xFF00FF00 | (colour & 0xFF0000) >> 16 | (colour & 0xFF) << 16;
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,12 @@ import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
@@ -35,6 +38,28 @@ public final class InventoryUtil {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a slot inside a player's compartment to a slot in the full player's inventory.
|
||||
* <p>
|
||||
* {@link Inventory#tick()} passes in a slot to {@link Item#inventoryTick(ItemStack, Level, Entity, int, boolean)}.
|
||||
* However, this slot corresponds to the index within the current compartment (items, armour, offhand) and not
|
||||
* the actual slot.
|
||||
* <p>
|
||||
* This method searches the relevant compartments (inventory and offhand, skipping armour) for the stack, returning
|
||||
* its slot if found.
|
||||
*
|
||||
* @param player The player holding the item.
|
||||
* @param slot The slot inside the compartment.
|
||||
* @param stack The stack being ticked.
|
||||
* @return The inventory slot, or {@code -1} if the item could not be found in the inventory.
|
||||
*/
|
||||
public static int getInventorySlotFromCompartment(Player player, int slot, ItemStack stack) {
|
||||
if (stack.isEmpty()) throw new IllegalArgumentException("Cannot search for empty stack");
|
||||
if (player.getInventory().getItem(slot) == stack) return slot;
|
||||
if (player.getInventory().getItem(Inventory.SLOT_OFFHAND) == stack) return Inventory.SLOT_OFFHAND;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static @Nullable Container getEntityContainer(ServerLevel level, BlockPos pos, Direction side) {
|
||||
var vecStart = new Vec3(
|
||||
pos.getX() + 0.5 + 0.6 * side.getStepX(),
|
||||
|
||||
@@ -109,8 +109,9 @@ public final class TickScheduler {
|
||||
return State.UNLOADED;
|
||||
} else {
|
||||
// This should be impossible: either the block entity is at the above position, or it has been removed.
|
||||
if (level.getBlockEntity(pos) != blockEntity) {
|
||||
throw new IllegalStateException("Expected " + blockEntity + " at " + pos);
|
||||
var currentBlockEntity = level.getBlockEntity(pos);
|
||||
if (currentBlockEntity != blockEntity) {
|
||||
throw new IllegalStateException("Expected " + blockEntity + " at " + pos + ", got " + currentBlockEntity);
|
||||
}
|
||||
|
||||
// Otherwise schedule a tick and remove it from the queue.
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"commands.computercraft.track.start.stop": "Führe %s aus um die Aufzeichnung zu stoppen und die Ergebnisse anzusehen",
|
||||
"commands.computercraft.track.start.synopsis": "Startet die Aufzeichnung von Computern",
|
||||
"commands.computercraft.track.stop.action": "Klicke um die Aufzeichnung zu stoppen",
|
||||
"commands.computercraft.track.stop.desc": "Stopt die Aufzeichnung aller Computer Events und Laufzeiten",
|
||||
"commands.computercraft.track.stop.desc": "Stopt die Aufzeichnung aller Computer-Events und Laufzeiten",
|
||||
"commands.computercraft.track.stop.not_enabled": "Momentan werden keine Computer aufgezeichnet",
|
||||
"commands.computercraft.track.stop.synopsis": "Stoppt die Aufzeichnung aller Computer",
|
||||
"commands.computercraft.track.synopsis": "Zeichnet die Laufzeiten von Computern auf.",
|
||||
@@ -75,7 +75,7 @@
|
||||
"gui.computercraft.config.default_computer_settings": "Computer-Standardeinstellungen",
|
||||
"gui.computercraft.config.default_computer_settings.tooltip": "eine mit Komma separierte Liste an standardmäßige Systemeinstellungen für neuen Computern.\nBeispiel: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\nwürde jegliche Autovervollständigung deaktivieren.",
|
||||
"gui.computercraft.config.disabled_generic_methods": "Generische Methoden deaktiviert.",
|
||||
"gui.computercraft.config.disabled_generic_methods.tooltip": "Eine Liste an generischen Methoden oder Methodenquellen zum deaktivieren.\nGenerische Methoden sind Methoden die zu einem block/block entity hinzugefügt werden, insofern kein expliziter Peripheral Provider\ngefunden wurde. Mitbetroffen sind Inventarmethoden (d.h. inventory.getItemDetail,\ninventory.pushItems) und, wenn in Forge gespielt wird, die fluid_storage und energy_storage\nMethoden.\nMethoden in dieser Liste können entweder Gruppen von Methoden (wie computercraft:inventory)\noder einzelne Methoden (wie computercraft:inventory#pushItems) sein.\n",
|
||||
"gui.computercraft.config.disabled_generic_methods.tooltip": "Eine Liste an generischen Methoden oder Methodenquellen zum deaktivieren.\nGenerische Methoden sind Methoden die zu einem Block oder Blockentity hinzugefügt werden, insofern kein expliziter Peripheral Provider\ngefunden wurde. Mitbetroffen sind Inventarmethoden (d.h. inventory.getItemDetail,\ninventory.pushItems) und, wenn in Forge gespielt wird, die fluid_storage und energy_storage\nMethoden.\nMethoden in dieser Liste können entweder Gruppen von Methoden (wie computercraft:inventory)\noder einzelne Methoden (wie computercraft:inventory#pushItems) sein.\n",
|
||||
"gui.computercraft.config.execution": "Ausführung",
|
||||
"gui.computercraft.config.execution.computer_threads": "Computer Threads",
|
||||
"gui.computercraft.config.execution.computer_threads.tooltip": "Setzt die Anzahl an Hintergrundprozessen fest, auf denen Computer laufen können. Eine hohe Nummer heißt,\ndass mehrere Computer zur selben Zeit laufen können, jedoch aber auch ggf. mehr Verzögerungen verursachen. Bitte beachte, dass manche mods\nnicht mit einer Anzahl an Hintergrundprozessen laufen, die höher als 1 ist. Benutze also mit bedacht.\nBereich: > 1",
|
||||
@@ -133,15 +133,15 @@
|
||||
"upgrade.minecraft.diamond_sword.adjective": "Nahkampf",
|
||||
"argument.computercraft.computer.id": "Computer ID",
|
||||
"argument.computercraft.computer.instance": "einzigartige Instanz ID",
|
||||
"argument.computercraft.computer.label": "Computer name",
|
||||
"argument.computercraft.unknown_computer_family": "Unbekannte computer familie '%s'",
|
||||
"argument.computercraft.computer.label": "Computername",
|
||||
"argument.computercraft.unknown_computer_family": "Unbekannte Computerfamilie '%s'",
|
||||
"gui.computercraft.config.floppy_space_limit.tooltip": "Die maximale Dateisystem Größe von Disketten (in bytes).",
|
||||
"gui.computercraft.config.http.bandwidth": "Bandbreite",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload": "Globales upload limit",
|
||||
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Die maximale Geschwindigkeit aller Computer in bytes/s mit der Heruntergeladen werden kann.\nBereich: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_download": "Globales download limit",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload": "Globale Uploadbandbreite",
|
||||
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Die maximale Bandbreite aller Computer in bytes/s mit der heruntergeladen werden kann.\nBereich: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_download": "Globale Downloadbandbreite",
|
||||
"gui.computercraft.config.http.bandwidth.tooltip": "Limitiert die Bandbreite der Computer.",
|
||||
"argument.computercraft.computer.family": "Computer familie",
|
||||
"gui.computercraft.config.execution.max_main_global_time.tooltip": "Die maximale Zeit in millisekunden, in der Aufgaben ausgeführt werden.\nAnmerkung: Diese Zeit wird höchstwarscheinlich überschritten und dient nur als ungefähre Grenze.\nLimit: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "Die maximale Hochladungs Geschwindigkeit aller Computer in bytes/s.\nBereich: > 1"
|
||||
"argument.computercraft.computer.family": "Computerfamilie",
|
||||
"gui.computercraft.config.execution.max_main_global_time.tooltip": "Die maximale Zeit in Millisekunden, in der Aufgaben ausgeführt werden.\nAnmerkung: Diese Zeit wird höchstwarscheinlich überschritten und dient nur als ungefähre Grenze.\nLimit: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "Die maximale Bandbreite aller Computer in bytes/s mit der hochgeladen werden kann.\nBereich: > 1"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
{
|
||||
"argument.computercraft.argument_expected": "引数が期待される",
|
||||
"argument.computercraft.computer.distance": "エンティティまでの距離",
|
||||
"argument.computercraft.computer.family": "コンピューターファミリー",
|
||||
"argument.computercraft.computer.id": "コンピューターID",
|
||||
"argument.computercraft.computer.instance": "固有インスタンスID",
|
||||
"argument.computercraft.computer.label": "コンピューターラベル",
|
||||
"argument.computercraft.computer.many_matching": "'%s'に一致する複数のコンピューター (インスタンス %s)",
|
||||
"argument.computercraft.computer.no_matching": "'%s'に一致するコンピュータはありません",
|
||||
"argument.computercraft.computer.no_matching": "'%s'に一致するコンピューターはありません",
|
||||
"argument.computercraft.tracking_field.no_field": "'%s'は未知のフィールドです",
|
||||
"argument.computercraft.unknown_computer_family": "'%s'は未知のコンピューターファミリーです",
|
||||
"block.computercraft.cable": "ネットワークケーブル",
|
||||
"block.computercraft.computer_advanced": "高度なコンピューター",
|
||||
"block.computercraft.computer_command": "コマンドコンピューター",
|
||||
@@ -12,9 +18,9 @@
|
||||
"block.computercraft.monitor_normal": "モニター",
|
||||
"block.computercraft.printer": "プリンター",
|
||||
"block.computercraft.speaker": "スピーカー",
|
||||
"block.computercraft.turtle_advanced": "高度なタートル",
|
||||
"block.computercraft.turtle_advanced.upgraded": "高度な%sタートル",
|
||||
"block.computercraft.turtle_advanced.upgraded_twice": "高度な%s%sタートル",
|
||||
"block.computercraft.turtle_advanced": "アドバンスドタートル",
|
||||
"block.computercraft.turtle_advanced.upgraded": "アドバンスド%sタートル",
|
||||
"block.computercraft.turtle_advanced.upgraded_twice": "アドバンスド%s%sタートル",
|
||||
"block.computercraft.turtle_normal": "タートル",
|
||||
"block.computercraft.turtle_normal.upgraded": "%sタートル",
|
||||
"block.computercraft.turtle_normal.upgraded_twice": "%s%sタートル",
|
||||
@@ -24,11 +30,11 @@
|
||||
"block.computercraft.wireless_modem_normal": "無線モデム",
|
||||
"chat.computercraft.wired_modem.peripheral_connected": "周辺の\"%s\"のネットワークに接続されました",
|
||||
"chat.computercraft.wired_modem.peripheral_disconnected": "周辺の\"%s\"のネットワークから切断されました",
|
||||
"commands.computercraft.desc": "/computercraft コマンドは、コンピュータとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。",
|
||||
"commands.computercraft.dump.action": "このコンピュータの詳細を表示します",
|
||||
"commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.dump.open_path": "このコンピュータのファイルを表示します",
|
||||
"commands.computercraft.dump.synopsis": "コンピュータの状態を表示します。",
|
||||
"commands.computercraft.desc": "/computercraft コマンドは、コンピューターとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。",
|
||||
"commands.computercraft.dump.action": "このコンピューターの詳細を表示します",
|
||||
"commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピューターのインスタンスID (例えば 123), コンピューターID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.dump.open_path": "このコンピューターのファイルを表示します",
|
||||
"commands.computercraft.dump.synopsis": "コンピューターの状態を表示します。",
|
||||
"commands.computercraft.generic.additional_rows": "%d行を追加…",
|
||||
"commands.computercraft.generic.exception": "未処理の例外 (%s)",
|
||||
"commands.computercraft.generic.no": "N",
|
||||
@@ -39,65 +45,182 @@
|
||||
"commands.computercraft.help.no_children": "%s にサブコマンドはありません",
|
||||
"commands.computercraft.help.no_command": "%s というコマンドはありません",
|
||||
"commands.computercraft.help.synopsis": "特定のコマンドのヘルプを提供します",
|
||||
"commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピュータフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。",
|
||||
"commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピューターフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。",
|
||||
"commands.computercraft.queue.synopsis": "computer_command インベントをコマンドコンピューターに送信します",
|
||||
"commands.computercraft.shutdown.desc": "指定されたコンピュータ、指定されていない場合はすべてのコンピュータをシャットダウンします。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.shutdown.desc": "指定されたコンピューター、指定されていない場合はすべてのコンピューターをシャットダウンします。 コンピューターのインスタンスID (例えば 123), コンピューターID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.shutdown.done": "%s/%s コンピューターをシャットダウンしました",
|
||||
"commands.computercraft.shutdown.synopsis": "コンピュータをリモートでシャットダウンする。",
|
||||
"commands.computercraft.synopsis": "コンピュータを制御するためのさまざまなコマンド。",
|
||||
"commands.computercraft.shutdown.synopsis": "コンピューターをリモートでシャットダウンする。",
|
||||
"commands.computercraft.synopsis": "コンピューターを制御するためのさまざまなコマンド。",
|
||||
"commands.computercraft.tp.action": "このコンピューターへテレポートします",
|
||||
"commands.computercraft.tp.desc": "コンピュータの場所にテレポート.コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.tp.synopsis": "特定のコンピュータにテレポート。",
|
||||
"commands.computercraft.track.desc": "コンピュータの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。",
|
||||
"commands.computercraft.tp.desc": "コンピューターの場所にテレポート.コンピューターのインスタンスID(例えば 123)またはコンピューターID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.tp.synopsis": "特定のコンピューターにテレポート。",
|
||||
"commands.computercraft.track.desc": "コンピューターの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。",
|
||||
"commands.computercraft.track.dump.computer": "コンピューター",
|
||||
"commands.computercraft.track.dump.desc": "コンピュータの最新の追跡結果をダンプしてください。",
|
||||
"commands.computercraft.track.dump.desc": "コンピューターの最新の追跡結果をダンプしてください。",
|
||||
"commands.computercraft.track.dump.no_timings": "利用可能なタイミングはありません",
|
||||
"commands.computercraft.track.dump.synopsis": "最新の追跡結果をダンプしてください",
|
||||
"commands.computercraft.track.start.desc": "すべてのコンピュータの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。",
|
||||
"commands.computercraft.track.start.desc": "すべてのコンピューターの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。",
|
||||
"commands.computercraft.track.start.stop": "トラッキングを停止して結果を表示するには %s を実行してください",
|
||||
"commands.computercraft.track.start.synopsis": "すべてのコンピュータの追跡を開始します",
|
||||
"commands.computercraft.track.start.synopsis": "すべてのコンピューターの追跡を開始します。",
|
||||
"commands.computercraft.track.stop.action": "追跡を中止するためにクリックしてください",
|
||||
"commands.computercraft.track.stop.desc": "すべてのコンピュータのイベントと実行時間の追跡を停止します",
|
||||
"commands.computercraft.track.stop.not_enabled": "現在コンピュータを追跡していません",
|
||||
"commands.computercraft.track.stop.synopsis": "すべてのコンピュータの追跡を停止します",
|
||||
"commands.computercraft.track.synopsis": "コンピュータの実行時間を追跡します。",
|
||||
"commands.computercraft.turn_on.desc": "指定されているコンピュータを起動します。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.track.stop.desc": "すべてのコンピューターのイベントと実行時間の追跡を停止します",
|
||||
"commands.computercraft.track.stop.not_enabled": "現在コンピューターを追跡していません",
|
||||
"commands.computercraft.track.stop.synopsis": "すべてのコンピューターの追跡を停止します。",
|
||||
"commands.computercraft.track.synopsis": "コンピューターの実行時間を追跡します。",
|
||||
"commands.computercraft.turn_on.desc": "指定されているコンピューターを起動します。 コンピューターのインスタンスID (例えば 123), コンピューターID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
|
||||
"commands.computercraft.turn_on.done": "%s/%s コンピューターを起動しました",
|
||||
"commands.computercraft.turn_on.synopsis": "コンピューターをリモートで起動します。",
|
||||
"commands.computercraft.view.action": "このコンピュータを見ます",
|
||||
"commands.computercraft.view.desc": "コンピュータのターミナルを開き、コンピュータのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.view.action": "このコンピューターを見ます",
|
||||
"commands.computercraft.view.desc": "コンピューターのターミナルを開き、コンピューターのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピューターのインスタンスID(例えば 123)またはコンピューターID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.view.not_player": "非プレイヤー用のターミナルを開くことができません",
|
||||
"commands.computercraft.view.synopsis": "コンピュータのターミナルを表示します。",
|
||||
"gui.computercraft.pocket_computer_overlay": "ポケットコンピュータを開いています。 ESCを押して閉じます。",
|
||||
"gui.computercraft.tooltip.computer_id": "コンピュータID: %s",
|
||||
"commands.computercraft.view.synopsis": "コンピューターのターミナルを表示します。",
|
||||
"gui.computercraft.config.command_require_creative": "コマンドコンピューターはクリエイティブモードが必要です。",
|
||||
"gui.computercraft.config.command_require_creative.tooltip": "コマンドコンピューターと対話するためにはプレイヤーがクリエイティブモードかつOP権限保有者でなければなりません。\nこれはバニラのコマンドブロックのデフォルト挙動です。",
|
||||
"gui.computercraft.config.computer_space_limit": "コンピューターの限容制限(バイト)",
|
||||
"gui.computercraft.config.computer_space_limit.tooltip": "コンピューターとタートルのディスク容量制限、バイト単位。",
|
||||
"gui.computercraft.config.default_computer_settings": "デフォルトのコンピューター設定",
|
||||
"gui.computercraft.config.default_computer_settings.tooltip": "新しいコンピューターに設定するデフォルトのシステム設定のコンマ区切りのリスト。\n例: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\nは全ての自動補完を無効にします。",
|
||||
"gui.computercraft.config.disabled_generic_methods": "無効化するジェネリックメソッド",
|
||||
"gui.computercraft.config.disabled_generic_methods.tooltip": "無効にするジェネリックメソッドまたはメソッドソースのリスト。\nジェネリックメソッドは、明示的な周辺プロバイダがない場合にブロック/ブロックエンティティに追加されるメソッドです。\nこれには、インベントリメソッド (inventory.getItemDetail や inventory.pushItems) や、(Forgeであれば)fluid_storage や energy_storage メソッドが含まれます。\nこのリストに含まれるメソッドは、メソッド群全体 (computercraft:inventory) か、単一のメソッド (computercraft:inventory#pushItems) のどちらかになります。",
|
||||
"gui.computercraft.config.execution": "実行",
|
||||
"gui.computercraft.config.execution.computer_threads": "コンピューターのスレッド",
|
||||
"gui.computercraft.config.execution.computer_threads.tooltip": "コンピューターが実行できるスレッド数を設定する。\n数値が高いほどより多くのコンピューターが一度に実行できますが、ラグを誘発する可能性があります。\nスレッド数が1より大きいと動作しないMODもあるので注意してください\n範囲: > 1",
|
||||
"gui.computercraft.config.execution.max_main_computer_time": "サーバーティックのコンピューター時間上限",
|
||||
"gui.computercraft.config.execution.max_main_computer_time.tooltip": "コンピューターが1ティックで実行できる理想的な最大時間、ミリ秒単位。\nどれぐらい時間がかかるか不明であるため、上限を超える可能性があることに注意。\nこれは平均時間の上限を目的とする。\n範囲: > 1",
|
||||
"gui.computercraft.config.execution.max_main_global_time": "サーバーティックのグローバル回数上限",
|
||||
"gui.computercraft.config.execution.max_main_global_time.tooltip": "1ティックでタスクを実行できる最大時間、ミリ秒単位。\nどれぐらい時間がかかるか不明であるため、上限を超える可能性があることに注意。\nこれは平均回数の上限を時間とする。",
|
||||
"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_download": "グローバルダウンロード制限",
|
||||
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "1秒間にダウンロードできるバイト数。これはすべてのコンピュータで共有されます。(byte/s).\n範囲: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload": "グローバルアップロード制限",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "1秒間にアップロードできるバイト数。これはすべてのコンピュータで共有されます。(byte/s).\n範囲: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.tooltip": "コンピュータが使用する帯域幅を制限する。",
|
||||
"gui.computercraft.config.http.enabled": "HTTP APIを有効にする",
|
||||
"gui.computercraft.config.http.enabled.tooltip": "コンピュータの\"http\" APIを有効にする。これを無効にすると、多くのユーザーが依存している\"pastebin\"と\"wget\"プログラムも無効になる。\nこのオプションはオンのままにしておき、よりきめ細かい制御を行うために\"ルール\"の設定オプションを使用することを推奨する。",
|
||||
"gui.computercraft.config.http.max_requests": "最大同時リクエスト数",
|
||||
"gui.computercraft.config.http.max_requests.tooltip": "コンピューターが一度にできるhttpリクエストの数。追加のリクエストはキューに入れられ、実行中のリクエストが終了したときに送信されます。無制限の場合は0に設定します。\n範囲: > 0",
|
||||
"gui.computercraft.config.http.max_websockets": "最大同時ウェブソケット数",
|
||||
"gui.computercraft.config.http.max_websockets.tooltip": "コンピュータが一度に開くことのできるウェブソケットの数。\n範囲: > 1",
|
||||
"gui.computercraft.config.http.proxy": "プロキシ",
|
||||
"gui.computercraft.config.http.proxy.host": "ホスト名",
|
||||
"gui.computercraft.config.http.proxy.host.tooltip": "プロキシサーバーのホスト名またはIPアドレス。",
|
||||
"gui.computercraft.config.http.proxy.port": "ポート",
|
||||
"gui.computercraft.config.http.proxy.port.tooltip": "プロキシサーバーのポート。\n範囲: 1 ~ 65536",
|
||||
"gui.computercraft.config.http.proxy.tooltip": "HTTPとウェブソケットリクエストをプロキシサーバ経由でトンネリングする。\"use_proxy\"が\"true\"(デフォルトでは\"off\")に設定されている HTTPルールにのみ影響します。\nプロキシに認証が必要な場合は、\"computercraft-server.toml\"と同じディレクトリに\"myuser:mypassword\"のようにユーザー名とパスワードをコロンで区切って記述した\"computercraft-proxy.pw\"ファイルを作成します。\nSOCKS4プロキシでは、ユーザー名のみが必要です。",
|
||||
"gui.computercraft.config.http.proxy.type": "プロキシ種類",
|
||||
"gui.computercraft.config.http.proxy.type.tooltip": "使用するプロキシの種類。\n許可された値: HTTP, HTTPS, SOCKS4, SOCKS5",
|
||||
"gui.computercraft.config.http.rules": "ルールの許可/拒否",
|
||||
"gui.computercraft.config.http.rules.tooltip": "特定のドメインやIPに対する\"http\" APIの動作を制御するルールのリスト。それぞれのルールはホスト名とオプションのポートに対して対応し、 リクエストに対していくつかのプロパティを設定します。 ルールは順番に評価され、前のルールが後のルールを上書きします。\n\n有効なプロパティ:\n - \"host\" (必須): このルールが対応するドメインまたはIPアドレス。 これはドメイン名(\"pastebin.com\")、ワイルドカード(\"*.pastebin.com\")、あるいはCIDR表記(\"127.0.0.0/8\")となります。\n - \"port\" (オプション): 80や 443など、特定のポートに対するリクエストにのみマッチする。.\n\n - \"action\" (オプション): このリクエストを許可するか拒否するか。\n - \"max_download\" (オプション): このリクエストでコンピューターがダウンロードできる最大サイズ(バイト単位)。\n - \"max_upload\" (オプション): このリクエストでコンピューターがアップロードできる最大サイズ(バイト)。\n - \"max_websocket_message\" (オプション): コンピューターが1つのウェブソケット・パケットで送受信できる最大サイズ(バイト)。\n - \"use_proxy\" (オプション): HTTP/SOCKSプロキシが設定されている場合は、その使用を有効にする。",
|
||||
"gui.computercraft.config.http.tooltip": "HTTP APIの制御",
|
||||
"gui.computercraft.config.http.websocket_enabled": "ウェブソケットを有効にする",
|
||||
"gui.computercraft.config.http.websocket_enabled.tooltip": "httpウェブソケットの使用を有効にする。これには、\"http_enable\"オプションもtrueである必要があります。.",
|
||||
"gui.computercraft.config.log_computer_errors": "コンピュータのエラーを記録する",
|
||||
"gui.computercraft.config.log_computer_errors.tooltip": "周辺機器やその他のLuaオブジェクトが発生させた例外を記録します。これにより、MODの作者が問題をデバッグしやすくなりますが、バグを含んだメソッドを使用した場合、ログスパムが発生する可能性があります。",
|
||||
"gui.computercraft.config.maximum_open_files": "1台のコンピューターで開けるファイルの最大数",
|
||||
"gui.computercraft.config.maximum_open_files.tooltip": "コンピューターが同時に開くことができるファイルの数を設定します。無制限の場合は0に設定します。\n範囲: > 0",
|
||||
"gui.computercraft.config.monitor_distance": "モニター距離",
|
||||
"gui.computercraft.config.monitor_distance.tooltip": "モニターがレンダリングする最大距離。デフォルトは標準的なタイルエンティティの制限値ですが、より大きなモニタを構築したい場合は拡張することができます。\n範囲: 16 ~ 1024",
|
||||
"gui.computercraft.config.monitor_renderer": "モニターレンダラー",
|
||||
"gui.computercraft.config.monitor_renderer.tooltip": "モニターに使用するレンダラー。一般的に、この値は\"best\"に保つたれるべきです。 - モニターにパフォーマンス上の問題がある場合は、別のレンダラーを試してみるとよいでしょう。\n許可された値: BEST, TBO, VBO",
|
||||
"gui.computercraft.config.peripheral": "周辺機器",
|
||||
"gui.computercraft.config.peripheral.command_block_enabled": "コマンドブロック周辺機器を有効にする",
|
||||
"gui.computercraft.config.peripheral.command_block_enabled.tooltip": "コマンドブロック周辺機器サポートを有効にする",
|
||||
"gui.computercraft.config.peripheral.max_notes_per_tick": "コンピューターが一度に演奏できる最大音符数",
|
||||
"gui.computercraft.config.peripheral.max_notes_per_tick.tooltip": "スピーカーが一度に演奏できる最大音符数。\n範囲: > 1",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range": "モデム範囲(高高度)",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range.tooltip": "晴天時の最大高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm": "モデム範囲(高高度、悪天候)",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm.tooltip": "悪天候の最大高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_range": "モデム範囲(デフォルト)",
|
||||
"gui.computercraft.config.peripheral.modem_range.tooltip": "晴天時の低高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_range_during_storm": "モデム範囲(悪天候)",
|
||||
"gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "荒天時の低高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.monitor_bandwidth": "モニター帯域幅",
|
||||
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "1ティックあたりのモニターデータ送信量の上限。注:\n - 帯域幅は圧縮前に測定されるため、クライアントに送信されるデータはより小さくなります。\n - これは、パケットを送信するプレーヤーの数を無視する。1人のプレーヤーのモニターを更新することは、20人に送信するのと同じ帯域幅の制限を消費する。\n - フルサイズのモニターは~25kbのデータを送信する。そのため、デフォルト(1MB)では、1回のティックで~40個のモニターを更新することができる。\n 無効にするには 0 を設定する。\n範囲: > 0",
|
||||
"gui.computercraft.config.peripheral.tooltip": "周辺機器に関する各種オプション。",
|
||||
"gui.computercraft.config.term_sizes": "ターミナルサイズ",
|
||||
"gui.computercraft.config.term_sizes.computer": "コンピューター",
|
||||
"gui.computercraft.config.term_sizes.computer.height": "ターミナルの高さ",
|
||||
"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": "モニターの最大高さ",
|
||||
"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.monitor.width.tooltip": "範囲: 1 ~ 32",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer": "ポケットコンピューター",
|
||||
"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.tooltip": "ポケットコンピュータのターミナルサイズ。",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.width": "ターミナルの幅",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "範囲: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.tooltip": "各種コンピュータのターミナルサイズを設定します。ターミナルサイズが大きくなるとより多くの帯域幅を必要としますので、注意して使用してください。",
|
||||
"gui.computercraft.config.turtle": "タートル",
|
||||
"gui.computercraft.config.turtle.advanced_fuel_limit": "アドバンスドタートルの燃料制限",
|
||||
"gui.computercraft.config.turtle.advanced_fuel_limit.tooltip": "アドバンスドタートルの燃料制限。\n範囲: > 0",
|
||||
"gui.computercraft.config.turtle.can_push": "タートルのによるエンティティ押出し",
|
||||
"gui.computercraft.config.turtle.can_push.tooltip": "trueに設定すると、タートルは空間がある場合停止する代わりにエンティティを押し出す。",
|
||||
"gui.computercraft.config.turtle.need_fuel": "燃料を有効にする",
|
||||
"gui.computercraft.config.turtle.need_fuel.tooltip": "タートルズが移動に燃料を必要とするかどうかを設定する。",
|
||||
"gui.computercraft.config.turtle.normal_fuel_limit": "タートルの燃料制限",
|
||||
"gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "タートルの燃料制限。\n範囲: > 0",
|
||||
"gui.computercraft.config.turtle.tooltip": "タートルに関する様々なオプション。",
|
||||
"gui.computercraft.config.upload_max_size": "ファイルアップロードサイズ制限(バイト)",
|
||||
"gui.computercraft.config.upload_max_size.tooltip": "ファイルアップロードサイズの上限をバイト数で指定します。1KiBから16MiBの範囲でなければなりません。 \nアップロードは1ティックで処理されることを覚えておいてください - 大きなファイルやネットワークパフォーマンスの低下はネットワーキングスレッドを停止させる可能性があります。\nディスク容量にも注意してください!\n範囲: 1024 ~ 16777216",
|
||||
"gui.computercraft.config.upload_nag_delay": "アップロード催促遅延",
|
||||
"gui.computercraft.config.upload_nag_delay.tooltip": "未処理の入力について通知するまでの遅延時間(秒)。無効にするには0を設定する。\n範囲: 0 ~ 60",
|
||||
"gui.computercraft.pocket_computer_overlay": "ポケットコンピューターを開いています。 ESCを押して閉じます。",
|
||||
"gui.computercraft.terminal": "コンピューターターミナル",
|
||||
"gui.computercraft.tooltip.computer_id": "コンピューターID: %s",
|
||||
"gui.computercraft.tooltip.copy": "クリップボードにコピー",
|
||||
"gui.computercraft.tooltip.disk_id": "ディスクID: %s",
|
||||
"gui.computercraft.tooltip.terminate": "現在実行中のコードを停止する",
|
||||
"gui.computercraft.tooltip.terminate.key": "Ctrl+T 長押し",
|
||||
"gui.computercraft.tooltip.turn_off": "このコンピュータをオフにする",
|
||||
"gui.computercraft.tooltip.turn_off": "このコンピューターをオフにする",
|
||||
"gui.computercraft.tooltip.turn_off.key": "Ctrl+S 長押し",
|
||||
"gui.computercraft.tooltip.turn_on": "このコンピュータをオンにする",
|
||||
"gui.computercraft.tooltip.turn_on": "このコンピューターをオンにする",
|
||||
"gui.computercraft.upload.failed": "アップロードに失敗しました",
|
||||
"gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピュータを起動する必要があります。",
|
||||
"gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピューターを起動する必要があります。",
|
||||
"gui.computercraft.upload.failed.corrupted": "アップロード時にファイルが破損しました。 もう一度やり直してください。",
|
||||
"gui.computercraft.upload.failed.generic": "ファイルのアップロードに失敗しました(%s)",
|
||||
"gui.computercraft.upload.failed.name_too_long": "ファイル名が長すぎてアップロードできません。",
|
||||
"gui.computercraft.upload.failed.too_many_files": "多くのファイルをアップロードできません。",
|
||||
"gui.computercraft.upload.failed.too_much": "アップロードするにはファイルが大きスギます。",
|
||||
"gui.computercraft.upload.no_response": "ファイルの転送",
|
||||
"gui.computercraft.upload.no_response.msg": "コンピュータが転送されたファイルを使用していません。プログラム %s を実行して再試行する必要があります。",
|
||||
"item.computercraft.disk": "フロッピーディスク",
|
||||
"item.computercraft.pocket_computer_advanced": "高度なポケットコンピュータ",
|
||||
"item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピュータ",
|
||||
"item.computercraft.pocket_computer_normal": "ポケットコンピュータ",
|
||||
"item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピュータ",
|
||||
"item.computercraft.pocket_computer_advanced": "高度なポケットコンピューター",
|
||||
"item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピューター",
|
||||
"item.computercraft.pocket_computer_normal": "ポケットコンピューター",
|
||||
"item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピューター",
|
||||
"item.computercraft.printed_book": "印刷された本",
|
||||
"item.computercraft.printed_page": "印刷された紙",
|
||||
"item.computercraft.printed_pages": "印刷された紙束",
|
||||
"item.computercraft.treasure_disk": "フロッピーディスク",
|
||||
"itemGroup.computercraft": "ComputerCraft",
|
||||
"tag.item.computercraft.computer": "コンピューター",
|
||||
"tag.item.computercraft.monitor": "モニター",
|
||||
"tag.item.computercraft.turtle": "タートル",
|
||||
"tag.item.computercraft.wired_modem": "有線モデム",
|
||||
"tracking_field.computercraft.avg": "%s (平均)",
|
||||
"tracking_field.computercraft.computer_tasks.name": "タスク",
|
||||
"tracking_field.computercraft.count": "%s (回)",
|
||||
"tracking_field.computercraft.fs.name": "ファイルシステム演算",
|
||||
"tracking_field.computercraft.http_download.name": "HTTPダウンロード",
|
||||
"tracking_field.computercraft.http_requests.name": "HTTPリクエスト",
|
||||
"tracking_field.computercraft.http_upload.name": "HTTPアップロード",
|
||||
"tracking_field.computercraft.java_allocation.name": "Java割当",
|
||||
"tracking_field.computercraft.max": "%s (最大)",
|
||||
"tracking_field.computercraft.peripheral.name": "実行呼び出し",
|
||||
"tracking_field.computercraft.server_tasks.name": "サーバータスク",
|
||||
"tracking_field.computercraft.turtle_ops.name": "タートル操作",
|
||||
"tracking_field.computercraft.websocket_incoming.name": "Websocket 受信",
|
||||
"tracking_field.computercraft.websocket_outgoing.name": "Websocket 送信",
|
||||
"upgrade.computercraft.speaker.adjective": "騒音",
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
{
|
||||
"argument.computercraft.argument_expected": "Beklenen Argüman",
|
||||
"argument.computercraft.computer.distance": "Varlığa olan mesafe",
|
||||
"argument.computercraft.computer.family": "Bilgisayar ailesi",
|
||||
"argument.computercraft.computer.id": "Bilgisayar Kimliği",
|
||||
"argument.computercraft.computer.instance": "Benzersiz örnek kimliği",
|
||||
"argument.computercraft.computer.label": "Bilgisayar etiketi",
|
||||
"argument.computercraft.computer.many_matching": "Birden fazla bilgisayar eşleşiyor '%s' (örnekler %s)",
|
||||
"argument.computercraft.computer.no_matching": "Eşleşen bilgisayar yok '%s'",
|
||||
"argument.computercraft.tracking_field.no_field": "Bilinmeyen alan '%s'",
|
||||
"argument.computercraft.unknown_computer_family": "Bilinmeyen bilgisayar ailesi '%s'",
|
||||
"block.computercraft.cable": "Ağ Kablosu",
|
||||
"block.computercraft.computer_advanced": "Gelişmiş Bilgisayar",
|
||||
"block.computercraft.computer_command": "Komut Bilgisayarı",
|
||||
"block.computercraft.computer_normal": "Bilgisayar",
|
||||
"block.computercraft.disk_drive": "Disk Sürücüsü",
|
||||
"block.computercraft.monitor_advanced": "Gelişmiş Monitör",
|
||||
"block.computercraft.monitor_normal": "Monitör",
|
||||
"block.computercraft.printer": "Yazıcı",
|
||||
"block.computercraft.speaker": "Hoparlör",
|
||||
"block.computercraft.turtle_advanced": "Gelişmiş Kaplumbağa",
|
||||
"block.computercraft.turtle_advanced.upgraded": "Gelişmiş %s Kaplumbağa",
|
||||
"block.computercraft.turtle_advanced.upgraded_twice": "Gelişmiş %s %s Kaplumbağa",
|
||||
"block.computercraft.turtle_normal": "Kaplumbağa",
|
||||
"block.computercraft.turtle_normal.upgraded": "%s Kaplumbağa",
|
||||
"block.computercraft.turtle_normal.upgraded_twice": "%s %s Kaplumbağa",
|
||||
"block.computercraft.wired_modem": "Kablolu Modem",
|
||||
"block.computercraft.wired_modem_full": "Kablolu Modem",
|
||||
"block.computercraft.wireless_modem_advanced": "Ender Modem",
|
||||
"block.computercraft.wireless_modem_normal": "Kablosuz Modem",
|
||||
"chat.computercraft.wired_modem.peripheral_connected": "Çevre birimi \"%s\" ağa bağlandı",
|
||||
"chat.computercraft.wired_modem.peripheral_disconnected": "Çevre birimi \"%s\" ağ ile bağlantısı kesildi",
|
||||
"commands.computercraft.desc": "/computercraft komutu, bilgisayarları kontrol etmek ve bilgisayarlarla etkileşimde bulunmak için çeşitli hata ayıklama ve yönetici araçları sağlar.",
|
||||
"commands.computercraft.dump.action": "Bu bilgisayar hakkında daha fazla bilgi görüntüle",
|
||||
"commands.computercraft.dump.desc": "Tüm bilgisayarların durumunu veya bir bilgisayarla ilgili belirli bilgileri görüntüleme. Bilgisayarın örnek kimliğini (örn. 123), bilgisayar kimliğini (örn. #123) ya da etiketini (örn. \"@Bilgisayar\") belirtebilirsiniz.",
|
||||
"commands.computercraft.dump.open_path": "Bu bilgisayarın dosyalarını görüntüle",
|
||||
"commands.computercraft.dump.synopsis": "Bilgisayarların durumunu görüntüle.",
|
||||
"commands.computercraft.generic.additional_rows": "%d ek satır…",
|
||||
"commands.computercraft.generic.exception": "Beklenmeyen durum (%s)",
|
||||
"commands.computercraft.generic.no": "H",
|
||||
"commands.computercraft.generic.no_position": "<no poz>",
|
||||
"commands.computercraft.generic.position": "%s, %s, %s",
|
||||
"commands.computercraft.generic.yes": "E",
|
||||
"commands.computercraft.help.desc": "Bu yardım mesajını görüntüler",
|
||||
"commands.computercraft.help.no_children": "%s alt komutları yok",
|
||||
"commands.computercraft.help.no_command": "Böyle bir komut yok '%s'",
|
||||
"commands.computercraft.help.synopsis": "Belirli bir komut için yardım sağlar",
|
||||
"commands.computercraft.queue.desc": "Komut bilgisayarına bir computer_command olayı gönder, ek argümanları geçerek. Bu çoğunlukla harita yapımcıları için tasarlanmıştır ve /trigger'ın daha bilgisayar dostu bir versiyonu olarak işlev görür. Herhangi bir oyuncu komutu çalıştırabilir, bu da büyük olasılıkla bir metin bileşeninin tıklama olayı aracılığıyla yapılır.",
|
||||
"commands.computercraft.queue.synopsis": "Bir komut bilgisayarına computer_command olayı gönder",
|
||||
"commands.computercraft.shutdown.desc": "Listelenen bilgisayarları veya hiçbiri belirtilmemişse tümünü kapatma. Bilgisayarın örnek kimliğini (örn. 123), bilgisayar kimliğini (örn. #123) ya da etiketini (örn. \"@Bilgisayar\") belirtebilirsiniz.",
|
||||
"commands.computercraft.shutdown.done": "%s/%s bilgisayarı kapat",
|
||||
"commands.computercraft.shutdown.synopsis": "Bilgisayarları uzaktan kapat.",
|
||||
"commands.computercraft.synopsis": "Bilgisayarları kontrol etmek için çeşitli komutlar.",
|
||||
"commands.computercraft.tp.action": "Bu bilgisayara ışınlan",
|
||||
"commands.computercraft.tp.desc": "Bir bilgisayarın bulunduğu yere ışınlan. Bilgisayarın örnek kimliğini (örn. 123), bilgisayar kimliğini (örn. #123) belirtebilirsiniz.",
|
||||
"commands.computercraft.tp.synopsis": "Belirli bir bilgisayara ışınlan.",
|
||||
"commands.computercraft.track.desc": "Bilgisayarların ne kadar süre çalıştığını ve kaç olay işlediğini takip et. Bu, /forge track'e benzer bir şekilde bilgi sunar ve gecikmeyi teşhis etmek için yararlı olabilir.",
|
||||
"commands.computercraft.track.dump.computer": "Bilgisayar",
|
||||
"commands.computercraft.track.dump.desc": "Bilgisayar takibinin en son sonuçlarını boşalt.",
|
||||
"commands.computercraft.track.dump.no_timings": "Zamanlama mevcut değil",
|
||||
"commands.computercraft.track.dump.synopsis": "En son izleme sonuçlarını boşalt",
|
||||
"commands.computercraft.track.start.desc": "Tüm bilgisayarların yürütme sürelerini ve olay sayılarını izlemeye başla. Bu, önceki çalıştırmaların sonuçlarını atacaktır.",
|
||||
"commands.computercraft.track.start.stop": "İzlemeyi durdurmak ve sonuçları görüntülemek için %s çalıştır",
|
||||
"commands.computercraft.track.start.synopsis": "Tüm bilgisayarları izlemeyi başlat",
|
||||
"commands.computercraft.track.stop.action": "İzlemeyi durdurmak için tıkla",
|
||||
"commands.computercraft.track.stop.desc": "Tüm bilgisayarların olaylarını ve yürütme sürelerini izlemeyi durdur",
|
||||
"commands.computercraft.track.stop.not_enabled": "Şu anda bilgisayarlar izlenmiyor",
|
||||
"commands.computercraft.track.stop.synopsis": "Tüm bilgisayarları izlemeyi durdur",
|
||||
"commands.computercraft.track.synopsis": "Bilgisayarların yürütme sürelerini takip et.",
|
||||
"commands.computercraft.turn_on.desc": "Listelenen bilgisayarları aç. Bilgisayarın örnek kimliğini (örn. 123), bilgisayar kimliğini (örn. #123) ya da etiketini (örn. \"@Bilgisayar\") belirtebilirsiniz.",
|
||||
"commands.computercraft.turn_on.done": "%s/%s bilgisayar açıldı",
|
||||
"commands.computercraft.turn_on.synopsis": "Bilgisayarları uzaktan aç.",
|
||||
"commands.computercraft.view.action": "Bu bilgisayarı görüntüle",
|
||||
"commands.computercraft.view.desc": "Bir bilgisayarın terminalinin açılması, bir bilgisayarın uzaktan kontrolüne izin verir. Bu, kaplumbağanın envanterlerine erişim sağlamaz. Bilgisayarın örnek kimliğini (örn. 123), bilgisayar kimliğini (örn. #123) belirtebilirsiniz.",
|
||||
"commands.computercraft.view.not_player": "Oyuncu olmayanlar için terminal açılamıyor",
|
||||
"commands.computercraft.view.synopsis": "Bir bilgisayarın terminalini görüntüle.",
|
||||
"gui.computercraft.config.command_require_creative": "Komut bilgisayarları yaratıcı modu gerektirir",
|
||||
"gui.computercraft.config.command_require_creative.tooltip": "Oyuncuların komut bilgisayarlarıyla etkileşime girebilmesi için yaratıcı modda olmalarını\nve op'lu olmalarını gerektirir. Bu, vanilla'nın Komut blokları için varsayılan davranıştır.",
|
||||
"gui.computercraft.config.computer_space_limit": "Bilgisayar alan sınırı (bayt)",
|
||||
"gui.computercraft.config.computer_space_limit.tooltip": "Bilgisayarlar ve kaplumbağalar için bayt cinsinden disk alanı sınırı.",
|
||||
"gui.computercraft.config.default_computer_settings": "Varsayılan Bilgisayar ayarları",
|
||||
"gui.computercraft.config.default_computer_settings.tooltip": "Yeni bilgisayarlarda ayarlanacak varsayılan sistem ayarlarının virgülle\nayrılmış bir listesi.\nÖrnek: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\ntüm otomatik tamamlamayı devre dışı bırakır.",
|
||||
"gui.computercraft.config.disabled_generic_methods": "Devre dışı bırakılmış genel yöntemler",
|
||||
"gui.computercraft.config.disabled_generic_methods.tooltip": "Devre dışı bırakılacak genel yöntemlerin veya yöntem kaynaklarının bir listesi.\nGenel yöntemler açık bir çevresel sağlayıcı olmadığında bir blok/blok varlığına\neklenen yöntemlerdir. Bu, envanter yöntemlerini (yani inventory.getItemDetail,\ninventory.pushItems), ve (Forge'da ise), fluid_storage ve energy_storage\nyöntemlerini içerir.\nBu listedeki yöntemler bir grup yöntem (computercraft:inventory)\nya da tek bir yöntem (computercraft:inventory#pushItems) olabilir.\n",
|
||||
"gui.computercraft.config.execution": "Yürüt",
|
||||
"gui.computercraft.config.execution.computer_threads": "Bilgisayar iş parçacıkları",
|
||||
"gui.computercraft.config.execution.computer_threads.tooltip": "Bilgisayarların çalışabileceği iş parçacığı sayısını ayarla. Daha yüksek bir\nsayı, aynı anda daha fazla bilgisayarın çalışabileceği anlamına gelir, ancak\ngecikmeye neden olabilir. Lütfen bazı modların 1'den yüksek iş parçacığı sayısı\nile çalışmayabileceğini unutmayın. Dikkatli kullanın.\nAralık: > 1",
|
||||
"gui.computercraft.config.execution.max_main_computer_time": "Sunucu tik bilgisayar zaman sınırı",
|
||||
"gui.computercraft.config.execution.max_main_computer_time.tooltip": "Milisaniye cinsinden, bilgisayarın bir tik içinde yürütebileceği ideal maksimum süre.\nUnutmayın, ne kadar süreceğini söylemenin bir yolu olmadığı için büyük olasılıkla\nbu sınırı aşacağız - bu, ortalama sürenin üst sınırı olmayı\namaçlamaktadır.\nAralık: > 1",
|
||||
"gui.computercraft.config.execution.max_main_global_time": "Sunucu tik genel zaman sınırı",
|
||||
"gui.computercraft.config.execution.max_main_global_time.tooltip": "Milisaniye cinsinden, tek bir tikte görevleri yürütmek için harcanabilecek\nmaksimum süre.\nUnutmayın, ne kadar süreceğini söylemenin bir yolu olmadığı için büyük\nolasılıkla bu sınırı aşacağız - bu, ortalama sürenin üst sınırı olmayı\namaçlamaktadır.\nAralık: > 1",
|
||||
"gui.computercraft.config.execution.tooltip": "Bilgisayarların yürütme davranışını kontrol eder. Bu, büyük ölçüde sunuculara\nince ayar yapmak için tasarlanmıştır ve genellikle dokunulması gerekmez.",
|
||||
"gui.computercraft.config.floppy_space_limit": "Disket alan sınırı (bayt)",
|
||||
"gui.computercraft.config.floppy_space_limit.tooltip": "Disketler için bayt cinsinden disk alanı sınırı.",
|
||||
"gui.computercraft.config.http": "HTTP",
|
||||
"gui.computercraft.config.http.bandwidth": "Bant Genişliği",
|
||||
"gui.computercraft.config.http.bandwidth.global_download": "Genel indirme sınırı",
|
||||
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Bir saniye içinde indirilebilecek bayt sayısı. Bu tüm bilgisayarlar arasında paylaşılır. (bayt/s).\nAralık: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload": "Genel yükleme sınırı",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "Bir saniye içinde yüklenebilecek bayt sayısı. Bu tüm bilgisayarlar arasında paylaşılır. (bayt/s).\nAralık: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.tooltip": "Bilgisayarlar tarafından kullanılan bant genişliğini sınırlar.",
|
||||
"gui.computercraft.config.http.enabled": "HTTP API'sini etkinleştir",
|
||||
"gui.computercraft.config.http.enabled.tooltip": "Bilgisayarlarda \"http\" API'sini etkinleştir. Bunu devre dışı bırakmak, birçok kullanıcının güvendiği \"pastebin\" ve\n\"wget\" programlarını da devre dışı bırakır. Bunu açık bırakmanız ve daha ayrıntılı denetim uygulamak için\n\"rules\" yapılandırma seçeneğini kullanmanız önerilir.",
|
||||
"gui.computercraft.config.http.max_requests": "Maksimum eşzamanlı istekler",
|
||||
"gui.computercraft.config.http.max_requests.tooltip": "Bir bilgisayarın tek seferde yapabileceği http isteği sayısı. Ek istekler\nkuyruğa alınacak ve çalışan istekler bittiğinde gönderilecektir. Sınırsız için\n0 olarak ayarla.\nAralık: > 0",
|
||||
"gui.computercraft.config.http.max_websockets": "Maksimum eşzamanlı websocketleri",
|
||||
"gui.computercraft.config.http.max_websockets.tooltip": "Bir bilgisayarın tek seferde açık tutabileceği websocket sayısı.\nAralık: > 1",
|
||||
"gui.computercraft.config.http.proxy": "Proxy",
|
||||
"gui.computercraft.config.http.proxy.host": "Ana bilgisayar adı",
|
||||
"gui.computercraft.config.http.proxy.host.tooltip": "Proxy sunucusunun ana bilgisayar adı veya IP adresi.",
|
||||
"gui.computercraft.config.http.proxy.port": "Port",
|
||||
"gui.computercraft.config.http.proxy.port.tooltip": "Proxy sunucusunun portu.\nAralık: 1 ~ 65536",
|
||||
"gui.computercraft.config.http.proxy.tooltip": "HTTP ve websocket isteklerini bir proxy sunucusu üzerinden aktarır. Yalnızca \n\"use_proxy\" olan HTTP kurallarını etkiler, true olarak ayarla (varsayılan\nolarak kapalıdır).\nProxy için kimlik doğrulaması gerekiyorsa, \"computercraft-server.toml\" ile\naynı dizinde, iki nokta üst üste ile ayrılmış kullanıcı adı ve parolayı içeren\nbir \"computercraft-proxy.pw\" dosyası oluştur. örn. \"myuser:mypassword\".\nSOCKS4 proxy'leri için yalnızca kullanıcı adı gereklidir.",
|
||||
"gui.computercraft.config.http.proxy.type": "Proxy türü",
|
||||
"gui.computercraft.config.http.proxy.type.tooltip": "Kullanılacak proxy türü.\nİzin Verilen Değerler: HTTP, HTTPS, SOCKS4, SOCKS5",
|
||||
"gui.computercraft.config.http.rules": "Kurallara izin ver/reddet",
|
||||
"gui.computercraft.config.http.rules.tooltip": "Belirli alan adları veya IP'ler için \"http\" API davranışını denetleyen\nkurallar listesi. Her kural bir ana bilgisayar adı ve isteğe bağlı bir port ile\neşleşir ve ardından istek için çeşitli özellikler ayarlar. Kurallar sırayla\ndeğerlendirilir, yani önceki kurallar sonrakileri geçersiz kılar.\n\nGeçerli özellikler:\n - \"host\" (gerekli): Bu kuralın eşleştiği alan adı veya IP adresi. Bu bir alan adı olabilir\n (\"pastebin.com\"), wildcard (\"*.pastebin.com\") ya da CIDR gösterimi (\"127.0.0.0/8\").\n - \"port\" (isteğe bağlı): Belirli bir porta yönelik istekleri eşleştir\n80 veya 443 gibi\n\n - \"action\" (isteğe bağlı): Bu isteğe izin verilip verilmeyeceği veya\nreddedilip reddedilmeyeceği.\n - \"max_download\" (isteğe bağlı): Bir bilgisayarın bu istekte indirebileceği\nmaksimum boyut (bayt cinsinden).\n - \"max_upload\" (isteğe bağlı): Bir bilgisayarın bu istekte yükleyebileceği\nmaksimum boyut (bayt cinsinden).\n - \"max_websocket_message\" (isteğe bağlı): Bir bilgisayarın bir websocket\npaketinde gönderebileceği veya alabileceği maksimum boyut (bayt cinsinden).\n - \"use_proxy\" (isteğe bağlı): Yapılandırılmışsa HTTP/SOCKS proxy'sinin kullanımını etkinleştir.",
|
||||
"gui.computercraft.config.http.tooltip": "HTTP API'sini kontrol eder",
|
||||
"gui.computercraft.config.http.websocket_enabled": "Websocket'leri etkinleştir",
|
||||
"gui.computercraft.config.http.websocket_enabled.tooltip": "Http websocket kullanımını etkinleştir. Bunun için \"http_enable\" seçeneğinin de true olması gerekir.",
|
||||
"gui.computercraft.config.log_computer_errors": "Bilgisayar hatalarını günlüğe kaydet",
|
||||
"gui.computercraft.config.log_computer_errors.tooltip": "Çevre birimleri ve diğer Lua nesneleri tarafından atılan istisnaları\ngünlüğe kaydet. Bu, mod yapımcılarının sorunları ayıklamasını kolaylaştırır\nancak insanların hatalı yöntemler kullanması durumunda günlük spam'ına neden\nolabilir.",
|
||||
"gui.computercraft.config.maximum_open_files": "Bilgisayar başına maksimum açık dosya",
|
||||
"gui.computercraft.config.maximum_open_files.tooltip": "Bir bilgisayarda aynı anda kaç dosyanın açık olabileceğini ayarla. Sınırsız için 0 olarak ayarla.\nAralık: > 0",
|
||||
"gui.computercraft.config.monitor_distance": "Monitör mesafesi",
|
||||
"gui.computercraft.config.monitor_distance.tooltip": "Monitörlerin görüntü oluşturacağı maksimum mesafe. Bu, varsayılan olarak\nstandart tile entity sınırıdır, ancak daha büyük monitörler oluşturmak\nisterseniz genişletilebilir.\nAralık: 16 ~ 1024",
|
||||
"gui.computercraft.config.monitor_renderer": "Monitör oluşturucu",
|
||||
"gui.computercraft.config.monitor_renderer.tooltip": "Monitörler için kullanılacak oluşturucu. Genellikle bu \"best\" değerinde\ntutulmalıdır - monitörlerde performans sorunları varsa, alternatif oluşturucuları\ndenemek isteyebilirsiniz.\nİzin Verilen Değerler: BEST, TBO, VBO",
|
||||
"gui.computercraft.config.peripheral": "Çevre birimleri",
|
||||
"gui.computercraft.config.peripheral.command_block_enabled": "Komut bloğu çevresel birimini etkinleştir",
|
||||
"gui.computercraft.config.peripheral.command_block_enabled.tooltip": "Komut Bloğu çevre birimi desteğini etkinleştir",
|
||||
"gui.computercraft.config.peripheral.max_notes_per_tick": "Bir bilgisayarın aynı anda çalabileceği maksimum nota sayısı",
|
||||
"gui.computercraft.config.peripheral.max_notes_per_tick.tooltip": "Bir hoparlörün aynı anda çalabileceği maksimum nota miktarı.\nAralık: > 1",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range": "Modem menzili (yüksek irtifa)",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range.tooltip": "Kablosuz Modemlerin açık havada maksimum irtifadaki menzili, metre cinsinden.\nAralık: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm": "Modem menzili (yüksek irtifa, kötü hava koşulları)",
|
||||
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm.tooltip": "Kablosuz Modemlerin fırtınalı havalarda maksimum irtifadaki menzili, metre cinsinden.\nAralık: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_range": "Modem menzili (varsayılan)",
|
||||
"gui.computercraft.config.peripheral.modem_range.tooltip": "Kablosuz Modemlerin açık havada alçak irtifadaki menzili, metre cinsinden.\nAralık: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.modem_range_during_storm": "Modem menzili (kötü hava koşulları)",
|
||||
"gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "Kablosuz Modemlerin fırtınalı havalarda alçak irtifadaki menzili, metre cinsinden.\nAralık: 0 ~ 100000",
|
||||
"gui.computercraft.config.peripheral.monitor_bandwidth": "Monitör bant genişliği",
|
||||
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "Tik başına* ne kadar monitör verisi gönderilebileceğinin sınırı. Not:\n - Bant genişliği sıkıştırmadan önce ölçülür, bu nedenle istemciye gönderilen veriler\n daha küçüktür.\n - Bu, bir paketin gönderildiği oyuncu sayısını göz ardı eder. Bir oyuncu için bir\nmonitörü güncellemek, 20 oyuncuya göndermekle aynı bant genişliği sınırını tüketir.\n - Tam boyutlu bir monitör ~25kb veri gönderir. Dolayısıyla varsayılan değer (1MB)\ntek bir tikte ~40 monitörün güncellenmesine izin verir.\nDevre dışı bırakmak için 0 olarak ayarla.\nAralık: > 0",
|
||||
"gui.computercraft.config.peripheral.tooltip": "Çevre birimlerine ilişkin çeşitli seçenekler.",
|
||||
"gui.computercraft.config.term_sizes": "Terminal boyutları",
|
||||
"gui.computercraft.config.term_sizes.computer": "Bilgisayar",
|
||||
"gui.computercraft.config.term_sizes.computer.height": "Terminal yüksekliği",
|
||||
"gui.computercraft.config.term_sizes.computer.height.tooltip": "Aralık: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.computer.tooltip": "Bilgisayarların terminal boyutu.",
|
||||
"gui.computercraft.config.term_sizes.computer.width": "Terminal genişliği",
|
||||
"gui.computercraft.config.term_sizes.computer.width.tooltip": "Aralık: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.monitor": "Monitör",
|
||||
"gui.computercraft.config.term_sizes.monitor.height": "Maks monitör yüksekliği",
|
||||
"gui.computercraft.config.term_sizes.monitor.height.tooltip": "Aralık: 1 ~ 32",
|
||||
"gui.computercraft.config.term_sizes.monitor.tooltip": "Maksimum monitör boyutu (blok olarak).",
|
||||
"gui.computercraft.config.term_sizes.monitor.width": "Maks monitör genişliği",
|
||||
"gui.computercraft.config.term_sizes.monitor.width.tooltip": "Aralık: 1 ~ 32",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer": "Cep Bilgisayarı",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.height": "Terminal yüksekliği",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "Aralık: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.tooltip": "Cep bilgisayarlarının terminal boyutu.",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.width": "Terminal genişliği",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "Aralık: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.tooltip": "Çeşitli bilgisayar terminallerinin boyutunu yapılandır.\nDaha büyük terminaller daha fazla bant genişliği gerektirir, bu nedenle dikkatli\nkullanın.",
|
||||
"gui.computercraft.config.turtle": "Kaplumbağalar",
|
||||
"gui.computercraft.config.turtle.advanced_fuel_limit": "Gelişmiş Kaplumbağa yakıt limiti",
|
||||
"gui.computercraft.config.turtle.advanced_fuel_limit.tooltip": "Gelişmiş Kaplumbağaların yakıt limiti.\nAralık: > 0",
|
||||
"gui.computercraft.config.turtle.can_push": "Kaplumbağalar varlıkları itebilir",
|
||||
"gui.computercraft.config.turtle.can_push.tooltip": "true olarak ayarlanırsa, Kaplumbağalar yer varsa durmak yerine varlıkları yolun\ndışına iter.",
|
||||
"gui.computercraft.config.turtle.need_fuel": "Yakıtı etkinleştir",
|
||||
"gui.computercraft.config.turtle.need_fuel.tooltip": "Kaplumbağaların hareket etmek için yakıta ihtiyaç duyup duymadığını ayarla.",
|
||||
"gui.computercraft.config.turtle.normal_fuel_limit": "Kaplumbağa yakıt limiti",
|
||||
"gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "Kaplumbağaların yakıt limiti.\nAralık: > 0",
|
||||
"gui.computercraft.config.turtle.tooltip": "Kaplumbağalarla ilgili çeşitli seçenekler.",
|
||||
"gui.computercraft.config.upload_max_size": "Dosya yükleme boyut sınırı (bayt)",
|
||||
"gui.computercraft.config.upload_max_size.tooltip": "Bayt cinsinden dosya yükleme boyut sınırı. 1 KiB ve 16 MiB aralığında olmalıdır.\nYüklemelerin tek bir tikte işlendiğini unutmayın - büyük dosyalar veya düşük ağ\nperformansı ağ iş parçacığını bekletebilir. Ve disk alanına dikkat edin!\nAralık: 1024 ~ 16777216",
|
||||
"gui.computercraft.config.upload_nag_delay": "Yükleme nag gecikmesi",
|
||||
"gui.computercraft.config.upload_nag_delay.tooltip": "Beklenmeyen içe aktarmalar hakkında bildirimde bulunacağımız saniye cinsinden gecikme. Devre dışı bırakmak için 0 olarak ayarla.\nAralık: 0 ~ 60",
|
||||
"gui.computercraft.pocket_computer_overlay": "Cep bilgisayarı açık. Kapatmak için ESC tuşuna bas.",
|
||||
"gui.computercraft.terminal": "Bilgisayar terminali",
|
||||
"gui.computercraft.tooltip.computer_id": "Bilgisayar Kimliği: %s",
|
||||
"gui.computercraft.tooltip.copy": "Panoya kopyala",
|
||||
"gui.computercraft.tooltip.disk_id": "Disk Kimliği: %s",
|
||||
"gui.computercraft.tooltip.terminate": "Çalışmakta olan kodu durdur",
|
||||
"gui.computercraft.tooltip.terminate.key": "Ctrl+T basılı tut",
|
||||
"gui.computercraft.tooltip.turn_off": "Bu bilgisayarı kapat",
|
||||
"gui.computercraft.tooltip.turn_off.key": "Ctrl+S basılı tut",
|
||||
"gui.computercraft.tooltip.turn_on": "Bu bilgisayarı aç",
|
||||
"gui.computercraft.upload.failed": "Yükleme Başarısız",
|
||||
"gui.computercraft.upload.failed.computer_off": "Dosyaları yüklemeden önce bilgisayarı açmalısın.",
|
||||
"gui.computercraft.upload.failed.corrupted": "Dosyalar yüklenirken bozuldu. Lütfen tekrar dene.",
|
||||
"gui.computercraft.upload.failed.generic": "Dosya yükleme başarısız (%s)",
|
||||
"gui.computercraft.upload.failed.name_too_long": "Dosya adları yüklenemeyecek kadar uzun.",
|
||||
"gui.computercraft.upload.failed.too_many_files": "Bu kadar dosya yüklenemiyor.",
|
||||
"gui.computercraft.upload.failed.too_much": "Dosyalarınız yüklenemeyecek kadar büyük.",
|
||||
"gui.computercraft.upload.no_response": "Dosyalar Aktarılıyor",
|
||||
"gui.computercraft.upload.no_response.msg": "Bilgisayarınız aktarılan dosyalarınızı kullanmadı. %s programını çalıştırmanız ve tekrar denemeniz gerekebilir.",
|
||||
"item.computercraft.disk": "Disket",
|
||||
"item.computercraft.pocket_computer_advanced": "Gelişmiş Cep Bilgisayarı",
|
||||
"item.computercraft.pocket_computer_advanced.upgraded": "Gelişmiş %s Cep Bilgisayarı",
|
||||
"item.computercraft.pocket_computer_normal": "Cep Bilgisayarı",
|
||||
"item.computercraft.pocket_computer_normal.upgraded": "%s Cep Bilgisayarı",
|
||||
"item.computercraft.printed_book": "Yazdırılmış Kitap",
|
||||
"item.computercraft.printed_page": "Yazdırılmış Sayfa",
|
||||
"item.computercraft.printed_pages": "Yazdırılmış Sayfalar",
|
||||
"item.computercraft.treasure_disk": "Disket",
|
||||
"itemGroup.computercraft": "ComputerCraft",
|
||||
"tag.item.computercraft.computer": "Bilgisayarlar",
|
||||
"tag.item.computercraft.dyeable": "Boyanabilir eşyalar",
|
||||
"tag.item.computercraft.monitor": "Monitörler",
|
||||
"tag.item.computercraft.turtle": "Kaplumbağalar",
|
||||
"tag.item.computercraft.turtle_can_place": "Kaplumbağa-koyulabilir eşyalar",
|
||||
"tag.item.computercraft.wired_modem": "Kablolu modemler",
|
||||
"tracking_field.computercraft.avg": "%s (ort)",
|
||||
"tracking_field.computercraft.computer_tasks.name": "Görevler",
|
||||
"tracking_field.computercraft.count": "%s (sayı)",
|
||||
"tracking_field.computercraft.fs.name": "Dosya sistemi işlemleri",
|
||||
"tracking_field.computercraft.http_download.name": "HTTP indir",
|
||||
"tracking_field.computercraft.http_requests.name": "HTTP istekleri",
|
||||
"tracking_field.computercraft.http_upload.name": "HTTP yükleme",
|
||||
"tracking_field.computercraft.java_allocation.name": "Java Ayırmaları",
|
||||
"tracking_field.computercraft.max": "%s (maks)",
|
||||
"tracking_field.computercraft.peripheral.name": "Çevre birimi çağrıları",
|
||||
"tracking_field.computercraft.server_tasks.name": "Sunucu görevleri",
|
||||
"tracking_field.computercraft.turtle_ops.name": "Kaplumbağa işlemleri",
|
||||
"tracking_field.computercraft.websocket_incoming.name": "Websocket gelen",
|
||||
"tracking_field.computercraft.websocket_outgoing.name": "Websocket giden",
|
||||
"upgrade.computercraft.speaker.adjective": "Gürültücü",
|
||||
"upgrade.computercraft.wireless_modem_advanced.adjective": "Ender",
|
||||
"upgrade.computercraft.wireless_modem_normal.adjective": "Kablosuz",
|
||||
"upgrade.minecraft.crafting_table.adjective": "Üretken",
|
||||
"upgrade.minecraft.diamond_axe.adjective": "Düşen",
|
||||
"upgrade.minecraft.diamond_hoe.adjective": "Çiftçi",
|
||||
"upgrade.minecraft.diamond_pickaxe.adjective": "Madenci",
|
||||
"upgrade.minecraft.diamond_shovel.adjective": "Kazıcı",
|
||||
"upgrade.minecraft.diamond_sword.adjective": "Dövüşçü"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 212 B |
@@ -0,0 +1,3 @@
|
||||
SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
@@ -6,16 +6,11 @@ package dan200.computercraft.mixin.gametest;
|
||||
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.gametest.framework.GameTestInfo;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(GameTestHelper.class)
|
||||
public interface GameTestHelperAccessor {
|
||||
@Invoker
|
||||
AABB callGetBounds();
|
||||
|
||||
@Accessor
|
||||
GameTestInfo getTestInfo();
|
||||
}
|
||||
|
||||
@@ -19,9 +19,11 @@ import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.LeverBlock
|
||||
import net.minecraft.world.level.block.RedstoneLampBlock
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.lwjgl.glfw.GLFW
|
||||
@@ -115,6 +117,19 @@ class Computer_Test {
|
||||
thenOnComputer { callPeripheral("right", "size").assertArrayEquals(54) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a computer item is dropped on explosion.
|
||||
*/
|
||||
@GameTest
|
||||
fun Drops_on_explosion(context: GameTestHelper) = context.sequence {
|
||||
thenExecute {
|
||||
val explosionPos = Vec3.atCenterOf(context.absolutePos(BlockPos(2, 2, 2)))
|
||||
context.level.explode(null, explosionPos.x, explosionPos.y, explosionPos.z, 2.0f, Level.ExplosionInteraction.TNT)
|
||||
|
||||
context.assertItemEntityCountIs(ModRegistry.Items.COMPUTER_NORMAL.get(), 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the client can open the computer UI and interact with it.
|
||||
*/
|
||||
|
||||
@@ -160,5 +160,7 @@ class Monitor_Test {
|
||||
}
|
||||
|
||||
thenScreenshot()
|
||||
|
||||
thenExecute { helper.level.dayTime = Times.NOON }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,5 +53,7 @@ class Printout_Test {
|
||||
}
|
||||
|
||||
thenScreenshot()
|
||||
|
||||
thenExecute { helper.level.dayTime = Times.NOON }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,6 +693,21 @@ class Turtle_Test {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a turtle can break a block that explodes, causing the turtle itself to explode.
|
||||
*
|
||||
* @see [#585](https://github.com/cc-tweaked/CC-Tweaked/issues/585).
|
||||
*/
|
||||
@GameTest
|
||||
fun Breaks_exploding_block(context: GameTestHelper) = context.sequence {
|
||||
thenOnComputer { turtle.dig(Optional.empty()) }
|
||||
thenWaitUntil { context.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 2)) }
|
||||
thenExecute {
|
||||
context.assertItemEntityCountIs(ModRegistry.Items.TURTLE_NORMAL.get(), 1)
|
||||
context.assertItemEntityCountIs(Items.BONE_BLOCK, 65)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render turtles as an item.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@ import net.minecraft.world.Container
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.UseOnContext
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
@@ -252,6 +253,16 @@ fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: Strin
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to [GameTestHelper.assertItemEntityCountIs], but searching anywhere in the structure bounds.
|
||||
*/
|
||||
fun GameTestHelper.assertItemEntityCountIs(expected: Item, count: Int) {
|
||||
val actualCount = getEntities(EntityType.ITEM).sumOf { if (it.item.`is`(expected)) it.item.count else 0 }
|
||||
if (actualCount != count) {
|
||||
throw GameTestAssertException("Expected $count ${expected.description.string} items to exist (found $actualCount)")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getName(type: BlockEntityType<*>): ResourceLocation = RegistryWrappers.BLOCK_ENTITY_TYPES.getKey(type)!!
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.gui.screens.TitleScreen
|
||||
import net.minecraft.client.tutorial.TutorialSteps
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.gametest.framework.*
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.sounds.SoundSource
|
||||
@@ -74,6 +75,7 @@ object ClientTestHooks {
|
||||
minecraft.options.cloudStatus().set(CloudStatus.OFF)
|
||||
minecraft.options.particles().set(ParticleStatus.MINIMAL)
|
||||
minecraft.options.tutorialStep = TutorialSteps.NONE
|
||||
minecraft.options.pauseOnLostFocus = false
|
||||
minecraft.options.renderDistance().set(6)
|
||||
minecraft.options.gamma().set(1.0)
|
||||
minecraft.options.getSoundSourceOptionInstance(SoundSource.MUSIC).set(0.0)
|
||||
@@ -93,7 +95,7 @@ object ClientTestHooks {
|
||||
LEVEL_NAME,
|
||||
LevelSettings("Test Level", GameType.CREATIVE, false, Difficulty.EASY, true, rules, WorldDataConfiguration.DEFAULT),
|
||||
WorldOptions(WorldOptions.randomSeed(), false, false),
|
||||
) { WorldPresets.createNormalWorldDimensions(it) }
|
||||
) { it.registryOrThrow(Registries.WORLD_PRESET).getOrThrow(WorldPresets.FLAT).createWorldDimensions() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +110,20 @@ object ClientTestHooks {
|
||||
val testTracker = when (val tracker = this.testTracker) {
|
||||
null -> {
|
||||
if (server.overworld().players().isEmpty()) return
|
||||
|
||||
// Place our players above where the tests will run, looking down. This at least ensures they're in the
|
||||
// right area when the tests start running.
|
||||
for (player in server.overworld().players()) {
|
||||
player.abilities.flying = true
|
||||
player.onUpdateAbilities()
|
||||
player.connection.teleport(0.0, -30.0, 0.0, 0.0f, 90.0f)
|
||||
player.inventory.clearContent()
|
||||
}
|
||||
|
||||
// Wait for all chunks to be rendered.
|
||||
if (!Minecraft.getInstance().isRenderingStable()) return
|
||||
|
||||
// Then a little more just in case.
|
||||
if (startupDelay >= 0) {
|
||||
// TODO: Is there a better way? Maybe set a flag when the client starts rendering?
|
||||
startupDelay--
|
||||
|
||||
@@ -13,7 +13,13 @@ import dan200.computercraft.shared.computer.core.ServerContext
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.*
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.GameRules
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
@@ -173,4 +179,23 @@ object TestHooks {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a hook that makes breaking a bone block spawn an explosion.
|
||||
*
|
||||
* It would be more Correct to register a custom block, but that's quite a lot of work, and doesn't seem worth it
|
||||
* for test code.
|
||||
*
|
||||
* See also [Turtle_Test.Breaks_exploding_block].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun onBeforeDestroyBlock(level: LevelAccessor, pos: BlockPos, state: BlockState): Boolean {
|
||||
if (state.block === Blocks.BONE_BLOCK && level is ServerLevel) {
|
||||
val explosionPos = Vec3.atCenterOf(pos)
|
||||
level.explode(null, explosionPos.x, explosionPos.y, explosionPos.z, 4.0f, Level.ExplosionInteraction.TNT)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
138
projects/common/src/testMod/resources/data/cctest/structures/computer_test.drops_on_explosion.snbt
generated
Normal file
138
projects/common/src/testMod/resources/data/cctest/structures/computer_test.drops_on_explosion.snbt
generated
Normal file
@@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [0, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [0, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [0, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [0, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [0, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 1], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 2], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 3], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [1, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [2, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:computer_normal{facing:east,state:off}", nbt: {On: 0b, id: "computercraft:computer_normal"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [3, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 1], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 2], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 3], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 1], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [1, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [2, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [3, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 1], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 2], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 3], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:obsidian",
|
||||
"minecraft:barrier",
|
||||
"minecraft:air",
|
||||
"computercraft:computer_normal{facing:east,state:off}"
|
||||
]
|
||||
}
|
||||
139
projects/common/src/testMod/resources/data/cctest/structures/turtle_test.breaks_exploding_block.snbt
generated
Normal file
139
projects/common/src/testMod/resources/data/cctest/structures/turtle_test.breaks_exploding_block.snbt
generated
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [0, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [0, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [0, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [0, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [1, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [2, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [3, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 0], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 1], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 2], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 3], state: "minecraft:obsidian"},
|
||||
{pos: [4, 0, 4], state: "minecraft:obsidian"},
|
||||
{pos: [0, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 1], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 2], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 3], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [1, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [2, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [], Label: "turtle_test.breaks_exploding_block", LeftUpgrade: "minecraft:diamond_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0}}, On: 1b, Owner: {LowerId: -5670393268852517359L, Name: "Player172", UpperId: 3578583684139923613L}, Slot: 0, Items: [{Count: 64b, Slot: 0b, id: "minecraft:bone_block"}], id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:bone_block{axis:y}"},
|
||||
{pos: [2, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [3, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 1], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 2], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 3], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 1], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [1, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [2, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [3, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 1], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 2], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 3], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:obsidian",
|
||||
"minecraft:barrier",
|
||||
"minecraft:bone_block{axis:y}",
|
||||
"minecraft:air",
|
||||
"computercraft:turtle_normal{facing:south,waterlogged:false}"
|
||||
]
|
||||
}
|
||||
@@ -130,10 +130,10 @@
|
||||
],
|
||||
entities: [
|
||||
{blockPos: [2, 1, 0], pos: [2.5d, 1.0d, 0.5d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CustomName: '{"text":"turtle_test.render_turtle_items"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [125.5d, -58.0d, 53.6501934495752d], Pose: {}, Rotation: [0.14965993f, 4.066999f], ShowArms: 0b, Small: 0b, UUID: [I; -1678989666, 1780632657, -1267321893, 665166246], id: "minecraft:armor_stand"}},
|
||||
{blockPos: [3, 1, 3], pos: [3.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 671334450, -268547745, -1360971514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
|
||||
{blockPos: [3, 3, 3], pos: [3.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 671334422, -268542345, -1362491514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
|
||||
{blockPos: [1, 1, 3], pos: [1.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 625334450, -268647745, -1360971514, -649724242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
|
||||
{blockPos: [1, 3, 3], pos: [1.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 675334422, -268542245, -1362491514, -649755242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}
|
||||
{blockPos: [3, 1, 3], pos: [3.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 671334450, -268547745, -1360971514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
|
||||
{blockPos: [3, 3, 3], pos: [3.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 671334422, -268542345, -1362491514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
|
||||
{blockPos: [1, 1, 3], pos: [1.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 625334450, -268647745, -1360971514, -649724242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
|
||||
{blockPos: [1, 3, 3], pos: [1.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 675334422, -268542245, -1362491514, -649755242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}
|
||||
],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
|
||||
@@ -8,9 +8,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The result of invoking a Lua method.
|
||||
@@ -55,6 +53,12 @@ public final class MethodResult {
|
||||
* <p>
|
||||
* In order to provide a custom object with methods, one may return a {@link IDynamicLuaObject}, or an arbitrary
|
||||
* class with {@link LuaFunction} annotations. Anything else will be converted to {@code nil}.
|
||||
* <p>
|
||||
* Shared objects in a {@link MethodResult} will preserve their sharing when converted to Lua values. For instance,
|
||||
* {@code Map<?, ?> m = new HashMap(); return MethodResult.of(m, m); } will return two values {@code a}, {@code b}
|
||||
* where {@code a == b}. The one exception to this is Java's singleton collections ({@link List#of()},
|
||||
* {@link Set#of()} and {@link Map#of()}), which are always converted to new table. This is not true for other
|
||||
* singleton collections, such as those provided by {@link Collections} or Guava.
|
||||
*
|
||||
* @param value The value to return to the calling Lua function.
|
||||
* @return A method result which returns immediately with the given value.
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.peripheral;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A thread-safe collection of computers.
|
||||
* <p>
|
||||
* This collection is intended to be used by peripherals that need to maintain a set of all attached computers.
|
||||
* <p>
|
||||
* It is recommended to use over Java's built-in concurrent collections (e.g. {@link CopyOnWriteArraySet} or
|
||||
* {@link ConcurrentHashMap}), as {@link AttachedComputerSet} ensures that computers cannot be accessed after they are
|
||||
* detached, guaranteeing that {@link NotAttachedException}s will not be thrown.
|
||||
* <p>
|
||||
* To ensure this, {@link AttachedComputerSet} is not directly iterable, as we cannot ensure that computers are not
|
||||
* detached while the iterator is running (and so trying to use the computer would error). Instead, computers should be
|
||||
* looped over using {@link #forEach(Consumer)}.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
*
|
||||
* <pre>{@code
|
||||
* public class MyPeripheral implements IPeripheral {
|
||||
* private final AttachedComputerSet computers = new ComputerCollection();
|
||||
*
|
||||
* @Override
|
||||
* public void attach(IComputerAccess computer) {
|
||||
* computers.add(computer);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void detach(IComputerAccess computer) {
|
||||
* computers.remove(computer);
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @see IComputerAccess
|
||||
* @see IPeripheral#attach(IComputerAccess)
|
||||
* @see IPeripheral#detach(IComputerAccess)
|
||||
*/
|
||||
public final class AttachedComputerSet {
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Set<IComputerAccess> computers = new HashSet<>(0);
|
||||
|
||||
/**
|
||||
* Add a computer to this collection of computers. This should be called from
|
||||
* {@link IPeripheral#attach(IComputerAccess)}.
|
||||
*
|
||||
* @param computer The computer to add.
|
||||
*/
|
||||
public void add(IComputerAccess computer) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
computers.add(computer);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a computer from this collection of computers. This should be called from
|
||||
* {@link IPeripheral#detach(IComputerAccess)}.
|
||||
*
|
||||
* @param computer The computer to remove.
|
||||
*/
|
||||
public void remove(IComputerAccess computer) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
computers.remove(computer);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an action to each computer in this collection.
|
||||
*
|
||||
* @param action The action to apply.
|
||||
*/
|
||||
public void forEach(Consumer<? super IComputerAccess> action) {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
computers.forEach(action);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@linkplain IComputerAccess#queueEvent(String, Object...) Queue an event} on all computers.
|
||||
*
|
||||
* @param event The name of the event to queue.
|
||||
* @param arguments The arguments for this event.
|
||||
* @see IComputerAccess#queueEvent(String, Object...)
|
||||
*/
|
||||
public void queueEvent(String event, @Nullable Object... arguments) {
|
||||
forEach(c -> c.queueEvent(event, arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this collection contains any computers.
|
||||
* <p>
|
||||
* This method is primarily intended for presentation purposes (such as rendering an icon in the UI if a computer
|
||||
* is attached to your peripheral). Due to the multi-threaded nature of peripherals, it is not recommended to guard
|
||||
* any logic behind this check.
|
||||
* <p>
|
||||
* For instance, {@code if(computers.hasComputers()) computers.queueEvent("foo");} contains a race condition, as
|
||||
* there's no guarantee that any computers are still attached within the body of the if statement.
|
||||
*
|
||||
* @return Whether this collection is non-empty.
|
||||
*/
|
||||
public boolean hasComputers() {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
return !computers.isEmpty();
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public interface IPeripheral {
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called when when a computer is attaching to the peripheral.
|
||||
* Is called when a computer is attaching to the peripheral.
|
||||
* <p>
|
||||
* This will occur when a peripheral is placed next to an active computer, when a computer is turned on next to a
|
||||
* peripheral, when a turtle travels into a square next to a peripheral, or when a wired modem adjacent to this
|
||||
@@ -48,8 +48,9 @@ public interface IPeripheral {
|
||||
* {@code peripheral.call()}. This method can be used to keep track of which computers are attached to the
|
||||
* peripheral, or to take action when attachment occurs.
|
||||
* <p>
|
||||
* Be aware that will be called from both the server thread and ComputerCraft Lua thread, and so must be thread-safe
|
||||
* and reentrant.
|
||||
* Be aware that may be called from both the server thread and ComputerCraft Lua thread, and so must be thread-safe
|
||||
* and reentrant. If you need to store a list of attached computers, it is recommended you use a
|
||||
* {@link AttachedComputerSet}.
|
||||
*
|
||||
* @param computer The interface to the computer that is being attached. Remember that multiple computers can be
|
||||
* attached to a peripheral at once.
|
||||
@@ -68,8 +69,9 @@ public interface IPeripheral {
|
||||
* This method can be used to keep track of which computers are attached to the peripheral, or to take action when
|
||||
* detachment occurs.
|
||||
* <p>
|
||||
* Be aware that this will be called from both the server and ComputerCraft Lua thread, and must be thread-safe
|
||||
* and reentrant.
|
||||
* Be aware that this may be called from both the server and ComputerCraft Lua thread, and must be thread-safe
|
||||
* and reentrant. If you need to store a list of attached computers, it is recommended you use a
|
||||
* {@link AttachedComputerSet}.
|
||||
*
|
||||
* @param computer The interface to the computer that is being detached. Remember that multiple computers can be
|
||||
* attached to a peripheral at once.
|
||||
|
||||
@@ -7,6 +7,7 @@ import cc.tweaked.gradle.getAbsolutePath
|
||||
plugins {
|
||||
`java-library`
|
||||
`java-test-fixtures`
|
||||
alias(libs.plugins.shadow)
|
||||
|
||||
id("cc-tweaked.kotlin-convention")
|
||||
id("cc-tweaked.java-convention")
|
||||
@@ -57,3 +58,22 @@ val checkChangelog by tasks.registering(cc.tweaked.gradle.CheckChangelog::class)
|
||||
}
|
||||
|
||||
tasks.check { dependsOn(checkChangelog) }
|
||||
|
||||
// We configure the shadow jar to ship netty-codec and all its dependencies, relocating them under the
|
||||
// dan200.computercraft.core package.
|
||||
// This is used as part of the Forge build, so that our version of netty-codec is loaded under the GAME layer, and so
|
||||
// has access to our jar-in-jar'ed jzlib.
|
||||
tasks.shadowJar {
|
||||
minimize()
|
||||
|
||||
dependencies {
|
||||
include(dependency(libs.netty.codec.get()))
|
||||
include(dependency(libs.netty.http.get()))
|
||||
include(dependency(libs.netty.socks.get()))
|
||||
include(dependency(libs.netty.proxy.get()))
|
||||
}
|
||||
|
||||
for (pkg in listOf("io.netty.handler.codec", "io.netty.handler.proxy")) {
|
||||
relocate(pkg, "dan200.computercraft.core.vendor.$pkg")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class FSAPI implements ILuaAPI {
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String[] list(String path) throws LuaException {
|
||||
public final List<String> list(String path) throws LuaException {
|
||||
try (var ignored = environment.time(Metrics.FS_OPS)) {
|
||||
return getFileSystem().list(path);
|
||||
} catch (FileSystemException e) {
|
||||
|
||||
@@ -122,7 +122,7 @@ public class OSAPI implements ILuaAPI {
|
||||
}
|
||||
|
||||
private static long getEpochForCalendar(Calendar c) {
|
||||
return c.getTime().getTime();
|
||||
return c.getTimeInMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +298,7 @@ public class OSAPI implements ILuaAPI {
|
||||
* textutils.formatTime(os.time())
|
||||
* }</pre>
|
||||
* @cc.since 1.2
|
||||
* @cc.changed 1.80pr1 Add support for getting the local local and UTC time.
|
||||
* @cc.changed 1.80pr1 Add support for getting the local and UTC time.
|
||||
* @cc.changed 1.82.0 Arguments are now case insensitive.
|
||||
* @cc.changed 1.83.0 {@link #time(IArguments)} now accepts table arguments and converts them to UNIX timestamps.
|
||||
* @see #date To get a date table that can be converted with this function.
|
||||
@@ -403,21 +403,39 @@ public class OSAPI implements ILuaAPI {
|
||||
* function. The format string can also be prefixed with an exclamation mark
|
||||
* ({@code !}) to use UTC time instead of the server's local timezone.
|
||||
* <p>
|
||||
* If the format is exactly {@code *t} (optionally prefixed with {@code !}), a
|
||||
* table will be returned instead. This table has fields for the year, month,
|
||||
* day, hour, minute, second, day of the week, day of the year, and whether
|
||||
* Daylight Savings Time is in effect. This table can be converted to a UNIX
|
||||
* timestamp (days since 1 January 1970) with {@link #date}.
|
||||
* If the format is exactly {@code "*t"} (or {@code "!*t"} ), a table
|
||||
* representation of the timestamp will be returned instead. This table has
|
||||
* fields for the year, month, day, hour, minute, second, day of the week,
|
||||
* day of the year, and whether Daylight Savings Time is in effect. This
|
||||
* table can be converted back to a timestamp with {@link #time(IArguments)}.
|
||||
*
|
||||
* @param formatA The format of the string to return. This defaults to {@code %c}, which expands to a string similar to "Sat Dec 24 16:58:00 2011".
|
||||
* @param timeA The time to convert to a string. This defaults to the current time.
|
||||
* @return The resulting format string.
|
||||
* @param timeA The timestamp to convert to a string. This defaults to the current time.
|
||||
* @return The resulting formated string, or table.
|
||||
* @throws LuaException If an invalid format is passed.
|
||||
* @cc.since 1.83.0
|
||||
* @cc.usage Print the current date in a user-friendly string.
|
||||
* <pre>{@code
|
||||
* os.date("%A %d %B %Y") -- See the reference above!
|
||||
* }</pre>
|
||||
*
|
||||
* @cc.usage Convert a timestamp to a table.
|
||||
* <pre>{@code
|
||||
* os.date("!*t", 1242534247)
|
||||
* --[=[ {
|
||||
* -- Date
|
||||
* year = 2009,
|
||||
* month = 5,
|
||||
* day = 17,
|
||||
* yday = 137,
|
||||
* wday = 1,
|
||||
* -- Time
|
||||
* hour = 4,
|
||||
* min = 24,
|
||||
* sec = 7,
|
||||
* isdst = false,
|
||||
* } ]=]
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object date(Optional<String> formatA, Optional<Long> timeA) throws LuaException {
|
||||
|
||||
@@ -177,9 +177,9 @@ public abstract class AbstractHandle {
|
||||
/**
|
||||
* Read the remainder of the file.
|
||||
*
|
||||
* @return The file, or {@code null} if at the end of it.
|
||||
* @return The remaining contents of the file, or {@code null} in the event of an error.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} if we are at the end.
|
||||
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} in the event of an error.
|
||||
* @cc.since 1.80pr1
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -57,7 +57,7 @@ interface AddressPredicate {
|
||||
prefixSize = Integer.parseInt(prefixSizeStr);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidRuleException(String.format(
|
||||
"Invalid host host '%s': Cannot extract size of CIDR mask from '%s'.",
|
||||
"Invalid host '%s': Cannot extract size of CIDR mask from '%s'.",
|
||||
addressStr + '/' + prefixSizeStr, prefixSizeStr
|
||||
));
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ final class Generator<T> {
|
||||
}
|
||||
|
||||
// Fold over the original method's arguments, excluding the target in reverse. For each argument, we reduce
|
||||
// a method of type type (target, args..., arg_n, context..., IArguments) -> _ to (target, args..., context..., IArguments) -> _
|
||||
// a method of type (target, args..., arg_n, context..., IArguments) -> _ to (target, args..., context..., IArguments) -> _
|
||||
// until eventually we've flattened the whole list.
|
||||
for (var i = parameterTypes.size() - 1; i >= 0; i--) {
|
||||
handle = MethodHandles.foldArguments(handle, i + 1, argSelectors.get(i));
|
||||
|
||||
@@ -122,6 +122,10 @@ public class FileSystem {
|
||||
}
|
||||
|
||||
var lastSlash = path.lastIndexOf('/');
|
||||
|
||||
// If the trailing segment is a "..", then just append another one.
|
||||
if (path.substring(lastSlash < 0 ? 0 : lastSlash + 1).equals("..")) return path + "/..";
|
||||
|
||||
if (lastSlash >= 0) {
|
||||
return path.substring(0, lastSlash);
|
||||
} else {
|
||||
@@ -145,7 +149,7 @@ public class FileSystem {
|
||||
return getMount(sanitizePath(path)).getAttributes(sanitizePath(path));
|
||||
}
|
||||
|
||||
public synchronized String[] list(String path) throws FileSystemException {
|
||||
public synchronized List<String> list(String path) throws FileSystemException {
|
||||
path = sanitizePath(path);
|
||||
var mount = getMount(path);
|
||||
|
||||
@@ -161,10 +165,8 @@ public class FileSystem {
|
||||
}
|
||||
|
||||
// Return list
|
||||
var array = new String[list.size()];
|
||||
list.toArray(array);
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
list.sort(Comparator.naturalOrder());
|
||||
return list;
|
||||
}
|
||||
|
||||
public synchronized boolean exists(String path) throws FileSystemException {
|
||||
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.core.Logging;
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.util.LuaUtil;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.core.util.SanitisedError;
|
||||
import org.slf4j.Logger;
|
||||
@@ -94,11 +95,8 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
|
||||
private void addAPI(LuaState state, LuaTable globals, ILuaAPI api) throws LuaError {
|
||||
// Add the methods of an API to the global table
|
||||
var table = wrapLuaObject(api);
|
||||
if (table == null) {
|
||||
LOG.warn("API {} does not provide any methods", api);
|
||||
table = new LuaTable();
|
||||
}
|
||||
var table = new LuaTable();
|
||||
if (!makeLuaObject(api, table)) LOG.warn("API {} does not provide any methods", api);
|
||||
|
||||
var names = api.getNames();
|
||||
for (var name : names) globals.rawset(name, table);
|
||||
@@ -162,13 +160,16 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
timeout.removeListener(timeoutListener);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private LuaTable wrapLuaObject(Object object) {
|
||||
var table = new LuaTable();
|
||||
var found = luaMethods.forEachMethod(object, (target, name, method, info) ->
|
||||
/**
|
||||
* Populate a table with methods from an object.
|
||||
*
|
||||
* @param object The object to draw methods from.
|
||||
* @param table The table to fill.
|
||||
* @return Whether any methods were found.
|
||||
*/
|
||||
private boolean makeLuaObject(Object object, LuaTable table) {
|
||||
return luaMethods.forEachMethod(object, (target, name, method, info) ->
|
||||
table.rawset(name, new ResultInterpreterFunction(this, method, target, context, name)));
|
||||
|
||||
return found ? table : null;
|
||||
}
|
||||
|
||||
private LuaValue toValue(@Nullable Object object, @Nullable IdentityHashMap<Object, LuaValue> values) throws LuaError {
|
||||
@@ -183,26 +184,36 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
return ValueFactory.valueOf(bytes);
|
||||
}
|
||||
|
||||
// We have a more complex object, which is possibly recursive. First look up our object in the lookup map,
|
||||
// and reuse it if present.
|
||||
if (values == null) values = new IdentityHashMap<>(1);
|
||||
var result = values.get(object);
|
||||
if (result != null) return result;
|
||||
|
||||
if (object instanceof ILuaFunction) {
|
||||
return new ResultInterpreterFunction(this, FUNCTION_METHOD, object, context, object.toString());
|
||||
var function = new ResultInterpreterFunction(this, FUNCTION_METHOD, object, context, object.toString());
|
||||
values.put(object, function);
|
||||
return function;
|
||||
}
|
||||
|
||||
if (object instanceof IDynamicLuaObject) {
|
||||
LuaValue wrapped = wrapLuaObject(object);
|
||||
if (wrapped == null) wrapped = new LuaTable();
|
||||
values.put(object, wrapped);
|
||||
return wrapped;
|
||||
var table = new LuaTable();
|
||||
makeLuaObject(object, table);
|
||||
values.put(object, table);
|
||||
return table;
|
||||
}
|
||||
|
||||
// The following objects may be recursive. In these instances, we need to be careful to store the value *before*
|
||||
// recursing, to avoid stack overflows.
|
||||
|
||||
if (object instanceof Map<?, ?> map) {
|
||||
// Don't share singleton values, and instead convert them to a new table.
|
||||
if (LuaUtil.isSingletonMap(map)) return new LuaTable();
|
||||
|
||||
var table = new LuaTable();
|
||||
values.put(object, table);
|
||||
|
||||
for (Map.Entry<?, ?> pair : map.entrySet()) {
|
||||
for (var pair : map.entrySet()) {
|
||||
var key = toValue(pair.getKey(), values);
|
||||
var value = toValue(pair.getValue(), values);
|
||||
if (!key.isNil() && !value.isNil()) table.rawset(key, value);
|
||||
@@ -211,24 +222,29 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
}
|
||||
|
||||
if (object instanceof Collection<?> objects) {
|
||||
// Don't share singleton values, and instead convert them to a new table.
|
||||
if (LuaUtil.isSingletonCollection(objects)) return new LuaTable();
|
||||
|
||||
var table = new LuaTable(objects.size(), 0);
|
||||
values.put(object, table);
|
||||
|
||||
var i = 0;
|
||||
for (Object child : objects) table.rawset(++i, toValue(child, values));
|
||||
for (var child : objects) table.rawset(++i, toValue(child, values));
|
||||
return table;
|
||||
}
|
||||
|
||||
if (object instanceof Object[] objects) {
|
||||
var table = new LuaTable(objects.length, 0);
|
||||
values.put(object, table);
|
||||
|
||||
for (var i = 0; i < objects.length; i++) table.rawset(i + 1, toValue(objects[i], values));
|
||||
return table;
|
||||
}
|
||||
|
||||
var wrapped = wrapLuaObject(object);
|
||||
if (wrapped != null) {
|
||||
values.put(object, wrapped);
|
||||
return wrapped;
|
||||
var table = new LuaTable();
|
||||
if (makeLuaObject(object, table)) {
|
||||
values.put(object, table);
|
||||
return table;
|
||||
}
|
||||
|
||||
LOG.warn(Logging.JAVA_ERROR, "Received unknown type '{}', returning nil.", object.getClass().getName());
|
||||
|
||||
@@ -12,15 +12,13 @@ public class Palette {
|
||||
|
||||
private final boolean colour;
|
||||
private final double[][] colours = new double[PALETTE_SIZE][3];
|
||||
private final byte[][] byteColours = new byte[PALETTE_SIZE][4];
|
||||
private final int[] byteColours = new int[PALETTE_SIZE];
|
||||
|
||||
public static final Palette DEFAULT = new Palette(true);
|
||||
|
||||
public Palette(boolean colour) {
|
||||
this.colour = colour;
|
||||
resetColours();
|
||||
|
||||
for (var i = 0; i < PALETTE_SIZE; i++) byteColours[i][3] = (byte) 255;
|
||||
}
|
||||
|
||||
public void setColour(int i, double r, double g, double b) {
|
||||
@@ -30,15 +28,17 @@ public class Palette {
|
||||
colours[i][2] = b;
|
||||
|
||||
if (colour) {
|
||||
byteColours[i][0] = (byte) (int) (r * 255);
|
||||
byteColours[i][1] = (byte) (int) (g * 255);
|
||||
byteColours[i][2] = (byte) (int) (b * 255);
|
||||
byteColours[i] = packColour((int) (r * 255), (int) (g * 255), (int) (b * 255));
|
||||
} else {
|
||||
var grey = (byte) (int) ((r + g + b) / 3 * 255);
|
||||
byteColours[i][0] = byteColours[i][1] = byteColours[i][2] = grey;
|
||||
var grey = (int) ((r + g + b) / 3 * 255);
|
||||
byteColours[i] = packColour(grey, grey, grey);
|
||||
}
|
||||
}
|
||||
|
||||
private static int packColour(int r, int g, int b) {
|
||||
return 255 << 24 | (r & 255) << 16 | (g & 255) << 8 | b & 255;
|
||||
}
|
||||
|
||||
public void setColour(int i, Colour colour) {
|
||||
setColour(i, colour.getR(), colour.getG(), colour.getB());
|
||||
}
|
||||
@@ -48,26 +48,20 @@ public class Palette {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the colour as a set of RGB values suitable for rendering. Colours are automatically converted to greyscale
|
||||
* Get the colour as a set of ARGB values suitable for rendering. Colours are automatically converted to greyscale
|
||||
* when using a black and white palette.
|
||||
* <p>
|
||||
* This returns a byte array, suitable for being used directly by our terminal vertex format.
|
||||
* This returns a packed 32-bit ARGB colour.
|
||||
*
|
||||
* @param i The colour index.
|
||||
* @return The number as a tuple of bytes.
|
||||
* @return The actual RGB colour.
|
||||
*/
|
||||
public byte[] getRenderColours(int i) {
|
||||
public int getRenderColours(int i) {
|
||||
return byteColours[i];
|
||||
}
|
||||
|
||||
public void resetColour(int i) {
|
||||
if (i >= 0 && i < PALETTE_SIZE) setColour(i, Colour.VALUES[i]);
|
||||
}
|
||||
|
||||
public void resetColours() {
|
||||
for (var i = 0; i < Colour.VALUES.length; i++) {
|
||||
resetColour(i);
|
||||
}
|
||||
for (var i = 0; i < Colour.VALUES.length; i++) setColour(i, Colour.VALUES[i]);
|
||||
}
|
||||
|
||||
public static int encodeRGB8(double[] rgb) {
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
package dan200.computercraft.core.util;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public enum Colour {
|
||||
BLACK(0x111111),
|
||||
RED(0xcc4c4c),
|
||||
@@ -30,15 +28,6 @@ public enum Colour {
|
||||
return Colour.VALUES[colour];
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Colour fromHex(int colour) {
|
||||
for (var entry : VALUES) {
|
||||
if (entry.getHex() == colour) return entry;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private final int hex;
|
||||
private final float red, green, blue;
|
||||
|
||||
|
||||
@@ -4,9 +4,18 @@
|
||||
|
||||
package dan200.computercraft.core.util;
|
||||
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class LuaUtil {
|
||||
private static final List<?> EMPTY_LIST = List.of();
|
||||
private static final Set<?> EMPTY_SET = Set.of();
|
||||
private static final Map<?, ?> EMPTY_MAP = Map.of();
|
||||
|
||||
public static Object[] consArray(Object value, Collection<?> rest) {
|
||||
if (rest.isEmpty()) return new Object[]{ value };
|
||||
|
||||
@@ -14,7 +23,33 @@ public class LuaUtil {
|
||||
var out = new Object[rest.size() + 1];
|
||||
out[0] = value;
|
||||
var i = 1;
|
||||
for (Object additionalType : rest) out[i++] = additionalType;
|
||||
for (var additionalType : rest) out[i++] = additionalType;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a value is a singleton collection, such as one created with {@link List#of()}.
|
||||
* <p>
|
||||
* These collections are treated specially by {@link ILuaMachine} implementations: we skip sharing for them, and
|
||||
* create a new table each time.
|
||||
*
|
||||
* @param value The value to test.
|
||||
* @return Whether this is a singleton collection.
|
||||
*/
|
||||
public static boolean isSingletonCollection(Collection<?> value) {
|
||||
return value == EMPTY_LIST || value == EMPTY_SET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a value is a singleton map, such as one created with {@link Map#of()}.
|
||||
* <p>
|
||||
* These collections are treated specially by {@link ILuaMachine} implementations: we skip sharing for them, and
|
||||
* create a new table each time.
|
||||
*
|
||||
* @param value The value to test.
|
||||
* @return Whether this is a singleton map.
|
||||
*/
|
||||
public static boolean isSingletonMap(Map<?, ?> value) {
|
||||
return value == EMPTY_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,13 +851,32 @@ 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.
|
||||
|
||||
Lua has a rather different data model to Javascript/JSON. As a result, some Lua
|
||||
values do not serialise cleanly into JSON.
|
||||
|
||||
- Lua tables can contain arbitrary key-value pairs, but JSON only accepts arrays,
|
||||
and objects (which require a string key). When serialising a table, if it only
|
||||
has numeric keys, then it will be treated as an array. Otherwise, the table will
|
||||
be serialised to an object using the string keys. Non-string keys (such as numbers
|
||||
or tables) will be dropped.
|
||||
|
||||
A consequence of this is that an empty table will always be serialised to an object,
|
||||
not an array. [`textutils.empty_json_array`] may be used to express an empty array.
|
||||
|
||||
- Lua strings are an a sequence of raw bytes, and do not have any specific encoding.
|
||||
However, JSON strings must be valid unicode. By default, non-ASCII characters in a
|
||||
string are serialised to their unicode code point (for instance, `"\xfe"` is
|
||||
converted to `"\u00fe"`). The `unicode_strings` option may be set to treat all input
|
||||
strings as UTF-8.
|
||||
|
||||
- Lua does not distinguish between missing keys (`undefined` in JS) and ones explicitly
|
||||
set to `null`. As a result `{ x = nil }` is serialised to `{}`. [`textutils.json_null`]
|
||||
may be used to get an explicit null value (`{ x = textutils.json_null }` will serialise
|
||||
to `{"x": null}`).
|
||||
|
||||
@param[1] t The value to serialise. Like [`textutils.serialise`], this should not
|
||||
contain recursive tables or functions.
|
||||
@tparam[1,opt] {
|
||||
@@ -925,22 +944,21 @@ unserialiseJSON = unserialise_json
|
||||
-- @since 1.31
|
||||
function urlEncode(str)
|
||||
expect(1, str, "string")
|
||||
if str then
|
||||
str = string.gsub(str, "\n", "\r\n")
|
||||
str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c)
|
||||
local n = string.byte(c)
|
||||
if n < 128 then
|
||||
-- ASCII
|
||||
return string.format("%%%02X", n)
|
||||
else
|
||||
-- Non-ASCII (encode as UTF-8)
|
||||
return
|
||||
string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) ..
|
||||
string.format("%%%02X", 128 + bit32.band(n, 63))
|
||||
end
|
||||
end)
|
||||
str = string.gsub(str, " ", "+")
|
||||
end
|
||||
local gsub, byte, format, band, arshift = string.gsub, string.byte, string.format, bit32.band, bit32.arshift
|
||||
|
||||
str = gsub(str, "\n", "\r\n")
|
||||
str = gsub(str, "[^A-Za-z0-9%-%_%.]", function(c)
|
||||
if c == " " then return "+" end
|
||||
|
||||
local n = byte(c)
|
||||
if n < 128 then
|
||||
-- ASCII
|
||||
return format("%%%02X", n)
|
||||
else
|
||||
-- Non-ASCII (encode as UTF-8)
|
||||
return format("%%%02X%%%02X", 192 + band(arshift(n, 6), 31), 128 + band(n, 63))
|
||||
end
|
||||
end)
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ local vector = {
|
||||
--
|
||||
-- @tparam Vector self The first vector to compute the dot product of.
|
||||
-- @tparam Vector o The second vector to compute the dot product of.
|
||||
-- @treturn Vector The dot product of `self` and `o`.
|
||||
-- @treturn number The dot product of `self` and `o`.
|
||||
-- @usage v1:dot(v2)
|
||||
dot = function(self, o)
|
||||
if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
# New features in CC: Tweaked 1.113.1
|
||||
|
||||
* Update Japanese translation (konumatakaki).
|
||||
* Improve performance of `textutils.urlEncode`.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix overflow when converting recursive objects from Java to Lua.
|
||||
* Fix websocket compression not working under Forge.
|
||||
|
||||
# New features in CC: Tweaked 1.113.0
|
||||
|
||||
* Allow placing printed pages and books in lecterns.
|
||||
|
||||
Several bug fixes:
|
||||
* Various documentation fixes (MCJack123)
|
||||
* Fix computers and turtles not being dropped when exploded with TNT.
|
||||
* Fix crash when turtles are broken while mining a block.
|
||||
* Fix pocket computer terminals not updating when in the off-hand.
|
||||
|
||||
# New features in CC: Tweaked 1.112.0
|
||||
|
||||
* Report a custom error when using `!` instead of `not`.
|
||||
@@ -800,7 +819,7 @@ And several bug fixes:
|
||||
# New features in CC: Tweaked 1.86.2
|
||||
|
||||
* Fix peripheral.getMethods returning an empty table.
|
||||
* Update to Minecraft 1.15.2. This is currently alpha-quality and so is missing missing features and may be unstable.
|
||||
* Update to Minecraft 1.15.2. This is currently alpha-quality and so is missing features and may be unstable.
|
||||
|
||||
# New features in CC: Tweaked 1.86.1
|
||||
|
||||
@@ -1416,7 +1435,7 @@ And several bug fixes:
|
||||
* Turtles can now compare items in their inventories
|
||||
* Turtles can place signs with text on them with `turtle.place( [signText] )`
|
||||
* Turtles now optionally require fuel items to move, and can refuel themselves
|
||||
* The size of the the turtle inventory has been increased to 16
|
||||
* The size of the turtle inventory has been increased to 16
|
||||
* The size of the turtle screen has been increased
|
||||
* New turtle functions: `turtle.compareTo( [slotNum] )`, `turtle.craft()`, `turtle.attack()`, `turtle.attackUp()`, `turtle.attackDown()`, `turtle.dropUp()`, `turtle.dropDown()`, `turtle.getFuelLevel()`, `turtle.refuel()`
|
||||
* New disk function: disk.getID()
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
New features in CC: Tweaked 1.112.0
|
||||
New features in CC: Tweaked 1.113.1
|
||||
|
||||
* Report a custom error when using `!` instead of `not`.
|
||||
* Update several translations (zyxkad, MineKID-LP).
|
||||
* Add `cc.strings.split` function.
|
||||
* Update Japanese translation (konumatakaki).
|
||||
* Improve performance of `textutils.urlEncode`.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix `drive.getAudioTitle` returning `nil` when no disk is inserted.
|
||||
* Preserve item data when upgrading pocket computers.
|
||||
* Add missing bounds check to `cc.strings.wrap` (Lupus950).
|
||||
* Fix modems not moving with Create contraptions.
|
||||
* Fix overflow when converting recursive objects from Java to Lua.
|
||||
* Fix websocket compression not working under Forge.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
Convert between streams of DFPWM audio data and a list of amplitudes.
|
||||
|
||||
DFPWM (Dynamic Filter Pulse Width Modulation) is an audio codec designed by GreaseMonkey. It's a relatively compact
|
||||
format compared to raw PCM data, only using 1 bit per sample, but is simple enough to simple enough to encode and decode
|
||||
in real time.
|
||||
format compared to raw PCM data, only using 1 bit per sample, but is simple enough to encode and decode in real time.
|
||||
|
||||
Typically DFPWM audio is read from [the filesystem][`fs.ReadHandle`] or a [a web request][`http.Response`] as a string,
|
||||
and converted a format suitable for [`speaker.playAudio`].
|
||||
|
||||
## Encoding and decoding files
|
||||
This modules exposes two key functions, [`make_decoder`] and [`make_encoder`], which construct a new decoder or encoder.
|
||||
This module exposes two key functions, [`make_decoder`] and [`make_encoder`], which construct a new decoder or encoder.
|
||||
The returned encoder/decoder is itself a function, which converts between the two kinds of data.
|
||||
|
||||
These encoders and decoders have lots of hidden state, so you should be careful to use the same encoder or decoder for
|
||||
@@ -21,9 +20,9 @@ a specific audio stream. Typically you will want to create a decoder for each st
|
||||
for each one you write.
|
||||
|
||||
## Converting audio to DFPWM
|
||||
DFPWM is not a popular file format and so standard audio processing tools will not have an option to export to it.
|
||||
DFPWM is not a popular file format and so standard audio processing tools may not have an option to export to it.
|
||||
Instead, you can convert audio files online using [music.madefor.cc], the [LionRay Wav Converter][LionRay] Java
|
||||
application or development builds of [FFmpeg].
|
||||
application or [FFmpeg] 5.1 or later.
|
||||
|
||||
[music.madefor.cc]: https://music.madefor.cc/ "DFPWM audio converter for Computronics and CC: Tweaked"
|
||||
[LionRay]: https://github.com/gamax92/LionRay/ "LionRay Wav Converter "
|
||||
@@ -211,7 +210,7 @@ end
|
||||
|
||||
--[[- A convenience function for encoding a complete file of audio at once.
|
||||
|
||||
This should only be used for complete pieces of audio. If you are writing writing multiple chunks to the same place,
|
||||
This should only be used for complete pieces of audio. If you are writing multiple chunks to the same place,
|
||||
you should use an encoder returned by [`make_encoder`] instead.
|
||||
|
||||
@tparam { number... } input The table of amplitude data.
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.time.Duration.ofSeconds;
|
||||
@@ -30,6 +33,26 @@ public class ComputerTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateObjects() {
|
||||
class CustomApi implements ILuaAPI {
|
||||
@Override
|
||||
public String[] getNames() {
|
||||
return new String[]{ "custom" };
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final Object[] getObjects() {
|
||||
return new Object[]{ List.of(), List.of() };
|
||||
}
|
||||
}
|
||||
|
||||
ComputerBootstrap.run("""
|
||||
local x, y = custom.getObjects()
|
||||
assert(x ~= y)
|
||||
""", i -> i.addApi(new CustomApi()), 50);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
var stream = ComputerTest.class.getClassLoader().getResourceAsStream("benchmark.lua");
|
||||
try (var reader = new InputStreamReader(Objects.requireNonNull(stream), StandardCharsets.UTF_8)) {
|
||||
|
||||
@@ -158,6 +158,65 @@ describe("The fs library", function()
|
||||
expect(fs.combine("", "a")):eq("a")
|
||||
expect(fs.combine("a", "", "b", "c")):eq("a/b/c")
|
||||
end)
|
||||
|
||||
it("preserves pattern characters", function()
|
||||
expect(fs.combine("foo*?")):eq("foo*?")
|
||||
end)
|
||||
|
||||
it("sanitises paths", function()
|
||||
expect(fs.combine("foo\":<>|")):eq("foo")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.getName", function()
|
||||
it("returns 'root' for the empty path", function()
|
||||
expect(fs.getName("")):eq("root")
|
||||
expect(fs.getName("foo/..")):eq("root")
|
||||
end)
|
||||
|
||||
it("returns the file name", function()
|
||||
expect(fs.getName("foo/bar")):eq("bar")
|
||||
expect(fs.getName("foo/bar/")):eq("bar")
|
||||
expect(fs.getName("../foo")):eq("foo")
|
||||
end)
|
||||
|
||||
it("returns '..' for parent directories", function()
|
||||
expect(fs.getName("..")):eq("..")
|
||||
end)
|
||||
|
||||
it("preserves pattern characters", function()
|
||||
expect(fs.getName("foo*?")):eq("foo*?")
|
||||
end)
|
||||
|
||||
it("sanitises paths", function()
|
||||
expect(fs.getName("foo\":<>|")):eq("foo")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.getDir", function()
|
||||
it("returns '..' for the empty path", function()
|
||||
expect(fs.getDir("")):eq("..")
|
||||
expect(fs.getDir("foo/..")):eq("..")
|
||||
end)
|
||||
|
||||
it("returns the directory name", function()
|
||||
expect(fs.getDir("foo/bar")):eq("foo")
|
||||
expect(fs.getDir("foo/bar/")):eq("foo")
|
||||
expect(fs.getDir("../foo")):eq("..")
|
||||
end)
|
||||
|
||||
it("returns '..' for parent directories", function()
|
||||
expect(fs.getDir("..")):eq("../..")
|
||||
expect(fs.getDir("../..")):eq("../../..")
|
||||
end)
|
||||
|
||||
it("preserves pattern characters", function()
|
||||
expect(fs.getDir("foo*?/x")):eq("foo*?")
|
||||
end)
|
||||
|
||||
it("sanitises paths", function()
|
||||
expect(fs.getDir("foo\":<>|/x")):eq("foo")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.getSize", function()
|
||||
@@ -200,6 +259,14 @@ describe("The fs library", function()
|
||||
handle.close()
|
||||
end)
|
||||
|
||||
it("reading an empty file returns nil", function()
|
||||
local file = create_test_file ""
|
||||
|
||||
local handle = fs.open(file, mode)
|
||||
expect(handle.read()):eq(nil)
|
||||
handle.close()
|
||||
end)
|
||||
|
||||
it("can read a line of text", function()
|
||||
local file = create_test_file "some\nfile\r\ncontents\n\n"
|
||||
|
||||
@@ -223,6 +290,16 @@ describe("The fs library", function()
|
||||
expect(handle.readLine(true)):eq(nil)
|
||||
handle.close()
|
||||
end)
|
||||
|
||||
it("readAll always returns a string", function()
|
||||
local contents = "some\nfile\ncontents"
|
||||
local file = create_test_file "some\nfile\ncontents"
|
||||
|
||||
local handle = fs.open(file, mode)
|
||||
expect(handle.readAll()):eq(contents)
|
||||
expect(handle.readAll()):eq("")
|
||||
handle.close()
|
||||
end)
|
||||
end
|
||||
|
||||
describe("reading", function()
|
||||
|
||||
@@ -188,4 +188,39 @@ describe("The os library", function()
|
||||
expect.error(os.loadAPI, nil):eq("bad argument #1 (string expected, got nil)")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("os.queueEvent", function()
|
||||
local function roundtrip(...)
|
||||
local event_name = ("event_%08x"):format(math.random(1, 0x7FFFFFFF))
|
||||
os.queueEvent(event_name, ...)
|
||||
return select(2, os.pullEvent(event_name))
|
||||
end
|
||||
|
||||
it("preserves references in tables", function()
|
||||
local tbl = {}
|
||||
local xs = roundtrip({ tbl, tbl })
|
||||
expect(xs[1]):eq(xs[2])
|
||||
end)
|
||||
|
||||
it("handles recursive tables", function()
|
||||
local tbl = {}
|
||||
tbl[1] = tbl
|
||||
|
||||
local xs = roundtrip(tbl)
|
||||
expect(xs):eq(xs[1])
|
||||
end)
|
||||
|
||||
it("does not preserve references in separate args", function()
|
||||
-- I'm not sure I like this behaviour, but it is what CC has always done.
|
||||
local tbl = {}
|
||||
local xs, ys = roundtrip(tbl, tbl)
|
||||
expect(xs):ne(ys)
|
||||
end)
|
||||
|
||||
it("clones objects", function()
|
||||
local tbl = {}
|
||||
local xs = roundtrip(tbl)
|
||||
expect(xs):ne(tbl)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user