Reworked oredict storage bus

have a recipe now
expression matcher and validator from GTCEU by brachy84
This commit is contained in:
PrototypeTrousers
2022-01-20 22:43:18 -03:00
parent df28a5520f
commit a14d160a07
8 changed files with 635 additions and 224 deletions
@@ -13,6 +13,7 @@ import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.PacketSwitchGuis;
import appeng.core.sync.packets.PacketValueConfig;
import appeng.parts.misc.PartOreDicStorageBus;
import appeng.util.item.OreDictFilterMatcher;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.entity.player.InventoryPlayer;
@@ -26,7 +27,7 @@ public class GuiPartOreDictStorageBus extends AEBaseGui
private GuiTabButton priority;
private GuiImgButton partition;
private static final Pattern ORE_DICTIONARY_FILTER = Pattern.compile( "\\*?[a-zA-Z0-9_|\\\\+(){,}?!=\\-\\[:\\]&^$]*\\*?" );
private static final Pattern ORE_DICTIONARY_FILTER = Pattern.compile( "[(!]* *[0-9a-zA-Z*]* *\\)*( *[&|^]? *[(!]* *[0-9a-zA-Z*]* *\\)*)*" );
private MEGuiTextField searchFieldInputs;
public GuiPartOreDictStorageBus( final InventoryPlayer inventoryPlayer, final PartOreDicStorageBus te )
@@ -103,6 +104,7 @@ public class GuiPartOreDictStorageBus extends AEBaseGui
if( !searchFieldInputs.isFocused() && wasFocused )
{
searchFieldInputs.setText( OreDictFilterMatcher.validateExp( searchFieldInputs.getText() ) );
NetworkHandler.instance().sendToServer( new PacketValueConfig( "OreDictStorageBus.save", searchFieldInputs.getText() ) );
}
@@ -125,8 +127,11 @@ public class GuiPartOreDictStorageBus extends AEBaseGui
public void drawFG( int offsetX, int offsetY, int mouseX, int mouseY )
{
this.fontRenderer.drawString( this.getGuiDisplayName( GuiText.OreDictStorageBus.getLocal() ), 8, 6, 4210752 );
this.fontRenderer.drawString( this.searchFieldInputs.getText().length() + " / " + this.searchFieldInputs.getMaxStringLength(), 8, 36, 4210752 );
this.fontRenderer.drawSplitString( "Supports regex. Or use * as a wildcard", 6, 48, 174, 4210752 );
this.fontRenderer.drawString( this.searchFieldInputs.getText().length() + " / " + this.searchFieldInputs.getMaxStringLength(), 120, 36, 4210752 );
this.fontRenderer.drawString( "& = AND " + "| = OR", 8, 36, 4210752 );
this.fontRenderer.drawString( "^ = XOR " + "! = NOT", 8, 48, 4210752 );
this.fontRenderer.drawString( "() for priority " + "* for wildcard", 8, 60, 4210752 );
this.fontRenderer.drawString( "Ex.: *Redstone*&!dustRedstone", 8, 72, 4210752 );
}
@Override
@@ -1,27 +1,18 @@
package appeng.container.implementations;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.FuzzyMode;
import appeng.api.config.Settings;
import appeng.api.config.StorageFilter;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.container.AEBaseContainer;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.PacketValueConfig;
import appeng.parts.misc.PartOreDicStorageBus;
import appeng.util.Platform;
import appeng.util.helpers.ItemHandlerUtil;
import appeng.util.item.AEItemStack;
import appeng.util.item.OreReference;
import appeng.util.iterators.NullIterator;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.oredict.OreDictionary;
import java.io.IOException;
@@ -103,7 +94,7 @@ public class ContainerPartOreDictStorageBus extends AEBaseContainer
{
try
{
NetworkHandler.instance().sendTo( new PacketValueConfig( "OreDictStorageBus.sendRegex", part.getOreMatch() ), (EntityPlayerMP) getInventoryPlayer().player );
NetworkHandler.instance().sendTo( new PacketValueConfig( "OreDictStorageBus.sendRegex", part.getOreExp() ), (EntityPlayerMP) getInventoryPlayer().player );
}
catch( IOException e )
{
@@ -40,8 +40,7 @@ public class PartOreDicStorageBus extends PartStorageBus
@PartModels
public static final IPartModel MODELS_HAS_CHANNEL = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/storage_bus_has_channel" ) );
public String oreMatch;
public String oreExp = "";
OreDictPriorityList<IAEItemStack> priorityList;
public PartOreDicStorageBus( ItemStack is )
@@ -53,15 +52,15 @@ public class PartOreDicStorageBus extends PartStorageBus
public void readFromNBT( NBTTagCompound data )
{
super.readFromNBT( data );
this.oreMatch = data.getString( "oreMatch" );
this.priorityList = new OreDictPriorityList<>( OreHelper.INSTANCE.getMatchingOre( oreMatch ), oreMatch );
this.oreExp = data.getString( "oreMatch" );
this.priorityList = new OreDictPriorityList<>( OreHelper.INSTANCE.getMatchingOre( oreExp ), oreExp );
}
@Override
public void writeToNBT( NBTTagCompound data )
{
super.writeToNBT( data );
data.setString( "oreMatch", getOreMatch() );
data.setString( "oreMatch", getOreExp() );
}
@Override
@@ -172,21 +171,22 @@ public class PartOreDicStorageBus extends PartStorageBus
return this.handler;
}
public String getOreMatch()
public String getOreExp()
{
if( this.oreMatch == null )
if( this.oreExp == null )
{
return "";
}
return oreMatch;
return oreExp;
}
public void saveOreMatch( String oreMatch )
{
if( !this.oreMatch.equals( oreMatch ) )
if( !this.oreExp.equals( oreMatch ) )
{
this.oreMatch = oreMatch;
this.priorityList = new OreDictPriorityList<>( OreHelper.INSTANCE.getMatchingOre( oreMatch ), oreMatch );
this.oreExp = oreMatch;
this.priorityList = new OreDictPriorityList<>( OreHelper.INSTANCE.getMatchingOre( oreExp ), oreExp );
if( this.handler != null )
{
handler.setPartitionList( this.priorityList );
@@ -0,0 +1,383 @@
package appeng.util.item;
import java.util.ArrayList;
import java.util.List;
/**
* @author brachy84
* @butcherer PrototypeTrousers
*/
public class OreDictFilterMatcher
{
/**
* Parses the given expression and creates a List.
*
* @param expression expr to parse
* @return match rule list
*/
public static List<MatchRule> parseExpression( String expression )
{
List<MatchRule> rules = new ArrayList<>();
parseExpression( rules, expression );
return rules;
}
/**
* Parses the given expression and puts them into the given list.
*
* @param rules list to fill
* @param expression expr to parse
* @return the position of the expr. Is only relevant for sub rules
*/
public static int parseExpression( List<MatchRule> rules, String expression )
{
rules.clear();
StringBuilder builder = new StringBuilder();
for( int i = 0; i < expression.length(); i++ )
{
char c = expression.charAt( i );
if( c == ' ' )
{
continue;
}
if( c == '(' )
{
List<MatchRule> subRules = new ArrayList<>();
i = parseExpression( subRules, expression.substring( i + 1 ) ) + i + 1;
rules.add( MatchRule.group( subRules, builder.toString() ) );
builder = new StringBuilder();
}
else
{
switch ( c )
{
case '&':
rules.add( new MatchRule( builder.toString() ) );
rules.add( new MatchRule( MatchLogic.AND ) );
builder = new StringBuilder();
break;
case '|':
rules.add( new MatchRule( builder.toString() ) );
rules.add( new MatchRule( MatchLogic.OR ) );
builder = new StringBuilder();
break;
case '^':
rules.add( new MatchRule( builder.toString() ) );
rules.add( new MatchRule( MatchLogic.XOR ) );
builder = new StringBuilder();
break;
case ')':
rules.add( new MatchRule( builder.toString() ) );
return i + 1;
default:
builder.append( c );
}
}
}
if( builder.length() > 0 )
{
rules.add( new MatchRule( builder.toString() ) );
}
return expression.length();
}
/**
* Matches the given string against a list of rules.
* The string does not have to be an oreDict.
*
* @param rules to check against
* @param oreDict string to check
* @return if the string matches the rules
*/
public static boolean matches( List<MatchRule> rules, String oreDict )
{
boolean first = true;
boolean lastResult = false;
MatchLogic lastLogic = null;
for( MatchRule rule : rules )
{
if( lastLogic == null )
{
if( rule.logic == MatchLogic.AND || rule.logic == MatchLogic.OR || rule.logic == MatchLogic.XOR )
{
lastLogic = rule.logic;
continue;
}
}
if( lastLogic != null || first )
{
if( lastLogic != null )
{
switch ( lastLogic )
{
case AND:
{
if( !lastResult )
{
return false;
}
break;
}
case OR:
{
if( lastResult )
{
return true;
}
break;
}
}
}
boolean newResult;
if( rule.isGroup() )
{
newResult = rule.logic == MatchLogic.NOT ^ matches( rule.subRules, oreDict );
}
else
{
newResult = matches( rule, oreDict );
}
if( lastLogic == MatchLogic.XOR )
{
if( lastResult == newResult )
{
return false;
}
}
lastLogic = null;
lastResult = newResult;
first = false;
}
}
return lastResult;
}
private static boolean matches( MatchRule rule, String oreDict )
{
String filter = rule.expression;
if( filter.equals( "*" ) )
{
return true;
}
boolean startWild = filter.startsWith( "*" ), endWild = filter.endsWith( "*" );
if( startWild )
{
filter = filter.substring( 1 );
}
String[] parts = filter.split( "\\*+" );
return ( rule.logic == MatchLogic.NOT ) ^ matches( parts, oreDict, startWild, endWild );
}
private static boolean matches( String[] filter, String oreDict, boolean startWild, boolean endWild )
{
String lastlastPart = filter[0];
String lastPart = filter[0];
int index = oreDict.indexOf( lastPart );
if( ( !startWild && index != 0 ) || index < 0 )
{
return false;
}
boolean didGoBack = false;
for( int i = 1; i < filter.length; i++ )
{
String part = filter[i];
int newIndex = oreDict.indexOf( part, index + lastPart.length() );
if( newIndex < 0 )
{
if( i > 1 && !didGoBack )
{
i -= 2;
lastPart = lastlastPart;
didGoBack = true;
continue;
}
return false;
}
lastlastPart = lastPart;
lastPart = part;
index = newIndex;
if( didGoBack )
{
didGoBack = false;
}
}
if( endWild || lastPart.length() + index == oreDict.length() )
{
return true;
}
for( int i = filter.length - 1; i < filter.length; i++ )
{
String part = filter[i];
int newIndex = oreDict.indexOf( part, index + lastPart.length() );
if( newIndex < 0 )
{
if( i > 1 && !didGoBack )
{
i -= 2;
lastPart = lastlastPart;
didGoBack = true;
continue;
}
return false;
}
lastlastPart = lastPart;
lastPart = part;
index = newIndex;
if( didGoBack )
{
didGoBack = false;
}
}
return lastPart.length() + index == oreDict.length();
}
public static String validateExp( String input )
{
// remove all operators that are double
input = input.replaceAll( "\\*{2,}", "*" );
input = input.replaceAll( "&{2,}", "&" );
input = input.replaceAll( "\\|{2,}", "|" );
input = input.replaceAll( "!{2,}", "!" );
input = input.replaceAll( "\\^{2,}", "^" );
input = input.replaceAll( " {2,}", " " );
// move ( and ) so it doesn't create invalid expressions f.e. xxx (& yyy) => xxx & (yyy)
// append or prepend ( and ) if the amount is not equal
StringBuilder builder = new StringBuilder();
int unclosed = 0;
char last = ' ';
for( int i = 0; i < input.length(); i++ )
{
char c = input.charAt( i );
if( c == ' ' )
{
if( last != '(' )
{
builder.append( " " );
}
continue;
}
if( c == '(' )
{
unclosed++;
}
else if( c == ')' )
{
unclosed--;
if( last == '&' || last == '|' || last == '^' )
{
int l = builder.lastIndexOf( " " + last );
int l2 = builder.lastIndexOf( "" + last );
builder.insert( l == l2 - 1 ? l : l2, ")" );
continue;
}
if( i > 0 && builder.charAt( builder.length() - 1 ) == ' ' )
{
builder.deleteCharAt( builder.length() - 1 );
}
}
else if( ( c == '&' || c == '|' || c == '^' ) && last == '(' )
{
builder.deleteCharAt( builder.lastIndexOf( "(" ) );
builder.append( c ).append( " (" );
continue;
}
builder.append( c );
last = c;
}
if( unclosed > 0 )
{
for( int i = 0; i < unclosed; i++ )
{
builder.append( ")" );
}
}
else if( unclosed < 0 )
{
unclosed = -unclosed;
for( int i = 0; i < unclosed; i++ )
{
builder.insert( 0, "(" );
}
}
input = builder.toString();
input = input.replaceAll( " {2,}", " " );
return input;
}
public static class MatchRule
{
public final MatchLogic logic;
public final String expression;
private final List<MatchRule> subRules;
private MatchRule( MatchLogic logic, String expression, List<MatchRule> subRules )
{
if( expression.startsWith( "!" ) )
{
logic = MatchLogic.NOT;
expression = expression.substring( 1 );
}
this.logic = logic;
this.expression = expression;
this.subRules = subRules;
}
public MatchRule( MatchLogic logic, String expression )
{
this( logic, expression, null );
}
public MatchRule( MatchLogic logic )
{
this( logic, "" );
}
public MatchRule( String expression )
{
this( MatchLogic.ANY, expression );
}
public static MatchRule not( String expression, boolean not )
{
return new MatchRule( not ? MatchLogic.NOT : MatchLogic.ANY, expression );
}
public static MatchRule group( List<MatchRule> subRules, String expression )
{
MatchLogic logic = expression.startsWith( "!" ) ? MatchLogic.NOT : MatchLogic.ANY;
return new MatchRule( logic, "", subRules );
}
public boolean isGroup()
{
return subRules != null;
}
}
public enum MatchLogic
{
OR,
AND,
XOR,
NOT,
ANY
}
}
+164 -200
View File
@@ -19,244 +19,208 @@
package appeng.util.item;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import appeng.api.storage.data.IAEItemStack;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.OreDictionary;
import appeng.api.storage.data.IAEItemStack;
import java.util.*;
public class OreHelper
{
public static final OreHelper INSTANCE = new OreHelper();
public static final OreHelper INSTANCE = new OreHelper();
/**
* A local cache to speed up OreDictionary lookups.
*/
private final LoadingCache<String, List<ItemStack>> oreDictCache = CacheBuilder.newBuilder().build( new CacheLoader<String, List<ItemStack>>()
{
@Override
public List<ItemStack> load( final String oreName )
{
return OreDictionary.getOres( oreName );
}
} );
/**
* A local cache to speed up OreDictionary lookups.
*/
private final LoadingCache<String, List<ItemStack>> oreDictCache = CacheBuilder.newBuilder().build( new CacheLoader<String, List<ItemStack>>()
{
@Override
public List<ItemStack> load( final String oreName )
{
return OreDictionary.getOres( oreName );
}
} );
private final Map<ItemRef, OreReference> references = new HashMap<>();
private final Map<ItemRef, OreReference> references = new HashMap<>();
/**
* Test if the passed {@link ItemStack} is an ore.
*
* @param itemStack the itemstack to test
*
* @return true if an ore entry exists, false otherwise
*/
public Optional<OreReference> getOre( final ItemStack itemStack )
{
final ItemRef ir = new ItemRef( itemStack );
/**
* Test if the passed {@link ItemStack} is an ore.
*
* @param itemStack the itemstack to test
* @return true if an ore entry exists, false otherwise
*/
public Optional<OreReference> getOre( final ItemStack itemStack )
{
final ItemRef ir = new ItemRef( itemStack );
if( !this.references.containsKey( ir ) )
{
final OreReference ref = new OreReference();
final Collection<Integer> ores = ref.getOres();
final Collection<String> set = ref.getEquivalents();
if( !this.references.containsKey( ir ) )
{
final OreReference ref = new OreReference();
final Collection<Integer> ores = ref.getOres();
final Collection<String> set = ref.getEquivalents();
final Set<String> toAdd = new HashSet<>();
final Set<String> toAdd = new HashSet<>();
for( final String ore : OreDictionary.getOreNames() )
{
// skip ore if it is a match already or null.
if( ore == null || toAdd.contains( ore ) )
{
continue;
}
for( final String ore : OreDictionary.getOreNames() )
{
// skip ore if it is a match already or null.
if( ore == null || toAdd.contains( ore ) )
{
continue;
}
for( final ItemStack oreItem : this.oreDictCache.getUnchecked( ore ) )
{
if( OreDictionary.itemMatches( oreItem, itemStack, false ) )
{
toAdd.add( ore );
break;
}
}
}
for( final ItemStack oreItem : this.oreDictCache.getUnchecked( ore ) )
{
if( OreDictionary.itemMatches( oreItem, itemStack, false ) )
{
toAdd.add( ore );
break;
}
}
}
for( final String ore : toAdd )
{
set.add( ore );
ores.add( OreDictionary.getOreID( ore ) );
}
for( final String ore : toAdd )
{
set.add( ore );
ores.add( OreDictionary.getOreID( ore ) );
}
if( !set.isEmpty() )
{
this.references.put( ir, ref );
}
else
{
this.references.put( ir, null );
}
}
if( !set.isEmpty() )
{
this.references.put( ir, ref );
}
else
{
this.references.put( ir, null );
}
}
return Optional.ofNullable( this.references.get( ir ) );
}
return Optional.ofNullable( this.references.get( ir ) );
}
boolean sameOre( final AEItemStack aeItemStack, final IAEItemStack is )
{
final OreReference a = aeItemStack.getOre().orElse( null );
final OreReference b = ( (AEItemStack) is ).getOre().orElse( null );
boolean sameOre( final AEItemStack aeItemStack, final IAEItemStack is )
{
final OreReference a = aeItemStack.getOre().orElse( null );
final OreReference b = ( (AEItemStack) is ).getOre().orElse( null );
return this.sameOre( a, b );
}
return this.sameOre( a, b );
}
public boolean sameOre( final OreReference a, final OreReference b )
{
if( a == null || b == null )
{
return false;
}
public boolean sameOre( final OreReference a, final OreReference b )
{
if( a == null || b == null )
{
return false;
}
if( a == b )
{
return true;
}
if( a == b )
{
return true;
}
final Collection<Integer> bOres = b.getOres();
for( final Integer ore : a.getOres() )
{
if( bOres.contains( ore ) )
{
return true;
}
}
final Collection<Integer> bOres = b.getOres();
for( final Integer ore : a.getOres() )
{
if( bOres.contains( ore ) )
{
return true;
}
}
return false;
}
return false;
}
boolean sameOre( final AEItemStack aeItemStack, final ItemStack o )
{
return aeItemStack.getOre().map( a ->
{
for( final String oreName : a.getEquivalents() )
{
for( final ItemStack oreItem : this.oreDictCache.getUnchecked( oreName ) )
{
if( OreDictionary.itemMatches( oreItem, o, false ) )
{
return true;
}
}
}
return false;
} ).orElse( false );
}
boolean sameOre( final AEItemStack aeItemStack, final ItemStack o )
{
return aeItemStack.getOre().map( a -> {
for( final String oreName : a.getEquivalents() )
{
for( final ItemStack oreItem : this.oreDictCache.getUnchecked( oreName ) )
{
if( OreDictionary.itemMatches( oreItem, o, false ) )
{
return true;
}
}
}
return false;
} ).orElse( false );
}
public Set<Integer> getMatchingOre( String allow )
{
List<String> matchingOres = new ArrayList<>();
ConcurrentMap<String, List<ItemStack>> oreKeys = oreDictCache.asMap();
public Set<Integer> getMatchingOre( String oreExp )
{
Set<Integer> matchingIds = new HashSet<>();
oreKeys.forEach( ( s, l ) -> {
List<OreDictFilterMatcher.MatchRule> rulesList = OreDictFilterMatcher.parseExpression( oreExp );
for( String ore : OreDictionary.getOreNames() )
{
if( OreDictFilterMatcher.matches( rulesList, ore ) )
{
matchingIds.add( OreDictionary.getOreID( ore ) );
}
}
return matchingIds;
}
if( allow.startsWith( "*" ) && allow.endsWith( "*" ) )
{
if( s.contains( allow.substring( 1, allow.length() - 1 ) ) )
{
matchingOres.add( s );
}
}
else if( allow.startsWith( "*" ) )
{
if( s.endsWith( allow.substring( 1 ) ) )
{
matchingOres.add( s );
}
}
else if( allow.endsWith( "*" ) )
{
if( s.startsWith( allow.substring( 0, allow.length() - 1 ) ) )
{
matchingOres.add( s );
}
}
else
{
if( s.matches( allow ) )
{
matchingOres.add( s );
}
}
} );
public List<ItemStack> getCachedOres( final String oreName )
{
return this.oreDictCache.getUnchecked( oreName );
}
Set<Integer> oreIDs = new HashSet<>();
for( String matchingOre : matchingOres )
{
oreIDs.add( OreDictionary.getOreID( matchingOre ) );
}
private static class ItemRef
{
return oreIDs;
}
private final Item ref;
private final int damage;
private final int hash;
public List<ItemStack> getCachedOres( final String oreName )
{
return this.oreDictCache.getUnchecked( oreName );
}
ItemRef( final ItemStack stack )
{
this.ref = stack.getItem();
private static class ItemRef
{
if( stack.getItem().isDamageable() )
{
this.damage = 0; // IGNORED
}
else
{
this.damage = stack.getItemDamage(); // might be important...
}
private final Item ref;
private final int damage;
private final int hash;
this.hash = this.ref.hashCode() ^ this.damage;
}
ItemRef( final ItemStack stack )
{
this.ref = stack.getItem();
@Override
public int hashCode()
{
return this.hash;
}
if( stack.getItem().isDamageable() )
{
this.damage = 0; // IGNORED
}
else
{
this.damage = stack.getItemDamage(); // might be important...
}
@Override
public boolean equals( final Object obj )
{
if( obj == null )
{
return false;
}
if( this.getClass() != obj.getClass() )
{
return false;
}
final ItemRef other = (ItemRef) obj;
return this.damage == other.damage && this.ref == other.ref;
}
this.hash = this.ref.hashCode() ^ this.damage;
}
@Override
public int hashCode()
{
return this.hash;
}
@Override
public boolean equals( final Object obj )
{
if( obj == null )
{
return false;
}
if( this.getClass() != obj.getClass() )
{
return false;
}
final ItemRef other = (ItemRef) obj;
return this.damage == other.damage && this.ref == other.ref;
}
@Override
public String toString()
{
return "ItemRef [ref=" + this.ref.getUnlocalizedName() + ", damage=" + this.damage + ", hash=" + this.hash + ']';
}
}
@Override
public String toString()
{
return "ItemRef [ref=" + this.ref.getUnlocalizedName() + ", damage=" + this.damage + ", hash=" + this.hash + ']';
}
}
}