mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-30 21:23:00 +00:00 
			
		
		
		
	Refactor common {Jar,Resource}Mount code into a parent class
This commit is contained in:
		| @@ -5,57 +5,32 @@ | ||||
|  */ | ||||
| package dan200.computercraft.shared.computer.core; | ||||
| 
 | ||||
| import com.google.common.cache.Cache; | ||||
| import com.google.common.cache.CacheBuilder; | ||||
| import com.google.common.io.ByteStreams; | ||||
| import dan200.computercraft.api.filesystem.Mount; | ||||
| import dan200.computercraft.core.apis.handles.ArrayByteChannel; | ||||
| import dan200.computercraft.core.filesystem.ArchiveMount; | ||||
| import dan200.computercraft.core.filesystem.FileSystem; | ||||
| import dan200.computercraft.core.util.IoUtil; | ||||
| import net.minecraft.ResourceLocationException; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| import net.minecraft.server.MinecraftServer; | ||||
| import net.minecraft.server.packs.resources.ResourceManager; | ||||
| import net.minecraft.server.packs.resources.SimplePreparableReloadListener; | ||||
| import net.minecraft.util.profiling.ProfilerFiller; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import javax.annotation.Nullable; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.IOException; | ||||
| import java.nio.channels.Channels; | ||||
| import java.nio.channels.ReadableByteChannel; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| 
 | ||||
