Here's a dotnet 5.0
or later struct that can save and load the current state of a System.Random
instance. This can surely be improved but it works as it should. Also, I'm pretty sure it only works with System.Random
instances that have been created with a Seed
parameter (System.Random(int Seed)
). I noticed while debugging that using the parameterless constructor of System.Random
yields an other type of ImplBase
named XoshiroImpl
that lacks seed support therefore making my code unusable.
This can be serialized with System.TEx
Net5CompatSeedImpl
class:
https://github.com/dotnet/runtime/blob/f7633f498a8be34bee739b240a0aa9ae6a660cd9/src/libraries/System.Private.CoreLib/src/System/Random.Net5CompatImpl.cs#L283
XoshiroImpl
class:
https://github.com/dotnet/runtime/blob/4017327955f1d8ddc43980eb1848c52fbb131dfc/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro128StarStarImpl.cs
using System.Reflection;
public struct RandomState
{
static RandomState()
{
ImplInfo = typeof(Random).GetField("_impl", BindingFlags.Instance | BindingFlags.NonPublic)!;
PrngInfo = Type.GetType(Net5CompatSeedImplName)!.GetField("_prng", BindingFlags.Instance | BindingFlags.NonPublic)!;
Type compatPrngType = Type.GetType(CompatPrngName)!;
seedArrayInfo = compatPrngType.GetField(SeedArrayInfoName, BindingFlags.Instance | BindingFlags.NonPublic)!;
inextInfo = compatPrngType.GetField(InextInfoName, BindingFlags.Instance | BindingFlags.NonPublic )!;
inextpInfo = compatPrngType.GetField(InextpInfoName, BindingFlags.Instance | BindingFlags.NonPublic)!;
}
public const string CompatPrngName = "System.Random+CompatPrng";
public const string Net5CompatSeedImplName = "System.Random+Net5CompatSeedImpl";
public const string SeedArrayInfoName = "_seedArray";
public const string InextInfoName = "_inext";
public const string InextpInfoName = "_inextp";
private static FieldInfo ImplInfo;
private static FieldInfo PrngInfo;
private static FieldInfo seedArrayInfo;
private static FieldInfo inextInfo;
private static FieldInfo inextpInfo;
public int[] seedState { get; set; }
public int inext { get; set; }
public int inextp { get; set; }
public static RandomState GetState(Random random)
{
object o = GetCompatPrng(random);
RandomState state = new RandomState();
state.seedState = (int[])seedArrayInfo.GetValue(o)!;
state.inext = (int)inextInfo.GetValue(o)!;
state.inextp = (int)inextpInfo.GetValue(o)!;
return state;
}
//Random > Impl > CompatPrng
public static object GetImpl(Random random) => ImplInfo.GetValueDirect(__makeref(random))!;
public static object GetCompatPrng(object impl) => PrngInfo.GetValueDirect(__makeref(impl))!;
public static object GetCompatPrng(Random random)
{
object impl = GetImpl(random);
return PrngInfo.GetValueDirect(__makeref(impl))!;
}
public static void SetState(Random random, RandomState state)
{
object impl = GetImpl(random);
TypedReference implref = __makeref(impl);
object prng = PrngInfo.GetValueDirect(implref)!;
seedArrayInfo.SetValue(prng, state.seedState);
inextInfo.SetValue(prng, state.inext);
inextpInfo.SetValue(prng, state.inextp);
PrngInfo.SetValueDirect(implref, prng);
//Testing. can be removed.
/*object o2 = GetCompatPrng(impl);
DehFwk.Debug.Log("orig: " + ((int[])seedArrayInfo.GetValue(prng)!).Length + "| new: " + ((int[])seedArrayInfo.GetValue(o2)!).Length + " vs " + state.seedState.Length);
DehFwk.Debug.Log("orig: " + inextInfo.GetValue(prng)! + " " + "| new: " + inextInfo.GetValue(o2) + " vs " + state.inext);
DehFwk.Debug.Log("orig: " + inextpInfo.GetValue(prng) + "| new: " + inextpInfo.GetValue(o2) + " vs " + state.inextp);*/
}
}