mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 05:33:00 +00:00 
			
		
		
		
	Move all event handlers to a common class
While this does now involve a little more indirection, by having a single location for all hooks it's much easier to keep event listeners consistent between loaders. The diff is pretty noisy (I've inlined some classes, and ClientRegistry got a big restructure), but functionality should be the same.
This commit is contained in:
		| @@ -24,6 +24,7 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities; | ||||
| import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraftforge.fml.event.config.ModConfigEvent; | ||||
| import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; | ||||
| import net.minecraftforge.registries.NewRegistryEvent; | ||||
| import net.minecraftforge.registries.RegistryBuilder; | ||||
| @@ -113,4 +114,13 @@ public final class ComputerCraft { | ||||
|         ForgeDetailRegistries.FLUID_STACK.addProvider(FluidData::fill); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void sync(ModConfigEvent.Loading event) { | ||||
|         Config.sync(event.getConfig()); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void sync(ModConfigEvent.Reloading event) { | ||||
|         Config.sync(event.getConfig()); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,33 +5,158 @@ | ||||
|  */ | ||||
| package dan200.computercraft.client; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import com.mojang.blaze3d.audio.Channel; | ||||
| import com.mojang.blaze3d.vertex.PoseStack; | ||||
| import dan200.computercraft.api.turtle.TurtleSide; | ||||
| import dan200.computercraft.client.pocket.ClientPocketComputers; | ||||
| import dan200.computercraft.client.render.CableHighlightRenderer; | ||||
| import dan200.computercraft.client.render.ItemPocketRenderer; | ||||
| import dan200.computercraft.client.render.ItemPrintoutRenderer; | ||||
| import dan200.computercraft.client.render.MonitorHighlightRenderer; | ||||
| import dan200.computercraft.client.sound.SpeakerManager; | ||||
| import dan200.computercraft.shared.CommonHooks; | ||||
| import dan200.computercraft.shared.command.CommandComputerCraft; | ||||
| import dan200.computercraft.shared.computer.core.ServerContext; | ||||
| import dan200.computercraft.shared.media.items.ItemPrintout; | ||||
| import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.ClientPlayerNetworkEvent; | ||||
| import net.minecraftforge.event.level.LevelEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import dan200.computercraft.shared.peripheral.monitor.TileMonitor; | ||||
| import dan200.computercraft.shared.pocket.items.ItemPocketComputer; | ||||
| import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||
| import dan200.computercraft.shared.util.PauseAwareTimer; | ||||
| import net.minecraft.Util; | ||||
| import net.minecraft.client.Camera; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.client.sounds.AudioStream; | ||||
| import net.minecraft.client.sounds.SoundEngine; | ||||
| import net.minecraft.world.InteractionHand; | ||||
| import net.minecraft.world.entity.decoration.ItemFrame; | ||||
| import net.minecraft.world.item.ItemStack; | ||||
| import net.minecraft.world.phys.BlockHitResult; | ||||
| import net.minecraft.world.phys.HitResult; | ||||
| 
 | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public class ClientHooks { | ||||
|     @SubscribeEvent | ||||
|     public static void onWorldUnload(LevelEvent.Unload event) { | ||||
|         if (event.getLevel().isClientSide()) { | ||||
|             ClientMonitor.destroyAll(); | ||||
|             SpeakerManager.reset(); | ||||
| import java.io.File; | ||||
| import java.util.function.Consumer; | ||||
| 
 | ||||
| /** | ||||
|  * Event listeners for client-only code. | ||||
|  * <p> | ||||
|  * This is the client-only version of {@link CommonHooks}, and so should be where all client-specific event handlers are | ||||
|  * defined. | ||||
|  */ | ||||
| public final class ClientHooks { | ||||
|     private ClientHooks() { | ||||
|     } | ||||
| 
 | ||||
|     public static void onTick() { | ||||
|         FrameInfo.onTick(); | ||||
|     } | ||||
| 
 | ||||
|     public static void onRenderTick() { | ||||
|         PauseAwareTimer.tick(Minecraft.getInstance().isPaused()); | ||||
|         FrameInfo.onRenderTick(); | ||||
|     } | ||||
| 
 | ||||
|     public static void onWorldUnload() { | ||||
|         ClientMonitor.destroyAll(); | ||||
|         SpeakerManager.reset(); | ||||
|         ClientPocketComputers.reset(); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean onChatMessage(String message) { | ||||
|         return handleOpenComputerCommand(message); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) { | ||||
|         return CableHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit) | ||||
|             || MonitorHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean onRenderHeldItem( | ||||
|         PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, | ||||
|         float pitch, float equipProgress, float swingProgress, ItemStack stack | ||||
|     ) { | ||||
|         if (stack.getItem() instanceof ItemPocketComputer) { | ||||
|             ItemPocketRenderer.INSTANCE.renderItemFirstPerson(transform, render, lightTexture, hand, pitch, equipProgress, swingProgress, stack); | ||||
|             return true; | ||||
|         } | ||||
|         if (stack.getItem() instanceof ItemPrintout) { | ||||
|             ItemPrintoutRenderer.INSTANCE.renderItemFirstPerson(transform, render, lightTexture, hand, pitch, equipProgress, swingProgress, stack); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean onRenderItemFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int light) { | ||||
|         if (stack.getItem() instanceof ItemPrintout) { | ||||
|             ItemPrintoutRenderer.onRenderInFrame(transform, render, frame, stack, light); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     public static void onPlayStreaming(SoundEngine engine, Channel channel, AudioStream stream) { | ||||
|         SpeakerManager.onPlayStreaming(engine, channel, stream); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handle the {@link CommandComputerCraft#OPEN_COMPUTER} "clientside command". This isn't a true command, as we | ||||
|      * don't want it to actually be visible to the user. | ||||
|      * | ||||
|      * @param message The current chat message. | ||||
|      * @return Whether to cancel sending this message. | ||||
|      */ | ||||
|     private static boolean handleOpenComputerCommand(String message) { | ||||
|         if (!message.startsWith(CommandComputerCraft.OPEN_COMPUTER)) return false; | ||||
| 
 | ||||
|         var server = Minecraft.getInstance().getSingleplayerServer(); | ||||
|         if (server == null) return false; | ||||
| 
 | ||||
|         var idStr = message.substring(CommandComputerCraft.OPEN_COMPUTER.length()).trim(); | ||||
|         int id; | ||||
|         try { | ||||
|             id = Integer.parseInt(idStr); | ||||
|         } catch (NumberFormatException ignore) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         var file = new File(ServerContext.get(server).storageDir().toFile(), "computer/" + id); | ||||
|         if (!file.isDirectory()) return false; | ||||
| 
 | ||||
|         Util.getPlatform().openFile(file); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Add additional information about the currently targeted block to the debug screen. | ||||
|      * | ||||
|      * @param addText A callback which adds a single line of text. | ||||
|      */ | ||||
|     public static void addDebugInfo(Consumer<String> addText) { | ||||
|         var minecraft = Minecraft.getInstance(); | ||||
|         if (!minecraft.options.renderDebug || minecraft.level == null) return; | ||||
|         if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return; | ||||
| 
 | ||||
|         var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos()); | ||||
| 
 | ||||
|         if (tile instanceof TileMonitor monitor) { | ||||
|             addText.accept(""); | ||||
|             addText.accept( | ||||
|                 String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight()) | ||||
|             ); | ||||
|         } else if (tile instanceof TileTurtle turtle) { | ||||
|             addText.accept(""); | ||||
|             addText.accept("Targeted turtle:"); | ||||
|             addText.accept(String.format("Id: %d", turtle.getComputerID())); | ||||
|             addTurtleUpgrade(addText, turtle, TurtleSide.LEFT); | ||||
|             addTurtleUpgrade(addText, turtle, TurtleSide.RIGHT); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onLogIn(ClientPlayerNetworkEvent.LoggingIn event) { | ||||
|         ClientPocketComputers.reset(); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onLogOut(ClientPlayerNetworkEvent.LoggingOut event) { | ||||
|         ClientPocketComputers.reset(); | ||||
|     private static void addTurtleUpgrade(Consumer<String> out, TileTurtle turtle, TurtleSide side) { | ||||
|         var upgrade = turtle.getUpgrade(side); | ||||
|         if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.getUpgradeID())); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -10,9 +10,9 @@ import dan200.computercraft.api.client.ComputerCraftAPIClient; | ||||
| import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; | ||||
| import dan200.computercraft.client.gui.*; | ||||
| import dan200.computercraft.client.pocket.ClientPocketComputers; | ||||
| import dan200.computercraft.client.render.RenderTypes; | ||||
| import dan200.computercraft.client.render.TileEntityMonitorRenderer; | ||||
| import dan200.computercraft.client.render.TileEntityTurtleRenderer; | ||||
| import dan200.computercraft.client.render.TurtleModelLoader; | ||||
| import dan200.computercraft.client.turtle.TurtleModemModeller; | ||||
| import dan200.computercraft.core.util.Colour; | ||||
| import dan200.computercraft.shared.ModRegistry; | ||||
| @@ -21,26 +21,86 @@ import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; | ||||
| import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; | ||||
| import dan200.computercraft.shared.media.items.ItemDisk; | ||||
| import dan200.computercraft.shared.media.items.ItemTreasureDisk; | ||||
| import net.minecraft.client.color.item.ItemColor; | ||||
| import net.minecraft.client.gui.screens.MenuScreens; | ||||
| import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; | ||||
| import net.minecraft.client.renderer.ShaderInstance; | ||||
| import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; | ||||
| import net.minecraft.client.renderer.item.ItemProperties; | ||||
| import net.minecraft.client.renderer.item.ItemPropertyFunction; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| import net.minecraft.server.packs.resources.ResourceManager; | ||||
| import net.minecraft.world.item.Item; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.ModelEvent; | ||||
| import net.minecraftforge.client.event.RegisterColorHandlersEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; | ||||
| import net.minecraft.world.item.ItemStack; | ||||
| import net.minecraft.world.level.ItemLike; | ||||
| import net.minecraft.world.level.block.entity.BlockEntity; | ||||
| import net.minecraft.world.level.block.entity.BlockEntityType; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.function.BiConsumer; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| /** | ||||
|  * Registers textures and models for items. | ||||
|  * 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. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) | ||||
| public final class ClientRegistry { | ||||
|     private ClientRegistry() { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Register any client-side objects which don't have to be done on the main thread. | ||||
|      */ | ||||
|     public static void register() { | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided( | ||||
|             new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_speaker_left"), | ||||
|             new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_speaker_right") | ||||
|         )); | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided( | ||||
|             new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_crafting_table_left"), | ||||
|             new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_crafting_table_right") | ||||
|         )); | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false)); | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true)); | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Register any client-side objects which must be done on the main thread. | ||||
|      */ | ||||
|     public static void registerMainThread() { | ||||
|         MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register(ModRegistry.Menus.COMPUTER.get(), GuiComputer::new); | ||||
|         MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), GuiComputer::new); | ||||
|         MenuScreens.<ContainerComputerBase, NoTermComputerScreen<ContainerComputerBase>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new); | ||||
|         MenuScreens.register(ModRegistry.Menus.TURTLE.get(), GuiTurtle::new); | ||||
| 
 | ||||
|         MenuScreens.register(ModRegistry.Menus.PRINTER.get(), GuiPrinter::new); | ||||
|         MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), GuiDiskDrive::new); | ||||
|         MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), GuiPrintout::new); | ||||
| 
 | ||||
|         MenuScreens.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), GuiComputer::new); | ||||
| 
 | ||||
|         registerItemProperty("state", | ||||
|             (stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal(), | ||||
|             ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED | ||||
|         ); | ||||
|         registerItemProperty("coloured", | ||||
|             (stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0, | ||||
|             ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     @SafeVarargs | ||||
|     @SuppressWarnings("deprecation") | ||||
|     private static void registerItemProperty(String name, ItemPropertyFunction getter, Supplier<? extends Item>... items) { | ||||
|         var id = new ResourceLocation(ComputerCraft.MOD_ID, name); | ||||
|         for (var item : items) ItemProperties.register(item.get(), id, getter); | ||||
|     } | ||||
| 
 | ||||
|     private static final String[] EXTRA_MODELS = new String[]{ | ||||
|         // Turtle upgrades | ||||
|         "block/turtle_modem_normal_off_left", | ||||
| @@ -64,114 +124,63 @@ public final class ClientRegistry { | ||||
|         "block/turtle_elf_overlay", | ||||
|     }; | ||||
| 
 | ||||
|     private ClientRegistry() { | ||||
|     public static void registerExtraModels(Consumer<ResourceLocation> register) { | ||||
|         for (var model : EXTRA_MODELS) register.accept(new ResourceLocation(ComputerCraft.MOD_ID, model)); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void registerModelLoaders(ModelEvent.RegisterGeometryLoaders event) { | ||||
|         event.register("turtle", TurtleModelLoader.INSTANCE); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void registerModels(ModelEvent.RegisterAdditional event) { | ||||
|         for (var model : EXTRA_MODELS) { | ||||
|             event.register(new ResourceLocation(ComputerCraft.MOD_ID, model)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onItemColours(RegisterColorHandlersEvent.Item event) { | ||||
|     public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) { | ||||
|         if (ModRegistry.Items.DISK == null || ModRegistry.Blocks.TURTLE_NORMAL == null) { | ||||
|             ComputerCraft.log.warn("Block/item registration has failed. Skipping registration of item colours."); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         event.register( | ||||
|         register.accept( | ||||
|             (stack, layer) -> layer == 1 ? ((ItemDisk) stack.getItem()).getColour(stack) : 0xFFFFFF, | ||||
|             ModRegistry.Items.DISK.get() | ||||
|         ); | ||||
| 
 | ||||
|         event.register( | ||||
|         register.accept( | ||||
|             (stack, layer) -> layer == 1 ? ItemTreasureDisk.getColour(stack) : 0xFFFFFF, | ||||
|             ModRegistry.Items.TREASURE_DISK.get() | ||||
|         ); | ||||
| 
 | ||||
|         event.register((stack, layer) -> { | ||||
|             switch (layer) { | ||||
|                 case 0: | ||||
|                 default: | ||||
|                     return 0xFFFFFF; | ||||
|                 case 1: // Frame colour | ||||
|                     return IColouredItem.getColourBasic(stack); | ||||
|                 case 2: { // Light colour | ||||
|                     var light = ClientPocketComputers.get(stack).getLightState(); | ||||
|                     return light == -1 ? Colour.BLACK.getHex() : light; | ||||
|                 } | ||||
|         register.accept(ClientRegistry::getPocketColour, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()); | ||||
|         register.accept(ClientRegistry::getPocketColour, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get()); | ||||
| 
 | ||||
|         register.accept(ClientRegistry::getTurtleColour, ModRegistry.Blocks.TURTLE_NORMAL.get()); | ||||
|         register.accept(ClientRegistry::getTurtleColour, ModRegistry.Blocks.TURTLE_ADVANCED.get()); | ||||
|     } | ||||
| 
 | ||||
|     private static int getPocketColour(ItemStack stack, int layer) { | ||||
|         switch (layer) { | ||||
|             case 0: | ||||
|             default: | ||||
|                 return 0xFFFFFF; | ||||
|             case 1: // Frame colour | ||||
|                 return IColouredItem.getColourBasic(stack); | ||||
|             case 2: { // Light colour | ||||
|                 var light = ClientPocketComputers.get(stack).getLightState(); | ||||
|                 return light == -1 ? Colour.BLACK.getHex() : light; | ||||
|             } | ||||
|         }, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get()); | ||||
| 
 | ||||
|         // Setup turtle colours | ||||
|         event.register( | ||||
|             (stack, tintIndex) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF, | ||||
|             ModRegistry.Blocks.TURTLE_NORMAL.get(), ModRegistry.Blocks.TURTLE_ADVANCED.get() | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void setupClient(FMLClientSetupEvent event) { | ||||
|         // Setup TESRs | ||||
|         BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new); | ||||
|         BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new); | ||||
|         BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new); | ||||
|         BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new); | ||||
| 
 | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided( | ||||
|             new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_speaker_left"), | ||||
|             new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_speaker_right") | ||||
|         )); | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided( | ||||
|             new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_crafting_table_left"), | ||||
|             new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_crafting_table_right") | ||||
|         )); | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false)); | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true)); | ||||
|         ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem()); | ||||
| 
 | ||||
|         event.enqueueWork(() -> { | ||||
|             registerContainers(); | ||||
| 
 | ||||
|             registerItemProperty("state", | ||||
|                 (stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal(), | ||||
|                 ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED | ||||
|             ); | ||||
|             registerItemProperty("coloured", | ||||
|                 (stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0, | ||||
|                 ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED | ||||
|             ); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @SafeVarargs | ||||
|     private static void registerItemProperty(String name, ItemPropertyFunction getter, Supplier<? extends Item>... items) { | ||||
|         var id = new ResourceLocation(ComputerCraft.MOD_ID, name); | ||||
|         for (var item : items) { | ||||
|             ItemProperties.register(item.get(), id, getter); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static int getTurtleColour(ItemStack stack, int layer) { | ||||
|         return layer == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF; | ||||
|     } | ||||
| 
 | ||||
|     private static void registerContainers() { | ||||
|         // My IDE doesn't think so, but we do actually need these generics. | ||||
|     public static void registerBlockEntityRenderers(BlockEntityRenderRegistry register) { | ||||
|         register.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new); | ||||
|         register.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new); | ||||
|         register.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new); | ||||
|         register.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new); | ||||
|     } | ||||
| 
 | ||||
|         MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register(ModRegistry.Menus.COMPUTER.get(), GuiComputer::new); | ||||
|         MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), GuiComputer::new); | ||||
|         MenuScreens.<ContainerComputerBase, NoTermComputerScreen<ContainerComputerBase>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new); | ||||
|         MenuScreens.register(ModRegistry.Menus.TURTLE.get(), GuiTurtle::new); | ||||
|     public interface BlockEntityRenderRegistry { | ||||
|         <T extends BlockEntity> void register(BlockEntityType<? extends T> type, BlockEntityRendererProvider<T> provider); | ||||
|     } | ||||
| 
 | ||||
|         MenuScreens.register(ModRegistry.Menus.PRINTER.get(), GuiPrinter::new); | ||||
|         MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), GuiDiskDrive::new); | ||||
|         MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), GuiPrintout::new); | ||||
| 
 | ||||
|         MenuScreens.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), GuiComputer::new); | ||||
|     public static void registerShaders(ResourceManager resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException { | ||||
|         RenderTypes.registerShaders(resources, load); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,84 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
| package dan200.computercraft.client; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.sound.SpeakerSound; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.*; | ||||
| import net.minecraftforge.client.event.sound.PlayStreamingSourceEvent; | ||||
| import net.minecraftforge.event.TickEvent; | ||||
| import net.minecraftforge.event.level.LevelEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| /** | ||||
|  * Forge-specific dispatch for {@link ClientHooks}. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public final class ForgeClientHooks { | ||||
|     private ForgeClientHooks() { | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onTick(TickEvent.ClientTickEvent event) { | ||||
|         if (event.phase == TickEvent.Phase.START) ClientHooks.onTick(); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderTick(TickEvent.RenderTickEvent event) { | ||||
|         if (event.phase == TickEvent.Phase.START) ClientHooks.onRenderTick(); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onWorldUnload(LevelEvent.Unload event) { | ||||
|         if (event.getLevel().isClientSide()) ClientHooks.onWorldUnload(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void drawHighlight(RenderHighlightEvent.Block event) { | ||||
|         if (ClientHooks.drawHighlight(event.getPoseStack(), event.getMultiBufferSource(), event.getCamera(), event.getTarget())) { | ||||
|             event.setCanceled(true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onClientSendMessage(ClientChatEvent event) { | ||||
|         if (ClientHooks.onChatMessage(event.getMessage())) event.setCanceled(true); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderText(CustomizeGuiOverlayEvent.DebugText event) { | ||||
|         ClientHooks.addDebugInfo(event.getRight()::add); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderInHand(RenderHandEvent event) { | ||||
|         if (ClientHooks.onRenderHeldItem( | ||||
|             event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(), | ||||
|             event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() | ||||
|         )) { | ||||
|             event.setCanceled(true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderInFrame(RenderItemInFrameEvent event) { | ||||
|         if (ClientHooks.onRenderItemFrame( | ||||
|             event.getPoseStack(), event.getMultiBufferSource(), event.getItemFrameEntity(), event.getItemStack(), event.getPackedLight() | ||||
|         )) { | ||||
|             event.setCanceled(true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void playStreaming(PlayStreamingSourceEvent event) { | ||||
|         if (!(event.getSound() instanceof SpeakerSound sound) || sound.getStream() == null) return; | ||||
|         ClientHooks.onPlayStreaming(event.getEngine(), event.getChannel(), sound.getStream()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -0,0 +1,56 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
| package dan200.computercraft.client; | ||||
| 
 | ||||
| import dan200.computercraft.api.ComputerCraftAPI; | ||||
| import dan200.computercraft.client.render.TurtleModelLoader; | ||||
| import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.ModelEvent; | ||||
| import net.minecraftforge.client.event.RegisterColorHandlersEvent; | ||||
| import net.minecraftforge.client.event.RegisterShadersEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| /** | ||||
|  * Registers textures and models for items. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraftAPI.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) | ||||
| public final class ForgeClientRegistry { | ||||
|     private ForgeClientRegistry() { | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void registerModelLoaders(ModelEvent.RegisterGeometryLoaders event) { | ||||
|         event.register("turtle", TurtleModelLoader.INSTANCE); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void registerModels(ModelEvent.RegisterAdditional event) { | ||||
|         ClientRegistry.registerExtraModels(event::register); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void registerShaders(RegisterShadersEvent event) throws IOException { | ||||
|         ClientRegistry.registerShaders(event.getResourceManager(), event::registerShader); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onItemColours(RegisterColorHandlersEvent.Item event) { | ||||
|         ClientRegistry.registerItemColours(event::register); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void setupClient(FMLClientSetupEvent event) { | ||||
|         ClientRegistry.register(); | ||||
|         ClientRegistry.registerBlockEntityRenderers(BlockEntityRenderers::register); | ||||
| 
 | ||||
|         event.enqueueWork(ClientRegistry::registerMainThread); | ||||
|     } | ||||
| } | ||||
| @@ -5,13 +5,6 @@ | ||||
|  */ | ||||
| package dan200.computercraft.client; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.event.TickEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public final class FrameInfo { | ||||
|     private static int tick; | ||||
|     private static long renderFrame; | ||||
| @@ -27,13 +20,11 @@ public final class FrameInfo { | ||||
|         return renderFrame; | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onTick(TickEvent.ClientTickEvent event) { | ||||
|         if (event.phase == TickEvent.Phase.START) tick++; | ||||
|     public static void onTick() { | ||||
|         tick++; | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderTick(TickEvent.RenderTickEvent event) { | ||||
|         if (event.phase == TickEvent.Phase.START) renderFrame++; | ||||
|     public static void onRenderTick() { | ||||
|         renderFrame++; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,19 +5,17 @@ | ||||
|  */ | ||||
| package dan200.computercraft.client.render; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import com.mojang.blaze3d.vertex.PoseStack; | ||||
| import dan200.computercraft.shared.ModRegistry; | ||||
| import dan200.computercraft.shared.peripheral.modem.wired.BlockCable; | ||||
| import dan200.computercraft.shared.peripheral.modem.wired.CableShapes; | ||||
| import dan200.computercraft.shared.util.WorldUtil; | ||||
| import net.minecraft.client.Camera; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.client.renderer.RenderType; | ||||
| import net.minecraft.util.Mth; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.RenderHighlightEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraft.world.phys.BlockHitResult; | ||||
| 
 | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public final class CableHighlightRenderer { | ||||
|     private CableHighlightRenderer() { | ||||
|     } | ||||
| @@ -25,37 +23,36 @@ public final class CableHighlightRenderer { | ||||
|     /** | ||||
|      * Draw an outline for a specific part of a cable "Multipart". | ||||
|      * | ||||
|      * @param event The event to observe | ||||
|      * @param transform    The current transformation matrix. | ||||
|      * @param bufferSource The buffer to draw to. | ||||
|      * @param camera       The current camera. | ||||
|      * @param hit          The block hit result for the current player. | ||||
|      * @return If we rendered a custom outline. | ||||
|      * @see net.minecraft.client.renderer.LevelRenderer#renderHitOutline | ||||
|      */ | ||||
|     @SubscribeEvent | ||||
|     public static void drawHighlight(RenderHighlightEvent.Block event) { | ||||
|         var hit = event.getTarget(); | ||||
|     public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) { | ||||
|         var pos = hit.getBlockPos(); | ||||
|         var world = event.getCamera().getEntity().getCommandSenderWorld(); | ||||
|         var info = event.getCamera(); | ||||
|         var world = camera.getEntity().getCommandSenderWorld(); | ||||
| 
 | ||||
|         var state = world.getBlockState(pos); | ||||
| 
 | ||||
|         // We only care about instances with both cable and modem. | ||||
|         if (state.getBlock() != ModRegistry.Blocks.CABLE.get() || state.getValue(BlockCable.MODEM).getFacing() == null || !state.getValue(BlockCable.CABLE)) { | ||||
|             return; | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         event.setCanceled(true); | ||||
| 
 | ||||
|         var shape = WorldUtil.isVecInside(CableShapes.getModemShape(state), hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ())) | ||||
|             ? CableShapes.getModemShape(state) | ||||
|             : CableShapes.getCableShape(state); | ||||
| 
 | ||||
|         var cameraPos = info.getPosition(); | ||||
|         var cameraPos = camera.getPosition(); | ||||
|         var xOffset = pos.getX() - cameraPos.x(); | ||||
|         var yOffset = pos.getY() - cameraPos.y(); | ||||
|         var zOffset = pos.getZ() - cameraPos.z(); | ||||
| 
 | ||||
|         var buffer = event.getMultiBufferSource().getBuffer(RenderType.lines()); | ||||
|         var matrix4f = event.getPoseStack().last().pose(); | ||||
|         var normal = event.getPoseStack().last().normal(); | ||||
|         var buffer = bufferSource.getBuffer(RenderType.lines()); | ||||
|         var matrix4f = transform.last().pose(); | ||||
|         var normal = transform.last().normal(); | ||||
|         // TODO: Can we just accesstransformer out LevelRenderer.renderShape? | ||||
|         shape.forAllEdges((x1, y1, z1, x2, y2, z2) -> { | ||||
|             var xDelta = (float) (x2 - x1); | ||||
| @@ -77,5 +74,7 @@ public final class CableHighlightRenderer { | ||||
|                 .normal(normal, xDelta, yDelta, zDelta) | ||||
|                 .endVertex(); | ||||
|         }); | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,50 +0,0 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
| package dan200.computercraft.client.render; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.turtle.TurtleSide; | ||||
| import dan200.computercraft.shared.peripheral.monitor.TileMonitor; | ||||
| import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.world.phys.BlockHitResult; | ||||
| import net.minecraft.world.phys.HitResult; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public class DebugOverlay { | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderText(CustomizeGuiOverlayEvent.DebugText event) { | ||||
|         var minecraft = Minecraft.getInstance(); | ||||
|         if (!minecraft.options.renderDebug || minecraft.level == null) return; | ||||
|         if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return; | ||||
| 
 | ||||
|         var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos()); | ||||
| 
 | ||||
|         if (tile instanceof TileMonitor monitor) { | ||||
|             event.getRight().add(""); | ||||
|             event.getRight().add( | ||||
|                 String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight()) | ||||
|             ); | ||||
|         } else if (tile instanceof TileTurtle turtle) { | ||||
|             event.getRight().add(""); | ||||
|             event.getRight().add("Targeted turtle:"); | ||||
|             event.getRight().add(String.format("Id: %d", turtle.getComputerID())); | ||||
|             addTurtleUpgrade(event.getRight(), turtle, TurtleSide.LEFT); | ||||
|             addTurtleUpgrade(event.getRight(), turtle, TurtleSide.RIGHT); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static void addTurtleUpgrade(List<String> out, TileTurtle turtle, TurtleSide side) { | ||||
|         var upgrade = turtle.getUpgrade(side); | ||||
|         if (upgrade != null) out.add(String.format("Upgrade[%s]: %s", side, upgrade.getUpgradeID())); | ||||
|     } | ||||
| } | ||||
| @@ -30,7 +30,7 @@ public abstract class ItemMapLikeRenderer { | ||||
|      */ | ||||
|     protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light); | ||||
| 
 | ||||
|     protected void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) { | ||||
|     public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) { | ||||
|         Player player = Minecraft.getInstance().player; | ||||
| 
 | ||||
|         transform.pushPose(); | ||||
|   | ||||
| @@ -8,7 +8,6 @@ package dan200.computercraft.client.render; | ||||
| import com.mojang.blaze3d.vertex.PoseStack; | ||||
| import com.mojang.math.Matrix4f; | ||||
| import com.mojang.math.Vector3f; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.pocket.ClientPocketComputers; | ||||
| import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.core.util.Colour; | ||||
| @@ -16,10 +15,6 @@ import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.pocket.items.ItemPocketComputer; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.world.item.ItemStack; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.RenderHandEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| import static dan200.computercraft.client.render.ComputerBorderRenderer.*; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| @@ -28,25 +23,12 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FON | ||||
| /** | ||||
|  * Emulates map rendering for pocket computers. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public final class ItemPocketRenderer extends ItemMapLikeRenderer { | ||||
|     private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer(); | ||||
|     public static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer(); | ||||
| 
 | ||||
|     private ItemPocketRenderer() { | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderInHand(RenderHandEvent event) { | ||||
|         var stack = event.getItemStack(); | ||||
|         if (!(stack.getItem() instanceof ItemPocketComputer)) return; | ||||
| 
 | ||||
|         event.setCanceled(true); | ||||
|         INSTANCE.renderItemFirstPerson( | ||||
|             event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(), | ||||
|             event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) { | ||||
|         var computer = ClientPocketComputers.get(stack); | ||||
|   | ||||
| @@ -7,16 +7,11 @@ package dan200.computercraft.client.render; | ||||
| 
 | ||||
| import com.mojang.blaze3d.vertex.PoseStack; | ||||
| import com.mojang.math.Vector3f; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.media.items.ItemPrintout; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.world.entity.EntityType; | ||||
| import net.minecraft.world.entity.decoration.ItemFrame; | ||||
| import net.minecraft.world.item.ItemStack; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.RenderHandEvent; | ||||
| import net.minecraftforge.client.event.RenderItemInFrameEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| import static dan200.computercraft.client.render.PrintoutRenderer.*; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| @@ -27,25 +22,12 @@ import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENG | ||||
| /** | ||||
|  * Emulates map and item-frame rendering for printouts. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public final class ItemPrintoutRenderer extends ItemMapLikeRenderer { | ||||
|     private static final ItemPrintoutRenderer INSTANCE = new ItemPrintoutRenderer(); | ||||
|     public static final ItemPrintoutRenderer INSTANCE = new ItemPrintoutRenderer(); | ||||
| 
 | ||||
|     private ItemPrintoutRenderer() { | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderInHand(RenderHandEvent event) { | ||||
|         var stack = event.getItemStack(); | ||||
|         if (!(stack.getItem() instanceof ItemPrintout)) return; | ||||
| 
 | ||||
|         event.setCanceled(true); | ||||
|         INSTANCE.renderItemFirstPerson( | ||||
|             event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(), | ||||
|             event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) { | ||||
|         transform.mulPose(Vector3f.XP.rotationDegrees(180f)); | ||||
| @@ -55,13 +37,8 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer { | ||||
|         drawPrintout(transform, render, stack, light); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRenderInFrame(RenderItemInFrameEvent event) { | ||||
|         var stack = event.getItemStack(); | ||||
|     public static void onRenderInFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int packedLight) { | ||||
|         if (!(stack.getItem() instanceof ItemPrintout)) return; | ||||
|         event.setCanceled(true); | ||||
| 
 | ||||
|         var transform = event.getPoseStack(); | ||||
| 
 | ||||
|         // Move a little bit forward to ensure we're not clipping with the frame | ||||
|         transform.translate(0.0f, 0.0f, -0.001f); | ||||
| @@ -69,8 +46,8 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer { | ||||
|         transform.scale(0.95f, 0.95f, -0.95f); | ||||
|         transform.translate(-0.5f, -0.5f, 0.0f); | ||||
| 
 | ||||
|         var light = event.getItemFrameEntity().getType() == EntityType.GLOW_ITEM_FRAME ? 0xf000d2 : event.getPackedLight(); // See getLightVal. | ||||
|         drawPrintout(transform, event.getMultiBufferSource(), stack, light); | ||||
|         var light = frame.getType() == EntityType.GLOW_ITEM_FRAME ? 0xf000d2 : packedLight; // See getLightVal. | ||||
|         drawPrintout(transform, render, stack, light); | ||||
|     } | ||||
| 
 | ||||
|     private static void drawPrintout(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) { | ||||
|   | ||||
| @@ -5,17 +5,16 @@ | ||||
|  */ | ||||
| package dan200.computercraft.client.render; | ||||
| 
 | ||||
| import com.mojang.blaze3d.vertex.PoseStack; | ||||
| import com.mojang.blaze3d.vertex.VertexConsumer; | ||||
| import com.mojang.math.Matrix3f; | ||||
| import com.mojang.math.Matrix4f; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.peripheral.monitor.TileMonitor; | ||||
| import net.minecraft.client.Camera; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.client.renderer.RenderType; | ||||
| import net.minecraft.core.Direction; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.RenderHighlightEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraft.world.phys.BlockHitResult; | ||||
| 
 | ||||
| import java.util.EnumSet; | ||||
| 
 | ||||
| @@ -25,23 +24,19 @@ import static net.minecraft.core.Direction.*; | ||||
|  * Overrides monitor highlighting to only render the outline of the <em>whole</em> monitor, rather than the current | ||||
|  * block. This means you do not get an intrusive outline on top of the screen. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public final class MonitorHighlightRenderer { | ||||
|     private MonitorHighlightRenderer() { | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void drawHighlight(RenderHighlightEvent.Block event) { | ||||
|     public static boolean drawHighlight(PoseStack transformStack, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) { | ||||
|         // Preserve normal behaviour when crouching. | ||||
|         if (event.getCamera().getEntity().isCrouching()) return; | ||||
|         if (camera.getEntity().isCrouching()) return false; | ||||
| 
 | ||||
|         var world = event.getCamera().getEntity().getCommandSenderWorld(); | ||||
|         var pos = event.getTarget().getBlockPos(); | ||||
|         var world = camera.getEntity().getCommandSenderWorld(); | ||||
|         var pos = hit.getBlockPos(); | ||||
| 
 | ||||
|         var tile = world.getBlockEntity(pos); | ||||
|         if (!(tile instanceof TileMonitor monitor)) return; | ||||
| 
 | ||||
|         event.setCanceled(true); | ||||
|         if (!(tile instanceof TileMonitor monitor)) return false; | ||||
| 
 | ||||
|         // Determine which sides are part of the external faces of the monitor, and so which need to be rendered. | ||||
|         var faces = EnumSet.allOf(Direction.class); | ||||
| @@ -52,13 +47,12 @@ public final class MonitorHighlightRenderer { | ||||
|         if (monitor.getYIndex() != 0) faces.remove(monitor.getDown().getOpposite()); | ||||
|         if (monitor.getYIndex() != monitor.getHeight() - 1) faces.remove(monitor.getDown()); | ||||
| 
 | ||||
|         var transformStack = event.getPoseStack(); | ||||
|         var cameraPos = event.getCamera().getPosition(); | ||||
|         var cameraPos = camera.getPosition(); | ||||
|         transformStack.pushPose(); | ||||
|         transformStack.translate(pos.getX() - cameraPos.x(), pos.getY() - cameraPos.y(), pos.getZ() - cameraPos.z()); | ||||
| 
 | ||||
|         // I wish I could think of a better way to do this | ||||
|         var buffer = event.getMultiBufferSource().getBuffer(RenderType.lines()); | ||||
|         var buffer = bufferSource.getBuffer(RenderType.lines()); | ||||
|         var transform = transformStack.last().pose(); | ||||
|         var normal = transformStack.last().normal(); | ||||
|         if (faces.contains(NORTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 0, UP); | ||||
| @@ -75,6 +69,7 @@ public final class MonitorHighlightRenderer { | ||||
|         if (faces.contains(EAST) || faces.contains(UP)) line(buffer, transform, normal, 1, 1, 0, SOUTH); | ||||
| 
 | ||||
|         transformStack.popPose(); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     private static void line(VertexConsumer buffer, Matrix4f transform, Matrix3f normal, float x, float y, float z, Direction direction) { | ||||
|   | ||||
| @@ -14,15 +14,13 @@ import net.minecraft.client.renderer.RenderStateShard; | ||||
| import net.minecraft.client.renderer.RenderType; | ||||
| import net.minecraft.client.renderer.ShaderInstance; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.RegisterShadersEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraft.server.packs.resources.ResourceManager; | ||||
| 
 | ||||
| import javax.annotation.Nonnull; | ||||
| import java.io.IOException; | ||||
| import java.util.function.BiConsumer; | ||||
| import java.util.function.Consumer; | ||||
| 
 | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) | ||||
| public class RenderTypes { | ||||
|     public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20); | ||||
| 
 | ||||
| @@ -62,11 +60,10 @@ public class RenderTypes { | ||||
|         return GameRenderer.getRendertypeTextShader(); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void registerShaders(RegisterShadersEvent event) throws IOException { | ||||
|         event.registerShader( | ||||
|     public static void registerShaders(ResourceManager resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException { | ||||
|         load.accept( | ||||
|             new MonitorTextureBufferShader( | ||||
|                 event.getResourceManager(), | ||||
|                 resources, | ||||
|                 new ResourceLocation(ComputerCraft.MOD_ID, "monitor_tbo"), | ||||
|                 MONITOR_TBO.format() | ||||
|             ), | ||||
|   | ||||
| @@ -5,18 +5,22 @@ | ||||
|  */ | ||||
| package dan200.computercraft.client.sound; | ||||
| 
 | ||||
| import com.mojang.blaze3d.audio.Channel; | ||||
| import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral; | ||||
| import io.netty.buffer.ByteBuf; | ||||
| import net.minecraft.client.sounds.AudioStream; | ||||
| import net.minecraft.client.sounds.SoundEngine; | ||||
| import org.lwjgl.BufferUtils; | ||||
| 
 | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
| import javax.sound.sampled.AudioFormat; | ||||
| import java.io.IOException; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.nio.ByteOrder; | ||||
| import java.util.ArrayDeque; | ||||
| import java.util.Queue; | ||||
| import java.util.concurrent.Executor; | ||||
| 
 | ||||
| class DfpwmStream implements AudioStream { | ||||
|     private static final int PREC = 10; | ||||
| @@ -26,6 +30,23 @@ class DfpwmStream implements AudioStream { | ||||
| 
 | ||||
|     private final Queue<ByteBuffer> buffers = new ArrayDeque<>(2); | ||||
| 
 | ||||
|     /** | ||||
|      * The {@link Channel} which this sound is playing on. | ||||
|      * | ||||
|      * @see SpeakerInstance#pushAudio(ByteBuf) | ||||
|      */ | ||||
|     @Nullable | ||||
|     Channel channel; | ||||
| 
 | ||||
|     /** | ||||
|      * The underlying {@link SoundEngine} executor. | ||||
|      * | ||||
|      * @see SpeakerInstance#pushAudio(ByteBuf) | ||||
|      * @see SoundEngine#executor | ||||
|      */ | ||||
|     @Nullable | ||||
|     Executor executor; | ||||
| 
 | ||||
|     private int charge = 0; // q | ||||
|     private int strength = 0; // s | ||||
|     private int lowPassCharge; | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| package dan200.computercraft.client.sound; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.core.util.Nullability; | ||||
| import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; | ||||
| import io.netty.buffer.ByteBuf; | ||||
| import net.minecraft.client.Minecraft; | ||||
| @@ -32,9 +33,11 @@ public class SpeakerInstance { | ||||
|         currentStream.push(buffer); | ||||
| 
 | ||||
|         // If we've got nothing left in the buffer, enqueue an additional one just in case. | ||||
|         if (exhausted && sound != null && sound.stream == stream && sound.channel != null) { | ||||
|             sound.executor.execute(() -> { | ||||
|                 if (!sound.channel.stopped()) sound.channel.pumpBuffers(1); | ||||
|         if (exhausted && sound != null && sound.stream == stream && stream.channel != null && stream.executor != null) { | ||||
|             var actualStream = sound.stream; | ||||
|             stream.executor.execute(() -> { | ||||
|                 var channel = Nullability.assertNonNull(actualStream.channel); | ||||
|                 if (!channel.stopped()) channel.pumpBuffers(1); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -5,11 +5,10 @@ | ||||
|  */ | ||||
| package dan200.computercraft.client.sound; | ||||
| 
 | ||||
| import com.mojang.blaze3d.audio.Channel; | ||||
| import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.sound.PlayStreamingSourceEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraft.client.sounds.AudioStream; | ||||
| import net.minecraft.client.sounds.SoundEngine; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| import java.util.UUID; | ||||
| @@ -18,17 +17,15 @@ import java.util.concurrent.ConcurrentHashMap; | ||||
| /** | ||||
|  * Maps speakers source IDs to a {@link SpeakerInstance}. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(Dist.CLIENT) | ||||
| public class SpeakerManager { | ||||
|     private static final Map<UUID, SpeakerInstance> sounds = new ConcurrentHashMap<>(); | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void playStreaming(PlayStreamingSourceEvent event) { | ||||
|         if (!(event.getSound() instanceof SpeakerSound sound) || sound.stream == null) return; | ||||
|     public static void onPlayStreaming(SoundEngine engine, Channel channel, AudioStream stream) { | ||||
|         if (!(stream instanceof DfpwmStream dfpwmStream)) return; | ||||
| 
 | ||||
|         // Associate the sound with the current channel, so SpeakerInstance.pushAudio can queue audio immediately. | ||||
|         sound.channel = event.getChannel(); | ||||
|         sound.executor = event.getEngine().executor; | ||||
|         // Associate the stream with the current channel, so SpeakerInstance.pushAudio can queue audio immediately. | ||||
|         dfpwmStream.channel = channel; | ||||
|         dfpwmStream.executor = engine.executor; | ||||
|     } | ||||
| 
 | ||||
|     public static SpeakerInstance getSound(UUID source) { | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
|  */ | ||||
| package dan200.computercraft.client.sound; | ||||
| 
 | ||||
| import com.mojang.blaze3d.audio.Channel; | ||||
| import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; | ||||
| import net.minecraft.client.resources.sounds.AbstractSoundInstance; | ||||
| import net.minecraft.client.resources.sounds.Sound; | ||||
| @@ -19,11 +18,8 @@ import net.minecraft.world.entity.Entity; | ||||
| 
 | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.concurrent.CompletableFuture; | ||||
| import java.util.concurrent.Executor; | ||||
| 
 | ||||
| public class SpeakerSound extends AbstractSoundInstance implements TickableSoundInstance { | ||||
|     Channel channel; | ||||
|     Executor executor; | ||||
|     DfpwmStream stream; | ||||
| 
 | ||||
|     private Entity entity; | ||||
| @@ -69,4 +65,8 @@ public class SpeakerSound extends AbstractSoundInstance implements TickableSound | ||||
|     public CompletableFuture<AudioStream> getStream(@Nonnull SoundBufferLibrary soundBuffers, @Nonnull Sound sound, boolean looping) { | ||||
|         return stream != null ? CompletableFuture.completedFuture(stream) : super.getStream(soundBuffers, sound, looping); | ||||
|     } | ||||
| 
 | ||||
|     public AudioStream getStream() { | ||||
|         return stream; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,57 +7,52 @@ package dan200.computercraft.shared; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.core.apis.http.NetworkUtils; | ||||
| import dan200.computercraft.shared.command.CommandComputerCraft; | ||||
| import dan200.computercraft.shared.computer.core.ResourceMount; | ||||
| import dan200.computercraft.shared.computer.core.ServerContext; | ||||
| import dan200.computercraft.shared.computer.metrics.ComputerMBean; | ||||
| import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; | ||||
| import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork; | ||||
| import dan200.computercraft.shared.platform.PlatformHelper; | ||||
| import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher; | ||||
| import dan200.computercraft.shared.util.DropConsumer; | ||||
| import dan200.computercraft.shared.util.TickScheduler; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| import net.minecraft.server.MinecraftServer; | ||||
| import net.minecraft.server.dedicated.DedicatedServer; | ||||
| import net.minecraft.server.level.ServerPlayer; | ||||
| import net.minecraft.server.packs.resources.PreparableReloadListener; | ||||
| import net.minecraft.world.entity.Entity; | ||||
| import net.minecraft.world.item.ItemStack; | ||||
| import net.minecraft.world.level.chunk.LevelChunk; | ||||
| import net.minecraft.world.level.storage.loot.BuiltInLootTables; | ||||
| import net.minecraft.world.level.storage.loot.LootPool; | ||||
| import net.minecraft.world.level.storage.loot.entries.LootTableReference; | ||||
| import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; | ||||
| import net.minecraftforge.event.*; | ||||
| import net.minecraftforge.event.server.ServerStartingEvent; | ||||
| import net.minecraftforge.event.server.ServerStoppedEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraftforge.server.ServerLifecycleHooks; | ||||
| 
 | ||||
| import javax.annotation.Nullable; | ||||
| import java.util.Arrays; | ||||
| import java.util.HashSet; | ||||
| import java.util.Set; | ||||
| import java.util.function.BiConsumer; | ||||
| 
 | ||||
| /** | ||||
|  * Miscellaneous hooks which are present on the client and server. | ||||
|  * Event listeners for server/common code. | ||||
|  * <p> | ||||
|  * These should possibly be refactored into separate classes at some point, but are fine here for now. | ||||
|  * | ||||
|  * @see dan200.computercraft.client.ClientHooks For client-specific ones. | ||||
|  * All event handlers should be defined in this class, and then invoked from a loader-specific event handler. This means | ||||
|  * it's much easier to ensure that each hook is called in all loader source sets. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID) | ||||
| public final class CommonHooks { | ||||
|     private CommonHooks() { | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onServerTick(TickEvent.ServerTickEvent event) { | ||||
|         if (event.phase == TickEvent.Phase.START) { | ||||
|             ServerContext.get(ServerLifecycleHooks.getCurrentServer()).tick(); | ||||
|         } | ||||
|     public static void onServerTickStart(MinecraftServer server) { | ||||
|         ServerContext.get(server).tick(); | ||||
|         TickScheduler.tick(); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRegisterCommand(RegisterCommandsEvent event) { | ||||
|         CommandComputerCraft.register(event.getDispatcher()); | ||||
|     public static void onServerTickEnd() { | ||||
|         MonitorWatcher.onTick(); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onServerStarting(ServerStartingEvent event) { | ||||
|         var server = event.getServer(); | ||||
|     public static void onServerStarting(MinecraftServer server) { | ||||
|         if (server instanceof DedicatedServer dediServer && dediServer.getProperties().enableJmxMonitoring) { | ||||
|             ComputerMBean.register(); | ||||
|         } | ||||
| @@ -67,8 +62,7 @@ public final class CommonHooks { | ||||
|         ComputerMBean.start(server); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onServerStopped(ServerStoppedEvent event) { | ||||
|     public static void onServerStopped() { | ||||
|         resetState(); | ||||
|     } | ||||
| 
 | ||||
| @@ -78,6 +72,10 @@ public final class CommonHooks { | ||||
|         NetworkUtils.reset(); | ||||
|     } | ||||
| 
 | ||||
|     public static void onChunkWatch(LevelChunk chunk, ServerPlayer player) { | ||||
|         MonitorWatcher.onWatch(chunk, player); | ||||
|     } | ||||
| 
 | ||||
|     public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation(ComputerCraft.MOD_ID, "treasure_disk"); | ||||
| 
 | ||||
|     private static final Set<ResourceLocation> TABLES = new HashSet<>(Arrays.asList( | ||||
| @@ -93,32 +91,26 @@ public final class CommonHooks { | ||||
|         BuiltInLootTables.VILLAGE_CARTOGRAPHER | ||||
|     )); | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void lootLoad(LootTableLoadEvent event) { | ||||
|         var name = event.getName(); | ||||
|         if (!name.getNamespace().equals("minecraft") || !TABLES.contains(name)) return; | ||||
| 
 | ||||
|         event.getTable().addPool(LootPool.lootPool() | ||||
|     public static @Nullable LootPool.Builder getExtraLootPool(ResourceLocation lootTable) { | ||||
|         if (!lootTable.getNamespace().equals("minecraft") || !TABLES.contains(lootTable)) return null; | ||||
| 
 | ||||
|         return LootPool.lootPool() | ||||
|             .add(LootTableReference.lootTableReference(LOOT_TREASURE_DISK)) | ||||
|             .setRolls(ConstantValue.exactly(1)) | ||||
|             .name("computercraft_treasure") | ||||
|             .build()); | ||||
|             .setRolls(ConstantValue.exactly(1)); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onAddReloadListeners(AddReloadListenerEvent event) { | ||||
|         event.addListener(ResourceMount.RELOAD_LISTENER); | ||||
|         event.addListener(TurtleUpgrades.instance()); | ||||
|         event.addListener(PocketUpgrades.instance()); | ||||
|     public static void onDatapackReload(BiConsumer<String, PreparableReloadListener> addReload) { | ||||
|         addReload.accept("mounts", ResourceMount.RELOAD_LISTENER); | ||||
|         addReload.accept("turtle_upgrades", TurtleUpgrades.instance()); | ||||
|         addReload.accept("pocket_upgrades", PocketUpgrades.instance()); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onDatapackSync(OnDatapackSyncEvent event) { | ||||
|         var packet = new UpgradesLoadedMessage(); | ||||
|         if (event.getPlayer() == null) { | ||||
|             PlatformHelper.get().sendToAllPlayers(packet, event.getPlayerList().getServer()); | ||||
|         } else { | ||||
|             PlatformHelper.get().sendToPlayer(packet, event.getPlayer()); | ||||
|         } | ||||
|     public static boolean onEntitySpawn(Entity entity) { | ||||
|         return DropConsumer.onEntitySpawn(entity); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean onLivingDrop(Entity entity, ItemStack stack) { | ||||
|         return DropConsumer.onLivingDrop(entity, stack); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,11 +14,8 @@ import dan200.computercraft.core.apis.http.options.Action; | ||||
| import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; | ||||
| import net.minecraftforge.common.ForgeConfigSpec; | ||||
| import net.minecraftforge.common.ForgeConfigSpec.ConfigValue; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.ModLoadingContext; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraftforge.fml.config.ModConfig; | ||||
| import net.minecraftforge.fml.event.config.ModConfigEvent; | ||||
| import org.apache.logging.log4j.core.Filter; | ||||
| import org.apache.logging.log4j.core.LoggerContext; | ||||
| import org.apache.logging.log4j.core.filter.MarkerFilter; | ||||
| @@ -28,7 +25,6 @@ import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| 
 | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) | ||||
| public final class Config { | ||||
|     private static final int MODEM_MAX_RANGE = 100000; | ||||
| 
 | ||||
| @@ -422,19 +418,9 @@ public final class Config { | ||||
|         ComputerCraft.uploadNagDelay = uploadNagDelay.get(); | ||||
|     } | ||||
| 
 | ||||
|     private static void sync(ModConfig config) { | ||||
|     public static void sync(ModConfig config) { | ||||
|         if (!config.getModId().equals(ComputerCraft.MOD_ID)) return; | ||||
|         if (config.getType() == ModConfig.Type.SERVER) syncServer(); | ||||
|         if (config.getType() == ModConfig.Type.CLIENT) syncClient(); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void sync(ModConfigEvent.Loading event) { | ||||
|         sync(event.getConfig()); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void sync(ModConfigEvent.Reloading event) { | ||||
|         sync(event.getConfig()); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,9 @@ package dan200.computercraft.shared; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.ComputerCraftAPI; | ||||
| import dan200.computercraft.shared.command.CommandComputerCraft; | ||||
| import dan200.computercraft.shared.computer.blocks.TileComputer; | ||||
| import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; | ||||
| import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral; | ||||
| import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive; | ||||
| import dan200.computercraft.shared.peripheral.modem.wired.TileCable; | ||||
| @@ -16,13 +18,20 @@ import dan200.computercraft.shared.peripheral.modem.wireless.TileWirelessModem; | ||||
| import dan200.computercraft.shared.peripheral.monitor.TileMonitor; | ||||
| import dan200.computercraft.shared.peripheral.printer.TilePrinter; | ||||
| import dan200.computercraft.shared.peripheral.speaker.TileSpeaker; | ||||
| import dan200.computercraft.shared.platform.PlatformHelper; | ||||
| import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||
| import dan200.computercraft.shared.util.CapabilityProvider; | ||||
| import dan200.computercraft.shared.util.SidedCapabilityProvider; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| import net.minecraft.world.level.block.entity.BlockEntity; | ||||
| import net.minecraft.world.level.block.entity.CommandBlockEntity; | ||||
| import net.minecraftforge.event.AttachCapabilitiesEvent; | ||||
| import net.minecraftforge.event.*; | ||||
| import net.minecraftforge.event.entity.EntityJoinLevelEvent; | ||||
| import net.minecraftforge.event.entity.living.LivingDropsEvent; | ||||
| import net.minecraftforge.event.level.ChunkWatchEvent; | ||||
| import net.minecraftforge.event.server.ServerStartingEvent; | ||||
| import net.minecraftforge.event.server.ServerStoppedEvent; | ||||
| import net.minecraftforge.eventbus.api.EventPriority; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraftforge.items.wrapper.InvWrapper; | ||||
| @@ -31,8 +40,54 @@ import net.minecraftforge.items.wrapper.SidedInvWrapper; | ||||
| import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL; | ||||
| import static net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER; | ||||
| 
 | ||||
| /** | ||||
|  * Forge-specific dispatch for {@link CommonHooks}. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraftAPI.MOD_ID) | ||||
| public class ForgeCommonHooks { | ||||
|     @SubscribeEvent | ||||
|     public static void onServerTick(TickEvent.ServerTickEvent event) { | ||||
|         switch (event.phase) { | ||||
|             case START -> CommonHooks.onServerTickStart(event.getServer()); | ||||
|             case END -> CommonHooks.onServerTickEnd(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onServerStarting(ServerStartingEvent event) { | ||||
|         CommonHooks.onServerStarting(event.getServer()); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onServerStopped(ServerStoppedEvent event) { | ||||
|         CommonHooks.onServerStopped(); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onRegisterCommand(RegisterCommandsEvent event) { | ||||
|         CommandComputerCraft.register(event.getDispatcher()); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onChunkWatch(ChunkWatchEvent.Watch event) { | ||||
|         CommonHooks.onChunkWatch(event.getChunk(), event.getPlayer()); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onAddReloadListeners(AddReloadListenerEvent event) { | ||||
|         CommonHooks.onDatapackReload((id, listener) -> event.addListener(listener)); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onDatapackSync(OnDatapackSyncEvent event) { | ||||
|         var packet = new UpgradesLoadedMessage(); | ||||
|         if (event.getPlayer() == null) { | ||||
|             PlatformHelper.get().sendToAllPlayers(packet, event.getPlayerList().getServer()); | ||||
|         } else { | ||||
|             PlatformHelper.get().sendToPlayer(packet, event.getPlayer()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final ResourceLocation PERIPHERAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "peripheral"); | ||||
|     private static final ResourceLocation WIRED_ELEMENT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_node"); | ||||
|     private static final ResourceLocation INVENTORY = new ResourceLocation(ComputerCraftAPI.MOD_ID, "inventory"); | ||||
| @@ -83,4 +138,20 @@ public class ForgeCommonHooks { | ||||
|             CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, () -> new CommandBlockPeripheral(commandBlock)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void lootLoad(LootTableLoadEvent event) { | ||||
|         var pool = CommonHooks.getExtraLootPool(event.getName()); | ||||
|         if (pool != null) event.getTable().addPool(pool.build()); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent(priority = EventPriority.HIGHEST) | ||||
|     public static void onEntitySpawn(EntityJoinLevelEvent event) { | ||||
|         if (CommonHooks.onEntitySpawn(event.getEntity())) event.setCanceled(true); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent(priority = EventPriority.LOW) | ||||
|     public static void onLivingDrops(LivingDropsEvent event) { | ||||
|         event.getDrops().removeIf(itemEntity -> CommonHooks.onLivingDrop(event.getEntity(), itemEntity.getItem())); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ package dan200.computercraft.shared; | ||||
| 
 | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
| import dan200.computercraft.api.ComputerCraftAPI; | ||||
| import dan200.computercraft.api.detail.IDetailProvider; | ||||
| import dan200.computercraft.api.detail.VanillaDetailRegistries; | ||||
| import dan200.computercraft.api.media.IMedia; | ||||
| import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; | ||||
| @@ -99,6 +100,12 @@ import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; | ||||
| import java.util.function.BiFunction; | ||||
| import java.util.function.Function; | ||||
| 
 | ||||
| /** | ||||
|  * Registers ComputerCraft's registry entries and additional objects, such as {@link CauldronInteraction}s and | ||||
|  * {@link IDetailProvider}s | ||||
|  * <p> | ||||
|  * The functions in this class should be called from a loader-specific class. | ||||
|  */ | ||||
| public final class ModRegistry { | ||||
|     private static final CreativeModeTab mainItemGroup = new CreativeTabMain(); | ||||
| 
 | ||||
| @@ -354,6 +361,9 @@ public final class ModRegistry { | ||||
|         public static final RegistryEntry<ImpostorShapelessRecipe.Serializer> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", ImpostorShapelessRecipe.Serializer::new); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Register any objects which don't have to be done on the main thread. | ||||
|      */ | ||||
|     public static void register() { | ||||
|         Blocks.REGISTRY.register(); | ||||
|         BlockEntities.REGISTRY.register(); | ||||
| @@ -379,6 +389,9 @@ public final class ModRegistry { | ||||
|         VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockData::fill); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Register any objects which must be done on the main thread. | ||||
|      */ | ||||
|     public static void registerMainThread() { | ||||
|         CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), ItemTurtle.CAULDRON_INTERACTION); | ||||
|         CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), ItemTurtle.CAULDRON_INTERACTION); | ||||
|   | ||||
| @@ -1,56 +0,0 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
| package dan200.computercraft.shared.command; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.computer.core.ServerContext; | ||||
| import net.minecraft.Util; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.server.MinecraftServer; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.client.event.ClientChatEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| import java.io.File; | ||||
| 
 | ||||
| /** | ||||
|  * Basic client-side commands. | ||||
|  * <p> | ||||
|  * Simply hooks into client chat messages and intercepts matching strings. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) | ||||
| public final class ClientCommands { | ||||
|     public static final String OPEN_COMPUTER = "/computercraft open-computer "; | ||||
| 
 | ||||
|     private ClientCommands() { | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onClientSendMessage(ClientChatEvent event) { | ||||
|         // Emulate the command on the client side | ||||
|         if (event.getMessage().startsWith(OPEN_COMPUTER)) { | ||||
|             MinecraftServer server = Minecraft.getInstance().getSingleplayerServer(); | ||||
|             if (server == null) return; | ||||
| 
 | ||||
|             event.setCanceled(true); | ||||
| 
 | ||||
|             var idStr = event.getMessage().substring(OPEN_COMPUTER.length()).trim(); | ||||
|             int id; | ||||
|             try { | ||||
|                 id = Integer.parseInt(idStr); | ||||
|             } catch (NumberFormatException ignore) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             var file = new File(ServerContext.get(server).storageDir().toFile(), "computer/" + id); | ||||
|             if (!file.isDirectory()) return; | ||||
| 
 | ||||
|             Util.getPlatform().openFile(file); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -51,6 +51,7 @@ import static net.minecraft.commands.Commands.literal; | ||||
| 
 | ||||
| public final class CommandComputerCraft { | ||||
|     public static final UUID SYSTEM_UUID = new UUID(0, 0); | ||||
|     public static final String OPEN_COMPUTER = "/computercraft open-computer "; | ||||
| 
 | ||||
|     private CommandComputerCraft() { | ||||
|     } | ||||
| @@ -310,7 +311,7 @@ public final class CommandComputerCraft { | ||||
| 
 | ||||
|         return link( | ||||
|             text("\u270E"), | ||||
|             ClientCommands.OPEN_COMPUTER + id, | ||||
|             OPEN_COMPUTER + id, | ||||
|             translate("commands.computercraft.dump.open_path") | ||||
|         ); | ||||
|     } | ||||
|   | ||||
| @@ -12,13 +12,15 @@ import dan200.computercraft.api.peripheral.IComputerAccess; | ||||
| import dan200.computercraft.api.peripheral.IPeripheral; | ||||
| import dan200.computercraft.shared.MediaProviders; | ||||
| import dan200.computercraft.shared.common.TileGeneric; | ||||
| import dan200.computercraft.shared.network.client.PlayRecordClientMessage; | ||||
| import dan200.computercraft.shared.platform.PlatformHelper; | ||||
| import dan200.computercraft.shared.util.DefaultInventory; | ||||
| import dan200.computercraft.shared.util.InventoryUtil; | ||||
| import dan200.computercraft.shared.util.RecordUtil; | ||||
| import net.minecraft.core.BlockPos; | ||||
| import net.minecraft.core.Direction; | ||||
| import net.minecraft.nbt.CompoundTag; | ||||
| import net.minecraft.network.chat.Component; | ||||
| import net.minecraft.server.level.ServerLevel; | ||||
| import net.minecraft.world.*; | ||||
| import net.minecraft.world.entity.item.ItemEntity; | ||||
| import net.minecraft.world.entity.player.Inventory; | ||||
| @@ -29,6 +31,7 @@ import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; | ||||
| import net.minecraft.world.level.block.entity.BlockEntityType; | ||||
| import net.minecraft.world.level.block.state.BlockState; | ||||
| import net.minecraft.world.phys.BlockHitResult; | ||||
| import net.minecraft.world.phys.Vec3; | ||||
| 
 | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
| @@ -408,14 +411,18 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory | ||||
|         var contents = getDiskMedia(); | ||||
|         var record = contents != null ? contents.getAudio(diskStack) : null; | ||||
|         if (record != null) { | ||||
|             RecordUtil.playRecord(record, contents.getAudioTitle(diskStack), getLevel(), getBlockPos()); | ||||
|             playRecord(new PlayRecordClientMessage(getBlockPos(), record, contents.getAudioTitle(diskStack))); | ||||
|         } else { | ||||
|             RecordUtil.playRecord(null, null, getLevel(), getBlockPos()); | ||||
|             stopRecord(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void stopRecord() { | ||||
|         RecordUtil.playRecord(null, null, getLevel(), getBlockPos()); | ||||
|         playRecord(new PlayRecordClientMessage(getBlockPos())); | ||||
|     } | ||||
| 
 | ||||
|     private void playRecord(PlayRecordClientMessage message) { | ||||
|         PlatformHelper.get().sendToAllAround(message, (ServerLevel) getLevel(), Vec3.atCenterOf(getBlockPos()), 64); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|   | ||||
| @@ -10,15 +10,12 @@ import dan200.computercraft.shared.computer.terminal.TerminalState; | ||||
| import dan200.computercraft.shared.network.client.MonitorClientMessage; | ||||
| import dan200.computercraft.shared.platform.PlatformHelper; | ||||
| import net.minecraft.server.level.ServerLevel; | ||||
| import net.minecraftforge.event.TickEvent; | ||||
| import net.minecraftforge.event.level.ChunkWatchEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraft.server.level.ServerPlayer; | ||||
| import net.minecraft.world.level.chunk.LevelChunk; | ||||
| 
 | ||||
| import java.util.ArrayDeque; | ||||
| import java.util.Queue; | ||||
| 
 | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID) | ||||
| public final class MonitorWatcher { | ||||
|     private static final Queue<TileMonitor> watching = new ArrayDeque<>(); | ||||
| 
 | ||||
| @@ -33,27 +30,23 @@ public final class MonitorWatcher { | ||||
|         watching.add(monitor); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onWatch(ChunkWatchEvent.Watch event) { | ||||
|     public static void onWatch(LevelChunk chunk, ServerPlayer player) { | ||||
|         // Find all origin monitors who are not already on the queue and send the | ||||
|         // monitor data to the player. | ||||
|         for (var te : event.getChunk().getBlockEntities().values()) { | ||||
|         for (var te : chunk.getBlockEntities().values()) { | ||||
|             if (!(te instanceof TileMonitor monitor)) continue; | ||||
| 
 | ||||
|             var serverMonitor = getMonitor(monitor); | ||||
|             if (serverMonitor == null || monitor.enqueued) continue; | ||||
| 
 | ||||
|             var state = getState(monitor, serverMonitor); | ||||
|             PlatformHelper.get().sendToPlayer(new MonitorClientMessage(monitor.getBlockPos(), state), event.getPlayer()); | ||||
|             PlatformHelper.get().sendToPlayer(new MonitorClientMessage(monitor.getBlockPos(), state), player); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void onTick(TickEvent.ServerTickEvent event) { | ||||
|     public static void onTick() { | ||||
|         // Find all enqueued monitors and send their contents to all nearby players. | ||||
| 
 | ||||
|         if (event.phase != TickEvent.Phase.END) return; | ||||
| 
 | ||||
|         var limit = ComputerCraft.monitorBandwidth; | ||||
|         var obeyLimit = limit > 0; | ||||
| 
 | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
|  */ | ||||
| package dan200.computercraft.shared.util; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import net.minecraft.core.BlockPos; | ||||
| import net.minecraft.core.Direction; | ||||
| import net.minecraft.world.entity.Entity; | ||||
| @@ -13,17 +12,11 @@ import net.minecraft.world.entity.item.ItemEntity; | ||||
| import net.minecraft.world.item.ItemStack; | ||||
| import net.minecraft.world.level.Level; | ||||
| import net.minecraft.world.phys.AABB; | ||||
| import net.minecraftforge.event.entity.EntityJoinLevelEvent; | ||||
| import net.minecraftforge.event.entity.living.LivingDropsEvent; | ||||
| import net.minecraftforge.eventbus.api.EventPriority; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.function.Function; | ||||
| 
 | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID) | ||||
| public final class DropConsumer { | ||||
|     private DropConsumer() { | ||||
|     } | ||||
| @@ -72,22 +65,21 @@ public final class DropConsumer { | ||||
|         if (!remaining.isEmpty()) remainingDrops.add(remaining); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent(priority = EventPriority.HIGHEST) | ||||
|     public static void onEntitySpawn(EntityJoinLevelEvent event) { | ||||
|     public static boolean onEntitySpawn(Entity entity) { | ||||
|         // Capture any nearby item spawns | ||||
|         if (dropWorld == event.getLevel() && event.getEntity() instanceof ItemEntity | ||||
|             && dropBounds.contains(event.getEntity().position())) { | ||||
|             handleDrops(((ItemEntity) event.getEntity()).getItem()); | ||||
|             event.setCanceled(true); | ||||
|         if (dropWorld == entity.getLevel() && entity instanceof ItemEntity | ||||
|             && dropBounds.contains(entity.position())) { | ||||
|             handleDrops(((ItemEntity) entity).getItem()); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent(priority = EventPriority.LOW) | ||||
|     public static void onLivingDrops(LivingDropsEvent drops) { | ||||
|         if (dropEntity == null || drops.getEntity() != dropEntity) return; | ||||
|     public static boolean onLivingDrop(Entity entity, ItemStack stack) { | ||||
|         if (dropEntity == null || entity != dropEntity) return false; | ||||
| 
 | ||||
|         for (var drop : drops.getDrops()) handleDrops(drop.getItem()); | ||||
|         drops.getDrops().clear(); | ||||
|         drops.setCanceled(true); | ||||
|         handleDrops(stack); | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,16 +6,10 @@ | ||||
| package dan200.computercraft.shared.util; | ||||
| 
 | ||||
| import net.minecraft.Util; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraftforge.api.distmarker.Dist; | ||||
| import net.minecraftforge.event.TickEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| /** | ||||
|  * A monotonically increasing clock which accounts for the game being paused. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(Dist.CLIENT) | ||||
| public final class PauseAwareTimer { | ||||
|     private static boolean paused; | ||||
|     private static long pauseTime; | ||||
| @@ -28,11 +22,7 @@ public final class PauseAwareTimer { | ||||
|         return (paused ? pauseTime : Util.getNanos()) - pauseOffset; | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void tick(TickEvent.RenderTickEvent event) { | ||||
|         if (event.phase != TickEvent.Phase.START) return; | ||||
| 
 | ||||
|         var isPaused = Minecraft.getInstance().isPaused(); | ||||
|     public static void tick(boolean isPaused) { | ||||
|         if (isPaused == paused) return; | ||||
| 
 | ||||
|         if (isPaused) { | ||||
|   | ||||
| @@ -1,25 +0,0 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
| package dan200.computercraft.shared.util; | ||||
| 
 | ||||
| import dan200.computercraft.shared.network.NetworkMessage; | ||||
| import dan200.computercraft.shared.network.client.PlayRecordClientMessage; | ||||
| import dan200.computercraft.shared.platform.PlatformHelper; | ||||
| import net.minecraft.core.BlockPos; | ||||
| import net.minecraft.server.level.ServerLevel; | ||||
| import net.minecraft.sounds.SoundEvent; | ||||
| import net.minecraft.world.level.Level; | ||||
| import net.minecraft.world.phys.Vec3; | ||||
| 
 | ||||
| public final class RecordUtil { | ||||
|     private RecordUtil() { | ||||
|     } | ||||
| 
 | ||||
|     public static void playRecord(SoundEvent record, String recordInfo, Level world, BlockPos pos) { | ||||
|         NetworkMessage packet = record != null ? new PlayRecordClientMessage(pos, record, recordInfo) : new PlayRecordClientMessage(pos); | ||||
|         PlatformHelper.get().sendToAllAround(packet, (ServerLevel) world, Vec3.atCenterOf(pos), 64); | ||||
|     } | ||||
| } | ||||
| @@ -5,14 +5,10 @@ | ||||
|  */ | ||||
| package dan200.computercraft.shared.util; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import net.minecraft.core.BlockPos; | ||||
| import net.minecraft.world.level.LevelAccessor; | ||||
| import net.minecraft.world.level.block.Block; | ||||
| import net.minecraft.world.level.block.entity.BlockEntity; | ||||
| import net.minecraftforge.event.TickEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| import java.util.Queue; | ||||
| import java.util.concurrent.ConcurrentLinkedDeque; | ||||
| @@ -23,7 +19,6 @@ import java.util.concurrent.atomic.AtomicBoolean; | ||||
|  * <p> | ||||
|  * We use this when modems and other peripherals change a block in a different thread. | ||||
|  */ | ||||
| @Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID) | ||||
| public final class TickScheduler { | ||||
|     private TickScheduler() { | ||||
|     } | ||||
| @@ -35,10 +30,7 @@ public final class TickScheduler { | ||||
|         if (world != null && !world.isClientSide && !token.scheduled.getAndSet(true)) toTick.add(token); | ||||
|     } | ||||
| 
 | ||||
|     @SubscribeEvent | ||||
|     public static void tick(TickEvent.ServerTickEvent event) { | ||||
|         if (event.phase != TickEvent.Phase.START) return; | ||||
| 
 | ||||
|     public static void tick() { | ||||
|         Token token; | ||||
|         while ((token = toTick.poll()) != null) { | ||||
|             token.scheduled.set(false); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates