1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 04:00:30 +00:00

Cleanup resource mount reloading

- Subscribe to the "on add reload listener" event, otherwise we don't
   get reloads beyond the first one! This means we no longer need to
   cast the resource manager to a reloadable one.
 - Change the mount cache so it's keyed on path, rather than "path ✕
   manager".
 - Update the reload listener just to use the mount cache, rather than
   having its own separate list. I really don't understand what I was
   thinking before.
This commit is contained in:
Jonathan Coates 2021-11-24 19:07:12 +00:00
parent c2dc8bf675
commit d3563a3854
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
3 changed files with 42 additions and 51 deletions

View File

@ -28,6 +28,7 @@ import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import dan200.computercraft.shared.util.IDAssigner;
import dan200.computercraft.shared.wired.WiredNode;
import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.resources.IResourceManager;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
@ -101,7 +102,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
@Override
public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath )
{
IReloadableResourceManager manager = (IReloadableResourceManager) ServerLifecycleHooks.getCurrentServer().getDataPackRegistries().getResourceManager();
IResourceManager manager = ServerLifecycleHooks.getCurrentServer().getDataPackRegistries().getResourceManager();
ResourceMount mount = ResourceMount.get( domain, subPath, manager );
return mount.exists( "" ) ? mount : null;
}

View File

@ -7,19 +7,17 @@ package dan200.computercraft.core.filesystem;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.MapMaker;
import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import dan200.computercraft.shared.util.IoUtil;
import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.client.resources.ReloadListener;
import net.minecraft.profiler.IProfiler;
import net.minecraft.resources.IResource;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.ResourceLocationException;
import net.minecraftforge.resource.IResourceType;
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -28,9 +26,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
public final class ResourceMount implements IMount
{
@ -58,50 +57,37 @@ public final class ResourceMount implements IMount
.<FileEntry, byte[]>weigher( ( k, v ) -> v.length )
.build();
private static final MapMaker CACHE_TEMPLATE = new MapMaker().weakValues().concurrencyLevel( 1 );
/**
* Maintain a cache of currently loaded resource mounts. This cache is invalidated when currentManager changes.
*/
private static final Map<IReloadableResourceManager, Map<ResourceLocation, ResourceMount>> MOUNT_CACHE = new WeakHashMap<>( 2 );
private static final Map<ResourceLocation, ResourceMount> MOUNT_CACHE = new HashMap<>( 2 );
private final String namespace;
private final String subPath;
private final IReloadableResourceManager manager;
private IResourceManager manager;
@Nullable
private FileEntry root;
public static ResourceMount get( String namespace, String subPath, IReloadableResourceManager manager )
public static ResourceMount get( String namespace, String subPath, IResourceManager manager )
{
Map<ResourceLocation, ResourceMount> cache;
ResourceLocation path = new ResourceLocation( namespace, subPath );
synchronized( MOUNT_CACHE )
{
cache = MOUNT_CACHE.get( manager );
if( cache == null ) MOUNT_CACHE.put( manager, cache = CACHE_TEMPLATE.makeMap() );
}
ResourceLocation path = new ResourceLocation( namespace, subPath );
synchronized( cache )
{
ResourceMount mount = cache.get( path );
if( mount == null ) cache.put( path, mount = new ResourceMount( namespace, subPath, manager ) );
ResourceMount mount = MOUNT_CACHE.get( path );
if( mount == null ) MOUNT_CACHE.put( path, mount = new ResourceMount( namespace, subPath, manager ) );
return mount;
}
}
private ResourceMount( String namespace, String subPath, IReloadableResourceManager manager )
private ResourceMount( String namespace, String subPath, IResourceManager manager )
{
this.namespace = namespace;
this.subPath = subPath;
this.manager = manager;
Listener.INSTANCE.add( manager, this );
if( root == null ) load();
load( manager );
}
private void load()
private void load( IResourceManager manager )
{
boolean hasAny = false;
String existingNamespace = null;
@ -118,6 +104,7 @@ public final class ResourceMount implements IMount
hasAny = true;
}
this.manager = manager;
root = hasAny ? newRoot : null;
if( !hasAny )
@ -293,35 +280,30 @@ public final class ResourceMount implements IMount
}
/**
* A {@link ISelectiveResourceReloadListener} which reloads any associated mounts.
*
* While people should really be keeping a permanent reference to this, some people construct it every
* method call, so let's make this as small as possible.
* A {@link ReloadListener} which reloads any associated mounts and correctly updates the resource manager they
* point to.
*/
static class Listener implements ISelectiveResourceReloadListener
public static final ReloadListener<Void> RELOAD_LISTENER = new ReloadListener<Void>()
{
private static final Listener INSTANCE = new Listener();
private final Set<ResourceMount> mounts = Collections.newSetFromMap( new WeakHashMap<>() );
private final Set<IReloadableResourceManager> managers = Collections.newSetFromMap( new WeakHashMap<>() );
@Nonnull
@Override
public void onResourceManagerReload( @Nonnull IResourceManager manager )
protected Void prepare( @Nonnull IResourceManager manager, @Nonnull IProfiler profiler )
{
// FIXME: Remove this. We need this patch in order to prevent trying to load ReloadRequirements.
onResourceManagerReload( manager, x -> true );
profiler.push( "Reloading ComputerCraft mounts" );
try
{
for( ResourceMount mount : MOUNT_CACHE.values() ) mount.load( manager );
}
finally
{
profiler.pop();
}
return null;
}
@Override
public synchronized void onResourceManagerReload( @Nonnull IResourceManager manager, @Nonnull Predicate<IResourceType> predicate )
protected void apply( @Nonnull Void result, @Nonnull IResourceManager manager, @Nonnull IProfiler profiler )
{
for( ResourceMount mount : mounts ) mount.load();
}
synchronized void add( IReloadableResourceManager manager, ResourceMount mount )
{
if( managers.add( manager ) ) manager.registerReloadListener( this );
mounts.add( mount );
}
}
};
}

View File

@ -8,6 +8,7 @@ package dan200.computercraft.shared;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.core.tracking.ComputerMBean;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.command.CommandComputerCraft;
@ -23,6 +24,7 @@ import net.minecraft.loot.TableLootEntry;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.event.LootTableLoadEvent;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
@ -138,4 +140,10 @@ public final class CommonHooks
.name( "computercraft_treasure" )
.build() );
}
@SubscribeEvent
public static void onAddReloadListeners( AddReloadListenerEvent event )
{
event.addListener( ResourceMount.RELOAD_LISTENER );
}
}