1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-04 15:43:00 +00:00

Update to 1.20.4

This commit is contained in:
Jonathan Coates
2024-01-31 20:55:14 +00:00
parent f26e443e81
commit fc834cd97f
217 changed files with 2303 additions and 3027 deletions

View File

@@ -7,13 +7,13 @@ package dan200.computercraft.client;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.sound.SpeakerSound;
import net.minecraft.commands.CommandSourceStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.*;
import net.minecraftforge.client.event.sound.PlayStreamingSourceEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.client.event.*;
import net.neoforged.neoforge.client.event.sound.PlayStreamingSourceEvent;
import net.neoforged.neoforge.event.TickEvent;
import net.neoforged.neoforge.event.level.LevelEvent;
/**
* Forge-specific dispatch for {@link ClientHooks}.

View File

@@ -7,16 +7,15 @@ package dan200.computercraft.client;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent;
import dan200.computercraft.client.model.turtle.TurtleModelLoader;
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ModelEvent;
import net.minecraftforge.client.event.RegisterClientReloadListenersEvent;
import net.minecraftforge.client.event.RegisterColorHandlersEvent;
import net.minecraftforge.client.event.RegisterShadersEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModLoader;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.neoforge.client.event.*;
import java.io.IOException;
@@ -33,7 +32,7 @@ public final class ForgeClientRegistry {
@SubscribeEvent
public static void registerModelLoaders(ModelEvent.RegisterGeometryLoaders event) {
event.register("turtle", TurtleModelLoader.INSTANCE);
event.register(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle"), TurtleModelLoader.INSTANCE);
}
/**
@@ -49,7 +48,7 @@ public final class ForgeClientRegistry {
if (gatheredModellers) return;
gatheredModellers = true;
ModLoader.get().postEvent(new RegisterTurtleModellersEvent());
ModLoader.get().postEvent(new RegisterTurtleModellersEvent(TurtleUpgradeModellers::register));
}
}
@@ -74,6 +73,11 @@ public final class ForgeClientRegistry {
ClientRegistry.registerItemColours(event::register);
}
@SubscribeEvent
public static void registerMenuScreens(RegisterMenuScreensEvent event) {
ClientRegistry.registerMenuScreens(event::register);
}
@SubscribeEvent
public static void registerReloadListeners(RegisterClientReloadListenersEvent event) {
ClientRegistry.registerReloadListeners(event::registerReloadListener, Minecraft.getInstance());

View File

@@ -10,7 +10,7 @@ import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.api.v0.IrisTextVertexSink;
import net.minecraftforge.fml.ModList;
import net.neoforged.fml.ModList;
import java.nio.ByteBuffer;
import java.util.Optional;

View File

@@ -12,8 +12,8 @@ import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.BakedModelWrapper;
import net.minecraftforge.client.model.data.ModelData;
import net.neoforged.neoforge.client.model.BakedModelWrapper;
import net.neoforged.neoforge.client.model.data.ModelData;
import org.jetbrains.annotations.Nullable;
import java.util.List;

View File

@@ -12,8 +12,8 @@ import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.BakedModelWrapper;
import net.minecraftforge.client.model.data.ModelData;
import net.neoforged.neoforge.client.model.BakedModelWrapper;
import net.neoforged.neoforge.client.model.data.ModelData;
import javax.annotation.Nullable;
import java.util.List;

View File

@@ -9,7 +9,7 @@ import dan200.computercraft.client.model.TransformedBakedModel;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.model.BakedModelWrapper;
import net.neoforged.neoforge.client.model.BakedModelWrapper;
import java.util.List;
import java.util.function.Function;

View File

@@ -15,9 +15,9 @@ import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraftforge.client.model.geometry.IGeometryBakingContext;
import net.minecraftforge.client.model.geometry.IGeometryLoader;
import net.minecraftforge.client.model.geometry.IUnbakedGeometry;
import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext;
import net.neoforged.neoforge.client.model.geometry.IGeometryLoader;
import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry;
import java.util.function.Function;

View File

@@ -10,7 +10,7 @@ import dan200.computercraft.client.model.FoiledModel;
import dan200.computercraft.client.render.ModelRenderer;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.server.ServerNetworkContext;
import dan200.computercraft.shared.platform.NetworkHandler;
import dan200.computercraft.shared.platform.ForgeMessageType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.resources.model.BakedModel;
@@ -18,12 +18,13 @@ import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ServerGamePacketListener;
import net.minecraft.network.protocol.common.ServerCommonPacketListener;
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.model.data.ModelData;
import net.neoforged.neoforge.client.model.data.ModelData;
import javax.annotation.Nullable;
import java.util.Arrays;
@@ -44,8 +45,8 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper {
}
@Override
public Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> message) {
return NetworkHandler.createServerboundPacket(message);
public Packet<ServerCommonPacketListener> createPacket(NetworkMessage<ServerNetworkContext> message) {
return new ServerboundCustomPayloadPacket(ForgeMessageType.createPayload(message));
}
@Override

View File

@@ -15,7 +15,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.ModelData;
import net.neoforged.neoforge.client.model.data.ModelData;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;

View File

@@ -6,7 +6,7 @@ package dan200.computercraft.mixin.client;
import dan200.computercraft.shared.command.CommandComputerCraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraftforge.client.ClientCommandHandler;
import net.neoforged.neoforge.client.ClientCommandHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;

View File

@@ -8,60 +8,73 @@ import com.electronwill.nightconfig.core.file.FileConfig;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.ForgeComputerCraftAPI;
import dan200.computercraft.api.detail.ForgeDetailRegistries;
import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.api.network.wired.WiredElementCapability;
import dan200.computercraft.api.peripheral.PeripheralCapability;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.impl.Services;
import dan200.computercraft.shared.CommonHooks;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.details.FluidData;
import dan200.computercraft.shared.integration.MoreRedIntegration;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral;
import dan200.computercraft.shared.peripheral.generic.methods.EnergyMethods;
import dan200.computercraft.shared.peripheral.generic.methods.FluidMethods;
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity;
import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEntity;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity;
import dan200.computercraft.shared.platform.ForgeConfigFile;
import dan200.computercraft.shared.platform.NetworkHandler;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.config.ModConfigEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.registries.NewRegistryEvent;
import net.minecraftforge.registries.RegistryBuilder;
import dan200.computercraft.shared.platform.ForgeMessageType;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModLoadingContext;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.config.ModConfig;
import net.neoforged.fml.event.config.ModConfigEvent;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlerEvent;
import net.neoforged.neoforge.registries.NewRegistryEvent;
import net.neoforged.neoforge.registries.RegistryBuilder;
import javax.annotation.Nullable;
@Mod(ComputerCraftAPI.MOD_ID)
@Mod.EventBusSubscriber(modid = ComputerCraftAPI.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
public final class ComputerCraft {
public ComputerCraft() {
ModRegistry.register();
private static @Nullable IEventBus eventBus;
public ComputerCraft(IEventBus eventBus) {
withEventBus(eventBus, ModRegistry::register);
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ((ForgeConfigFile) ConfigSpec.serverSpec).spec());
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ((ForgeConfigFile) ConfigSpec.clientSpec).spec());
}
NetworkHandler.setup();
private static void withEventBus(IEventBus eventBus, Runnable task) {
ComputerCraft.eventBus = eventBus;
task.run();
ComputerCraft.eventBus = null;
}
public static IEventBus getEventBus() {
var bus = eventBus;
if (bus == null) throw new NullPointerException("Bus is not available.");
return bus;
}
@SubscribeEvent
public static void registerRegistries(NewRegistryEvent event) {
event.create(new RegistryBuilder<TurtleUpgradeSerialiser<?>>()
.setName(TurtleUpgradeSerialiser.registryId().location())
.disableSaving().disableSync());
event.create(new RegistryBuilder<PocketUpgradeSerialiser<?>>()
.setName(PocketUpgradeSerialiser.registryId().location())
.disableSaving().disableSync());
}
@SubscribeEvent
public static void registerCapabilities(RegisterCapabilitiesEvent event) {
event.register(WiredElement.class);
event.register(IPeripheral.class);
event.create(new RegistryBuilder<>(ITurtleUpgrade.serialiserRegistryKey()));
event.create(new RegistryBuilder<>(IPocketUpgrade.serialiserRegistryKey()));
}
@SubscribeEvent
@@ -72,13 +85,61 @@ public final class ComputerCraft {
ComputerCraftAPI.registerGenericSource(new FluidMethods());
ComputerCraftAPI.registerGenericSource(new EnergyMethods());
ForgeComputerCraftAPI.registerGenericCapability(ForgeCapabilities.ITEM_HANDLER);
ForgeComputerCraftAPI.registerGenericCapability(ForgeCapabilities.ENERGY);
ForgeComputerCraftAPI.registerGenericCapability(ForgeCapabilities.FLUID_HANDLER);
ForgeComputerCraftAPI.registerGenericCapability(Capabilities.ItemHandler.BLOCK);
ForgeComputerCraftAPI.registerGenericCapability(Capabilities.FluidHandler.BLOCK);
ForgeComputerCraftAPI.registerGenericCapability(Capabilities.EnergyStorage.BLOCK);
ForgeDetailRegistries.FLUID_STACK.addProvider(FluidData::fill);
}
if (ModList.get().isLoaded(MoreRedIntegration.MOD_ID)) MoreRedIntegration.setup();
@SubscribeEvent
public static void registerNetwork(RegisterPayloadHandlerEvent event) {
var registrar = event.registrar(ComputerCraftAPI.MOD_ID).versioned(ComputerCraftAPI.getInstalledVersion());
for (var type : NetworkMessages.getServerbound()) {
var forgeType = ForgeMessageType.cast(type);
registrar.play(forgeType.id(), forgeType.reader(), builder -> builder.server(
(t, context) -> context.workHandler().execute(() -> t.payload().handle(() -> (ServerPlayer) context.player().orElseThrow()))
));
}
for (var type : NetworkMessages.getClientbound()) {
var forgeType = ForgeMessageType.cast(type);
registrar.play(forgeType.id(), forgeType.reader(), builder -> builder.client(
(t, context) -> context.workHandler().execute(() -> t.payload().handle(ClientHolderHolder.get()))
));
}
}
/**
* Attach capabilities to our block entities.
*
* @param event The event to register capabilities with.
*/
@SubscribeEvent
public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) {
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.COMPUTER_NORMAL.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.COMPUTER_ADVANCED.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.TURTLE_NORMAL.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.SPEAKER.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.PRINTER.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.DISK_DRIVE.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.MONITOR_NORMAL.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), (b, d) -> b.peripheral());
event.registerBlockEntity(
PeripheralCapability.get(), BlockEntityType.COMMAND_BLOCK,
(b, d) -> Config.enableCommandBlock ? new CommandBlockPeripheral(b) : null
);
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.WIRELESS_MODEM_NORMAL.get(), WirelessModemBlockEntity::getPeripheral);
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.WIRELESS_MODEM_ADVANCED.get(), WirelessModemBlockEntity::getPeripheral);
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get(), WiredModemFullBlockEntity::getPeripheral);
event.registerBlockEntity(PeripheralCapability.get(), ModRegistry.BlockEntities.CABLE.get(), CableBlockEntity::getPeripheral);
event.registerBlockEntity(WiredElementCapability.get(), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get(), (b, d) -> b.getElement());
event.registerBlockEntity(WiredElementCapability.get(), ModRegistry.BlockEntities.CABLE.get(), CableBlockEntity::getWiredElement);
}
@SubscribeEvent
@@ -107,4 +168,24 @@ public final class ComputerCraft {
public static void onCreativeTab(BuildCreativeModeTabContentsEvent event) {
CommonHooks.onBuildCreativeTab(event.getTabKey(), event.getParameters(), event);
}
/**
* This holds an instance of {@link ClientNetworkContext}. This is a separate class to ensure that the instance is
* lazily created when needed on the client.
*/
private static final class ClientHolderHolder {
private static final @Nullable ClientNetworkContext INSTANCE;
private static final @Nullable Throwable ERROR;
static {
var helper = Services.tryLoad(ClientNetworkContext.class);
INSTANCE = helper.instance();
ERROR = helper.error();
}
static ClientNetworkContext get() {
var instance = INSTANCE;
return instance == null ? Services.raise(ClientNetworkContext.class, ERROR) : instance;
}
}
}

