Resource Manager
Major resource manager changes. Things now properly lazy load. There code for deferred loading, but its not currently active
This commit is contained in:
parent
d9a6e8215c
commit
98db61a4a5
37
Config.cs
37
Config.cs
@ -17,29 +17,6 @@ public class DescAttribute : Attribute
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class ResRefConfig<T> : res.Ref<T> where T: Config
|
||||
{
|
||||
public ResRefConfig()
|
||||
{
|
||||
}
|
||||
|
||||
public ResRefConfig( string filename, T cfg )
|
||||
: base( filename, cfg )
|
||||
{
|
||||
}
|
||||
|
||||
override public void OnDeserialize( object enclosing )
|
||||
{
|
||||
base.OnDeserialize( enclosing );
|
||||
|
||||
var cfg = Config.load<T>( filename );
|
||||
|
||||
res = cfg;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Config
|
||||
{
|
||||
@ -52,20 +29,22 @@ public class Config
|
||||
|
||||
static public void startup()
|
||||
{
|
||||
res.Mgr.register<Config>( res_load );
|
||||
res.Mgr.registerSub<Config>( res_load );
|
||||
res.Mgr.register<Config>( load );
|
||||
res.Mgr.registerSub(typeof(Config));
|
||||
}
|
||||
|
||||
|
||||
#region SaveLoad
|
||||
static public ResRefConfig<Config> res_load( string filename )
|
||||
/*
|
||||
static public res.Ref<Config> res_load( string filename )
|
||||
{
|
||||
return new ResRefConfig<Config>( filename, load( filename ) );
|
||||
return new res.Ref<Config>( filename, load( filename ) );
|
||||
}
|
||||
*/
|
||||
|
||||
static public ResRefConfig<T> res_load<T>( string filename ) where T : Config
|
||||
static public T res_load<T>( string filename ) where T : Config
|
||||
{
|
||||
return new ResRefConfig<T>( filename, load<T>( filename ) );
|
||||
return load<T>( filename );
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
2
Conn.cs
2
Conn.cs
@ -20,7 +20,7 @@ public interface IProcess
|
||||
|
||||
|
||||
|
||||
public class Conn
|
||||
public class Conn
|
||||
{
|
||||
public Socket Sock { get { return m_socket; } }
|
||||
public Stream Stream { get { return m_streamNet; } }
|
||||
|
||||
15
Imm.cs
Normal file
15
Imm.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// A spot for immutable helpers
|
||||
|
||||
public static class imm
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
32
Log.cs
32
Log.cs
@ -90,7 +90,7 @@ namespace lib
|
||||
s_log = null;
|
||||
}
|
||||
|
||||
static private Log s_log;
|
||||
static public Log s_log;
|
||||
|
||||
/*
|
||||
static public Log log
|
||||
@ -103,37 +103,37 @@ namespace lib
|
||||
*/
|
||||
|
||||
// Forwards.
|
||||
static public void fatal( string msg, string cat = "unknown", object obj = null )
|
||||
static public void fatal( string msg, string cat = "unk", object obj = null )
|
||||
{
|
||||
log(msg, LogType.Fatal, cat, obj);
|
||||
}
|
||||
|
||||
static public void error( string msg, string cat = "unknown", object obj = null )
|
||||
static public void error( string msg, string cat = "unk", object obj = null )
|
||||
{
|
||||
log(msg, LogType.Error, cat, obj);
|
||||
}
|
||||
|
||||
static public void warn( string msg, string cat = "unknown", object obj = null )
|
||||
static public void warn( string msg, string cat = "unk", object obj = null )
|
||||
{
|
||||
log( msg, LogType.Warn, cat, obj );
|
||||
}
|
||||
|
||||
static public void info( string msg, string cat = "unknown", object obj = null )
|
||||
static public void info( string msg, string cat = "unk", object obj = null )
|
||||
{
|
||||
log(msg, LogType.Info, cat, obj);
|
||||
}
|
||||
|
||||
static public void debug( string msg, string cat = "unknown", object obj = null )
|
||||
static public void debug( string msg, string cat = "unk", object obj = null )
|
||||
{
|
||||
log(msg, LogType.Debug, cat, obj);
|
||||
}
|
||||
|
||||
static public void trace( string msg, string cat = "unknown", object obj = null )
|
||||
static public void trace( string msg, string cat = "unk", object obj = null )
|
||||
{
|
||||
log(msg, LogType.Trace, cat, obj);
|
||||
}
|
||||
|
||||
static public void log( string msg, LogType type = LogType.Debug, string cat = "unknown", object obj = null )
|
||||
static public void log( string msg, LogType type = LogType.Debug, string cat = "unk", object obj = null )
|
||||
{
|
||||
lock(s_log)
|
||||
{
|
||||
@ -143,6 +143,22 @@ namespace lib
|
||||
}
|
||||
}
|
||||
|
||||
//This might seem a little odd, but the intent is that usually you wont need to set notExpectedValue.
|
||||
static public void expected<T>( T value, string falseString, string trueString = "", T notExpectedValue = default(T) )
|
||||
{
|
||||
|
||||
var name = nameof(value);
|
||||
|
||||
if( !value.Equals( notExpectedValue ) )
|
||||
{
|
||||
lib.Log.info( $"Properly got {value}{trueString}" );
|
||||
}
|
||||
else
|
||||
{
|
||||
lib.Log.warn( $"Got {notExpectedValue} instead of {value}{falseString}" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Log( string filename )
|
||||
{
|
||||
|
||||
@ -44,6 +44,9 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections.Immutable, Version=1.2.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Collections.Immutable.1.6.0-preview3.19128.7\lib\netstandard2.0\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
@ -67,6 +70,7 @@
|
||||
<Compile Include="Clock.cs" />
|
||||
<Compile Include="Config.cs" />
|
||||
<Compile Include="Helpers.cs" />
|
||||
<Compile Include="Imm.cs" />
|
||||
<Compile Include="Log.cs" />
|
||||
<Compile Include="math\AngleSingle.cs" />
|
||||
<Compile Include="math\AngleType.cs" />
|
||||
@ -131,6 +135,9 @@
|
||||
<Compile Include="XmlFormatter.cs" />
|
||||
<Compile Include="XmlFormatter2.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
4
packages.config
Normal file
4
packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="System.Collections.Immutable" version="1.6.0-preview3.19128.7" targetFramework="net462" />
|
||||
</packages>
|
||||
446
res/Resource.cs
446
res/Resource.cs
@ -5,169 +5,349 @@ using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
namespace res
|
||||
{
|
||||
|
||||
[Serializable]
|
||||
public class Ref : lib.I_Serialize
|
||||
{
|
||||
public string filename { get { return m_filename; } }
|
||||
using ImmDefLoad = ImmutableQueue<(string name, Ref)>;
|
||||
|
||||
//For construction
|
||||
public Ref()
|
||||
|
||||
[Serializable]
|
||||
public class Ref : lib.I_Serialize
|
||||
{
|
||||
}
|
||||
public string filename { get { return m_filename; } }
|
||||
|
||||
public Ref( string filename )
|
||||
{
|
||||
m_filename = filename;
|
||||
}
|
||||
|
||||
virtual public void OnSerialize()
|
||||
{
|
||||
}
|
||||
|
||||
virtual public void OnDeserialize( object enclosing )
|
||||
{
|
||||
}
|
||||
|
||||
virtual public void OnChange()
|
||||
{
|
||||
}
|
||||
|
||||
private string m_filename;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Ref<T> : Ref
|
||||
{
|
||||
public T res{ get{ return m_res; } set{ m_res = value; } }
|
||||
|
||||
//For construction
|
||||
public Ref()
|
||||
{
|
||||
}
|
||||
|
||||
public Ref( string filename )
|
||||
: base( filename )
|
||||
{
|
||||
}
|
||||
|
||||
public Ref( string filename, T res ) : base( filename )
|
||||
{
|
||||
m_res = res;
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
private T m_res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public class Resource
|
||||
{
|
||||
static public Mgr mgr;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public class Loader<T>
|
||||
{
|
||||
static public T load( string filename )
|
||||
{
|
||||
Debug.Assert( false, "Specialize Loader for your type for file" );
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
*/
|
||||
public delegate Ref Load( string filename );
|
||||
public delegate Ref LoadType( string filename, Type t );
|
||||
|
||||
|
||||
public class Mgr
|
||||
{
|
||||
|
||||
|
||||
static public void startup()
|
||||
{
|
||||
Resource.mgr = new Mgr();
|
||||
}
|
||||
|
||||
static public void register<T>( Load loader )
|
||||
{
|
||||
Debug.Assert( !Resource.mgr.m_loaders.ContainsKey( typeof( T ) ) );
|
||||
Resource.mgr.m_loaders[ typeof( T ) ] = loader;
|
||||
}
|
||||
|
||||
//Register all subclasses of a particular type
|
||||
static public void registerSub<T>( Load loaderOfType )
|
||||
{
|
||||
|
||||
Type[] typeParams = new Type[1];
|
||||
foreach( var mi in typeof( T ).GetMethods() )
|
||||
//For construction
|
||||
public Ref()
|
||||
{
|
||||
if( mi.Name == "res_load" && mi.IsGenericMethod )
|
||||
}
|
||||
|
||||
public Ref( string filename )
|
||||
{
|
||||
m_filename = filename;
|
||||
}
|
||||
|
||||
virtual public void OnSerialize()
|
||||
{
|
||||
}
|
||||
|
||||
virtual public void OnDeserialize( object enclosing )
|
||||
{
|
||||
}
|
||||
|
||||
virtual public void OnChange()
|
||||
{
|
||||
}
|
||||
|
||||
virtual internal void load()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private string m_filename;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Ref<T> : Ref where T : class
|
||||
{
|
||||
public T res => m_res != null ? m_res : m_res = Mgr.load<T>( filename );
|
||||
|
||||
//For serialization
|
||||
public Ref()
|
||||
{
|
||||
}
|
||||
|
||||
public Ref( string filename )
|
||||
:
|
||||
base( filename )
|
||||
{
|
||||
}
|
||||
|
||||
public Ref( string filename, T res ) : base( filename )
|
||||
{
|
||||
m_res = res;
|
||||
}
|
||||
|
||||
|
||||
override internal void load()
|
||||
{
|
||||
m_res = Mgr.load<T>( filename );
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
private T m_res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public class Resource
|
||||
{
|
||||
static public Mgr mgr;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public class Loader<T>
|
||||
{
|
||||
static public T load( string filename )
|
||||
{
|
||||
Debug.Assert( false, "Specialize Loader for your type for file" );
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public delegate T Load<out T>( string filename );
|
||||
|
||||
|
||||
class LoadHolder
|
||||
{
|
||||
internal virtual object load()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LoadHolder<T> : LoadHolder
|
||||
{
|
||||
public LoadHolder( Load<T> _dlgtLoad )
|
||||
{
|
||||
dlgtLoad = _dlgtLoad;
|
||||
}
|
||||
|
||||
|
||||
public Load<T> dlgtLoad;
|
||||
|
||||
internal override object load()
|
||||
{
|
||||
return load();
|
||||
}
|
||||
}
|
||||
|
||||
//generic classes make a new static per generic type
|
||||
class ResCache<T> where T : class
|
||||
{
|
||||
public static T s_default = default;
|
||||
public static ImmutableDictionary<string, WeakReference<T>> s_cache = ImmutableDictionary<string, WeakReference<T>>.Empty;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class Mgr
|
||||
{
|
||||
|
||||
|
||||
static public void startup()
|
||||
{
|
||||
Resource.mgr = new Mgr();
|
||||
}
|
||||
|
||||
static public void register<T>( Load<T> loader )
|
||||
{
|
||||
Debug.Assert( !Resource.mgr.m_loaders.ContainsKey( typeof( T ) ) );
|
||||
|
||||
var lh = new LoadHolder<T>( loader );
|
||||
|
||||
ImmutableInterlocked.TryAdd( ref Resource.mgr.m_loaders, typeof(T), lh );
|
||||
}
|
||||
|
||||
//Register all subclasses of a particular type
|
||||
//???? Should we just always do this?
|
||||
static public void registerSub( Type baseType )
|
||||
{
|
||||
|
||||
Type[] typeParams = new Type[1];
|
||||
foreach( var mi in baseType.GetMethods() )
|
||||
{
|
||||
foreach( var ass in AppDomain.CurrentDomain.GetAssemblies() )
|
||||
if( mi.Name == "res_load" && mi.IsGenericMethod )
|
||||
{
|
||||
foreach( var t in ass.GetTypes() )
|
||||
foreach( var ass in AppDomain.CurrentDomain.GetAssemblies() )
|
||||
{
|
||||
if( t.IsSubclassOf( typeof( T ) ) )
|
||||
foreach( var t in ass.GetTypes() )
|
||||
{
|
||||
typeParams[0] = t;
|
||||
var mi_ng = mi.MakeGenericMethod( typeParams );
|
||||
Resource.mgr.m_loaders[ t ] = (Load)Delegate.CreateDelegate( typeof(Load), mi_ng );
|
||||
if( t.IsSubclassOf( baseType ) )
|
||||
{
|
||||
typeParams[0] = t;
|
||||
var mi_ng = mi.MakeGenericMethod( typeParams );
|
||||
|
||||
var loadGenType = typeof(Load<>);
|
||||
|
||||
var loadType = loadGenType.MakeGenericType( t );
|
||||
|
||||
var loader = Delegate.CreateDelegate( loadType, mi_ng );
|
||||
|
||||
var lhGenType = typeof(LoadHolder<>);
|
||||
|
||||
var lhType = lhGenType.MakeGenericType( t );
|
||||
|
||||
var lh = Activator.CreateInstance( lhType, loader ) as LoadHolder;
|
||||
|
||||
ImmutableInterlocked.TryAdd( ref Resource.mgr.m_loaders, t, lh );
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public Ref<T> load<T>( string filename ) where T : class
|
||||
{
|
||||
Load loader;
|
||||
Resource.mgr.m_loaders.TryGetValue( typeof( T ), out loader );
|
||||
|
||||
if( loader != null )
|
||||
static public Ref<T> lookup<T>( string filename ) where T : class
|
||||
{
|
||||
var rf_raw = loader( filename );
|
||||
Ref<T> rf = rf_raw as Ref<T>;
|
||||
return rf;
|
||||
/*
|
||||
LoadHolder loader_gen;
|
||||
Resource.mgr.m_loaders.TryGetValue( typeof( T ), out loader_gen );
|
||||
|
||||
var loaderHolder = loader_gen as LoadHolder<T>;
|
||||
|
||||
if( loaderHolder != null )
|
||||
{
|
||||
var rf_raw = loaderHolder.dlgtLoad( filename );
|
||||
Ref<T> rf = rf_raw as Ref<T>;
|
||||
return rf;
|
||||
}
|
||||
*/
|
||||
|
||||
return new Ref<T>( filename );
|
||||
}
|
||||
|
||||
return new Ref<T>( filename );
|
||||
}
|
||||
|
||||
static public Ref load( string filename, Type t )
|
||||
{
|
||||
Load loader;
|
||||
Resource.mgr.m_loaders.TryGetValue( t, out loader );
|
||||
|
||||
if( loader != null )
|
||||
//*
|
||||
static public Ref lookup( string filename, Type t )
|
||||
{
|
||||
var rf_raw = loader( filename );
|
||||
return rf_raw;
|
||||
/*
|
||||
LoadHolder loader_gen;
|
||||
Resource.mgr.m_loaders.TryGetValue( t, out loader_gen );
|
||||
|
||||
var lhGenType = typeof(LoadHolder<>);
|
||||
|
||||
|
||||
if( loaderHolder != null )
|
||||
{
|
||||
var rf_raw = loaderHolder.load( filename );
|
||||
return rf_raw;
|
||||
}
|
||||
*/
|
||||
|
||||
return new Ref( filename );
|
||||
}
|
||||
//*/
|
||||
|
||||
static public T load<T>( string filename ) where T : class
|
||||
{
|
||||
if( ResCache<T>.s_cache.TryGetValue( filename, out var wr ) )
|
||||
{
|
||||
if( wr.TryGetTarget(out var v) ) return v;
|
||||
|
||||
lib.Log.info( $"{filename} was in cache, but its been dropped, reloading." );
|
||||
}
|
||||
|
||||
lib.Log.warn( $"Block Loading {filename}." );
|
||||
|
||||
var newV = actualLoad<T>( filename );
|
||||
|
||||
return newV;
|
||||
}
|
||||
|
||||
return new Ref<object>( filename );
|
||||
}
|
||||
static public T actualLoad<T>( string filename ) where T : class
|
||||
{
|
||||
if( s_loading.TryGetValue( filename, out var evt ) )
|
||||
{
|
||||
evt.WaitOne();
|
||||
|
||||
private Mgr()
|
||||
{
|
||||
if( ResCache<T>.s_cache.TryGetValue( filename, out var wr ) )
|
||||
{
|
||||
if( wr.TryGetTarget( out var v ) )
|
||||
return v;
|
||||
|
||||
lib.Log.error( $"{filename} was in cache, but its been dropped, reloading." );
|
||||
}
|
||||
}
|
||||
|
||||
var evtNew = new AutoResetEvent( false );
|
||||
|
||||
if( ImmutableInterlocked.TryAdd( ref s_loading, filename, evtNew ) )
|
||||
{
|
||||
if( Resource.mgr.m_loaders.TryGetValue( typeof(T), out var loaderGen ) )
|
||||
{
|
||||
var loader = loaderGen as LoadHolder<T>;
|
||||
|
||||
var v = loader.dlgtLoad( filename );
|
||||
|
||||
var weak = new WeakReference<T>( v );
|
||||
|
||||
var alreadyAdded = !ImmutableInterlocked.TryAdd( ref ResCache<T>.s_cache, filename, weak );
|
||||
|
||||
evtNew.Set();
|
||||
|
||||
//Done loading
|
||||
if( !ImmutableInterlocked.TryRemove( ref s_loading, filename, out var oldEvt ) )
|
||||
{
|
||||
lib.Log.error( $"Error removing loading event for {filename}" );
|
||||
}
|
||||
|
||||
if( alreadyAdded )
|
||||
{
|
||||
lib.Log.error( $"Key {filename} already existed, though it shouldnt." );
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
else
|
||||
{
|
||||
lib.Log.error( $"Loader could not be found for type {typeof(T)}" );
|
||||
|
||||
return ResCache<T>.s_default;
|
||||
}
|
||||
}
|
||||
|
||||
return actualLoad<T>( filename );
|
||||
}
|
||||
|
||||
static object s_loadingLock = new object();
|
||||
|
||||
static ImmutableDictionary< string, AutoResetEvent > s_loading = ImmutableDictionary< string, AutoResetEvent >.Empty;
|
||||
static ImmDefLoad s_deferredLoad = ImmDefLoad.Empty;
|
||||
|
||||
|
||||
Mgr()
|
||||
{
|
||||
var ts = new ThreadStart( deferredLoader );
|
||||
|
||||
m_deferredLoader = new Thread( ts );
|
||||
|
||||
m_deferredLoader.Start();
|
||||
}
|
||||
|
||||
void deferredLoader()
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
Thread.Sleep( 1 );
|
||||
|
||||
if( ImmutableInterlocked.TryDequeue( ref s_deferredLoad, out var v ) )
|
||||
{
|
||||
v.Item2.load();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ImmutableDictionary<Type, LoadHolder> m_loaders = ImmutableDictionary<Type, LoadHolder>.Empty;
|
||||
|
||||
Thread m_deferredLoader;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Dictionary<Type, Load> m_loaders = new Dictionary<Type, Load>();
|
||||
//private Dictionary<Type, LoadType> m_loadersOfType = new Dictionary<Type, LoadType>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user