1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-25 06:52:17 +00:00

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

This commit is contained in:
Jonathan Coates 2025-05-16 18:38:21 +01:00
commit 0790a8346a
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
33 changed files with 258 additions and 285 deletions

View File

@ -26,7 +26,7 @@ slf4j = "2.0.16"
asm = "9.7.1"
autoService = "1.1.1"
checkerFramework = "3.42.0"
cobalt = { strictly = "0.9.5" }
cobalt = { strictly = "0.9.6" }
commonsCli = "1.6.0"
jetbrainsAnnotations = "24.1.0"
jspecify = "1.0.0"

View File

@ -59,10 +59,10 @@ public final class SelectUpgradeModel<T> implements TurtleUpgradeModel {
}
private TurtleUpgradeModel getModel(UpgradeData<ITurtleUpgrade> upgrade) {
var value = upgrade.data().get(component);
if (value == null || value.isEmpty()) return fallback;
var value = upgrade.get(component);
if (value == null) return fallback;
var model = cases.get(value.get());
var model = cases.get(value);
return model != null ? model : fallback;
}

View File

@ -7,8 +7,11 @@ package dan200.computercraft.api.upgrades;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.Nullable;
/**
* An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data.
@ -17,7 +20,9 @@ import net.minecraft.world.item.ItemStack;
* @param data The upgrade's data.
* @param <T> The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}.
*/
public record UpgradeData<T extends UpgradeBase>(Holder.Reference<T> holder, DataComponentPatch data) {
public record UpgradeData<T extends UpgradeBase>(
Holder.Reference<T> holder, DataComponentPatch data
) implements DataComponentGetter {
/**
* A utility method to construct a new {@link UpgradeData} instance.
*
@ -66,4 +71,17 @@ public record UpgradeData<T extends UpgradeBase>(Holder.Reference<T> holder, Dat
public ItemStack getUpgradeItem() {
return upgrade().getUpgradeItem(data).copy();
}
/**
* Get a component from the upgrade's {@link #data()} .
*
* @param component The component get.
* @param <U> The type of the component's value.
* @return The component, or {@code null} if not present.
*/
@Override
public <U> @Nullable U get(DataComponentType<? extends U> component) {
var result = data().get(component);
return result == null ? null : result.orElse(null);
}
}

View File

@ -191,7 +191,6 @@ public final class LanguageProvider implements DataProvider {
// Metrics
add(Metrics.COMPUTER_TASKS, "Tasks");
add(Metrics.SERVER_TASKS, "Server tasks");
add(Metrics.JAVA_ALLOCATION, "Java Allocations");
add(Metrics.PERIPHERAL_OPS, "Peripheral calls");
add(Metrics.FS_OPS, "Filesystem operations");
add(Metrics.HTTP_REQUESTS, "HTTP requests");

View File

@ -219,7 +219,6 @@
"tracking_field.computercraft.http_download.name": "HTTP download",
"tracking_field.computercraft.http_requests.name": "HTTP requests",
"tracking_field.computercraft.http_upload.name": "HTTP upload",
"tracking_field.computercraft.java_allocation.name": "Java Allocations",
"tracking_field.computercraft.max": "%s (max)",
"tracking_field.computercraft.peripheral.name": "Peripheral calls",
"tracking_field.computercraft.server_tasks.name": "Server tasks",

View File

@ -34,6 +34,7 @@ import dan200.computercraft.shared.computer.blocks.ComputerBlock;
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.core.TerminalSize;
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
import dan200.computercraft.shared.computer.items.ServerComputerReference;
import dan200.computercraft.shared.config.Config;
@ -360,6 +361,13 @@ public final class ModRegistry {
.persistent(StorageCapacity.CODEC).networkSynchronized(StorageCapacity.STREAM_CODEC)
);
/**
* The terminal size of a computer.
*/
public static final RegistryEntry<DataComponentType<TerminalSize>> TERMINAL_SIZE = register("terminal_size", b -> b
.persistent(TerminalSize.CODEC).networkSynchronized(TerminalSize.STREAM_CODEC)
);
/**
* The left upgrade of a turtle.
*

View File

@ -10,10 +10,16 @@ import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.core.TerminalSize;
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
@ -23,17 +29,52 @@ import net.minecraft.world.level.block.state.BlockState;
import org.jspecify.annotations.Nullable;
public class ComputerBlockEntity extends AbstractComputerBlockEntity {
private static final String NBT_TERMINAL_SIZE = "TerminalSize";
private @Nullable TerminalSize terminalSize;
private @Nullable IPeripheral peripheral;
public ComputerBlockEntity(BlockEntityType<? extends ComputerBlockEntity> type, BlockPos pos, BlockState state, ComputerFamily family) {
super(type, pos, state, family);
}
@Override
protected void loadServer(CompoundTag nbt, HolderLookup.Provider registries) {
super.loadServer(nbt, registries);
terminalSize = NBTUtil.decodeFrom(TerminalSize.CODEC, registries, nbt, NBT_TERMINAL_SIZE);
}
@Override
public void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) {
super.saveAdditional(nbt, registries);
NBTUtil.encodeTo(TerminalSize.CODEC, registries, nbt, NBT_TERMINAL_SIZE, terminalSize);
}
@Override
protected void applyImplicitComponents(DataComponentGetter component) {
super.applyImplicitComponents(component);
terminalSize = component.get(ModRegistry.DataComponents.TERMINAL_SIZE.get());
}
@Override
protected void collectSafeComponents(DataComponentMap.Builder builder) {
super.collectSafeComponents(builder);
builder.set(ModRegistry.DataComponents.TERMINAL_SIZE.get(), terminalSize);
}
@Override
@Deprecated
public void removeComponentsFromTag(CompoundTag tag) {
super.removeComponentsFromTag(tag);
tag.remove(NBT_TERMINAL_SIZE);
}
@Override
protected ServerComputer createComputer(int id) {
return new ServerComputer((ServerLevel) getLevel(), getBlockPos(), ServerComputer.properties(id, getFamily())
.label(getLabel())
.terminalSize(ConfigSpec.computerTermWidth.get(), ConfigSpec.computerTermHeight.get())
.terminalSize(terminalSize != null ? terminalSize : new TerminalSize(ConfigSpec.computerTermWidth.get(), ConfigSpec.computerTermHeight.get()))
.storageCapacity(storageCapacity)
);
}

View File

@ -295,10 +295,9 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
return this;
}
public Properties terminalSize(int width, int height) {
if (width <= 0 || height <= 0) throw new IllegalArgumentException("Terminal size must be positive");
this.terminalWidth = width;
this.terminalHeight = height;
public Properties terminalSize(TerminalSize size) {
this.terminalWidth = size.width();
this.terminalHeight = size.height();
return this;
}

View File

@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.computer.core;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.util.ExtraCodecs;
/**
* The size of a computer terminal.
*
* @param width The terminal's width.
* @param height The terminal's height.
*/
public record TerminalSize(int width, int height) {
public static final int MAX_SIZE = 255;
public static final Codec<TerminalSize> CODEC = RecordCodecBuilder.create(instance -> instance.group(
ExtraCodecs.intRange(1, MAX_SIZE).fieldOf("width").forGetter(TerminalSize::width),
ExtraCodecs.intRange(1, MAX_SIZE).fieldOf("height").forGetter(TerminalSize::height)
).apply(instance, TerminalSize::new));
public static final StreamCodec<ByteBuf, TerminalSize> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT, TerminalSize::width,
ByteBufCodecs.VAR_INT, TerminalSize::height,
TerminalSize::new
);
public TerminalSize {
checkBounds("width", width);
checkBounds("height", height);
}
private static void checkBounds(String name, int value) {
if (value < 1 || value > MAX_SIZE) {
throw new IllegalArgumentException(name + " must be between 1 and " + MAX_SIZE);
}
}
}

View File

@ -11,6 +11,7 @@ import dan200.computercraft.core.Logging;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.ProxyType;
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
import dan200.computercraft.shared.computer.core.TerminalSize;
import dan200.computercraft.shared.platform.PlatformHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Filter;
@ -342,13 +343,13 @@ public final class ConfigSpec {
.push("term_sizes");
builder.comment("Terminal size of computers.").push("computer");
computerTermWidth = builder.comment("Width of computer terminal").defineInRange("width", Config.DEFAULT_COMPUTER_TERM_WIDTH, 1, 255);
computerTermHeight = builder.comment("Height of computer terminal").defineInRange("height", Config.DEFAULT_COMPUTER_TERM_HEIGHT, 1, 255);
computerTermWidth = builder.comment("Width of computer terminal").defineInRange("width", Config.DEFAULT_COMPUTER_TERM_WIDTH, 1, TerminalSize.MAX_SIZE);
computerTermHeight = builder.comment("Height of computer terminal").defineInRange("height", Config.DEFAULT_COMPUTER_TERM_HEIGHT, 1, TerminalSize.MAX_SIZE);
builder.pop();
builder.comment("Terminal size of pocket computers.").push("pocket_computer");
pocketTermWidth = builder.comment("Width of pocket computer terminal").defineInRange("width", Config.DEFAULT_POCKET_TERM_WIDTH, 1, 255);
pocketTermHeight = builder.comment("Height of pocket computer terminal").defineInRange("height", Config.DEFAULT_POCKET_TERM_HEIGHT, 1, 255);
pocketTermWidth = builder.comment("Width of pocket computer terminal").defineInRange("width", Config.DEFAULT_POCKET_TERM_WIDTH, 1, TerminalSize.MAX_SIZE);
pocketTermHeight = builder.comment("Height of pocket computer terminal").defineInRange("height", Config.DEFAULT_POCKET_TERM_HEIGHT, 1, TerminalSize.MAX_SIZE);
builder.pop();
builder.comment("Maximum size of monitors (in blocks).").push("monitor");

View File

@ -7,7 +7,6 @@ package dan200.computercraft.shared.pocket.core;
import dan200.computercraft.api.component.ComputerComponents;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage;
import dan200.computercraft.shared.network.server.ServerNetworking;
@ -40,10 +39,7 @@ public final class PocketServerComputer extends ServerComputer {
private Set<ServerPlayer> tracking = Set.of();
PocketServerComputer(PocketBrain brain, PocketHolder holder, ServerComputer.Properties properties) {
super(holder.level(), holder.blockPos(), properties
.terminalSize(ConfigSpec.pocketTermWidth.get(), ConfigSpec.pocketTermHeight.get())
.addComponent(ComputerComponents.POCKET, brain)
);
super(holder.level(), holder.blockPos(), properties.addComponent(ComputerComponents.POCKET, brain));
this.brain = brain;
}

View File

@ -10,12 +10,10 @@ import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.impl.UpgradeManager;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
import dan200.computercraft.shared.computer.core.ServerContext;
import dan200.computercraft.shared.computer.core.*;
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
import dan200.computercraft.shared.computer.items.ServerComputerReference;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.pocket.core.PocketBrain;
@ -186,6 +184,10 @@ public class PocketComputerItem extends Item {
var brain = new PocketBrain(holder, ServerComputer.properties(computerID, getFamily())
.label(getLabel(stack))
.storageCapacity(StorageCapacity.getOrDefault(stack.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), -1))
.terminalSize(stack.getOrDefault(
ModRegistry.DataComponents.TERMINAL_SIZE.get(),
new TerminalSize(ConfigSpec.pocketTermWidth.get(), ConfigSpec.pocketTermHeight.get())
))
);
brain.setUpgrades(getUpgradeWithData(stack, PocketSide.BACK), getUpgradeWithData(stack, PocketSide.BOTTOM));
var computer = brain.computer();

View File

@ -18,6 +18,7 @@ import dan200.computercraft.shared.computer.blocks.ComputerPeripheral;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.core.TerminalSize;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.container.BasicContainer;
import dan200.computercraft.shared.platform.PlatformHelper;
@ -84,7 +85,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
protected ServerComputer createComputer(int id) {
var computer = new ServerComputer((ServerLevel) getLevel(), getBlockPos(), ServerComputer.properties(id, getFamily())
.label(getLabel())
.terminalSize(Config.TURTLE_TERM_WIDTH, Config.TURTLE_TERM_HEIGHT)
.terminalSize(new TerminalSize(Config.TURTLE_TERM_WIDTH, Config.TURTLE_TERM_HEIGHT))
.storageCapacity(storageCapacity)
.addComponent(ComputerComponents.TURTLE, brain)
);

View File

@ -99,7 +99,9 @@ public final class TurtleUpgradeRecipe extends CustomRecipe {
TurtleItem.getUpgradeWithData(turtle, TurtleSide.RIGHT),
};
// Get the upgrades for the new items
// Get the upgrades for the new items.
// Note: because the turtle is facing towards us, the directions are flipped. Items placed to the left
// of the turtle, are equipped on its right (and vice versa).
var items = new ItemStack[]{ rightItem, leftItem };
for (var i = 0; i < 2; i++) {
if (!items[i].isEmpty()) {

View File

@ -164,7 +164,7 @@ public final class TickScheduler {
UNLOADED,
}
private record ChunkReference(ResourceKey<Level> level, Long position) {
private record ChunkReference(ResourceKey<Level> level, long position) {
@Override
public String toString() {
return "ChunkReference(" + level + " at " + new ChunkPos(position) + ")";

View File

@ -217,7 +217,6 @@
"tracking_field.computercraft.http_download.name": "Stahování HTTP",
"tracking_field.computercraft.http_requests.name": "Požadavky HTTP",
"tracking_field.computercraft.http_upload.name": "Nahrávání HTTP",
"tracking_field.computercraft.java_allocation.name": "Java alokace",
"tracking_field.computercraft.max": "%s (max)",
"tracking_field.computercraft.peripheral.name": "Periferní volání",
"tracking_field.computercraft.server_tasks.name": "Serverové úlohy",

View File

@ -206,7 +206,6 @@
"tracking_field.computercraft.http_download.name": "Descarga HTTP",
"tracking_field.computercraft.http_requests.name": "Peticiones HTTP",
"tracking_field.computercraft.http_upload.name": "Subida HTTP",
"tracking_field.computercraft.java_allocation.name": "Asignaciones de Java",
"tracking_field.computercraft.max": "%s (max)",
"tracking_field.computercraft.peripheral.name": "Llamadas del periférico",
"tracking_field.computercraft.server_tasks.name": "Tareas del servidor",

View File

@ -215,7 +215,6 @@
"tracking_field.computercraft.http_download.name": "Téléchargement HTTP",
"tracking_field.computercraft.http_requests.name": "Requêtes HTTP",
"tracking_field.computercraft.http_upload.name": "Publication HTTP",
"tracking_field.computercraft.java_allocation.name": "Allocations Java",
"tracking_field.computercraft.max": "%s (max)",
"tracking_field.computercraft.peripheral.name": "Appels aux périphériques",
"tracking_field.computercraft.server_tasks.name": "Tâches du serveur",

View File

@ -12,9 +12,12 @@
"block.computercraft.cable": "Cavo Di Rete",
"block.computercraft.computer_advanced": "Computer Avanzato",
"block.computercraft.computer_command": "Computer Comando",
"block.computercraft.computer_normal": "Computer",
"block.computercraft.disk_drive": "Lettore Di Dischi",
"block.computercraft.monitor_advanced": "Monitor Avanzato",
"block.computercraft.monitor_normal": "Monitor",
"block.computercraft.printer": "Stampante",
"block.computercraft.redstone_relay": "Relè di redstone",
"block.computercraft.speaker": "Altoparlante",
"block.computercraft.turtle_advanced": "Tartaruga Avanzata",
"block.computercraft.turtle_advanced.upgraded": "Tartaruga %s Avanzata",
@ -35,6 +38,7 @@
"commands.computercraft.dump.synopsis": "Mostra lo stato dei computer.",
"commands.computercraft.generic.additional_rows": "%d colonne aggiuntive…",
"commands.computercraft.generic.exception": "Eccezione non gestita (%s)",
"commands.computercraft.generic.no": "N",
"commands.computercraft.generic.yes": "S",
"commands.computercraft.help.desc": "Mostra questo messaggio d'aiuto",
"commands.computercraft.help.no_children": "%s non ha sottocomandi",
@ -50,6 +54,7 @@
"commands.computercraft.tp.desc": "Teletrasporta alla posizione di un computer. Puoi specificare il computer con l'instance id (e.g. 123) o con l'id (e.g. #123).",
"commands.computercraft.tp.synopsis": "Teletrasporta al computer specificato.",
"commands.computercraft.track.desc": "Monitora per quanto tempo i computer vengono eseguiti e quanti eventi ricevono. Questo comando fornisce le informazioni in modo simile a /forge track e può essere utile per diagnosticare il lag.",
"commands.computercraft.track.dump.computer": "Computer",
"commands.computercraft.track.dump.desc": "Cancella gli ultimi risultati del monitoraggio dei computer.",
"commands.computercraft.track.dump.no_timings": "No ci sono tempi disponibili",
"commands.computercraft.track.dump.synopsis": "Cancella gli ultimi risultati monitorati",
@ -86,6 +91,7 @@
"gui.computercraft.config.execution.tooltip": "Controlla comportamento esecuzione dei computer. Questo è largamente utilizzato\nper ritoccare la performance dei server, e generale non dovrebbe essere toccato.",
"gui.computercraft.config.floppy_space_limit": "Limite spazio Disco Floppy (bytes)",
"gui.computercraft.config.floppy_space_limit.tooltip": "Limite di spazio di archiviazione per i dischi floppy, in byte.",
"gui.computercraft.config.http": "HTTP",
"gui.computercraft.config.http.bandwidth": "Banda larga",
"gui.computercraft.config.http.bandwidth.global_download": "Limite download globale",
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Numero di byte che possono essere scaricati in un secondo. Questo è condiviso tra tutti i computer. (bytes/s).",
@ -98,6 +104,7 @@
"gui.computercraft.config.http.max_requests.tooltip": "Il numero di richieste http che un computer può fare alla volta. Ulteriori richieste verranno messe in coda, ed inviate quando le richieste correnti sono terminate. Imposta a 0 per illimitato.",
"gui.computercraft.config.http.max_websockets": "Connessioni websocket massime",
"gui.computercraft.config.http.max_websockets.tooltip": "Il numero di websocket che un computer può avere aperte allo stesso momento.Imposta a 0 per illimitato.",
"gui.computercraft.config.http.proxy": "Proxy",
"gui.computercraft.config.http.proxy.host": "Nome host",
"gui.computercraft.config.http.proxy.host.tooltip": "Il nome dell'host o l'indirizzo IP del server proxy.",
"gui.computercraft.config.http.proxy.port": "Porta",
@ -135,16 +142,24 @@
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "Il limite di quanti dati dei monitor possono essere inviati *al tick*. Nota:\n - La banda larga è misurata prima della compressione, così che il dato inviato al client è\n più piccolo.\n - Questo ignora il numero di giocatori a cui viene inviato il pacchetto. Aggiornare un monitor\n per un giocatore consuma lo stesso limite di banda larga dell'invio a 20 giocatori.\n - Un monitor alla massima grandezza invia ~25kb di dati. Quindi il valore predefinito (1MB) concede\n ~40 monitor di essere aggiornati in un singolo tick.\nImposta a 0 per disattivare.",
"gui.computercraft.config.peripheral.tooltip": "Opzioni varie riguardo le periferiche.",
"gui.computercraft.config.term_sizes": "Dimensioni terminale",
"gui.computercraft.config.term_sizes.computer": "Computer",
"gui.computercraft.config.term_sizes.computer.height": "Altezza terminale",
"gui.computercraft.config.term_sizes.computer.height.tooltip": "Altezza del terminale del computer",
"gui.computercraft.config.term_sizes.computer.tooltip": "Dimensioni del terminale dei computer.",
"gui.computercraft.config.term_sizes.computer.width": "Larghezza terminale",
"gui.computercraft.config.term_sizes.computer.width.tooltip": "Larghezza del terminale del computer",
"gui.computercraft.config.term_sizes.monitor": "Monitor",
"gui.computercraft.config.term_sizes.monitor.height": "Massima altezza del monitor",
"gui.computercraft.config.term_sizes.monitor.height.tooltip": "Altezza massima dei monitor",
"gui.computercraft.config.term_sizes.monitor.tooltip": "Massima grandezza dei monitor (in blocchi).",
"gui.computercraft.config.term_sizes.monitor.width": "Larghezza massima del monitor",
"gui.computercraft.config.term_sizes.monitor.width.tooltip": "Larghezza massima dei monitor",
"gui.computercraft.config.term_sizes.pocket_computer": "Computer Tascabile",
"gui.computercraft.config.term_sizes.pocket_computer.height": "Altezza terminale",
"gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "Altezza del terminale dei computer tascabili",
"gui.computercraft.config.term_sizes.pocket_computer.tooltip": "Dimensioni del terminale dei computer tascabili.",
"gui.computercraft.config.term_sizes.pocket_computer.width": "Larghezza terminale",
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "Larghezza del terminale del computer tascabile",
"gui.computercraft.config.term_sizes.tooltip": "Configura le dimensioni dei terminali di vari computer.\nTerminali più grandi richiedono più banda larga, usa con cautela.",
"gui.computercraft.config.turtle": "Tartarughe",
"gui.computercraft.config.turtle.advanced_fuel_limit": "Limite carburante tartarughe avanzate",
@ -188,6 +203,7 @@
"item.computercraft.printed_page": "Pagina Stampata",
"item.computercraft.printed_pages": "Pagine Stampate",
"item.computercraft.treasure_disk": "Disco Floppy",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "Computer",
"tag.item.computercraft.monitor": "Monitor",
"tag.item.computercraft.turtle": "Tartarughe",
@ -199,7 +215,6 @@
"tracking_field.computercraft.http_download.name": "Download HTTP",
"tracking_field.computercraft.http_requests.name": "Richieste HTTP",
"tracking_field.computercraft.http_upload.name": "Upload HTTP",
"tracking_field.computercraft.java_allocation.name": "Allocazioni Java",
"tracking_field.computercraft.max": "%s (massimo)",
"tracking_field.computercraft.peripheral.name": "Chiamate alle periferiche",
"tracking_field.computercraft.server_tasks.name": "Attività server",
@ -207,6 +222,8 @@
"tracking_field.computercraft.websocket_incoming.name": "Websocket in arrivo",
"tracking_field.computercraft.websocket_outgoing.name": "Websocket in uscita",
"upgrade.computercraft.speaker.adjective": "Rumoroso",
"upgrade.computercraft.wireless_modem_advanced.adjective": "Ender",
"upgrade.computercraft.wireless_modem_normal.adjective": "Senza fili",
"upgrade.minecraft.crafting_table.adjective": "Artigiana",
"upgrade.minecraft.diamond_axe.adjective": "Taglialegna",
"upgrade.minecraft.diamond_hoe.adjective": "Contadina",

View File

@ -217,7 +217,6 @@
"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": "サーバータスク",

View File

@ -208,7 +208,6 @@
"tracking_field.computercraft.http_download.name": "Download HTTP",
"tracking_field.computercraft.http_requests.name": "Solicitações HTTP",
"tracking_field.computercraft.http_upload.name": "Upload de HTTP",
"tracking_field.computercraft.java_allocation.name": "Alocações Java",
"tracking_field.computercraft.max": "%s (máximo)",
"tracking_field.computercraft.peripheral.name": "Chamadas Periféricas",
"tracking_field.computercraft.server_tasks.name": "Tarefas do servidor",

View File

@ -109,6 +109,7 @@
"gui.computercraft.config.log_computer_errors": "Регистрировать ошибки компьютера",
"gui.computercraft.config.log_computer_errors.tooltip": "Регистрировать исключения, вызванные периферийными устройствами и другими объектами Lua. Это облегчает\nдля авторам модов устранение проблем, но может привести к спаму в логах, если люди будут использовать\nглючные методы.",
"gui.computercraft.config.maximum_open_files": "Максимальное количество файлов, открытых на одном компьютере",
"gui.computercraft.config.peripheral": "Периферия",
"gui.computercraft.config.term_sizes": "Размер терминала",
"gui.computercraft.config.term_sizes.computer": "Компьютер",
"gui.computercraft.config.term_sizes.computer.height": "Высота терминала",

View File

@ -1,8 +1,14 @@
{
"argument.computercraft.argument_expected": "Argument förväntas",
"argument.computercraft.computer.distance": "Distans till entitet",
"argument.computercraft.computer.family": "Datorfamilj",
"argument.computercraft.computer.id": "Dator-ID",
"argument.computercraft.computer.instance": "Unikt instans-ID",
"argument.computercraft.computer.label": "Datoretikett",
"argument.computercraft.computer.many_matching": "Flera datorer matchar '%s' (%s träffar)",
"argument.computercraft.computer.no_matching": "Inga datorer matchar '%s'",
"argument.computercraft.tracking_field.no_field": "Okänt fält '%s'",
"argument.computercraft.unknown_computer_family": "Okänd datorfamilj '%s'",
"block.computercraft.cable": "Nätverkskabel",
"block.computercraft.computer_advanced": "Avancerad Dator",
"block.computercraft.computer_command": "Kommandodator",
@ -66,7 +72,10 @@
"commands.computercraft.view.desc": "Öppna datorns terminal för att möjligöra fjärrstyrning. Detta ger inte tillgång till turtlens inventory. Du kan ange en dators instans-id (t.ex. 123) eller dator-id (t.ex. #123).",
"commands.computercraft.view.not_player": "Kan inte öppna terminalen för en ickespelare",
"commands.computercraft.view.synopsis": "Titta på datorns terminal.",
"gui.computercraft.config.command_require_creative": "Kommandodatorer kräver kreativt läge",
"gui.computercraft.config.command_require_creative.tooltip": "Kräv att spelare är i kreativt läge och har operatörsrättigheter för att kunna interagera med kommandodatorer.\nDetta är standardbeteendet för vaniljversionens kommandoblock.",
"gui.computercraft.config.computer_space_limit": "Dator maximalt utrymme (bytes)",
"gui.computercraft.config.computer_space_limit.tooltip": "Gränsen för diskutrymme för datorer och sköldpaddor, i bytes.",
"gui.computercraft.config.default_computer_settings": "Standard Datorinställningar",
"gui.computercraft.config.execution.computer_threads": "Dator trådar",
"gui.computercraft.config.floppy_space_limit": "Diskett maximalt utrymme (bytes)",

View File

@ -217,7 +217,6 @@
"tracking_field.computercraft.http_download.name": "HTTP indirme",
"tracking_field.computercraft.http_requests.name": "HTTP istekleri",
"tracking_field.computercraft.http_upload.name": "HTTP yükleme",
"tracking_field.computercraft.java_allocation.name": "Java'ya Ayrılan",
"tracking_field.computercraft.max": "%s (azami)",
"tracking_field.computercraft.peripheral.name": "Çevre birimi çağrıları",
"tracking_field.computercraft.server_tasks.name": "Sunucu görevleri",

View File

@ -215,7 +215,6 @@
"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": "Серверних задач",

View File

@ -217,7 +217,6 @@
"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": "服务器任务",

View File

@ -29,7 +29,7 @@ public class ItemStackMatcher extends TypeSafeMatcher<ItemStack> {
@Override
protected void describeMismatchSafely(ItemStack item, Description description) {
description.appendText("was ").appendValue(stack).appendValue(stack.getComponentsPatch());
description.appendText("was ").appendValue(item).appendValue(item.getComponentsPatch());
}
public static Matcher<ItemStack> isStack(ItemStack stack) {

View File

@ -15,6 +15,7 @@ import dan200.computercraft.api.upgrades.UpgradeData
import dan200.computercraft.core.apis.PeripheralAPI
import dan200.computercraft.gametest.api.*
import dan200.computercraft.gametest.core.TestHooks
import dan200.computercraft.impl.TurtleUpgrades
import dan200.computercraft.mixin.gametest.GameTestHelperAccessor
import dan200.computercraft.mixin.gametest.GameTestInfoAccessor
import dan200.computercraft.shared.ModRegistry
@ -31,6 +32,7 @@ import dan200.computercraft.shared.util.WaterloggableHelpers
import dan200.computercraft.test.core.assertArrayEquals
import dan200.computercraft.test.core.computer.LuaTaskContext
import dan200.computercraft.test.core.computer.getApi
import dan200.computercraft.test.shared.ItemStackMatcher.isStack
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.gametest.framework.GameTestHelper
@ -971,6 +973,86 @@ class Turtle_Test {
thenWaitUntil { helper.assertContainerLikeEmpty(BlockPos(2, 2, 2)) }
}
/**
* Test that turtles can be crafted with upgrades.
*/
@GameTest(template = Structures.DEFAULT)
fun Can_upgrades_be_crafted(helper: GameTestHelper) = helper.immediate {
fun turtle(left: UpgradeData<ITurtleUpgrade>? = null, right: UpgradeData<ITurtleUpgrade>? = null): ItemStack {
val stack = ItemStack(ModRegistry.Items.TURTLE_NORMAL.get())
if (left != null) stack.set(ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get(), left)
if (right != null) stack.set(ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), right)
return stack
}
val pick = TurtleUpgrades.instance().get(helper.level.registryAccess(), ItemStack(Items.DIAMOND_PICKAXE))!!
val sword = TurtleUpgrades.instance().get(helper.level.registryAccess(), ItemStack(Items.DIAMOND_SWORD))!!
// Check we can craft with upgrades
assertThat(
"Craft with item on left equips on right",
helper.craftItem(
ItemStack(Items.DIAMOND_PICKAXE), turtle(), ItemStack.EMPTY,
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
),
isStack(turtle(right = pick)),
)
assertThat(
"Craft with item on right equips on left",
helper.craftItem(
ItemStack.EMPTY, turtle(), ItemStack(Items.DIAMOND_PICKAXE),
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
),
isStack(turtle(left = pick)),
)
assertThat(
"Craft with two items",
helper.craftItem(
ItemStack(Items.DIAMOND_SWORD), turtle(), ItemStack(Items.DIAMOND_PICKAXE),
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
),
isStack(turtle(left = pick, right = sword)),
)
assertThat(
"Maintains upgrades",
helper.craftItem(
ItemStack(Items.DIAMOND_SWORD), turtle(left = pick), ItemStack.EMPTY,
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
),
isStack(turtle(left = pick, right = sword)),
)
// Cannot craft when already have item
helper.assertNotCraftable(
ItemStack.EMPTY, turtle(left = sword), ItemStack(Items.DIAMOND_PICKAXE),
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
)
// Cannot craft with an invalid upgrade
helper.assertNotCraftable(
ItemStack.EMPTY, turtle(), ItemStack(Items.DIRT),
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
)
// Cannot craft with extra items in the inventory
helper.assertNotCraftable(
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack(Items.DIRT),
ItemStack.EMPTY, turtle(), ItemStack(Items.DIAMOND_PICKAXE),
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
)
helper.assertNotCraftable(
ItemStack.EMPTY, turtle(), ItemStack(Items.DIAMOND_PICKAXE),
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack(Items.DIRT),
ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY,
)
}
/**
* Render turtles as an item.
*/

View File

@ -12,13 +12,11 @@ import dan200.computercraft.core.Logging;
import dan200.computercraft.core.computer.TimeoutState;
import dan200.computercraft.core.metrics.Metrics;
import dan200.computercraft.core.metrics.MetricsObserver;
import dan200.computercraft.core.metrics.ThreadAllocations;
import dan200.computercraft.core.util.ThreadUtils;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Objects;
import java.util.TreeSet;
import java.util.concurrent.ThreadFactory;
@ -489,9 +487,6 @@ public final class ComputerThread implements ComputerScheduler {
}
private void runImpl() {
var workerThreadIds = new long[workersReadOnly().length];
Arrays.fill(workerThreadIds, Thread.currentThread().threadId());
while (state.get() < CLOSED) {
computerLock.lock();
try {
@ -506,33 +501,12 @@ public final class ComputerThread implements ComputerScheduler {
computerLock.unlock();
}
checkRunners(workerThreadIds);
checkRunners();
}
}
private void checkRunners(long[] workerThreadIds) {
var workers = workersReadOnly();
long[] allocations;
if (ThreadAllocations.isSupported()) {
// If allocation tracking is supported, update the current thread IDs and then fetch the total allocated
// memory. When dealing with multiple workers, it's more efficient to getAllocatedBytes in bulk rather
// than, hence doing it within the worker loop.
// However, this does mean we need to maintain an array of worker thread IDs. We could have a shared
// array and update it within .addWorker(_), but that's got all sorts of thread-safety issues. It ends
// up being easier (and not too inefficient) to just recompute the array each time.
for (var i = 0; i < workers.length; i++) {
var runner = workers[i];
if (runner != null) workerThreadIds[i] = runner.owner.threadId();
}
allocations = ThreadAllocations.getAllocatedBytes(workerThreadIds);
} else {
allocations = null;
}
var allocationTime = System.nanoTime();
for (var i = 0; i < workers.length; i++) {
var runner = workers[i];
private void checkRunners() {
for (var runner : workersReadOnly()) {
if (runner == null) continue;
// If the worker has no work, skip
@ -542,11 +516,6 @@ public final class ComputerThread implements ComputerScheduler {
// Refresh the timeout state. Will set the pause/soft timeout flags as appropriate.
executor.timeout.refresh();
// And track the allocated memory.
if (allocations != null) {
executor.updateAllocations(new ThreadAllocation(workerThreadIds[i], allocations[i], allocationTime));
}
// If we're still within normal execution times (TIMEOUT) or soft abort (ABORT_TIMEOUT),
// then we can let the Lua machine do its work.
var remainingTime = executor.timeout.getRemainingTime();
@ -774,9 +743,6 @@ public final class ComputerThread implements ComputerScheduler {
public static final AtomicReferenceFieldUpdater<ExecutorImpl, ExecutorState> STATE = AtomicReferenceFieldUpdater.newUpdater(
ExecutorImpl.class, ExecutorState.class, "$state"
);
public static final AtomicReferenceFieldUpdater<ExecutorImpl, ThreadAllocation> THREAD_ALLOCATION = AtomicReferenceFieldUpdater.newUpdater(
ExecutorImpl.class, ThreadAllocation.class, "$threadAllocation"
);
final Worker worker;
private final MetricsObserver metrics;
@ -788,17 +754,6 @@ public final class ComputerThread implements ComputerScheduler {
@Keep
private volatile ExecutorState $state = ExecutorState.IDLE;
/**
* Information about allocations on the currently executing thread.
* <p>
* {@linkplain #beforeWork() Before starting any work}, we set this to the current thread and the current
* {@linkplain ThreadAllocations#getAllocatedBytes(long) amount of allocated memory}. When the computer
* {@linkplain #afterWork()} finishes executing, we set this back to null and compute the difference between the
* two, updating the {@link Metrics#JAVA_ALLOCATION} metric.
*/
@Keep
private volatile @Nullable ThreadAllocation $threadAllocation = null;
/**
* The amount of time this computer has used on a theoretical machine which shares work evenly amongst computers.
*
@ -825,11 +780,6 @@ public final class ComputerThread implements ComputerScheduler {
void beforeWork() {
vRuntimeStart = System.nanoTime();
timeout.startTimer(scaledPeriod());
if (ThreadAllocations.isSupported()) {
var current = Thread.currentThread().threadId();
THREAD_ALLOCATION.set(this, new ThreadAllocation(current, ThreadAllocations.getAllocatedBytes(current), System.nanoTime()));
}
}
/**
@ -841,55 +791,10 @@ public final class ComputerThread implements ComputerScheduler {
timeout.reset();
metrics.observe(Metrics.COMPUTER_TASKS, timeout.getExecutionTime());
if (ThreadAllocations.isSupported()) {
var current = Thread.currentThread().threadId();
var info = THREAD_ALLOCATION.getAndSet(this, null);
assert info.threadId() == current;
var allocatedTotal = ThreadAllocations.getAllocatedBytes(current);
var allocated = allocatedTotal - info.allocatedBytes();
if (allocated > 0) {
metrics.observe(Metrics.JAVA_ALLOCATION, allocated);
} else if (allocated < 0) {
LOG.warn(
"""
Allocated a negative number of bytes ({})!
Previous measurement at t={} on Thread #{} = {}
Current measurement at t={} on Thread #{} = {}""",
allocated,
info.time(), info.threadId(), info.allocatedBytes(),
System.nanoTime(), current, allocatedTotal
);
}
}
var state = STATE.getAndUpdate(this, ExecutorState::requeue);
return state == ExecutorState.REPEAT;
}
/**
* Update the per-thread allocation information.
*
* @param allocation The latest allocation information.
*/
void updateAllocations(ThreadAllocation allocation) {
ThreadAllocation current;
long allocated;
do {
// Probe the current information - if it's null or the thread has changed, then the worker has already
// finished and this information is out-of-date, so just abort.
current = THREAD_ALLOCATION.get(this);
if (current == null || current.threadId() != allocation.threadId()) return;
// Then compute the difference since the previous measurement. If the new value is less than the current
// one, then it must be out-of-date. Again, just abort.
allocated = allocation.allocatedBytes() - current.allocatedBytes();
if (allocated <= 0) return;
} while (!THREAD_ALLOCATION.compareAndSet(this, current, allocation));
metrics.observe(Metrics.JAVA_ALLOCATION, allocated);
}
@Override
public void submit() {
var state = STATE.getAndUpdate(this, ExecutorState::enqueue);
@ -918,14 +823,4 @@ public final class ComputerThread implements ComputerScheduler {
return hasPendingWork();
}
}
/**
* Allocation information about a specific thread.
*
* @param threadId The ID of this thread.
* @param allocatedBytes The amount of memory this thread has allocated.
* @param time The time (in nanoseconds) when this time was computed.
*/
private record ThreadAllocation(long threadId, long allocatedBytes, long time) {
}
}

View File

@ -77,7 +77,7 @@ public class CobaltLuaMachine implements ILuaMachine {
try {
var globals = state.globals();
CoreLibraries.debugGlobals(state);
Bit32Lib.add(state, globals);
Bit32Lib.add(state);
ErrorInfoLib.add(state);
globals.rawset("_HOST", ValueFactory.valueOf(environment.hostString()));
globals.rawset("_CC_DEFAULT_SETTINGS", ValueFactory.valueOf(CoreConfig.defaultComputerSettings));

View File

@ -14,8 +14,6 @@ public final class Metrics {
public static final Metric.Event COMPUTER_TASKS = new Metric.Event("computer_tasks", "ns", Metric::formatTime);
public static final Metric.Event SERVER_TASKS = new Metric.Event("server_tasks", "ns", Metric::formatTime);
public static final Metric.Event JAVA_ALLOCATION = new Metric.Event("java_allocation", "bytes", Metric::formatBytes);
public static final Metric.Event PERIPHERAL_OPS = new Metric.Event("peripheral", "ns", Metric::formatTime);
public static final Metric.Event FS_OPS = new Metric.Event("fs", "ns", Metric::formatTime);

View File

@ -1,114 +0,0 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.core.metrics;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
/**
* Provides a way to get the memory allocated by a specific thread.
* <p>
* This uses Hotspot-specific functionality, so may not be available on all JVMs. Consumers should call
* {@link #isSupported()} before calling more specific methods.
*
* @see com.sun.management.ThreadMXBean
*/
public final class ThreadAllocations {
private static final Logger LOG = LoggerFactory.getLogger(ThreadAllocations.class);
private static final @Nullable MethodHandle threadAllocatedBytes;
private static final @Nullable MethodHandle threadsAllocatedBytes;
static {
MethodHandle threadAllocatedBytesHandle, threadsAllocatedBytesHandle;
try {
var threadMxBean = Class.forName("com.sun.management.ThreadMXBean").asSubclass(ThreadMXBean.class);
var bean = ManagementFactory.getPlatformMXBean(threadMxBean);
// Enable allocation tracking.
threadMxBean.getMethod("setThreadAllocatedMemoryEnabled", boolean.class).invoke(bean, true);
// Just probe this method once to check it doesn't error.
threadMxBean.getMethod("getCurrentThreadAllocatedBytes").invoke(bean);
threadAllocatedBytesHandle = MethodHandles.publicLookup()
.findVirtual(threadMxBean, "getThreadAllocatedBytes", MethodType.methodType(long.class, long.class))
.bindTo(bean);
threadsAllocatedBytesHandle = MethodHandles.publicLookup()
.findVirtual(threadMxBean, "getThreadAllocatedBytes", MethodType.methodType(long[].class, long[].class))
.bindTo(bean);
} catch (LinkageError | ReflectiveOperationException | RuntimeException e) {
LOG.warn("Cannot track allocated memory of computer threads", e);
threadAllocatedBytesHandle = threadsAllocatedBytesHandle = null;
}
threadAllocatedBytes = threadAllocatedBytesHandle;
threadsAllocatedBytes = threadsAllocatedBytesHandle;
}
private ThreadAllocations() {
}
/**
* Check whether the current JVM provides information about per-thread allocations.
*
* @return Whether per-thread allocation information is available.
*/
public static boolean isSupported() {
return threadAllocatedBytes != null;
}
/**
* Get an approximation the amount of memory a thread has allocated over its lifetime.
*
* @param threadId The ID of the thread.
* @return The allocated memory, in bytes.
* @see com.sun.management.ThreadMXBean#getThreadAllocatedBytes(long)
*/
public static long getAllocatedBytes(long threadId) {
if (threadAllocatedBytes == null) {
throw new UnsupportedOperationException("Allocated bytes are not supported");
}
try {
return (long) threadAllocatedBytes.invokeExact(threadId);
} catch (Throwable t) {
throw throwUnchecked0(t); // Should never occur, but if it does it's guaranteed to be a runtime exception.
}
}
/**
* Get an approximation the amount of memory a thread has allocated over its lifetime.
* <p>
* This is equivalent to calling {@link #getAllocatedBytes(long)} for each thread in {@code threadIds}.
*
* @param threadIds An array of thread IDs.
* @return An array with the same length as {@code threadIds}, containing the allocated memory for each thread.
* @see com.sun.management.ThreadMXBean#getThreadAllocatedBytes(long[])
*/
public static long[] getAllocatedBytes(long[] threadIds) {
if (threadsAllocatedBytes == null) {
throw new UnsupportedOperationException("Allocated bytes are not supported");
}
try {
return (long[]) threadsAllocatedBytes.invokeExact(threadIds);
} catch (Throwable t) {
throw throwUnchecked0(t); // Should never occur, but if it does it's guaranteed to be a runtime exception.
}
}
@SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" })
private static <T extends Throwable> T throwUnchecked0(Throwable t) throws T {
throw (T) t;
}
}

View File

@ -5,21 +5,22 @@
package dan200.computercraft.core.computer.computerthread;
import dan200.computercraft.core.computer.TimeoutState;
import dan200.computercraft.core.metrics.Metrics;
import dan200.computercraft.core.metrics.ThreadAllocations;
import dan200.computercraft.test.core.ConcurrentHelpers;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.jupiter.api.Assertions.*;
@Timeout(value = 15)
@ -91,21 +92,4 @@ public class ComputerThreadTest {
manager.startAndWait(computer);
}
@Test
public void testAllocationTracking() throws Exception {
Assumptions.assumeTrue(ThreadAllocations.isSupported(), "Allocation tracking is supported");
var size = 1024 * 1024 * 64;
var computer = manager.createWorker((executor, timeout) -> {
// Allocate some slab of memory. We try to blackhole the allocated object, but it's pretty naive
// so who knows how useful it'll be.
assertNotEquals(0, Objects.toString(new byte[size]).length());
});
manager.startAndWait(computer);
assertThat(computer.getMetric(Metrics.JAVA_ALLOCATION), allOf(
greaterThan((long) size), lessThan((long) (size + (size >> 2)))
));
}
}