2023-03-15 21:52:13 +00:00
|
|
|
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: LicenseRef-CCPL
|
|
|
|
|
2019-01-12 17:51:26 +00:00
|
|
|
package dan200.computercraft.client;
|
|
|
|
|
2024-01-30 22:00:36 +00:00
|
|
|
import com.mojang.brigadier.CommandDispatcher;
|
|
|
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
|
|
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
|
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
2022-11-09 20:10:24 +00:00
|
|
|
import dan200.computercraft.api.ComputerCraftAPI;
|
2024-01-16 23:00:05 +00:00
|
|
|
import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
|
2024-01-30 22:00:36 +00:00
|
|
|
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
2021-06-05 09:09:28 +00:00
|
|
|
import dan200.computercraft.client.gui.*;
|
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
|
|
|
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
2022-11-08 10:46:09 +00:00
|
|
|
import dan200.computercraft.client.render.RenderTypes;
|
2022-11-09 23:58:56 +00:00
|
|
|
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
|
|
|
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
2022-07-28 18:02:38 +00:00
|
|
|
import dan200.computercraft.client.turtle.TurtleModemModeller;
|
2023-07-22 19:51:50 +00:00
|
|
|
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
2022-11-06 18:27:21 +00:00
|
|
|
import dan200.computercraft.core.util.Colour;
|
2022-11-06 17:14:53 +00:00
|
|
|
import dan200.computercraft.shared.ModRegistry;
|
2024-01-30 22:00:36 +00:00
|
|
|
import dan200.computercraft.shared.command.CommandComputerCraft;
|
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 dan200.computercraft.shared.computer.core.ComputerState;
|
2024-01-30 22:00:36 +00:00
|
|
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
2022-11-09 23:58:56 +00:00
|
|
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
|
|
|
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
|
|
|
import dan200.computercraft.shared.media.items.DiskItem;
|
2024-01-30 22:00:36 +00:00
|
|
|
import net.minecraft.Util;
|
2023-08-27 17:02:51 +00:00
|
|
|
import net.minecraft.client.Minecraft;
|
2022-11-08 10:46:09 +00:00
|
|
|
import net.minecraft.client.color.item.ItemColor;
|
2021-08-03 20:46:53 +00:00
|
|
|
import net.minecraft.client.gui.screens.MenuScreens;
|
2024-01-31 20:55:14 +00:00
|
|
|
import net.minecraft.client.gui.screens.Screen;
|
|
|
|
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
2022-11-09 23:58:56 +00:00
|
|
|
import net.minecraft.client.multiplayer.ClientLevel;
|
2022-11-08 10:46:09 +00:00
|
|
|
import net.minecraft.client.renderer.ShaderInstance;
|
|
|
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
2023-03-15 21:04:11 +00:00
|
|
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
2022-11-09 23:58:56 +00:00
|
|
|
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
2021-08-03 20:46:53 +00:00
|
|
|
import net.minecraft.client.renderer.item.ItemProperties;
|
2024-01-30 22:00:36 +00:00
|
|
|
import net.minecraft.network.chat.Component;
|
2021-08-03 20:46:53 +00:00
|
|
|
import net.minecraft.resources.ResourceLocation;
|
2023-08-27 17:02:51 +00:00
|
|
|
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
2022-12-08 19:45:02 +00:00
|
|
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
2024-04-25 19:17:43 +00:00
|
|
|
import net.minecraft.util.FastColor;
|
2022-11-09 23:58:56 +00:00
|
|
|
import net.minecraft.world.entity.LivingEntity;
|
2024-01-31 20:55:14 +00:00
|
|
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
|
|
|
import net.minecraft.world.inventory.MenuType;
|
2021-08-03 20:46:53 +00:00
|
|
|
import net.minecraft.world.item.Item;
|
2022-11-08 10:46:09 +00:00
|
|
|
import net.minecraft.world.item.ItemStack;
|
2024-04-25 19:17:43 +00:00
|
|
|
import net.minecraft.world.item.component.DyedItemColor;
|
2022-11-08 10:46:09 +00:00
|
|
|
import net.minecraft.world.level.ItemLike;
|
|
|
|
|
2022-11-09 23:58:56 +00:00
|
|
|
import javax.annotation.Nullable;
|
2024-01-30 22:00:36 +00:00
|
|
|
import java.io.File;
|
2022-11-08 10:46:09 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.function.BiConsumer;
|
|
|
|
import java.util.function.Consumer;
|
2021-06-05 10:36:08 +00:00
|
|
|
import java.util.function.Supplier;
|
2019-01-12 17:51:26 +00:00
|
|
|
|
|
|
|
/**
|
2022-11-08 10:46:09 +00:00
|
|
|
* Registers client-side objects, such as {@link BlockEntityRendererProvider}s and
|
|
|
|
* {@link MenuScreens.ScreenConstructor}.
|
|
|
|
* <p>
|
|
|
|
* The functions in this class should be called from a loader-specific class.
|
|
|
|
*
|
|
|
|
* @see ModRegistry The common registry for actual game objects.
|
2019-01-12 17:51:26 +00:00
|
|
|
*/
|
2019-03-29 21:21:39 +00:00
|
|
|
public final class ClientRegistry {
|
2022-11-08 10:46:09 +00:00
|
|
|
private ClientRegistry() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register any client-side objects which don't have to be done on the main thread.
|
|
|
|
*/
|
|
|
|
public static void register() {
|
2023-03-15 21:04:11 +00:00
|
|
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), MonitorBlockEntityRenderer::new);
|
|
|
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
|
|
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
|
|
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new);
|
2022-11-08 10:46:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register any client-side objects which must be done on the main thread.
|
2024-03-22 20:19:32 +00:00
|
|
|
*
|
|
|
|
* @param itemProperties Callback to register item properties.
|
2022-11-08 10:46:09 +00:00
|
|
|
*/
|
2024-03-22 20:19:32 +00:00
|
|
|
public static void registerMainThread(RegisterItemProperty itemProperties) {
|
|
|
|
registerItemProperty(itemProperties, "state",
|
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
|
|
|
new UnclampedPropertyFunction((stack, world, player, random) -> {
|
|
|
|
var computer = ClientPocketComputers.get(stack);
|
|
|
|
return (computer == null ? ComputerState.OFF : computer.getState()).ordinal();
|
|
|
|
}),
|
2022-11-08 10:46:09 +00:00
|
|
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
|
|
|
);
|
2024-03-22 20:19:32 +00:00
|
|
|
registerItemProperty(itemProperties, "coloured",
|
2024-04-25 19:17:43 +00:00
|
|
|
(stack, world, player, random) -> DyedItemColor.getOrDefault(stack, -1) != -1 ? 1 : 0,
|
2022-11-08 10:46:09 +00:00
|
|
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-01-31 20:55:14 +00:00
|
|
|
public static void registerMenuScreens(RegisterMenuScreen register) {
|
|
|
|
register.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
|
|
|
register.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
|
|
|
register.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
|
|
|
register.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
|
|
|
|
|
|
|
|
register.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
|
|
|
|
register.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
|
|
|
|
register.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
|
|
|
|
|
|
|
|
register.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface RegisterMenuScreen {
|
|
|
|
<M extends AbstractContainerMenu, U extends Screen & MenuAccess<M>> void register(MenuType<? extends M> type, MenuScreens.ScreenConstructor<M, U> factory);
|
|
|
|
}
|
|
|
|
|
2024-01-16 23:00:05 +00:00
|
|
|
public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) {
|
|
|
|
register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
|
|
|
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
|
|
|
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
|
|
|
|
));
|
|
|
|
register.register(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
|
|
|
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
|
|
|
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
|
|
|
|
));
|
|
|
|
register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
|
|
|
|
register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
|
|
|
|
register.register(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
|
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
@SafeVarargs
|
2024-03-22 20:19:32 +00:00
|
|
|
private static void registerItemProperty(RegisterItemProperty itemProperties, String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
2022-11-09 20:10:24 +00:00
|
|
|
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
2024-03-22 20:19:32 +00:00
|
|
|
for (var item : items) itemProperties.register(item.get(), id, getter);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register an item property via {@link ItemProperties#register}. Forge and Fabric expose different methods, so we
|
|
|
|
* supply this via mod-loader-specific code.
|
|
|
|
*/
|
|
|
|
public interface RegisterItemProperty {
|
|
|
|
void register(Item item, ResourceLocation name, ClampedItemPropertyFunction property);
|
2022-11-08 10:46:09 +00:00
|
|
|
}
|
|
|
|
|
2023-08-27 17:02:51 +00:00
|
|
|
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
|
|
|
register.accept(GuiSprites.initialise(minecraft.getTextureManager()));
|
|
|
|
}
|
|
|
|
|
2019-03-29 21:21:39 +00:00
|
|
|
private static final String[] EXTRA_MODELS = new String[]{
|
2022-10-22 10:55:30 +00:00
|
|
|
"block/turtle_colour",
|
|
|
|
"block/turtle_elf_overlay",
|
2023-03-31 17:14:44 +00:00
|
|
|
"block/turtle_rainbow_overlay",
|
|
|
|
"block/turtle_trans_overlay",
|
2019-01-12 17:51:26 +00:00
|
|
|
};
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static void registerExtraModels(Consumer<ResourceLocation> register) {
|
2022-11-09 20:10:24 +00:00
|
|
|
for (var model : EXTRA_MODELS) register.accept(new ResourceLocation(ComputerCraftAPI.MOD_ID, model));
|
2023-07-22 19:51:50 +00:00
|
|
|
TurtleUpgradeModellers.getDependencies().forEach(register);
|
2019-01-12 17:51:26 +00:00
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
|
|
|
register.accept(
|
2024-04-25 19:17:43 +00:00
|
|
|
(stack, layer) -> layer == 1 ? DiskItem.getColour(stack) : -1,
|
2022-11-06 17:14:53 +00:00
|
|
|
ModRegistry.Items.DISK.get()
|
2020-04-30 10:33:31 +00:00
|
|
|
);
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
register.accept(
|
2024-04-25 19:17:43 +00:00
|
|
|
(stack, layer) -> layer == 1 ? DyedItemColor.getOrDefault(stack, Colour.BLUE.getARGB()) : -1,
|
2022-11-06 17:14:53 +00:00
|
|
|
ModRegistry.Items.TREASURE_DISK.get()
|
2019-02-10 09:55:06 +00:00
|
|
|
);
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
register.accept(ClientRegistry::getPocketColour, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get());
|
|
|
|
register.accept(ClientRegistry::getPocketColour, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
|
2022-11-03 23:43:14 +00:00
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
register.accept(ClientRegistry::getTurtleColour, ModRegistry.Blocks.TURTLE_NORMAL.get());
|
|
|
|
register.accept(ClientRegistry::getTurtleColour, ModRegistry.Blocks.TURTLE_ADVANCED.get());
|
2021-06-05 10:36:08 +00:00
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
private static int getPocketColour(ItemStack stack, int layer) {
|
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
|
|
|
return switch (layer) {
|
2024-04-25 19:17:43 +00:00
|
|
|
default -> -1;
|
|
|
|
case 1 -> DyedItemColor.getOrDefault(stack, -1); // Frame colour
|
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
|
|
|
case 2 -> { // Light colour
|
|
|
|
var computer = ClientPocketComputers.get(stack);
|
2024-04-25 19:17:43 +00:00
|
|
|
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getARGB() : FastColor.ARGB32.opaque(computer.getLightState());
|
2022-11-08 10:46:09 +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.
2024-03-17 12:21:21 +00:00
|
|
|
};
|
2021-06-05 09:09:28 +00:00
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
private static int getTurtleColour(ItemStack stack, int layer) {
|
2024-04-25 19:17:43 +00:00
|
|
|
return layer == 0 ? DyedItemColor.getOrDefault(stack, -1) : -1;
|
2022-11-08 10:46:09 +00:00
|
|
|
}
|
2021-06-05 10:36:08 +00:00
|
|
|
|
2022-12-08 19:45:02 +00:00
|
|
|
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
2022-11-08 10:46:09 +00:00
|
|
|
RenderTypes.registerShaders(resources, load);
|
2021-06-05 09:09:28 +00:00
|
|
|
}
|
2022-11-09 23:58:56 +00:00
|
|
|
|
|
|
|
private record UnclampedPropertyFunction(
|
|
|
|
ClampedItemPropertyFunction function
|
|
|
|
) implements ClampedItemPropertyFunction {
|
|
|
|
@Override
|
|
|
|
public float unclampedCall(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int layer) {
|
|
|
|
return function.unclampedCall(stack, level, entity, layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Deprecated
|
|
|
|
@Override
|
|
|
|
public float call(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int layer) {
|
|
|
|
return function.unclampedCall(stack, level, entity, layer);
|
|
|
|
}
|
|
|
|
}
|
2024-01-30 22:00:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Register client-side commands.
|
|
|
|
*
|
|
|
|
* @param dispatcher The dispatcher to register the commands to.
|
|
|
|
* @param sendError A function to send an error message.
|
|
|
|
* @param <T> The type of the client-side command context.
|
|
|
|
*/
|
|
|
|
public static <T> void registerClientCommands(CommandDispatcher<T> dispatcher, BiConsumer<T, Component> sendError) {
|
|
|
|
dispatcher.register(LiteralArgumentBuilder.<T>literal(CommandComputerCraft.CLIENT_OPEN_FOLDER)
|
|
|
|
.requires(x -> Minecraft.getInstance().getSingleplayerServer() != null)
|
|
|
|
.then(RequiredArgumentBuilder.<T, Integer>argument("computer_id", IntegerArgumentType.integer(0))
|
|
|
|
.executes(c -> handleOpenComputerCommand(c.getSource(), sendError, c.getArgument("computer_id", Integer.class)))
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle the {@link CommandComputerCraft#CLIENT_OPEN_FOLDER} command.
|
|
|
|
*
|
|
|
|
* @param context The command context.
|
|
|
|
* @param sendError A function to send an error message.
|
|
|
|
* @param id The computer's id.
|
|
|
|
* @param <T> The type of the client-side command context.
|
|
|
|
* @return {@code 1} if a folder was opened, {@code 0} otherwise.
|
|
|
|
*/
|
|
|
|
private static <T> int handleOpenComputerCommand(T context, BiConsumer<T, Component> sendError, int id) {
|
|
|
|
var server = Minecraft.getInstance().getSingleplayerServer();
|
|
|
|
if (server == null) {
|
|
|
|
sendError.accept(context, Component.literal("Not on a single-player server"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
var file = new File(ServerContext.get(server).storageDir().toFile(), "computer/" + id);
|
|
|
|
if (!file.isDirectory()) {
|
|
|
|
sendError.accept(context, Component.literal("Computer's folder does not exist"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Util.getPlatform().openFile(file);
|
|
|
|
return 1;
|
|
|
|
}
|
2019-01-12 17:51:26 +00:00
|
|
|
}
|