1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-15 12:40: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.util.IDAssigner;
import dan200.computercraft.shared.wired.WiredNode; import dan200.computercraft.shared.wired.WiredNode;
import net.minecraft.resources.IReloadableResourceManager; import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.resources.IResourceManager;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
@ -101,7 +102,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
@Override @Override
public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath ) 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 ); ResourceMount mount = ResourceMount.get( domain, subPath, manager );
return mount.exists( "" ) ? mount : null; 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.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.collect.MapMaker;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel; import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import dan200.computercraft.shared.util.IoUtil; 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.IResource;
import net.minecraft.resources.IResourceManager; import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.ResourceLocationException; import net.minecraft.util.ResourceLocationException;
import net.minecraftforge.resource.IResourceType;
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -28,9 +26,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel; 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.concurrent.TimeUnit;
import java.util.function.Predicate;
public final class ResourceMount implements IMount public final class ResourceMount implements IMount
{ {
@ -58,50 +57,37 @@ public final class ResourceMount implements IMount
.<FileEntry, byte[]>weigher( ( k, v ) -> v.length ) .<FileEntry, byte[]>weigher( ( k, v ) -> v.length )
.build(); .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. * 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 namespace;
private final String subPath; private final String subPath;
private final IReloadableResourceManager manager; private IResourceManager manager;
@Nullable @Nullable
private FileEntry root; 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 ) synchronized( MOUNT_CACHE )
{ {
cache = MOUNT_CACHE.get( manager ); ResourceMount mount = MOUNT_CACHE.get( path );
if( cache == null ) MOUNT_CACHE.put( manager, cache = CACHE_TEMPLATE.makeMap() ); if( mount == null ) MOUNT_CACHE.put( path, mount = new ResourceMount( namespace, subPath, manager ) );
}
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 ) );
return mount; return mount;
} }
} }
private ResourceMount( String namespace, String subPath, IReloadableResourceManager manager ) private ResourceMount( String namespace, String subPath, IResourceManager manager )
{ {
this.namespace = namespace; this.namespace = namespace;
this.subPath = subPath; this.subPath = subPath;
this.manager = manager; load( manager );
Listener.INSTANCE.add( manager, this );
if( root == null ) load();
} }
private void load() private void load( IResourceManager manager )
{ {
boolean hasAny = false; boolean hasAny = false;
String existingNamespace = null; String existingNamespace = null;
@ -118,6 +104,7 @@ public final class ResourceMount implements IMount
hasAny = true; hasAny = true;
} }
this.manager = manager;
root = hasAny ? newRoot : null; root = hasAny ? newRoot : null;
if( !hasAny ) if( !hasAny )
@ -293,35 +280,30 @@ public final class ResourceMount implements IMount
} }
/** /**
* A {@link ISelectiveResourceReloadListener} which reloads any associated mounts. * A {@link ReloadListener} which reloads any associated mounts and correctly updates the resource manager they
* * point to.
* 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.
*/ */
static class Listener implements ISelectiveResourceReloadListener public static final ReloadListener<Void> RELOAD_LISTENER = new ReloadListener<Void>()
{ {
private static final Listener INSTANCE = new Listener(); @Nonnull
private final Set<ResourceMount> mounts = Collections.newSetFromMap( new WeakHashMap<>() );
private final Set<IReloadableResourceManager> managers = Collections.newSetFromMap( new WeakHashMap<>() );
@Override @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. profiler.push( "Reloading ComputerCraft mounts" );
onResourceManagerReload( manager, x -> true ); try
{
for( ResourceMount mount : MOUNT_CACHE.values() ) mount.load( manager );
}
finally
{
profiler.pop();
}
return null;
} }
@Override @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.ComputerCraft;
import dan200.computercraft.core.apis.http.NetworkUtils; import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.computer.MainThread; import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.core.tracking.ComputerMBean; import dan200.computercraft.core.tracking.ComputerMBean;
import dan200.computercraft.core.tracking.Tracking; import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.command.CommandComputerCraft;
@ -23,6 +24,7 @@ import net.minecraft.loot.TableLootEntry;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.event.LootTableLoadEvent; import net.minecraftforge.event.LootTableLoadEvent;
import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.TickEvent;
@ -138,4 +140,10 @@ public final class CommonHooks
.name( "computercraft_treasure" ) .name( "computercraft_treasure" )
.build() ); .build() );
} }
@SubscribeEvent
public static void onAddReloadListeners( AddReloadListenerEvent event )
{
event.addListener( ResourceMount.RELOAD_LISTENER );
}
} }