1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-04-17 00:03:12 +00:00

Allow overriding computer/floppy capacity

This adds a new "computercraft:storage_capacity" component to items (and
"Capacity" NBT tag to BEs), that overrides the capacity for the given
item.

Fixes #1814
This commit is contained in:
Jonathan Coates 2025-01-21 09:57:14 +00:00
parent 53425c1e76
commit 4f3663ccc9
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
12 changed files with 129 additions and 38 deletions

View File

@ -93,6 +93,7 @@ import dan200.computercraft.shared.turtle.upgrades.TurtleTool;
import dan200.computercraft.shared.util.ComponentMap;
import dan200.computercraft.shared.util.DataComponentUtil;
import dan200.computercraft.shared.util.NonNegativeId;
import dan200.computercraft.shared.util.StorageCapacity;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
@ -316,6 +317,17 @@ public final class ModRegistry {
.persistent(NonNegativeId.CODEC).networkSynchronized(NonNegativeId.STREAM_CODEC)
);
/**
* The storage capacity of a computer or disk.
*
* @see AbstractComputerItem
* @see PocketComputerItem
* @see DiskItem
*/
public static final RegistryEntry<DataComponentType<StorageCapacity>> STORAGE_CAPACITY = register("storage_capacity", b -> b
.persistent(StorageCapacity.CODEC).networkSynchronized(StorageCapacity.STREAM_CODEC)
);
/**
* The left upgrade of a turtle.
*

View File

@ -23,6 +23,7 @@ import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.Container;
@ -43,10 +44,12 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
private static final String NBT_ID = "ComputerId";
private static final String NBT_LABEL = "Label";
private static final String NBT_ON = "On";
private static final String NBT_CAPACITY = "Capacity";
private @Nullable UUID instanceID = null;
private int computerID = -1;
protected @Nullable String label = null;
protected long storageCapacity = -1;
private boolean on = false;
boolean startOn = false;
private boolean fresh = false;
@ -143,6 +146,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
// Save ID, label and power state
if (computerID >= 0) nbt.putInt(NBT_ID, computerID);
if (label != null) nbt.putString(NBT_LABEL, label);
if (storageCapacity > 0) nbt.putLong(NBT_CAPACITY, storageCapacity);
nbt.putBoolean(NBT_ON, on);
lockCode.addToTag(nbt);
@ -164,6 +168,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
// Load ID, label and power state
computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
storageCapacity = nbt.contains(NBT_CAPACITY, Tag.TAG_ANY_NUMERIC) ? nbt.getLong(NBT_CAPACITY) : -1;
on = startOn = nbt.getBoolean(NBT_ON);
lockCode = LockCode.fromTag(nbt);
@ -174,6 +179,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
super.applyImplicitComponents(component);
label = DataComponentUtil.getCustomName(component.get(DataComponents.CUSTOM_NAME));
computerID = NonNegativeId.getId(component.get(ModRegistry.DataComponents.COMPUTER_ID.get()));
storageCapacity = StorageCapacity.getOrDefault(component.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), -1);
lockCode = component.getOrDefault(DataComponents.LOCK, LockCode.NO_LOCK);
}
@ -182,6 +188,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
super.collectImplicitComponents(builder);
builder.set(ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.of(computerID));
builder.set(DataComponents.CUSTOM_NAME, label == null ? null : Component.literal(label));
builder.set(ModRegistry.DataComponents.STORAGE_CAPACITY.get(), storageCapacity > 0 ? new StorageCapacity(storageCapacity) : null);
if (lockCode != LockCode.NO_LOCK) builder.set(DataComponents.LOCK, lockCode);
}
@ -191,6 +198,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
super.removeComponentsFromTag(tag);
tag.remove(NBT_ID);
tag.remove(NBT_LABEL);
tag.remove(NBT_CAPACITY);
tag.remove(LockCode.TAG_LOCK);
}
@ -397,14 +405,16 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
// We need this for pick block on the client side.
var nbt = super.getUpdateTag(registries);
if (label != null) nbt.putString(NBT_LABEL, label);
if (computerID >= 0) nbt.putInt(NBT_ID, computerID);
if (label != null) nbt.putString(NBT_LABEL, label);
if (storageCapacity > 0) nbt.putLong(NBT_CAPACITY, storageCapacity);
return nbt;
}
protected void loadClient(CompoundTag nbt, HolderLookup.Provider registries) {
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
storageCapacity = nbt.contains(NBT_CAPACITY, Tag.TAG_ANY_NUMERIC) ? nbt.getLong(NBT_CAPACITY) : -1;
}
protected void transferStateFrom(AbstractComputerBlockEntity copy) {
@ -413,6 +423,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
instanceID = copy.instanceID;
computerID = copy.computerID;
label = copy.label;
storageCapacity = copy.storageCapacity;
on = copy.on;
startOn = copy.startOn;
lockCode = copy.lockCode;

View File

@ -35,6 +35,7 @@ public class ComputerBlockEntity extends AbstractComputerBlockEntity {
return new ServerComputer((ServerLevel) getLevel(), getBlockPos(), ServerComputer.properties(id, getFamily())
.label(getLabel())
.terminalSize(ConfigSpec.computerTermWidth.get(), ConfigSpec.computerTermHeight.get())
.storageCapacity(storageCapacity)
);
}

View File

@ -21,6 +21,7 @@ import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage;
@ -49,15 +50,9 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
private final NetworkedTerminal terminal;
private final AtomicBoolean terminalChanged = new AtomicBoolean(false);
private int ticksSincePing;
private final long storageCapacity;
@Deprecated
public ServerComputer(
ServerLevel level, BlockPos position, int computerID, @Nullable String label, ComputerFamily family, int terminalWidth, int terminalHeight,
ComponentMap baseComponents
) {
this(level, position, properties(computerID, family).label(label).terminalSize(terminalWidth, terminalHeight).addComponents(baseComponents));
}
private int ticksSincePing;
public ServerComputer(ServerLevel level, BlockPos position, Properties properties) {
this.level = level;
@ -68,6 +63,8 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
terminal = new NetworkedTerminal(properties.terminalWidth, properties.terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged);
metrics = context.metrics().createMetricObserver(this);
storageCapacity = properties.storageCapacity;
var componentBuilder = ComponentMap.builder();
componentBuilder.add(ComponentMap.METRICS, metrics);
if (family == ComputerFamily.COMMAND) {
@ -269,7 +266,8 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
@Override
public final WritableMount createRootMount() {
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + computer.getID(), Config.computerSpaceLimit);
var capacity = storageCapacity <= 0 ? ConfigSpec.computerSpaceLimit.get() : storageCapacity;
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + computer.getID(), capacity);
}
public static Properties properties(int computerID, ComputerFamily family) {
@ -277,13 +275,13 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
}
public static final class Properties {
private final int computerID;
private @Nullable String label;
private final ComputerFamily family;
private int terminalWidth = Config.DEFAULT_COMPUTER_TERM_WIDTH;
private int terminalHeight = Config.DEFAULT_COMPUTER_TERM_HEIGHT;
private long storageCapacity = -1;
private final ComponentMap.Builder components = ComponentMap.builder();
private Properties(int computerID, ComputerFamily family) {
@ -303,14 +301,20 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
return this;
}
/**
* Override the storage capacity for this computer.
*
* @param capacity The capacity for this computer's drive, or {@code -1} to use the default.
* @return {@code this}, for chaining.
*/
public Properties storageCapacity(long capacity) {
storageCapacity = capacity;
return this;
}
public <T> Properties addComponent(ComputerComponent<T> component, T value) {
components.add(component, value);
return this;
}
private Properties addComponents(ComponentMap components) {
this.components.add(components);
return this;
}
}
}

