From 6e7cbf25e88cebadfcd82b361e81d4e43715aab2 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 4 Jun 2023 11:24:04 +0100 Subject: [PATCH 1/4] Clean up turtle/pocket computer item creation - Remove ITurtleItem (and ITurtleBlockEntity): this was, AFAIK, mostly a relic of the pre-1.13 code where we had multiple turtle items. I do like the theory of abstracting everything out behind an interface, but given there's only one concrete implementation, I'm not convinced it's worth it right now. - Remove TurtleItemFactory/PocketComputerItemFactory: we now prefer calling the instance .create(...) method where we have the item available (for instance upgrade recipes). In the cases we don't (creating an item the first time round), we now move the static .create(...) method to the actual item class. --- .../computercraft/data/RecipeProvider.java | 37 +++++++++------- .../inventory/AbstractComputerMenu.java | 2 +- .../shared/integration/RecipeModHelpers.java | 18 +++++--- .../integration/UpgradeRecipeGenerator.java | 33 +++++++------- .../integration/jei/JEIComputerCraft.java | 4 +- .../pocket/items/PocketComputerItem.java | 12 ++++- .../items/PocketComputerItemFactory.java | 25 ----------- .../recipes/PocketComputerUpgradeRecipe.java | 6 +-- .../turtle/blocks/ITurtleBlockEntity.java | 32 -------------- .../shared/turtle/blocks/TurtleBlock.java | 14 ++++-- .../turtle/blocks/TurtleBlockEntity.java | 11 +---- .../shared/turtle/items/ITurtleItem.java | 24 ---------- .../shared/turtle/items/TurtleItem.java | 22 +++++++--- .../turtle/items/TurtleItemFactory.java | 44 ------------------- .../turtle/recipes/TurtleOverlayRecipe.java | 9 ++-- .../shared/turtle/recipes/TurtleRecipe.java | 4 +- .../turtle/recipes/TurtleUpgradeRecipe.java | 13 +++--- 17 files changed, 106 insertions(+), 204 deletions(-) delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/ITurtleBlockEntity.java delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/turtle/items/ITurtleItem.java delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItemFactory.java diff --git a/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java b/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java index dd4da45cf..46854e04d 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java @@ -15,8 +15,8 @@ import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.platform.RecipeIngredients; import dan200.computercraft.shared.platform.RegistryWrappers; -import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import dan200.computercraft.shared.pocket.items.PocketComputerItem; +import dan200.computercraft.shared.turtle.items.TurtleItem; import dan200.computercraft.shared.util.ColourUtils; import net.minecraft.advancements.critereon.InventoryChangeTrigger; import net.minecraft.advancements.critereon.ItemPredicate; @@ -38,6 +38,7 @@ import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Blocks; +import java.util.List; import java.util.Locale; import java.util.function.Consumer; @@ -93,20 +94,23 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { } } + private static List turtleItems() { + return List.of(ModRegistry.Items.TURTLE_NORMAL.get(), ModRegistry.Items.TURTLE_ADVANCED.get()); + } + /** * Register a crafting recipe for each turtle upgrade. * * @param add The callback to add recipes. */ private void turtleUpgrades(Consumer add) { - for (var family : ComputerFamily.values()) { - var base = TurtleItemFactory.create(-1, null, -1, family, null, null, 0, null); - if (base.isEmpty()) continue; + for (var turtleItem : turtleItems()) { + var base = turtleItem.create(-1, null, -1, null, null, 0, null); - var nameId = family.name().toLowerCase(Locale.ROOT); + var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT); for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) { - var result = TurtleItemFactory.create(-1, null, -1, family, null, upgrade, -1, null); + var result = turtleItem.create(-1, null, -1, null, upgrade, -1, null); ShapedRecipeBuilder .shaped(RecipeCategory.REDSTONE, result.getItem()) .group(String.format("%s:turtle_%s", ComputerCraftAPI.MOD_ID, nameId)) @@ -125,20 +129,24 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { } } + private static List pocketComputerItems() { + return List.of(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get()); + } + /** * Register a crafting recipe for each pocket upgrade. * * @param add The callback to add recipes. */ private void pocketUpgrades(Consumer add) { - for (var family : ComputerFamily.values()) { - var base = PocketComputerItemFactory.create(-1, null, -1, family, null); + for (var pocket : pocketComputerItems()) { + var base = pocket.create(-1, null, -1, null); if (base.isEmpty()) continue; - var nameId = family.name().toLowerCase(Locale.ROOT); + var nameId = pocket.getFamily().name().toLowerCase(Locale.ROOT); for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) { - var result = PocketComputerItemFactory.create(-1, null, -1, family, upgrade); + var result = pocket.create(-1, null, -1, upgrade); ShapedRecipeBuilder .shaped(RecipeCategory.REDSTONE, result.getItem()) .group(String.format("%s:pocket_%s", ComputerCraftAPI.MOD_ID, nameId)) @@ -180,11 +188,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { } private void turtleOverlay(Consumer add, String overlay, Consumer build) { - for (var family : ComputerFamily.values()) { - var base = TurtleItemFactory.create(-1, null, -1, family, null, null, 0, null); - if (base.isEmpty()) continue; + for (var turtleItem : turtleItems()) { + var base = turtleItem.create(-1, null, -1, null, null, 0, null); - var nameId = family.name().toLowerCase(Locale.ROOT); + var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT); var group = "%s:turtle_%s_overlay".formatted(ComputerCraftAPI.MOD_ID, nameId); var builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.REDSTONE, base.getItem()) diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/AbstractComputerMenu.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/AbstractComputerMenu.java index 6d1544dec..443383e51 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/AbstractComputerMenu.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/AbstractComputerMenu.java @@ -5,7 +5,6 @@ package dan200.computercraft.shared.computer.inventory; import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.menu.ComputerMenu; @@ -13,6 +12,7 @@ import dan200.computercraft.shared.computer.menu.ServerInputHandler; import dan200.computercraft.shared.computer.menu.ServerInputState; import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; import dan200.computercraft.shared.computer.terminal.TerminalState; +import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.container.SingleContainerData; import dan200.computercraft.shared.network.container.ComputerContainerData; import net.minecraft.world.entity.player.Player; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java index 69e443890..1e93c30a9 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java @@ -7,21 +7,25 @@ package dan200.computercraft.shared.integration; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.impl.PocketUpgrades; import dan200.computercraft.impl.TurtleUpgrades; +import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import dan200.computercraft.shared.pocket.items.PocketComputerItem; +import dan200.computercraft.shared.turtle.items.TurtleItem; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.Supplier; /** * Utilities for recipe mod plugins (such as JEI). */ public final class RecipeModHelpers { static final List MAIN_FAMILIES = Arrays.asList(ComputerFamily.NORMAL, ComputerFamily.ADVANCED); + static final List> TURTLES = List.of(ModRegistry.Items.TURTLE_NORMAL, ModRegistry.Items.TURTLE_ADVANCED); + static final List> POCKET_COMPUTERS = List.of(ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED); private RecipeModHelpers() { } @@ -49,13 +53,17 @@ public final class RecipeModHelpers { */ public static List getExtraStacks() { List upgradeItems = new ArrayList<>(); - for (var family : MAIN_FAMILIES) { + for (var turtleSupplier : TURTLES) { + var turtle = turtleSupplier.get(); for (var upgrade : TurtleUpgrades.instance().getUpgrades()) { - upgradeItems.add(TurtleItemFactory.create(-1, null, -1, family, null, upgrade, 0, null)); + upgradeItems.add(turtle.create(-1, null, -1, null, upgrade, 0, null)); } + } + for (var pocketSupplier : POCKET_COMPUTERS) { + var pocket = pocketSupplier.get(); for (var upgrade : PocketUpgrades.instance().getUpgrades()) { - upgradeItems.add(PocketComputerItemFactory.create(-1, null, -1, family, upgrade)); + upgradeItems.add(pocket.create(-1, null, -1, upgrade)); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java index 36e86ed51..0dbe35b5d 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java @@ -12,9 +12,7 @@ import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.impl.PocketUpgrades; import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.pocket.items.PocketComputerItem; -import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; import dan200.computercraft.shared.turtle.items.TurtleItem; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; import net.minecraft.core.NonNullList; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; @@ -28,7 +26,8 @@ import javax.annotation.Nullable; import java.util.*; import java.util.function.Function; -import static dan200.computercraft.shared.integration.RecipeModHelpers.MAIN_FAMILIES; +import static dan200.computercraft.shared.integration.RecipeModHelpers.POCKET_COMPUTERS; +import static dan200.computercraft.shared.integration.RecipeModHelpers.TURTLES; /** * Provides dynamic recipe and usage information for upgraded turtle and pocket computers. This is intended to be @@ -218,17 +217,16 @@ public class UpgradeRecipeGenerator { private static ItemStack turtleWith(ItemStack stack, @Nullable ITurtleUpgrade left, @Nullable ITurtleUpgrade right) { var item = (TurtleItem) stack.getItem(); - return TurtleItemFactory.create( - item.getComputerID(stack), item.getLabel(stack), item.getColour(stack), item.getFamily(), + return item.create( + item.getComputerID(stack), item.getLabel(stack), item.getColour(stack), left, right, item.getFuelLevel(stack), item.getOverlay(stack) ); } private static ItemStack pocketWith(ItemStack stack, @Nullable IPocketUpgrade back) { var item = (PocketComputerItem) stack.getItem(); - return PocketComputerItemFactory.create( - item.getComputerID(stack), item.getLabel(stack), item.getColour(stack), item.getFamily(), - back + return item.create( + item.getComputerID(stack), item.getLabel(stack), item.getColour(stack), back ); } @@ -267,20 +265,25 @@ public class UpgradeRecipeGenerator { if (recipes != null) return recipes; recipes = this.recipes = new ArrayList<>(4); - for (var family : MAIN_FAMILIES) { - if (turtle != null) { + + if (turtle != null) { + for (var turtleSupplier : TURTLES) { + var turtleItem = turtleSupplier.get(); recipes.add(turtle( ingredient, // Right upgrade, recipe on left - Ingredient.of(TurtleItemFactory.create(-1, null, -1, family, null, null, 0, null)), - TurtleItemFactory.create(-1, null, -1, family, null, turtle, 0, null) + Ingredient.of(turtleItem.create(-1, null, -1, null, null, 0, null)), + turtleItem.create(-1, null, -1, null, turtle, 0, null) )); } + } - if (pocket != null) { + if (pocket != null) { + for (var pocketSupplier : POCKET_COMPUTERS) { + var pocketItem = pocketSupplier.get(); recipes.add(pocket( ingredient, - Ingredient.of(PocketComputerItemFactory.create(-1, null, -1, family, null)), - PocketComputerItemFactory.create(-1, null, -1, family, pocket) + Ingredient.of(pocketItem.create(-1, null, -1, null)), + pocketItem.create(-1, null, -1, pocket) )); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java index c2c7f5825..b6b014697 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java @@ -10,7 +10,7 @@ import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.integration.RecipeModHelpers; import dan200.computercraft.shared.media.items.DiskItem; import dan200.computercraft.shared.pocket.items.PocketComputerItem; -import dan200.computercraft.shared.turtle.items.ITurtleItem; +import dan200.computercraft.shared.turtle.items.TurtleItem; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.RecipeTypes; @@ -71,7 +71,7 @@ public class JEIComputerCraft implements IModPlugin { */ private static final IIngredientSubtypeInterpreter turtleSubtype = (stack, ctx) -> { var item = stack.getItem(); - if (!(item instanceof ITurtleItem turtle)) return IIngredientSubtypeInterpreter.NONE; + if (!(item instanceof TurtleItem turtle)) return IIngredientSubtypeInterpreter.NONE; var name = new StringBuilder("turtle:"); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java index 7d3abc57b..a3648ba0c 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java @@ -12,6 +12,7 @@ import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.impl.PocketUpgrades; +import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerContext; @@ -45,7 +46,6 @@ import java.util.List; public class PocketComputerItem extends Item implements IComputerItem, IMedia, IColouredItem { private static final String NBT_UPGRADE = "Upgrade"; private static final String NBT_UPGRADE_INFO = "UpgradeInfo"; - public static final String NBT_LIGHT = "Light"; public static final String NBT_ON = "On"; private static final String NBT_INSTANCE = "InstanceId"; @@ -58,6 +58,14 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I this.family = family; } + public static ItemStack create(int id, @Nullable String label, int colour, ComputerFamily family, @Nullable IPocketUpgrade upgrade) { + return switch (family) { + case NORMAL -> ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().create(id, label, colour, upgrade); + case ADVANCED -> ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().create(id, label, colour, upgrade); + default -> ItemStack.EMPTY; + }; + } + public ItemStack create(int id, @Nullable String label, int colour, @Nullable IPocketUpgrade upgrade) { var result = new ItemStack(this); if (id >= 0) result.getOrCreateTag().putInt(NBT_ID, id); @@ -234,7 +242,7 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I @Override public ItemStack withFamily(ItemStack stack, ComputerFamily family) { - return PocketComputerItemFactory.create( + return create( getComputerID(stack), getLabel(stack), getColour(stack), family, getUpgrade(stack) ); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java deleted file mode 100644 index 010ef708b..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. -// -// SPDX-License-Identifier: LicenseRef-CCPL - -package dan200.computercraft.shared.pocket.items; - -import dan200.computercraft.api.pocket.IPocketUpgrade; -import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.computer.core.ComputerFamily; -import net.minecraft.world.item.ItemStack; - -import javax.annotation.Nullable; - -public final class PocketComputerItemFactory { - private PocketComputerItemFactory() { - } - - public static ItemStack create(int id, @Nullable String label, int colour, ComputerFamily family, @Nullable IPocketUpgrade upgrade) { - return switch (family) { - case NORMAL -> ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().create(id, label, colour, upgrade); - case ADVANCED -> ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().create(id, label, colour, upgrade); - default -> ItemStack.EMPTY; - }; - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java index 9ffa630b6..6c74ea7b6 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java @@ -7,9 +7,7 @@ package dan200.computercraft.shared.pocket.recipes; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.impl.PocketUpgrades; import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.pocket.items.PocketComputerItem; -import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; import net.minecraft.core.RegistryAccess; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; @@ -31,7 +29,7 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe { @Override public ItemStack getResultItem(RegistryAccess registryAccess) { - return PocketComputerItemFactory.create(-1, null, -1, ComputerFamily.NORMAL, null); + return ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().create(-1, null, -1, null); } @Override @@ -86,7 +84,7 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe { var computerID = itemComputer.getComputerID(computer); var label = itemComputer.getLabel(computer); var colour = itemComputer.getColour(computer); - return PocketComputerItemFactory.create(computerID, label, colour, family, upgrade); + return itemComputer.create(computerID, label, colour, upgrade); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/ITurtleBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/ITurtleBlockEntity.java deleted file mode 100644 index 2ee127bba..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/ITurtleBlockEntity.java +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. -// -// SPDX-License-Identifier: LicenseRef-CCPL - -package dan200.computercraft.shared.turtle.blocks; - -import dan200.computercraft.api.turtle.ITurtleAccess; -import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.shared.computer.blocks.IComputerBlockEntity; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.phys.Vec3; - -import javax.annotation.Nullable; - -public interface ITurtleBlockEntity extends IComputerBlockEntity { - int getColour(); - - @Nullable - ResourceLocation getOverlay(); - - @Nullable - ITurtleUpgrade getUpgrade(TurtleSide side); - - ITurtleAccess getAccess(); - - Vec3 getRenderOffset(float f); - - float getRenderYaw(float f); - - float getToolRenderAngle(TurtleSide side, float f); -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java index 59c3f0c99..7e058698e 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java @@ -11,8 +11,7 @@ import dan200.computercraft.shared.computer.blocks.AbstractComputerBlockEntity; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.platform.RegistryEntry; import dan200.computercraft.shared.turtle.core.TurtleBrain; -import dan200.computercraft.shared.turtle.items.ITurtleItem; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import dan200.computercraft.shared.turtle.items.TurtleItem; import dan200.computercraft.shared.util.BlockEntityHelpers; import dan200.computercraft.shared.util.WaterloggableHelpers; import net.minecraft.core.BlockPos; @@ -126,7 +125,7 @@ public class TurtleBlock extends AbstractComputerBlock implem if (!world.isClientSide && tile instanceof TurtleBlockEntity turtle) { if (entity instanceof Player player) turtle.setOwningPlayer(player.getGameProfile()); - if (stack.getItem() instanceof ITurtleItem item) { + if (stack.getItem() instanceof TurtleItem item) { // Set Upgrades for (var side : TurtleSide.values()) { turtle.getAccess().setUpgrade(side, item.getUpgrade(stack, side)); @@ -157,7 +156,14 @@ public class TurtleBlock extends AbstractComputerBlock implem @Override protected ItemStack getItem(AbstractComputerBlockEntity tile) { - return tile instanceof TurtleBlockEntity turtle ? TurtleItemFactory.create(turtle) : ItemStack.EMPTY; + if (!(tile instanceof TurtleBlockEntity turtle)) return ItemStack.EMPTY; + + var access = turtle.getAccess(); + return TurtleItem.create( + turtle.getComputerID(), turtle.getLabel(), access.getColour(), turtle.getFamily(), + access.getUpgrade(TurtleSide.LEFT), access.getUpgrade(TurtleSide.RIGHT), + access.getFuelLevel(), turtle.getOverlay() + ); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java index 9929d4447..e5c9e2f00 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java @@ -39,7 +39,7 @@ import net.minecraft.world.phys.Vec3; import javax.annotation.Nullable; import java.util.Collections; -public class TurtleBlockEntity extends AbstractComputerBlockEntity implements BasicContainer, ITurtleBlockEntity { +public class TurtleBlockEntity extends AbstractComputerBlockEntity implements BasicContainer { public static final int INVENTORY_SIZE = 16; public static final int INVENTORY_WIDTH = 4; public static final int INVENTORY_HEIGHT = 4; @@ -189,39 +189,30 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba onTileEntityChange(); } - // ITurtleTile - - @Override public @Nullable ITurtleUpgrade getUpgrade(TurtleSide side) { return brain.getUpgrade(side); } - @Override public int getColour() { return brain.getColour(); } - @Override public @Nullable ResourceLocation getOverlay() { return brain.getOverlay(); } - @Override public ITurtleAccess getAccess() { return brain; } - @Override public Vec3 getRenderOffset(float f) { return brain.getRenderOffset(f); } - @Override public float getRenderYaw(float f) { return brain.getVisualYaw(f); } - @Override public float getToolRenderAngle(TurtleSide side, float f) { return brain.getToolRenderAngle(side, f); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/ITurtleItem.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/ITurtleItem.java deleted file mode 100644 index 5dfb4751a..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/ITurtleItem.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. -// -// SPDX-License-Identifier: LicenseRef-CCPL - -package dan200.computercraft.shared.turtle.items; - -import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.shared.common.IColouredItem; -import dan200.computercraft.shared.computer.items.IComputerItem; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; - -import javax.annotation.Nullable; - -public interface ITurtleItem extends IComputerItem, IColouredItem { - @Nullable - ITurtleUpgrade getUpgrade(ItemStack stack, TurtleSide side); - - int getFuelLevel(ItemStack stack); - - @Nullable - ResourceLocation getOverlay(ItemStack stack); -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItem.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItem.java index 59620a755..91bc45717 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItem.java @@ -9,6 +9,7 @@ import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.impl.TurtleUpgrades; +import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.items.AbstractComputerItem; @@ -24,11 +25,25 @@ import javax.annotation.Nullable; import static dan200.computercraft.shared.turtle.core.TurtleBrain.*; -public class TurtleItem extends AbstractComputerItem implements ITurtleItem { +public class TurtleItem extends AbstractComputerItem implements IColouredItem { public TurtleItem(TurtleBlock block, Properties settings) { super(block, settings); } + public static ItemStack create( + int id, @Nullable String label, int colour, ComputerFamily family, + @Nullable ITurtleUpgrade leftUpgrade, @Nullable ITurtleUpgrade rightUpgrade, + int fuelLevel, @Nullable ResourceLocation overlay + ) { + return switch (family) { + case NORMAL -> + ModRegistry.Items.TURTLE_NORMAL.get().create(id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay); + case ADVANCED -> + ModRegistry.Items.TURTLE_ADVANCED.get().create(id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay); + default -> ItemStack.EMPTY; + }; + } + public ItemStack create( int id, @Nullable String label, int colour, @Nullable ITurtleUpgrade leftUpgrade, @Nullable ITurtleUpgrade rightUpgrade, @@ -99,7 +114,7 @@ public class TurtleItem extends AbstractComputerItem implements ITurtleItem { @Override public ItemStack withFamily(ItemStack stack, ComputerFamily family) { - return TurtleItemFactory.create( + return create( getComputerID(stack), getLabel(stack), getColour(stack), family, getUpgrade(stack, TurtleSide.LEFT), getUpgrade(stack, TurtleSide.RIGHT), @@ -107,7 +122,6 @@ public class TurtleItem extends AbstractComputerItem implements ITurtleItem { ); } - @Override public @Nullable ITurtleUpgrade getUpgrade(ItemStack stack, TurtleSide side) { var tag = stack.getTag(); if (tag == null) return null; @@ -116,13 +130,11 @@ public class TurtleItem extends AbstractComputerItem implements ITurtleItem { return tag.contains(key) ? TurtleUpgrades.instance().get(tag.getString(key)) : null; } - @Override public @Nullable ResourceLocation getOverlay(ItemStack stack) { var tag = stack.getTag(); return tag != null && tag.contains(NBT_OVERLAY) ? new ResourceLocation(tag.getString(NBT_OVERLAY)) : null; } - @Override public int getFuelLevel(ItemStack stack) { var tag = stack.getTag(); return tag != null && tag.contains(NBT_FUEL) ? tag.getInt(NBT_FUEL) : 0; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItemFactory.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItemFactory.java deleted file mode 100644 index 81953a899..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItemFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. -// -// SPDX-License-Identifier: LicenseRef-CCPL - -package dan200.computercraft.shared.turtle.items; - -import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.turtle.blocks.ITurtleBlockEntity; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; - -import javax.annotation.Nullable; - -public final class TurtleItemFactory { - private TurtleItemFactory() { - } - - public static ItemStack create(ITurtleBlockEntity turtle) { - var access = turtle.getAccess(); - - return create( - turtle.getComputerID(), turtle.getLabel(), turtle.getColour(), turtle.getFamily(), - access.getUpgrade(TurtleSide.LEFT), access.getUpgrade(TurtleSide.RIGHT), - access.getFuelLevel(), turtle.getOverlay() - ); - } - - public static ItemStack create( - int id, @Nullable String label, int colour, ComputerFamily family, - @Nullable ITurtleUpgrade leftUpgrade, @Nullable ITurtleUpgrade rightUpgrade, - int fuelLevel, @Nullable ResourceLocation overlay - ) { - return switch (family) { - case NORMAL -> - ModRegistry.Items.TURTLE_NORMAL.get().create(id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay); - case ADVANCED -> - ModRegistry.Items.TURTLE_ADVANCED.get().create(id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay); - default -> ItemStack.EMPTY; - }; - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleOverlayRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleOverlayRecipe.java index 4e6cf5c0b..4076a3662 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleOverlayRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleOverlayRecipe.java @@ -9,9 +9,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.turtle.items.ITurtleItem; import dan200.computercraft.shared.turtle.items.TurtleItem; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; import net.minecraft.core.NonNullList; import net.minecraft.core.RegistryAccess; import net.minecraft.network.FriendlyByteBuf; @@ -22,7 +20,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.*; /** - * A {@link ShapelessRecipe} which sets the {@linkplain ITurtleItem#getOverlay(ItemStack)} turtle's overlay} instead. + * A {@link ShapelessRecipe} which sets the {@linkplain TurtleItem#getOverlay(ItemStack)} turtle's overlay} instead. */ public class TurtleOverlayRecipe extends ShapelessRecipe { private final ResourceLocation overlay; @@ -35,12 +33,11 @@ public class TurtleOverlayRecipe extends ShapelessRecipe { } private static ItemStack make(ItemStack stack, ResourceLocation overlay) { - var turtle = (ITurtleItem) stack.getItem(); - return TurtleItemFactory.create( + var turtle = (TurtleItem) stack.getItem(); + return turtle.create( turtle.getComputerID(stack), turtle.getLabel(stack), turtle.getColour(stack), - turtle.getFamily(), turtle.getUpgrade(stack, TurtleSide.LEFT), turtle.getUpgrade(stack, TurtleSide.RIGHT), turtle.getFuelLevel(stack), diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java index 2451c8324..2b786a5db 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java @@ -8,7 +8,7 @@ import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.computer.recipe.ComputerFamilyRecipe; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import dan200.computercraft.shared.turtle.items.TurtleItem; import net.minecraft.core.NonNullList; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; @@ -31,7 +31,7 @@ public final class TurtleRecipe extends ComputerFamilyRecipe { var computerID = item.getComputerID(stack); var label = item.getLabel(stack); - return TurtleItemFactory.create(computerID, label, -1, getFamily(), null, null, 0, null); + return TurtleItem.create(computerID, label, -1, getFamily(), null, null, 0, null); } public static class Serializer extends ComputerFamilyRecipe.Serializer { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java index 690116d56..4dcd9e5a2 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java @@ -8,9 +8,7 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.turtle.items.ITurtleItem; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import dan200.computercraft.shared.turtle.items.TurtleItem; import net.minecraft.core.RegistryAccess; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.CraftingContainer; @@ -32,7 +30,7 @@ public final class TurtleUpgradeRecipe extends CustomRecipe { @Override public ItemStack getResultItem(RegistryAccess registryAccess) { - return TurtleItemFactory.create(-1, null, -1, ComputerFamily.NORMAL, null, null, 0, null); + return ModRegistry.Items.TURTLE_NORMAL.get().create(-1, null, -1, null, null, 0, null); } @Override @@ -58,7 +56,7 @@ public final class TurtleUpgradeRecipe extends CustomRecipe { return ItemStack.EMPTY; } - if (item.getItem() instanceof ITurtleItem) { + if (item.getItem() instanceof TurtleItem) { // Item is a turtle if (turtle.isEmpty()) { turtle = item; @@ -105,8 +103,7 @@ public final class TurtleUpgradeRecipe extends CustomRecipe { // At this point we have a turtle + 1 or 2 items // Get the turtle we already have - var itemTurtle = (ITurtleItem) turtle.getItem(); - var family = itemTurtle.getFamily(); + var itemTurtle = (TurtleItem) turtle.getItem(); var upgrades = new ITurtleUpgrade[]{ itemTurtle.getUpgrade(turtle, TurtleSide.LEFT), itemTurtle.getUpgrade(turtle, TurtleSide.RIGHT), @@ -128,7 +125,7 @@ public final class TurtleUpgradeRecipe extends CustomRecipe { var fuelLevel = itemTurtle.getFuelLevel(turtle); var colour = itemTurtle.getColour(turtle); var overlay = itemTurtle.getOverlay(turtle); - return TurtleItemFactory.create(computerID, label, colour, family, upgrades[0], upgrades[1], fuelLevel, overlay); + return itemTurtle.create(computerID, label, colour, upgrades[0], upgrades[1], fuelLevel, overlay); } @Override From 8fdef542c977fdfde887bd2039cf73a7e8fcb1fa Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 4 Jun 2023 12:49:46 +0100 Subject: [PATCH 2/4] Update to latest JEI --- gradle/libs.versions.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 07956e0cb..9ac7e1707 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,7 +34,7 @@ slf4j = "1.7.36" # Minecraft mods iris = "1.5.2+1.19.4" -jei = "11.3.0.262" +jei = "13.1.0.11" modmenu = "6.1.0-rc.1" oculus = "1.2.5" rei = "10.0.578" @@ -91,9 +91,9 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" } fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" } iris = { module = "maven.modrinth:iris", version.ref = "iris" } -jei-api = { module = "mezz.jei:jei-1.19.2-common-api", version.ref = "jei" } -jei-fabric = { module = "mezz.jei:jei-1.19.2-fabric", version.ref = "jei" } -jei-forge = { module = "mezz.jei:jei-1.19.2-forge", version.ref = "jei" } +jei-api = { module = "mezz.jei:jei-1.19.4-common-api", version.ref = "jei" } +jei-fabric = { module = "mezz.jei:jei-1.19.4-fabric", version.ref = "jei" } +jei-forge = { module = "mezz.jei:jei-1.19.4-forge", version.ref = "jei" } mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" } modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" } oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" } @@ -148,10 +148,10 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"] # Minecraft externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"] externalMods-forge-compile = ["oculus", "jei-api"] -externalMods-forge-runtime = [] +externalMods-forge-runtime = ["jei-forge"] externalMods-fabric = ["nightConfig-core", "nightConfig-toml"] externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"] -externalMods-fabric-runtime = ["modmenu"] +externalMods-fabric-runtime = ["jei-fabric", "modmenu"] # Testing test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"] From d45f2bfe80f81d927d427b1b110b01012757cd9b Mon Sep 17 00:00:00 2001 From: Commandcracker <49335821+Commandcracker@users.noreply.github.com> Date: Sun, 4 Jun 2023 14:10:04 +0200 Subject: [PATCH 3/4] Fix channel names in colors documentation (#1467) --- .../main/resources/data/computercraft/lua/rom/apis/colors.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/colors.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/colors.lua index 249c69f6f..5bdbf9fd2 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/colors.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/colors.lua @@ -273,7 +273,7 @@ end --- Combine a three-colour RGB value into one hexadecimal representation. -- -- @tparam number r The red channel, should be between 0 and 1. --- @tparam number g The red channel, should be between 0 and 1. +-- @tparam number g The green channel, should be between 0 and 1. -- @tparam number b The blue channel, should be between 0 and 1. -- @treturn number The combined hexadecimal colour. -- @usage @@ -296,7 +296,7 @@ end -- -- @tparam number rgb The combined hexadecimal colour. -- @treturn number The red channel, will be between 0 and 1. --- @treturn number The red channel, will be between 0 and 1. +-- @treturn number The green channel, will be between 0 and 1. -- @treturn number The blue channel, will be between 0 and 1. -- @usage -- ```lua From c91bb5ac33696180a108895fd1a11c30c51a85d6 Mon Sep 17 00:00:00 2001 From: Drew Edwards Date: Tue, 6 Jun 2023 19:58:24 +0100 Subject: [PATCH 4/4] Add support for proxying HTTP requests (#1461) --- gradle/libs.versions.toml | 2 + .../shared/config/AddressRuleConfig.java | 8 +- .../shared/config/ConfigFile.java | 15 +++- .../shared/config/ConfigSpec.java | 43 +++++++++- .../shared/config/ProxyPasswordConfig.java | 48 +++++++++++ projects/core/build.gradle.kts | 2 + .../dan200/computercraft/core/CoreConfig.java | 6 ++ .../core/apis/http/NetworkUtils.java | 79 +++++++++++++++++-- .../core/apis/http/options/Action.java | 3 +- .../core/apis/http/options/Options.java | 4 +- .../apis/http/options/PartialOptions.java | 13 ++- .../core/apis/http/options/ProxyType.java | 18 +++++ .../core/apis/http/request/HttpRequest.java | 3 +- .../core/apis/http/websocket/Websocket.java | 3 +- projects/fabric/build.gradle.kts | 4 + .../shared/platform/FabricConfigFile.java | 8 +- projects/forge/build.gradle.kts | 8 ++ .../dan200/computercraft/ComputerCraft.java | 7 +- .../shared/platform/ForgeConfigFile.java | 2 +- 19 files changed, 251 insertions(+), 25 deletions(-) create mode 100644 projects/common/src/main/java/dan200/computercraft/shared/config/ProxyPasswordConfig.java create mode 100644 projects/core/src/main/java/dan200/computercraft/core/apis/http/options/ProxyType.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9ac7e1707..fa3ffc7c3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -83,6 +83,8 @@ kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" } +netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" } +netty-proxy = { module = "io.netty:netty-handler-proxy", version.ref = "netty" } nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" } nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" } slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/config/AddressRuleConfig.java b/projects/common/src/main/java/dan200/computercraft/shared/config/AddressRuleConfig.java index 5c6cd20a2..8974c9185 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/config/AddressRuleConfig.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/config/AddressRuleConfig.java @@ -45,6 +45,9 @@ class AddressRuleConfig { config.setComment("max_websocket_message", "The maximum size (in bytes) that a computer can send or receive in one websocket packet."); config.set("max_websocket_message", AddressRule.WEBSOCKET_MESSAGE); + + config.setComment("use_proxy", "Enable use of the HTTP/SOCKS proxy if it is configured."); + config.set("use_proxy", false); } return config; @@ -58,6 +61,7 @@ class AddressRuleConfig { && check(builder, "max_upload", Number.class) && check(builder, "max_download", Number.class) && check(builder, "websocket_message", Number.class) + && check(builder, "use_proxy", Boolean.class) && AddressRule.parse(hostObj, port, PartialOptions.DEFAULT) != null; } @@ -71,12 +75,14 @@ class AddressRuleConfig { var maxUpload = unboxOptLong(get(builder, "max_upload", Number.class).map(Number::longValue)); var maxDownload = unboxOptLong(get(builder, "max_download", Number.class).map(Number::longValue)); var websocketMessage = unboxOptInt(get(builder, "websocket_message", Number.class).map(Number::intValue)); + var useProxy = get(builder, "use_proxy", Boolean.class); var options = new PartialOptions( action, maxUpload, maxDownload, - websocketMessage + websocketMessage, + useProxy ); return AddressRule.parse(hostObj, port, options); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/config/ConfigFile.java b/projects/common/src/main/java/dan200/computercraft/shared/config/ConfigFile.java index 346fb5e49..afb239e9a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/config/ConfigFile.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/config/ConfigFile.java @@ -8,6 +8,7 @@ import com.google.common.base.Splitter; import javax.annotation.Nullable; import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.nio.file.Path; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; @@ -141,6 +142,18 @@ public interface ConfigFile { * @param onChange The function to run on change. * @return The built config file. */ - public abstract ConfigFile build(Runnable onChange); + public abstract ConfigFile build(ConfigListener onChange); + } + + @FunctionalInterface + interface ConfigListener { + /** + * The function called then a config file is changed. + * + * @param path The path to the config file. This will be {@code null} when the config file does not exist on + * disk, such as when synced from a server to the client. + * @see Builder#build(ConfigListener) + */ + void onConfigChanged(@Nullable Path path); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/config/ConfigSpec.java b/projects/common/src/main/java/dan200/computercraft/shared/config/ConfigSpec.java index 9f751ce30..fdd603ade 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/config/ConfigSpec.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/config/ConfigSpec.java @@ -5,10 +5,12 @@ package dan200.computercraft.shared.config; import com.electronwill.nightconfig.core.UnmodifiableConfig; +import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.core.CoreConfig; import dan200.computercraft.core.Logging; import dan200.computercraft.core.apis.http.NetworkUtils; import dan200.computercraft.core.apis.http.options.Action; +import dan200.computercraft.core.apis.http.options.ProxyType; import dan200.computercraft.core.computer.mainthread.MainThreadConfig; import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.platform.PlatformHelper; @@ -16,6 +18,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.filter.MarkerFilter; +import javax.annotation.Nullable; +import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -49,6 +53,10 @@ public final class ConfigSpec { public static final ConfigFile.Value httpDownloadBandwidth; public static final ConfigFile.Value httpUploadBandwidth; + public static final ConfigFile.Value httpProxyType; + public static final ConfigFile.Value httpProxyHost; + public static final ConfigFile.Value httpProxyPort; + public static final ConfigFile.Value commandBlockEnabled; public static final ConfigFile.Value modemRange; public static final ConfigFile.Value modemHighAltitudeRange; @@ -222,6 +230,30 @@ public final class ConfigSpec { builder.pop(); + builder + .comment(""" + Tunnels HTTP and websocket requests through a proxy server. Only affects HTTP + rules with "use_proxy" set to true (off by default). + If authentication is required for the proxy, create a "computercraft-proxy.pw" + file in the same directory as "computercraft-server.toml", containing the + username and password separated by a colon, e.g. "myuser:mypassword". For + SOCKS4 proxies only the username is required.""") + .push("proxy"); + + httpProxyType = builder + .comment("The type of proxy to use.") + .defineEnum("type", CoreConfig.httpProxyType); + + httpProxyHost = builder + .comment("The hostname or IP address of the proxy server.") + .define("host", CoreConfig.httpProxyHost); + + httpProxyPort = builder + .comment("The port of the proxy server.") + .defineInRange("port", CoreConfig.httpProxyPort, 1, 65536); + + builder.pop(); + builder.pop(); } @@ -339,7 +371,7 @@ public final class ConfigSpec { clientSpec = clientBuilder.build(ConfigSpec::syncClient); } - public static void syncServer() { + public static void syncServer(@Nullable Path path) { // General Config.computerSpaceLimit = computerSpaceLimit.get(); Config.floppySpaceLimit = floppySpaceLimit.get(); @@ -370,6 +402,13 @@ public final class ConfigSpec { CoreConfig.httpMaxWebsockets = httpMaxWebsockets.get(); CoreConfig.httpDownloadBandwidth = httpDownloadBandwidth.get(); CoreConfig.httpUploadBandwidth = httpUploadBandwidth.get(); + + CoreConfig.httpProxyType = httpProxyType.get(); + CoreConfig.httpProxyHost = httpProxyHost.get(); + CoreConfig.httpProxyPort = httpProxyPort.get(); + + if (path != null) ProxyPasswordConfig.init(path.resolveSibling(ComputerCraftAPI.MOD_ID + "-proxy.pw")); + NetworkUtils.reloadConfig(); // Peripheral @@ -396,7 +435,7 @@ public final class ConfigSpec { Config.monitorHeight = monitorHeight.get(); } - public static void syncClient() { + public static void syncClient(@Nullable Path path) { Config.monitorRenderer = monitorRenderer.get(); Config.monitorDistance = monitorDistance.get(); Config.uploadNagDelay = uploadNagDelay.get(); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/config/ProxyPasswordConfig.java b/projects/common/src/main/java/dan200/computercraft/shared/config/ProxyPasswordConfig.java new file mode 100644 index 000000000..3c45f64f4 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/config/ProxyPasswordConfig.java @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.config; + +import dan200.computercraft.core.CoreConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +record ProxyPasswordConfig(String username, String password) { + private static final Logger LOG = LoggerFactory.getLogger(ProxyPasswordConfig.class); + + @Nullable + private static ProxyPasswordConfig loadFromFile(@Nullable Path path) { + if (path == null || !path.toFile().exists()) return null; + + try (var br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + var line = br.readLine(); + if (line == null) return null; + + var parts = line.trim().split(":", 2); + if (parts.length == 0) return null; + + return new ProxyPasswordConfig(parts[0], parts.length == 2 ? parts[1] : ""); + } catch (IOException e) { + LOG.error("Failed to load proxy password from {}.", path, e); + return null; + } + } + + static void init(@Nullable Path path) { + var config = loadFromFile(path); + if (config == null) { + CoreConfig.httpProxyUsername = ""; + CoreConfig.httpProxyPassword = ""; + } else { + CoreConfig.httpProxyUsername = config.username; + CoreConfig.httpProxyPassword = config.password; + } + } +} diff --git a/projects/core/build.gradle.kts b/projects/core/build.gradle.kts index ba5e8ef9b..fc84c1771 100644 --- a/projects/core/build.gradle.kts +++ b/projects/core/build.gradle.kts @@ -21,6 +21,8 @@ dependencies { implementation(libs.guava) implementation(libs.jzlib) implementation(libs.netty.http) + implementation(libs.netty.socks) + implementation(libs.netty.proxy) implementation(libs.slf4j) implementation(libs.asm) diff --git a/projects/core/src/main/java/dan200/computercraft/core/CoreConfig.java b/projects/core/src/main/java/dan200/computercraft/core/CoreConfig.java index 01dee60b2..46cff11c7 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/CoreConfig.java +++ b/projects/core/src/main/java/dan200/computercraft/core/CoreConfig.java @@ -6,6 +6,7 @@ package dan200.computercraft.core; import dan200.computercraft.core.apis.http.options.Action; import dan200.computercraft.core.apis.http.options.AddressRule; +import dan200.computercraft.core.apis.http.options.ProxyType; import java.util.List; import java.util.OptionalInt; @@ -34,4 +35,9 @@ public final class CoreConfig { public static int httpMaxWebsockets = 4; public static int httpDownloadBandwidth = 32 * 1024 * 1024; public static int httpUploadBandwidth = 32 * 1024 * 1024; + public static ProxyType httpProxyType = ProxyType.HTTP; + public static String httpProxyHost = ""; + public static int httpProxyPort = 8080; + public static String httpProxyUsername = ""; + public static String httpProxyPassword = ""; } diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java index 3d0aa167b..517e67790 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java @@ -4,6 +4,7 @@ package dan200.computercraft.core.apis.http; +import com.google.common.base.Strings; import dan200.computercraft.core.CoreConfig; import dan200.computercraft.core.apis.http.options.Action; import dan200.computercraft.core.apis.http.options.AddressRule; @@ -17,11 +18,16 @@ import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException; +import io.netty.handler.proxy.HttpProxyHandler; +import io.netty.handler.proxy.Socks4ProxyHandler; +import io.netty.handler.proxy.Socks5ProxyHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslHandler; import io.netty.handler.timeout.ReadTimeoutException; import io.netty.handler.traffic.AbstractTrafficShapingHandler; import io.netty.handler.traffic.GlobalTrafficShapingHandler; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +38,7 @@ import java.net.InetSocketAddress; import java.net.URI; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; /** * Just a shared object for executing simple HTTP related tasks. @@ -57,8 +64,7 @@ public final class NetworkUtils { private static @Nullable SslContext sslContext; private static boolean triedSslContext = false; - @Nullable - private static SslContext makeSslContext() { + private static @Nullable SslContext makeSslContext() { if (triedSslContext) return sslContext; synchronized (sslLock) { if (triedSslContext) return sslContext; @@ -133,6 +139,66 @@ public final class NetworkUtils { return options; } + /** + * Creates a proxy handler for a specific domain. Returns null if a proxy is not required for this HTTP rule, or + * throws if it is required but is not configured correctly. + *

+ * Note, this may require a DNS lookup, and so should not be executed on the main CC thread. + * + * @param options The options for the host to be proxied. + * @param timeout The timeout for this connection. Currently only used for establishing the SSL initialisation. + * @return A consumer that takes a {@link SocketChannel} and injects the proxy handler.. + * @throws HTTPRequestException If a proxy is required but not configured correctly. + */ + public static @Nullable Consumer getProxyHandler(Options options, int timeout) throws HTTPRequestException { + if (!options.useProxy) return null; + + var type = CoreConfig.httpProxyType; + var host = CoreConfig.httpProxyHost; + var port = CoreConfig.httpProxyPort; + var username = CoreConfig.httpProxyUsername; + var password = CoreConfig.httpProxyPassword; + + if (Strings.isNullOrEmpty(host)) { + throw new HTTPRequestException("Proxy host not configured"); + } + + var proxyAddress = new InetSocketAddress(host, port); + if (proxyAddress.isUnresolved()) throw new HTTPRequestException("Unknown proxy host"); + + return switch (type) { + case HTTP -> ch -> ch.pipeline().addLast(new HttpProxyHandler(proxyAddress, username, password)); + case HTTPS -> { + var sslContext = getSslContext(); + yield ch -> { + var p = ch.pipeline(); + // If we're using an HTTPS proxy, we need to add an SSL handler for the proxy too. + p.addLast(makeSslHandler(ch, sslContext, timeout, host, port)); + p.addLast(new HttpProxyHandler(proxyAddress, username, password)); + }; + } + case SOCKS4 -> ch -> ch.pipeline().addLast(new Socks4ProxyHandler(proxyAddress, username)); + case SOCKS5 -> ch -> ch.pipeline().addLast(new Socks5ProxyHandler(proxyAddress, username, password)); + }; + } + + /** + * Make an SSL handler for the remote host. + * + * @param ch The channel the handler will be added to. + * @param sslContext The SSL context, if present. + * @param timeout The timeout on this channel. + * @param peerHost The host to connect to. + * @param peerPort The port to connect to. + * @return The SSL handler. + * @see io.netty.handler.ssl.SslHandler + */ + private static SslHandler makeSslHandler(SocketChannel ch, @NotNull SslContext sslContext, int timeout, String peerHost, int peerPort) { + var handler = sslContext.newHandler(ch.alloc(), peerHost, peerPort); + if (timeout > 0) handler.setHandshakeTimeoutMillis(timeout); + return handler; + } + /** * Set up some basic properties of the channel. This adds a timeout, the traffic shaping handler, and the SSL * handler. @@ -141,19 +207,20 @@ public final class NetworkUtils { * @param uri The URI to connect to. * @param socketAddress The address of the socket to connect to. * @param sslContext The SSL context, if present. + * @param proxy The proxy handler, if present. * @param timeout The timeout on this channel. * @see io.netty.channel.ChannelInitializer */ - public static void initChannel(SocketChannel ch, URI uri, InetSocketAddress socketAddress, @Nullable SslContext sslContext, int timeout) { + public static void initChannel(SocketChannel ch, URI uri, InetSocketAddress socketAddress, @Nullable SslContext sslContext, @Nullable Consumer proxy, int timeout) { if (timeout > 0) ch.config().setConnectTimeoutMillis(timeout); var p = ch.pipeline(); p.addLast(SHAPING_HANDLER); + if (proxy != null) proxy.accept(ch); + if (sslContext != null) { - var handler = sslContext.newHandler(ch.alloc(), uri.getHost(), socketAddress.getPort()); - if (timeout > 0) handler.setHandshakeTimeoutMillis(timeout); - p.addLast(handler); + p.addLast(makeSslHandler(ch, sslContext, timeout, uri.getHost(), socketAddress.getPort())); } } diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/Action.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/Action.java index 7b8483121..ffdcfb3b1 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/Action.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/Action.java @@ -4,6 +4,7 @@ package dan200.computercraft.core.apis.http.options; +import java.util.Optional; import java.util.OptionalInt; import java.util.OptionalLong; @@ -12,7 +13,7 @@ public enum Action { DENY; private final PartialOptions partial = new PartialOptions( - this, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty() + this, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), Optional.empty() ); public PartialOptions toPartial() { diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/Options.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/Options.java index 6205e1ded..ddc3cab0a 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/Options.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/Options.java @@ -13,11 +13,13 @@ public final class Options { public final long maxUpload; public final long maxDownload; public final int websocketMessage; + public final boolean useProxy; - Options(Action action, long maxUpload, long maxDownload, int websocketMessage) { + Options(Action action, long maxUpload, long maxDownload, int websocketMessage, boolean useProxy) { this.action = action; this.maxUpload = maxUpload; this.maxDownload = maxDownload; this.websocketMessage = websocketMessage; + this.useProxy = useProxy; } } diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/PartialOptions.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/PartialOptions.java index 9717f936b..1d4d6824f 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/PartialOptions.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/PartialOptions.java @@ -7,28 +7,31 @@ package dan200.computercraft.core.apis.http.options; import com.google.errorprone.annotations.Immutable; import javax.annotation.Nullable; +import java.util.Optional; import java.util.OptionalInt; import java.util.OptionalLong; @Immutable public final class PartialOptions { public static final PartialOptions DEFAULT = new PartialOptions( - null, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty() + null, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), Optional.empty() ); private final @Nullable Action action; private final OptionalLong maxUpload; private final OptionalLong maxDownload; private final OptionalInt websocketMessage; + private final Optional useProxy; @SuppressWarnings("Immutable") // Lazily initialised, so this mutation is invisible in the public API private @Nullable Options options; - public PartialOptions(@Nullable Action action, OptionalLong maxUpload, OptionalLong maxDownload, OptionalInt websocketMessage) { + public PartialOptions(@Nullable Action action, OptionalLong maxUpload, OptionalLong maxDownload, OptionalInt websocketMessage, Optional useProxy) { this.action = action; this.maxUpload = maxUpload; this.maxDownload = maxDownload; this.websocketMessage = websocketMessage; + this.useProxy = useProxy; } Options toOptions() { @@ -38,7 +41,8 @@ public final class PartialOptions { action == null ? Action.DENY : action, maxUpload.orElse(AddressRule.MAX_UPLOAD), maxDownload.orElse(AddressRule.MAX_DOWNLOAD), - websocketMessage.orElse(AddressRule.WEBSOCKET_MESSAGE) + websocketMessage.orElse(AddressRule.WEBSOCKET_MESSAGE), + useProxy.orElse(false) ); } @@ -56,7 +60,8 @@ public final class PartialOptions { action == null && other.action != null ? other.action : action, maxUpload.isPresent() ? maxUpload : other.maxUpload, maxDownload.isPresent() ? maxDownload : other.maxDownload, - websocketMessage.isPresent() ? websocketMessage : other.websocketMessage + websocketMessage.isPresent() ? websocketMessage : other.websocketMessage, + useProxy.isPresent() ? useProxy : other.useProxy ); } } diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/ProxyType.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/ProxyType.java new file mode 100644 index 000000000..cbbfa35a3 --- /dev/null +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/ProxyType.java @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.core.apis.http.options; + +/** + * The type of proxy to use for HTTP requests. + * + * @see dan200.computercraft.core.apis.http.NetworkUtils#getProxyHandler(Options, int) + * @see dan200.computercraft.core.CoreConfig#httpProxyType + */ +public enum ProxyType { + HTTP, + HTTPS, + SOCKS4, + SOCKS5 +} diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java index d7d40c1c0..33fdab50e 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java @@ -124,6 +124,7 @@ public class HttpRequest extends Resource { var socketAddress = NetworkUtils.getAddress(uri, ssl); var options = NetworkUtils.getOptions(uri.getHost(), socketAddress); var sslContext = ssl ? NetworkUtils.getSslContext() : null; + var proxy = NetworkUtils.getProxyHandler(options, timeout); // getAddress may have a slight delay, so let's perform another cancellation check. if (isClosed()) return; @@ -145,7 +146,7 @@ public class HttpRequest extends Resource { .handler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) { - NetworkUtils.initChannel(ch, uri, socketAddress, sslContext, timeout); + NetworkUtils.initChannel(ch, uri, socketAddress, sslContext, proxy, timeout); var p = ch.pipeline(); if (timeout > 0) p.addLast(new ReadTimeoutHandler(timeout, TimeUnit.MILLISECONDS)); diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java index 282b04c79..3dfb3fd8e 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java @@ -117,6 +117,7 @@ public class Websocket extends Resource { var socketAddress = NetworkUtils.getAddress(uri, ssl); var options = NetworkUtils.getOptions(uri.getHost(), socketAddress); var sslContext = ssl ? NetworkUtils.getSslContext() : null; + var proxy = NetworkUtils.getProxyHandler(options, timeout); // getAddress may have a slight delay, so let's perform another cancellation check. if (isClosed()) return; @@ -127,7 +128,7 @@ public class Websocket extends Resource { .handler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) { - NetworkUtils.initChannel(ch, uri, socketAddress, sslContext, timeout); + NetworkUtils.initChannel(ch, uri, socketAddress, sslContext, proxy, timeout); var subprotocol = headers.get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); var handshaker = new NoOriginWebSocketHandshaker( diff --git a/projects/fabric/build.gradle.kts b/projects/fabric/build.gradle.kts index f9e8fb6b0..576bac769 100644 --- a/projects/fabric/build.gradle.kts +++ b/projects/fabric/build.gradle.kts @@ -54,6 +54,8 @@ dependencies { include(libs.cobalt) include(libs.jzlib) include(libs.netty.http) + include(libs.netty.socks) + include(libs.netty.proxy) include(libs.nightConfig.core) include(libs.nightConfig.toml) @@ -65,6 +67,8 @@ dependencies { // in our POM, and this is the easiest way. runtimeOnly(libs.cobalt) runtimeOnly(libs.netty.http) + runtimeOnly(libs.netty.socks) + runtimeOnly(libs.netty.proxy) annotationProcessorEverywhere(libs.autoService) diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricConfigFile.java b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricConfigFile.java index 1115f9b95..1294e3428 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricConfigFile.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/FabricConfigFile.java @@ -34,11 +34,11 @@ public class FabricConfigFile implements ConfigFile { private final ConfigSpec spec; private final Trie entries; - private final Runnable onChange; + private final ConfigListener onChange; private @Nullable CommentedFileConfig config; - public FabricConfigFile(ConfigSpec spec, Trie entries, Runnable onChange) { + public FabricConfigFile(ConfigSpec spec, Trie entries, ConfigListener onChange) { this.spec = spec; this.entries = entries; this.onChange = onChange; @@ -95,7 +95,7 @@ public class FabricConfigFile implements ConfigFile { LOG.warn("Incorrect key {} was corrected from {} to {}", String.join(".", entryPath), oldValue, newValue); }); - onChange.run(); + onChange.onConfigChanged(config.getNioPath()); return corrected > 0; } @@ -204,7 +204,7 @@ public class FabricConfigFile implements ConfigFile { } @Override - public ConfigFile build(Runnable onChange) { + public ConfigFile build(ConfigListener onChange) { return new FabricConfigFile(spec, entries, onChange); } } diff --git a/projects/forge/build.gradle.kts b/projects/forge/build.gradle.kts index bb32e08fe..589e44f0d 100644 --- a/projects/forge/build.gradle.kts +++ b/projects/forge/build.gradle.kts @@ -147,6 +147,14 @@ dependencies { jarJar.ranged(this, "[${libs.versions.netty.get()},)") isTransitive = false } + minecraftEmbed(libs.netty.socks) { + jarJar.ranged(this, "[${libs.versions.netty.get()},)") + isTransitive = false + } + minecraftEmbed(libs.netty.proxy) { + jarJar.ranged(this, "[${libs.versions.netty.get()},)") + isTransitive = false + } testFixturesApi(libs.bundles.test) testFixturesApi(libs.bundles.kotlin) diff --git a/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java b/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java index fded03ddb..b47f39772 100644 --- a/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java @@ -4,6 +4,7 @@ package dan200.computercraft; +import com.electronwill.nightconfig.core.file.FileConfig; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ForgeComputerCraftAPI; import dan200.computercraft.api.detail.ForgeDetailRegistries; @@ -94,10 +95,12 @@ public final class ComputerCraft { private static void syncConfig(ModConfig config) { if (!config.getModId().equals(ComputerCraftAPI.MOD_ID)) return; + var path = config.getConfigData() instanceof FileConfig fileConfig ? fileConfig.getNioPath() : null; + if (config.getType() == ModConfig.Type.SERVER && ((ForgeConfigFile) ConfigSpec.serverSpec).spec().isLoaded()) { - ConfigSpec.syncServer(); + ConfigSpec.syncServer(path); } else if (config.getType() == ModConfig.Type.CLIENT) { - ConfigSpec.syncClient(); + ConfigSpec.syncClient(path); } } } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeConfigFile.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeConfigFile.java index eba7500ce..2f44b2e73 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeConfigFile.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/platform/ForgeConfigFile.java @@ -117,7 +117,7 @@ public final class ForgeConfigFile implements ConfigFile { } @Override - public ConfigFile build(Runnable onChange) { + public ConfigFile build(ConfigListener onChange) { var spec = builder.build(); entries.stream().forEach(x -> { if (x instanceof ValueImpl value) value.owner = spec;