mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Multithread SerializationManager initialize.
This commit is contained in:
@@ -20,7 +20,7 @@ namespace Robust.Client.ResourceManagement
|
||||
{
|
||||
var sawmill = _logManager.GetSawmill("res.preload");
|
||||
|
||||
if (!_configurationManager.GetCVar(CVars.TexturePreloadingEnabled))
|
||||
if (!_configurationManager.GetCVar(CVars.ResTexturePreloadingEnabled))
|
||||
{
|
||||
sawmill.Debug($"Skipping texture preloading due to CVar value.");
|
||||
return;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.3" />
|
||||
<PackageReference Include="nfluidsynth" Version="0.3.1" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
|
||||
@@ -518,17 +518,6 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<float> AngularSlop =
|
||||
CVarDef.Create("physics.angularslop", 2.0f / 180.0f * MathF.PI);
|
||||
|
||||
/// <summary>
|
||||
/// The radius of the polygon/edge shape skin. This should not be modified. Making
|
||||
/// this smaller means polygons will have an insufficient buffer for continuous collision.
|
||||
/// Making it larger may create artifacts for vertex collision.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Default is set to be 2 x linearslop. TODO Should we listen to linearslop changes?
|
||||
/// </remarks>
|
||||
public static readonly CVarDef<float> PolygonRadius =
|
||||
CVarDef.Create("physics.polygonradius", 2 * 0.005f);
|
||||
|
||||
/// <summary>
|
||||
/// If true, it will run a GiftWrap convex hull on all polygon inputs.
|
||||
/// This makes for a more stable engine when given random input,
|
||||
@@ -583,9 +572,12 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<bool> ResCheckPathCasing =
|
||||
CVarDef.Create("res.checkpathcasing", false);
|
||||
|
||||
public static readonly CVarDef<bool> TexturePreloadingEnabled =
|
||||
public static readonly CVarDef<bool> ResTexturePreloadingEnabled =
|
||||
CVarDef.Create("res.texturepreloadingenabled", true, CVar.CLIENTONLY);
|
||||
|
||||
public static readonly CVarDef<bool> ResTexturePreloadCache =
|
||||
CVarDef.Create("res.texture_preload_cache", true, CVar.CLIENTONLY);
|
||||
|
||||
|
||||
/*
|
||||
* DEBUG
|
||||
|
||||
@@ -349,7 +349,7 @@ namespace Robust.Shared.Physics.Collision
|
||||
|
||||
internal EPCollider(IConfigurationManager configManager)
|
||||
{
|
||||
_polygonRadius = configManager.GetCVar(CVars.PolygonRadius);
|
||||
_polygonRadius = PhysicsConstants.PolygonRadius;
|
||||
_angularSlop = configManager.GetCVar(CVars.AngularSlop);
|
||||
_polygonB = new TempPolygon(configManager);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
public EdgeShape(Vector2 start, Vector2 end)
|
||||
{
|
||||
Set(start, end);
|
||||
_radius = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.PolygonRadius);
|
||||
_radius = PhysicsConstants.PolygonRadius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
|
||||
public PhysShapeAabb()
|
||||
{
|
||||
_radius = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.PolygonRadius);
|
||||
_radius = PhysicsConstants.PolygonRadius;
|
||||
}
|
||||
|
||||
public Box2 ComputeAABB(Transform transform, int childIndex)
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
|
||||
public PolygonShape()
|
||||
{
|
||||
_radius = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.PolygonRadius);
|
||||
_radius = PhysicsConstants.PolygonRadius;
|
||||
}
|
||||
|
||||
public PolygonShape(float radius)
|
||||
|
||||
15
Robust.Shared/Physics/PhysicsConstants.cs
Normal file
15
Robust.Shared/Physics/PhysicsConstants.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Robust.Shared.Physics
|
||||
{
|
||||
internal static class PhysicsConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// The radius of the polygon/edge shape skin. This should not be modified. Making
|
||||
/// this smaller means polygons will have an insufficient buffer for continuous collision.
|
||||
/// Making it larger may create artifacts for vertex collision.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Default is set to be 2 x linearslop. TODO Should we listen to linearslop changes?
|
||||
/// </remarks>
|
||||
public const float PolygonRadius = 2 * 0.005f;
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,10 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Prototypes
|
||||
@@ -18,9 +18,9 @@ namespace Robust.Shared.Prototypes
|
||||
/// Prototype that represents game entities.
|
||||
/// </summary>
|
||||
[Prototype("entity", -1)]
|
||||
public class EntityPrototype : IPrototype, IInheritingPrototype
|
||||
public class EntityPrototype : IPrototype, IInheritingPrototype, ISerializationHooks
|
||||
{
|
||||
private readonly ILocalizationManager _loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
private ILocalizationManager _loc = default!;
|
||||
|
||||
private static readonly Dictionary<string, string> LocPropertiesDefault = new();
|
||||
|
||||
@@ -155,6 +155,11 @@ namespace Robust.Shared.Prototypes
|
||||
Components.Add("MetaData", new MetaDataComponent());
|
||||
}
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
_loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
}
|
||||
|
||||
public bool TryGetComponent<T>(string name, [NotNullWhen(true)] out T? component) where T : IComponent
|
||||
{
|
||||
if (!Components.TryGetValue(name, out var componentUnCast))
|
||||
|
||||
@@ -193,7 +193,7 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
}
|
||||
|
||||
public class PrototypeManager : IPrototypeManager, IPostInjectInit
|
||||
public class PrototypeManager : IPrototypeManager
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] protected readonly IResourceManager Resources = default!;
|
||||
@@ -223,6 +223,7 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
ReloadPrototypeTypes();
|
||||
}
|
||||
|
||||
public IEnumerable<T> EnumeratePrototypes<T>() where T : class, IPrototype
|
||||
@@ -605,12 +606,6 @@ namespace Robust.Shared.Prototypes
|
||||
|
||||
#endregion IPrototypeManager members
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
_reflectionManager.OnAssemblyAdded += (_, _) => ReloadPrototypeTypes();
|
||||
ReloadPrototypeTypes();
|
||||
}
|
||||
|
||||
private void ReloadPrototypeTypes()
|
||||
{
|
||||
Clear();
|
||||
|
||||
@@ -111,5 +111,6 @@ namespace Robust.Shared.Reflection
|
||||
bool TryParseEnumReference(string reference, [NotNullWhen(true)] out Enum? @enum);
|
||||
|
||||
Type? YamlTypeTagLookup(Type baseType, string typeName);
|
||||
IEnumerable<Type> FindAllTypes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Robust.Shared.Reflection
|
||||
|
||||
private readonly Dictionary<string, Enum> _enumCache = new();
|
||||
|
||||
private readonly List<Type> _getAllTypesCache = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Type> GetAllChildren<T>(bool inclusive = false)
|
||||
{
|
||||
@@ -42,56 +44,47 @@ namespace Robust.Shared.Reflection
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Type> GetAllChildren(Type baseType, bool inclusive = false)
|
||||
{
|
||||
var typeLists = new List<Type[]>(Assemblies.Count);
|
||||
try
|
||||
{
|
||||
// There's very little assemblies, so storing these temporarily is cheap.
|
||||
// We need to do it ahead of time so that we can catch ReflectionTypeLoadException HERE,
|
||||
// so whoever is using us doesn't have to handle them.
|
||||
foreach (var assembly in Assemblies)
|
||||
{
|
||||
typeLists.Add(assembly.GetTypes());
|
||||
}
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
Logger.Error("Caught ReflectionTypeLoadException! Dumping child exceptions:");
|
||||
if (e.LoaderExceptions != null)
|
||||
{
|
||||
foreach (var inner in e.LoaderExceptions)
|
||||
{
|
||||
if (inner != null)
|
||||
{
|
||||
Logger.Error(inner.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
EnsureGetAllTypesCache();
|
||||
|
||||
throw;
|
||||
foreach (var type in _getAllTypesCache)
|
||||
{
|
||||
if (!baseType.IsAssignableFrom(type) || type.IsAbstract)
|
||||
continue;
|
||||
|
||||
if (baseType == type && !inclusive)
|
||||
continue;
|
||||
|
||||
yield return type;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureGetAllTypesCache()
|
||||
{
|
||||
if (_getAllTypesCache.Count != 0)
|
||||
return;
|
||||
|
||||
var totalLength = 0;
|
||||
var typeSets = new List<Type[]>();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
typeSets.Add(types);
|
||||
totalLength += types.Length;
|
||||
}
|
||||
|
||||
foreach (var t in typeLists)
|
||||
{
|
||||
foreach (var type in t)
|
||||
{
|
||||
if (!baseType.IsAssignableFrom(type) || type.IsAbstract)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_getAllTypesCache.Capacity = totalLength;
|
||||
|
||||
foreach (var typeSet in typeSets)
|
||||
{
|
||||
foreach (var type in typeSet)
|
||||
{
|
||||
var attribute = (ReflectAttribute?) Attribute.GetCustomAttribute(type, typeof(ReflectAttribute));
|
||||
|
||||
if (!(attribute?.Discoverable ?? ReflectAttribute.DEFAULT_DISCOVERABLE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (baseType == type && !inclusive)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return type;
|
||||
_getAllTypesCache.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,6 +94,7 @@ namespace Robust.Shared.Reflection
|
||||
public void LoadAssemblies(IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
this.assemblies.AddRange(assemblies);
|
||||
_getAllTypesCache.Clear();
|
||||
OnAssemblyAdded?.Invoke(this, new ReflectionUpdateEventArgs(this));
|
||||
}
|
||||
|
||||
@@ -167,14 +161,14 @@ namespace Robust.Shared.Reflection
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Type> FindTypesWithAttribute(Type attributeType)
|
||||
{
|
||||
var types = new List<Type>();
|
||||
EnsureGetAllTypesCache();
|
||||
return _getAllTypesCache.Where(type => Attribute.IsDefined(type, attributeType));
|
||||
}
|
||||
|
||||
foreach (var assembly in Assemblies)
|
||||
{
|
||||
types.AddRange(assembly.GetTypes().Where(type => Attribute.IsDefined(type, attributeType)));
|
||||
}
|
||||
|
||||
return types;
|
||||
public IEnumerable<Type> FindAllTypes()
|
||||
{
|
||||
EnsureGetAllTypesCache();
|
||||
return _getAllTypesCache;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -12,9 +13,9 @@ namespace Robust.Shared.Serialization.Manager
|
||||
|
||||
private readonly Dictionary<Type, Type> _constantsMapping = new();
|
||||
|
||||
private void InitializeFlagsAndConstants()
|
||||
private void InitializeFlagsAndConstants(IEnumerable<Type> flags, IEnumerable<Type> constants)
|
||||
{
|
||||
foreach (Type constType in _reflectionManager.FindTypesWithAttribute<ConstantsForAttribute>())
|
||||
foreach (Type constType in constants)
|
||||
{
|
||||
if (!constType.IsEnum)
|
||||
{
|
||||
@@ -37,7 +38,7 @@ namespace Robust.Shared.Serialization.Manager
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var bitflagType in _reflectionManager.FindTypesWithAttribute<FlagsForAttribute>())
|
||||
foreach (var bitflagType in flags)
|
||||
{
|
||||
if (!bitflagType.IsEnum)
|
||||
{
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace Robust.Shared.Serialization.Manager
|
||||
private readonly Dictionary<Type, Type> _genericCopierTypes = new();
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericValidatorTypes = new();
|
||||
|
||||
private void InitializeTypeSerializers()
|
||||
private void InitializeTypeSerializers(IEnumerable<Type> typeSerializers)
|
||||
{
|
||||
foreach (var type in _reflectionManager.FindTypesWithAttribute<TypeSerializerAttribute>())
|
||||
foreach (var type in typeSerializers)
|
||||
{
|
||||
RegisterSerializer(type);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
@@ -6,6 +7,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
@@ -41,59 +43,79 @@ namespace Robust.Shared.Serialization.Manager
|
||||
public void Initialize()
|
||||
{
|
||||
if (_initializing)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(SerializationManager)} is already being initialized.");
|
||||
}
|
||||
|
||||
if (_initialized)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(SerializationManager)} has already been initialized.");
|
||||
}
|
||||
|
||||
_initializing = true;
|
||||
|
||||
DependencyCollection = IoCManager.Instance ?? throw new NullReferenceException();
|
||||
|
||||
InitializeFlagsAndConstants();
|
||||
InitializeTypeSerializers();
|
||||
var flagsTypes = new ConcurrentBag<Type>();
|
||||
var constantsTypes = new ConcurrentBag<Type>();
|
||||
var typeSerializers = new ConcurrentBag<Type>();
|
||||
var meansDataDef = new ConcurrentBag<Type>();
|
||||
var implicitDataDefForInheritors = new ConcurrentBag<Type>();
|
||||
|
||||
var registrations = new HashSet<Type>();
|
||||
CollectAttributedTypes(flagsTypes, constantsTypes, typeSerializers, meansDataDef, implicitDataDefForInheritors);
|
||||
|
||||
foreach (var baseType in _reflectionManager.FindTypesWithAttribute<ImplicitDataDefinitionForInheritorsAttribute>())
|
||||
InitializeFlagsAndConstants(flagsTypes, constantsTypes);
|
||||
InitializeTypeSerializers(typeSerializers);
|
||||
|
||||
// This is a bag, not a hash set.
|
||||
// Duplicates are fine since the CWT<,> won't re-run the constructor if it's already in there.
|
||||
var registrations = new ConcurrentBag<Type>();
|
||||
|
||||
foreach (var baseType in implicitDataDefForInheritors)
|
||||
{
|
||||
if (!baseType.IsAbstract && !baseType.IsInterface && !baseType.IsGenericTypeDefinition) registrations.Add(baseType);
|
||||
foreach (var child in _reflectionManager.GetAllChildren(baseType))
|
||||
// Inherited attributes don't work with interfaces.
|
||||
if (baseType.IsInterface)
|
||||
{
|
||||
if (child.IsAbstract || child.IsInterface || child.IsGenericTypeDefinition) continue;
|
||||
registrations.Add(child);
|
||||
foreach (var child in _reflectionManager.GetAllChildren(baseType))
|
||||
{
|
||||
if (child.IsAbstract || child.IsGenericTypeDefinition)
|
||||
continue;
|
||||
|
||||
registrations.Add(child);
|
||||
}
|
||||
}
|
||||
else if (!baseType.IsAbstract && !baseType.IsGenericTypeDefinition)
|
||||
{
|
||||
registrations.Add(baseType);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var meansAttr in _reflectionManager.FindTypesWithAttribute<MeansDataDefinitionAttribute>())
|
||||
Parallel.ForEach(_reflectionManager.FindAllTypes(), type =>
|
||||
{
|
||||
foreach (var type in _reflectionManager.FindTypesWithAttribute(meansAttr))
|
||||
foreach (var meansDataDefAttr in meansDataDef)
|
||||
{
|
||||
registrations.Add(type);
|
||||
if (type.IsDefined(meansDataDefAttr))
|
||||
registrations.Add(type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var type in registrations)
|
||||
var sawmill = Logger.GetSawmill(LogCategory);
|
||||
|
||||
Parallel.ForEach(registrations, type =>
|
||||
{
|
||||
if (type.IsAbstract || type.IsInterface || type.IsGenericTypeDefinition)
|
||||
{
|
||||
Logger.DebugS(LogCategory, $"Skipping registering data definition for type {type} since it is abstract or an interface");
|
||||
continue;
|
||||
sawmill.Debug(
|
||||
$"Skipping registering data definition for type {type} since it is abstract or an interface");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!type.IsValueType && type.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
|
||||
.FirstOrDefault(m => m.GetParameters().Length == 0) == null)
|
||||
{
|
||||
Logger.DebugS(LogCategory, $"Skipping registering data definition for type {type} since it has no parameterless ctor");
|
||||
continue;
|
||||
sawmill.Debug(
|
||||
$"Skipping registering data definition for type {type} since it has no parameterless ctor");
|
||||
return;
|
||||
}
|
||||
|
||||
DataDefinitions.GetValue(type, CreateDefinitionCallback);
|
||||
}
|
||||
});
|
||||
|
||||
var error = new StringBuilder();
|
||||
|
||||
@@ -110,17 +132,42 @@ namespace Robust.Shared.Serialization.Manager
|
||||
throw new ArgumentException($"Duplicate data field tags found in:\n{error}");
|
||||
}
|
||||
|
||||
foreach (var type in _reflectionManager.FindTypesWithAttribute<CopyByRefAttribute>())
|
||||
{
|
||||
_copyByRefRegistrations.Add(type);
|
||||
}
|
||||
|
||||
_copyByRefRegistrations.Add(typeof(Type));
|
||||
|
||||
_initialized = true;
|
||||
_initializing = false;
|
||||
}
|
||||
|
||||
private void CollectAttributedTypes(
|
||||
ConcurrentBag<Type> flagsTypes,
|
||||
ConcurrentBag<Type> constantsTypes,
|
||||
ConcurrentBag<Type> typeSerializers,
|
||||
ConcurrentBag<Type> meansDataDef,
|
||||
ConcurrentBag<Type> implicitDataDefForInheritors)
|
||||
{
|
||||
// IsDefined is extremely slow. Great.
|
||||
Parallel.ForEach(_reflectionManager.FindAllTypes(), type =>
|
||||
{
|
||||
if (type.IsDefined(typeof(FlagsForAttribute), false))
|
||||
flagsTypes.Add(type);
|
||||
|
||||
if (type.IsDefined(typeof(ConstantsForAttribute), false))
|
||||
constantsTypes.Add(type);
|
||||
|
||||
if (type.IsDefined(typeof(TypeSerializerAttribute)))
|
||||
typeSerializers.Add(type);
|
||||
|
||||
if (type.IsDefined(typeof(MeansDataDefinitionAttribute)))
|
||||
meansDataDef.Add(type);
|
||||
|
||||
if (type.IsDefined(typeof(ImplicitDataDefinitionForInheritorsAttribute), true))
|
||||
implicitDataDefForInheritors.Add(type);
|
||||
|
||||
if (type.IsDefined(typeof(CopyByRefAttribute)))
|
||||
_copyByRefRegistrations.Add(type);
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly ConditionalWeakTable<Type, DataDefinition>.CreateValueCallback
|
||||
CreateDefinitionCallback = CreateDataDefinition;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary
|
||||
{
|
||||
[TypeSerializer]
|
||||
// [TypeSerializer]
|
||||
public class PrototypeIdDictionarySerializer<TValue, TPrototype> :
|
||||
ITypeSerializer<Dictionary<string, TValue>, MappingDataNode>,
|
||||
ITypeSerializer<SortedDictionary<string, TValue>, MappingDataNode>,
|
||||
|
||||
@@ -590,7 +590,7 @@ namespace Robust.UnitTesting
|
||||
(CVars.DiscordEnabled.Name, "false"),
|
||||
|
||||
// Avoid preloading textures.
|
||||
(CVars.TexturePreloadingEnabled.Name, "false"),
|
||||
(CVars.ResTexturePreloadingEnabled.Name, "false"),
|
||||
});
|
||||
|
||||
GameLoop = new IntegrationGameLoop(DependencyCollection.Resolve<IGameTiming>(),
|
||||
|
||||
Reference in New Issue
Block a user