View File

@ -9,8 +9,9 @@ import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.util.DataComponentUtil;
import dan200.computercraft.shared.util.StorageCapacity;
import net.minecraft.ChatFormatting;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
@ -53,6 +54,9 @@ public class AbstractComputerItem extends BlockItem implements IMedia {
@Override
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
var id = stack.get(ModRegistry.DataComponents.COMPUTER_ID.get());
return id != null ? ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id.id(), Config.computerSpaceLimit) : null;
if (id == null) return null;
var capacity = StorageCapacity.getOrDefault(stack.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), ConfigSpec.computerSpaceLimit);
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id.id(), capacity);
}
}

View File

@ -12,8 +12,6 @@ import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
* @see ConfigSpec The definition of our config values.
*/
public final class Config {
public static int computerSpaceLimit = 1000 * 1000;
public static int floppySpaceLimit = 125 * 1000;
public static int uploadMaxSize = 512 * 1024; // 512 KB
public static boolean commandRequireCreative = true;

View File

@ -97,11 +97,11 @@ public final class ConfigSpec {
{ // General computers
computerSpaceLimit = builder
.comment("The disk space limit for computers and turtles, in bytes.")
.define("computer_space_limit", Config.computerSpaceLimit);
.define("computer_space_limit", 1000 * 1000);
floppySpaceLimit = builder
.comment("The disk space limit for floppy disks, in bytes.")
.define("floppy_space_limit", Config.floppySpaceLimit);
.define("floppy_space_limit", 125 * 1000);
uploadMaxSize = builder
.comment("""
@ -384,8 +384,6 @@ public final class ConfigSpec {
public static void syncServer(@Nullable Path path) {
// General
Config.computerSpaceLimit = computerSpaceLimit.get();
Config.floppySpaceLimit = floppySpaceLimit.get();
Config.uploadMaxSize = uploadMaxSize.get();
CoreConfig.maximumFilesOpen = maximumFilesOpen.get();
CoreConfig.defaultComputerSettings = defaultComputerSettings.get();

View File

@ -10,8 +10,9 @@ import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.util.NonNegativeId;
import dan200.computercraft.shared.util.StorageCapacity;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
@ -64,7 +65,8 @@ public class DiskItem extends Item implements IMedia {
@Override
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
var diskID = NonNegativeId.getOrCreate(level.getServer(), stack, ModRegistry.DataComponents.DISK_ID.get(), "disk");
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "disk/" + diskID, Config.floppySpaceLimit);
var capacity = StorageCapacity.getOrDefault(stack.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), ConfigSpec.floppySpaceLimit);
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "disk/" + diskID, capacity);
}
public static int getDiskID(ItemStack stack) {

View File

@ -18,16 +18,13 @@ 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.items.ServerComputerReference;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.pocket.core.PocketBrain;
import dan200.computercraft.shared.pocket.core.PocketHolder;
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider;
import dan200.computercraft.shared.util.DataComponentUtil;
import dan200.computercraft.shared.util.IDAssigner;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.NonNegativeId;
import dan200.computercraft.shared.util.*;
import net.minecraft.ChatFormatting;
import net.minecraft.core.HolderLookup;
import net.minecraft.network.chat.Component;
@ -198,7 +195,9 @@ public class PocketComputerItem extends Item implements IMedia {
var computerID = NonNegativeId.getOrCreate(level.getServer(), stack, ModRegistry.DataComponents.COMPUTER_ID.get(), IDAssigner.COMPUTER);
var brain = new PocketBrain(
holder, getUpgradeWithData(stack),
ServerComputer.properties(computerID, getFamily()).label(getLabel(stack))
ServerComputer.properties(computerID, getFamily())
.label(getLabel(stack))
.storageCapacity(StorageCapacity.getOrDefault(stack.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), -1))
);
var computer = brain.computer();
@ -265,10 +264,10 @@ public class PocketComputerItem extends Item implements IMedia {
@Override
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
var id = stack.get(ModRegistry.DataComponents.COMPUTER_ID.get());
if (id != null) {
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id.id(), Config.computerSpaceLimit);
}
return null;
if (id == null) return null;
var capacity = StorageCapacity.getOrDefault(stack.get(ModRegistry.DataComponents.STORAGE_CAPACITY.get()), ConfigSpec.computerSpaceLimit);
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id.id(), capacity);
}
private static boolean isMarkedOn(ItemStack stack) {

View File

@ -82,6 +82,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
var computer = new ServerComputer((ServerLevel) getLevel(), getBlockPos(), ServerComputer.properties(id, getFamily())
.label(getLabel())
.terminalSize(Config.TURTLE_TERM_WIDTH, Config.TURTLE_TERM_HEIGHT)
.storageCapacity(storageCapacity)
.addComponent(ComputerComponents.TURTLE, brain)
);
brain.setupComputer(computer);

View File

@ -26,7 +26,7 @@ import javax.annotation.Nullable;
public record NonNegativeId(int id) {
public static final Codec<NonNegativeId> CODEC = ExtraCodecs.NON_NEGATIVE_INT.xmap(NonNegativeId::new, NonNegativeId::id);
public static final StreamCodec<ByteBuf, NonNegativeId> STREAM_CODEC = ByteBufCodecs.INT.map(NonNegativeId::new, NonNegativeId::id);
public static final StreamCodec<ByteBuf, NonNegativeId> STREAM_CODEC = ByteBufCodecs.VAR_INT.map(NonNegativeId::new, NonNegativeId::id);
public NonNegativeId {
if (id < 0) throw new IllegalArgumentException("ID must be >= 0");

View File

@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.util;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.config.ConfigSpec;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import javax.annotation.Nullable;
import java.util.function.Supplier;
/**
* A data component that sets the storage capacity of a computer or disk.
* <p>
* This component is not present by default, and consumers should fall back to a globally-configured config value
* (e.g. {@link ConfigSpec#computerSpaceLimit}, {@link ConfigSpec#floppySpaceLimit}).
*
* @param capacity The capacity of this medium.
* @see ServerComputer.Properties#storageCapacity(long)
* @see ModRegistry.DataComponents#STORAGE_CAPACITY
*/
public record StorageCapacity(long capacity) {
public static final Codec<StorageCapacity> CODEC = Codec.LONG.validate(x ->
x > 0 ? DataResult.success(x) : DataResult.error(() -> "Capacity must be positive: " + x)
).xmap(StorageCapacity::new, StorageCapacity::capacity);
public static final StreamCodec<ByteBuf, StorageCapacity> STREAM_CODEC = ByteBufCodecs.VAR_LONG.map(StorageCapacity::new, StorageCapacity::capacity);
public StorageCapacity {
if (capacity <= 0) throw new IllegalArgumentException("Capacity must be > 0");
}
/**
* Get the configured capacity, or return a default value.
*
* @param capacity The capacity to get.
* @param fallback The value to fall back to. This is typically a config value.
* @return The capacity for this computer or disk.
*/
public static long getOrDefault(@Nullable StorageCapacity capacity, Supplier<Integer> fallback) {
return capacity == null ? fallback.get() : capacity.capacity();
}
/**
* Get the configured capacity, or return a default value.
*
* @param capacity The capacity to get.
* @param fallback The value to fall back to. This is typically {@code -1}.
* @return The capacity for this computer or disk.
*/
public static long getOrDefault(@Nullable StorageCapacity capacity, long fallback) {
return capacity == null ? fallback : capacity.capacity();
}
}