From c301ce88ab42269420edabe5a90725c2f072ac8c Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 25 Jan 2020 21:49:52 -0800 Subject: [PATCH] Update Random to be a bit more thread safe Random isn't thread safe; users of PKHeX.Core.dll might run multithreaded operations (see PKSM + ALM), so we need to have a thread-specific RNG available. Thread Local get; to improve performance, save the random object locally whenever it is used more than once in the method. https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadlocal-1?redirectedfrom=MSDN&view=netframework-4.8 https://stackoverflow.com/questions/18333885/threadstatic-v-s-threadlocalt-is-generic-better-than-attribute/18337158#18337158 --- .../Editing/Applicators/GenderApplicator.cs | 3 +- PKHeX.Core/Editing/CommonEdits.cs | 2 +- PKHeX.Core/Legality/Enums/EncounterTime.cs | 5 ++-- PKHeX.Core/Legality/RNG/PIDGenerator.cs | 13 +++++---- PKHeX.Core/Legality/Restrictions/Memories.cs | 3 +- PKHeX.Core/Legality/Structures/IVersion.cs | 3 +- PKHeX.Core/MysteryGifts/PGF.cs | 14 +++++---- PKHeX.Core/MysteryGifts/WB7.cs | 17 ++++++----- PKHeX.Core/MysteryGifts/WC6.cs | 13 +++++---- PKHeX.Core/MysteryGifts/WC7.cs | 13 +++++---- PKHeX.Core/MysteryGifts/WC8.cs | 12 ++++---- PKHeX.Core/PKM/PK1.cs | 7 +++-- PKHeX.Core/PKM/PK2.cs | 7 +++-- PKHeX.Core/PKM/PKM.cs | 29 ++++++++++++------- PKHeX.Core/PKM/Shared/PokeSize.cs | 6 +++- PKHeX.Core/PKM/Util/PKX.cs | 13 +++++---- PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs | 5 ++-- .../Substructures/Gen8/RaidSpawnList8.cs | 5 ++-- PKHeX.Core/Util/RandUtil.cs | 19 +++++++----- .../Controls/PKM Editor/StatEditor.cs | 3 +- PKHeX.WinForms/MainWindow/Main.cs | 5 +++- .../Subforms/Save Editors/Gen5/SAV_Misc5.cs | 5 ++-- .../Save Editors/Gen6/SAV_PokeBlockORAS.cs | 3 +- 23 files changed, 128 insertions(+), 77 deletions(-) diff --git a/PKHeX.Core/Editing/Applicators/GenderApplicator.cs b/PKHeX.Core/Editing/Applicators/GenderApplicator.cs index ab06da27877..e50e8a564bd 100644 --- a/PKHeX.Core/Editing/Applicators/GenderApplicator.cs +++ b/PKHeX.Core/Editing/Applicators/GenderApplicator.cs @@ -66,8 +66,9 @@ public static int GetSaneGender(this PKM pk) /// Desired . public static void SetATKIVGender(this PKM pk, int gender) { + var rnd = Util.Rand; while (pk.Gender != gender) - pk.IV_ATK = Util.Rand.Next(pk.MaxIV + 1); + pk.IV_ATK = rnd.Next(16); } } } \ No newline at end of file diff --git a/PKHeX.Core/Editing/CommonEdits.cs b/PKHeX.Core/Editing/CommonEdits.cs index 4a7fc839d93..fc9fc981d7a 100644 --- a/PKHeX.Core/Editing/CommonEdits.cs +++ b/PKHeX.Core/Editing/CommonEdits.cs @@ -90,7 +90,7 @@ public static void SetAbilityIndex(this PKM pk, int abilIndex) if (pk is PK5 pk5 && abilIndex == 2) pk5.HiddenAbility = true; else if (pk.Format <= 5) - pk.PID = PKX.GetRandomPID(pk.Species, pk.Gender, pk.Version, pk.Nature, pk.AltForm, (uint)(abilIndex * 0x10001)); + pk.PID = PKX.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.AltForm, (uint)(abilIndex * 0x10001)); pk.RefreshAbility(abilIndex); } diff --git a/PKHeX.Core/Legality/Enums/EncounterTime.cs b/PKHeX.Core/Legality/Enums/EncounterTime.cs index cfc8a46e2ff..59152446446 100644 --- a/PKHeX.Core/Legality/Enums/EncounterTime.cs +++ b/PKHeX.Core/Legality/Enums/EncounterTime.cs @@ -20,11 +20,12 @@ internal static class EncounterTimeExtension internal static int RandomValidTime(this EncounterTime t1) { - int val = Util.Rand.Next(1, 4); + var rnd = Util.Rand; + int val = rnd.Next(1, 4); if (t1 == EncounterTime.Any) return val; while (!t1.Contains(val)) - val = Util.Rand.Next(1, 4); + val = rnd.Next(1, 4); return val; } } diff --git a/PKHeX.Core/Legality/RNG/PIDGenerator.cs b/PKHeX.Core/Legality/RNG/PIDGenerator.cs index cfaa0554889..8c90dd6dde1 100644 --- a/PKHeX.Core/Legality/RNG/PIDGenerator.cs +++ b/PKHeX.Core/Legality/RNG/PIDGenerator.cs @@ -401,14 +401,15 @@ private static void SetRandomWildPID(PKM pk, int nature, int ability, int gender private static void SetRandomIVs(PKM pk) { + var rng = Util.Rand; pk.IVs = new[] { - Util.Rand.Next(32), - Util.Rand.Next(32), - Util.Rand.Next(32), - Util.Rand.Next(32), - Util.Rand.Next(32), - Util.Rand.Next(32), + rng.Next(32), + rng.Next(32), + rng.Next(32), + rng.Next(32), + rng.Next(32), + rng.Next(32), }; } } diff --git a/PKHeX.Core/Legality/Restrictions/Memories.cs b/PKHeX.Core/Legality/Restrictions/Memories.cs index c16e0d44348..52992f76a94 100644 --- a/PKHeX.Core/Legality/Restrictions/Memories.cs +++ b/PKHeX.Core/Legality/Restrictions/Memories.cs @@ -183,9 +183,10 @@ public static bool CanHaveIntensity(int memory, int intensity) public static int GetRandomFeeling(int memory, int max = 24) { var bits = MemoryFeelings[memory]; + var rnd = Util.Rand; while (true) { - int feel = Util.Rand.Next(max); + int feel = rnd.Next(max); if ((bits & (1 << feel)) != 0) return feel; } diff --git a/PKHeX.Core/Legality/Structures/IVersion.cs b/PKHeX.Core/Legality/Structures/IVersion.cs index 351c81e4d26..fa578598170 100644 --- a/PKHeX.Core/Legality/Structures/IVersion.cs +++ b/PKHeX.Core/Legality/Structures/IVersion.cs @@ -38,9 +38,10 @@ private static GameVersion GetSingleVersion(this IVersion ver) const int max = (int) GameVersion.RB; if ((int)ver.Version < max) return ver.Version; + var rnd = Util.Rand; while (true) // this isn't optimal, but is low maintenance { - var game = (GameVersion)Util.Rand.Next(1, max); + var game = (GameVersion)rnd.Next(1, max); if (ver.CanBeReceivedBy(game)) return game; } diff --git a/PKHeX.Core/MysteryGifts/PGF.cs b/PKHeX.Core/MysteryGifts/PGF.cs index 55a1caf7371..beda012644c 100644 --- a/PKHeX.Core/MysteryGifts/PGF.cs +++ b/PKHeX.Core/MysteryGifts/PGF.cs @@ -166,6 +166,8 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if (!IsPokémon) throw new ArgumentException(nameof(IsPokémon)); + var rnd = Util.Rand; + var dt = DateTime.Now; if (Day == 0) { @@ -174,14 +176,14 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) Year = (byte)dt.Year; } - int currentLevel = Level > 0 ? Level : Util.Rand.Next(100) + 1; + int currentLevel = Level > 0 ? Level : rnd.Next(1, 101); var pi = PersonalTable.B2W2.GetFormeEntry(Species, Form); PK5 pk = new PK5 { Species = Species, HeldItem = HeldItem, Met_Level = currentLevel, - Nature = Nature != -1 ? Nature : Util.Rand.Next(25), + Nature = Nature != -1 ? Nature : rnd.Next(25), AltForm = Form, Version = OriginGame == 0 ? SAV.Game : OriginGame, Language = Language == 0 ? SAV.Language : Language, @@ -223,7 +225,7 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) FatefulEncounter = true, }; if (SAV.Generation > 5 && OriginGame == 0) // Gen6+, give random gen5 game - pk.Version = (int)GameVersion.W + Util.Rand.Next(4); + pk.Version = (int)GameVersion.W + rnd.Next(4); if (Move1 == 0) // No moves defined pk.Moves = MoveLevelUp.GetEncounterMoves(Species, Form, Level, (GameVersion)pk.Version); @@ -304,7 +306,8 @@ private void SetPID(PKM pk, int av) pk.PID = Util.Rand32(); // Force Gender - do { pk.PID = (pk.PID & 0xFFFFFF00) | (uint)Util.Rand.Next(0x100); } + var rnd = Util.Rand; + do { pk.PID = (pk.PID & 0xFFFFFF00) | (uint)rnd.Next(0x100); } while (!pk.IsGenderValid()); if (PIDType == 2) // Always @@ -327,8 +330,9 @@ private void SetPID(PKM pk, int av) private void SetIVs(PKM pk) { int[] finalIVs = new int[6]; + var rnd = Util.Rand; for (int i = 0; i < IVs.Length; i++) - finalIVs[i] = IVs[i] == 0xFF ? Util.Rand.Next(pk.MaxIV + 1) : IVs[i]; + finalIVs[i] = IVs[i] == 0xFF ? rnd.Next(32) : IVs[i]; pk.IVs = finalIVs; } diff --git a/PKHeX.Core/MysteryGifts/WB7.cs b/PKHeX.Core/MysteryGifts/WB7.cs index df3dc9bb499..d412840aaa8 100644 --- a/PKHeX.Core/MysteryGifts/WB7.cs +++ b/PKHeX.Core/MysteryGifts/WB7.cs @@ -317,7 +317,9 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if (!IsPokémon) throw new ArgumentException(nameof(IsPokémon)); - int currentLevel = Level > 0 ? Level : Util.Rand.Next(100) + 1; + var rnd = Util.Rand; + + int currentLevel = Level > 0 ? Level : rnd.Next(1, 101); int metLevel = MetLevel > 0 ? MetLevel : currentLevel; var pi = PersonalTable.GG.GetFormeEntry(Species, Form); var OT = GetOT(SAV.Language); @@ -370,7 +372,7 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if ((SAV.Generation > Format && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version)) { // give random valid game - do { pk.Version = (int)GameVersion.GP + Util.Rand.Next(2); } + do { pk.Version = (int)GameVersion.GP + rnd.Next(2); } while (!CanBeReceivedByVersion(pk.Version)); } @@ -390,8 +392,8 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) SetEggMetData(pk); pk.CurrentFriendship = pk.IsEgg ? pi.HatchCycles : pi.BaseFriendship; - pk.HeightScalar = Util.Rand.Next(0x100); - pk.WeightScalar = Util.Rand.Next(0x100); + pk.HeightScalar = rnd.Next(0x100); + pk.WeightScalar = rnd.Next(0x100); pk.ResetCalculatedValues(); // cp & dimensions pk.RefreshChecksum(); @@ -458,18 +460,19 @@ private void SetIVs(PKM pk) { int[] finalIVs = new int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); + var rng = Util.Rand; if (ivflag == 0) // Random IVs { for (int i = 0; i < 6; i++) - finalIVs[i] = IVs[i] > 31 ? Util.Rand.Next(pk.MaxIV + 1) : IVs[i]; + finalIVs[i] = IVs[i] > 31 ? rng.Next(32) : IVs[i]; } else // 1/2/3 perfect IVs { int IVCount = ivflag - 0xFB; - do { finalIVs[Util.Rand.Next(6)] = 31; } + do { finalIVs[rng.Next(6)] = 31; } while (finalIVs.Count(iv => iv == 31) < IVCount); for (int i = 0; i < 6; i++) - finalIVs[i] = finalIVs[i] == 31 ? pk.MaxIV : Util.Rand.Next(pk.MaxIV + 1); + finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32); } pk.IVs = finalIVs; } diff --git a/PKHeX.Core/MysteryGifts/WC6.cs b/PKHeX.Core/MysteryGifts/WC6.cs index 64c8fe0087f..c35ea0a9a82 100644 --- a/PKHeX.Core/MysteryGifts/WC6.cs +++ b/PKHeX.Core/MysteryGifts/WC6.cs @@ -270,7 +270,9 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if (!IsPokémon) throw new ArgumentException(nameof(IsPokémon)); - int currentLevel = Level > 0 ? Level : Util.Rand.Next(100) + 1; + var rnd = Util.Rand; + + int currentLevel = Level > 0 ? Level : rnd.Next(1, 101); var pi = PersonalTable.AO.GetFormeEntry(Species, Form); PK6 pk = new PK6 { @@ -342,7 +344,7 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if ((SAV.Generation > Format && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version)) { // give random valid game - do { pk.Version = (int)GameVersion.X + Util.Rand.Next(4); } + do { pk.Version = (int)GameVersion.X + rnd.Next(4); } while (!CanBeReceivedByVersion(pk.Version)); } @@ -438,18 +440,19 @@ private void SetIVs(PKM pk) { int[] finalIVs = new int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); + var rnd = Util.Rand; if (ivflag == 0) // Random IVs { for (int i = 0; i < 6; i++) - finalIVs[i] = IVs[i] > 31 ? Util.Rand.Next(pk.MaxIV + 1) : IVs[i]; + finalIVs[i] = IVs[i] > 31 ? rnd.Next(32) : IVs[i]; } else // 1/2/3 perfect IVs { int IVCount = ivflag - 0xFB; - do { finalIVs[Util.Rand.Next(6)] = 31; } + do { finalIVs[rnd.Next(6)] = 31; } while (finalIVs.Count(iv => iv == 31) < IVCount); for (int i = 0; i < 6; i++) - finalIVs[i] = finalIVs[i] == 31 ? pk.MaxIV : Util.Rand.Next(pk.MaxIV + 1); + finalIVs[i] = finalIVs[i] == 31 ? 31 : rnd.Next(32); } pk.IVs = finalIVs; } diff --git a/PKHeX.Core/MysteryGifts/WC7.cs b/PKHeX.Core/MysteryGifts/WC7.cs index 27fc8889145..0576aeb222f 100644 --- a/PKHeX.Core/MysteryGifts/WC7.cs +++ b/PKHeX.Core/MysteryGifts/WC7.cs @@ -313,7 +313,9 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if (!IsPokémon) throw new ArgumentException(nameof(IsPokémon)); - int currentLevel = Level > 0 ? Level : Util.Rand.Next(100) + 1; + var rnd = Util.Rand; + + int currentLevel = Level > 0 ? Level : rnd.Next(1, 101); int metLevel = MetLevel > 0 ? MetLevel : currentLevel; var pi = PersonalTable.USUM.GetFormeEntry(Species, Form); PK7 pk = new PK7 @@ -384,7 +386,7 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if ((SAV.Generation > Format && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version)) { // give random valid game - do { pk.Version = (int)GameVersion.SN + Util.Rand.Next(4); } + do { pk.Version = (int)GameVersion.SN + rnd.Next(4); } while (!CanBeReceivedByVersion(pk.Version)); } @@ -469,18 +471,19 @@ private void SetIVs(PKM pk) { int[] finalIVs = new int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); + var rng = Util.Rand; if (ivflag == 0) // Random IVs { for (int i = 0; i < 6; i++) - finalIVs[i] = IVs[i] > 31 ? Util.Rand.Next(pk.MaxIV + 1) : IVs[i]; + finalIVs[i] = IVs[i] > 31 ? rng.Next(32) : IVs[i]; } else // 1/2/3 perfect IVs { int IVCount = ivflag - 0xFB; - do { finalIVs[Util.Rand.Next(6)] = 31; } + do { finalIVs[rng.Next(6)] = 31; } while (finalIVs.Count(iv => iv == 31) < IVCount); for (int i = 0; i < 6; i++) - finalIVs[i] = finalIVs[i] == 31 ? pk.MaxIV : Util.Rand.Next(pk.MaxIV + 1); + finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32); } pk.IVs = finalIVs; } diff --git a/PKHeX.Core/MysteryGifts/WC8.cs b/PKHeX.Core/MysteryGifts/WC8.cs index ec71b2604bb..1ee7a7a50dd 100644 --- a/PKHeX.Core/MysteryGifts/WC8.cs +++ b/PKHeX.Core/MysteryGifts/WC8.cs @@ -299,7 +299,7 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if (!IsPokémon) throw new ArgumentException(nameof(IsPokémon)); - int currentLevel = Level > 0 ? Level : Util.Rand.Next(100) + 1; + int currentLevel = Level > 0 ? Level : Util.Rand.Next(1, 101); int metLevel = MetLevel > 0 ? MetLevel : currentLevel; var pi = PersonalTable.SWSH.GetFormeEntry(Species, Form); var OT = GetOT(SAV.Language); @@ -356,7 +356,8 @@ public override PKM ConvertToPKM(ITrainerInfo SAV, EncounterCriteria criteria) if ((SAV.Generation > Format && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version)) { // give random valid game - do { pk.Version = (int)GameVersion.SW + Util.Rand.Next(2); } + var rnd = Util.Rand; + do { pk.Version = (int)GameVersion.SW + rnd.Next(2); } while (!CanBeReceivedByVersion(pk.Version)); } @@ -471,18 +472,19 @@ private void SetIVs(PKM pk) { int[] finalIVs = new int[6]; var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); + var rng = Util.Rand; if (ivflag == 0) // Random IVs { for (int i = 0; i < 6; i++) - finalIVs[i] = IVs[i] > 31 ? Util.Rand.Next(pk.MaxIV + 1) : IVs[i]; + finalIVs[i] = IVs[i] > 31 ? rng.Next(32) : IVs[i]; } else // 1/2/3 perfect IVs { int IVCount = ivflag - 0xFB; - do { finalIVs[Util.Rand.Next(6)] = 31; } + do { finalIVs[rng.Next(6)] = 31; } while (finalIVs.Count(iv => iv == 31) < IVCount); for (int i = 0; i < 6; i++) - finalIVs[i] = finalIVs[i] == 31 ? pk.MaxIV : Util.Rand.Next(pk.MaxIV + 1); + finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32); } pk.IVs = finalIVs; } diff --git a/PKHeX.Core/PKM/PK1.cs b/PKHeX.Core/PKM/PK1.cs index d45e2c2153d..894ae939477 100644 --- a/PKHeX.Core/PKM/PK1.cs +++ b/PKHeX.Core/PKM/PK1.cs @@ -167,8 +167,11 @@ public PK7 ConvertToPK7() // IVs var new_ivs = new int[6]; int flawless = Species == (int)Core.Species.Mew ? 5 : 3; - for (var i = 0; i < new_ivs.Length; i++) new_ivs[i] = Util.Rand.Next(pk7.MaxIV + 1); - for (var i = 0; i < flawless; i++) new_ivs[i] = 31; + var rnd = Util.Rand; + for (var i = 0; i < new_ivs.Length; i++) + new_ivs[i] = rnd.Next(pk7.MaxIV + 1); + for (var i = 0; i < flawless; i++) + new_ivs[i] = 31; Util.Shuffle(new_ivs); pk7.IVs = new_ivs; diff --git a/PKHeX.Core/PKM/PK2.cs b/PKHeX.Core/PKM/PK2.cs index 8635f9e59d7..a25dcc0fd96 100644 --- a/PKHeX.Core/PKM/PK2.cs +++ b/PKHeX.Core/PKM/PK2.cs @@ -167,8 +167,11 @@ public PK7 ConvertToPK7() var special = Species == 151 || Species == 251; var new_ivs = new int[6]; int flawless = special ? 5 : 3; - for (var i = 0; i < new_ivs.Length; i++) new_ivs[i] = Util.Rand.Next(pk7.MaxIV + 1); - for (var i = 0; i < flawless; i++) new_ivs[i] = 31; + var rnd = Util.Rand; + for (var i = 0; i < new_ivs.Length; i++) + new_ivs[i] = rnd.Next(pk7.MaxIV + 1); + for (var i = 0; i < flawless; i++) + new_ivs[i] = 31; Util.Shuffle(new_ivs); pk7.IVs = new_ivs; diff --git a/PKHeX.Core/PKM/PKM.cs b/PKHeX.Core/PKM/PKM.cs index ade6274f2a7..4e7417b7242 100644 --- a/PKHeX.Core/PKM/PKM.cs +++ b/PKHeX.Core/PKM/PKM.cs @@ -937,7 +937,8 @@ private int GetBasePP(int move) /// public virtual void SetShiny() { - do { PID = PKX.GetRandomPID(Species, Gender, Version, Nature, AltForm, PID); } + var rnd = Util.Rand; + do { PID = PKX.GetRandomPID(rnd, Species, Gender, Version, Nature, AltForm, PID); } while (!IsShiny); if (Format >= 6 && (Gen3 || Gen4 || Gen5)) EncryptionConstant = PID; @@ -948,7 +949,8 @@ public virtual void SetShiny() /// public void SetShinySID() { - if (IsShiny) return; + if (IsShiny) + return; var xor = TID ^ (PID >> 16) ^ (PID & 0xFFFF); SID = (int)(xor & 0xFFF8) | Util.Rand.Next(8); } @@ -962,7 +964,9 @@ public void SetShinySID() /// public void SetPIDGender(int gender) { - do PID = PKX.GetRandomPID(Species, gender, Version, Nature, AltForm, PID); while (IsShiny); + var rnd = Util.Rand; + do PID = PKX.GetRandomPID(rnd, Species, gender, Version, Nature, AltForm, PID); + while (IsShiny); if (Format >= 6 && (Gen3 || Gen4 || Gen5)) EncryptionConstant = PID; } @@ -976,7 +980,9 @@ public void SetPIDGender(int gender) /// public void SetPIDNature(int nature) { - do PID = PKX.GetRandomPID(Species, Gender, Version, nature, AltForm, PID); while (IsShiny); + var rnd = Util.Rand; + do PID = PKX.GetRandomPID(rnd, Species, Gender, Version, nature, AltForm, PID); + while (IsShiny); if (Format >= 6 && (Gen3 || Gen4 || Gen5)) EncryptionConstant = PID; } @@ -1007,8 +1013,9 @@ public int[] SetRandomIVs(int? flawless = null) return SetRandomIVsGO(); int[] ivs = new int[6]; + var rnd = Util.Rand; for (int i = 0; i < 6; i++) - ivs[i] = Util.Rand.Next(MaxIV + 1); + ivs[i] = rnd.Next(MaxIV + 1); int count = flawless ?? GetFlawlessIVCount(); if (count != 0) @@ -1023,10 +1030,11 @@ public int[] SetRandomIVs(int? flawless = null) private int[] SetRandomIVsGO() { int[] ivs = new int[6]; - ivs[0] = (Util.Rand.Next(16) << 1) | 1; // hp - ivs[1] = ivs[4] = (Util.Rand.Next(16) << 1) | 1; // attack - ivs[2] = ivs[5] = (Util.Rand.Next(16) << 1) | 1; // defense - ivs[3] = Util.Rand.Next(MaxIV + 1); // speed + var rnd = Util.Rand; + ivs[0] = (rnd.Next(16) << 1) | 1; // hp + ivs[1] = ivs[4] = (rnd.Next(16) << 1) | 1; // attack + ivs[2] = ivs[5] = (rnd.Next(16) << 1) | 1; // defense + ivs[3] = rnd.Next(MaxIV + 1); // speed return IVs = ivs; } @@ -1040,10 +1048,11 @@ public int[] SetRandomIVs(IReadOnlyList template, int? flawless = null) { int count = flawless ?? GetFlawlessIVCount(); int[] ivs = new int[6]; + var rnd = Util.Rand; do { for (int i = 0; i < 6; i++) - ivs[i] = template[i] < 0 ? Util.Rand.Next(MaxIV + 1) : template[i]; + ivs[i] = template[i] < 0 ? rnd.Next(MaxIV + 1) : template[i]; } while (ivs.Count(z => z == MaxIV) < count); IVs = ivs; diff --git a/PKHeX.Core/PKM/Shared/PokeSize.cs b/PKHeX.Core/PKM/Shared/PokeSize.cs index 06d41e5d1cf..323aadb8246 100644 --- a/PKHeX.Core/PKM/Shared/PokeSize.cs +++ b/PKHeX.Core/PKM/Shared/PokeSize.cs @@ -45,6 +45,10 @@ public static int GetRandomScalar(this PokeSize size) /// /// Gets a random size scalar with a triangular distribution (copying official implementation). /// - public static int GetRandomScalar() => Util.Rand.Next(0x81) + Util.Rand.Next(0x80); + public static int GetRandomScalar() + { + var rnd = Util.Rand; + return rnd.Next(0x81) + rnd.Next(0x80); + } } } \ No newline at end of file diff --git a/PKHeX.Core/PKM/Util/PKX.cs b/PKHeX.Core/PKM/Util/PKX.cs index 9059a870a1f..e4a80173491 100644 --- a/PKHeX.Core/PKM/Util/PKX.cs +++ b/PKHeX.Core/PKM/Util/PKX.cs @@ -37,15 +37,15 @@ public static class PKX /// Array containing randomized EVs (H/A/B/S/C/D) public static int[] GetRandomEVs(int generation = Generation) { + var rnd = Util.Rand; if (generation > 2) { var evs = new int[6]; do { int max = 510; - int randomEV() => (byte)Math.Min(Util.Rand.Next(Math.Min(300, max)), 252); for (int i = 0; i < evs.Length - 1; i++) - max -= evs[i] = randomEV(); + max -= evs[i] = (byte)Math.Min(rnd.Next(Math.Min(300, max)), 252); evs[5] = max; } while (evs[5] > 252); Util.Shuffle(evs); @@ -55,7 +55,7 @@ public static int[] GetRandomEVs(int generation = Generation) { var evs = new int[6]; for (int i = 0; i < evs.Length; i++) - evs[i] = Util.Rand.Next(ushort.MaxValue + 1); + evs[i] = rnd.Next(ushort.MaxValue + 1); return evs; } } @@ -107,6 +107,7 @@ public static void ModifyStatsForNature(ushort[] stats, int nature) /// /// Gets a random PID according to specifications. /// + /// RNG to use /// National Dex ID /// Current Gender /// Origin Generation @@ -115,17 +116,17 @@ public static void ModifyStatsForNature(ushort[] stats, int nature) /// Current PID /// Used to retain ability bits. /// Rerolled PID. - public static uint GetRandomPID(int species, int cg, int origin, int nature, int form, uint OLDPID) + public static uint GetRandomPID(Random rnd, int species, int cg, int origin, int nature, int form, uint OLDPID) { uint bits = OLDPID & 0x00010001; int gt = Personal[species].Gender; if (origin >= 24) - return Util.Rand32(); + return Util.Rand32(rnd); bool g3unown = origin <= 5 && species == (int)Species.Unown; while (true) // Loop until we find a suitable PID { - uint pid = Util.Rand32(); + uint pid = Util.Rand32(rnd); // Gen 3/4: Nature derived from PID if (origin <= 15 && pid%25 != nature) diff --git a/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs b/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs index 7c09152f753..a9a6ac50a36 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/Puff6.cs @@ -35,16 +35,17 @@ public void Reset() public void MaxCheat(bool special = false) { + var rnd = Util.Rand; if (special) { for (int i = 0; i < PuffSlots; i++) - Data[Offset + i] = (byte)(21 + Util.Rand.Next(2)); // Supreme Wish or Honor + Data[Offset + i] = (byte)(21 + rnd.Next(2)); // Supreme Wish or Honor } else { for (int i = 0; i < PuffSlots; i++) Data[Offset + i] = (byte)((i % MaxPuffID) + 1); - Util.Shuffle(Data, Offset, Offset + PuffSlots); + Util.Shuffle(Data, Offset, Offset + PuffSlots, rnd); } PuffCount = PuffSlots; } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/RaidSpawnList8.cs b/PKHeX.Core/Saves/Substructures/Gen8/RaidSpawnList8.cs index edd89d199d5..becee61e6b8 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/RaidSpawnList8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/RaidSpawnList8.cs @@ -21,10 +21,11 @@ public RaidSpawnDetail[] GetAllRaids() public void ActivateAllRaids(bool rare, bool isEvent) { + var rnd = Util.Rand; for (int i = 0; i < RaidCount; i++) { - var star = (byte)Util.Rand.Next(0, 5); - var rand = (byte)Util.Rand.Next(0, 100); + var star = (byte)rnd.Next(0, 5); + var rand = (byte)rnd.Next(0, 100); GetRaid(i).Activate(star, rand, rare, isEvent); } } diff --git a/PKHeX.Core/Util/RandUtil.cs b/PKHeX.Core/Util/RandUtil.cs index 8b1f629a8be..9f40b1df95e 100644 --- a/PKHeX.Core/Util/RandUtil.cs +++ b/PKHeX.Core/Util/RandUtil.cs @@ -1,23 +1,25 @@ using System; using System.Collections.Generic; +using System.Threading; namespace PKHeX.Core { public static partial class Util { - public static readonly Random Rand = new Random(); + // Multithread safe rand, ha + public static Random Rand => _local.Value; - public static uint Rand32() - { - return (uint)Rand.Next(1 << 30) << 2 | (uint)Rand.Next(1 << 2); - } + private static readonly ThreadLocal _local = new ThreadLocal(() => new Random()); + + public static uint Rand32() => Rand32(Rand); + public static uint Rand32(Random rnd) => (uint)rnd.Next(1 << 30) << 2 | (uint)rnd.Next(1 << 2); /// /// Shuffles the order of items within a collection of items. /// /// Item type /// Item collection - public static void Shuffle(IList items) => Shuffle(items, 0, items.Count); + public static void Shuffle(IList items) => Shuffle(items, 0, items.Count, Rand); /// /// Shuffles the order of items within a collection of items. @@ -26,11 +28,12 @@ public static uint Rand32() /// Item collection /// Starting position /// Ending position - public static void Shuffle(IList items, int start, int end) + /// RNG object to use + public static void Shuffle(IList items, int start, int end, Random rnd) { for (int i = start; i < end; i++) { - int index = i + Rand.Next(end - i); + int index = i + rnd.Next(end - i); T t = items[index]; items[index] = items[i]; items[i] = t; diff --git a/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs index bd1d4f8bc10..f2046439f74 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs @@ -364,8 +364,9 @@ private void UpdateRandomAVs(object sender, EventArgs e) a.AwakeningSetAllTo(0); break; default: + var rnd = Util.Rand; foreach (var index in Enumerable.Range(0, 6)) - a.SetAV(index, Util.Rand.Next(Legal.AwakeningMax + 1)); + a.SetAV(index, rnd.Next(Legal.AwakeningMax + 1)); break; } LoadAVs(a); diff --git a/PKHeX.WinForms/MainWindow/Main.cs b/PKHeX.WinForms/MainWindow/Main.cs index 83a77293209..c87c2bd09e5 100644 --- a/PKHeX.WinForms/MainWindow/Main.cs +++ b/PKHeX.WinForms/MainWindow/Main.cs @@ -23,7 +23,10 @@ namespace PKHeX.WinForms public partial class Main : Form { private static readonly Version CurrentProgramVersion = Assembly.GetExecutingAssembly().GetName().Version; - + static async Task GetLegal(PKM pkm) + { + return await Task.Run(() => new LegalityAnalysis(pkm)); + } public Main() { Form splash = null; // popup a splash screen in another thread diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs index 814fb55ed20..5034bc7a39b 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs @@ -672,14 +672,15 @@ private void SetGenders(EntreeSlot slot) private void B_RandForest_Click(object sender, EventArgs e) { var source = (SAV is SAV5B2W2 ? Encounters5.B2W2_DreamWorld : Encounters5.BW_DreamWorld).ToList(); + var rnd = Util.Rand; foreach (var s in AllSlots) { - int index = Util.Rand.Next(source.Count); + int index = rnd.Next(source.Count); var slot = source[index]; source.Remove(slot); s.Species = slot.Species; s.Form = slot.Form; - s.Move = slot.Moves[Util.Rand.Next(slot.Moves.Count)]; + s.Move = slot.Moves[rnd.Next(slot.Moves.Count)]; s.Gender = slot.Gender == -1 ? PersonalTable.B2W2[slot.Species].RandomGender() : slot.Gender; } ChangeArea(null, EventArgs.Empty); // refresh diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs index 9c6ccb546b6..5ced8b245b0 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen6/SAV_PokeBlockORAS.cs @@ -47,9 +47,10 @@ private void B_RandomizeBerries_Click(object sender, EventArgs e) // Randomize the trees. byte[] tree = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x40, 0x01, 0x00, 0x00, 0x00, }; var plantable = Legal.Pouch_Berry_XY; // 0 index is None, skip with rand + var rnd = Util.Rand; for (int i = 0; i < 90; i++) // amount of plots in the game { - ushort berry = plantable[Util.Rand.Next(1, plantable.Length)]; // get random berry item ID from list + ushort berry = plantable[rnd.Next(1, plantable.Length)]; // get random berry item ID from list BitConverter.GetBytes(berry).CopyTo(tree, 6); // put berry into tree. tree.CopyTo(SAV.Data, SAV.BerryField + (0x10 * i)); // put tree into plot }