2022-12-08 19:00:41 +01:00
package io.gitlab.jfronny.respackopts.filters ;
2024-04-24 19:33:50 +02:00
import io.gitlab.jfronny.libjf.LibJf ;
2022-12-08 19:00:41 +01:00
import io.gitlab.jfronny.libjf.ResourcePath ;
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents ;
import io.gitlab.jfronny.respackopts.Respackopts ;
2023-08-19 19:29:13 +02:00
import io.gitlab.jfronny.respackopts.filters.util.DirRpoResult ;
2022-12-08 19:00:41 +01:00
import io.gitlab.jfronny.respackopts.gson.AttachmentHolder ;
import io.gitlab.jfronny.respackopts.model.DirRpo ;
2024-04-24 19:33:50 +02:00
import io.gitlab.jfronny.respackopts.model.GC_DirRpo ;
2023-09-02 12:56:13 +02:00
import io.gitlab.jfronny.respackopts.model.cache.CacheKey ;
2022-12-08 19:00:41 +01:00
import io.gitlab.jfronny.respackopts.model.cache.CachedPackState ;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability ;
2023-09-02 12:56:13 +02:00
import io.gitlab.jfronny.respackopts.muscript.RespackoptsFS ;
2022-12-08 19:00:41 +01:00
import io.gitlab.jfronny.respackopts.util.MetaCache ;
import net.minecraft.resource.* ;
import net.minecraft.util.Identifier ;
import java.io.* ;
2023-08-20 12:06:29 +02:00
import java.util.* ;
2022-12-08 19:00:41 +01:00
public enum DirFilterEvents implements UserResourceEvents . Open , UserResourceEvents . FindResource {
INSTANCE ;
public static void init ( ) {
UserResourceEvents . OPEN . register ( INSTANCE ) ;
UserResourceEvents . FIND_RESOURCE . register ( INSTANCE ) ;
}
@Override
public InputSupplier < InputStream > open ( ResourceType type , Identifier id , InputSupplier < InputStream > previous , ResourcePack pack ) {
if ( ! MetaCache . hasCapability ( pack , PackCapability . DirFilter ) ) return previous ;
String path = new ResourcePath ( type , id ) . getName ( ) ;
2023-08-20 12:06:29 +02:00
List < DirRpo > rpo = findDirRpos ( pack , parent ( path ) ) ;
2023-09-02 12:56:13 +02:00
CacheKey key = MetaCache . getKeyByPack ( pack ) ;
RespackoptsFS fs = new RespackoptsFS ( pack ) ;
2023-08-19 19:29:13 +02:00
//TODO use pattern matching for switch
2023-09-02 12:56:13 +02:00
DirRpoResult result = DirRpoResult . compute ( path , rpo , key , fs ) ;
2023-08-19 19:29:13 +02:00
if ( result = = DirRpoResult . ORIGINAL ) return previous ; // Using original file
if ( result = = DirRpoResult . IGNORE ) return null ; // No fallback
// Use fallback
DirRpoResult . Replacement replacement = ( DirRpoResult . Replacement ) result ;
2023-10-05 18:18:04 +02:00
String fallback = replacement . toFallback ( path ) ;
MetaCache . addDependency ( key , path , fallback ) ;
return fs . open ( fallback ) ;
2022-12-08 19:00:41 +01:00
}
@Override
public ResourcePack . ResultConsumer findResources ( ResourceType type , String namespace , String prefix , ResourcePack . ResultConsumer previous , ResourcePack pack ) {
// Warning: the Identifiers here DON'T CONTAIN THE TYPE!
// Therefore, it needs to be added when calling a method that generates a ResourcePath!
if ( ! MetaCache . hasCapability ( pack , PackCapability . DirFilter ) ) return previous ;
2023-08-20 12:06:29 +02:00
boolean dirFilterAdditive = MetaCache . hasCapability ( pack , PackCapability . DirFilterAdditive ) ;
String searchPrefix = type . getDirectory ( ) + " / " + namespace + " / " + prefix ;
Set < String > additionalSearched = new HashSet < > ( ) ;
2023-09-02 12:56:13 +02:00
CacheKey key = MetaCache . getKeyByPack ( pack ) ;
RespackoptsFS fs = new RespackoptsFS ( pack ) ;
2022-12-08 19:00:41 +01:00
return ( identifier , value ) - > {
2023-08-20 12:06:29 +02:00
String path = path ( type , identifier ) ;
2023-08-19 19:29:13 +02:00
//TODO use pattern matching for switch
2023-08-20 12:06:29 +02:00
List < DirRpo > relevantRpos = findDirRpos ( pack , parent ( path ) ) ;
2023-09-02 12:56:13 +02:00
DirRpoResult result = DirRpoResult . compute ( path , relevantRpos , key , fs ) ;
2023-08-19 19:29:13 +02:00
if ( result = = DirRpoResult . ORIGINAL ) { // Using original file
previous . accept ( identifier , value ) ;
return ;
2022-12-08 19:00:41 +01:00
}
2023-08-19 19:29:13 +02:00
if ( result = = DirRpoResult . IGNORE ) return ; // No fallback
// New search for fallback path
DirRpoResult . Replacement replacement = ( DirRpoResult . Replacement ) result ;
String newPath = replacement . toFallback ( path ) ;
2023-08-20 12:06:29 +02:00
if ( newPath . split ( " / " , 3 ) . length ! = 3 ) {
Respackopts . LOGGER . error ( " Directory fallback path MUST be long enough to support representation as identifier (3 segments), but is too short: " + newPath ) ;
return ;
}
if ( ! dirFilterAdditive ) {
// Only return this single result, don't search for others
2023-10-05 18:18:04 +02:00
MetaCache . addDependency ( key , path , newPath ) ;
2023-09-02 12:56:13 +02:00
previous . accept ( identifier , fs . open ( newPath ) ) ;
2023-08-20 12:06:29 +02:00
return ;
}
// Find other files in the fallback directory
String fallbackDir = replacement . fallback . prefix ( ) ;
if ( ! additionalSearched . add ( fallbackDir ) ) return ; // Already searched
int prefixSize = replacement . original . prefix ( ) . length ( ) ;
if ( prefixSize < searchPrefix . length ( ) ) {
if ( ! searchPrefix . startsWith ( replacement . original . prefix ( ) ) ) {
Respackopts . LOGGER . error ( " Unexpected prefix path " + replacement . original . prefix ( ) + " for search prefix " + searchPrefix + " , skipping " ) ;
return ;
}
fallbackDir + = searchPrefix . substring ( prefixSize ) ;
} else if ( ! replacement . original . prefix ( ) . startsWith ( searchPrefix ) ) {
Respackopts . LOGGER . error ( " Unexpected prefix path " + replacement . original . prefix ( ) + " for search prefix " + searchPrefix + " , skipping " ) ;
return ;
}
if ( fallbackDir . split ( " / " , 3 ) . length ! = 3 ) {
Respackopts . LOGGER . error ( " Directory fallback path MUST be long enough to support representation as identifier (3 segments), but is too short: " + fallbackDir ) ;
return ;
}
ResourcePath rp = new ResourcePath ( fallbackDir ) ;
pack . findResources ( rp . getType ( ) , rp . getId ( ) . getNamespace ( ) , rp . getId ( ) . getPath ( ) , ( resource , resVal ) - > {
String fallbackPath = path ( rp . getType ( ) , resource ) ;
2023-10-05 18:18:04 +02:00
String orig = replacement . toOriginal ( fallbackPath ) ;
MetaCache . addDependency ( key , orig , fallbackPath ) ;
previous . accept ( new ResourcePath ( orig ) . getId ( ) , resVal ) ;
2023-08-20 12:06:29 +02:00
} ) ;
2023-08-19 19:29:13 +02:00
} ;
2022-12-08 19:00:41 +01:00
}
2023-08-20 12:06:29 +02:00
private String path ( ResourceType type , Identifier identifier ) {
return type . getDirectory ( ) + " / " + identifier . getNamespace ( ) + " / " + identifier . getPath ( ) ;
}
private String parent ( String path ) {
int li = path . lastIndexOf ( '/' ) ;
return li < = 0 ? null : path . substring ( 0 , li ) ;
}
2023-08-19 19:29:13 +02:00
/ * *
2023-08-20 12:06:29 +02:00
* Identify all directory RPOs relevant to the directory at { @code path } ( IE in it or its parents ) , from outermost to innermost
2023-08-19 19:29:13 +02:00
* /
2023-08-20 12:06:29 +02:00
private List < DirRpo > findDirRpos ( ResourcePack pack , String path ) {
if ( path = = null ) return List . of ( ) ;
2023-09-02 12:56:13 +02:00
CacheKey key = MetaCache . getKeyByPack ( pack ) ;
RespackoptsFS fs = new RespackoptsFS ( pack ) ;
CachedPackState state = MetaCache . getState ( key ) ;
2023-08-19 19:29:13 +02:00
var cache = state . cachedDirRPOs ( ) ;
2023-08-23 18:52:38 +02:00
{
// This is outside computeIfAbsent because it could cause modification of the map, which is unsupported within it
List < DirRpo > cached = cache . get ( path ) ;
if ( cached ! = null ) return cached ;
}
2023-08-20 12:06:29 +02:00
List < DirRpo > parentRPOs = findDirRpos ( pack , parent ( path ) ) ;
2023-08-19 19:29:13 +02:00
synchronized ( cache ) { // This is synchronized as multiple resources might be accessed at the same time, potentially causing a CME here
2023-08-20 12:06:29 +02:00
return cache . computeIfAbsent ( path , $ - > {
2023-09-02 12:56:13 +02:00
String rp = path + " / " + Respackopts . FILE_EXTENSION ;
InputSupplier < InputStream > is = UserResourceEvents . disable ( ( ) - > fs . open ( rp ) ) ;
2023-08-19 19:29:13 +02:00
if ( is = = null ) return parentRPOs ;
2023-10-05 18:18:04 +02:00
if ( state . tracker ( ) ! = null ) state . tracker ( ) . addDependency ( path , rp ) ;
2023-08-19 19:29:13 +02:00
try ( Reader w = new InputStreamReader ( is . get ( ) ) ) {
List < DirRpo > currentRPOs = new LinkedList < > ( parentRPOs ) ;
2024-04-24 19:33:50 +02:00
DirRpo newRPO = AttachmentHolder . attach ( state . metadata ( ) . version , ( ) - > GC_DirRpo . deserialize ( w , LibJf . LENIENT_TRANSPORT ) ) ;
2023-08-20 12:06:29 +02:00
newRPO . hydrate ( path ) ;
2023-08-19 19:29:13 +02:00
if ( newRPO . fallback ! = null & & ! newRPO . fallback . endsWith ( " / " ) )
newRPO . fallback + = " / " ;
currentRPOs . add ( newRPO ) ;
return currentRPOs ;
} catch ( IOException e ) {
2023-09-02 12:56:13 +02:00
Respackopts . LOGGER . error ( " Couldn't open dir rpo " + rp , e ) ;
2023-08-19 19:29:13 +02:00
}
return parentRPOs ;
} ) ;
2022-12-08 19:00:41 +01:00
}
}
}