CC-Tweaked/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity...

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

449 lines
16 KiB
Java
Raw Normal View History

// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
//
// SPDX-License-Identifier: LicenseRef-CCPL
package dan200.computercraft.shared.computer.blocks;
import com.google.common.base.Strings;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.impl.BundledRedstone;
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.ServerContext;
import dan200.computercraft.shared.platform.ComponentAccess;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.util.*;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
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.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.Container;
import net.minecraft.world.LockCode;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.Nameable;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable;
import java.util.Objects;
Replace integer instance IDs with UUIDs Here's a fun bug you can try at home: - Create a new world - Spawn in a pocket computer, turn it on, and place it in a chest. - Reload the world - the pocket computer in the chest should now be off. - Spawn in a new pocket computer, and turn it on. The computer in chest will also appear to be on! This bug has been present since pocket computers were added (27th March, 2024). When a pocket computer is added to a player's inventory, it is assigned a unique *per-session* "instance id" , which is used to find the associated computer. Note the "per-session" there - these ids will be reused if you reload the world (or restart the server). In the above bug, we see the following: - The first pocket computer is assigned an instance id of 0. - After reloading, the second pocket computer is assigned an instance id of 0. - If the first pocket computer was in our inventory, it'd be ticked and assigned a new instance id. However, because it's in an inventory, it keeps its old one. - Both computers look up their client-side computer state and get the same value, meaning the first pocket computer mirrors the second! To fix this, we now ensure instance ids are entirely unique (not just per-session). Rather than sequentially assigning an int, we now use a random UUID (we probably could get away with a random long, but this feels more idiomatic). This has a couple of user-visible changes: - /computercraft no longer lists instance ids outside of dumping an individual computer. - The @c[instance=...] selector uses UUIDs. We still use int instance ids for the legacy selector, but that'll be removed in a later MC version. - Pocket computers now store a UUID rather than an int. Related to this change (I made this change first, but then they got kinda mixed up together), we now only create PocketComputerData when receiving server data. This makes the code a little uglier in some places (the data may now be null), but means we don't populate the client-side pocket computer map with computers the server doesn't know about.
2024-03-17 12:21:21 +00:00
import java.util.UUID;
2017-05-06 23:07:42 +00:00
public abstract class AbstractComputerBlockEntity extends BlockEntity implements IComputerBlockEntity, Nameable, MenuProvider {
private static final String NBT_ID = "ComputerId";
private static final String NBT_LABEL = "Label";
private static final String NBT_ON = "On";
Replace integer instance IDs with UUIDs Here's a fun bug you can try at home: - Create a new world - Spawn in a pocket computer, turn it on, and place it in a chest. - Reload the world - the pocket computer in the chest should now be off. - Spawn in a new pocket computer, and turn it on. The computer in chest will also appear to be on! This bug has been present since pocket computers were added (27th March, 2024). When a pocket computer is added to a player's inventory, it is assigned a unique *per-session* "instance id" , which is used to find the associated computer. Note the "per-session" there - these ids will be reused if you reload the world (or restart the server). In the above bug, we see the following: - The first pocket computer is assigned an instance id of 0. - After reloading, the second pocket computer is assigned an instance id of 0. - If the first pocket computer was in our inventory, it'd be ticked and assigned a new instance id. However, because it's in an inventory, it keeps its old one. - Both computers look up their client-side computer state and get the same value, meaning the first pocket computer mirrors the second! To fix this, we now ensure instance ids are entirely unique (not just per-session). Rather than sequentially assigning an int, we now use a random UUID (we probably could get away with a random long, but this feels more idiomatic). This has a couple of user-visible changes: - /computercraft no longer lists instance ids outside of dumping an individual computer. - The @c[instance=...] selector uses UUIDs. We still use int instance ids for the legacy selector, but that'll be removed in a later MC version. - Pocket computers now store a UUID rather than an int. Related to this change (I made this change first, but then they got kinda mixed up together), we now only create PocketComputerData when receiving server data. This makes the code a little uglier in some places (the data may now be null), but means we don't populate the client-side pocket computer map with computers the server doesn't know about.
2024-03-17 12:21:21 +00:00
private @Nullable UUID instanceID = null;
private int computerID = -1;
protected @Nullable String label = null;
private boolean on = false;
boolean startOn = false;
private boolean fresh = false;
private int invalidSides = 0;
private final ComponentAccess<IPeripheral> peripherals = PlatformHelper.get().createPeripheralAccess(this, d -> invalidSides |= 1 << d.ordinal());
private LockCode lockCode = LockCode.NO_LOCK;
private final ComputerFamily family;
public AbstractComputerBlockEntity(BlockEntityType<? extends AbstractComputerBlockEntity> type, BlockPos pos, BlockState state, ComputerFamily family) {
super(type, pos, state);
this.family = family;
}
protected void unload() {
Remove ClientComputer Historically CC has maintained two computer registries; one on the server (which runs the actual computer) and one on the client (which stores the terminal and some small bits of additional data). This means when a user opens the computer UI, we send the terminal contents and store it in the client computer registry. We then send the instance id alongside the "open container" packet, which is used to look up the client computer (and thus terminal) in our client-side registry. This patch makes the computer menu syncing behaviour more consistent with vanilla. The initial terminal contents is sent alongside the "open container" packet, and subsequent terminal changes apply /just/ to the open container. Computer on/off state is synced via a vanilla ContainerData/IIntArray. Likewise, sending user input to the server now targets the open container, rather than an arbitrary instance id. The one remaining usage of ClientComputer is for pocket computers. For these, we still need to sync the current on/off/blinking state and the pocket computer light. We don't need the full ClientComputer interface for this case (after all, you can't send input to a pocket computer someone else is holding!). This means we can tear out ClientComputer and ClientComputerRegistry, replacing it with a much simpler ClientPocketComputers store. This in turn allows the following changes: - Remove IComputer, as we no longer need to abstract over client and server computers. - Likewise, we can merge ComputerRegistry into the server registry. This commit also cleans up the handling of instance IDs a little bit: ServerComputers are now responsible for generating their ID and adding/removing themselves from the registry. - As the client-side terminal will never be null, we can remove a whole bunch of null checks throughout the codebase. - As the terminal is available immediately, we don't need to explicitly pass in terminal sizes to the computer GUIs. This means we're no longer reliant on those config values on the client side! - Remove the "request computer state" packet. Pocket computers now store which players need to know the computer state, automatically sending data when a new player starts tracking the computer.
2022-10-21 17:17:42 +00:00
if (getLevel().isClientSide) return;
var computer = getServerComputer();
if (computer != null) computer.close();
Replace integer instance IDs with UUIDs Here's a fun bug you can try at home: - Create a new world - Spawn in a pocket computer, turn it on, and place it in a chest. - Reload the world - the pocket computer in the chest should now be off. - Spawn in a new pocket computer, and turn it on. The computer in chest will also appear to be on! This bug has been present since pocket computers were added (27th March, 2024). When a pocket computer is added to a player's inventory, it is assigned a unique *per-session* "instance id" , which is used to find the associated computer. Note the "per-session" there - these ids will be reused if you reload the world (or restart the server). In the above bug, we see the following: - The first pocket computer is assigned an instance id of 0. - After reloading, the second pocket computer is assigned an instance id of 0. - If the first pocket computer was in our inventory, it'd be ticked and assigned a new instance id. However, because it's in an inventory, it keeps its old one. - Both computers look up their client-side computer state and get the same value, meaning the first pocket computer mirrors the second! To fix this, we now ensure instance ids are entirely unique (not just per-session). Rather than sequentially assigning an int, we now use a random UUID (we probably could get away with a random long, but this feels more idiomatic). This has a couple of user-visible changes: - /computercraft no longer lists instance ids outside of dumping an individual computer. - The @c[instance=...] selector uses UUIDs. We still use int instance ids for the legacy selector, but that'll be removed in a later MC version. - Pocket computers now store a UUID rather than an int. Related to this change (I made this change first, but then they got kinda mixed up together), we now only create PocketComputerData when receiving server data. This makes the code a little uglier in some places (the data may now be null), but means we don't populate the client-side pocket computer map with computers the server doesn't know about.
2024-03-17 12:21:21 +00:00
instanceID = null;
}
@Override
public void setRemoved() {
super.setRemoved();
unload();
}
protected float getInteractRange() {
return Container.DEFAULT_DISTANCE_BUFFER;
}
public boolean isUsable(Player player) {
return BaseContainerBlockEntity.canUnlock(player, lockCode, getDisplayName())
&& Container.stillValidBlockEntity(this, player, getInteractRange());
}
protected void serverTick() {
if (getLevel().isClientSide) return;
if (computerID < 0 && !startOn) return; // Don't tick if we don't need a computer!
var computer = createServerComputer();
// Update any peripherals that have changed.
if (invalidSides != 0) {
for (var direction : DirectionUtil.FACINGS) {
if (DirectionUtil.isSet(invalidSides, direction)) refreshPeripheral(computer, direction);
}
}
// If the computer isn't on and should be, then turn it on
if (startOn || (fresh && on)) {
computer.turnOn();
startOn = false;
}
computer.keepAlive();
fresh = false;
computerID = computer.getID();
// If the on state has changed, mark as as dirty.
var newOn = computer.isOn();
if (on != newOn) {
on = newOn;
setChanged();
}
// If the label has changed, mark as dirty and sync to client.
var newLabel = computer.getLabel();
if (!Objects.equals(label, newLabel)) {
label = newLabel;
BlockEntityHelpers.updateBlock(this);
}
// Update the block state if needed.
updateBlockState(computer.getState());
var changes = computer.pollAndResetChanges();
if (changes != 0) {
for (var direction : DirectionUtil.FACINGS) {
if ((changes & (1 << remapToLocalSide(direction).ordinal())) != 0) updateRedstoneTo(direction);
}
}
}
protected abstract void updateBlockState(ComputerState newState);
@Override
public void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) {
// Save ID, label and power state
if (computerID >= 0) nbt.putInt(NBT_ID, computerID);
2019-06-15 07:00:20 +00:00
if (label != null) nbt.putString(NBT_LABEL, label);
nbt.putBoolean(NBT_ON, on);
lockCode.addToTag(nbt);
super.saveAdditional(nbt, registries);
}
@Override
public final void loadAdditional(CompoundTag nbt, HolderLookup.Provider registries) {
super.loadAdditional(nbt, registries);
if (level != null && level.isClientSide) {
loadClient(nbt, registries);
} else {
loadServer(nbt, registries);
}
}
protected void loadServer(CompoundTag nbt, HolderLookup.Provider registries) {
// Load ID, label and power state
computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
2019-06-15 07:00:20 +00:00
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
on = startOn = nbt.getBoolean(NBT_ON);
lockCode = LockCode.fromTag(nbt);
}
@Override
protected void applyImplicitComponents(DataComponentInput component) {
super.applyImplicitComponents(component);
label = DataComponentUtil.getCustomName(component.get(DataComponents.CUSTOM_NAME));
computerID = NonNegativeId.getId(component.get(ModRegistry.DataComponents.COMPUTER_ID.get()));
lockCode = component.getOrDefault(DataComponents.LOCK, LockCode.NO_LOCK);
}
@Override
protected void collectImplicitComponents(DataComponentMap.Builder builder) {
super.collectImplicitComponents(builder);
builder.set(ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.of(computerID));
builder.set(DataComponents.CUSTOM_NAME, label == null ? null : Component.literal(label));
if (lockCode != LockCode.NO_LOCK) builder.set(DataComponents.LOCK, lockCode);
}
@Override
@Deprecated
public void removeComponentsFromTag(CompoundTag tag) {
super.removeComponentsFromTag(tag);
tag.remove(NBT_ID);
tag.remove(NBT_LABEL);
tag.remove(LockCode.TAG_LOCK);
}
protected boolean isPeripheralBlockedOnSide(ComputerSide localSide) {
return false;
}
protected abstract Direction getDirection();
protected ComputerSide remapToLocalSide(Direction globalSide) {
return remapLocalSide(DirectionUtil.toLocal(getDirection(), globalSide));
}
protected ComputerSide remapLocalSide(ComputerSide localSide) {
return localSide;
}
/**
* Update the redstone input on a particular side.
* <p>
* This is called <em>immediately</em> when a neighbouring block changes (see {@link #neighborChanged(BlockPos)}).
*
* @param computer The current server computer.
* @param dir The direction to update in.
* @param targetPos The position of the adjacent block, equal to {@code getBlockPos().offset(dir)}.
*/
private void updateRedstoneInput(ServerComputer computer, Direction dir, BlockPos targetPos) {
var offsetSide = dir.getOpposite();
2019-04-09 10:11:12 +00:00
var localDir = remapToLocalSide(dir);
computer.setRedstoneInput(localDir, RedstoneUtil.getRedstoneInput(getLevel(), targetPos, dir));
computer.setBundledRedstoneInput(localDir, BundledRedstone.getOutput(getLevel(), targetPos, offsetSide));
2017-05-03 09:14:39 +00:00
}
/**
* Update the peripheral on a particular side.
* <p>
* This is called from {@link #serverTick()}, after a peripheral has been marked as invalid (such as in
* {@link #neighborChanged(BlockPos)})
*
* @param computer The current server computer.
* @param dir The direction to update in.
*/
private void refreshPeripheral(ServerComputer computer, Direction dir) {
invalidSides &= ~(1 << dir.ordinal());
var localDir = remapToLocalSide(dir);
if (isPeripheralBlockedOnSide(localDir)) return;
var peripheral = peripherals.get(dir);
computer.setPeripheral(localDir, peripheral);
}
public void updateInputsImmediately() {
2017-05-03 09:14:39 +00:00
var computer = getServerComputer();
if (computer != null) updateInputsImmediately(computer);
}
/**
* Update all redstone and peripherals.
* <p>
* This should only be really be called when the computer is being ticked (though there are some cases where it
* won't be), as peripheral scanning requires adjacent tiles to be in a "correct" state - which may not be the case
* if they're still updating!
*
* @param computer The current computer instance.
*/
private void updateInputsImmediately(ServerComputer computer) {
var pos = getBlockPos();
for (var dir : DirectionUtil.FACINGS) {
updateRedstoneInput(computer, dir, pos.relative(dir));
refreshPeripheral(computer, dir);
2017-05-03 09:14:39 +00:00
}
}
/**
* Called when a neighbour block changes.
* <p>
* This finds the side the neighbour block is on, and updates the inputs accordingly.
* <p>
* We do <strong>NOT</strong> update the peripheral immediately. Blocks and block entities are sometimes
* inconsistent at the point where an update is received, and so we instead just mark that side as dirty (see
* {@link #invalidSides}) and refresh it {@linkplain #serverTick() next tick}.
*
* @param neighbour The position of the neighbour block.
*/
public void neighborChanged(BlockPos neighbour) {
var computer = getServerComputer();
if (computer == null) return;
for (var dir : DirectionUtil.FACINGS) {
var offset = getBlockPos().relative(dir);
if (offset.equals(neighbour)) {
updateRedstoneInput(computer, dir, offset);
invalidSides |= 1 << dir.ordinal();
return;
}
}
// If the position is not any adjacent one, update all inputs. This is pretty terrible, but some redstone mods
// handle this incorrectly.
for (var dir : DirectionUtil.FACINGS) updateRedstoneInput(computer, dir, getBlockPos().relative(dir));
invalidSides = DirectionUtil.ALL_SIDES; // Mark all peripherals as dirty.
}
/**
* Called when a neighbour block's shape changes.
* <p>
* Unlike {@link #neighborChanged(BlockPos)}, we don't update redstone, only peripherals.
*
* @param direction The side that changed.
*/
public void neighbourShapeChanged(Direction direction) {
invalidSides |= 1 << direction.ordinal();
}
/**
* Update outputs in a specific direction.
*
* @param direction The direction to propagate outputs in.
*/
protected void updateRedstoneTo(Direction direction) {
RedstoneUtil.propagateRedstoneOutput(getLevel(), getBlockPos(), direction);
var computer = getServerComputer();
if (computer != null) updateRedstoneInput(computer, direction, getBlockPos().relative(direction));
}
/**
* Update all redstone outputs.
*/
public void updateRedstone() {
for (var dir : DirectionUtil.FACINGS) updateRedstoneTo(dir);
}
@Override
public final int getComputerID() {
return computerID;
}
@Override
public final @Nullable String getLabel() {
return label;
}
@Override
public final void setComputerID(int id) {
if (getLevel().isClientSide || computerID == id) return;
computerID = id;
BlockEntityHelpers.updateBlock(this);
}
@Override
public final void setLabel(@Nullable String label) {
if (getLevel().isClientSide || Objects.equals(this.label, label)) return;
this.label = label;
var computer = getServerComputer();
if (computer != null) computer.setLabel(label);
BlockEntityHelpers.updateBlock(this);
}
@Override
public ComputerFamily getFamily() {
return family;
}
public final ServerComputer createServerComputer() {
var server = getLevel().getServer();
if (server == null) throw new IllegalStateException("Cannot access server computer on the client.");
var changed = false;
var computer = ServerContext.get(server).registry().get(instanceID);
if (computer == null) {
if (computerID < 0) {
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir(server, IDAssigner.COMPUTER);
BlockEntityHelpers.updateBlock(this);
}
Remove ClientComputer Historically CC has maintained two computer registries; one on the server (which runs the actual computer) and one on the client (which stores the terminal and some small bits of additional data). This means when a user opens the computer UI, we send the terminal contents and store it in the client computer registry. We then send the instance id alongside the "open container" packet, which is used to look up the client computer (and thus terminal) in our client-side registry. This patch makes the computer menu syncing behaviour more consistent with vanilla. The initial terminal contents is sent alongside the "open container" packet, and subsequent terminal changes apply /just/ to the open container. Computer on/off state is synced via a vanilla ContainerData/IIntArray. Likewise, sending user input to the server now targets the open container, rather than an arbitrary instance id. The one remaining usage of ClientComputer is for pocket computers. For these, we still need to sync the current on/off/blinking state and the pocket computer light. We don't need the full ClientComputer interface for this case (after all, you can't send input to a pocket computer someone else is holding!). This means we can tear out ClientComputer and ClientComputerRegistry, replacing it with a much simpler ClientPocketComputers store. This in turn allows the following changes: - Remove IComputer, as we no longer need to abstract over client and server computers. - Likewise, we can merge ComputerRegistry into the server registry. This commit also cleans up the handling of instance IDs a little bit: ServerComputers are now responsible for generating their ID and adding/removing themselves from the registry. - As the client-side terminal will never be null, we can remove a whole bunch of null checks throughout the codebase. - As the terminal is available immediately, we don't need to explicitly pass in terminal sizes to the computer GUIs. This means we're no longer reliant on those config values on the client side! - Remove the "request computer state" packet. Pocket computers now store which players need to know the computer state, automatically sending data when a new player starts tracking the computer.
2022-10-21 17:17:42 +00:00
computer = createComputer(computerID);
instanceID = computer.register();
fresh = true;
changed = true;
}
if (changed) updateInputsImmediately(computer);
return computer;
}
protected abstract ServerComputer createComputer(int id);
@Nullable
public ServerComputer getServerComputer() {
return getLevel().isClientSide || getLevel().getServer() == null ? null : ServerContext.get(getLevel().getServer()).registry().get(instanceID);
}
// Networking stuff
@Override
public final ClientboundBlockEntityDataPacket getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
// We need this for pick block on the client side.
var nbt = super.getUpdateTag(registries);
2019-06-15 07:00:20 +00:00
if (label != null) nbt.putString(NBT_LABEL, label);
if (computerID >= 0) nbt.putInt(NBT_ID, computerID);
return nbt;
}
protected void loadClient(CompoundTag nbt, HolderLookup.Provider registries) {
2019-06-15 07:00:20 +00:00
label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
}
protected void transferStateFrom(AbstractComputerBlockEntity copy) {
Replace integer instance IDs with UUIDs Here's a fun bug you can try at home: - Create a new world - Spawn in a pocket computer, turn it on, and place it in a chest. - Reload the world - the pocket computer in the chest should now be off. - Spawn in a new pocket computer, and turn it on. The computer in chest will also appear to be on! This bug has been present since pocket computers were added (27th March, 2024). When a pocket computer is added to a player's inventory, it is assigned a unique *per-session* "instance id" , which is used to find the associated computer. Note the "per-session" there - these ids will be reused if you reload the world (or restart the server). In the above bug, we see the following: - The first pocket computer is assigned an instance id of 0. - After reloading, the second pocket computer is assigned an instance id of 0. - If the first pocket computer was in our inventory, it'd be ticked and assigned a new instance id. However, because it's in an inventory, it keeps its old one. - Both computers look up their client-side computer state and get the same value, meaning the first pocket computer mirrors the second! To fix this, we now ensure instance ids are entirely unique (not just per-session). Rather than sequentially assigning an int, we now use a random UUID (we probably could get away with a random long, but this feels more idiomatic). This has a couple of user-visible changes: - /computercraft no longer lists instance ids outside of dumping an individual computer. - The @c[instance=...] selector uses UUIDs. We still use int instance ids for the legacy selector, but that'll be removed in a later MC version. - Pocket computers now store a UUID rather than an int. Related to this change (I made this change first, but then they got kinda mixed up together), we now only create PocketComputerData when receiving server data. This makes the code a little uglier in some places (the data may now be null), but means we don't populate the client-side pocket computer map with computers the server doesn't know about.
2024-03-17 12:21:21 +00:00
if (copy.computerID != computerID || !Objects.equals(copy.instanceID, instanceID)) {
unload();
instanceID = copy.instanceID;
computerID = copy.computerID;
label = copy.label;
on = copy.on;
startOn = copy.startOn;
lockCode = copy.lockCode;
BlockEntityHelpers.updateBlock(this);
}
Replace integer instance IDs with UUIDs Here's a fun bug you can try at home: - Create a new world - Spawn in a pocket computer, turn it on, and place it in a chest. - Reload the world - the pocket computer in the chest should now be off. - Spawn in a new pocket computer, and turn it on. The computer in chest will also appear to be on! This bug has been present since pocket computers were added (27th March, 2024). When a pocket computer is added to a player's inventory, it is assigned a unique *per-session* "instance id" , which is used to find the associated computer. Note the "per-session" there - these ids will be reused if you reload the world (or restart the server). In the above bug, we see the following: - The first pocket computer is assigned an instance id of 0. - After reloading, the second pocket computer is assigned an instance id of 0. - If the first pocket computer was in our inventory, it'd be ticked and assigned a new instance id. However, because it's in an inventory, it keeps its old one. - Both computers look up their client-side computer state and get the same value, meaning the first pocket computer mirrors the second! To fix this, we now ensure instance ids are entirely unique (not just per-session). Rather than sequentially assigning an int, we now use a random UUID (we probably could get away with a random long, but this feels more idiomatic). This has a couple of user-visible changes: - /computercraft no longer lists instance ids outside of dumping an individual computer. - The @c[instance=...] selector uses UUIDs. We still use int instance ids for the legacy selector, but that'll be removed in a later MC version. - Pocket computers now store a UUID rather than an int. Related to this change (I made this change first, but then they got kinda mixed up together), we now only create PocketComputerData when receiving server data. This makes the code a little uglier in some places (the data may now be null), but means we don't populate the client-side pocket computer map with computers the server doesn't know about.
2024-03-17 12:21:21 +00:00
copy.instanceID = null;
}
@Override
public Component getName() {
return hasCustomName()
? Component.literal(label)
: Component.translatable(getBlockState().getBlock().getDescriptionId());
}
@Override
public boolean hasCustomName() {
return !Strings.isNullOrEmpty(label);
}
@Nullable
@Override
public Component getCustomName() {
return hasCustomName() ? Component.literal(label) : null;
}
@Override
public Component getDisplayName() {
return Nameable.super.getDisplayName();
}
}