Merge branch 'mc-1.19.x' of github.com:cc-tweaked/CC-Tweaked into mc-1.19.x
This commit is contained in:
commit
84bbc321c4
|
@ -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"
|
||||
|
@ -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" }
|
||||
|
@ -91,9 +93,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 +150,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"]
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
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.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 @@ private void diskColours(Consumer<FinishedRecipe> add) {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<TurtleItem> 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<FinishedRecipe> 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 @@ private void turtleUpgrades(Consumer<FinishedRecipe> add) {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<PocketComputerItem> 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<FinishedRecipe> 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 @@ private void turtleOverlays(Consumer<FinishedRecipe> add) {
|
|||
}
|
||||
|
||||
private void turtleOverlay(Consumer<FinishedRecipe> add, String overlay, Consumer<ShapelessRecipeBuilder> 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())
|
||||
|
|
|
@ -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.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;
|
||||
|
|
|
@ -45,6 +45,9 @@ The maximum size (in bytes) that a computer can upload in a single request. This
|
|||
|
||||
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 @@ && check(builder, "port", Number.class)
|
|||
&& 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 @@ public static AddressRule parseRule(UnmodifiableConfig builder) {
|
|||
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);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
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 void pop() {
|
|||
* @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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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<Integer> httpDownloadBandwidth;
|
||||
public static final ConfigFile.Value<Integer> httpUploadBandwidth;
|
||||
|
||||
public static final ConfigFile.Value<ProxyType> httpProxyType;
|
||||
public static final ConfigFile.Value<String> httpProxyHost;
|
||||
public static final ConfigFile.Value<Integer> httpProxyPort;
|
||||
|
||||
public static final ConfigFile.Value<Boolean> commandBlockEnabled;
|
||||
public static final ConfigFile.Value<Integer> modemRange;
|
||||
public static final ConfigFile.Value<Integer> modemHighAltitudeRange;
|
||||
|
@ -222,6 +230,30 @@ CIDR notation ("127.0.0.0/8").
|
|||
|
||||
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 @@ CIDR notation ("127.0.0.0/8").
|
|||
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 static void syncServer() {
|
|||
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 static void syncServer() {
|
|||
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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,21 +7,25 @@
|
|||
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<ComputerFamily> MAIN_FAMILIES = Arrays.asList(ComputerFamily.NORMAL, ComputerFamily.ADVANCED);
|
||||
static final List<Supplier<TurtleItem>> TURTLES = List.of(ModRegistry.Items.TURTLE_NORMAL, ModRegistry.Items.TURTLE_ADVANCED);
|
||||
static final List<Supplier<PocketComputerItem>> POCKET_COMPUTERS = List.of(ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED);
|
||||
|
||||
private RecipeModHelpers() {
|
||||
}
|
||||
|
@ -49,13 +53,17 @@ public static boolean shouldRemoveRecipe(ResourceLocation id) {
|
|||
*/
|
||||
public static List<ItemStack> getExtraStacks() {
|
||||
List<ItemStack> 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
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 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 List<T> findRecipesWithOutput(ItemStack stack) {
|
|||
|
||||
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 @@ List<T> getRecipes() {
|
|||
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)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
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 void onRuntimeAvailable(IJeiRuntime runtime) {
|
|||
*/
|
||||
private static final IIngredientSubtypeInterpreter<ItemStack> 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:");
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
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 @@
|
|||
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 PocketComputerItem(Properties settings, ComputerFamily family) {
|
|||
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 ComputerFamily getFamily() {
|
|||
|
||||
@Override
|
||||
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
|
||||
return PocketComputerItemFactory.create(
|
||||
return create(
|
||||
getComputerID(stack), getLabel(stack), getColour(stack),
|
||||
family, getUpgrade(stack)
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -7,9 +7,7 @@
|
|||
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 boolean canCraftInDimensions(int x, int y) {
|
|||
|
||||
@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 ItemStack assemble(CraftingContainer inventory, RegistryAccess registryAc
|
|||
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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -11,8 +11,7 @@
|
|||
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 void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable L
|
|||
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 float getExplosionResistance(BlockState state, BlockGetter world, BlockPo
|
|||
|
||||
@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
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
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 void setDirection(Direction dir) {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -9,6 +9,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.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
|
||||
|
@ -24,11 +25,25 @@
|
|||
|
||||
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 String getCreatorModId(ItemStack stack) {
|
|||
|
||||
@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 ItemStack withFamily(ItemStack stack, ComputerFamily family) {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ITurtleUpgrade getUpgrade(ItemStack stack, TurtleSide side) {
|
||||
var tag = stack.getTag();
|
||||
if (tag == null) return null;
|
||||
|
@ -116,13 +130,11 @@ public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
|
|||
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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -9,9 +9,7 @@
|
|||
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.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 TurtleOverlayRecipe(ResourceLocation id, String group, CraftingBookCatego
|
|||
}
|
||||
|
||||
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),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
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 @@ protected ItemStack convert(IComputerItem item, ItemStack stack) {
|
|||
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<TurtleRecipe> {
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
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 boolean canCraftInDimensions(int x, int y) {
|
|||
|
||||
@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 ItemStack assemble(CraftingContainer inventory, RegistryAccess registryAc
|
|||
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 ItemStack assemble(CraftingContainer inventory, RegistryAccess registryAc
|
|||
|
||||
// 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 ItemStack assemble(CraftingContainer inventory, RegistryAccess registryAc
|
|||
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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
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 @@ private 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 = "";
|
||||
}
|
||||
|
|
|
@ -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.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.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 @@ private 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 static Options getOptions(String host, InetSocketAddress address) throws
|
|||
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.
|
||||
* <p>
|
||||
* 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<SocketChannel> 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 static Options getOptions(String host, InetSocketAddress address) throws
|
|||
* @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<SocketChannel> 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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,28 +7,31 @@
|
|||
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<Boolean> 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<Boolean> useProxy) {
|
||||
this.action = action;
|
||||
this.maxUpload = maxUpload;
|
||||
this.maxDownload = maxDownload;
|
||||
this.websocketMessage = websocketMessage;
|
||||
this.useProxy = useProxy;
|
||||
}
|
||||
|
||||
Options toOptions() {
|
||||
|
@ -38,7 +41,8 @@ Options toOptions() {
|
|||
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 @@ PartialOptions merge(PartialOptions other) {
|
|||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -124,6 +124,7 @@ private void doRequest(URI uri, HttpMethod method) {
|
|||
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 @@ private void doRequest(URI uri, HttpMethod method) {
|
|||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@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));
|
||||
|
|
|
@ -117,6 +117,7 @@ private void doConnect() {
|
|||
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 @@ private void doConnect() {
|
|||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -34,11 +34,11 @@ public class FabricConfigFile implements ConfigFile {
|
|||
|
||||
private final ConfigSpec spec;
|
||||
private final Trie<String, Entry> entries;
|
||||
private final Runnable onChange;
|
||||
private final ConfigListener onChange;
|
||||
|
||||
private @Nullable CommentedFileConfig config;
|
||||
|
||||
public FabricConfigFile(ConfigSpec spec, Trie<String, Entry> entries, Runnable onChange) {
|
||||
public FabricConfigFile(ConfigSpec spec, Trie<String, Entry> entries, ConfigListener onChange) {
|
||||
this.spec = spec;
|
||||
this.entries = entries;
|
||||
this.onChange = onChange;
|
||||
|
@ -95,7 +95,7 @@ private synchronized boolean loadConfig() {
|
|||
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 <V extends Enum<V>> Value<V> defineEnum(String path, V defaultValue) {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFile build(Runnable onChange) {
|
||||
public ConfigFile build(ConfigListener onChange) {
|
||||
return new FabricConfigFile(spec, entries, onChange);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 static void sync(ModConfigEvent.Reloading event) {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ public <V extends Enum<V>> ConfigFile.Value<V> defineEnum(String path, V default
|
|||
}
|
||||
|
||||
@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;
|
||||
|
|
Loading…
Reference in New Issue