mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-24 16:07:01 +00:00
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.
This commit is contained in:
parent
1a5dc92bd4
commit
5d8c46c7e6
@ -113,6 +113,8 @@ sourceSets.all {
|
|||||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||||
option("NullAway:CheckOptionalEmptiness")
|
option("NullAway:CheckOptionalEmptiness")
|
||||||
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||||
|
|
||||||
|
excludedPaths = ".*/jmh_generated/.*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import dan200.computercraft.core.util.Colour;
|
|||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||||
import dan200.computercraft.shared.common.IColouredItem;
|
import dan200.computercraft.shared.common.IColouredItem;
|
||||||
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||||
@ -91,7 +92,10 @@ public final class ClientRegistry {
|
|||||||
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
||||||
|
|
||||||
registerItemProperty("state",
|
registerItemProperty("state",
|
||||||
new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()),
|
new UnclampedPropertyFunction((stack, world, player, random) -> {
|
||||||
|
var computer = ClientPocketComputers.get(stack);
|
||||||
|
return (computer == null ? ComputerState.OFF : computer.getState()).ordinal();
|
||||||
|
}),
|
||||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||||
);
|
);
|
||||||
registerItemProperty("coloured",
|
registerItemProperty("coloured",
|
||||||
@ -155,17 +159,14 @@ public final class ClientRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int getPocketColour(ItemStack stack, int layer) {
|
private static int getPocketColour(ItemStack stack, int layer) {
|
||||||
switch (layer) {
|
return switch (layer) {
|
||||||
case 0:
|
default -> 0xFFFFFF;
|
||||||
default:
|
case 1 -> IColouredItem.getColourBasic(stack); // Frame colour
|
||||||
return 0xFFFFFF;
|
case 2 -> { // Light colour
|
||||||
case 1: // Frame colour
|
var computer = ClientPocketComputers.get(stack);
|
||||||
return IColouredItem.getColourBasic(stack);
|
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||||
case 2: { // Light colour
|
|
||||||
var light = ClientPocketComputers.get(stack).getLightState();
|
|
||||||
return light == -1 ? Colour.BLACK.getHex() : light;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getTurtleColour(ItemStack stack, int layer) {
|
private static int getTurtleColour(ItemStack stack, int layer) {
|
||||||
|
@ -67,14 +67,12 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||||
var computer = ClientPocketComputers.get(instanceId, terminal.colour);
|
ClientPocketComputers.setState(instanceId, state, lightState, terminal);
|
||||||
computer.setState(state, lightState);
|
|
||||||
if (terminal.hasTerminal()) computer.setTerminal(terminal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePocketComputerDeleted(int instanceId) {
|
public void handlePocketComputerDeleted(UUID instanceId) {
|
||||||
ClientPocketComputers.remove(instanceId);
|
ClientPocketComputers.remove(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,21 +4,26 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.pocket;
|
package dan200.computercraft.client.pocket;
|
||||||
|
|
||||||
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.ServerComputer;
|
||||||
|
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||||
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps {@link ServerComputer#getInstanceID()} to locals {@link PocketComputerData}.
|
* Maps {@link ServerComputer#getInstanceUUID()} to locals {@link PocketComputerData}.
|
||||||
* <p>
|
* <p>
|
||||||
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
|
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
|
||||||
*/
|
*/
|
||||||
public final class ClientPocketComputers {
|
public final class ClientPocketComputers {
|
||||||
private static final Int2ObjectMap<PocketComputerData> instances = new Int2ObjectOpenHashMap<>();
|
private static final Map<UUID, PocketComputerData> instances = new HashMap<>();
|
||||||
|
|
||||||
private ClientPocketComputers() {
|
private ClientPocketComputers() {
|
||||||
}
|
}
|
||||||
@ -27,25 +32,32 @@ public final class ClientPocketComputers {
|
|||||||
instances.clear();
|
instances.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void remove(int id) {
|
public static void remove(UUID id) {
|
||||||
instances.remove(id);
|
instances.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or create a pocket computer.
|
* Set the state of a pocket computer.
|
||||||
*
|
*
|
||||||
* @param instanceId The instance ID of the pocket computer.
|
* @param instanceId The instance ID of the pocket computer.
|
||||||
* @param advanced Whether this computer has an advanced terminal.
|
* @param state The computer state of the pocket computer.
|
||||||
* @return The pocket computer data.
|
* @param lightColour The current colour of the modem light.
|
||||||
|
* @param terminalData The current terminal contents.
|
||||||
*/
|
*/
|
||||||
public static PocketComputerData get(int instanceId, boolean advanced) {
|
public static void setState(UUID instanceId, ComputerState state, int lightColour, TerminalState terminalData) {
|
||||||
var computer = instances.get(instanceId);
|
var computer = instances.get(instanceId);
|
||||||
if (computer == null) instances.put(instanceId, computer = new PocketComputerData(advanced));
|
if (computer == null) {
|
||||||
return computer;
|
var terminal = new NetworkedTerminal(terminalData.width, terminalData.height, terminalData.colour);
|
||||||
|
instances.put(instanceId, computer = new PocketComputerData(state, lightColour, terminal));
|
||||||
|
} else {
|
||||||
|
computer.setState(state, lightColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (terminalData.hasTerminal()) terminalData.apply(computer.getTerminal());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PocketComputerData get(ItemStack stack) {
|
public static @Nullable PocketComputerData get(ItemStack stack) {
|
||||||
var family = stack.getItem() instanceof PocketComputerItem computer ? computer.getFamily() : ComputerFamily.NORMAL;
|
var id = PocketComputerItem.getInstanceID(stack);
|
||||||
return get(PocketComputerItem.getInstanceID(stack), family != ComputerFamily.NORMAL);
|
return id == null ? null : instances.get(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.pocket;
|
package dan200.computercraft.client.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
|
||||||
import dan200.computercraft.shared.config.Config;
|
|
||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,20 +18,22 @@ import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
|||||||
* @see ClientPocketComputers The registry which holds pocket computers.
|
* @see ClientPocketComputers The registry which holds pocket computers.
|
||||||
* @see PocketServerComputer The server-side pocket computer.
|
* @see PocketServerComputer The server-side pocket computer.
|
||||||
*/
|
*/
|
||||||
public class PocketComputerData {
|
public final class PocketComputerData {
|
||||||
private final NetworkedTerminal terminal;
|
private final NetworkedTerminal terminal;
|
||||||
private ComputerState state = ComputerState.OFF;
|
private ComputerState state;
|
||||||
private int lightColour = -1;
|
private int lightColour;
|
||||||
|
|
||||||
public PocketComputerData(boolean colour) {
|
PocketComputerData(ComputerState state, int lightColour, NetworkedTerminal terminal) {
|
||||||
terminal = new NetworkedTerminal(Config.pocketTermWidth, Config.pocketTermHeight, colour);
|
this.state = state;
|
||||||
|
this.lightColour = lightColour;
|
||||||
|
this.terminal = terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLightState() {
|
public int getLightState() {
|
||||||
return state != ComputerState.OFF ? lightColour : -1;
|
return state != ComputerState.OFF ? lightColour : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Terminal getTerminal() {
|
public NetworkedTerminal getTerminal() {
|
||||||
return terminal;
|
return terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,12 +41,8 @@ public class PocketComputerData {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setState(ComputerState state, int lightColour) {
|
void setState(ComputerState state, int lightColour) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.lightColour = lightColour;
|
this.lightColour = lightColour;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTerminal(TerminalState state) {
|
|
||||||
state.apply(terminal);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import dan200.computercraft.client.pocket.ClientPocketComputers;
|
|||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
|
import dan200.computercraft.shared.config.Config;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@ -32,10 +33,16 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
@Override
|
@Override
|
||||||
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
|
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
|
||||||
var computer = ClientPocketComputers.get(stack);
|
var computer = ClientPocketComputers.get(stack);
|
||||||
var terminal = computer.getTerminal();
|
var terminal = computer == null ? null : computer.getTerminal();
|
||||||
|
|
||||||
var termWidth = terminal.getWidth();
|
int termWidth, termHeight;
|
||||||
var termHeight = terminal.getHeight();
|
if (terminal == null) {
|
||||||
|
termWidth = Config.pocketTermWidth;
|
||||||
|
termHeight = Config.pocketTermHeight;
|
||||||
|
} else {
|
||||||
|
termWidth = terminal.getWidth();
|
||||||
|
termHeight = terminal.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
var width = termWidth * FONT_WIDTH + MARGIN * 2;
|
var width = termWidth * FONT_WIDTH + MARGIN * 2;
|
||||||
var height = termHeight * FONT_HEIGHT + MARGIN * 2;
|
var height = termHeight * FONT_HEIGHT + MARGIN * 2;
|
||||||
@ -60,14 +67,15 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
||||||
|
|
||||||
// Render the light
|
// Render the light
|
||||||
var lightColour = ClientPocketComputers.get(stack).getLightState();
|
var lightColour = computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||||
if (lightColour == -1) lightColour = Colour.BLACK.getHex();
|
|
||||||
renderLight(transform, bufferSource, lightColour, width, height);
|
renderLight(transform, bufferSource, lightColour, width, height);
|
||||||
|
|
||||||
FixedWidthFontRenderer.drawTerminal(
|
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
|
||||||
FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL)),
|
if (terminal == null) {
|
||||||
MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN
|
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, width, height);
|
||||||
);
|
} else {
|
||||||
|
FixedWidthFontRenderer.drawTerminal(quadEmitter, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
transform.popPose();
|
transform.popPose();
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ public final class CommandComputerCraft {
|
|||||||
} else if (b.getLevel() == world) {
|
} else if (b.getLevel() == world) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return Integer.compare(a.getInstanceID(), b.getInstanceID());
|
return a.getInstanceUUID().compareTo(b.getInstanceUUID());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -159,7 +159,8 @@ public final class CommandComputerCraft {
|
|||||||
*/
|
*/
|
||||||
private static int dumpComputer(CommandSourceStack source, ServerComputer computer) {
|
private static int dumpComputer(CommandSourceStack source, ServerComputer computer) {
|
||||||
var table = new TableBuilder("Dump");
|
var table = new TableBuilder("Dump");
|
||||||
table.row(header("Instance"), text(Integer.toString(computer.getInstanceID())));
|
table.row(header("Instance ID"), text(Integer.toString(computer.getInstanceID())));
|
||||||
|
table.row(header("Instance UUID"), text(computer.getInstanceUUID().toString()));
|
||||||
table.row(header("Id"), text(Integer.toString(computer.getID())));
|
table.row(header("Id"), text(Integer.toString(computer.getID())));
|
||||||
table.row(header("Label"), text(computer.getLabel()));
|
table.row(header("Label"), text(computer.getLabel()));
|
||||||
table.row(header("On"), bool(computer.isOn()));
|
table.row(header("On"), bool(computer.isOn()));
|
||||||
@ -338,11 +339,7 @@ public final class CommandComputerCraft {
|
|||||||
if (computer == null) {
|
if (computer == null) {
|
||||||
out.append("#" + computerId + " ").append(coloured("(unloaded)", ChatFormatting.GRAY));
|
out.append("#" + computerId + " ").append(coloured("(unloaded)", ChatFormatting.GRAY));
|
||||||
} else {
|
} else {
|
||||||
out.append(link(
|
out.append(makeComputerDumpCommand(computer));
|
||||||
text("#" + computerId + " ").append(coloured("(instance " + computer.getInstanceID() + ")", ChatFormatting.GRAY)),
|
|
||||||
makeComputerCommand("dump", computer),
|
|
||||||
Component.translatable("commands.computercraft.dump.action")
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// And, if we're a player, some useful links
|
// And, if we're a player, some useful links
|
||||||
@ -372,10 +369,6 @@ public final class CommandComputerCraft {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String makeComputerCommand(String command, ServerComputer computer) {
|
|
||||||
return String.format("/computercraft %s @c[instance=%d]", command, computer.getInstanceID());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Component linkPosition(CommandSourceStack context, ServerComputer computer) {
|
private static Component linkPosition(CommandSourceStack context, ServerComputer computer) {
|
||||||
if (ModRegistry.Permissions.PERMISSION_TP.test(context)) {
|
if (ModRegistry.Permissions.PERMISSION_TP.test(context)) {
|
||||||
return link(
|
return link(
|
||||||
|
@ -16,7 +16,10 @@ import dan200.computercraft.shared.computer.core.ServerContext;
|
|||||||
import net.minecraft.advancements.critereon.MinMaxBounds;
|
import net.minecraft.advancements.critereon.MinMaxBounds;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.commands.SharedSuggestionProvider;
|
import net.minecraft.commands.SharedSuggestionProvider;
|
||||||
|
import net.minecraft.commands.arguments.UuidArgument;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.ComponentContents;
|
||||||
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
@ -30,17 +33,21 @@ import java.util.stream.Stream;
|
|||||||
import static dan200.computercraft.shared.command.CommandUtils.suggestOnServer;
|
import static dan200.computercraft.shared.command.CommandUtils.suggestOnServer;
|
||||||
import static dan200.computercraft.shared.command.Exceptions.*;
|
import static dan200.computercraft.shared.command.Exceptions.*;
|
||||||
import static dan200.computercraft.shared.command.arguments.ArgumentParserUtils.consume;
|
import static dan200.computercraft.shared.command.arguments.ArgumentParserUtils.consume;
|
||||||
|
import static dan200.computercraft.shared.command.text.ChatHelpers.makeComputerDumpCommand;
|
||||||
|
|
||||||
public record ComputerSelector(
|
public record ComputerSelector(
|
||||||
String selector,
|
String selector,
|
||||||
OptionalInt instanceId,
|
OptionalInt instanceId,
|
||||||
|
@Nullable UUID instanceUuid,
|
||||||
OptionalInt computerId,
|
OptionalInt computerId,
|
||||||
@Nullable String label,
|
@Nullable String label,
|
||||||
@Nullable ComputerFamily family,
|
@Nullable ComputerFamily family,
|
||||||
@Nullable AABB bounds,
|
@Nullable AABB bounds,
|
||||||
@Nullable MinMaxBounds.Doubles range
|
@Nullable MinMaxBounds.Doubles range
|
||||||
) {
|
) {
|
||||||
private static final ComputerSelector all = new ComputerSelector("@c[]", OptionalInt.empty(), OptionalInt.empty(), null, null, null, null);
|
private static final ComputerSelector all = new ComputerSelector("@c[]", OptionalInt.empty(), null, OptionalInt.empty(), null, null, null, null);
|
||||||
|
|
||||||
|
private static UuidArgument uuidArgument = UuidArgument.uuid();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ComputerSelector} which matches all computers.
|
* A {@link ComputerSelector} which matches all computers.
|
||||||
@ -59,8 +66,13 @@ public record ComputerSelector(
|
|||||||
*/
|
*/
|
||||||
public Stream<ServerComputer> find(CommandSourceStack source) {
|
public Stream<ServerComputer> find(CommandSourceStack source) {
|
||||||
var context = ServerContext.get(source.getServer());
|
var context = ServerContext.get(source.getServer());
|
||||||
if (instanceId.isPresent()) {
|
if (instanceId().isPresent()) {
|
||||||
var computer = context.registry().get(instanceId.getAsInt());
|
var computer = context.registry().get(instanceId().getAsInt());
|
||||||
|
return computer != null && matches(source, computer) ? Stream.of(computer) : Stream.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instanceUuid() != null) {
|
||||||
|
var computer = context.registry().get(instanceUuid());
|
||||||
return computer != null && matches(source, computer) ? Stream.of(computer) : Stream.of();
|
return computer != null && matches(source, computer) ? Stream.of(computer) : Stream.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +91,7 @@ public record ComputerSelector(
|
|||||||
if (computers.isEmpty()) throw COMPUTER_ARG_NONE.create(selector);
|
if (computers.isEmpty()) throw COMPUTER_ARG_NONE.create(selector);
|
||||||
if (computers.size() == 1) return computers.iterator().next();
|
if (computers.size() == 1) return computers.iterator().next();
|
||||||
|
|
||||||
var builder = new StringBuilder();
|
var builder = MutableComponent.create(ComponentContents.EMPTY);
|
||||||
var first = true;
|
var first = true;
|
||||||
for (var computer : computers) {
|
for (var computer : computers) {
|
||||||
if (first) {
|
if (first) {
|
||||||
@ -88,12 +100,12 @@ public record ComputerSelector(
|
|||||||
builder.append(", ");
|
builder.append(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(computer.getInstanceID());
|
builder.append(makeComputerDumpCommand(computer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// We have an incorrect number of computers: throw an error
|
// We have an incorrect number of computers: throw an error
|
||||||
throw COMPUTER_ARG_MANY.create(selector, builder.toString());
|
throw COMPUTER_ARG_MANY.create(selector, builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,6 +117,7 @@ public record ComputerSelector(
|
|||||||
*/
|
*/
|
||||||
public boolean matches(CommandSourceStack source, ServerComputer computer) {
|
public boolean matches(CommandSourceStack source, ServerComputer computer) {
|
||||||
return (instanceId().isEmpty() || computer.getInstanceID() == instanceId().getAsInt())
|
return (instanceId().isEmpty() || computer.getInstanceID() == instanceId().getAsInt())
|
||||||
|
&& (instanceUuid() == null || computer.getInstanceUUID().equals(instanceUuid()))
|
||||||
&& (computerId().isEmpty() || computer.getID() == computerId().getAsInt())
|
&& (computerId().isEmpty() || computer.getID() == computerId().getAsInt())
|
||||||
&& (label == null || Objects.equals(computer.getLabel(), label))
|
&& (label == null || Objects.equals(computer.getLabel(), label))
|
||||||
&& (family == null || computer.getFamily() == family)
|
&& (family == null || computer.getFamily() == family)
|
||||||
@ -127,6 +140,7 @@ public record ComputerSelector(
|
|||||||
if (consume(reader, "@c[")) {
|
if (consume(reader, "@c[")) {
|
||||||
parseSelector(builder, reader);
|
parseSelector(builder, reader);
|
||||||
} else {
|
} else {
|
||||||
|
// TODO(1.20.5): Only parse computer ids here.
|
||||||
var kind = reader.peek();
|
var kind = reader.peek();
|
||||||
if (kind == '@') {
|
if (kind == '@') {
|
||||||
reader.skip();
|
reader.skip();
|
||||||
@ -143,7 +157,7 @@ public record ComputerSelector(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var selector = reader.getString().substring(start, reader.getCursor());
|
var selector = reader.getString().substring(start, reader.getCursor());
|
||||||
return new ComputerSelector(selector, builder.instanceId, builder.computerId, builder.label, builder.family, builder.bounds, builder.range);
|
return new ComputerSelector(selector, builder.instanceId, builder.instanceUuid, builder.computerId, builder.label, builder.family, builder.bounds, builder.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void parseSelector(Builder builder, StringReader reader) throws CommandSyntaxException {
|
private static void parseSelector(Builder builder, StringReader reader) throws CommandSyntaxException {
|
||||||
@ -260,6 +274,7 @@ public record ComputerSelector(
|
|||||||
|
|
||||||
private static final class Builder {
|
private static final class Builder {
|
||||||
private OptionalInt instanceId = OptionalInt.empty();
|
private OptionalInt instanceId = OptionalInt.empty();
|
||||||
|
private @Nullable UUID instanceUuid = null;
|
||||||
private OptionalInt computerId = OptionalInt.empty();
|
private OptionalInt computerId = OptionalInt.empty();
|
||||||
private @Nullable String label;
|
private @Nullable String label;
|
||||||
private @Nullable ComputerFamily family;
|
private @Nullable ComputerFamily family;
|
||||||
@ -282,8 +297,8 @@ public record ComputerSelector(
|
|||||||
var optionList = new Option[]{
|
var optionList = new Option[]{
|
||||||
new Option(
|
new Option(
|
||||||
"instance",
|
"instance",
|
||||||
(reader, builder) -> builder.instanceId = OptionalInt.of(reader.readInt()),
|
(reader, builder) -> builder.instanceUuid = uuidArgument.parse(reader),
|
||||||
suggestComputers(c -> Integer.toString(c.getInstanceID()))
|
suggestComputers(c -> c.getInstanceUUID().toString())
|
||||||
),
|
),
|
||||||
new Option(
|
new Option(
|
||||||
"id",
|
"id",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.command.text;
|
package dan200.computercraft.shared.command.text;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.chat.ClickEvent;
|
import net.minecraft.network.chat.ClickEvent;
|
||||||
@ -73,4 +74,16 @@ public final class ChatHelpers {
|
|||||||
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("gui.computercraft.tooltip.copy")))
|
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("gui.computercraft.tooltip.copy")))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String makeComputerCommand(String command, ServerComputer computer) {
|
||||||
|
return String.format("/computercraft %s @c[instance=%s]", command, computer.getInstanceUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Component makeComputerDumpCommand(ServerComputer computer) {
|
||||||
|
return link(
|
||||||
|
text("#" + computer.getID()),
|
||||||
|
makeComputerCommand("dump", computer),
|
||||||
|
Component.translatable("commands.computercraft.dump.action")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,14 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class AbstractComputerBlockEntity extends BlockEntity implements IComputerBlockEntity, Nameable, MenuProvider {
|
public abstract class AbstractComputerBlockEntity extends BlockEntity implements IComputerBlockEntity, Nameable, MenuProvider {
|
||||||
private static final String NBT_ID = "ComputerId";
|
private static final String NBT_ID = "ComputerId";
|
||||||
private static final String NBT_LABEL = "Label";
|
private static final String NBT_LABEL = "Label";
|
||||||
private static final String NBT_ON = "On";
|
private static final String NBT_ON = "On";
|
||||||
|
|
||||||
private int instanceID = -1;
|
private @Nullable UUID instanceID = null;
|
||||||
private int computerID = -1;
|
private int computerID = -1;
|
||||||
protected @Nullable String label = null;
|
protected @Nullable String label = null;
|
||||||
private boolean on = false;
|
private boolean on = false;
|
||||||
@ -66,7 +67,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
|
|
||||||
var computer = getServerComputer();
|
var computer = getServerComputer();
|
||||||
if (computer != null) computer.close();
|
if (computer != null) computer.close();
|
||||||
instanceID = -1;
|
instanceID = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -401,7 +402,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void transferStateFrom(AbstractComputerBlockEntity copy) {
|
protected void transferStateFrom(AbstractComputerBlockEntity copy) {
|
||||||
if (copy.computerID != computerID || copy.instanceID != instanceID) {
|
if (copy.computerID != computerID || !Objects.equals(copy.instanceID, instanceID)) {
|
||||||
unload();
|
unload();
|
||||||
instanceID = copy.instanceID;
|
instanceID = copy.instanceID;
|
||||||
computerID = copy.computerID;
|
computerID = copy.computerID;
|
||||||
@ -411,7 +412,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
lockCode = copy.lockCode;
|
lockCode = copy.lockCode;
|
||||||
BlockEntityHelpers.updateBlock(this);
|
BlockEntityHelpers.updateBlock(this);
|
||||||
}
|
}
|
||||||
copy.instanceID = -1;
|
copy.instanceID = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -26,11 +26,13 @@ import net.minecraft.server.level.ServerLevel;
|
|||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class ServerComputer implements InputHandler, ComputerEnvironment {
|
public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||||
private final int instanceID;
|
private final int instanceID;
|
||||||
|
private final UUID instanceUUID = UUID.randomUUID();
|
||||||
|
|
||||||
private ServerLevel level;
|
private ServerLevel level;
|
||||||
private BlockPos position;
|
private BlockPos position;
|
||||||
@ -119,9 +121,9 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
return computer.pollAndResetChanges();
|
return computer.pollAndResetChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int register() {
|
public UUID register() {
|
||||||
ServerContext.get(level.getServer()).registry().add(instanceID, this);
|
ServerContext.get(level.getServer()).registry().add(this);
|
||||||
return instanceID;
|
return instanceUUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unload() {
|
void unload() {
|
||||||
@ -130,7 +132,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
unload();
|
unload();
|
||||||
ServerContext.get(level.getServer()).registry().remove(instanceID);
|
ServerContext.get(level.getServer()).registry().remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendToAllInteracting(Function<AbstractContainerMenu, NetworkMessage<ClientNetworkContext>> createPacket) {
|
private void sendToAllInteracting(Function<AbstractContainerMenu, NetworkMessage<ClientNetworkContext>> createPacket) {
|
||||||
@ -150,6 +152,10 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
return instanceID;
|
return instanceID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UUID getInstanceUUID() {
|
||||||
|
return instanceUUID;
|
||||||
|
}
|
||||||
|
|
||||||
public int getID() {
|
public int getID() {
|
||||||
return computer.getID();
|
return computer.getID();
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class ServerComputerRegistry {
|
public class ServerComputerRegistry {
|
||||||
private static final Random RANDOM = new Random();
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
private final int sessionId = RANDOM.nextInt();
|
private final int sessionId = RANDOM.nextInt();
|
||||||
private final Int2ObjectMap<ServerComputer> computers = new Int2ObjectOpenHashMap<>();
|
private final Int2ObjectMap<ServerComputer> computersByInstanceId = new Int2ObjectOpenHashMap<>();
|
||||||
|
private final Map<UUID, ServerComputer> computersByInstanceUuid = new HashMap<>();
|
||||||
private int nextInstanceId;
|
private int nextInstanceId;
|
||||||
|
|
||||||
public int getSessionID() {
|
public int getSessionID() {
|
||||||
@ -28,11 +28,16 @@ public class ServerComputerRegistry {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public ServerComputer get(int instanceID) {
|
public ServerComputer get(int instanceID) {
|
||||||
return instanceID >= 0 ? computers.get(instanceID) : null;
|
return instanceID >= 0 ? computersByInstanceId.get(instanceID) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public ServerComputer get(int sessionId, int instanceId) {
|
public ServerComputer get(@Nullable UUID instanceID) {
|
||||||
|
return instanceID != null ? computersByInstanceUuid.get(instanceID) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ServerComputer get(int sessionId, @Nullable UUID instanceId) {
|
||||||
return sessionId == this.sessionId ? get(instanceId) : null;
|
return sessionId == this.sessionId ? get(instanceId) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,28 +55,36 @@ public class ServerComputerRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(int instanceID, ServerComputer computer) {
|
void add(ServerComputer computer) {
|
||||||
remove(instanceID);
|
var instanceID = computer.getInstanceID();
|
||||||
computers.put(instanceID, computer);
|
var instanceUUID = computer.getInstanceUUID();
|
||||||
nextInstanceId = Math.max(nextInstanceId, instanceID + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(int instanceID) {
|
if (computersByInstanceId.containsKey(instanceID)) {
|
||||||
var computer = get(instanceID);
|
throw new IllegalStateException("Duplicate computer " + instanceID);
|
||||||
if (computer != null) {
|
|
||||||
computer.unload();
|
|
||||||
computer.onRemoved();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
computers.remove(instanceID);
|
if (computersByInstanceUuid.containsKey(instanceUUID)) {
|
||||||
|
throw new IllegalStateException("Duplicate computer " + instanceUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
computersByInstanceId.put(instanceID, computer);
|
||||||
|
computersByInstanceUuid.put(instanceUUID, computer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(ServerComputer computer) {
|
||||||
|
computer.unload();
|
||||||
|
computer.onRemoved();
|
||||||
|
computersByInstanceId.remove(computer.getInstanceID());
|
||||||
|
computersByInstanceUuid.remove(computer.getInstanceUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
for (var computer : getComputers()) computer.unload();
|
for (var computer : getComputers()) computer.unload();
|
||||||
computers.clear();
|
computersByInstanceId.clear();
|
||||||
|
computersByInstanceUuid.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<ServerComputer> getComputers() {
|
public Collection<ServerComputer> getComputers() {
|
||||||
return computers.values();
|
return computersByInstanceId.values();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ public class ViewComputerMenu extends ComputerMenuWithoutInventory {
|
|||||||
|
|
||||||
private static boolean canInteractWith(ServerComputer computer, Player player) {
|
private static boolean canInteractWith(ServerComputer computer, Player player) {
|
||||||
// If this computer no longer exists then discard it.
|
// If this computer no longer exists then discard it.
|
||||||
if (ServerContext.get(computer.getLevel().getServer()).registry().get(computer.getInstanceID()) != computer) {
|
if (ServerContext.get(computer.getLevel().getServer()).registry().get(computer.getInstanceUUID()) != computer) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +30,9 @@ public interface ClientNetworkContext {
|
|||||||
|
|
||||||
void handlePlayRecord(BlockPos pos, @Nullable SoundEvent sound, @Nullable String name);
|
void handlePlayRecord(BlockPos pos, @Nullable SoundEvent sound, @Nullable String name);
|
||||||
|
|
||||||
void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal);
|
void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, TerminalState terminal);
|
||||||
|
|
||||||
void handlePocketComputerDeleted(int instanceId);
|
void handlePocketComputerDeleted(UUID instanceId);
|
||||||
|
|
||||||
void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio audio);
|
void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio audio);
|
||||||
|
|
||||||
|
@ -13,24 +13,26 @@ import dan200.computercraft.shared.network.NetworkMessages;
|
|||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides additional data about a client computer, such as its ID and current state.
|
* Provides additional data about a client computer, such as its ID and current state.
|
||||||
*/
|
*/
|
||||||
public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkContext> {
|
public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkContext> {
|
||||||
private final int instanceId;
|
private final UUID clientId;
|
||||||
private final ComputerState state;
|
private final ComputerState state;
|
||||||
private final int lightState;
|
private final int lightState;
|
||||||
private final TerminalState terminal;
|
private final TerminalState terminal;
|
||||||
|
|
||||||
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
||||||
instanceId = computer.getInstanceID();
|
clientId = computer.getInstanceUUID();
|
||||||
state = computer.getState();
|
state = computer.getState();
|
||||||
lightState = computer.getLight();
|
lightState = computer.getLight();
|
||||||
terminal = sendTerminal ? computer.getTerminalState() : new TerminalState((NetworkedTerminal) null);
|
terminal = sendTerminal ? computer.getTerminalState() : new TerminalState((NetworkedTerminal) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PocketComputerDataMessage(FriendlyByteBuf buf) {
|
public PocketComputerDataMessage(FriendlyByteBuf buf) {
|
||||||
instanceId = buf.readVarInt();
|
clientId = buf.readUUID();
|
||||||
state = buf.readEnum(ComputerState.class);
|
state = buf.readEnum(ComputerState.class);
|
||||||
lightState = buf.readVarInt();
|
lightState = buf.readVarInt();
|
||||||
terminal = new TerminalState(buf);
|
terminal = new TerminalState(buf);
|
||||||
@ -38,7 +40,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(FriendlyByteBuf buf) {
|
public void write(FriendlyByteBuf buf) {
|
||||||
buf.writeVarInt(instanceId);
|
buf.writeUUID(clientId);
|
||||||
buf.writeEnum(state);
|
buf.writeEnum(state);
|
||||||
buf.writeVarInt(lightState);
|
buf.writeVarInt(lightState);
|
||||||
terminal.write(buf);
|
terminal.write(buf);
|
||||||
@ -46,7 +48,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClientNetworkContext context) {
|
public void handle(ClientNetworkContext context) {
|
||||||
context.handlePocketComputerData(instanceId, state, lightState, terminal);
|
context.handlePocketComputerData(clientId, state, lightState, terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,21 +9,23 @@ import dan200.computercraft.shared.network.NetworkMessage;
|
|||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
public class PocketComputerDeletedClientMessage implements NetworkMessage<ClientNetworkContext> {
|
public class PocketComputerDeletedClientMessage implements NetworkMessage<ClientNetworkContext> {
|
||||||
private final int instanceId;
|
private final UUID instanceId;
|
||||||
|
|
||||||
public PocketComputerDeletedClientMessage(int instanceId) {
|
public PocketComputerDeletedClientMessage(UUID instanceId) {
|
||||||
this.instanceId = instanceId;
|
this.instanceId = instanceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PocketComputerDeletedClientMessage(FriendlyByteBuf buffer) {
|
public PocketComputerDeletedClientMessage(FriendlyByteBuf buffer) {
|
||||||
instanceId = buffer.readVarInt();
|
instanceId = buffer.readUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(FriendlyByteBuf buf) {
|
public void write(FriendlyByteBuf buf) {
|
||||||
buf.writeVarInt(instanceId);
|
buf.writeUUID(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -190,6 +190,6 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
|||||||
@Override
|
@Override
|
||||||
protected void onRemoved() {
|
protected void onRemoved() {
|
||||||
super.onRemoved();
|
super.onRemoved();
|
||||||
ServerNetworking.sendToAllPlayers(new PocketComputerDeletedClientMessage(getInstanceID()), getLevel().getServer());
|
ServerNetworking.sendToAllPlayers(new PocketComputerDeletedClientMessage(getInstanceUUID()), getLevel().getServer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ import net.minecraft.world.level.Level;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class PocketComputerItem extends Item implements IComputerItem, IMedia, IColouredItem {
|
public class PocketComputerItem extends Item implements IComputerItem, IMedia, IColouredItem {
|
||||||
private static final String NBT_UPGRADE = "Upgrade";
|
private static final String NBT_UPGRADE = "Upgrade";
|
||||||
@ -188,10 +189,9 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PocketServerComputer createServerComputer(ServerLevel level, Entity entity, @Nullable Container inventory, ItemStack stack) {
|
public PocketServerComputer createServerComputer(ServerLevel level, Entity entity, @Nullable Container inventory, ItemStack stack) {
|
||||||
var sessionID = getSessionID(stack);
|
|
||||||
|
|
||||||
var registry = ServerContext.get(level.getServer()).registry();
|
var registry = ServerContext.get(level.getServer()).registry();
|
||||||
var computer = (PocketServerComputer) registry.get(sessionID, getInstanceID(stack));
|
var computer = (PocketServerComputer) registry.get(getSessionID(stack), getInstanceID(stack));
|
||||||
if (computer == null) {
|
if (computer == null) {
|
||||||
var computerID = getComputerID(stack);
|
var computerID = getComputerID(stack);
|
||||||
if (computerID < 0) {
|
if (computerID < 0) {
|
||||||
@ -201,8 +201,9 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
|
|
||||||
computer = new PocketServerComputer(level, entity.blockPosition(), getComputerID(stack), getLabel(stack), getFamily());
|
computer = new PocketServerComputer(level, entity.blockPosition(), getComputerID(stack), getLabel(stack), getFamily());
|
||||||
|
|
||||||
setInstanceID(stack, computer.register());
|
var tag = stack.getOrCreateTag();
|
||||||
setSessionID(stack, registry.getSessionID());
|
tag.putInt(NBT_SESSION, registry.getSessionID());
|
||||||
|
tag.putUUID(NBT_INSTANCE, computer.register());
|
||||||
|
|
||||||
var upgrade = getUpgrade(stack);
|
var upgrade = getUpgrade(stack);
|
||||||
|
|
||||||
@ -267,13 +268,9 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getInstanceID(ItemStack stack) {
|
public static @Nullable UUID getInstanceID(ItemStack stack) {
|
||||||
var nbt = stack.getTag();
|
var nbt = stack.getTag();
|
||||||
return nbt != null && nbt.contains(NBT_INSTANCE) ? nbt.getInt(NBT_INSTANCE) : -1;
|
return nbt != null && nbt.hasUUID(NBT_INSTANCE) ? nbt.getUUID(NBT_INSTANCE) : null;
|
||||||
}
|
|
||||||
|
|
||||||
private static void setInstanceID(ItemStack stack, int instanceID) {
|
|
||||||
stack.getOrCreateTag().putInt(NBT_INSTANCE, instanceID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getSessionID(ItemStack stack) {
|
private static int getSessionID(ItemStack stack) {
|
||||||
@ -281,10 +278,6 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
return nbt != null && nbt.contains(NBT_SESSION) ? nbt.getInt(NBT_SESSION) : -1;
|
return nbt != null && nbt.contains(NBT_SESSION) ? nbt.getInt(NBT_SESSION) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setSessionID(ItemStack stack, int sessionID) {
|
|
||||||
stack.getOrCreateTag().putInt(NBT_SESSION, sessionID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isMarkedOn(ItemStack stack) {
|
private static boolean isMarkedOn(ItemStack stack) {
|
||||||
var nbt = stack.getTag();
|
var nbt = stack.getTag();
|
||||||
return nbt != null && nbt.getBoolean(NBT_ON);
|
return nbt != null && nbt.getBoolean(NBT_ON);
|
||||||
|
@ -22,6 +22,7 @@ import org.junit.jupiter.params.provider.MethodSource;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
@ -39,19 +40,19 @@ class ComputerSelectorTest {
|
|||||||
public static Arguments[] getArgumentTestCases() {
|
public static Arguments[] getArgumentTestCases() {
|
||||||
return new Arguments[]{
|
return new Arguments[]{
|
||||||
// Legacy selectors
|
// Legacy selectors
|
||||||
Arguments.of("@some_label", new ComputerSelector("@some_label", OptionalInt.empty(), OptionalInt.empty(), "some_label", null, null, null)),
|
Arguments.of("@some_label", new ComputerSelector("@some_label", OptionalInt.empty(), null, OptionalInt.empty(), "some_label", null, null, null)),
|
||||||
Arguments.of("~normal", new ComputerSelector("~normal", OptionalInt.empty(), OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
|
Arguments.of("~normal", new ComputerSelector("~normal", OptionalInt.empty(), null, OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
|
||||||
Arguments.of("#123", new ComputerSelector("#123", OptionalInt.empty(), OptionalInt.of(123), null, null, null, null)),
|
Arguments.of("#123", new ComputerSelector("#123", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
|
||||||
Arguments.of("123", new ComputerSelector("123", OptionalInt.of(123), OptionalInt.empty(), null, null, null, null)),
|
Arguments.of("123", new ComputerSelector("123", OptionalInt.of(123), null, OptionalInt.empty(), null, null, null, null)),
|
||||||
// New selectors
|
// New selectors
|
||||||
Arguments.of("@c[]", new ComputerSelector("@c[]", OptionalInt.empty(), OptionalInt.empty(), null, null, null, null)),
|
Arguments.of("@c[]", new ComputerSelector("@c[]", OptionalInt.empty(), null, OptionalInt.empty(), null, null, null, null)),
|
||||||
Arguments.of("@c[instance=123]", new ComputerSelector("@c[instance=123]", OptionalInt.of(123), OptionalInt.empty(), null, null, null, null)),
|
Arguments.of("@c[instance=5e18f505-62f7-46f8-83f3-792f03224724]", new ComputerSelector("@c[instance=5e18f505-62f7-46f8-83f3-792f03224724]", OptionalInt.empty(), UUID.fromString("5e18f505-62f7-46f8-83f3-792f03224724"), OptionalInt.empty(), null, null, null, null)),
|
||||||
Arguments.of("@c[id=123]", new ComputerSelector("@c[id=123]", OptionalInt.empty(), OptionalInt.of(123), null, null, null, null)),
|
Arguments.of("@c[id=123]", new ComputerSelector("@c[id=123]", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
|
||||||
Arguments.of("@c[label=\"foo\"]", new ComputerSelector("@c[label=\"foo\"]", OptionalInt.empty(), OptionalInt.empty(), "foo", null, null, null)),
|
Arguments.of("@c[label=\"foo\"]", new ComputerSelector("@c[label=\"foo\"]", OptionalInt.empty(), null, OptionalInt.empty(), "foo", null, null, null)),
|
||||||
Arguments.of("@c[family=normal]", new ComputerSelector("@c[family=normal]", OptionalInt.empty(), OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
|
Arguments.of("@c[family=normal]", new ComputerSelector("@c[family=normal]", OptionalInt.empty(), null, OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
|
||||||
// Complex selectors
|
// Complex selectors
|
||||||
Arguments.of("@c[ id = 123 , ]", new ComputerSelector("@c[ id = 123 , ]", OptionalInt.empty(), OptionalInt.of(123), null, null, null, null)),
|
Arguments.of("@c[ id = 123 , ]", new ComputerSelector("@c[ id = 123 , ]", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
|
||||||
Arguments.of("@c[id=123,family=normal]", new ComputerSelector("@c[id=123,family=normal]", OptionalInt.empty(), OptionalInt.of(123), null, ComputerFamily.NORMAL, null, null)),
|
Arguments.of("@c[id=123,family=normal]", new ComputerSelector("@c[id=123,family=normal]", OptionalInt.empty(), null, OptionalInt.of(123), null, ComputerFamily.NORMAL, null, null)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ class Pocket_Computer_Test {
|
|||||||
// And ensure its synced to the client.
|
// And ensure its synced to the client.
|
||||||
thenIdle(4)
|
thenIdle(4)
|
||||||
thenOnClient {
|
thenOnClient {
|
||||||
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)
|
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)!!
|
||||||
assertEquals(ComputerState.ON, pocketComputer.state)
|
assertEquals(ComputerState.ON, pocketComputer.state)
|
||||||
|
|
||||||
val term = pocketComputer.terminal
|
val term = pocketComputer.terminal
|
||||||
@ -54,7 +54,7 @@ class Pocket_Computer_Test {
|
|||||||
// And ensure the new computer state and terminal are sent.
|
// And ensure the new computer state and terminal are sent.
|
||||||
thenIdle(4)
|
thenIdle(4)
|
||||||
thenOnClient {
|
thenOnClient {
|
||||||
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)
|
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)!!
|
||||||
assertEquals(ComputerState.BLINKING, pocketComputer.state)
|
assertEquals(ComputerState.BLINKING, pocketComputer.state)
|
||||||
|
|
||||||
val term = pocketComputer.terminal
|
val term = pocketComputer.terminal
|
||||||
|
Loading…
Reference in New Issue
Block a user