Merge branch 'mc-1.19.x' of github.com:cc-tweaked/CC-Tweaked into mc-1.19.x
|
@ -11,6 +11,7 @@ body:
|
|||
- 1.16.x
|
||||
- 1.18.x
|
||||
- 1.19.x
|
||||
- 1.20.x
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
|
|
@ -104,6 +104,7 @@ tasks.withType(JavaCompile::class.java).configureEach {
|
|||
|
||||
tasks.processResources {
|
||||
exclude("**/*.license")
|
||||
exclude(".cache")
|
||||
}
|
||||
|
||||
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
||||
|
|
|
@ -33,7 +33,6 @@ val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
|
|||
enabled = apiToken != ""
|
||||
|
||||
val mainFile = upload("282001", modPublishing.output.get().archiveFile)
|
||||
dependsOn(modPublishing.output) // See https://github.com/Darkhax/CurseForgeGradle/pull/7.
|
||||
mainFile.changelog =
|
||||
"Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||
mainFile.changelogType = "markdown"
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import org.gradle.kotlin.dsl.`maven-publish`
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
|
|
|
@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
|||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.104.0
|
||||
modVersion=1.105.0
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.19.4
|
||||
|
|
|
@ -50,7 +50,7 @@ junit = "5.9.2"
|
|||
# Build tools
|
||||
cctJavadoc = "1.7.0"
|
||||
checkstyle = "10.3.4"
|
||||
curseForgeGradle = "1.0.11"
|
||||
curseForgeGradle = "1.0.14"
|
||||
errorProne-core = "2.18.0"
|
||||
errorProne-plugin = "3.0.1"
|
||||
fabric-loom = "1.1.10"
|
||||
|
|
|
@ -75,7 +75,9 @@ public interface ITurtleAccess {
|
|||
* @param f The subframe fraction.
|
||||
* @return A vector containing the floating point co-ordinates at which the turtle resides.
|
||||
* @see #getVisualYaw(float)
|
||||
* @deprecated Will be removed in 1.20.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
Vec3 getVisualPosition(float f);
|
||||
|
||||
/**
|
||||
|
@ -84,7 +86,9 @@ public interface ITurtleAccess {
|
|||
* @param f The subframe fraction.
|
||||
* @return The yaw the turtle is facing.
|
||||
* @see #getVisualPosition(float)
|
||||
* @deprecated Will be removed in 1.20.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
float getVisualYaw(float f);
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -104,7 +105,7 @@ public final Upgrade<R> simpleWithCustomItem(ResourceLocation id, R serialiser,
|
|||
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
|
||||
|
||||
@Override
|
||||
public final CompletableFuture<?> run(CachedOutput cache) {
|
||||
public CompletableFuture<?> run(CachedOutput cache) {
|
||||
var base = output.getOutputFolder().resolve("data");
|
||||
|
||||
Set<ResourceLocation> seen = new HashSet<>();
|
||||
|
@ -127,7 +128,7 @@ public final CompletableFuture<?> run(CachedOutput cache) {
|
|||
}
|
||||
});
|
||||
|
||||
this.upgrades = upgrades;
|
||||
this.upgrades = Collections.unmodifiableList(upgrades);
|
||||
return Util.sequenceFailFast(futures);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
* A {@link TableFormatter} subclass which writes directly to {@linkplain ChatComponent the chat GUI}.
|
||||
* <p>
|
||||
* Each message written gets a special {@link GuiMessageTag}, so we can remove the previous table of the same
|
||||
* {@link TableBuilder#getId() id}.
|
||||
* {@linkplain TableBuilder#getId() id}.
|
||||
*/
|
||||
public class ClientTableFormatter implements TableFormatter {
|
||||
public static final ClientTableFormatter INSTANCE = new ClientTableFormatter();
|
||||
|
|
|
@ -26,9 +26,11 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
|||
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_normal.png");
|
||||
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_advanced.png");
|
||||
|
||||
private static final int TEX_WIDTH = 254;
|
||||
private static final int TEX_WIDTH = 278;
|
||||
private static final int TEX_HEIGHT = 217;
|
||||
|
||||
private static final int FULL_TEX_SIZE = 512;
|
||||
|
||||
public TurtleScreen(TurtleMenu container, Inventory player, Component title) {
|
||||
super(container, player, title, BORDER);
|
||||
|
||||
|
@ -45,16 +47,16 @@ protected TerminalWidget createTerminal() {
|
|||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
||||
var advanced = family == ComputerFamily.ADVANCED;
|
||||
RenderSystem.setShaderTexture(0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL);
|
||||
blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, TEX_WIDTH, TEX_HEIGHT);
|
||||
blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
|
||||
|
||||
// Render selected slot
|
||||
var slot = getMenu().getSelectedSlot();
|
||||
if (slot >= 0) {
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
var slotX = slot % 4;
|
||||
var slotY = slot / 4;
|
||||
blit(transform,
|
||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18,
|
||||
0, 217, 24, 24
|
||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0,
|
||||
0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.narration.NarratedElementType;
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
@ -81,6 +82,11 @@ public boolean charTyped(char ch, int modifiers) {
|
|||
@Override
|
||||
public boolean keyPressed(int key, int scancode, int modifiers) {
|
||||
if (key == GLFW.GLFW_KEY_ESCAPE) return false;
|
||||
if (Screen.isPaste(key)) {
|
||||
paste();
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((modifiers & GLFW.GLFW_MOD_CONTROL) != 0) {
|
||||
switch (key) {
|
||||
case GLFW.GLFW_KEY_T -> {
|
||||
|
@ -92,32 +98,6 @@ public boolean keyPressed(int key, int scancode, int modifiers) {
|
|||
case GLFW.GLFW_KEY_R -> {
|
||||
if (rebootTimer < 0) rebootTimer = 0;
|
||||
}
|
||||
case GLFW.GLFW_KEY_V -> {
|
||||
// Ctrl+V for paste
|
||||
var clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
|
||||
if (clipboard != null) {
|
||||
// Clip to the first occurrence of \r or \n
|
||||
var newLineIndex1 = clipboard.indexOf("\r");
|
||||
var newLineIndex2 = clipboard.indexOf("\n");
|
||||
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
|
||||
} else if (newLineIndex1 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex1);
|
||||
} else if (newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex2);
|
||||
}
|
||||
|
||||
// Filter the string
|
||||
clipboard = SharedConstants.filterText(clipboard);
|
||||
if (!clipboard.isEmpty()) {
|
||||
// Clip to 512 characters and queue the event
|
||||
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
|
||||
computer.queueEvent("paste", new Object[]{ clipboard });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +111,29 @@ public boolean keyPressed(int key, int scancode, int modifiers) {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void paste() {
|
||||
var clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
|
||||
|
||||
// Clip to the first occurrence of \r or \n
|
||||
var newLineIndex1 = clipboard.indexOf('\r');
|
||||
var newLineIndex2 = clipboard.indexOf('\n');
|
||||
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
|
||||
} else if (newLineIndex1 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex1);
|
||||
} else if (newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex2);
|
||||
}
|
||||
|
||||
// Filter the string
|
||||
clipboard = SharedConstants.filterText(clipboard);
|
||||
if (!clipboard.isEmpty()) {
|
||||
// Clip to 512 characters and queue the event
|
||||
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
|
||||
computer.queueEvent("paste", new Object[]{ clipboard });
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyReleased(int key, int scancode, int modifiers) {
|
||||
// Queue the "key_up" event and remove from the down set
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.data.client;
|
||||
|
||||
import dan200.computercraft.data.DataProviders;
|
||||
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSources;
|
||||
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A version of {@link DataProviders} which relies on client-side classes.
|
||||
* <p>
|
||||
* This is called from {@link DataProviders#add(DataProviders.GeneratorSink)}.
|
||||
*/
|
||||
public final class ClientDataProviders {
|
||||
private ClientDataProviders() {
|
||||
}
|
||||
|
||||
public static void add(DataProviders.GeneratorSink generator) {
|
||||
generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> {
|
||||
out.accept(new ResourceLocation("blocks"), List.of(
|
||||
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
|
||||
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty())
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -37,6 +37,14 @@
|
|||
import static net.minecraft.data.models.model.TextureMapping.getBlockTexture;
|
||||
|
||||
class BlockModelProvider {
|
||||
private static final TextureSlot CURSOR = TextureSlot.create("cursor");
|
||||
|
||||
private static final ModelTemplate COMPUTER_ON = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")),
|
||||
Optional.empty(),
|
||||
TextureSlot.FRONT, TextureSlot.SIDE, TextureSlot.TOP, CURSOR
|
||||
);
|
||||
|
||||
private static final ModelTemplate MONITOR_BASE = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/monitor_base")),
|
||||
Optional.empty(),
|
||||
|
@ -142,11 +150,18 @@ private static void registerPrinter(BlockModelGenerators generators) {
|
|||
private static void registerComputer(BlockModelGenerators generators, ComputerBlock<?> block) {
|
||||
generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(block)
|
||||
.with(createHorizontalFacingDispatch())
|
||||
.with(createModelDispatch(ComputerBlock.STATE, state -> ModelTemplates.CUBE_ORIENTABLE.createWithSuffix(
|
||||
block, "_" + state.getSerializedName(),
|
||||
TextureMapping.orientableCube(block).put(TextureSlot.FRONT, getBlockTexture(block, "_front" + state.getTexture())),
|
||||
generators.modelOutput
|
||||
)))
|
||||
.with(createModelDispatch(ComputerBlock.STATE, state -> switch (state) {
|
||||
case OFF -> ModelTemplates.CUBE_ORIENTABLE.createWithSuffix(
|
||||
block, "_" + state.getSerializedName(),
|
||||
TextureMapping.orientableCube(block),
|
||||
generators.modelOutput
|
||||
);
|
||||
case ON, BLINKING -> COMPUTER_ON.createWithSuffix(
|
||||
block, "_" + state.getSerializedName(),
|
||||
TextureMapping.orientableCube(block).put(CURSOR, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer" + state.getTexture())),
|
||||
generators.modelOutput
|
||||
);
|
||||
}))
|
||||
);
|
||||
generators.delegateItemModel(block, getModelLocation(block, "_blinking"));
|
||||
}
|
||||
|
|
|
@ -4,16 +4,20 @@
|
|||
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
||||
import net.minecraft.data.models.BlockModelGenerators;
|
||||
import net.minecraft.data.models.ItemModelGenerators;
|
||||
import net.minecraft.data.tags.TagsProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
|
@ -37,11 +41,22 @@ public static void add(GeneratorSink generator) {
|
|||
generator.models(BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels);
|
||||
|
||||
generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades));
|
||||
|
||||
// Unfortunately we rely on some client-side classes in this code. We just load in the client side data provider
|
||||
// and invoke that.
|
||||
try {
|
||||
Class.forName("dan200.computercraft.data.client.ClientDataProviders")
|
||||
.getMethod("add", GeneratorSink.class).invoke(null, generator);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
interface GeneratorSink {
|
||||
public interface GeneratorSink {
|
||||
<T extends DataProvider> T add(DataProvider.Factory<T> factory);
|
||||
|
||||
<T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output);
|
||||
|
||||
void lootTable(List<SubProviderEntry> tables);
|
||||
|
||||
TagsProvider<Block> blockTags(Consumer<TagProvider.TagConsumer<Block>> tags);
|
||||
|
|
|
@ -230,6 +230,11 @@ private void addTranslations() {
|
|||
addConfigEntry(ConfigSpec.httpDownloadBandwidth, "Global download limit");
|
||||
addConfigEntry(ConfigSpec.httpUploadBandwidth, "Global upload limit");
|
||||
|
||||
addConfigGroup(ConfigSpec.serverSpec, "http.proxy", "Proxy");
|
||||
addConfigEntry(ConfigSpec.httpProxyHost, "Host name");
|
||||
addConfigEntry(ConfigSpec.httpProxyPort, "Port");
|
||||
addConfigEntry(ConfigSpec.httpProxyType, "Proxy type");
|
||||
|
||||
addConfigGroup(ConfigSpec.serverSpec, "peripheral", "Peripherals");
|
||||
addConfigEntry(ConfigSpec.commandBlockEnabled, "Enable command block peripheral");
|
||||
addConfigEntry(ConfigSpec.modemRange, "Modem range (default)");
|
||||
|
|
|
@ -213,8 +213,10 @@ public AbstractContainerMenu createMenu(int id, Inventory player, Player entity)
|
|||
getMetricsInstance(context.getSource()).start();
|
||||
|
||||
var stopCommand = "/computercraft track stop";
|
||||
Object[] args = new Object[]{ link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action")) };
|
||||
context.getSource().sendSuccess(Component.translatable("commands.computercraft.track.start.stop", args), false);
|
||||
context.getSource().sendSuccess(Component.translatable(
|
||||
"commands.computercraft.track.start.stop",
|
||||
link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action"))
|
||||
), false);
|
||||
return 1;
|
||||
}))
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
|
@ -16,24 +15,6 @@
|
|||
public interface BasicContainer extends Container {
|
||||
NonNullList<ItemStack> getContents();
|
||||
|
||||
@Override
|
||||
default int getMaxStackSize() {
|
||||
return 64;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void startOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void stopOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean canPlaceItem(int slot, ItemStack stack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
default int getContainerSize() {
|
||||
return getContents().size();
|
||||
|
|
|
@ -5,14 +5,11 @@
|
|||
package dan200.computercraft.shared.details;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.EnchantedBookItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
|
@ -45,7 +42,9 @@ public static void fill(Map<? super String, Object> data, ItemStack stack) {
|
|||
}
|
||||
|
||||
data.put("tags", DetailHelpers.getTags(stack.getTags()));
|
||||
data.put("itemGroups", getItemGroups(stack));
|
||||
|
||||
// Include deprecated itemGroups field
|
||||
data.put("itemGroups", List.of());
|
||||
|
||||
var tag = stack.getTag();
|
||||
if (tag != null && tag.contains("display", Tag.TAG_COMPOUND)) {
|
||||
|
@ -84,27 +83,6 @@ private static Component parseTextComponent(Tag x) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all item groups an item stack pertains to.
|
||||
*
|
||||
* @param stack Stack to analyse
|
||||
* @return A filled list that contains pairs of item group IDs and their display names.
|
||||
*/
|
||||
private static List<Map<String, Object>> getItemGroups(ItemStack stack) {
|
||||
return CreativeModeTabs.allTabs().stream()
|
||||
.filter(x -> x.shouldDisplay() && x.getType() == CreativeModeTab.Type.CATEGORY && x.contains(stack))
|
||||
.map(group -> {
|
||||
Map<String, Object> groupData = new HashMap<>(2);
|
||||
|
||||
var id = PlatformHelper.get().getCreativeTabId(group);
|
||||
if (id != null) groupData.put("id", id.toString());
|
||||
|
||||
groupData.put("displayName", group.getDisplayName().getString());
|
||||
return groupData;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all visible enchantments from given stack. Try to follow all tooltip rules : order and visibility.
|
||||
*
|
||||
|
|
|
@ -190,7 +190,7 @@ public String getType() {
|
|||
* The speaker supports [all of Minecraft's noteblock instruments](https://minecraft.fandom.com/wiki/Note_Block#Instruments).
|
||||
* These are:
|
||||
* <p>
|
||||
* {@code "harp"}, {@code "basedrum"}, {@code "snare"}, {@code "hat"}, {@code "bass"}, @code "flute"},
|
||||
* {@code "harp"}, {@code "basedrum"}, {@code "snare"}, {@code "hat"}, {@code "bass"}, {@code "flute"},
|
||||
* {@code "bell"}, {@code "guitar"}, {@code "chime"}, {@code "xylophone"}, {@code "iron_xylophone"},
|
||||
* {@code "cow_bell"}, {@code "didgeridoo"}, {@code "bit"}, {@code "banjo"} and {@code "pling"}.
|
||||
*
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
@ -273,15 +272,6 @@ static PlatformHelper get() {
|
|||
*/
|
||||
int getBurnTime(ItemStack stack);
|
||||
|
||||
/**
|
||||
* Get a unique identifier for this creative tab.
|
||||
*
|
||||
* @param tab The tab to get
|
||||
* @return The unique identifier, or {@code null} if not available.
|
||||
*/
|
||||
@Nullable
|
||||
ResourceLocation getCreativeTabId(CreativeModeTab tab);
|
||||
|
||||
/**
|
||||
* Get the "container" item to be returned after crafting. For instance, crafting with a lava bucket should return
|
||||
* an empty bucket.
|
||||
|
|
|
@ -168,7 +168,7 @@ private static boolean deployOnBlock(
|
|||
if (Math.abs(hitY - 0.5f) < 0.01f) hitY = 0.45f;
|
||||
|
||||
// Check if there's something suitable to place onto
|
||||
var hit = new BlockHitResult(new Vec3(hitX, hitY, hitZ), side, position, false);
|
||||
var hit = new BlockHitResult(new Vec3(position.getX() + hitX, position.getY() + hitY, position.getZ() + hitZ), side, position, false);
|
||||
var context = new UseOnContext(turtlePlayer.player(), InteractionHand.MAIN_HAND, hit);
|
||||
if (!canDeployOnBlock(new BlockPlaceContext(context), turtle, turtlePlayer, position, side, adjacent, outErrorMessage)) {
|
||||
return false;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package dan200.computercraft.shared.turtle.inventory;
|
||||
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
|
@ -29,12 +30,13 @@ public final class TurtleMenu extends AbstractComputerMenu {
|
|||
public static final int PLAYER_START_Y = 134;
|
||||
public static final int TURTLE_START_X = SIDEBAR_WIDTH + 175;
|
||||
public static final int PLAYER_START_X = SIDEBAR_WIDTH + BORDER;
|
||||
public static final int UPGRADE_START_X = SIDEBAR_WIDTH + 254;
|
||||
|
||||
private final ContainerData data;
|
||||
|
||||
private TurtleMenu(
|
||||
int id, Predicate<Player> canUse, ComputerFamily family, @Nullable ServerComputer computer, @Nullable ComputerContainerData menuData,
|
||||
Inventory playerInventory, Container inventory, ContainerData data
|
||||
Inventory playerInventory, Container inventory, Container turtleUpgrades, ContainerData data
|
||||
) {
|
||||
super(ModRegistry.Menus.TURTLE.get(), id, canUse, family, computer, menuData);
|
||||
this.data = data;
|
||||
|
@ -58,19 +60,24 @@ private TurtleMenu(
|
|||
for (var x = 0; x < 9; x++) {
|
||||
addSlot(new Slot(playerInventory, x, PLAYER_START_X + x * 18, PLAYER_START_Y + 3 * 18 + 5));
|
||||
}
|
||||
|
||||
// Turtle upgrades
|
||||
addSlot(new UpgradeSlot(turtleUpgrades, TurtleSide.LEFT, 0, UPGRADE_START_X, PLAYER_START_Y + 1));
|
||||
addSlot(new UpgradeSlot(turtleUpgrades, TurtleSide.RIGHT, 1, UPGRADE_START_X, PLAYER_START_Y + 1 + 18));
|
||||
}
|
||||
|
||||
public static TurtleMenu ofBrain(int id, Inventory player, TurtleBrain turtle) {
|
||||
return new TurtleMenu(
|
||||
// Laziness in turtle.getOwner() is important here!
|
||||
id, p -> turtle.getOwner().stillValid(p), turtle.getFamily(), turtle.getOwner().createServerComputer(), null,
|
||||
player, turtle.getInventory(), (SingleContainerData) turtle::getSelectedSlot
|
||||
player, turtle.getInventory(), new UpgradeContainer(turtle), (SingleContainerData) turtle::getSelectedSlot
|
||||
);
|
||||
}
|
||||
|
||||
public static TurtleMenu ofMenuData(int id, Inventory player, ComputerContainerData data) {
|
||||
return new TurtleMenu(
|
||||
id, x -> true, data.family(), null, data, player, new SimpleContainer(TurtleBlockEntity.INVENTORY_SIZE), new SimpleContainerData(1)
|
||||
id, x -> true, data.family(), null, data,
|
||||
player, new SimpleContainer(TurtleBlockEntity.INVENTORY_SIZE), new SimpleContainer(2), new SimpleContainerData(1)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.turtle.inventory;
|
||||
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A fake {@link Container} which exposes the {@linkplain ITurtleAccess#getUpgrade(TurtleSide) upgrades} a turtle has.
|
||||
*
|
||||
* @see TurtleMenu
|
||||
* @see UpgradeSlot
|
||||
*/
|
||||
class UpgradeContainer implements Container {
|
||||
private static final int SIZE = 2;
|
||||
|
||||
private final ITurtleAccess turtle;
|
||||
|
||||
private final List<ITurtleUpgrade> lastUpgrade = Arrays.asList(null, null);
|
||||
private final NonNullList<ItemStack> lastStack = NonNullList.withSize(2, ItemStack.EMPTY);
|
||||
|
||||
UpgradeContainer(ITurtleAccess turtle) {
|
||||
this.turtle = turtle;
|
||||
}
|
||||
|
||||
private TurtleSide getSide(int slot) {
|
||||
return switch (slot) {
|
||||
case 0 -> TurtleSide.LEFT;
|
||||
case 1 -> TurtleSide.RIGHT;
|
||||
default -> throw new IllegalArgumentException("Invalid slot " + slot);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int slot) {
|
||||
var upgrade = turtle.getUpgrade(getSide(slot));
|
||||
|
||||
// We don't want to return getCraftingItem directly here, as consumers may mutate the stack (they shouldn't!,
|
||||
// but if they do it's a pain to track down). To avoid recreating the stack each tick, we maintain a simple
|
||||
// cache.
|
||||
if (upgrade == lastUpgrade.get(slot)) return lastStack.get(slot);
|
||||
|
||||
var stack = upgrade == null ? ItemStack.EMPTY : upgrade.getCraftingItem().copy();
|
||||
lastUpgrade.set(slot, upgrade);
|
||||
lastStack.set(slot, stack);
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int slot, ItemStack itemStack) {
|
||||
turtle.setUpgrade(getSide(slot), TurtleUpgrades.instance().get(itemStack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
for (var i = 0; i < SIZE; i++) {
|
||||
if (!getItem(i).isEmpty()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int slot, int count) {
|
||||
return count <= 0 ? ItemStack.EMPTY : removeItemNoUpdate(slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int slot) {
|
||||
var current = getItem(slot);
|
||||
setItem(slot, ItemStack.EMPTY);
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
for (var i = 0; i < SIZE; i++) setItem(i, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.turtle.inventory;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A slot in the turtle UI which holds the turtle's current upgrade.
|
||||
*
|
||||
* @see TurtleMenu
|
||||
*/
|
||||
public class UpgradeSlot extends Slot {
|
||||
public static final ResourceLocation LEFT_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/turtle_upgrade_left");
|
||||
public static final ResourceLocation RIGHT_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/turtle_upgrade_right");
|
||||
|
||||
private final TurtleSide side;
|
||||
|
||||
public UpgradeSlot(Container container, TurtleSide side, int slot, int xPos, int yPos) {
|
||||
super(container, slot, xPos, yPos);
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack stack) {
|
||||
return TurtleUpgrades.instance().get(stack) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Pair<ResourceLocation, ResourceLocation> getNoItemIcon() {
|
||||
return Pair.of(InventoryMenu.BLOCK_ATLAS, side == TurtleSide.LEFT ? LEFT_UPGRADE : RIGHT_UPGRADE);
|
||||
}
|
||||
}
|
|
@ -115,5 +115,67 @@
|
|||
"upgrade.minecraft.diamond_pickaxe.adjective": "Добывающая",
|
||||
"upgrade.minecraft.diamond_shovel.adjective": "Копающая",
|
||||
"upgrade.minecraft.diamond_sword.adjective": "Боевая",
|
||||
"gui.computercraft.pocket_computer_overlay": "Карманный компьютер открыт. Чтобы закрыть, нажми ESC."
|
||||
"gui.computercraft.pocket_computer_overlay": "Карманный компьютер открыт. Чтобы закрыть, нажми ESC.",
|
||||
"gui.computercraft.config.command_require_creative.tooltip": "Требовать творческий режим и права оператора для взаимодействия с\nкомандными компьютерами. Это поведение по умолчанию для Командных блоков ванильной игры.",
|
||||
"gui.computercraft.config.default_computer_settings.tooltip": "Разделенный запятыми список системных настроек по умолчанию на новых компьютерах.\nНапример: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\nотключит всё автодополнение.",
|
||||
"gui.computercraft.config.execution.computer_threads.tooltip": "Устанавливает количество потоков, на которых работают компьютеры. Большее число\nозначает, что больше компьютеров сможет работать одновременно, но может привести к лагу.\nОбратите внимание, что некоторые моды могут не работать с более чем одним потоком. Используйте с осторожностью.\nОграничение: > 1",
|
||||
"gui.computercraft.config.execution.max_main_computer_time.tooltip": "Идеальный максимум времени, которое отведено компьютеру на выполнение задач, в миллисекундах.\nМы вполне возможно выйдем за этот лимит, так как невозможно предсказать сколько\nвремени будет затрачено на выполнение задач, это лишь верхний лимит среднего значения времени.\nОграничение: > 1",
|
||||
"gui.computercraft.config.execution.max_main_global_time.tooltip": "Максимум времени, которое может быть потрачено на выполнение задач за один тик, в \nмиллисекундах. \nМы вполне возможно выйдем за этот лимит, так как невозможно предсказать сколько\nвремени будет затрачено на выполнение задач, это лишь верхний лимит среднего значения времени.\nОграничение: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_download": "Глобальный лимит на скачивание",
|
||||
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Количество байтов, которое можно скачать за секунду. Все компьютеры делят эту пропускную способность. (байты в секунду)\nОграничение: > 1",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "Количество байтов, которое можно загрузить за секунду. Все компьютеры делят эту пропускную способность. (байты в секунду)\nОграничение: > 1",
|
||||
"tracking_field.computercraft.http_requests.name": "HTTP запросы",
|
||||
"tracking_field.computercraft.turtle_ops.name": "Операции Черепашек",
|
||||
"gui.computercraft.config.http.enabled.tooltip": "Включить API \"http\" на Компьютерах. Это также отключает программы \"pastebin\" и \"wget\", \nкоторые нужны многим пользователям. Рекомендуется оставить это включенным и использовать \nконфиг \"rules\" для более тонкой настройки.",
|
||||
"gui.computercraft.config.http.max_websockets.tooltip": "Количество одновременно открытых веб-сокетов, которые может иметь компьютер. Установите на 0 для неограниченных веб-сокетов.\nОграничение: > 1",
|
||||
"gui.computercraft.config.term_sizes": "Размер терминала",
|
||||
"gui.computercraft.config.term_sizes.computer.height": "Высота терминала",
|
||||
"gui.computercraft.config.term_sizes.monitor.height": "Максимальная высота монитора",
|
||||
"gui.computercraft.config.term_sizes.monitor.width.tooltip": "Ограничение: 1 ~ 32",
|
||||
"gui.computercraft.config.turtle.advanced_fuel_limit": "Лимит топлива Продвинутых Черепашек",
|
||||
"gui.computercraft.config.turtle.need_fuel.tooltip": "Устанавливает, нуждаются ли Черепашки в топливе для передвижения.",
|
||||
"gui.computercraft.terminal": "Компьютерный терминал",
|
||||
"tracking_field.computercraft.computer_tasks.name": "Задачи",
|
||||
"tracking_field.computercraft.server_tasks.name": "Серверные задачи",
|
||||
"gui.computercraft.upload.no_response": "Перенос файлов",
|
||||
"tracking_field.computercraft.avg": "%s (среднее)",
|
||||
"gui.computercraft.config.command_require_creative": "Для использования командных компьютеров нужен творческий режим",
|
||||
"gui.computercraft.config.computer_space_limit": "Лимит места на компьютерах (в байтах)",
|
||||
"gui.computercraft.config.computer_space_limit.tooltip": "Лимит места на дисках компьютеров и черепашек, в байтах.",
|
||||
"gui.computercraft.config.default_computer_settings": "Настройки Компьютера по умолчанию",
|
||||
"gui.computercraft.config.disable_lua51_features": "Отключить функции Lua 5.1",
|
||||
"gui.computercraft.config.disable_lua51_features.tooltip": "Поставьте, чтобы отключить функции из Lua 5.1, которые будут убраны в будущих\nобновлениях. Полезно для того, чтобы улучшить совместимость вперед ваших программ.",
|
||||
"gui.computercraft.config.execution": "Выполнение",
|
||||
"gui.computercraft.config.execution.computer_threads": "Потоки компьютера",
|
||||
"gui.computercraft.config.execution.max_main_global_time": "Глобальный лимит времени на тик сервера",
|
||||
"gui.computercraft.config.execution.tooltip": "Контролирует поведение выполнения задач компьютеров. Эта настройка преднезначается для \nтонкой настройки серверов, и в основном не должна быть изменена.",
|
||||
"gui.computercraft.config.floppy_space_limit": "Лимит места на дискетах (байты)",
|
||||
"gui.computercraft.config.floppy_space_limit.tooltip": "Лимит места для хранения информации на дискетах, в байтах.",
|
||||
"gui.computercraft.config.http": "HTTP",
|
||||
"gui.computercraft.config.http.bandwidth": "Пропускная способность",
|
||||
"gui.computercraft.config.http.bandwidth.global_upload": "Глобальный лимит загрузки",
|
||||
"gui.computercraft.config.http.bandwidth.tooltip": "Ограничивает пропускную способность, используемую компьютерами.",
|
||||
"gui.computercraft.config.http.enabled": "Включить HTTP API",
|
||||
"gui.computercraft.config.http.max_requests": "Максимум одновременных запросов",
|
||||
"gui.computercraft.config.http.max_requests.tooltip": "Количество http-запросов, которые компьютер может сделать одновременно. Дополнительные запросы \nбудут поставлены в очередь, и отправлены когда существующие запросы будут выполнены. Установите на 0 для \nнеограниченных запросов.\nОграничение: > 0",
|
||||
"gui.computercraft.config.http.max_websockets": "Максимум одновременных веб-сокетов",
|
||||
"gui.computercraft.config.term_sizes.computer": "Компьютер",
|
||||
"gui.computercraft.config.term_sizes.computer.height.tooltip": "Ограничение: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.computer.tooltip": "Размер терминала на компьютерах.",
|
||||
"gui.computercraft.config.term_sizes.computer.width": "Ширина терминала",
|
||||
"gui.computercraft.config.term_sizes.computer.width.tooltip": "Ограничение: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.monitor": "Монитор",
|
||||
"gui.computercraft.config.term_sizes.monitor.height.tooltip": "Ограничение: 1 ~ 32",
|
||||
"gui.computercraft.config.term_sizes.monitor.tooltip": "Максимальный размер мониторов (в блоках).",
|
||||
"gui.computercraft.config.term_sizes.monitor.width": "Максимальная ширина мониторов",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.height": "Высота терминала",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "Ограничение: 1 ~ 255",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.width": "Ширина терминала",
|
||||
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "Ограничение: 1 ~ 255",
|
||||
"gui.computercraft.config.turtle": "Черепашки",
|
||||
"gui.computercraft.config.turtle.advanced_fuel_limit.tooltip": "Лимит топлива для Продвинутых Черепашек.\nОграничение: > 0",
|
||||
"gui.computercraft.config.turtle.need_fuel": "Включить механику топлива",
|
||||
"gui.computercraft.config.turtle.normal_fuel_limit": "Лимит топлива Черепашек",
|
||||
"gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "Лимит топлива для Черепашек.\nОграничение: > 0",
|
||||
"gui.computercraft.config.turtle.tooltip": "Разные настройки, связанные с черепашками."
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"parent": "minecraft:block/block",
|
||||
"render_type": "cutout",
|
||||
"textures": {
|
||||
"particle": "#front"
|
||||
},
|
||||
"display": {
|
||||
"firstperson_righthand": {
|
||||
"rotation": [ 0, 135, 0 ],
|
||||
"translation": [ 0, 0, 0 ],
|
||||
"scale": [ 0.40, 0.40, 0.40 ]
|
||||
}
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [ 0, 0, 0 ],
|
||||
"to": [ 16, 16, 16 ],
|
||||
"faces": {
|
||||
"down": { "texture": "#top", "cullface": "down" },
|
||||
"up": { "texture": "#top", "cullface": "up" },
|
||||
"north": { "texture": "#front", "cullface": "north" },
|
||||
"south": { "texture": "#side", "cullface": "south" },
|
||||
"west": { "texture": "#side", "cullface": "west" },
|
||||
"east": { "texture": "#side", "cullface": "east" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [ 0, 0, 0 ],
|
||||
"to": [ 16, 16, 16 ],
|
||||
"faces": {
|
||||
"north": {
|
||||
"texture": "#cursor",
|
||||
"cullface": "north",
|
||||
"forge_data": {"block_light": 15, "sky_light": 15}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
Before Width: | Height: | Size: 476 B |
Before Width: | Height: | Size: 417 B |
After Width: | Height: | Size: 110 B |
Before Width: | Height: | Size: 534 B |
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"animation": {
|
||||
"frametime": 8,
|
||||
"frames": [ 0, 1 ]
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 214 B |
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"animation": {
|
||||
"frametime": 8,
|
||||
"frames": [ 0, 1 ]
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 199 B |
After Width: | Height: | Size: 89 B |
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 983 B After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 129 B |
|
@ -0,0 +1,3 @@
|
|||
SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
After Width: | Height: | Size: 129 B |
|
@ -0,0 +1,3 @@
|
|||
SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
|
@ -36,7 +36,6 @@
|
|||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
|
@ -165,13 +164,6 @@ public boolean onNotifyNeighbour(Level level, BlockPos pos, BlockState block, Di
|
|||
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getCreativeTabId(CreativeModeTab tab) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeIngredients getRecipeIngredients() {
|
||||
throw new UnsupportedOperationException("Cannot query recipes inside tests");
|
||||
|
|
|
@ -443,28 +443,6 @@ public final Object getFreeSpace(String path) throws LuaException {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for files matching a string with wildcards.
|
||||
* <p>
|
||||
* This string is formatted like a normal path string, but can include any
|
||||
* number of wildcards ({@code *}) to look for files matching anything.
|
||||
* For example, <code>rom/*/command*</code> will look for any path starting with
|
||||
* {@code command} inside any subdirectory of {@code /rom}.
|
||||
*
|
||||
* @param path The wildcard-qualified path to search for.
|
||||
* @return A list of paths that match the search string.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
* @cc.since 1.6
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String[] find(String path) throws LuaException {
|
||||
try (var ignored = environment.time(Metrics.FS_OPS)) {
|
||||
return getFileSystem().find(path);
|
||||
} catch (FileSystemException e) {
|
||||
throw new LuaException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the capacity of the drive the path is located on.
|
||||
*
|
||||
|
|
|
@ -166,47 +166,6 @@ public synchronized String[] list(String path) throws FileSystemException {
|
|||
return array;
|
||||
}
|
||||
|
||||
private void findIn(String dir, List<String> matches, Pattern wildPattern) throws FileSystemException {
|
||||
var list = list(dir);
|
||||
for (var entry : list) {
|
||||
var entryPath = dir.isEmpty() ? entry : dir + "/" + entry;
|
||||
if (wildPattern.matcher(entryPath).matches()) {
|
||||
matches.add(entryPath);
|
||||
}
|
||||
if (isDir(entryPath)) {
|
||||
findIn(entryPath, matches, wildPattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized String[] find(String wildPath) throws FileSystemException {
|
||||
// Match all the files on the system
|
||||
wildPath = sanitizePath(wildPath, true);
|
||||
|
||||
// If we don't have a wildcard at all just check the file exists
|
||||
var starIndex = wildPath.indexOf('*');
|
||||
if (starIndex == -1) {
|
||||
return exists(wildPath) ? new String[]{ wildPath } : new String[0];
|
||||
}
|
||||
|
||||
// Find the all non-wildcarded directories. For instance foo/bar/baz* -> foo/bar
|
||||
var prevDir = wildPath.substring(0, starIndex).lastIndexOf('/');
|
||||
var startDir = prevDir == -1 ? "" : wildPath.substring(0, prevDir);
|
||||
|
||||
// If this isn't a directory then just abort
|
||||
if (!isDir(startDir)) return new String[0];
|
||||
|
||||
// Scan as normal, starting from this directory
|
||||
var wildPattern = Pattern.compile("^\\Q" + wildPath.replaceAll("\\*", "\\\\E[^\\\\/]*\\\\Q") + "\\E$");
|
||||
List<String> matches = new ArrayList<>();
|
||||
findIn(startDir, matches, wildPattern);
|
||||
|
||||
// Return matches
|
||||
var array = new String[matches.size()];
|
||||
matches.toArray(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
public synchronized boolean exists(String path) throws FileSystemException {
|
||||
path = sanitizePath(path);
|
||||
var mount = getMount(path);
|
||||
|
@ -400,21 +359,20 @@ private static String sanitizePath(String path) {
|
|||
|
||||
private static final Pattern threeDotsPattern = Pattern.compile("^\\.{3,}$");
|
||||
|
||||
// IMPORTANT: Both arrays are sorted by ASCII value.
|
||||
private static final char[] specialChars = new char[]{ '"', '*', ':', '<', '>', '?', '|' };
|
||||
private static final char[] specialCharsAllowWildcards = new char[]{ '"', ':', '<', '>', '|' };
|
||||
|
||||
public static String sanitizePath(String path, boolean allowWildcards) {
|
||||
// Allow windowsy slashes
|
||||
path = path.replace('\\', '/');
|
||||
|
||||
// Clean the path or illegal characters.
|
||||
final var specialChars = new char[]{
|
||||
'"', ':', '<', '>', '?', '|', // Sorted by ascii value (important)
|
||||
};
|
||||
|
||||
var cleanName = new StringBuilder();
|
||||
var allowedChars = allowWildcards ? specialCharsAllowWildcards : specialChars;
|
||||
for (var i = 0; i < path.length(); i++) {
|
||||
var c = path.charAt(i);
|
||||
if (c >= 32 && Arrays.binarySearch(specialChars, c) < 0 && (allowWildcards || c != '*')) {
|
||||
cleanName.append(c);
|
||||
}
|
||||
if (c >= 32 && Arrays.binarySearch(allowedChars, c) < 0) cleanName.append(c);
|
||||
}
|
||||
path = cleanName.toString();
|
||||
|
||||
|
|
|
@ -133,6 +133,93 @@ function fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs)
|
|||
return {}
|
||||
end
|
||||
|
||||
local function find_aux(path, parts, i, out)
|
||||
local part = parts[i]
|
||||
if not part then
|
||||
-- If we're at the end of the pattern, ensure our path exists and append it.
|
||||
if fs.exists(path) then out[#out + 1] = path end
|
||||
elseif part.exact then
|
||||
-- If we're an exact match, just recurse into this directory.
|
||||
return find_aux(fs.combine(path, part.contents), parts, i + 1, out)
|
||||
else
|
||||
-- Otherwise we're a pattern. Check we're a directory, then recurse into each
|
||||
-- matching file.
|
||||
if not fs.isDir(path) then return end
|
||||
|
||||
local files = fs.list(path)
|
||||
for j = 1, #files do
|
||||
local file = files[j]
|
||||
if file:find(part.contents) then find_aux(fs.combine(path, file), parts, i + 1, out) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local find_escape = {
|
||||
-- Escape standard Lua pattern characters
|
||||
["^"] = "%^", ["$"] = "%$", ["("] = "%(", [")"] = "%)", ["%"] = "%%",
|
||||
["."] = "%.", ["["] = "%[", ["]"] = "%]", ["+"] = "%+", ["-"] = "%-",
|
||||
-- Aside from our wildcards.
|
||||
["*"] = ".*",
|
||||
["?"] = ".",
|
||||
}
|
||||
|
||||
--[[- Searches for files matching a string with wildcards.
|
||||
|
||||
This string looks like a normal path string, but can include wildcards, which
|
||||
can match multiple paths:
|
||||
|
||||
- "?" matches any single character in a file name.
|
||||
- "*" matches any number of characters.
|
||||
|
||||
For example, `rom/*/command*` will look for any path starting with `command`
|
||||
inside any subdirectory of `/rom`.
|
||||
|
||||
Note that these wildcards match a single segment of the path. For instance
|
||||
`rom/*.lua` will include `rom/startup.lua` but _not_ include `rom/programs/list.lua`.
|
||||
|
||||
@tparam string path The wildcard-qualified path to search for.
|
||||
@treturn { string... } A list of paths that match the search string.
|
||||
@throws If the supplied path was invalid.
|
||||
@since 1.6
|
||||
@changed 1.106.0 Added support for the `?` wildcard.
|
||||
|
||||
@usage List all Markdown files in the help folder
|
||||
|
||||
fs.find("rom/help/*.md")
|
||||
]]
|
||||
function fs.find(pattern)
|
||||
expect(1, pattern, "string")
|
||||
|
||||
pattern = fs.combine(pattern) -- Normalise the path, removing ".."s.
|
||||
|
||||
-- If the pattern is trying to search outside the computer root, just abort.
|
||||
-- This will fail later on anyway.
|
||||
if pattern == ".." or pattern:sub(1, 3) == "../" then
|
||||
error("/" .. pattern .. ": Invalid Path", 2)
|
||||
end
|
||||
|
||||
-- If we've no wildcards, just check the file exists.
|
||||
if not pattern:find("[*?]") then
|
||||
if fs.exists(pattern) then return { pattern } else return {} end
|
||||
end
|
||||
|
||||
local parts = {}
|
||||
for part in pattern:gmatch("[^/]+") do
|
||||
if part:find("[*?]") then
|
||||
parts[#parts + 1] = {
|
||||
exact = false,
|
||||
contents = "^" .. part:gsub(".", find_escape) .. "$",
|
||||
}
|
||||
else
|
||||
parts[#parts + 1] = { exact = true, contents = part }
|
||||
end
|
||||
end
|
||||
|
||||
local out = {}
|
||||
find_aux("", parts, 1, out)
|
||||
return out
|
||||
end
|
||||
|
||||
--- Returns true if a path is mounted to the parent filesystem.
|
||||
--
|
||||
-- The root filesystem "/" is considered a mount, along with disk folders and
|
||||
|
|
|
@ -1,3 +1,27 @@
|
|||
# New features in CC: Tweaked 1.105.0
|
||||
|
||||
* Optimise JSON string parsing.
|
||||
* Add `colors.fromBlit` (Erb3).
|
||||
* Upload file size limit is now configurable (khankul).
|
||||
* Wired cables no longer have a distance limit.
|
||||
* Java methods now coerce values to strings consistently with Lua.
|
||||
* Add custom timeout support to the HTTP API.
|
||||
* Support custom proxies for HTTP requests (Lemmmy).
|
||||
* The `speaker` program now errors when playing HTML files.
|
||||
* `edit` now shows an error message when editing read-only files.
|
||||
* Update Ukranian translation (SirEdvin).
|
||||
|
||||
Several bug fixes:
|
||||
* Allow GPS hosts to only be 1 block apart.
|
||||
* Fix "Turn On"/"Turn Off" buttons being inverted in the computer GUI (Erb3).
|
||||
* Fix arrow keys not working in the printout UI.
|
||||
* Several documentation fixes (zyxkad, Lupus590, Commandcracker).
|
||||
* Fix monitor renderer debug text always being visible on Forge.
|
||||
* Fix crash when another mod changes the LoggerContext.
|
||||
* Fix the `monitor_renderer` option not being present in Fabric config files.
|
||||
* Pasting on MacOS/OSX now uses Cmd+V rather than Ctrl+V.
|
||||
* Fix turtles placing blocks upside down when at y<0.
|
||||
|
||||
# New features in CC: Tweaked 1.104.0
|
||||
|
||||
* Update to Minecraft 1.19.4.
|
||||
|
|
|
@ -1,31 +1,25 @@
|
|||
New features in CC: Tweaked 1.104.0
|
||||
New features in CC: Tweaked 1.105.0
|
||||
|
||||
* Update to Minecraft 1.19.4.
|
||||
* Turtles can now right click items "into" certain blocks (cauldrons and hives by default, configurable with the `computercraft:turtle_can_use` block tag).
|
||||
* Update Cobalt to 0.7:
|
||||
* `table` methods and `ipairs` now use metamethods.
|
||||
* Type errors now use the `__name` metatag.
|
||||
* Coroutines no longer run on multiple threads.
|
||||
* Timeout errors should be thrown more reliably.
|
||||
* `speaker` program now reports an error on common unsupported audio formats.
|
||||
* `multishell` now hides the implementation details of its terminal redirect from programs.
|
||||
* Use VBO monitor renderer by default.
|
||||
* Improve syntax errors when missing commas in tables, and on trailing commas in parameter lists.
|
||||
* Turtles can now hold flags.
|
||||
* Update several translations (Alessandro, chesiren, Erlend, RomanPlayer22).
|
||||
* Optimise JSON string parsing.
|
||||
* Add `colors.fromBlit` (Erb3).
|
||||
* Upload file size limit is now configurable (khankul).
|
||||
* Wired cables no longer have a distance limit.
|
||||
* Java methods now coerce values to strings consistently with Lua.
|
||||
* Add custom timeout support to the HTTP API.
|
||||
* Support custom proxies for HTTP requests (Lemmmy).
|
||||
* The `speaker` program now errors when playing HTML files.
|
||||
* `edit` now shows an error message when editing read-only files.
|
||||
* Update Ukranian translation (SirEdvin).
|
||||
|
||||
Several bug fixes:
|
||||
* `settings.load` now ignores malformed values created by editing the `.settings` file by hand.
|
||||
* Fix introduction dates on `os.cancelAlarm` and `os.cancelTimer` (MCJack123).
|
||||
* Fix the REPL syntax reporting crashing on valid parses.
|
||||
* Make writes to the ID file atomic.
|
||||
* Obey stack limits when transferring items with Fabric's APIs.
|
||||
* Ignore metatables in `textutils.serialize`.
|
||||
* Correctly recurse into NBT lists when computing the NBT hash (Lemmmy).
|
||||
* Fix advanced pocket computers rendering as greyscale.
|
||||
* Fix stack overflow when using `shell` as a hashbang program.
|
||||
* Fix websocket messages being empty when using a non-default compression settings.
|
||||
* Fix `gps.locate` returning `nan` when receiving a duplicate location (Wojbie).
|
||||
* Remove several thread safety issues inside Java-side argument parsing code.
|
||||
* Allow GPS hosts to only be 1 block apart.
|
||||
* Fix "Turn On"/"Turn Off" buttons being inverted in the computer GUI (Erb3).
|
||||
* Fix arrow keys not working in the printout UI.
|
||||
* Several documentation fixes (zyxkad, Lupus590, Commandcracker).
|
||||
* Fix monitor renderer debug text always being visible on Forge.
|
||||
* Fix crash when another mod changes the LoggerContext.
|
||||
* Fix the `monitor_renderer` option not being present in Fabric config files.
|
||||
* Pasting on MacOS/OSX now uses Cmd+V rather than Ctrl+V.
|
||||
* Fix turtles placing blocks upside down when at y<0.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
--
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
--- Read and draw nbt ("Nitrogen Fingers Text") images.
|
||||
--- Read and draw nft ("Nitrogen Fingers Text") images.
|
||||
--
|
||||
-- nft ("Nitrogen Fingers Text") is a file format for drawing basic images.
|
||||
-- Unlike the images that @{paintutils.parseImage} uses, nft supports coloured
|
||||
|
|
|
@ -87,6 +87,53 @@ describe("The fs library", function()
|
|||
end)
|
||||
end)
|
||||
|
||||
describe("fs.find", function()
|
||||
it("fails on invalid paths", function()
|
||||
expect.error(fs.find, ".."):eq("/..: Invalid Path")
|
||||
expect.error(fs.find, "../foo/bar"):eq("/../foo/bar: Invalid Path")
|
||||
end)
|
||||
|
||||
it("returns nothing on non-existent files", function()
|
||||
expect(fs.find("no/such/file")):same {}
|
||||
expect(fs.find("no/such/*")):same {}
|
||||
expect(fs.find("no/*/file")):same {}
|
||||
end)
|
||||
|
||||
it("returns a single file", function()
|
||||
expect(fs.find("rom")):same { "rom" }
|
||||
expect(fs.find("rom/motd.txt")):same { "rom/motd.txt" }
|
||||
end)
|
||||
|
||||
it("supports the '*' wildcard", function()
|
||||
expect(fs.find("rom/*")):same {
|
||||
"rom/apis",
|
||||
"rom/autorun",
|
||||
"rom/help",
|
||||
"rom/modules",
|
||||
"rom/motd.txt",
|
||||
"rom/programs",
|
||||
"rom/startup.lua",
|
||||
}
|
||||
expect(fs.find("rom/*/command")):same {
|
||||
"rom/apis/command",
|
||||
"rom/modules/command",
|
||||
"rom/programs/command",
|
||||
}
|
||||
|
||||
expect(fs.find("rom/*/lua*")):same {
|
||||
"rom/help/lua.txt",
|
||||
"rom/programs/lua.lua",
|
||||
}
|
||||
end)
|
||||
|
||||
it("supports the '?' wildcard", function()
|
||||
expect(fs.find("rom/programs/mo??.lua")):same {
|
||||
"rom/programs/motd.lua",
|
||||
"rom/programs/move.lua",
|
||||
}
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.combine", function()
|
||||
it("removes . and ..", function()
|
||||
expect(fs.combine("./a/b")):eq("a/b")
|
||||
|
|
|
@ -119,7 +119,7 @@ loom {
|
|||
|
||||
register("data") {
|
||||
configName = "Datagen"
|
||||
server()
|
||||
client()
|
||||
|
||||
runDir("run/dataGen")
|
||||
property("cct.pretty-json")
|
||||
|
@ -165,7 +165,6 @@ tasks.processResources {
|
|||
filesMatching("fabric.mod.json") {
|
||||
expand(mapOf("version" to modVersion))
|
||||
}
|
||||
exclude(".cache")
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
|
@ -251,3 +250,7 @@ publishing {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
required.project("fabric-api")
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.client.model.EmissiveComputerModel;
|
||||
import dan200.computercraft.client.model.turtle.TurtleModelLoader;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
|
@ -35,9 +36,12 @@ public static void init() {
|
|||
ClientRegistry.registerItemColours(ColorProviderRegistry.ITEM::register);
|
||||
ClientRegistry.registerMainThread();
|
||||
|
||||
|
||||
ModelLoadingRegistry.INSTANCE.registerModelProvider((manager, out) -> ClientRegistry.registerExtraModels(out));
|
||||
ModelLoadingRegistry.INSTANCE.registerResourceProvider(loader -> (path, ctx) -> TurtleModelLoader.load(loader, path));
|
||||
ModelLoadingRegistry.INSTANCE.registerResourceProvider(loader -> (path, ctx) -> EmissiveComputerModel.load(loader, path));
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.COMPUTER_NORMAL.get(), RenderType.cutout());
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.COMPUTER_COMMAND.get(), RenderType.cutout());
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.COMPUTER_ADVANCED.get(), RenderType.cutout());
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.MONITOR_NORMAL.get(), RenderType.cutout());
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.MONITOR_ADVANCED.get(), RenderType.cutout());
|
||||
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.model;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import net.fabricmc.fabric.api.client.model.ModelProviderException;
|
||||
import net.fabricmc.fabric.api.client.model.ModelResourceProvider;
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Wraps a computer's {@link BlockModel}/{@link BakedModel} to render the computer's cursor as an emissive quad.
|
||||
* <p>
|
||||
* While Fabric has a quite advanced rendering extension API (including support for custom materials), but unlike Forge
|
||||
* it doesn't expose this in the model JSON (though externals mods like <a href="https://github.com/vram-guild/json-model-extensions/">JMX</a>
|
||||
* do handle this).
|
||||
* <p>
|
||||
* Instead, we support emissive quads by injecting a custom {@linkplain ModelResourceProvider model loader/provider}
|
||||
* which targets a hard-coded list of computer models, and wraps the returned model in a custom
|
||||
* {@linkplain FabricBakedModel} implementation which renders specific quads as emissive.
|
||||
* <p>
|
||||
* See also the <code>assets/computercraft/models/block/computer_on.json</code> model, which is the base for all
|
||||
* emissive computer models.
|
||||
*/
|
||||
public final class EmissiveComputerModel {
|
||||
private static final Set<String> MODELS = Set.of(
|
||||
"item/computer_advanced",
|
||||
"block/computer_advanced_on",
|
||||
"block/computer_advanced_blinking",
|
||||
"item/computer_command",
|
||||
"block/computer_command_on",
|
||||
"block/computer_command_blinking",
|
||||
"item/computer_normal",
|
||||
"block/computer_normal_on",
|
||||
"block/computer_normal_blinking"
|
||||
);
|
||||
|
||||
private EmissiveComputerModel() {
|
||||
}
|
||||
|
||||
public static @Nullable UnbakedModel load(ResourceManager resources, ResourceLocation path) throws ModelProviderException {
|
||||
if (!path.getNamespace().equals(ComputerCraftAPI.MOD_ID) || !MODELS.contains(path.getPath())) return null;
|
||||
|
||||
JsonObject json;
|
||||
try (var reader = resources.openAsReader(new ResourceLocation(path.getNamespace(), "models/" + path.getPath() + ".json"))) {
|
||||
json = GsonHelper.parse(reader).getAsJsonObject();
|
||||
} catch (IOException e) {
|
||||
throw new ModelProviderException("Failed loading model " + path, e);
|
||||
}
|
||||
|
||||
// Parse a subset of the model JSON
|
||||
var parent = new ResourceLocation(GsonHelper.getAsString(json, "parent"));
|
||||
|
||||
Map<String, Either<Material, String>> textures = new HashMap<>();
|
||||
if (json.has("textures")) {
|
||||
var jsonObject = GsonHelper.getAsJsonObject(json, "textures");
|
||||
|
||||
for (var entry : jsonObject.entrySet()) {
|
||||
var texture = entry.getValue().getAsString();
|
||||
textures.put(entry.getKey(), texture.startsWith("#")
|
||||
? Either.right(texture.substring(1))
|
||||
: Either.left(new Material(InventoryMenu.BLOCK_ATLAS, new ResourceLocation(texture)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Unbaked(parent, textures);
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link UnbakedModel} which wraps the returned model using {@link Baked}.
|
||||
* <p>
|
||||
* This subclasses {@link BlockModel} to allow using these models as a parent of other models.
|
||||
*/
|
||||
private static final class Unbaked extends BlockModel {
|
||||
Unbaked(ResourceLocation parent, Map<String, Either<Material, String>> materials) {
|
||||
super(parent, List.of(), materials, null, null, ItemTransforms.NO_TRANSFORMS, List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel bake(ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState state, ResourceLocation location) {
|
||||
var baked = super.bake(baker, spriteGetter, state, location);
|
||||
if (!hasTexture("cursor")) return baked;
|
||||
|
||||
var render = RendererAccess.INSTANCE.getRenderer();
|
||||
if (render == null) return baked;
|
||||
|
||||
return new Baked(
|
||||
baked,
|
||||
spriteGetter.apply(getMaterial("cursor")),
|
||||
render.materialFinder().find(),
|
||||
render.materialFinder().emissive(0, true).find()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link FabricBakedModel} which renders quads using the {@code "cursor"} texture as emissive.
|
||||
*/
|
||||
private static final class Baked extends ForwardingBakedModel {
|
||||
private final TextureAtlasSprite cursor;
|
||||
private final RenderMaterial defaultMaterial;
|
||||
private final RenderMaterial emissiveMaterial;
|
||||
|
||||
Baked(BakedModel wrapped, TextureAtlasSprite cursor, RenderMaterial defaultMaterial, RenderMaterial emissiveMaterial) {
|
||||
this.wrapped = wrapped;
|
||||
this.cursor = cursor;
|
||||
this.defaultMaterial = defaultMaterial;
|
||||
this.emissiveMaterial = emissiveMaterial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanillaAdapter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<RandomSource> randomSupplier, RenderContext context) {
|
||||
emitQuads(context, state, randomSupplier.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitItemQuads(ItemStack stack, Supplier<RandomSource> randomSupplier, RenderContext context) {
|
||||
emitQuads(context, null, randomSupplier.get());
|
||||
}
|
||||
|
||||
private void emitQuads(RenderContext context, @Nullable BlockState state, RandomSource random) {
|
||||
var emitter = context.getEmitter();
|
||||
for (var faceIdx = 0; faceIdx <= ModelHelper.NULL_FACE_ID; faceIdx++) {
|
||||
var cullFace = ModelHelper.faceFromIndex(faceIdx);
|
||||
var quads = wrapped.getQuads(state, cullFace, random);
|
||||
|
||||
var count = quads.size();
|
||||
for (var i = 0; i < count; i++) {
|
||||
final var q = quads.get(i);
|
||||
emitter.fromVanilla(q, q.getSprite() == cursor ? emissiveMaterial : defaultMaterial, cullFace);
|
||||
emitter.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,6 +104,14 @@
|
|||
"gui.computercraft.config.http.max_requests.tooltip": "The number of http requests a computer can make at one time. Additional requests\nwill be queued, and sent when the running requests have finished. Set to 0 for\nunlimited.\nRange: > 0",
|
||||
"gui.computercraft.config.http.max_websockets": "Maximum concurrent websockets",
|
||||
"gui.computercraft.config.http.max_websockets.tooltip": "The number of websockets a computer can have open at one time. Set to 0 for unlimited.\nRange: > 1",
|
||||
"gui.computercraft.config.http.proxy": "Proxy",
|
||||
"gui.computercraft.config.http.proxy.host": "Host name",
|
||||
"gui.computercraft.config.http.proxy.host.tooltip": "The hostname or IP address of the proxy server.",
|
||||
"gui.computercraft.config.http.proxy.port": "Port",
|
||||
"gui.computercraft.config.http.proxy.port.tooltip": "The port of the proxy server.\nRange: 1 ~ 65536",
|
||||
"gui.computercraft.config.http.proxy.tooltip": "Tunnels HTTP and websocket requests through a proxy server. Only affects HTTP\nrules with \"use_proxy\" set to true (off by default).\nIf authentication is required for the proxy, create a \"computercraft-proxy.pw\"\nfile in the same directory as \"computercraft-server.toml\", containing the\nusername and password separated by a colon, e.g. \"myuser:mypassword\". For\nSOCKS4 proxies only the username is required.",
|
||||
"gui.computercraft.config.http.proxy.type": "Proxy type",
|
||||
"gui.computercraft.config.http.proxy.type.tooltip": "The type of proxy to use.\nAllowed Values: HTTP, HTTPS, SOCKS4, SOCKS5",
|
||||
"gui.computercraft.config.http.rules": "Allow/deny rules",
|
||||
"gui.computercraft.config.http.rules.tooltip": "A list of rules which control behaviour of the \"http\" API for specific domains or\nIPs. Each rule is an item with a 'host' to match against, and a series of\nproperties. Rules are evaluated in order, meaning earlier rules override later\nones.\nThe host may be a domain name (\"pastebin.com\"), wildcard (\"*.pastebin.com\") or\nCIDR notation (\"127.0.0.0/8\").\nIf no rules, the domain is blocked.",
|
||||
"gui.computercraft.config.http.tooltip": "Controls the HTTP API",
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_advanced_front_blink",
|
||||
"cursor": "computercraft:block/computer_blink",
|
||||
"front": "computercraft:block/computer_advanced_front",
|
||||
"side": "computercraft:block/computer_advanced_side",
|
||||
"top": "computercraft:block/computer_advanced_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_advanced_front_on",
|
||||
"cursor": "computercraft:block/computer_on",
|
||||
"front": "computercraft:block/computer_advanced_front",
|
||||
"side": "computercraft:block/computer_advanced_side",
|
||||
"top": "computercraft:block/computer_advanced_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_command_front_blink",
|
||||
"cursor": "computercraft:block/computer_blink",
|
||||
"front": "computercraft:block/computer_command_front",
|
||||
"side": "computercraft:block/computer_command_side",
|
||||
"top": "computercraft:block/computer_command_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_command_front_on",
|
||||
"cursor": "computercraft:block/computer_on",
|
||||
"front": "computercraft:block/computer_command_front",
|
||||
"side": "computercraft:block/computer_command_side",
|
||||
"top": "computercraft:block/computer_command_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_normal_front_blink",
|
||||
"cursor": "computercraft:block/computer_blink",
|
||||
"front": "computercraft:block/computer_normal_front",
|
||||
"side": "computercraft:block/computer_normal_side",
|
||||
"top": "computercraft:block/computer_normal_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_normal_front_on",
|
||||
"cursor": "computercraft:block/computer_on",
|
||||
"front": "computercraft:block/computer_normal_front",
|
||||
"side": "computercraft:block/computer_normal_side",
|
||||
"top": "computercraft:block/computer_normal_top"
|
||||
}
|
||||
|
|
6
projects/fabric/src/generated/resources/assets/minecraft/atlases/blocks.json
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"sources": [
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"},
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"}
|
||||
]
|
||||
}
|
|
@ -4,21 +4,25 @@
|
|||
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.loot.LootTableProvider;
|
||||
import net.minecraft.data.models.BlockModelGenerators;
|
||||
import net.minecraft.data.models.ItemModelGenerators;
|
||||
import net.minecraft.data.tags.TagsProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
@ -43,6 +47,27 @@ public <T extends DataProvider> T add(DataProvider.Factory<T> factory) {
|
|||
return generator.addProvider(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) {
|
||||
generator.addProvider((FabricDataOutput out) -> {
|
||||
var ourType = switch (type) {
|
||||
case SERVER_DATA -> PackOutput.Target.DATA_PACK;
|
||||
case CLIENT_RESOURCES -> PackOutput.Target.RESOURCE_PACK;
|
||||
};
|
||||
return new FabricCodecDataProvider<T>(out, ourType, directory, codec) {
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(BiConsumer<ResourceLocation, T> provider) {
|
||||
output.accept(provider);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lootTable(List<LootTableProvider.SubProviderEntry> tables) {
|
||||
for (var table : tables) {
|
||||
|
|
|
@ -256,12 +256,6 @@ public int getBurnTime(ItemStack stack) {
|
|||
return fuel == null ? 0 : fuel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getCreativeTabId(CreativeModeTab tab) {
|
||||
return tab.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getCraftingRemainingItem(ItemStack stack) {
|
||||
return stack.getRecipeRemainder();
|
||||
|
|
|
@ -104,6 +104,14 @@
|
|||
"gui.computercraft.config.http.max_requests.tooltip": "The number of http requests a computer can make at one time. Additional requests\nwill be queued, and sent when the running requests have finished. Set to 0 for\nunlimited.\nRange: > 0",
|
||||
"gui.computercraft.config.http.max_websockets": "Maximum concurrent websockets",
|
||||
"gui.computercraft.config.http.max_websockets.tooltip": "The number of websockets a computer can have open at one time. Set to 0 for unlimited.\nRange: > 1",
|
||||
"gui.computercraft.config.http.proxy": "Proxy",
|
||||
"gui.computercraft.config.http.proxy.host": "Host name",
|
||||
"gui.computercraft.config.http.proxy.host.tooltip": "The hostname or IP address of the proxy server.",
|
||||
"gui.computercraft.config.http.proxy.port": "Port",
|
||||
"gui.computercraft.config.http.proxy.port.tooltip": "The port of the proxy server.\nRange: 1 ~ 65536",
|
||||
"gui.computercraft.config.http.proxy.tooltip": "Tunnels HTTP and websocket requests through a proxy server. Only affects HTTP\nrules with \"use_proxy\" set to true (off by default).\nIf authentication is required for the proxy, create a \"computercraft-proxy.pw\"\nfile in the same directory as \"computercraft-server.toml\", containing the\nusername and password separated by a colon, e.g. \"myuser:mypassword\". For\nSOCKS4 proxies only the username is required.",
|
||||
"gui.computercraft.config.http.proxy.type": "Proxy type",
|
||||
"gui.computercraft.config.http.proxy.type.tooltip": "The type of proxy to use.\nAllowed Values: HTTP, HTTPS, SOCKS4, SOCKS5",
|
||||
"gui.computercraft.config.http.rules": "Allow/deny rules",
|
||||
"gui.computercraft.config.http.rules.tooltip": "A list of rules which control behaviour of the \"http\" API for specific domains or\nIPs. Each rule is an item with a 'host' to match against, and a series of\nproperties. Rules are evaluated in order, meaning earlier rules override later\nones.\nThe host may be a domain name (\"pastebin.com\"), wildcard (\"*.pastebin.com\") or\nCIDR notation (\"127.0.0.0/8\").\nIf no rules, the domain is blocked.",
|
||||
"gui.computercraft.config.http.tooltip": "Controls the HTTP API",
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_advanced_front_blink",
|
||||
"cursor": "computercraft:block/computer_blink",
|
||||
"front": "computercraft:block/computer_advanced_front",
|
||||
"side": "computercraft:block/computer_advanced_side",
|
||||
"top": "computercraft:block/computer_advanced_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_advanced_front_on",
|
||||
"cursor": "computercraft:block/computer_on",
|
||||
"front": "computercraft:block/computer_advanced_front",
|
||||
"side": "computercraft:block/computer_advanced_side",
|
||||
"top": "computercraft:block/computer_advanced_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_command_front_blink",
|
||||
"cursor": "computercraft:block/computer_blink",
|
||||
"front": "computercraft:block/computer_command_front",
|
||||
"side": "computercraft:block/computer_command_side",
|
||||
"top": "computercraft:block/computer_command_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_command_front_on",
|
||||
"cursor": "computercraft:block/computer_on",
|
||||
"front": "computercraft:block/computer_command_front",
|
||||
"side": "computercraft:block/computer_command_side",
|
||||
"top": "computercraft:block/computer_command_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_normal_front_blink",
|
||||
"cursor": "computercraft:block/computer_blink",
|
||||
"front": "computercraft:block/computer_normal_front",
|
||||
"side": "computercraft:block/computer_normal_side",
|
||||
"top": "computercraft:block/computer_normal_top"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"parent": "minecraft:block/orientable",
|
||||
"parent": "computercraft:block/computer_on",
|
||||
"textures": {
|
||||
"front": "computercraft:block/computer_normal_front_on",
|
||||
"cursor": "computercraft:block/computer_on",
|
||||
"front": "computercraft:block/computer_normal_front",
|
||||
"side": "computercraft:block/computer_normal_side",
|
||||
"top": "computercraft:block/computer_normal_top"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"sources": [
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"},
|
||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"}
|
||||
]
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
|
@ -14,18 +16,24 @@
|
|||
import net.minecraft.data.models.ItemModelGenerators;
|
||||
import net.minecraft.data.tags.ItemTagsProvider;
|
||||
import net.minecraft.data.tags.TagsProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraftforge.common.data.BlockTagsProvider;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
import net.minecraftforge.common.data.JsonCodecProvider;
|
||||
import net.minecraftforge.data.event.GatherDataEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
|
@ -46,6 +54,15 @@ public <T extends DataProvider> T add(DataProvider.Factory<T> factory) {
|
|||
return generator.addProvider(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) {
|
||||
generator.addProvider(out -> {
|
||||
Map<ResourceLocation, T> map = new HashMap<>();
|
||||
output.accept(map::put);
|
||||
return new JsonCodecProvider<>(out, existingFiles, ComputerCraftAPI.MOD_ID, JsonOps.INSTANCE, type, directory, codec, map);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lootTable(List<LootTableProvider.SubProviderEntry> tables) {
|
||||
add(out -> new LootTableProvider(out, Set.of(), tables));
|
||||
|
|
|
@ -96,13 +96,18 @@ public static int size(IItemHandler inventory) {
|
|||
* <p>
|
||||
* The returned information contains the same information as each item in
|
||||
* {@link #list}, as well as additional details like the display name
|
||||
* (`displayName`), item groups (`itemGroups`), which are the creative tabs
|
||||
* an item will appear under, and item and item durability (`damage`,
|
||||
* `maxDamage`, `durability`).
|
||||
* (`displayName`), and item and item durability (`damage`, `maxDamage`, `durability`).
|
||||
* <p>
|
||||
* Some items include more information (such as enchantments) - it is
|
||||
* recommended to print it out using @{textutils.serialize} or in the Lua
|
||||
* REPL, to explore what is available.
|
||||
* <p>
|
||||
* :::info Deprecated fields
|
||||
* Older versions of CC: Tweaked exposed an {@code itemGroups} field, listing the
|
||||
* creative tabs an item was available under. This information is no longer available on
|
||||
* more recent versions of the game, and so this field will always be empty. Do not use this
|
||||
* field in new code!
|
||||
* :::
|
||||
*
|
||||
* @param inventory The current inventory.
|
||||
* @param slot The slot to get information about.
|
||||
|
@ -119,10 +124,6 @@ public static int size(IItemHandler inventory) {
|
|||
* print(("%s (%s)"):format(item.displayName, item.name))
|
||||
* print(("Count: %d/%d"):format(item.count, item.maxCount))
|
||||
*
|
||||
* for _, group in pairs(item.itemGroups) do
|
||||
* print(("Group: %s"):format(group.displayName))
|
||||
* end
|
||||
*
|
||||
* if item.damage then
|
||||
* print(("Damage: %d/%d"):format(item.damage, item.maxDamage))
|
||||
* end
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
|
@ -52,7 +51,6 @@
|
|||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.common.CreativeModeTabRegistry;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.Tags;
|
||||
import net.minecraftforge.common.ToolActions;
|
||||
|
@ -258,12 +256,6 @@ public int getBurnTime(ItemStack stack) {
|
|||
return ForgeHooks.getBurnTime(stack, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getCreativeTabId(CreativeModeTab tab) {
|
||||
return CreativeModeTabRegistry.getName(tab);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getCraftingRemainingItem(ItemStack stack) {
|
||||
return stack.getCraftingRemainingItem();
|
||||
|
|