Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/main/java/com/lovetropics/gamemodebuild/ItemFilter.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.lovetropics.gamemodebuild;

import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
import net.minecraft.Util;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistryAccess;
Expand Down Expand Up @@ -66,17 +67,27 @@ private static Predicate<Item> parsePredicate(String predicate) {
private final List<Predicate<Item>> whitelistPredicates;
private final List<Predicate<Item>> blacklistPredicates;
private final SingleKeyCache<Key, List<ItemStack>> cache;
private final SingleKeyCache<Key, Predicate<ItemStack>> predicateCache;

private ItemFilter(List<Predicate<Item>> whitelist, List<Predicate<Item>> blacklist) {
this.whitelistPredicates = new ArrayList<>(whitelist);
this.blacklistPredicates = new ArrayList<>(blacklist);
cache = Util.singleKeyCache(key -> computeStacks(key.featureFlags(), key.registryAccess()));
predicateCache = Util.singleKeyCache(key -> {
List<ItemStack> stacks = cache.getValue(key);
Set<ItemStack> set = new ObjectOpenCustomHashSet<>(stacks, ItemStackLinkedSet.TYPE_AND_TAG);
return set::contains;
});
}

public List<ItemStack> getAllStacks(FeatureFlagSet enabledFeatures, RegistryAccess registryAccess) {
return cache.getValue(new Key(enabledFeatures, registryAccess));
}

public Predicate<ItemStack> getStackPredicate(FeatureFlagSet enabledFeatures, RegistryAccess registryAccess) {
return predicateCache.getValue(new Key(enabledFeatures, registryAccess));
}

private List<ItemStack> computeStacks(FeatureFlagSet enabledFeatures, HolderLookup.Provider registryAccess) {
if (whitelistPredicates.isEmpty()) {
return List.of();
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/lovetropics/gamemodebuild/client/GBClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,38 @@

import com.lovetropics.gamemodebuild.GamemodeBuild;
import com.lovetropics.gamemodebuild.container.BuildContainer;
import com.lovetropics.gamemodebuild.message.SetGamemodeBuildSlotPacket;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.client.event.InputEvent;
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;

@Mod(value = GamemodeBuild.MODID, dist = Dist.CLIENT)
@EventBusSubscriber(modid = GamemodeBuild.MODID, value = Dist.CLIENT)
public class GBClient {
public GBClient(IEventBus bus) {
bus.addListener((final RegisterMenuScreensEvent event) -> {
event.register(BuildContainer.TYPE.get(), BuildScreen::new);
});
}

@SubscribeEvent
public static void onInputTriggered(InputEvent.InteractionKeyMappingTriggered event) {
Minecraft minecraft = Minecraft.getInstance();
LocalPlayer player = minecraft.player;
if (event.isPickBlock() && player != null && GamemodeBuild.isActive(player)) {
event.setCanceled(true);
HitResult hitResult = minecraft.hitResult;
if (hitResult != null && hitResult.getType() == HitResult.Type.BLOCK) {
player.connection.send(new SetGamemodeBuildSlotPacket(((BlockHitResult) hitResult).getBlockPos()));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Optional;

import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
Expand Down Expand Up @@ -136,7 +137,7 @@ private static Command<CommandSourceStack> getListAddCommand(boolean whitelist)
} else {
GBConfigs.SERVER.addToBlacklist(name, entry, true);
}
PacketDistributor.sendToAllPlayers(new ListUpdateMessage(ListUpdateMessage.Operation.ADD, whitelist, name, entry));
PacketDistributor.sendToAllPlayers(new ListUpdateMessage(ListUpdateMessage.Operation.ADD, whitelist, name, Optional.of(entry)));
ctx.getSource().sendSuccess(() -> Component.literal("Added '" + entry + "' to " + name + (whitelist ? " whitelist" : " blacklist")), false);

return Command.SINGLE_SUCCESS;
Expand All @@ -152,7 +153,7 @@ private static Command<CommandSourceStack> getListRemoveCommand(boolean whitelis
boolean found = whitelist ? GBConfigs.SERVER.removeFromWhitelist(name, entry, true) : GBConfigs.SERVER.removeFromBlacklist(name, entry, true);
if (!found) throw FILTER_DID_NOT_EXIST.create();

PacketDistributor.sendToAllPlayers(new ListUpdateMessage(ListUpdateMessage.Operation.REMOVE, whitelist, name, entry));
PacketDistributor.sendToAllPlayers(new ListUpdateMessage(ListUpdateMessage.Operation.REMOVE, whitelist, name, Optional.of(entry)));
ctx.getSource().sendSuccess(() -> Component.literal("Removed '" + entry + "' from " + name + (whitelist ? " whitelist" : " blacklist")), false);

return Command.SINGLE_SUCCESS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public static void register(RegisterPayloadHandlersEvent event) {
UpdateFilterMessage.CODEC,
UpdateFilterMessage::handle
);
registrar.playToServer(
SetGamemodeBuildSlotPacket.TYPE,
SetGamemodeBuildSlotPacket.CODEC,
SetGamemodeBuildSlotPacket::handle
);

registrar.playBidirectional(
SetActiveMessage.TYPE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,20 @@
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.neoforged.neoforge.network.handling.IPayloadContext;

public record ListUpdateMessage(Operation operation, boolean whitelist, String name, String entry) implements CustomPacketPayload {
import java.util.Optional;

public record ListUpdateMessage(Operation operation, boolean whitelist, String name, Optional<String> entry) implements CustomPacketPayload {
public static final Type<ListUpdateMessage> TYPE = new Type<>(GamemodeBuild.rl("update_list"));
public static final StreamCodec<RegistryFriendlyByteBuf, ListUpdateMessage> CODEC = StreamCodec.composite(
ByteBufCodecs.idMapper(i -> Operation.values()[i], Operation::ordinal),
ListUpdateMessage::operation,
ByteBufCodecs.BOOL,
ListUpdateMessage::whitelist,
ByteBufCodecs.STRING_UTF8,
ListUpdateMessage::name,
ByteBufCodecs.STRING_UTF8,
ListUpdateMessage::entry,
ByteBufCodecs.idMapper(i -> Operation.values()[i], Operation::ordinal), ListUpdateMessage::operation,
ByteBufCodecs.BOOL, ListUpdateMessage::whitelist,
ByteBufCodecs.STRING_UTF8, ListUpdateMessage::name,
ByteBufCodecs.STRING_UTF8.apply(ByteBufCodecs::optional), ListUpdateMessage::entry,
ListUpdateMessage::new
);

public void handle(IPayloadContext context) {
String entry = this.entry.orElse("");
switch (operation) {
case ADD -> {
if (whitelist) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.lovetropics.gamemodebuild.message;

import com.lovetropics.gamemodebuild.GBConfigs;
import com.lovetropics.gamemodebuild.GamemodeBuild;
import com.lovetropics.gamemodebuild.container.GBStackMarker;
import com.lovetropics.gamemodebuild.state.GBPlayerStore;
import com.lovetropics.gamemodebuild.state.GBServerState;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.network.handling.IPayloadContext;

import java.util.function.Predicate;

// TODO: Won't need a custom packet in 1.21.4+ - it's server-authoritative
public record SetGamemodeBuildSlotPacket(BlockPos blockPos) implements CustomPacketPayload {
public static final Type<SetGamemodeBuildSlotPacket> TYPE = new Type<>(GamemodeBuild.rl("pick_block"));

public static final StreamCodec<RegistryFriendlyByteBuf, SetGamemodeBuildSlotPacket> CODEC = StreamCodec.composite(
BlockPos.STREAM_CODEC, SetGamemodeBuildSlotPacket::blockPos,
SetGamemodeBuildSlotPacket::new
);

public static void handle(SetGamemodeBuildSlotPacket packet, IPayloadContext ctx) {
if (!(ctx.player() instanceof ServerPlayer player) || !GBServerState.isActiveFor(player)) {
return;
}

BlockPos blockPos = packet.blockPos;
if (!player.canInteractWithBlock(blockPos, ServerPlayer.INTERACTION_DISTANCE_VERIFICATION_BUFFER)) {
return;
}

BlockState blockState = player.level().getBlockState(blockPos);

// Just fake it, it's not important
BlockHitResult hitResult = new BlockHitResult(Vec3.atCenterOf(blockPos), Direction.UP, blockPos, false);
ItemStack itemStack = blockState.getCloneItemStack(hitResult, player.level(), blockPos, player);
if (itemStack.isEmpty()) {
return;
}

FeatureFlagSet enabledFeatures = player.level().enabledFeatures();
RegistryAccess registryAccess = player.level().registryAccess();
Predicate<ItemStack> predicate = GBConfigs.SERVER.getFilter(GBPlayerStore.getList(player)).getStackPredicate(enabledFeatures, registryAccess);
if (!predicate.test(itemStack)) {
return;
}

GBStackMarker.mark(itemStack);

pickItem(player, itemStack);
}

private static void pickItem(ServerPlayer player, ItemStack itemStack) {
Inventory inventory = player.getInventory();
int slotWithItem = inventory.findSlotMatchingItem(itemStack);
if (slotWithItem == Inventory.NOT_FOUND_INDEX) {
pickFreshItem(inventory, itemStack);
} else {
pickExistingItem(inventory, slotWithItem);
}
player.connection.send(new ClientboundSetCarriedItemPacket(inventory.selected));
player.inventoryMenu.broadcastChanges();
}

private static void pickExistingItem(Inventory inventory, int slot) {
if (Inventory.isHotbarSlot(slot)) {
inventory.selected = slot;
} else {
inventory.pickSlot(slot);
}
}

private static void pickFreshItem(Inventory inventory, ItemStack itemStack) {
inventory.selected = inventory.getSuitableHotbarSlot();
if (!inventory.getItem(inventory.selected).isEmpty()) {
int freeSlot = inventory.getFreeSlot();
if (freeSlot != Inventory.NOT_FOUND_INDEX) {
inventory.setItem(freeSlot, inventory.getItem(inventory.selected));
}
}
inventory.setItem(inventory.selected, itemStack);
}

@Override
public Type<SetGamemodeBuildSlotPacket> type() {
return TYPE;
}
}
4 changes: 3 additions & 1 deletion src/main/resources/gamemodebuild.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
"injectors": {
"defaultRequire": 1
},
"minVersion": "0.8"
"minVersion": "0.8",
"client": [
]
}
Loading