public static void UpdateReachableAsync(int sleepTime = 30)
        {
            try {
                Thread.Sleep(sleepTime); // wait to let values update
                if (Updating)
                {
                    return;
                }
                Updating = true;
                var argsList = RustLogic ? new List <string> {
                    "reach-check",
                    // TODO maybe we won't pass these explicitly? since it's samefolder shit
                    "--areas",
                    $"\"{Randomizer.BasePath}areas.wotw\"",
                    "--locations",
                    $"\"{Randomizer.BasePath}loc_data.csv\"",
                    "--uber-states",
                    $"\"{Randomizer.BasePath}state_data.csv\"",
                } : new List <string> {
                    "-jar",
                    $"\"{Randomizer.BasePath}SeedGen.jar\" ",
                    "ReachCheck"
                };
                argsList.AddRange(new List <string> {
                    $"\"{SeedController.SeedFile}\"",
                    $"{InterOp.get_max_health()}",
                    $"{Convert.ToInt32(10*InterOp.get_max_energy())}",
                    $"{UberGet.value(6, 0).Int}",
                    $"{InterOp.get_ore()}",
                    $"{InterOp.get_experience()}",
                });
                if (RustLogic)
                {
                    argsList.AddRange(TrackedConds.Where(c => c.Met()).Select(t => $"u:{t.Id.GroupID},{t.Id.ID}"));
                }
                argsList.AddRange(SaveController.SkillsFound.Select((AbilityType at) => $"s:{(int)at}"));
                argsList.AddRange(Teleporter.TeleporterStates.Keys.Where(t => new Teleporter(t).Has()).Select(t => $"t:{(int)t}"));
                if (new QuestEvent(QuestEventType.Water).Has())
                {
                    argsList.Add("w:0");
                }
                argsList.AddRange(TrackedShards.Where(sh => new Shard(sh).Has()).Select(t => $"sh:{(int)t}"));
                var proc = new System.Diagnostics.Process();
                proc.StartInfo.FileName               = RustLogic ? @"seedgen.exe" : @"java.exe";
                proc.StartInfo.Arguments              = String.Join(" ", argsList);
                proc.StartInfo.CreateNoWindow         = true;
                proc.StartInfo.UseShellExecute        = false;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.RedirectStandardError  = true;
                proc.StartInfo.WorkingDirectory       = Randomizer.BasePath;
                proc.Start();
                if (!proc.WaitForExit(10000))
                {
                    Randomizer.Warn("MapController.waitForProc", "timed out waiting for reach check", false);
                }
                Reachable.Clear();
                var rawOutput = proc.StandardOutput.ReadToEnd();
                if (rawOutput.Trim() != "")
                {
                    foreach (var rawCond in rawOutput.Split(','))
                    {
                        try {
                            var frags = rawCond.Split('|');
                            var cond  = new UberStateCondition(int.Parse(frags[0]), frags[1]);
                            if (cond.Loc().Type == LocType.Shop)
                            {
                                if (cond.Met() || hintTypes.Contains(cond.Pickup().Type))
                                {
                                    continue; // bought it or it's a hint. Either way it's known to be non progression, so it does not show on the map
                                }
                                if (ShopSlot.Twillen.Any(e => e.State.Equals(cond.Id)))
                                {
                                    Reachable.Add(new UberStateCondition(2, "20000"));
                                }
                                else if (ShopSlot.Opher.Any(e => e.State.Equals(cond.Id)))
                                {
                                    Reachable.Add(new UberStateCondition(1, "20000"));
                                }
                                else if (ShopSlot.LupoStore.Any(e => e.State.Equals(cond.Id)))
                                {
                                    Reachable.Add(new UberStateCondition(48248, "20000"));
                                }
                            }
                            Reachable.Add(cond);
                        }
                        catch (Exception e) { Randomizer.Error($"GetReachableAsync (post-return) while parsing |{rawCond}|", e); }
                    }
                }

                /*
                 * if(Randomizer.Dev)
                 * Randomizer.Log($"Reach check:\nseed_gen_cli.exe {String.Join(" ", argsList)}\n gave output: \n{rawOutput}\n stderr was {proc.StandardError.ReadToEnd()}\nReachable after: {String.Join(" ", Reachable.Select(r => r.ToString()))}", false);
                 */
                InterOp.refresh_inlogic_filter();
            }
            catch (Exception e) { Randomizer.Error("GetReachableAsync", e); }
            Updating = false;
        }
        public static void UpdateReachableAsync(int sleepTime = 30)
        {
            try {
                Thread.Sleep(sleepTime); // wait to let values update
                if (Updating)
                {
                    return;
                }
                Updating = true;
                var argsList = new List <string> {
                    "-jar",
                    $"{Randomizer.BasePath}SeedGen.jar ",
                    "ReachCheck",
                    $"\"{SeedController.SeedFile}\"",
                    $"{InterOp.get_max_health()}",
                    $"{Convert.ToInt32(10*InterOp.get_max_energy())}",
                    $"{UberGet.value(6, 0).Int}",
                    $"{InterOp.get_ore()}",
                    $"{InterOp.get_experience()}",
                };
                // ^ this should probably be an array at this point...
                // TODO: send which key doors are already open
                argsList.AddRange(SaveController.SkillsFound.Select((AbilityType at) => $"s:{(int)at}"));
                argsList.AddRange(Teleporter.TeleporterStates.Keys.Where(t => new Teleporter(t).Has()).Select(t => $"t:{(int)t}"));
                if (new QuestEvent(QuestEventType.Water).Has())
                {
                    argsList.Add("w:0");
                }
                var proc = new System.Diagnostics.Process();
                proc.StartInfo.FileName               = @"java.exe";
                proc.StartInfo.Arguments              = String.Join(" ", argsList);
                proc.StartInfo.CreateNoWindow         = true;
                proc.StartInfo.UseShellExecute        = false;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.WorkingDirectory       = Randomizer.BasePath;
                proc.Start();
                if (!proc.WaitForExit(7500))
                {
                    Randomizer.Warn("MapController.waitForProc", "timed out waiting for reach check", false);
                }
                Reachable.Clear();
                var rawOutput = proc.StandardOutput.ReadToEnd();
                if (rawOutput.Trim() != "")
                {
                    foreach (var rawCond in rawOutput.Split(','))
                    {
                        try {
                            var frags = rawCond.Split('|');
                            var cond  = new UberStateCondition(int.Parse(frags[0]), frags[1]);
                            Reachable.Add(cond);
                        }
                        catch (Exception e) { Randomizer.Error($"GetReachableAsync (post-return) while parsing |{rawCond}|", e); }
                    }
                }
//        else
//          Randomizer.Log($"got output |{rawOutput}| from cmd. args: \"{String.Join(" ", argsList)}\" stderr was {proc.StandardError.ReadToEnd()}", false);
                InterOp.refresh_inlogic_filter();
            }
            catch (Exception e) { Randomizer.Error("GetReachableAsync", e); }
            Updating = false;
        }