2022-12-08 19:00:41 +01:00
package io.gitlab.jfronny.respackopts.filters ;
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 ;
import io.gitlab.jfronny.respackopts.model.cache.CachedPackState ;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability ;
import io.gitlab.jfronny.respackopts.util.MetaCache ;
import net.minecraft.resource.* ;
import net.minecraft.util.Identifier ;
import java.io.* ;
2023-08-19 19:29:13 +02:00
import java.util.LinkedList ;
import java.util.List ;
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-19 19:29:13 +02:00
List < DirRpo > rpo = findDirRpos ( pack , path ) ;
//TODO use pattern matching for switch
DirRpoResult result = DirRpoResult . compute ( path , rpo , MetaCache . getKeyByPack ( pack ) ) ;
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 ;
ResourcePath rp = new ResourcePath ( replacement . toFallback ( path ) ) ;
return pack . open ( rp . getType ( ) , rp . getId ( ) ) ;
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 ;
return ( identifier , value ) - > {
String path = type . getDirectory ( ) + " / " + identifier . getNamespace ( ) + " / " + identifier . getPath ( ) ;
2023-08-19 19:29:13 +02:00
//TODO use pattern matching for switch
DirRpoResult result = DirRpoResult . compute ( path , findDirRpos ( pack , path ) , MetaCache . getKeyByPack ( pack ) ) ;
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 ) ;
if ( newPath . split ( " / " , 3 ) . length = = 3 ) {
ResourcePath rp = new ResourcePath ( newPath ) ;
pack . findResources ( rp . getType ( ) , rp . getId ( ) . getNamespace ( ) , rp . getId ( ) . getPath ( ) , ( resource , resVal ) - > {
String fallbackPath = rp . getType ( ) . getDirectory ( ) + " / " + resource . getNamespace ( ) + " / " + resource . getPath ( ) ;
previous . accept ( new ResourcePath ( replacement . toOriginal ( fallbackPath ) ) . getId ( ) , resVal ) ;
} ) ;
} else Respackopts . LOGGER . error ( " Directory fallback path MUST be long enough to support representation as identifier (3 segments), but is too short: " + newPath ) ;
} ;
2022-12-08 19:00:41 +01:00
}
2023-08-19 19:29:13 +02:00
/ * *
* Identify all directory RPOs relevant to the file at name ( IE in its parent directories ) , from outermost to innermost
* /
private List < DirRpo > findDirRpos ( ResourcePack pack , String name ) {
2022-12-08 19:00:41 +01:00
int li = name . lastIndexOf ( '/' ) ;
2023-08-19 19:29:13 +02:00
if ( li < = 0 ) return List . of ( ) ;
2022-12-08 19:00:41 +01:00
name = name . substring ( 0 , li ) ;
2023-08-19 19:29:13 +02:00
CachedPackState state = MetaCache . getState ( MetaCache . getKeyByPack ( pack ) ) ;
var cache = state . cachedDirRPOs ( ) ;
// This is outside computeIfAbsent because it could cause modification of the map, which is unsupported within it
if ( cache . containsKey ( name ) ) return cache . get ( name ) ;
List < DirRpo > parentRPOs = findDirRpos ( pack , name ) ;
synchronized ( cache ) { // This is synchronized as multiple resources might be accessed at the same time, potentially causing a CME here
return cache . computeIfAbsent ( name , parentName - > {
ResourcePath rp ;
try {
rp = new ResourcePath ( parentName + " / " + Respackopts . FILE_EXTENSION ) ;
} catch ( Exception e ) {
return parentRPOs ;
}
InputSupplier < InputStream > is = UserResourceEvents . disable ( ( ) - > pack . open ( rp . getType ( ) , rp . getId ( ) ) ) ;
if ( is = = null ) return parentRPOs ;
try ( Reader w = new InputStreamReader ( is . get ( ) ) ) {
List < DirRpo > currentRPOs = new LinkedList < > ( parentRPOs ) ;
DirRpo newRPO = AttachmentHolder . deserialize ( state . metadata ( ) . version , w , DirRpo . class ) ;
newRPO . hydrate ( parentName ) ;
if ( newRPO . fallback ! = null & & ! newRPO . fallback . endsWith ( " / " ) )
newRPO . fallback + = " / " ;
currentRPOs . add ( newRPO ) ;
return currentRPOs ;
} catch ( IOException e ) {
Respackopts . LOGGER . error ( " Couldn't open dir rpo " + rp . getName ( ) , e ) ;
}
return parentRPOs ;
} ) ;
2022-12-08 19:00:41 +01:00
}
}
}