forked from BRNSystems/RegexingHoppers
Compare commits
3 Commits
main
...
feat/littl
Author | SHA1 | Date | |
---|---|---|---|
dd8a5d5866 | |||
45de93c38c | |||
fd53a7b3cb |
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,7 +4,8 @@
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
#VS Code things
|
||||
.vscode/
|
||||
# IntelliJ
|
||||
out/
|
||||
# mpeltonen/sbt-idea plugin
|
||||
|
71
README.md
Normal file
71
README.md
Normal file
@ -0,0 +1,71 @@
|
||||
# RegexingHoppers
|
||||
|
||||
RegexingHoppers is a Minecraft mod that enhances the functionality of hoppers by allowing the use of regular expressions (regex) to filter items passing through them. This mod adds a new layer of customization and control to item sorting and transportation systems in Minecraft.
|
||||
|
||||
## Features
|
||||
|
||||
- Use custom names for hoppers as regex patterns to filter items
|
||||
- Three new commands for testing and managing regex patterns
|
||||
- Configurable permission levels for commands
|
||||
|
||||
## Installation
|
||||
|
||||
1. Make sure you have Fabric Loader installed
|
||||
2. Download the latest version of RegexingHoppers from the releases page
|
||||
3. Place the downloaded .jar file in your Minecraft mods folder
|
||||
4. Launch Minecraft with the Fabric profile
|
||||
|
||||
## Usage
|
||||
|
||||
### Regex Filtering
|
||||
|
||||
To use regex filtering on a hopper:
|
||||
|
||||
1. Rename the hopper using an anvil with your desired regex pattern
|
||||
2. Place the renamed hopper in your item system
|
||||
3. Items matching the regex pattern will be allowed through, while others will be filtered out
|
||||
|
||||
### Commands
|
||||
|
||||
RegexingHoppers adds the following commands:
|
||||
|
||||
1. `/regexhoppers test <regex>`: Test a regex pattern against all item names
|
||||
2. `/regexhoppers list <regex>`: List all items matching the given regex pattern
|
||||
3. `/regexhoppers hand`: Display the name of the item in your main hand
|
||||
|
||||
By default, all players can use these commands. You can adjust the permission levels in the configuration file.
|
||||
|
||||
## Configuration
|
||||
|
||||
The mod includes a configuration file (`regexhoppers.json`) located in the `config` folder. You can adjust the permission levels required for each command:
|
||||
|
||||
```json
|
||||
{
|
||||
"permissions": [
|
||||
{
|
||||
"name": "regexhoppers.command.test",
|
||||
"level": 0
|
||||
},
|
||||
{
|
||||
"name": "regexhoppers.command.list",
|
||||
"level": 0
|
||||
},
|
||||
{
|
||||
"name": "regexhoppers.command.hand",
|
||||
"level": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Adjust the `level` value for each command to set the required permission level. A level of 0 allows all players to use the command.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter any issues or have any questions, please open an issue on our GitHub repository.
|
||||
|
||||
Happy filtering!
|
6
TODO.md
Normal file
6
TODO.md
Normal file
@ -0,0 +1,6 @@
|
||||
# TODO File
|
||||
|
||||
## Ideas
|
||||
|
||||
- Add a not match system
|
||||
- For example `not .*iron.*` will let all items, except iron ones, literaly the reverse of `.*iron.*`. It can be usefull to avoid too complexe regex
|
@ -1,20 +1,14 @@
|
||||
package systems.brn.regexinghoppers;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.minecraft.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import systems.brn.regexinghoppers.mixin.HopperBlockEntityAccessor;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import systems.brn.regexinghoppers.commands.RegexHoppersCommands;
|
||||
import systems.brn.regexinghoppers.config.ModConfig;
|
||||
|
||||
public class RegexingHoppers implements ModInitializer {
|
||||
// This logger is used to write text to the console and the log file.
|
||||
// It is considered best practice to use your mod id as the logger's name.
|
||||
// That way, it's clear which mod wrote info, warnings, and errors.
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("RegexingHoppers");
|
||||
|
||||
@Override
|
||||
@ -22,55 +16,10 @@ public class RegexingHoppers implements ModInitializer {
|
||||
// This code runs as soon as Minecraft is in a mod-load-ready state.
|
||||
// However, some things (like resources) may still be uninitialized.
|
||||
// Proceed with mild caution.
|
||||
ModConfig.getInstance();
|
||||
LOGGER.debug("RegexingHoppers initialized!");
|
||||
}
|
||||
|
||||
public static boolean shouldNotMove(Inventory hopper, String itemName) {
|
||||
// Log entering the method with given parameters
|
||||
LOGGER.debug("Entering shouldNotMove with itemName: {}", itemName);
|
||||
|
||||
if (hopper instanceof NamedScreenHandlerFactory factory) {
|
||||
String customName = factory.getDisplayName().getLiteralString();
|
||||
|
||||
// Log the custom name used for matching
|
||||
LOGGER.debug("Custom regex pattern from hopper: {}", customName);
|
||||
|
||||
if (customName != null && !customName.isEmpty()) {
|
||||
if (hopper instanceof HopperBlockEntity) {
|
||||
HopperBlockEntityAccessor hopperAccessor = (HopperBlockEntityAccessor) hopper;
|
||||
if (hopperAccessor.getTransferCooldown() > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
try {
|
||||
Pattern pattern = Pattern.compile(customName);
|
||||
Matcher matcher = pattern.matcher(itemName);
|
||||
|
||||
// Log the result of the regex matching
|
||||
boolean matches = matcher.matches();
|
||||
LOGGER.debug("Regex matching result: {}", matches);
|
||||
|
||||
if (!matches) {
|
||||
if (hopper instanceof HopperBlockEntity) {
|
||||
HopperBlockEntityAccessor hopperAccessor = (HopperBlockEntityAccessor) hopper;
|
||||
hopperAccessor.setTransferCooldown(8);
|
||||
}
|
||||
}
|
||||
return !matches;
|
||||
} catch (PatternSyntaxException e) {
|
||||
// Log exception if regex pattern is invalid
|
||||
LOGGER.debug("Invalid regex pattern: {}", customName, e);
|
||||
}
|
||||
} else {
|
||||
// Log case when custom name is null or empty
|
||||
LOGGER.debug("Custom name is null or empty, not performing regex matching.");
|
||||
}
|
||||
} else {
|
||||
// Log if hopper is not an instance of NamedScreenHandlerFactory
|
||||
LOGGER.debug("Hopper is not an instance of NamedScreenHandlerFactory.");
|
||||
}
|
||||
|
||||
// Default return value in case no conditions are met
|
||||
return false;
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
|
||||
RegexHoppersCommands.register(dispatcher);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package systems.brn.regexinghoppers.commands;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import systems.brn.regexinghoppers.config.ModConfig;
|
||||
import systems.brn.regexinghoppers.util.Tools;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.registry.Registries;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class RegexHoppersCommands {
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("RegexingHoppers");
|
||||
|
||||
// TODO: Add config file for permissions system
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
dispatcher.register(literal("regexhoppers")
|
||||
.then(literal("test")
|
||||
.requires(source -> source.hasPermissionLevel(ModConfig.getInstance().getTestPermission().level))
|
||||
.then(argument("regex", StringArgumentType.greedyString())
|
||||
.executes(context -> executeMatch(context.getSource(), StringArgumentType.getString(context, "regex")))))
|
||||
.then(literal("list")
|
||||
.requires(source -> source.hasPermissionLevel(ModConfig.getInstance().getListPermission().level))
|
||||
.then(argument("regex", StringArgumentType.greedyString())
|
||||
.executes(context -> executeList(context.getSource(), StringArgumentType.getString(context, "regex")))))
|
||||
.then(literal("hand")
|
||||
.requires(source -> source.hasPermissionLevel(ModConfig.getInstance().getHandPermission().level))
|
||||
.executes(context -> executeHand(context.getSource()))));
|
||||
}
|
||||
|
||||
private static int executeMatch(ServerCommandSource source, final String regexString) {
|
||||
try {
|
||||
int count = 0;
|
||||
|
||||
for (Identifier id : Registries.ITEM.getIds()) {
|
||||
Item item = Registries.ITEM.get(id);
|
||||
String itemName = Tools.getNameOf(item);
|
||||
if (Tools.itemMatch(itemName, regexString)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
final int total = count;
|
||||
|
||||
source.sendFeedback(() -> Text.literal(regexString + " match with " + total + " items"), false);
|
||||
return 1;
|
||||
} catch (PatternSyntaxException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int executeList(ServerCommandSource source, final String regexString) {
|
||||
try {
|
||||
int count = 0;
|
||||
String allMatchs = "";
|
||||
|
||||
for (Identifier id : Registries.ITEM.getIds()) {
|
||||
Item item = Registries.ITEM.get(id);
|
||||
String itemName = item.getName().getString().toLowerCase();
|
||||
if (Tools.itemMatch(itemName, regexString)) {
|
||||
count++;
|
||||
allMatchs += itemName + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
final int total = count;
|
||||
final String totalAllMatchs = allMatchs;
|
||||
|
||||
source.sendFeedback(() -> Text.literal("Full match list: \n" + totalAllMatchs + regexString + " match with " + total + " items\n"), false);
|
||||
return 1;
|
||||
} catch (PatternSyntaxException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int executeHand(ServerCommandSource source) {
|
||||
ServerPlayerEntity player = source.getPlayer();
|
||||
if (player != null) {
|
||||
ItemStack heldItem = player.getMainHandStack();
|
||||
if(heldItem != null) {
|
||||
source.sendFeedback(() -> Text.literal("Your main hand contain: '" + Tools.getNameOf(heldItem.getItem()) + "'" ), false);
|
||||
return 1;
|
||||
}
|
||||
source.sendFeedback(() -> Text.literal("No item found"), false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package systems.brn.regexinghoppers.config;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ModConfig {
|
||||
private static final String CONFIG_FILE = "regexhoppers.json";
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
private static String[] permissionsName = {
|
||||
"regexhoppers.command.test",
|
||||
"regexhoppers.command.list",
|
||||
"regexhoppers.command.hand"
|
||||
};
|
||||
|
||||
public ArrayList<PermissionEntry> permissions;
|
||||
|
||||
private static ModConfig INSTANCE;
|
||||
|
||||
public static ModConfig getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = load();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static ModConfig load() {
|
||||
Path configPath = FabricLoader.getInstance().getConfigDir().resolve(CONFIG_FILE);
|
||||
if (configPath.toFile().exists()) {
|
||||
try (Reader reader = new FileReader(configPath.toFile())) {
|
||||
ModConfig mconfig = GSON.fromJson(reader, ModConfig.class);
|
||||
mconfig.checkConfigFile();
|
||||
return mconfig;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
ModConfig config = new ModConfig();
|
||||
config.save();
|
||||
return config;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
// Should only run on the first time to generate the config file, or if file isn't loadable / broken
|
||||
this.permissions = new ArrayList<>();
|
||||
for (String permission : permissionsName) {
|
||||
this.permissions.add(new PermissionEntry(permission, 0));
|
||||
}
|
||||
|
||||
Path configPath = FabricLoader.getInstance().getConfigDir().resolve(CONFIG_FILE);
|
||||
try (Writer writer = new FileWriter(configPath.toFile())) {
|
||||
GSON.toJson(this, writer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkConfigFile() {
|
||||
if (this.permissions.size() < permissionsName.length) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
private PermissionEntry getIndex(int index) {
|
||||
this.checkConfigFile();
|
||||
return this.permissions.get(index);
|
||||
}
|
||||
|
||||
public PermissionEntry getTestPermission() {
|
||||
return this.getIndex(0);
|
||||
}
|
||||
|
||||
public PermissionEntry getListPermission() {
|
||||
return this.permissions.get(1);
|
||||
}
|
||||
|
||||
public PermissionEntry getHandPermission() {
|
||||
return this.permissions.get(2);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package systems.brn.regexinghoppers.config;
|
||||
|
||||
public class PermissionEntry {
|
||||
public String name;
|
||||
public int level;
|
||||
|
||||
public PermissionEntry(String name, int level) {
|
||||
this.name = name;
|
||||
this.level = level;
|
||||
}
|
||||
}
|
@ -3,40 +3,32 @@ package systems.brn.regexinghoppers.mixin;
|
||||
import net.minecraft.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import systems.brn.regexinghoppers.RegexingHoppers;
|
||||
import systems.brn.regexinghoppers.util.Tools;
|
||||
|
||||
@Mixin(HopperBlockEntity.class)
|
||||
public class RegexingHopperMixin {
|
||||
@Inject(method = "transfer(Lnet/minecraft/inventory/Inventory;Lnet/minecraft/inventory/Inventory;Lnet/minecraft/item/ItemStack;ILnet/minecraft/util/math/Direction;)Lnet/minecraft/item/ItemStack;",
|
||||
at = @At(value = "HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void processContainer(Inventory from, Inventory _to, ItemStack stack, int slot, Direction side, CallbackInfoReturnable<ItemStack> cir) {
|
||||
final String itemName = Registries.ITEM.getId(stack.getItem()).toString();
|
||||
if(from != null) {
|
||||
if (RegexingHoppers.shouldNotMove(from, itemName)) {
|
||||
cir.setReturnValue(stack);
|
||||
if(from instanceof HopperBlockEntity) {
|
||||
((HopperBlockEntityAccessor) from).setTransferCooldown(8);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@Inject(method = "canExtract", at = @At(value = "HEAD"), cancellable = true)
|
||||
private static void canExtractReplacer(Inventory outputInventory, Inventory inputInventory, ItemStack stack,
|
||||
int slot, Direction side, CallbackInfoReturnable<Boolean> cir) {
|
||||
|
||||
if (outputInventory == null) {
|
||||
return;
|
||||
}
|
||||
if(_to != null) {
|
||||
if (RegexingHoppers.shouldNotMove(_to, itemName)) {
|
||||
cir.setReturnValue(stack);
|
||||
if(_to instanceof HopperBlockEntity) {
|
||||
((HopperBlockEntityAccessor) _to).setTransferCooldown(8);
|
||||
}
|
||||
return;
|
||||
|
||||
if (outputInventory instanceof NamedScreenHandlerFactory factory) {
|
||||
String customName = factory.getDisplayName().getLiteralString();
|
||||
if (customName != null) {
|
||||
Boolean canExtract = Tools.shouldMove(outputInventory, stack.getItem());
|
||||
cir.setReturnValue(canExtract);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
56
src/main/java/systems/brn/regexinghoppers/util/Tools.java
Normal file
56
src/main/java/systems/brn/regexinghoppers/util/Tools.java
Normal file
@ -0,0 +1,56 @@
|
||||
package systems.brn.regexinghoppers.util;
|
||||
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||
import systems.brn.regexinghoppers.mixin.HopperBlockEntityAccessor;
|
||||
|
||||
public class Tools {
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("RegexingHoppers");
|
||||
|
||||
public static boolean itemMatch(String itemName, String regex) {
|
||||
return itemName.matches(regex);
|
||||
}
|
||||
|
||||
public static String getNameOf(Item item) {
|
||||
return item.getName().getString().toLowerCase();
|
||||
}
|
||||
|
||||
public static boolean shouldMove(Inventory hopper, Item item) {
|
||||
if (!(hopper instanceof NamedScreenHandlerFactory)) {
|
||||
// Log if hopper is not an instance of NamedScreenHandlerFactory
|
||||
LOGGER.debug("Hopper is not an instance of NamedScreenHandlerFactory.");
|
||||
return true;
|
||||
}
|
||||
NamedScreenHandlerFactory factory = (NamedScreenHandlerFactory) hopper;
|
||||
String customName = factory.getDisplayName().getLiteralString();
|
||||
if (customName == null || customName.isEmpty()) {
|
||||
// Log case when custom name is null or empty
|
||||
LOGGER.debug("Custom name is null or empty, not performing regex matching.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Test if this code still needed
|
||||
if (hopper instanceof HopperBlockEntity) {
|
||||
HopperBlockEntityAccessor hopperAccessor = (HopperBlockEntityAccessor) hopper;
|
||||
if (hopperAccessor.getTransferCooldown() > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return Tools.itemMatch(getNameOf(item), customName);
|
||||
} catch (PatternSyntaxException e) {
|
||||
// Log exception if regex pattern is invalid
|
||||
LOGGER.debug("Invalid regex pattern: {}", customName, e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user