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
2024-04-24 20:58:11 +02:00
public enum DirFilterEvents implements UserResourceEvents . OpenRoot , UserResourceEvents . Open , UserResourceEvents . FindResource {
2022-12-08 19:00:41 +01:00
INSTANCE ;
public static void init ( ) {
2024-04-24 20:58:11 +02:00
UserResourceEvents . OPEN_ROOT . register ( INSTANCE ) ;
2022-12-08 19:00:41 +01:00
UserResourceEvents . OPEN . register ( INSTANCE ) ;
UserResourceEvents . FIND_RESOURCE . register ( INSTANCE ) ;
}
2024-04-24 20:58:11 +02:00
@Override
public InputSupplier < InputStream > openRoot ( String [ ] fileName , InputSupplier < InputStream > previous , ResourcePack pack ) {
if ( skip ( pack ) ) return previous ;
return open ( previous , pack , String . join ( " / " , fileName ) ) ;
}
2022-12-08 19:00:41 +01:00
@Override
public InputSupplier < InputStream > open ( ResourceType type , Identifier id , InputSupplier < InputStream > previous , ResourcePack pack ) {
2024-04-24 20:58:11 +02:00
if ( skip ( pack ) ) return previous ;
return open ( previous , pack , new ResourcePath ( type , id ) . getName ( ) ) ;
}
private InputSupplier < InputStream > open ( InputSupplier < InputStream > previous , ResourcePack pack , String name ) {
List < DirRpo > rpo = findDirRpos ( pack , parent ( name ) ) ;
2023-09-02 12:56:13 +02:00
CacheKey key = MetaCache . getKeyByPack ( pack ) ;
RespackoptsFS fs = new RespackoptsFS ( pack ) ;
2024-04-24 20:58:11 +02:00
return switch ( DirRpoResult . compute ( name , rpo , key , fs ) ) {
case DirRpoResult . FixedState . ORIGINAL - > previous ; // Using original file
case DirRpoResult . FixedState . IGNORE - > null ; // No fallback
case DirRpoResult . Replacement replacement - > {
// Use fallback
String fallback = replacement . toFallback ( name ) ;
MetaCache . addDependency ( key , name , fallback ) ;
yield 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!
2024-04-24 20:58:11 +02:00
if ( skip ( pack ) ) 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 ) - > {
2024-04-24 20:58:11 +02:00
String path = new ResourcePath ( type , identifier ) . getName ( ) ;
2023-08-20 12:06:29 +02:00
List < DirRpo > relevantRpos = findDirRpos ( pack , parent ( path ) ) ;
2024-04-24 20:58:11 +02:00
switch ( DirRpoResult . compute ( path , relevantRpos , key , fs ) ) {
case DirRpoResult . FixedState . ORIGINAL - > {
// Using original file
previous . accept ( identifier , value ) ;
}
case DirRpoResult . FixedState . IGNORE - > {
// No fallback
}
case DirRpoResult . Replacement replacement - > {
// New search for fallback path
String newPath = replacement . toFallback ( path ) ;
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
MetaCache . addDependency ( key , path , newPath ) ;
previous . accept ( identifier , fs . open ( newPath ) ) ;
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 ) - > {
ResourceType type1 = rp . getType ( ) ;
String fallbackPath = new ResourcePath ( type1 , resource ) . getName ( ) ;
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 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
}
}
2024-04-24 20:58:11 +02:00
private boolean skip ( ResourcePack pack ) {
return ! MetaCache . hasCapability ( pack , PackCapability . DirFilter ) ;
}
2022-12-08 19:00:41 +01:00
}