1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-25 19:07:39 +00:00

Merge branch 'mc-1.20.x' into mc-1.21.x

This commit is contained in:
Jonathan Coates
2025-01-20 22:22:09 +00:00
53 changed files with 599 additions and 398 deletions

View File

@@ -40,6 +40,8 @@ dependencies {
}
tasks.processResources {
inputs.property("gitHash", cct.gitHash)
var props = mapOf("gitContributors" to cct.gitContributors.get().joinToString("\n"))
filesMatching("data/computercraft/lua/rom/help/credits.md") { expand(props) }
}

View File

@@ -31,7 +31,7 @@ import java.util.concurrent.atomic.AtomicLong;
* <li>Passes main thread tasks to the {@link MainThreadScheduler.Executor}.</li>
* </ul>
*/
public class Computer {
public class Computer implements ComputerEvents.Receiver {
private static final int START_DELAY = 50;
// Various properties of the computer
@@ -114,6 +114,7 @@ public class Computer {
executor.queueStop(false, true);
}
@Override
public void queueEvent(String event, @Nullable Object[] args) {
executor.queueEvent(event, args);
}

View File

@@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.core.computer;
import dan200.computercraft.core.util.StringUtil;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
/**
* Built-in events that can be queued on a computer.
*/
public final class ComputerEvents {
private ComputerEvents() {
}
public static void keyDown(Receiver receiver, int key, boolean repeat) {
receiver.queueEvent("key", new Object[]{ key, repeat });
}
public static void keyUp(Receiver receiver, int key) {
receiver.queueEvent("key_up", new Object[]{ key });
}
/**
* Type a character on the computer.
*
* @param receiver The computer to queue the event on.
* @param chr The character to type.
* @see StringUtil#isTypableChar(byte)
*/
public static void charTyped(Receiver receiver, byte chr) {
receiver.queueEvent("char", new Object[]{ new byte[]{ chr } });
}
/**
* Paste a string.
*
* @param receiver The computer to queue the event on.
* @param contents The string to paste.
* @see StringUtil#getClipboardString(String)
*/
public static void paste(Receiver receiver, ByteBuffer contents) {
receiver.queueEvent("paste", new Object[]{ contents });
}
public static void mouseClick(Receiver receiver, int button, int x, int y) {
receiver.queueEvent("mouse_click", new Object[]{ button, x, y });
}
public static void mouseUp(Receiver receiver, int button, int x, int y) {
receiver.queueEvent("mouse_up", new Object[]{ button, x, y });
}
public static void mouseDrag(Receiver receiver, int button, int x, int y) {
receiver.queueEvent("mouse_drag", new Object[]{ button, x, y });
}
public static void mouseScroll(Receiver receiver, int direction, int x, int y) {
receiver.queueEvent("mouse_scroll", new Object[]{ direction, x, y });
}
/**
* An object that can receive computer events.
*/
@FunctionalInterface
public interface Receiver {
void queueEvent(String event, @Nullable Object[] arguments);
}
}

View File

@@ -433,6 +433,16 @@ public final class ComputerThread implements ComputerScheduler {
return computerQueueSize() > idleWorkers.get();
}
/**
* Determine if no work is queued, and all workers are idle.
*
* @return If the threads are fully idle.
*/
@VisibleForTesting
boolean isFullyIdle() {
return computerQueueSize() == 0 && idleWorkers.get() >= workerCount();
}
private void workerFinished(WorkerThread worker) {
// We should only shut down a worker once! This should only happen if we fail to abort a worker and then the
// worker finishes normally.

View File

@@ -4,52 +4,116 @@
package dan200.computercraft.core.util;
import dan200.computercraft.core.computer.ComputerEvents;
import java.nio.ByteBuffer;
public final class StringUtil {
public static final int MAX_PASTE_LENGTH = 512;
private StringUtil() {
}
private static boolean isAllowed(char c) {
return (c >= ' ' && c <= '~') || (c >= 161 && c <= 172) || (c >= 174 && c <= 255);
}
private static String removeSpecialCharacters(String text, int length) {
var builder = new StringBuilder(length);
for (var i = 0; i < length; i++) {
var c = text.charAt(i);
builder.append(isAllowed(c) ? c : '?');
/**
* Convert a Unicode character to a terminal one.
*
* @param chr The Unicode character.
* @return The terminal character.
*/
public static byte unicodeToTerminal(int chr) {
// ASCII and latin1 map to themselves
if (chr == 0 || chr == '\t' || chr == '\n' || chr == '\r' || (chr >= ' ' && chr <= '~') || (chr >= 160 && chr <= 255)) {
return (byte) chr;
}
return builder.toString();
// Teletext block mosaics are *fairly* contiguous.
if (chr >= 0x1FB00 && chr <= 0x1FB13) return (byte) (chr + (129 - 0x1fb00));
if (chr >= 0x1FB14 && chr <= 0x1FB1D) return (byte) (chr + (150 - 0x1fb14));
// Everything else is just a manual lookup. For now, we just use a big switch statement, which we spin into a
// separate function to hopefully avoid inlining it here.
return unicodeToCraftOsFallback(chr);
}
public static String normaliseLabel(String text) {
return removeSpecialCharacters(text, Math.min(32, text.length()));
private static byte unicodeToCraftOsFallback(int c) {
return switch (c) {
case 0x263A -> 1;
case 0x263B -> 2;
case 0x2665 -> 3;
case 0x2666 -> 4;
case 0x2663 -> 5;
case 0x2660 -> 6;
case 0x2022 -> 7;
case 0x25D8 -> 8;
case 0x2642 -> 11;
case 0x2640 -> 12;
case 0x266A -> 14;
case 0x266B -> 15;
case 0x25BA -> 16;
case 0x25C4 -> 17;
case 0x2195 -> 18;
case 0x203C -> 19;
case 0x25AC -> 22;
case 0x21A8 -> 23;
case 0x2191 -> 24;
case 0x2193 -> 25;
case 0x2192 -> 26;
case 0x2190 -> 27;
case 0x221F -> 28;
case 0x2194 -> 29;
case 0x25B2 -> 30;
case 0x25BC -> 31;
case 0x1FB99 -> 127;
case 0x258C -> (byte) 149;
default -> '?';
};
}
/**
* Normalise a string from the clipboard, suitable for pasting into a computer.
* Check if a character is capable of being input and passed to a {@linkplain ComputerEvents#charTyped(ComputerEvents.Receiver, byte)
* "char" event}.
*
* @param chr The character to check.
* @return Whether this character can be typed.
*/
public static boolean isTypableChar(byte chr) {
return chr != 0 && chr != '\r' && chr != '\n';
}
private static boolean isAllowedInLabel(char c) {
// Limit to ASCII and latin1, excluding '§' (Minecraft's formatting character).
return (c >= ' ' && c <= '~') || (c >= 161 && c <= 255 && c != 167);
}
public static String normaliseLabel(String text) {
var length = Math.min(32, text.length());
var builder = new StringBuilder(length);
for (var i = 0; i < length; i++) {
var c = text.charAt(i);
builder.append(isAllowedInLabel(c) ? c : '?');
}
return builder.toString();
}
/**
* Convert a Java string to a Lua one (using the terminal charset), suitable for pasting into a computer.
* <p>
* This removes special characters and strips to the first line of text.
*
* @param clipboard The text from the clipboard.
* @return The normalised clipboard text.
* @return The encoded clipboard text.
*/
public static String normaliseClipboardString(String clipboard) {
// Clip to the first occurrence of \r or \n
var newLineIndex1 = clipboard.indexOf('\r');
var newLineIndex2 = clipboard.indexOf('\n');
public static ByteBuffer getClipboardString(String clipboard) {
var output = new byte[Math.min(MAX_PASTE_LENGTH, clipboard.length())];
var idx = 0;
int length;
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
length = Math.min(newLineIndex1, newLineIndex2);
} else if (newLineIndex1 >= 0) {
length = newLineIndex1;
} else if (newLineIndex2 >= 0) {
length = newLineIndex2;
} else {
length = clipboard.length();
var iterator = clipboard.codePoints().iterator();
while (iterator.hasNext() && idx <= output.length) {
var chr = unicodeToTerminal(iterator.next());
if (!isTypableChar(chr)) break;
output[idx++] = chr;
}
return removeSpecialCharacters(clipboard, Math.min(length, 512));
return ByteBuffer.wrap(output, 0, idx).asReadOnlyBuffer();
}
}