Policies now provide their own checks to whether the player applies or not.

Added GlobalPolicy.

Revamped config YAML structure for more consistency.

Added ChatColor back to commands, should be correctly implemented in the future.

Build environment is now passed to plugin manifest.

Added workflow to automatically build releases.
This commit is contained in:
天クマ 2025-12-15 14:16:07 -03:00
commit 81bf64c463
21 changed files with 312 additions and 241 deletions

44
.github/workflows/release-build.yml vendored Normal file
View file

@ -0,0 +1,44 @@
name: Release Build
on:
release:
types: [created]
jobs:
build-and-release:
runs-on: ubuntu-latest
env:
NEMESIS_VERSION_NAME: "${{ github.ref_name }}"
NEMESIS_BUILD_CHANNEL: "production"
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
- name: Set up Java
uses: actions/setup-java@v5.1.0
with:
distribution: temurin
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
- name: Download CB1060
run: |
mkdir -p libs
curl -L -o libs/craftbukkit-1060.jar \
https://archive.org/download/craftbukkit1060/craftbukkit1-7-3%281060%29.jar
- name: Build with Gradle
run: ./gradlew buildAll
- name: Upload JARs to release
uses: softprops/action-gh-release@v2.5.0
with:
files: build/libs/*.jar
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
body: |
Automated build for release **${{ github.ref_name }}**.
Includes the following artifacts:
- `$(ls build/libs/*.jar | tr '\n' '\n' | sed 's/^/ - /')`

View file

@ -7,7 +7,7 @@
[![Portuguese Wiki Badge](https://img.shields.io/badge/Portuguese-White?style=flat-square&label=Wiki&color=black)](https://mgr.rf.gd/w/Eye_of_Nemesis)
> [!IMPORTANT]
> This project is in a early stage, please report any bug you find.
> This project is in an early stage, please report any bug you find.
# Eye of Nemesis
Eye of Nemesis is a plugin that allows server admins to write policies that will deny or allow (black/whitelist) players to do specific things based on the value of nodes.

View file

@ -4,7 +4,7 @@ plugins {
}
group = "io.github.adrianvic"
version = System.getenv("NEMESIS_VERSION_NAME") ?: "1.0.3-SNAPSHOT"
version = System.getenv("NEMESIS_VERSION_NAME") ?: "unknown"
repositories {
mavenCentral()
@ -77,7 +77,8 @@ mcVersions.forEach { ver ->
manifest {
attributes(
"Nemesis-Impl-Version" to ver
"Nemesis-Impl-Version" to ver,
"Nemesis-Environment" to (System.getenv("NEMESIS_BUILD_CHANNEL") ?: "dev")
)
}

View file

@ -4,7 +4,6 @@ import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.impl.commands.Eye;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParsers;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
@ -49,30 +48,13 @@ public class b1_7_3 implements Glimmer {
}
List<Policy> allPolicies = new ArrayList<>();
for (Map<?, ?> map : result) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (entry.getKey() instanceof String k && entry.getValue() instanceof List<?> v) {
List<Policy> parsed = PolicyParsers.get(k).parse(v);
allPolicies.addAll(parsed);
}
for (Map<?, ?> policyMap : result) {
if (policyMap.get("type") != null && policyMap.get("type") instanceof String type) {
allPolicies.add(PolicyParsers.get(type).parse(policyMap));
}
}
return allPolicies;
}
@Override
public List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies) {
List<Policy> result = new ArrayList<>();
for (Policy p : policies) {
if (p instanceof LocationPolicy lp) {
for (List<Box> boxList : lp.locations()) {
for (Box b : boxList) {
if (b.contains(entity.getLocation().toVector())) result.add(p);
}
}
}
}
return result;
return allPolicies;
}
@Override

View file

@ -0,0 +1,9 @@
Policies:
# NO SPACES
- name: "Block-illegal-items"
type: global # global / location / permission / list of types
allowList: false # Will deny anything that's not allowed by the nodes if set to true
nodes:
- useItem:
value:
- SAND

View file

@ -1,5 +1,8 @@
package io.github.adrianvic.nemesiseye;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.Location;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -7,6 +10,7 @@ import java.util.Map;
import java.util.regex.Pattern;
public class DataShifter {
public static boolean safeMatches(String expression, String against) {
String cleanPattern = expression.trim();
Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE);
@ -40,14 +44,38 @@ public class DataShifter {
return out;
}
public static List<Map<?, ?>> parseValueToListOfMaps(List<?> values) {
List<Map<?, ?>> result = new ArrayList<>();
public static List<Glimmer.Box> configLocationParser(Object rawLocations) {
Glimmer glim = Nemesis.getInstance().getGlimmer();
for (Object o : values) {
if (o instanceof Map<?, ?> raw) {
result.add(raw);
}
if (rawLocations == null) {
return List.of();
}
return result;
// Parsing locations
List<?> groups = rawLocations instanceof List ? (List<?>) rawLocations : List.of();
ArrayList<Glimmer.Box> boxes = new ArrayList<>(groups.size());
// Now iterate over regions
for (Object rObj : groups) {
Map<?, ?> region = (Map<?, ?>) rObj;
Map<?, ?> c1 = (Map<?, ?>) region.get("corner1");
Map<?, ?> c2 = (Map<?, ?>) region.get("corner2");
double x1 = ((Number) c1.get("x")).doubleValue();
double y1 = ((Number) c1.get("y")).doubleValue();
double z1 = ((Number) c1.get("z")).doubleValue();
double x2 = ((Number) c2.get("x")).doubleValue();
double y2 = ((Number) c2.get("y")).doubleValue();
double z2 = ((Number) c2.get("z")).doubleValue();
Location loc1 = new Location(glim.getWorlds().getFirst(), x1, y1, z1);
Location loc2 = new Location(glim.getWorlds().getFirst(), x2, y2, z2);
boxes.add(Glimmer.Box.of(loc1, loc2));
}
return boxes;
}
}

View file

@ -48,6 +48,10 @@ public class Validator {
public static List<Policy> getPoliciesForEntity(HumanEntity entity) {
List<Policy> ps = Config.getInstance().getPolicies();
return glim.getApplyingPoliciesForEntity(entity, ps);
List<Policy> result = new ArrayList<>();
for (Policy policy : ps) {
if (policy.applies(entity)) result.add(policy);
}
return result;
}
}

View file

@ -3,7 +3,7 @@ package io.github.adrianvic.nemesiseye.commands;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.commands.sub.*;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import io.github.adrianvic.nemesiseye.commands.Commands;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.*;
@ -16,10 +16,10 @@ public class EyeCore {
public boolean onCommand(CommandSender commandSender, Command command, String s, String [] strings) {
if (strings.length == 0) {
glim.sendMessage(commandSender, """
Eye of Nemesis version %s
%sEye of Nemesis%s version %s%s%s
Usage: '/eye <command>'
Use '/eye help' for a list of available commands
""".formatted(Nemesis.getInstance().getDescription().getVersion()));
""".formatted(ChatColor.AQUA, ChatColor.WHITE, ChatColor.GRAY, Nemesis.getInstance().getDescription().getVersion(), ChatColor.WHITE));
} else {
Subcommand sub = Commands.get(strings[0].toLowerCase());
if (sub == null) {

View file

@ -4,6 +4,7 @@ import io.github.adrianvic.nemesiseye.Config;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
@ -18,11 +19,11 @@ public class PolicyInfo implements Subcommand {
for (Policy policy : policies) {
if (policy.name().equals(strings[0])) {
glim.sendMessage(commandSender, String.format("""
Showing info for policy "%s":
Showing info for policy %s%s%s:
Type: %s
Nodes: %s
%s
""", policy.name(), "location", policy.nodes().size(), policy.allowlist() ? "Is allowlist" : "Is blacklist"));
""", ChatColor.GREEN, policy.name(), ChatColor.WHITE, "location", policy.nodes().size(), policy.allowlist() ? "Is allowlist" : "Is blacklist"));
}
}
return true;

View file

@ -1,13 +1,13 @@
package io.github.adrianvic.nemesiseye.policy;
import org.bukkit.entity.HumanEntity;
import java.util.List;
public interface Policy {
String name();
List<PolicyNode> nodes();
boolean allowlist();
boolean applies(HumanEntity entity);
default PolicyParser getParser() {
return PolicyParsers.get("");
}
}

View file

@ -14,15 +14,17 @@ public record PolicyNode(String type, List<Object> values, boolean isWhitelist)
List<Object> values = new ArrayList<>();
Object val = entry.getValue();
if (val instanceof String s) {
values.add(s);
} else if (val instanceof List<?> l) {
values.addAll(l);
} else if (val instanceof Map<?,?> map) {
values.add(map);
}
if (val instanceof Map<?,?> valMap) {
if (valMap.get("values") instanceof String s) {
values.add(s);
} else if (valMap.get("values") instanceof List<?> l) {
values.addAll(l);
} else if (valMap.get("values") instanceof Map<?,?> map) {
values.add(map);
}
nodes.add(new PolicyNode(type, values, isWhitelist));
nodes.add(new PolicyNode(type, values, isWhitelist));
}
}
}
return nodes;

View file

@ -1,7 +1,7 @@
package io.github.adrianvic.nemesiseye.policy;
import java.util.List;
import java.util.Map;
public interface PolicyParser {
List<Policy> parse(List<?> raw);
Policy parse(Map<?, ?> raw);
}

View file

@ -1,5 +1,6 @@
package io.github.adrianvic.nemesiseye.policy;
import io.github.adrianvic.nemesiseye.policy.parser.GlobalPolicyParser;
import io.github.adrianvic.nemesiseye.policy.parser.LocationPolicyParser;
import java.util.HashMap;
@ -9,7 +10,8 @@ public class PolicyParsers {
private static final Map<String, PolicyParser> handlers = new HashMap<>();
static {
handlers.put("Location", new LocationPolicyParser());
handlers.put("location", new LocationPolicyParser());
handlers.put("global", new GlobalPolicyParser());
}
public static PolicyParser get(String type) {

View file

@ -0,0 +1,32 @@
package io.github.adrianvic.nemesiseye.policy.parser;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import io.github.adrianvic.nemesiseye.policy.PolicyParser;
import io.github.adrianvic.nemesiseye.policy.policies.GlobalPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class GlobalPolicyParser implements PolicyParser {
@Override
public Policy parse(Map<?, ?> raw) {
boolean allowlist = (boolean) raw.get("allowList");
String name = (String) raw.get("name");
// Nodes
Object rawNodes = raw.get("nodes");
List<Map<String, Object>> nodeList = new ArrayList<>();
if (rawNodes instanceof List<?> list) {
for (Object o : list) {
if (o instanceof Map<?, ?> map)
nodeList.add((Map<String, Object>) map);
}
}
List<PolicyNode> nodes = PolicyNode.parseNodes(nodeList, allowlist);
return new GlobalPolicy(name, nodes, allowlist);
}
}

View file

@ -5,7 +5,6 @@ import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.*;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.Location;
import java.util.ArrayList;
import java.util.List;
@ -14,16 +13,12 @@ import java.util.Map;
public class LocationPolicyParser implements PolicyParser {
private Glimmer glim = Nemesis.getInstance().getGlimmer();
public List<Policy> parse(List<?> raw) {
List<Policy> out = new ArrayList<>(raw.size());
List<Map<?, ?>> parsedRawMap = DataShifter.parseValueToListOfMaps(raw);
for (Map<?, ?> m : parsedRawMap) {
String name = (String) m.get("name");
boolean allowlist = Boolean.TRUE.equals(m.get("allowList"));
public Policy parse(Map<?, ?> raw) {
String name = (String) raw.get("name");
boolean allowlist = Boolean.TRUE.equals(raw.get("allowList"));
// Nodes
Object rawNodes = m.get("nodes");
Object rawNodes = raw.get("nodes");
List<Map<String, Object>> nodeList = new ArrayList<>();
if (rawNodes instanceof List<?> list) {
for (Object o : list) {
@ -34,37 +29,8 @@ public class LocationPolicyParser implements PolicyParser {
List<PolicyNode> nodes = PolicyNode.parseNodes(nodeList, allowlist);
// Parsing locations
List<ArrayList<Glimmer.Box>> locations = new ArrayList<>();
List<Glimmer.Box> locations = DataShifter.configLocationParser(raw.get("locations"));
Object rawLocations = m.get("locations");
List<?> groups = rawLocations instanceof List ? (List<?>) rawLocations : List.of();
ArrayList<Glimmer.Box> boxes = new ArrayList<>(groups.size());
// Now iterate over regions
for (Object rObj : groups) {
Map<?, ?> region = (Map<?, ?>) rObj;
Map<?, ?> c1 = (Map<?, ?>) region.get("corner1");
Map<?, ?> c2 = (Map<?, ?>) region.get("corner2");
double x1 = ((Number) c1.get("x")).doubleValue();
double y1 = ((Number) c1.get("y")).doubleValue();
double z1 = ((Number) c1.get("z")).doubleValue();
double x2 = ((Number) c2.get("x")).doubleValue();
double y2 = ((Number) c2.get("y")).doubleValue();
double z2 = ((Number) c2.get("z")).doubleValue();
Location loc1 = new Location(glim.getWorlds().getFirst(), x1, y1, z1);
Location loc2 = new Location(glim.getWorlds().getFirst(), x2, y2, z2);
boxes.add(Glimmer.Box.of(loc1, loc2));
}
locations.add(boxes);
out.add(new LocationPolicy(name, locations, nodes, allowlist));
}
return out;
return new LocationPolicy(name, locations, nodes, allowlist);
}
}

View file

@ -0,0 +1,13 @@
package io.github.adrianvic.nemesiseye.policy.policies;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.entity.HumanEntity;
import java.util.List;
public record GlobalPolicy(String name, List<PolicyNode> nodes, boolean allowlist) implements Policy {
public boolean applies(HumanEntity entity) {
return true;
}
}

View file

@ -3,8 +3,16 @@ package io.github.adrianvic.nemesiseye.policy.policies;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity;
import java.util.ArrayList;
import java.util.List;
public record LocationPolicy(String name, List<ArrayList<Glimmer.Box>> locations, List<PolicyNode> nodes, boolean allowlist) implements Policy {}
public record LocationPolicy(String name, List<Glimmer.Box> locations, List<PolicyNode> nodes, boolean allowlist) implements Policy {
@Override
public boolean applies(HumanEntity entity) {
for (Glimmer.Box box : locations) {
if (box.contains(entity.getLocation().toVector())) return true;
}
return false;
}
}

View file

@ -20,7 +20,6 @@ public interface Glimmer {
// Configuration
File loadConfigFile();
List<Policy> loadPoliciesFromFile(File file);
List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies);
// Items
boolean hasItemMeta(ItemStack item);
@ -54,7 +53,6 @@ public interface Glimmer {
&& v.getZ() >= z1 && v.getZ() <= z2;
}
public static Box of(Location loc1, Location loc2) { return new Box(loc1.getX(), loc1.getY(), loc1.getZ(), loc2.getX(), loc2.getY(), loc2.getZ()); }
}

View file

@ -1,121 +0,0 @@
Policies:
- Location:
# NO SPACES
- name: "Beta-1.7.3-items-only"
# Will deny anything that's not allowed by the nodes if set to true
allowList: true
nodes:
- useItem:
- AIR
- STONE
- COBBLESTONE
- "^(OAK|SPRUCE|BIRCH)_(LOG|SAPLING|PLANKS|LEAVES)$"
- "^(DIAMOND|GOLD|IRON|COAL|LAPIS|REDSTONE)_ORE$"
- "^(DIAMOND|GOLD|IRON|LAPIS)_BLOCK$"
- GRAVEL
- BEDROCK
- SAND
- SPONGE
- WET_SPONGE
- GLASS
- LAPIS_LAZULI
- COBWEB
- PISTON
- STICKY_PISTON
- GRASS
- DISPENSER
- NOTE_BLOCK
- SANDSTONE
- RED_BED
- "^(POWERED|DETECTOR)_RAIL$"
- RAIL
- SHORT_GRASS
- "^(WHITE|BLACK|GREEN|YELLOW|PINK|PURPLE|CYAN|BLUE|RED|LIME|BROWN|LIGHT_GRAY|GRAY)_(WOOL|DYE)$"
- POPPY
- DANDELION
- "^(RED|BROWN)_MUSHROOM$"
- "^(OAK|COBBLESTONE)_SLAB$"
- BRICK_BLOCK
- TNT
- BOOKSHELF
- OBSIDIAN
- MOSSY_COBBLESTONE
- TORCH
- SPAWNER
- REDSTONE
- CHEST
- CRAFTING_TABLE
- FARMLAND
- FURNACE
- SIGN
- LADDER
- "^(COBBLESTONE|OAK)_STAIRS$"
- LEVER
- "^(OAK|STONE)_PRESSURE_PLATE$"
- "^(OAK|IRON)_DOOR$"
- BLUE_ICE
- REDSTONE_TORCH
- STONE_BUTTON
- SNOW
- SNOW_BLOCK
- CLAY
- SUGAR_CANE
- JUKEBOX
- OAK_FENCE
- PUMPKIN
- NETHERRACK
- SOUL_SAND
- GLOWSTONE
- JACK_O_LANTERN
- CAKE
- REPEATER
- OAK_TRAPDOOR
- "^(IRON|STONE|DIAMOND|WOODEN|GOLDEN)_(SHOVEL|AXE|PICKAXE|SWORD|HOE)$"
- "^(IRON|LEATHER|DIAMOND|GOLDEN)_(HELMET|CHESTPLATE|LEGGINGS|BOOTS)$"
- STICK
- BOWL
- MUSHROOM_STEW
- FEATHER
- STRING
- GUNPOWDER
- WHEAT_SEEDS
- WHEAT
- FLINT
- FLINT_AND_STEEL
- PORKCHOP
- "^(COOKED|RAW)_(PORKCHOP|FISH)$"
- PAINTING
- GOLDEN_APPLE
- BUCKET
- "^(LAVA|MILK|WATER)_BUCKET$"
- MINECART
- SADDLE
- SNOWBALL
- OAK_BOAT
- LEATHER
- "^(FURNACE|CHEST)_MINECART$"
- EGG
- BOOK
- PAPER
- BRICK
- SLIME_BALL
- COMPASS
- FISHING_ROD
- CLOCK
- GLOWSTONE_DUST
- INK_SACw
- BONE_MEAL
- SUGAR
- COOKIE
- MAP
- FILLED_MAP
- SHEARS
- MUSIC_DISK_CAT
- MUSIC_DISK_13
- DIRT
- BREAD
- useEnchantment:
"theresnoenchantmentwiththisname": "3"
locations:
- corner1: { x: 2100, y: 256, z: 1400 }
corner2: { x: 1000, y: -64, z: 2200 }

View file

@ -4,7 +4,6 @@ import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.impl.commands.Eye;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParsers;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.Bukkit;
import org.bukkit.World;
@ -45,32 +44,14 @@ public class r1_21 implements Glimmer {
List<Map<?, ?>> rawPolicies = config.getMapList("Policies");
List<Policy> allPolicies = new ArrayList<>();
for (Map<?, ?> map : rawPolicies) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (entry.getKey() instanceof String k && entry.getValue() instanceof List<?> v) {
List<Policy> parsed = PolicyParsers.get(k).parse(v);
allPolicies.addAll(parsed);
}
}
}
return allPolicies;
}
@Override
public List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies) {
List<Policy> applyingLPS = new ArrayList<>();
for (Policy p : policies) {
if (p instanceof LocationPolicy lp) {
for (ArrayList<Box> boxes : lp.locations()) {
for (Box box : boxes) {
if (box.contains(entity.getLocation().toVector())) {
applyingLPS.add(lp);
}
}
}
for (Map<?, ?> policyMap : rawPolicies) {
if (policyMap.get("type") != null && policyMap.get("type") instanceof String type) {
allPolicies.add(PolicyParsers.get(type).parse(policyMap));
}
}
return applyingLPS;
return allPolicies;
}
@Override

View file

@ -0,0 +1,121 @@
Policies:
# NO SPACES
- name: "Beta-1.7.3-items-only"
type: "location"
allowList: true # Will deny anything that's not allowed by the nodes if set to true
locations:
- corner1: { x: 2100, y: 256, z: 1400 }
corner2: { x: 1000, y: -64, z: 2200 }
nodes:
- useItem:
values:
- AIR
- STONE
- COBBLESTONE
- "^(OAK|SPRUCE|BIRCH)_(LOG|SAPLING|PLANKS|LEAVES)$"
- "^(DIAMOND|GOLD|IRON|COAL|LAPIS|REDSTONE)_ORE$"
- "^(DIAMOND|GOLD|IRON|LAPIS)_BLOCK$"
- GRAVEL
- BEDROCK
- SAND
- SPONGE
- WET_SPONGE
- GLASS
- LAPIS_LAZULI
- COBWEB
- PISTON
- STICKY_PISTON
- GRASS
- DISPENSER
- NOTE_BLOCK
- SANDSTONE
- RED_BED
- "^(POWERED|DETECTOR)_RAIL$"
- RAIL
- SHORT_GRASS
- "^(WHITE|BLACK|GREEN|YELLOW|PINK|PURPLE|CYAN|BLUE|RED|LIME|BROWN|LIGHT_GRAY|GRAY)_(WOOL|DYE)$"
- POPPY
- DANDELION
- "^(RED|BROWN)_MUSHROOM$"
- "^(OAK|COBBLESTONE)_SLAB$"
- BRICK_BLOCK
- TNT
- BOOKSHELF
- OBSIDIAN
- MOSSY_COBBLESTONE
- TORCH
- SPAWNER
- REDSTONE
- CHEST
- CRAFTING_TABLE
- FARMLAND
- FURNACE
- SIGN
- LADDER
- "^(COBBLESTONE|OAK)_STAIRS$"
- LEVER
- "^(OAK|STONE)_PRESSURE_PLATE$"
- "^(OAK|IRON)_DOOR$"
- BLUE_ICE
- REDSTONE_TORCH
- STONE_BUTTON
- SNOW
- SNOW_BLOCK
- CLAY
- SUGAR_CANE
- JUKEBOX
- OAK_FENCE
- PUMPKIN
- NETHERRACK
- SOUL_SAND
- GLOWSTONE
- JACK_O_LANTERN
- CAKE
- REPEATER
- OAK_TRAPDOOR
- "^(IRON|STONE|DIAMOND|WOODEN|GOLDEN)_(SHOVEL|AXE|PICKAXE|SWORD|HOE)$"
- "^(IRON|LEATHER|DIAMOND|GOLDEN)_(HELMET|CHESTPLATE|LEGGINGS|BOOTS)$"
- STICK
- BOWL
- MUSHROOM_STEW
- FEATHER
- STRING
- GUNPOWDER
- WHEAT_SEEDS
- WHEAT
- FLINT
- FLINT_AND_STEEL
- PORKCHOP
- "^(COOKED|RAW)_(PORKCHOP|FISH)$"
- PAINTING
- GOLDEN_APPLE
- BUCKET
- "^(LAVA|MILK|WATER)_BUCKET$"
- MINECART
- SADDLE
- SNOWBALL
- OAK_BOAT
- LEATHER
- "^(FURNACE|CHEST)_MINECART$"
- EGG
- BOOK
- PAPER
- BRICK
- SLIME_BALL
- COMPASS
- FISHING_ROD
- CLOCK
- GLOWSTONE_DUST
- INK_SACw
- BONE_MEAL
- SUGAR
- COOKIE
- MAP
- FILLED_MAP
- SHEARS
- MUSIC_DISK_CAT
- MUSIC_DISK_13
- DIRT
- BREAD
- useEnchantment:
"flying bananas": "0"