| public final class ResourceMount implements Mount { | ||||
| /** | ||||
|  * A mount backed by Minecraft's {@link ResourceManager}. | ||||
|  * | ||||
|  * @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(MinecraftServer, String, String) | ||||
|  */ | ||||
| public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> { | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(ResourceMount.class); | ||||
| 
 | ||||
|     /** | ||||
|      * Only cache files smaller than 1MiB. | ||||
|      */ | ||||
|     private static final int MAX_CACHED_SIZE = 1 << 20; | ||||
| 
 | ||||
|     /** | ||||
|      * Limit the entire cache to 64MiB. | ||||
|      */ | ||||
|     private static final int MAX_CACHE_SIZE = 64 << 20; | ||||
| 
 | ||||
|     private static final byte[] TEMP_BUFFER = new byte[8192]; | ||||
| 
 | ||||
|     /** | ||||
|      * We maintain a cache of the contents of all files in the mount. This allows us to allow | ||||
|      * seeking within ROM files, and reduces the amount we need to access disk for computer startup. | ||||
|      */ | ||||
|     private static final Cache<FileEntry, byte[]> CONTENTS_CACHE = CacheBuilder.newBuilder() | ||||
|         .concurrencyLevel(4) | ||||
|         .expireAfterAccess(60, TimeUnit.SECONDS) | ||||
|         .maximumWeight(MAX_CACHE_SIZE) | ||||
|         .weakKeys() | ||||
|         .<FileEntry, byte[]>weigher((k, v) -> v.length) | ||||
|         .build(); | ||||
| 
 | ||||
|     /** | ||||
|      * Maintain a cache of currently loaded resource mounts. This cache is invalidated when currentManager changes. | ||||
|      */ | ||||
| @@ -65,9 +40,6 @@ public final class ResourceMount implements Mount { | ||||
|     private final String subPath; | ||||
|     private ResourceManager manager; | ||||
| 
 | ||||
|     @Nullable | ||||
|     private FileEntry root; | ||||
| 
 | ||||
|     public static ResourceMount get(String namespace, String subPath, ResourceManager manager) { | ||||
|         var path = new ResourceLocation(namespace, subPath); | ||||
|         synchronized (MOUNT_CACHE) { | ||||
| @@ -110,21 +82,6 @@ public final class ResourceMount implements Mount { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private @Nullable FileEntry get(String path) { | ||||
|         var lastEntry = root; | ||||
|         var lastIndex = 0; | ||||
| 
 | ||||
|         while (lastEntry != null && lastIndex < path.length()) { | ||||
|             var nextIndex = path.indexOf('/', lastIndex); | ||||
|             if (nextIndex < 0) nextIndex = path.length(); | ||||
| 
 | ||||
|             lastEntry = lastEntry.children == null ? null : lastEntry.children.get(path.substring(lastIndex, nextIndex)); | ||||
|             lastIndex = nextIndex + 1; | ||||
|         } | ||||
| 
 | ||||
|         return lastEntry; | ||||
|     } | ||||
| 
 | ||||
|     private void create(FileEntry lastEntry, String path) { | ||||
|         var lastIndex = 0; | ||||
|         while (lastIndex < path.length()) { | ||||
| @@ -152,96 +109,39 @@ public final class ResourceMount implements Mount { | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean exists(String path) { | ||||
|         return get(path) != null; | ||||
|     } | ||||
|     public long getSize(FileEntry file) { | ||||
|         var resource = manager.getResource(file.identifier).orElse(null); | ||||
|         if (resource == null) return 0; | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isDirectory(String path) { | ||||
|         var file = get(path); | ||||
|         return file != null && file.isDirectory(); | ||||
|     } | ||||
|         try (var stream = resource.open()) { | ||||
|             int total = 0, read = 0; | ||||
|             do { | ||||
|                 total += read; | ||||
|                 read = stream.read(TEMP_BUFFER); | ||||
|             } while (read > 0); | ||||
| 
 | ||||
|     @Override | ||||
|     public void list(String path, List<String> contents) throws IOException { | ||||
|         var file = get(path); | ||||
|         if (file == null || !file.isDirectory()) throw new IOException("/" + path + ": Not a directory"); | ||||
| 
 | ||||
|         file.list(contents); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public long getSize(String path) throws IOException { | ||||
|         var file = get(path); | ||||
|         if (file != null) { | ||||
|             if (file.size != -1) return file.size; | ||||
|             if (file.isDirectory()) return file.size = 0; | ||||
| 
 | ||||
|             var contents = CONTENTS_CACHE.getIfPresent(file); | ||||
|             if (contents != null) return file.size = contents.length; | ||||
| 
 | ||||
|             var resource = manager.getResource(file.identifier).orElse(null); | ||||
|             if (resource == null) return file.size = 0; | ||||
| 
 | ||||
|             try (var s = resource.open()) { | ||||
|                 int total = 0, read = 0; | ||||
|                 do { | ||||
|                     total += read; | ||||
|                     read = s.read(TEMP_BUFFER); | ||||
|                 } while (read > 0); | ||||
| 
 | ||||
|                 return file.size = total; | ||||
|             } catch (IOException e) { | ||||
|                 return file.size = 0; | ||||
|             } | ||||
|             return total; | ||||
|         } catch (IOException e) { | ||||
|             return 0; | ||||
|         } | ||||
| 
 | ||||
|         throw new IOException("/" + path + ": No such file"); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public ReadableByteChannel openForRead(String path) throws IOException { | ||||
|         var file = get(path); | ||||
|         if (file != null && !file.isDirectory()) { | ||||
|             var contents = CONTENTS_CACHE.getIfPresent(file); | ||||
|             if (contents != null) return new ArrayByteChannel(contents); | ||||
|     public byte[] getContents(FileEntry file) throws IOException { | ||||
|         var resource = manager.getResource(file.identifier).orElse(null); | ||||
|         if (resource == null) throw new FileNotFoundException(NO_SUCH_FILE); | ||||
| 
 | ||||
|             var resource = manager.getResource(file.identifier).orElse(null); | ||||
|             if (resource != null) { | ||||
|                 var stream = resource.open(); | ||||
|                 if (stream.available() > MAX_CACHED_SIZE) return Channels.newChannel(stream); | ||||
| 
 | ||||
|                 try { | ||||
|                     contents = ByteStreams.toByteArray(stream); | ||||
|                 } finally { | ||||
|                     IoUtil.closeQuietly(stream); | ||||
|                 } | ||||
| 
 | ||||
|                 CONTENTS_CACHE.put(file, contents); | ||||
|                 return new ArrayByteChannel(contents); | ||||
|             } | ||||
|         try (var stream = resource.open()) { | ||||
|             return stream.readAllBytes(); | ||||
|         } | ||||
| 
 | ||||
|         throw new IOException("/" + path + ": No such file"); | ||||
|     } | ||||
| 
 | ||||
|     private static class FileEntry { | ||||
|     protected static class FileEntry extends ArchiveMount.FileEntry<FileEntry> { | ||||
|         final ResourceLocation identifier; | ||||
|         @Nullable | ||||
|         Map<String, FileEntry> children; | ||||
|         long size = -1; | ||||
| 
 | ||||
|         FileEntry(ResourceLocation identifier) { | ||||
|             this.identifier = identifier; | ||||
|         } | ||||
| 
 | ||||
|         boolean isDirectory() { | ||||
|             return children != null; | ||||
|         } | ||||
| 
 | ||||
|         void list(List<String> contents) { | ||||
|             if (children != null) contents.addAll(children.keySet()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -254,7 +154,6 @@ public final class ResourceMount implements Mount { | ||||
|             profiler.push("Reloading ComputerCraft mounts"); | ||||
|             try { | ||||
|                 for (var mount : MOUNT_CACHE.values()) mount.load(manager); | ||||
|                 CONTENTS_CACHE.invalidateAll(); | ||||
|             } finally { | ||||
|                 profiler.pop(); | ||||
|             } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates