1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-26 07:03:22 +00:00

Move many Forge-specific methods behind PlatformHelper

There's still a lot of work to be done on turtles (TurtlePlayer, all the
turtle tool stuff), but we're a good way along now.
This commit is contained in:
Jonathan Coates 2022-11-09 14:41:46 +00:00
parent 22729f6f16
commit cc73fcd85d
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
15 changed files with 175 additions and 53 deletions

View File

@ -12,6 +12,8 @@
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.api.turtle.event.TurtleRefuelEvent;
import dan200.computercraft.impl.TurtleRefuelHandlers;
import dan200.computercraft.shared.Config;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.peripheral.generic.data.FluidData;
@ -20,6 +22,7 @@
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.platform.NetworkHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@ -31,6 +34,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.OptionalInt;
@Mod(ComputerCraft.MOD_ID)
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
public final class ComputerCraft {
@ -77,8 +82,18 @@ public final class ComputerCraft {
public ComputerCraft() {
Config.setup();
ModRegistry.register();
}
// Register a fallback handler for the turtle refuel event.
TurtleRefuelHandlers.register((turtle, stack, slot, limit) -> {
@SuppressWarnings("removal") var event = new TurtleRefuelEvent(turtle, stack);
MinecraftForge.EVENT_BUS.post(event);
if (event.getHandler() == null) return OptionalInt.empty();
if (limit == 0) return OptionalInt.of(0);
return OptionalInt.of(event.getHandler().refuel(turtle, stack, slot, limit));
});
NetworkHandler.setup();
}
@SubscribeEvent
public static void registerRegistries(NewRegistryEvent event) {
@ -99,8 +114,6 @@ public static void registerCapabilities(RegisterCapabilitiesEvent event) {
@SubscribeEvent
public static void init(FMLCommonSetupEvent event) {
NetworkHandler.setup();
event.enqueueWork(ModRegistry::registerMainThread);
ComputerCraftAPI.registerGenericSource(new InventoryMethods());

View File

@ -7,9 +7,7 @@
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
import dan200.computercraft.api.turtle.event.TurtleRefuelEvent;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.MinecraftForge;
import java.util.List;
import java.util.Objects;
@ -22,17 +20,6 @@
public final class TurtleRefuelHandlers {
private static final List<TurtleRefuelHandler> handlers = new CopyOnWriteArrayList<>();
static {
// Register a fallback handler for our event.
handlers.add((turtle, stack, slot, limit) -> {
@SuppressWarnings("removal") var event = new TurtleRefuelEvent(turtle, stack);
MinecraftForge.EVENT_BUS.post(event);
if (event.getHandler() == null) return OptionalInt.empty();
if (limit == 0) return OptionalInt.of(0);
return OptionalInt.of(event.getHandler().refuel(turtle, stack, slot, limit));
});
}
private TurtleRefuelHandlers() {
}

View File

@ -8,10 +8,10 @@
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.common.util.FakePlayer;
import java.util.Arrays;
import java.util.Locale;
@ -24,9 +24,7 @@ private CommandUtils() {
public static boolean isPlayer(CommandSourceStack output) {
var sender = output.getEntity();
return sender instanceof ServerPlayer
&& !(sender instanceof FakePlayer)
&& ((ServerPlayer) sender).connection != null;
return sender instanceof ServerPlayer player && !PlatformHelper.get().isFakePlayer(player);
}
@SuppressWarnings("unchecked")

View File

@ -19,10 +19,10 @@
import dan200.computercraft.shared.CommonHooks;
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
import dan200.computercraft.shared.util.IDAssigner;
import net.minecraft.SharedConstants;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraftforge.versions.mcp.MCPVersion;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -192,7 +192,8 @@ public InputStream createResourceFile(String domain, String subPath) {
@Nonnull
@Override
public String getHostString() {
return String.format("ComputerCraft %s (Minecraft %s)", ComputerCraftAPI.getInstalledVersion(), MCPVersion.getMCVersion());
var version = SharedConstants.getCurrentVersion().getName();
return String.format("ComputerCraft %s (Minecraft %s)", ComputerCraftAPI.getInstalledVersion(), version);
}
@Nonnull

View File

@ -6,6 +6,7 @@
package dan200.computercraft.shared.peripheral.generic.data;
import com.google.gson.JsonParseException;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.nbt.ListTag;
@ -99,7 +100,7 @@ private static Component parseTextComponent(@Nonnull Tag x) {
private static List<Map<String, Object>> getItemGroups(@Nonnull ItemStack stack) {
List<Map<String, Object>> groups = new ArrayList<>(1);
for (var group : stack.getItem().getCreativeTabs()) {
for (var group : PlatformHelper.get().getCreativeTabs(stack)) {
if (group == null) continue;
Map<String, Object> groupData = new HashMap<>(2);

View File

@ -6,9 +6,11 @@
package dan200.computercraft.shared.peripheral.monitor;
import dan200.computercraft.shared.common.BlockGeneric;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.platform.RegistryEntry;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
@ -22,7 +24,6 @@
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraftforge.common.util.FakePlayer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -90,7 +91,7 @@ public void setPlacedBy(@Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Bl
var entity = world.getBlockEntity(pos);
if (entity instanceof TileMonitor monitor && !world.isClientSide) {
// Defer the block update if we're being placed by another TE. See #691
if (livingEntity == null || livingEntity instanceof FakePlayer) {
if (livingEntity == null || (livingEntity instanceof ServerPlayer player && PlatformHelper.get().isFakePlayer(player))) {
monitor.updateNeighborsDeferred();
return;
}

View File

@ -26,9 +26,13 @@
import net.minecraft.world.WorldlyContainer;
import net.minecraft.world.entity.player.Player;
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;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -235,4 +239,81 @@ static PlatformHelper get() {
* @return A list of tags.
*/
List<TagKey<Item>> getDyeTags();
/**
* Get the amount of fuel an item provides.
*
* @param stack The item to burn.
* @return The amount of fuel it provides.
*/
int getBurnTime(ItemStack stack);
/**
* Get the creative tabs this stack belongs to.
*
* @param stack The current item.
* @return The creative tabs the item belongs to.
*/
Collection<CreativeModeTab> getCreativeTabs(ItemStack stack);
/**
* Get the "container" item to be returned after crafting. For instance, crafting with a lava bucket should return
* an empty bucket.
*
* @param stack The original item.
* @return The "remainder" item. May be {@link ItemStack#EMPTY}.
*/
ItemStack getCraftingRemainingItem(ItemStack stack);
/**
* A more general version of {@link #getCraftingRemainingItem(ItemStack)} which gets all remaining items for a
* recipe.
*
* @param player The player performing the crafting.
* @param recipe The recipe currently doing the crafting.
* @param container The crafting container.
* @return A list of items to return to the player after crafting.
*/
List<ItemStack> getRecipeRemainingItems(ServerPlayer player, Recipe<CraftingContainer> recipe, CraftingContainer container);
/**
* Fire an event after crafting has occurred.
*
* @param player The player performing the crafting.
* @param container The current crafting container.
* @param stack The resulting stack from crafting.
*/
void onItemCrafted(ServerPlayer player, CraftingContainer container, ItemStack stack);
/**
* Check whether we should notify neighbours in a particular direction.
*
* @param level The current level.
* @param pos The position of the current block.
* @param block The block which is performing the notification, should be equal to {@code level.getBlockState(pos)}.
* @param direction The direction we're notifying in.
* @return {@code true} if neighbours should be notified or {@code false} otherwise.
*/
boolean onNotifyNeighbour(Level level, BlockPos pos, BlockState block, Direction direction);
/**
* Determine if a player is not a real player.
*
* @param player The player to check.
* @return Whether this player is fake.
*/
default boolean isFakePlayer(ServerPlayer player) {
// Any subclass of ServerPlayer (i.e. Forge's FakePlayer) is assumed to be a fake.
return player.connection == null || player.getClass() != ServerPlayer.class;
}
/**
* Get the distance a player can reach.
*
* @param player The player who is reaching.
* @return The distance (in blocks) that a player can reach.
*/
default double getReachDistance(Player player) {
return player.isCreative() ? 5 : 4.5;
}
}

View File

@ -32,10 +32,13 @@
import net.minecraft.world.WorldlyContainerHolder;
import net.minecraft.world.entity.player.Player;
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.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -43,11 +46,13 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.Tags;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.extensions.IForgeMenuType;
import net.minecraftforge.common.util.NonNullConsumer;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.InvWrapper;
@ -59,10 +64,7 @@
import net.minecraftforge.registries.RegistryObject;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@ -236,6 +238,44 @@ public List<TagKey<Item>> getDyeTags() {
);
}
@Override
public int getBurnTime(ItemStack stack) {
return ForgeHooks.getBurnTime(stack, null);
}
@Override
public Collection<CreativeModeTab> getCreativeTabs(ItemStack stack) {
return stack.getItem().getCreativeTabs();
}
@Override
public ItemStack getCraftingRemainingItem(ItemStack stack) {
return stack.getCraftingRemainingItem();
}
@Override
public List<ItemStack> getRecipeRemainingItems(ServerPlayer player, Recipe<CraftingContainer> recipe, CraftingContainer container) {
ForgeHooks.setCraftingPlayer(player);
var result = recipe.getRemainingItems(container);
ForgeHooks.setCraftingPlayer(null);
return result;
}
@Override
public void onItemCrafted(ServerPlayer player, CraftingContainer container, ItemStack stack) {
ForgeEventFactory.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();
}
@Override
public double getReachDistance(Player player) {
return player.getReachDistance();
}
private record RegistryWrapperImpl<T>(
ResourceLocation name, ForgeRegistry<T> registry
) implements Registries.RegistryWrapper<T> {

View File

@ -7,8 +7,8 @@
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.ForgeHooks;
import javax.annotation.Nonnull;
import java.util.OptionalInt;
@ -27,7 +27,7 @@ public OptionalInt refuel(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack curr
var stack = turtle.getInventory().removeItem(slot, limit);
var fuelToGive = fuelPerItem * stack.getCount();
// Store the replacement item in the inventory
var replacementStack = ForgeHooks.getCraftingRemainingItem(stack);
var replacementStack = PlatformHelper.get().getCraftingRemainingItem(stack);
if (!replacementStack.isEmpty()) TurtleUtil.storeItemOrDrop(turtle, replacementStack);
turtle.getInventory().setChanged();
@ -36,6 +36,6 @@ public OptionalInt refuel(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack curr
}
private static int getFuelPerItem(@Nonnull ItemStack stack) {
return (ForgeHooks.getBurnTime(stack, null) * 5) / 100;
return (PlatformHelper.get().getBurnTime(stack) * 5) / 100;
}
}

View File

@ -6,6 +6,7 @@
package dan200.computercraft.shared.turtle.upgrades;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import net.minecraft.server.level.ServerLevel;
@ -15,8 +16,6 @@
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.event.ForgeEventFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -81,12 +80,9 @@ public List<ItemStack> doCrafting(Level world, int maxCount) {
results.add(result);
result.onCraftedBy(world, player, result.getCount());
ForgeEventFactory.firePlayerCraftingEvent(player, result, this);
ForgeHooks.setCraftingPlayer(player);
var remainders = recipe.getRemainingItems(this);
ForgeHooks.setCraftingPlayer(null);
PlatformHelper.get().onItemCrafted(player, this, result);
var remainders = PlatformHelper.get().getRecipeRemainingItems(player, recipe, this);
for (var slot = 0; slot < remainders.size(); slot++) {
var existing = getItem(slot);
var remainder = remainders.get(slot);

View File

@ -17,7 +17,6 @@
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.ShapedRecipe;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.crafting.CraftingHelper;
import javax.annotation.Nonnull;
@ -58,7 +57,7 @@ public static class Serializer implements RecipeSerializer<ImpostorRecipe> {
public ImpostorRecipe fromJson(@Nonnull ResourceLocation identifier, @Nonnull JsonObject json) {
var group = GsonHelper.getAsString(json, "group", "");
var recipe = RecipeSerializer.SHAPED_RECIPE.fromJson(identifier, json);
var result = CraftingHelper.getItemStack(GsonHelper.getAsJsonObject(json, "result"), true);
var result = ShapedRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(json, "result"));
return new ImpostorRecipe(identifier, group, recipe.getWidth(), recipe.getHeight(), recipe.getIngredients(), result);
}
@ -68,7 +67,7 @@ public ImpostorRecipe fromNetwork(@Nonnull ResourceLocation identifier, @Nonnull
var height = buf.readVarInt();
var group = buf.readUtf(Short.MAX_VALUE);
var items = NonNullList.withSize(width * height, Ingredient.EMPTY);
for (var k = 0; k < items.size(); ++k) items.set(k, Ingredient.fromNetwork(buf));
for (var k = 0; k < items.size(); k++) items.set(k, Ingredient.fromNetwork(buf));
var result = buf.readItem();
return new ImpostorRecipe(identifier, group, width, height, items, result);
}

View File

@ -17,9 +17,9 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.ShapedRecipe;
import net.minecraft.world.item.crafting.ShapelessRecipe;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.crafting.CraftingHelper;
import javax.annotation.Nonnull;
@ -66,8 +66,8 @@ public ImpostorShapelessRecipe fromJson(@Nonnull ResourceLocation id, @Nonnull J
throw new JsonParseException("Too many ingredients for shapeless recipe the max is 9");
}
var itemstack = CraftingHelper.getItemStack(GsonHelper.getAsJsonObject(json, "result"), true);
return new ImpostorShapelessRecipe(id, s, itemstack, ingredients);
var result = ShapedRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(json, "result"));
return new ImpostorShapelessRecipe(id, s, result, ingredients);
}
private NonNullList<Ingredient> readIngredients(JsonArray arrays) {

View File

@ -5,6 +5,7 @@
*/
package dan200.computercraft.shared.util;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
@ -12,9 +13,6 @@
import net.minecraft.world.level.block.DiodeBlock;
import net.minecraft.world.level.block.RedStoneWireBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.event.ForgeEventFactory;
import java.util.EnumSet;
public final class RedstoneUtil {
private RedstoneUtil() {
@ -39,10 +37,17 @@ public static int getRedstoneInput(Level world, BlockPos pos, Direction side) {
: power;
}
/**
* Propagate a redstone output to a particular side.
*
* @param world The current level.
* @param pos The current block's position.
* @param side The direction we're propagating to.
* @see DiodeBlock#updateNeighborsInFront(Level, BlockPos, BlockState)
*/
public static void propagateRedstoneOutput(Level world, BlockPos pos, Direction side) {
// Propagate ordinary output. See BlockRedstoneDiode.notifyNeighbors
var block = world.getBlockState(pos);
if (ForgeEventFactory.onNeighborNotify(world, pos, block, EnumSet.of(side), false).isCanceled()) return;
if (!PlatformHelper.get().onNotifyNeighbour(world, pos, block, side)) return;
var neighbourPos = pos.relative(side);
world.neighborChanged(neighbourPos, block.getBlock(), pos);

View File

@ -5,6 +5,7 @@
*/
package dan200.computercraft.shared.util;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
@ -23,7 +24,6 @@
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.common.ForgeMod;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -113,7 +113,7 @@ public static Vec3 getRayStart(Player entity) {
}
public static Vec3 getRayEnd(Player player) {
var reach = player.getAttribute(ForgeMod.REACH_DISTANCE.get()).getValue();
var reach = PlatformHelper.get().getReachDistance(player);
var look = player.getLookAngle();
return getRayStart(player).add(look.x * reach, look.y * reach, look.z * reach);
}

View File

@ -409,7 +409,7 @@ fun Peripheral_change(helper: GameTestHelper) = helper.sequence {
turtle.forward().await().assertArrayEquals(true, message = "Moved turtle forward")
turtle.back().await().assertArrayEquals(true, message = "Moved turtle forward")
}
thenIdle(1)
thenIdle(2) // Should happen immediately, but computers might be slow.
thenExecute {
assertEquals(
listOf(