View File

@@ -5,12 +5,12 @@
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;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.DataGenerator;
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;
@@ -21,16 +21,14 @@ 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 net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.common.data.BlockTagsProvider;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.common.data.JsonCodecProvider;
import net.neoforged.neoforge.data.event.GatherDataEvent;
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;
@@ -57,9 +55,16 @@ public class Generators {
@Override
public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) {
add(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);
var target = switch (type) {
case SERVER_DATA -> PackOutput.Target.DATA_PACK;
case CLIENT_RESOURCES -> PackOutput.Target.RESOURCE_PACK;
};
return new JsonCodecProvider<T>(out, target, directory, type, codec, registries, ComputerCraftAPI.MOD_ID, existingFiles) {
@Override
protected void gather() {
output.accept(this::unconditional);
}
};
});
}
@@ -73,7 +78,7 @@ public class Generators {
return add(out -> new BlockTagsProvider(out, registries, ComputerCraftAPI.MOD_ID, existingFiles) {
@Override
protected void addTags(HolderLookup.Provider registries) {
tags.accept(x -> new TagProvider.TagAppender<>(RegistryWrappers.BLOCKS, getOrCreateRawBuilder(x)));
tags.accept(x -> new TagProvider.TagAppender<>(BuiltInRegistries.BLOCK, getOrCreateRawBuilder(x)));
}
});
}
@@ -87,7 +92,7 @@ public class Generators {
tags.accept(new TagProvider.ItemTagConsumer() {
@Override
public TagProvider.TagAppender<Item> tag(TagKey<Item> tag) {
return new TagProvider.TagAppender<>(RegistryWrappers.ITEMS, getOrCreateRawBuilder(tag));
return new TagProvider.TagAppender<>(BuiltInRegistries.ITEM, getOrCreateRawBuilder(tag));
}
@Override

View File

@@ -7,21 +7,20 @@ package dan200.computercraft.impl;
import com.google.auto.service.AutoService;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.detail.DetailRegistry;
import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.impl.detail.DetailRegistryImpl;
import dan200.computercraft.shared.details.FluidData;
import dan200.computercraft.shared.peripheral.generic.ComponentLookup;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fml.ModList;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.fml.ModList;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.fluids.FluidStack;
import javax.annotation.Nullable;
import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT;
import java.util.Objects;
@AutoService(ComputerCraftAPIService.class)
public final class ComputerCraftAPIImpl extends AbstractComputerCraftAPI implements ComputerCraftAPIForgeService {
@@ -38,23 +37,30 @@ public final class ComputerCraftAPIImpl extends AbstractComputerCraftAPI impleme
}
@Override
public void registerPeripheralProvider(IPeripheralProvider provider) {
Peripherals.register(provider);
}
@Override
public void registerGenericCapability(Capability<?> capability) {
Peripherals.registerGenericCapability(capability);
}
@Override
public LazyOptional<WiredElement> getWiredElementAt(BlockGetter world, BlockPos pos, Direction side) {
var tile = world.getBlockEntity(pos);
return tile == null ? LazyOptional.empty() : tile.getCapability(CAPABILITY_WIRED_ELEMENT, side);
public void registerGenericCapability(BlockCapability<?, Direction> capability) {
Objects.requireNonNull(capability, "Capability cannot be null");
Peripherals.addGenericLookup(new CapabilityLookup<>(capability));
}
@Override
public DetailRegistry<FluidStack> getFluidStackDetailRegistry() {
return fluidStackDetails;
}
/**
* A {@link ComponentLookup} for {@linkplain BlockCapability capabilities}.
* <p>
* This is a record to ensure that adding the same capability multiple times only results in one lookup being
* present in the resulting list.
*
* @param capability The capability to lookup
* @param <T> The type of the capability we look up.
*/
private record CapabilityLookup<T>(BlockCapability<T, Direction> capability) implements ComponentLookup {
@Nullable
@Override
public T find(ServerLevel level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side) {
return level.getCapability(capability, pos, state, blockEntity, side);
}
}
}

View File

@@ -1,96 +0,0 @@
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
//
// SPDX-License-Identifier: LicenseRef-CCPL
package dan200.computercraft.impl;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.shared.peripheral.generic.ComponentLookup;
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
import dan200.computercraft.shared.platform.InvalidateCallback;
import dan200.computercraft.shared.util.CapabilityUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Objects;
import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
/**
* The registry for peripheral providers.
* <p>
* This lives in the {@code impl} package despite it not being part of the public API, in order to mirror Forge's class.
*/
public final class Peripherals {
private static final Logger LOG = LoggerFactory.getLogger(Peripherals.class);
private static final Collection<IPeripheralProvider> providers = new LinkedHashSet<>();
private static final GenericPeripheralProvider<InvalidateCallback> genericProvider = new GenericPeripheralProvider<>();
private Peripherals() {
}
public static synchronized void register(IPeripheralProvider provider) {
Objects.requireNonNull(provider, "provider cannot be null");
providers.add(provider);
}
public static void registerGenericLookup(ComponentLookup<InvalidateCallback> lookup) {
genericProvider.registerLookup(lookup);
}
/**
* A {@link ComponentLookup} for {@linkplain Capability capabilities}.
* <p>
* This is a record to ensure that adding the same capability multiple times only results in one lookup being
* present in the resulting list.
*
* @param capability The capability to lookup
* @param <T> The type of the capability we look up.
*/
private record CapabilityLookup<T>(Capability<T> capability) implements ComponentLookup<InvalidateCallback> {
@Nullable
@Override
public T find(ServerLevel level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side, InvalidateCallback invalidate) {
return CapabilityUtil.unwrap(CapabilityUtil.getCapability(blockEntity, this.capability(), side), invalidate);
}
}
public static void registerGenericCapability(Capability<?> capability) {
Objects.requireNonNull(capability, "Capability cannot be null");
registerGenericLookup(new CapabilityLookup<>(capability));
}
@Nullable
public static IPeripheral getPeripheral(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate) {
if (!world.isInWorldBounds(pos)) return null;
var block = world.getBlockEntity(pos);
if (block != null) {
var peripheral = block.getCapability(CAPABILITY_PERIPHERAL, side);
if (peripheral.isPresent()) return CapabilityUtil.unwrap(peripheral, invalidate);
}
// Try the handlers in order:
for (var peripheralProvider : providers) {
try {
var peripheral = peripheralProvider.getPeripheral(world, pos, side);
if (peripheral.isPresent()) return CapabilityUtil.unwrap(peripheral, invalidate);
} catch (Exception e) {
LOG.error("Peripheral provider " + peripheralProvider + " errored.", e);
}
}
return genericProvider.getPeripheral(world, pos, side, block, invalidate);
}
}

View File

@@ -1,22 +0,0 @@
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared;
import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.capabilities.CapabilityToken;
public final class Capabilities {
public static final Capability<IPeripheral> CAPABILITY_PERIPHERAL = CapabilityManager.get(new CapabilityToken<>() {
});
public static final Capability<WiredElement> CAPABILITY_WIRED_ELEMENT = CapabilityManager.get(new CapabilityToken<>() {
});
private Capabilities() {
}
}

View File

@@ -6,38 +6,18 @@ package dan200.computercraft.shared;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.client.UpgradesLoadedMessage;
import dan200.computercraft.shared.network.server.ServerNetworking;
import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral;
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity;
import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEntity;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import dan200.computercraft.shared.util.CapabilityProvider;
import dan200.computercraft.shared.util.SidedCapabilityProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.CommandBlockEntity;
import net.minecraftforge.event.*;
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import net.minecraftforge.event.level.ChunkWatchEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.items.wrapper.InvWrapper;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
import static net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER;
import net.neoforged.bus.api.EventPriority;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.event.*;
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
import net.neoforged.neoforge.event.entity.living.LivingDropsEvent;
import net.neoforged.neoforge.event.level.ChunkWatchEvent;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.neoforge.event.server.ServerStartingEvent;
import net.neoforged.neoforge.event.server.ServerStoppedEvent;
/**
* Forge-specific dispatch for {@link CommonHooks}.
@@ -57,6 +37,11 @@ public class ForgeCommonHooks {
CommonHooks.onServerStarting(event.getServer());
}
@SubscribeEvent
public static void onServerStarted(ServerStartedEvent event) {
CommonHooks.onServerStarted(event.getServer());
}
@SubscribeEvent
public static void onServerStopped(ServerStoppedEvent event) {
CommonHooks.onServerStopped();
@@ -68,7 +53,7 @@ public class ForgeCommonHooks {
}
@SubscribeEvent
public static void onChunkWatch(ChunkWatchEvent.Watch event) {
public static void onChunkWatch(ChunkWatchEvent.Sent event) {
CommonHooks.onChunkWatch(event.getChunk(), event.getPlayer());
}
@@ -87,57 +72,6 @@ public class ForgeCommonHooks {
}
}
private static final ResourceLocation PERIPHERAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "peripheral");
private static final ResourceLocation WIRED_ELEMENT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_node");
private static final ResourceLocation INVENTORY = new ResourceLocation(ComputerCraftAPI.MOD_ID, "inventory");
/**
* Attach capabilities to our block entities.
*
* @param event The {@link AttachCapabilitiesEvent} event.
*/
@SubscribeEvent
public static void onCapability(AttachCapabilitiesEvent<BlockEntity> event) {
var blockEntity = event.getObject();
if (blockEntity instanceof ComputerBlockEntity computer) {
CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, computer::peripheral);
} else if (blockEntity instanceof TurtleBlockEntity turtle) {
CapabilityProvider.attach(event, INVENTORY, ITEM_HANDLER, () -> new InvWrapper(turtle));
var peripheral = CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, turtle::peripheral);
turtle.onMoved(peripheral::invalidate);
} else if (blockEntity instanceof DiskDriveBlockEntity diskDrive) {
CapabilityProvider.attach(event, INVENTORY, ITEM_HANDLER, () -> new InvWrapper(diskDrive));
CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, diskDrive::peripheral);
} else if (blockEntity instanceof CableBlockEntity cable) {
var peripheralHandler = SidedCapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, cable::getPeripheral);
var elementHandler = SidedCapabilityProvider.attach(event, WIRED_ELEMENT, Capabilities.CAPABILITY_WIRED_ELEMENT, cable::getWiredElement);
cable.onModemChanged(() -> {
peripheralHandler.invalidate();
elementHandler.invalidate();
});
} else if (blockEntity instanceof WiredModemFullBlockEntity modem) {
SidedCapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, modem::getPeripheral);
CapabilityProvider.attach(event, WIRED_ELEMENT, Capabilities.CAPABILITY_WIRED_ELEMENT, modem::getElement);
} else if (blockEntity instanceof WirelessModemBlockEntity modem) {
var peripheral = SidedCapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, modem::getPeripheral);
modem.onModemChanged(peripheral::invalidate);
} else if (blockEntity instanceof MonitorBlockEntity monitor) {
CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, monitor::peripheral);
} else if (blockEntity instanceof SpeakerBlockEntity speaker) {
CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, speaker::peripheral);
} else if (blockEntity instanceof PrinterBlockEntity printer) {
CapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, printer::peripheral);
// We don't need to invalidate here as the block's can't be rotated on the X axis!
SidedCapabilityProvider.attach(
event, INVENTORY, ITEM_HANDLER,
s -> s == null ? new InvWrapper(printer) : new SidedInvWrapper(printer, s)
);
} else if (Config.enableCommandBlock && blockEntity instanceof CommandBlockEntity commandBlock) {
CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, () -> new CommandBlockPeripheral(commandBlock));
}
}
@SubscribeEvent
public static void lootLoad(LootTableLoadEvent event) {
var pool = CommonHooks.getExtraLootPool(event.getName());

View File

@@ -4,14 +4,14 @@
package dan200.computercraft.shared.details;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraftforge.fluids.FluidStack;
import net.minecraft.core.registries.BuiltInRegistries;
import net.neoforged.neoforge.fluids.FluidStack;
import java.util.Map;
public class FluidData {
public static void fillBasic(Map<? super String, Object> data, FluidStack stack) {
data.put("name", DetailHelpers.getId(RegistryWrappers.FLUIDS, stack.getFluid()));
data.put("name", DetailHelpers.getId(BuiltInRegistries.FLUID, stack.getFluid()));
data.put("amount", stack.getAmount());
}

View File

@@ -8,12 +8,12 @@ import com.google.auto.service.AutoService;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.command.UserLevel;
import net.minecraft.commands.CommandSourceStack;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.server.permission.PermissionAPI;
import net.minecraftforge.server.permission.events.PermissionGatherEvent;
import net.minecraftforge.server.permission.nodes.PermissionNode;
import net.minecraftforge.server.permission.nodes.PermissionType;
import net.minecraftforge.server.permission.nodes.PermissionTypes;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.server.permission.PermissionAPI;
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
import net.neoforged.neoforge.server.permission.nodes.PermissionType;
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
import java.util.ArrayList;
import java.util.List;
@@ -52,7 +52,7 @@ public final class ForgePermissionRegistry extends PermissionRegistry {
@Override
public void register() {
super.register();
MinecraftForge.EVENT_BUS.addListener((PermissionGatherEvent.Nodes event) -> event.addNodes(nodes));
NeoForge.EVENT_BUS.addListener((PermissionGatherEvent.Nodes event) -> event.addNodes(nodes));
}
@AutoService(PermissionRegistry.Provider.class)

View File

@@ -1,66 +0,0 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.integration;
import commoble.morered.api.MoreRedAPI;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
import dan200.computercraft.shared.util.SidedCapabilityProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
public class MoreRedIntegration {
public static final String MOD_ID = "morered";
private static final ResourceLocation ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, MOD_ID);
@SubscribeEvent
public static void attachBlockCapabilities(AttachCapabilitiesEvent<BlockEntity> event) {
var blockEntity = event.getObject();
if (blockEntity.getBlockState().getBlock() instanceof IBundledRedstoneBlock bundledBlock) {
// The API is a little unclear on whether this needs to be sided. The API design mirrors Block.getSignal
// (suggesting we can use wireFace.getOpposite(), which is what we did on older versions), but on the other
// hand that parameter is not guaranteed to be non-null (suggesting we should use the cap side instead).
SidedCapabilityProvider.attach(event, ID, MoreRedAPI.CHANNELED_POWER_CAPABILITY, side -> (world, wirePos, wireState, wireFace, channel) -> {
if (side == null) return 0; // It's not clear if there's a sensible implementation here.
var level = bundledBlock.getBundledRedstoneOutput(world, blockEntity.getBlockPos(), side);
return (level & (1 << channel)) != 0 ? 31 : 0;
});
}
}
public static void setup() {
MinecraftForge.EVENT_BUS.register(MoreRedIntegration.class);
ComputerCraftAPI.registerBundledRedstoneProvider(MoreRedIntegration::getBundledPower);
}
private static int getBundledPower(Level world, BlockPos pos, Direction side) {
var blockEntity = world.getBlockEntity(pos);
if (blockEntity == null) return -1;
var blockState = blockEntity.getBlockState();
// Skip ones already handled by CC. We can do this more efficiently.
if (blockState.getBlock() instanceof IBundledRedstoneBlock) return -1;
var powerCap = blockEntity.getCapability(MoreRedAPI.CHANNELED_POWER_CAPABILITY, side);
if (!powerCap.isPresent()) return -1;
var power = powerCap.orElseThrow(NullPointerException::new);
var mask = 0;
for (var i = 0; i < 16; i++) {
mask |= power.getPowerOnChannel(world, pos, blockState, side, i) > 0 ? (1 << i) : 0;
}
return mask;
}
}

View File

@@ -5,7 +5,7 @@
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.api.lua.LuaFunction;
import net.minecraftforge.energy.IEnergyStorage;
import net.neoforged.neoforge.energy.IEnergyStorage;
/**
* Fluid methods for Forge's {@link IEnergyStorage}.

View File

@@ -8,12 +8,12 @@ import dan200.computercraft.api.detail.ForgeDetailRegistries;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
import javax.annotation.Nullable;
import java.util.HashMap;
@@ -46,7 +46,7 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
String toName, Optional<Integer> limit, Optional<String> fluidName
) throws LuaException {
var fluid = fluidName.isPresent()
? getRegistryEntry(fluidName.get(), "fluid", RegistryWrappers.FLUIDS)
? getRegistryEntry(fluidName.get(), "fluid", BuiltInRegistries.FLUID)
: null;
// Find location to transfer to
@@ -71,7 +71,7 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
String fromName, Optional<Integer> limit, Optional<String> fluidName
) throws LuaException {
var fluid = fluidName.isPresent()
? getRegistryEntry(fluidName.get(), "fluid", RegistryWrappers.FLUIDS)
? getRegistryEntry(fluidName.get(), "fluid", BuiltInRegistries.FLUID)
: null;
// Find location to transfer to
@@ -91,11 +91,14 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
@Nullable
private static IFluidHandler extractHandler(@Nullable Object object) {
if (object instanceof BlockEntity blockEntity && blockEntity.isRemoved()) return null;
if (object instanceof BlockEntity blockEntity) {
if (blockEntity.isRemoved()) return null;
if (object instanceof ICapabilityProvider provider) {
var cap = provider.getCapability(ForgeCapabilities.FLUID_HANDLER);
if (cap.isPresent()) return cap.orElseThrow(NullPointerException::new);
var level = blockEntity.getLevel();
if (!(level instanceof ServerLevel serverLevel)) return null;
var result = serverLevel.getCapability(Capabilities.FluidHandler.BLOCK, blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, null);
if (result != null) return result;
}
if (object instanceof IFluidHandler handler) return handler;

View File

@@ -9,12 +9,12 @@ import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.shared.platform.ForgeContainerTransfer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.Container;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.wrapper.InvWrapper;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.wrapper.InvWrapper;
import javax.annotation.Nullable;
import java.util.HashMap;
@@ -109,11 +109,14 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
@Nullable
private static IItemHandler extractHandler(@Nullable Object object) {
if (object instanceof BlockEntity blockEntity && blockEntity.isRemoved()) return null;
if (object instanceof BlockEntity blockEntity) {
if (blockEntity.isRemoved()) return null;
if (object instanceof ICapabilityProvider provider) {
var cap = provider.getCapability(ForgeCapabilities.ITEM_HANDLER);
if (cap.isPresent()) return cap.orElseThrow(NullPointerException::new);
var level = blockEntity.getLevel();
if (!(level instanceof ServerLevel serverLevel)) return null;
var result = serverLevel.getCapability(Capabilities.ItemHandler.BLOCK, blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, null);
if (result != null) return result;
}
if (object instanceof IItemHandler handler) return handler;

View File

@@ -12,7 +12,7 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.common.util.FakePlayer;
import net.neoforged.neoforge.common.util.FakePlayer;
import javax.annotation.Nullable;
import java.util.OptionalInt;

View File

@@ -6,7 +6,7 @@ package dan200.computercraft.shared.platform;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.util.Trie;
import net.minecraftforge.common.ForgeConfigSpec;
import net.neoforged.neoforge.common.ModConfigSpec;
import javax.annotation.Nullable;
import java.util.ArrayList;
@@ -18,15 +18,15 @@ import java.util.stream.Stream;
* A {@link ConfigFile} which wraps Forge's config implementation.
*/
public final class ForgeConfigFile implements ConfigFile {
private final ForgeConfigSpec spec;
private final ModConfigSpec spec;
private final Trie<String, ConfigFile.Entry> entries;
public ForgeConfigFile(ForgeConfigSpec spec, Trie<String, Entry> entries) {
public ForgeConfigFile(ModConfigSpec spec, Trie<String, Entry> entries) {
this.spec = spec;
this.entries = entries;
}
public ForgeConfigSpec spec() {
public ModConfigSpec spec() {
return spec;
}
@@ -42,10 +42,10 @@ public final class ForgeConfigFile implements ConfigFile {
}
/**
* Wraps {@link ForgeConfigSpec.Builder} into our own config builder abstraction.
* Wraps {@link ModConfigSpec.Builder} into our own config builder abstraction.
*/
static class Builder extends ConfigFile.Builder {
private final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
private final ModConfigSpec.Builder builder = new ModConfigSpec.Builder();
private final Trie<String, ConfigFile.Entry> entries = new Trie<>();
private void translation(String name) {
@@ -80,7 +80,7 @@ public final class ForgeConfigFile implements ConfigFile {
return this;
}
private <T> ConfigFile.Value<T> defineValue(ForgeConfigSpec.ConfigValue<T> value) {
private <T> ConfigFile.Value<T> defineValue(ModConfigSpec.ConfigValue<T> value) {
var wrapped = new ValueImpl<>(value);
entries.setValue(value.getPath(), wrapped);
return wrapped;
@@ -129,7 +129,7 @@ public final class ForgeConfigFile implements ConfigFile {
private static final class GroupImpl implements ConfigFile.Group {
private final List<String> path;
private @Nullable ForgeConfigSpec owner;
private @Nullable ModConfigSpec owner;
private GroupImpl(List<String> path) {
this.path = path;
@@ -149,14 +149,14 @@ public final class ForgeConfigFile implements ConfigFile {
}
private static final class ValueImpl<T> implements ConfigFile.Value<T> {
private final ForgeConfigSpec.ConfigValue<T> value;
private @Nullable ForgeConfigSpec owner;
private final ModConfigSpec.ConfigValue<T> value;
private @Nullable ModConfigSpec owner;
private ValueImpl(ForgeConfigSpec.ConfigValue<T> value) {
private ValueImpl(ModConfigSpec.ConfigValue<T> value) {
this.value = value;
}
private ForgeConfigSpec.ValueSpec spec() {
private ModConfigSpec.ValueSpec spec() {
if (owner == null) throw new IllegalStateException("Config has not been built yet");
return owner.getSpec().get(value.getPath());
}

View File

@@ -5,7 +5,7 @@
package dan200.computercraft.shared.platform;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.neoforged.neoforge.items.IItemHandler;
public class ForgeContainerTransfer implements ContainerTransfer.Slotted {
private final IItemHandler handler;

View File

@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.platform;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
/**
* A {@link MessageType} implementation for Forge.
* <p>
* This wraps {@link NetworkMessage}s into a {@link CustomPacketPayload}, allowing us to easily use Minecraft's existing
* custom packets.
*
* @param id The id of this message.
* @param reader Read this message from a network buffer.
* @param <T> The type of our {@link NetworkMessage}.
*/
public record ForgeMessageType<T extends NetworkMessage<?>>(
ResourceLocation id, FriendlyByteBuf.Reader<Payload<T>> reader
) implements MessageType<T> {
public static <T extends NetworkMessage<?>> ForgeMessageType<T> cast(MessageType<T> type) {
return (ForgeMessageType<T>) type;
}
public static CustomPacketPayload createPayload(NetworkMessage<?> message) {
return new Payload<>(message);
}
public record Payload<T extends NetworkMessage<?>>(T payload) implements CustomPacketPayload {
@Override
public void write(FriendlyByteBuf buf) {
payload().write(buf);
}
@Override
public ResourceLocation id() {
return payload().type().id();
}
}
}

View File

@@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.platform;
import dan200.computercraft.shared.peripheral.generic.ComponentLookup;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullConsumer;
/**
* A function which may be called when a capability (or some other object) has been invalidated.
* <p>
* This extends {@link NonNullConsumer} for use with {@link LazyOptional#addListener(NonNullConsumer)}, and
* {@link Runnable} for use with {@link ComponentLookup}.
*/
public interface InvalidateCallback extends Runnable, NonNullConsumer<Object> {
@Override
default void accept(Object o) {
run();
}
/**
* Cast this callback to a {@link NonNullConsumer} of an arbitrary type.
*
* @param <T> The type of the consumer, normally a {@link LazyOptional}.
* @return {@code this}, but with a compatible type.
*/
@SuppressWarnings("unchecked")
default <T> NonNullConsumer<T> castConsumer() {
return (NonNullConsumer<T>) this;
}
}

View File

@@ -1,119 +0,0 @@
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.platform;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.impl.Services;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.server.ServerNetworkContext;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ServerGamePacketListener;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.function.Function;
import static dan200.computercraft.core.util.Nullability.assertNonNull;
public final class NetworkHandler {
private static final Logger LOG = LoggerFactory.getLogger(NetworkHandler.class);
private static final SimpleChannel network;
static {
var version = ComputerCraftAPI.getInstalledVersion();
network = NetworkRegistry.ChannelBuilder.named(new ResourceLocation(ComputerCraftAPI.MOD_ID, "network"))
.networkProtocolVersion(() -> version)
.clientAcceptedVersions(version::equals).serverAcceptedVersions(version::equals)
.simpleChannel();
}
private NetworkHandler() {
}
public static void setup() {
for (var type : NetworkMessages.getServerbound()) {
var forgeType = (MessageTypeImpl<? extends NetworkMessage<ServerNetworkContext>>) type;
registerMainThread(forgeType, NetworkDirection.PLAY_TO_SERVER, c -> () -> assertNonNull(c.getSender()));
}
for (var type : NetworkMessages.getClientbound()) {
var forgeType = (MessageTypeImpl<? extends NetworkMessage<ClientNetworkContext>>) type;
registerMainThread(forgeType, NetworkDirection.PLAY_TO_CLIENT, x -> ClientHolder.get());
}
}
@SuppressWarnings("unchecked")
public static Packet<ClientGamePacketListener> createClientboundPacket(NetworkMessage<ClientNetworkContext> packet) {
return (Packet<ClientGamePacketListener>) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_CLIENT);
}
@SuppressWarnings("unchecked")
public static Packet<ServerGamePacketListener> createServerboundPacket(NetworkMessage<ServerNetworkContext> packet) {
return (Packet<ServerGamePacketListener>) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_SERVER);
}
/**
* Register packet, and a thread-unsafe handler for it.
*
* @param <T> The type of the packet to send.
* @param <H> The context this packet is evaluated under.
* @param type The message type to register.
* @param direction A network direction which will be asserted before any processing of this message occurs
* @param handler Gets or constructs the handler for this packet.
*/
static <H, T extends NetworkMessage<H>> void registerMainThread(
MessageTypeImpl<T> type, NetworkDirection direction, Function<NetworkEvent.Context, H> handler
) {
network.messageBuilder(type.klass(), type.id(), direction)
.encoder(NetworkMessage::write)
.decoder(type.reader())
.consumerMainThread((packet, contextSup) -> {
try {
packet.handle(handler.apply(contextSup.get()));
} catch (RuntimeException | Error e) {
LOG.error("Failed handling packet", e);
throw e;
}
})
.add();
}
public record MessageTypeImpl<T extends NetworkMessage<?>>(
int id, Class<T> klass, Function<FriendlyByteBuf, T> reader
) implements MessageType<T> {
}
/**
* This holds an instance of {@link ClientNetworkContext}. This is a separate class to ensure that the instance is
* lazily created when needed on the client.
*/
private static final class ClientHolder {
private static final @Nullable ClientNetworkContext INSTANCE;
private static final @Nullable Throwable ERROR;
static {
var helper = Services.tryLoad(ClientNetworkContext.class);
INSTANCE = helper.instance();
ERROR = helper.error();
}
static ClientNetworkContext get() {
var instance = INSTANCE;
return instance == null ? Services.raise(ClientNetworkContext.class, ERROR) : instance;
}
}
}

View File

@@ -9,34 +9,39 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.serialization.JsonOps;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.network.wired.WiredElementCapability;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralCapability;
import dan200.computercraft.impl.Peripherals;
import dan200.computercraft.shared.Capabilities;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData;
import dan200.computercraft.shared.util.CapabilityUtil;
import dan200.computercraft.shared.util.InventoryUtil;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.common.ClientCommonPacketListener;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.TagKey;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.*;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -55,29 +60,28 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.Tags;
import net.minecraftforge.common.ToolActions;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.common.crafting.conditions.ICondition;
import net.minecraftforge.common.crafting.conditions.ModLoadedCondition;
import net.minecraftforge.common.extensions.IForgeMenuType;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.items.wrapper.InvWrapper;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import net.minecraftforge.network.NetworkHooks;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.RegistryManager;
import net.minecraftforge.registries.RegistryObject;
import net.neoforged.bus.api.Event;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.Tags;
import net.neoforged.neoforge.common.ToolActions;
import net.neoforged.neoforge.common.conditions.ConditionalOps;
import net.neoforged.neoforge.common.conditions.ICondition;
import net.neoforged.neoforge.common.conditions.ModLoadedCondition;
import net.neoforged.neoforge.common.extensions.IMenuTypeExtension;
import net.neoforged.neoforge.event.EventHooks;
import net.neoforged.neoforge.items.wrapper.InvWrapper;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import javax.annotation.Nullable;
import java.util.*;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.*;
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
@@ -92,50 +96,27 @@ public class PlatformHelperImpl implements PlatformHelper {
return new ForgeConfigFile.Builder();
}
@Override
public <T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object) {
var key = RegistryManager.ACTIVE.getRegistry(registry).getKey(object);
if (key == null) throw new IllegalArgumentException(object + " was not registered in " + registry);
return key;
}
@Override
public <T> T getRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id) {
var value = RegistryManager.ACTIVE.getRegistry(registry).getValue(id);
if (value == null) throw new IllegalArgumentException(id + " was not registered in " + registry);
return value;
}
@Override
public <T> RegistryWrappers.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> key) {
return new RegistryWrapperImpl<>(key.location(), RegistryManager.ACTIVE.getRegistry(key));
}
@Override
public <T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T>> registry) {
return new RegistrationHelperImpl<>(DeferredRegister.create(registry, ComputerCraftAPI.MOD_ID));
}
@Nullable
@Override
public <K> K tryGetRegistryObject(ResourceKey<Registry<K>> registry, ResourceLocation id) {
return RegistryManager.ACTIVE.getRegistry(registry).getValue(id);
}
@Override
public boolean shouldLoadResource(JsonObject object) {
return ICondition.shouldRegisterEntry(object);
return ICondition.conditionsMatched(JsonOps.INSTANCE, object);
}
@Override
public void addRequiredModCondition(JsonObject object, String modId) {
var conditions = GsonHelper.getAsJsonArray(object, "forge:conditions", null);
// FIXME: Test this, though maybe this should be implemented a different way anyway?
var conditions = GsonHelper.getAsJsonArray(object, ConditionalOps.DEFAULT_CONDITIONS_KEY, null);
if (conditions == null) {
conditions = new JsonArray();
object.add("forge:conditions", conditions);
object.add(ConditionalOps.DEFAULT_CONDITIONS_KEY, conditions);
}
conditions.add(CraftingHelper.serialize(new ModLoadedCondition(modId)));
conditions.add(ICondition.CODEC.encodeStart(JsonOps.INSTANCE, new ModLoadedCondition(modId)).getOrThrow(false, x -> {
}));
}
@Override
@@ -150,22 +131,27 @@ public class PlatformHelperImpl implements PlatformHelper {
@Override
public <C extends AbstractContainerMenu, T extends ContainerData> MenuType<C> createMenuType(Function<FriendlyByteBuf, T> reader, ContainerData.Factory<C, T> factory) {
return IForgeMenuType.create((id, player, data) -> factory.create(id, player, reader.apply(data)));
return IMenuTypeExtension.create((id, player, data) -> factory.create(id, player, reader.apply(data)));
}
@Override
public void openMenu(Player player, MenuProvider owner, ContainerData menu) {
NetworkHooks.openScreen((ServerPlayer) player, owner, menu::toBytes);
((ServerPlayer) player).openMenu(owner, menu::toBytes);
}
@Override
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
return new NetworkHandler.MessageTypeImpl<>(id, klass, reader);
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(ResourceLocation id, FriendlyByteBuf.Reader<T> reader) {
return new ForgeMessageType<>(id, b -> new ForgeMessageType.Payload<>(reader.apply(b)));
}
@Override
public Packet<ClientGamePacketListener> createPacket(NetworkMessage<ClientNetworkContext> message) {
return NetworkHandler.createClientboundPacket(message);
public Packet<ClientCommonPacketListener> createPacket(NetworkMessage<ClientNetworkContext> message) {
return new ClientboundCustomPayloadPacket(ForgeMessageType.createPayload(message));
}
@Override
public void invalidateComponent(BlockEntity owner) {
owner.invalidateCapabilities();
}
@Override
@@ -175,15 +161,13 @@ public class PlatformHelperImpl implements PlatformHelper {
@Override
public ComponentAccess<WiredElement> createWiredElementAccess(BlockEntity owner, Consumer<Direction> invalidate) {
return new CapabilityAccess<>(owner, Capabilities.CAPABILITY_WIRED_ELEMENT, invalidate);
return new ComponentAccessImpl<>(owner, WiredElementCapability.get(), invalidate);
}
@Override
public boolean hasWiredElementIn(Level level, BlockPos pos, Direction direction) {
if (!level.isLoaded(pos)) return false;
var blockEntity = level.getBlockEntity(pos.relative(direction));
return blockEntity != null && blockEntity.getCapability(Capabilities.CAPABILITY_WIRED_ELEMENT, direction.getOpposite()).isPresent();
return level.getCapability(WiredElementCapability.get(), pos.relative(direction), direction.getOpposite()) != null;
}
@Override
@@ -194,30 +178,13 @@ public class PlatformHelperImpl implements PlatformHelper {
@Nullable
@Override
public ContainerTransfer getContainer(ServerLevel level, BlockPos pos, Direction side) {
var block = level.getBlockState(pos);
if (block.getBlock() instanceof WorldlyContainerHolder holder) {
var container = holder.getContainer(block, level, pos);
return new ForgeContainerTransfer(new SidedInvWrapper(container, side));
}
var blockEntity = level.getBlockEntity(pos);
if (blockEntity != null) {
var inventory = blockEntity.getCapability(ForgeCapabilities.ITEM_HANDLER, side);
if (inventory.isPresent()) {
return new ForgeContainerTransfer(inventory.orElseThrow(NullPointerException::new));
}
}
var inventory = level.getCapability(Capabilities.ItemHandler.BLOCK, pos, side);
if (inventory != null) return new ForgeContainerTransfer(inventory);
var entity = InventoryUtil.getEntityContainer(level, pos, side);
return entity == null ? null : new ForgeContainerTransfer(new InvWrapper(entity));
}
@Nullable
@Override
public CompoundTag getShareTag(ItemStack item) {
return item.getShareTag();
}
@Override
public RecipeIngredients getRecipeIngredients() {
return new RecipeIngredients(
@@ -260,7 +227,7 @@ public class PlatformHelperImpl implements PlatformHelper {
@Override
public int getBurnTime(ItemStack stack) {
return ForgeHooks.getBurnTime(stack, null);
return CommonHooks.getBurnTime(stack, null);
}
@Override
@@ -275,20 +242,20 @@ public class PlatformHelperImpl implements PlatformHelper {
@Override
public List<ItemStack> getRecipeRemainingItems(ServerPlayer player, Recipe<CraftingContainer> recipe, CraftingContainer container) {
ForgeHooks.setCraftingPlayer(player);
CommonHooks.setCraftingPlayer(player);
var result = recipe.getRemainingItems(container);
ForgeHooks.setCraftingPlayer(null);
CommonHooks.setCraftingPlayer(null);
return result;
}
@Override
public void onItemCrafted(ServerPlayer player, CraftingContainer container, ItemStack stack) {
ForgeEventFactory.firePlayerCraftingEvent(player, stack, container);
EventHooks.firePlayerCraftingEvent(player, stack, container);
}
@Override
public boolean onNotifyNeighbour(Level level, BlockPos pos, BlockState block, Direction direction) {
return !ForgeEventFactory.onNeighborNotify(level, pos, block, EnumSet.of(direction), false).isCanceled();
return !EventHooks.onNeighborNotify(level, pos, block, EnumSet.of(direction), false).isCanceled();
}
@Override
@@ -308,14 +275,14 @@ public class PlatformHelperImpl implements PlatformHelper {
@Override
public InteractionResult canAttackEntity(ServerPlayer player, Entity entity) {
return ForgeHooks.onPlayerAttackTarget(player, entity) ? InteractionResult.PASS : InteractionResult.SUCCESS;
return CommonHooks.onPlayerAttackTarget(player, entity) ? InteractionResult.PASS : InteractionResult.SUCCESS;
}
@Override
public boolean interactWithEntity(ServerPlayer player, Entity entity, Vec3 hitPos) {
// Our behaviour is slightly different here - we call onInteractEntityAt before the interact methods, while
// Forge does the call afterwards (on the server, not on the client).
var interactAt = ForgeHooks.onInteractEntityAt(player, entity, hitPos, InteractionHand.MAIN_HAND);
var interactAt = CommonHooks.onInteractEntityAt(player, entity, hitPos, InteractionHand.MAIN_HAND);
if (interactAt == null) {
interactAt = entity.interactAt(player, hitPos.subtract(entity.position()), InteractionHand.MAIN_HAND);
}
@@ -327,7 +294,7 @@ public class PlatformHelperImpl implements PlatformHelper {
public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock) {
var level = player.level();
var pos = hit.getBlockPos();
var event = ForgeHooks.onRightClickBlock(player, InteractionHand.MAIN_HAND, pos, hit);
var event = CommonHooks.onRightClickBlock(player, InteractionHand.MAIN_HAND, pos, hit);
if (event.isCanceled()) return event.getCancellationResult();
var context = new UseOnContext(player, InteractionHand.MAIN_HAND, hit);
@@ -345,63 +312,19 @@ public class PlatformHelperImpl implements PlatformHelper {
return event.getUseItem() == Event.Result.DENY ? InteractionResult.PASS : stack.useOn(context);
}
private record RegistryWrapperImpl<T>(
ResourceLocation name, ForgeRegistry<T> registry
) implements RegistryWrappers.RegistryWrapper<T> {
private record RegistrationHelperImpl<R>(DeferredRegister<R> registry) implements RegistrationHelper<R> {
@Override
public int getId(T object) {
return registry.getID(object);
}
@Override
public ResourceLocation getKey(T object) {
var key = registry.getKey(object);
if (key == null) throw new IllegalStateException(object + " was not registered in " + name);
return key;
}
@Override
public T get(ResourceLocation location) {
var object = registry.getValue(location);
if (object == null) throw new IllegalStateException(location + " was not registered in " + name);
return object;
}
@Nullable
@Override
public T tryGet(ResourceLocation location) {
return registry.getValue(location);
}
@Override
public @Nullable T byId(int id) {
return registry.getValue(id);
}
@Override
public int size() {
return registry.getKeys().size();
}
@Override
public Iterator<T> iterator() {
return registry.iterator();
}
}
private record RegistrationHelperImpl<T>(DeferredRegister<T> registry) implements RegistrationHelper<T> {
@Override
public <U extends T> RegistryEntry<U> register(String name, Supplier<U> create) {
public <T extends R> RegistryEntry<T> register(String name, Supplier<T> create) {
return new RegistryEntryImpl<>(registry().register(name, create));
}
@Override
public void register() {
registry().register(FMLJavaModLoadingContext.get().getModEventBus());
registry().register(ComputerCraft.getEventBus());
}
}
private record RegistryEntryImpl<T>(RegistryObject<T> object) implements RegistryEntry<T> {
private record RegistryEntryImpl<R, T extends R>(DeferredHolder<R, T> object) implements RegistryEntry<T> {
@Override
public ResourceLocation id() {
return object().getId();
@@ -413,61 +336,52 @@ public class PlatformHelperImpl implements PlatformHelper {
}
}
private abstract static class ComponentAccessImpl<T> implements ComponentAccess<T> {
private static class ComponentAccessImpl<T> implements ComponentAccess<T> {
private final BlockEntity owner;
private final InvalidateCallback[] invalidators;
private final BlockCapability<T, Direction> capability;
private final Consumer<Direction> invalidate;
@SuppressWarnings({ "unchecked", "rawtypes" })
final BlockCapabilityCache<T, Direction>[] caches = new BlockCapabilityCache[6];
ComponentAccessImpl(BlockEntity owner, Consumer<Direction> invalidate) {
ComponentAccessImpl(BlockEntity owner, BlockCapability<T, Direction> capability, Consumer<Direction> invalidate) {
this.owner = owner;
// Generate a cache of invalidation functions so we can guarantee we only ever have one registered per
// capability - there's no way to remove these callbacks!
var invalidators = this.invalidators = new InvalidateCallback[6];
for (var dir : Direction.values()) invalidators[dir.ordinal()] = () -> invalidate.accept(dir);
this.capability = capability;
this.invalidate = invalidate;
}
@Nullable
protected abstract T get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate);
@Nullable
@Override
public T get(Direction direction) {
return get(getLevel(), owner.getBlockPos().relative(direction), direction.getOpposite(), invalidators[direction.ordinal()]);
var level = getLevel();
var cache = caches[direction.ordinal()];
if (cache == null) {
cache = caches[direction.ordinal()] = BlockCapabilityCache.create(
capability, level, owner.getBlockPos().relative(direction),
direction.getOpposite(), () -> !owner.isRemoved(), () -> this.invalidate.accept(direction)
);
}
return cache.getCapability();
}
final ServerLevel getLevel() {
return Objects.requireNonNull((ServerLevel) owner.getLevel(), "Block entity is not in a level");
}
}
private static class PeripheralAccess extends ComponentAccessImpl<IPeripheral> {
PeripheralAccess(BlockEntity owner, Consumer<Direction> invalidate) {
super(owner, invalidate);
super(owner, PeripheralCapability.get(), invalidate);
}
@Nullable
@Override
protected IPeripheral get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate) {
return Peripherals.getPeripheral(world, pos, side, invalidate);
}
}
public IPeripheral get(Direction direction) {
var result = super.get(direction);
if (result != null) return result;
private static class CapabilityAccess<T> extends ComponentAccessImpl<T> {
private final Capability<T> capability;
CapabilityAccess(BlockEntity owner, Capability<T> capability, Consumer<Direction> invalidate) {
super(owner, invalidate);
this.capability = capability;
}
@Nullable
@Override
protected T get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate) {
if (!world.isLoaded(pos)) return null;
var blockEntity = world.getBlockEntity(pos);
return blockEntity != null ? CapabilityUtil.unwrap(blockEntity.getCapability(capability, side), invalidate) : null;
var cache = caches[direction.ordinal()];
return Peripherals.getGenericPeripheral(cache.level(), cache.pos(), cache.context(), cache.level().getBlockEntity(cache.pos()));
}
}
}

View File

@@ -1,68 +0,0 @@
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.util;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import javax.annotation.Nullable;
import java.util.Objects;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
/**
* A basic {@link ICapabilityProvider} which provides a single capability, returning the same instance for every
* direction.
* <p>
* This is designed for use with {@link AttachCapabilitiesEvent}, to attach individual capabilities to a specific
* block entity.
*
* @param <T> The capability to provide.
*/
public final class CapabilityProvider<T> implements ICapabilityProvider {
private final Capability<T> cap;
private final Supplier<T> supplier;
private final BooleanSupplier isRemoved;
private @Nullable LazyOptional<T> instance;
private CapabilityProvider(Capability<T> cap, Supplier<T> supplier, BooleanSupplier isRemoved) {
this.cap = Objects.requireNonNull(cap, "Capability cannot be null");
this.supplier = supplier;
this.isRemoved = isRemoved;
}
public static <T> CapabilityProvider<T> attach(AttachCapabilitiesEvent<?> event, ResourceLocation id, Capability<T> cap, Supplier<T> instance) {
BooleanSupplier isRemoved
= event.getObject() instanceof BlockEntity be ? be::isRemoved
: event.getObject() instanceof Entity entity ? entity::isRemoved
: () -> true;
var provider = new CapabilityProvider<>(cap, instance, isRemoved);
event.addCapability(id, provider);
event.addListener(provider::invalidate);
return provider;
}
public void invalidate() {
instance = CapabilityUtil.invalidate(instance);
}
@Override
public <U> LazyOptional<U> getCapability(Capability<U> cap, @Nullable Direction side) {
if (cap != this.cap || isRemoved.getAsBoolean()) return LazyOptional.empty();
var instance = this.instance;
if (instance == null) {
var created = supplier.get();
instance = this.instance = created == null ? LazyOptional.empty() : LazyOptional.of(() -> created);
}
return instance.cast();
}
}

View File

@@ -1,57 +0,0 @@
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.util;
import dan200.computercraft.shared.platform.InvalidateCallback;
import net.minecraft.core.Direction;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nullable;
public final class CapabilityUtil {
private CapabilityUtil() {
}
@Nullable
public static <T> LazyOptional<T> invalidate(@Nullable LazyOptional<T> cap) {
if (cap != null) cap.invalidate();
return null;
}
public static <T> void invalidate(@Nullable LazyOptional<T>[] caps) {
if (caps == null) return;
for (var i = 0; i < caps.length; i++) {
var cap = caps[i];
if (cap != null) cap.invalidate();
caps[i] = null;
}
}
@Nullable
public static <T> T unwrap(LazyOptional<T> p, InvalidateCallback invalidate) {
if (!p.isPresent()) return null;
p.addListener(invalidate.castConsumer());
return p.orElseThrow(NullPointerException::new);
}
/**
* Find a capability, preferring the internal/null side but falling back to a given side if a mod doesn't support
* the internal one.
*
* @param provider The capability provider to get the capability from.
* @param capability The capability to get.
* @param side The side we'll fall back to.
* @param <T> The type of the underlying capability.
* @return The extracted capability, if present.
*/
public static <T> LazyOptional<T> getCapability(ICapabilityProvider provider, Capability<T> capability, Direction side) {
var cap = provider.getCapability(capability);
return cap.isPresent() ? cap : provider.getCapability(capability, side);
}
}

View File

@@ -1,80 +0,0 @@
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.util;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import javax.annotation.Nullable;
import java.util.Objects;
import java.util.function.BooleanSupplier;
/**
* A {@link ICapabilityProvider} which provides a different single capability, with different instances for each
* direction.
* <p>
* This is designed for use with {@link AttachCapabilitiesEvent}, to attach individual capabilities to a specific
* block entity.
*
* @param <T> The capability to provide.
* @see CapabilityProvider
*/
public final class SidedCapabilityProvider<T> implements ICapabilityProvider {
private final Capability<T> cap;
private final Provider<T> supplier;
private final BooleanSupplier isRemoved;
private @Nullable LazyOptional<T>[] instances;
private SidedCapabilityProvider(Capability<T> cap, Provider<T> supplier, BooleanSupplier isRemoved) {
this.cap = Objects.requireNonNull(cap, "Capability cannot be null");
this.supplier = supplier;
this.isRemoved = isRemoved;
}
public static <T> SidedCapabilityProvider<T> attach(AttachCapabilitiesEvent<?> event, ResourceLocation id, Capability<T> cap, Provider<T> supplier) {
BooleanSupplier isRemoved
= event.getObject() instanceof BlockEntity be ? be::isRemoved
: event.getObject() instanceof Entity entity ? entity::isRemoved
: () -> true;
var provider = new SidedCapabilityProvider<>(cap, supplier, isRemoved);
event.addCapability(id, provider);
event.addListener(provider::invalidate);
return provider;
}
public void invalidate() {
CapabilityUtil.invalidate(instances);
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <U> LazyOptional<U> getCapability(Capability<U> cap, @Nullable Direction side) {
if (cap != this.cap || isRemoved.getAsBoolean()) return LazyOptional.empty();
var instances = this.instances;
if (instances == null) instances = this.instances = new LazyOptional[6];
var index = side == null ? 6 : side.ordinal();
var instance = instances[index];
if (instance == null) {
var created = supplier.get(side);
instance = instances[index] = created == null ? LazyOptional.empty() : LazyOptional.of(() -> created);
}
return instance.cast();
}
public interface Provider<T> {
@Nullable
T get(@Nullable Direction direction);
}
}

View File

@@ -3,33 +3,33 @@
# SPDX-License-Identifier: MPL-2.0
# DirectVertexBuffer
protected com.mojang.blaze3d.vertex.VertexBuffer f_231217_ # vertexBufferId
protected com.mojang.blaze3d.vertex.VertexBuffer f_166861_ # indexType
protected com.mojang.blaze3d.vertex.VertexBuffer f_166863_ # indexCount
protected com.mojang.blaze3d.vertex.VertexBuffer f_166864_ # mode
protected com.mojang.blaze3d.vertex.VertexBuffer f_166865_ # sequentialIndices
protected com.mojang.blaze3d.vertex.VertexBuffer f_85917_ # format
protected com.mojang.blaze3d.vertex.VertexBuffer vertexBufferId
protected com.mojang.blaze3d.vertex.VertexBuffer indexType
protected com.mojang.blaze3d.vertex.VertexBuffer indexCount
protected com.mojang.blaze3d.vertex.VertexBuffer mode
protected com.mojang.blaze3d.vertex.VertexBuffer sequentialIndices
protected com.mojang.blaze3d.vertex.VertexBuffer format
# ClientTableFormatter
public net.minecraft.client.gui.components.ChatComponent f_93760_ # allMessages
public net.minecraft.client.gui.components.ChatComponent m_241120_()V # refreshTrimmedMessage
public net.minecraft.client.gui.components.ChatComponent allMessages
public net.minecraft.client.gui.components.ChatComponent refreshTrimmedMessage()V
# ItemPocketRenderer/ItemPrintoutRenderer
public net.minecraft.client.renderer.ItemInHandRenderer m_109312_(F)F # calculateMapTilt
public net.minecraft.client.renderer.ItemInHandRenderer m_109361_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/HumanoidArm;)V # renderMapHand
public net.minecraft.client.renderer.ItemInHandRenderer m_109346_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IFFLnet/minecraft/world/entity/HumanoidArm;)V # renderPlayerArm
public net.minecraft.client.renderer.ItemInHandRenderer calculateMapTilt(F)F
public net.minecraft.client.renderer.ItemInHandRenderer renderMapHand(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/HumanoidArm;)V
public net.minecraft.client.renderer.ItemInHandRenderer renderPlayerArm(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IFFLnet/minecraft/world/entity/HumanoidArm;)V
# SpeakerInstance/SpeakerManager
public com.mojang.blaze3d.audio.Channel m_83652_(I)V # pumpBuffers
public net.minecraft.client.sounds.SoundEngine f_120223_ # executor
public com.mojang.blaze3d.audio.Channel pumpBuffers(I)V
public net.minecraft.client.sounds.SoundEngine executor
# Data generators
public net.minecraft.data.models.BlockModelGenerators f_124477_ # blockStateOutput
public net.minecraft.data.models.BlockModelGenerators f_124478_ # modelOutput
public net.minecraft.data.models.BlockModelGenerators m_124797_(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/resources/ResourceLocation;)V # delegateItemModel
public net.minecraft.data.models.BlockModelGenerators m_124519_(Lnet/minecraft/world/item/Item;Lnet/minecraft/resources/ResourceLocation;)V # delegateItemModel
public net.minecraft.data.models.BlockModelGenerators m_124744_(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/data/models/model/TexturedModel$Provider;)V # createHorizontallyRotatedBlock
public net.minecraft.data.models.ItemModelGenerators f_125080_ # output
public net.minecraft.data.models.ItemModelGenerators m_125091_(Lnet/minecraft/world/item/Item;Ljava/lang/String;Lnet/minecraft/data/models/model/ModelTemplate;)V # generateFlatItem
public net.minecraft.data.models.ItemModelGenerators m_125088_(Lnet/minecraft/world/item/Item;Lnet/minecraft/data/models/model/ModelTemplate;)V # generateFlatItem
public net.minecraft.data.models.model.TextureSlot m_125898_(Ljava/lang/String;)Lnet/minecraft/data/models/model/TextureSlot; # create
public net.minecraft.data.models.BlockModelGenerators blockStateOutput
public net.minecraft.data.models.BlockModelGenerators modelOutput
public net.minecraft.data.models.BlockModelGenerators delegateItemModel(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/resources/ResourceLocation;)V
public net.minecraft.data.models.BlockModelGenerators delegateItemModel(Lnet/minecraft/world/item/Item;Lnet/minecraft/resources/ResourceLocation;)V
public net.minecraft.data.models.BlockModelGenerators createHorizontallyRotatedBlock(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/data/models/model/TexturedModel$Provider;)V
public net.minecraft.data.models.ItemModelGenerators output
public net.minecraft.data.models.ItemModelGenerators generateFlatItem(Lnet/minecraft/world/item/Item;Lnet/minecraft/data/models/model/ModelTemplate;)V
public net.minecraft.data.models.ItemModelGenerators generateFlatItem(Lnet/minecraft/world/item/Item;Ljava/lang/String;Lnet/minecraft/data/models/model/ModelTemplate;)V
public net.minecraft.data.models.model.TextureSlot create(Ljava/lang/String;)Lnet/minecraft/data/models/model/TextureSlot;

View File

@@ -3,7 +3,7 @@
# SPDX-License-Identifier: MPL-2.0
modLoader="javafml"
loaderVersion="[47,48)"
loaderVersion="[1,)"
issueTrackerURL="https://github.com/cc-tweaked/CC-Tweaked/issues"
logoFile="pack.png"
@@ -24,8 +24,11 @@ CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles a
'''
[[dependencies.computercraft]]
modId="forge"
mandatory=true
versionRange="[${forgeVersion},48)"
modId="neoforge"
type="required"
versionRange="[${neoVersion},20.5)"
ordering="NONE"
side="BOTH"
[[mixins]]
config = "computercraft-client.forge.mixins.json"

View File

@@ -7,7 +7,7 @@ package dan200.computercraft.shared.platform;
import dan200.computercraft.test.shared.WithMinecraft;
import dan200.computercraft.test.shared.platform.ContainerTransferContract;
import net.minecraft.world.Container;
import net.minecraftforge.items.wrapper.InvWrapper;
import net.neoforged.neoforge.items.wrapper.InvWrapper;
@WithMinecraft
public class ForgeContainerTransferTest implements ContainerTransferContract {

View File

@@ -5,35 +5,34 @@
package dan200.computercraft.gametest.core;
import dan200.computercraft.export.Exporter;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RegisterClientCommandsEvent;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.RegisterGameTestsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.EventPriority;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLEnvironment;
import net.neoforged.neoforge.client.event.RegisterClientCommandsEvent;
import net.neoforged.neoforge.client.event.ScreenEvent;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.RegisterCommandsEvent;
import net.neoforged.neoforge.event.RegisterGameTestsEvent;
import net.neoforged.neoforge.event.TickEvent;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
@Mod("cctest")
public class TestMod {
public TestMod() {
public TestMod(IEventBus modBus) {
TestHooks.init();
var bus = MinecraftForge.EVENT_BUS;
var bus = NeoForge.EVENT_BUS;
bus.addListener(EventPriority.LOW, (ServerStartedEvent e) -> TestHooks.onServerStarted(e.getServer()));
bus.addListener((RegisterCommandsEvent e) -> CCTestCommand.register(e.getDispatcher()));
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> TestMod::onInitializeClient);
if (FMLEnvironment.dist == Dist.CLIENT) TestMod.onInitializeClient();
var modBus = FMLJavaModLoadingContext.get().getModEventBus();
modBus.addListener((RegisterGameTestsEvent event) -> TestHooks.loadTests(event::register));
}
private static void onInitializeClient() {
var bus = MinecraftForge.EVENT_BUS;
var bus = NeoForge.EVENT_BUS;
bus.addListener((TickEvent.ServerTickEvent e) -> {
if (e.phase == TickEvent.Phase.START) ClientTestHooks.onServerTick(e.getServer());

View File

@@ -3,7 +3,7 @@
# SPDX-License-Identifier: MPL-2.0
modLoader="javafml"
loaderVersion="[30,)"
loaderVersion="[1,)"
issueTrackerURL="https://github.com/cc-tweaked/CC-Tweaked/issues"
displayURL="https://github.com/cc-tweaked/CC-Tweaked"
@@ -22,7 +22,10 @@ A test framework for ensuring CC: Tweaked works correctly.
[[dependencies.cctest]]
modId="computercraft"
mandatory=true
type="required"
versionRange="[1.0,)"
ordering="AFTER"
side="BOTH"
[[mixins]]
config="computercraft-gametest.mixins.json"