Example #1
0
 public TrackData()
 {
     if (TrackFileController.DontTrackYet)
     {
         keystones   = 0;
         spiritLight = 0;
         ore         = 0;
         skills      = new HashSet <String>();
         upgraded    = new HashSet <String>();
         events      = new HashSet <String>();
         teleporters = new HashSet <String>();
     }
     try {
         keystones   = InterOp.get_keystones();
         spiritLight = InterOp.get_experience();
         ore         = InterOp.get_ore();
         skills      = SaveController.SkillsFound.Select((AbilityType type) => trackName(type)).ToHashSet();
         upgraded    = ShopController.UpgradedWeapons.Select((AbilityType type) => $"{type.GetDescription().Replace(" ", "")}").ToHashSet();
         if (SaveController.HasAbility(AbilityType.DamageUpgrade1) && SaveController.HasAbility(AbilityType.DamageUpgrade2))
         {
             upgraded.Add(trackName(AbilityType.DamageUpgrade1));
         }
         events      = SaveController.WorldEvents.Select((QuestEventType type) => type.GetDescription()).ToHashSet();
         quests      = UberStateController.Quests.Where((UberState s) => s.ValueOr(new UberValue(0)).Int == s.Value.Int).Select(s => s.Name).ToHashSet();
         teleporters = Teleporter.TeleporterStates.Keys.Where((TeleporterType t) => (new Teleporter(t)).Has()).Select((TeleporterType t) => t.GetDescription()).ToHashSet();
         flags       = SeedController.Flags.Select(s => s.GetDescription()).ToHashSet();
     }
     catch (Exception e) {
         Randomizer.Error("TrackData()", e);
     }
 }
        private static UberState createUberStateEntry(UberId id)
        {
            if (!InterOp.get_uber_state_exists(id.GroupID, id.ID))
            {
                Randomizer.Error("cuse", $"Failed to find {id} in uber state system.", false);
                return(null);
            }

            byte[] buffer = new byte[256];
            int    len    = InterOp.get_uber_state_name(id.GroupID, id.ID, buffer, buffer.Length);
            string name   = System.Text.Encoding.ASCII.GetString(buffer, 0, len);

            len = InterOp.get_uber_state_group_name(id.GroupID, id.ID, buffer, buffer.Length);
            string groupName = System.Text.Encoding.ASCII.GetString(buffer, 0, len);

            var s = new UberState()
            {
                ID        = id.ID,
                GroupID   = id.GroupID,
                Name      = name,
                GroupName = groupName,
                Type      = InterOp.get_uber_state_type(id.GroupID, id.ID),
            };

            s.Value = CreateValue(s.Type, InterOp.get_uber_state_value(id.GroupID, id.ID));
            return(s);
        }
Example #3
0
        public static void Update()
        {
            if (NeedsNewGameInit)
            {
                NewGameInit();
            }
            bool SkipListners = SkipListenersNextUpdate;

            SkipListenersNextUpdate = false;

            var memory = Randomizer.Memory;
            Dictionary <long, UberState> uberStates = memory.GetUberStates();

            foreach (KeyValuePair <long, UberState> pair in uberStates)
            {
                try {
                    UberState state = pair.Value;
                    UberId    key   = state.GetUberId();

                    if (UberStates.TryGetValue(key, out UberState oldState))
                    {
                        UberValue value    = state.Value;
                        UberValue oldValue = oldState.Value;
                        if (value.Int != oldValue.Int)
                        {
                            var oldValFmt = oldState.FmtVal(); // get this now because we overwrite the value by reference
                            if (ShouldRevert(state))
                            {
                                Randomizer.Log($"Reverting state change of {state.Name} from {oldValFmt} to {state.FmtVal()}", false);
                                memory.WriteUberState(oldState);
                                continue;
                            }
                            HandleSpecial(state);
                            UberStates[key].Value = state.Value;
                            if (!SkipListners)
                            {
                                var  pos   = Randomizer.Memory.Position();
                                bool found = false;
                                if (value.Int > 0)
                                {
                                    found = SeedController.OnUberState(state);
                                }
                                if ((value.Int == 0 || !found) && !(state.GroupName == "statsUberStateGroup" || state.GroupName == "achievementsGroup"))
                                {
                                    Randomizer.Log($"State change: {state.Name} {state.ID} {state.GroupName} {state.GroupID} {state.Type} {state.FmtVal()} (was {oldValFmt}, pos ({Math.Round(pos.X)},{Math.Round(pos.Y)}) )", false);
                                }
                            }
                        }
                    }
                    else
                    {
                        UberStates[key] = state.Clone();
                    }
                } catch (Exception e) {
                    Randomizer.Error($"USC.Update {pair}", e);
                }
            }
        }
 public static void RegisterCheckable(CheckableHint ch)
 {
     if (nextCheckable == 20)
     {
         Randomizer.Error("HC.RegisterCheckable", "Max 10 checkable hints supported");
         return;
     }
     CheckableHints[ch] = new UberId(6, nextCheckable++);
 }
 public static void PopulateTrackedConds()
 {
     try {
         foreach (var line in File.ReadAllLines(Randomizer.BasePath + "state_data.csv"))
         {
             var data = line.Split(',');
             _trackedConds.Add(new UberStateCondition(data[1].ParseToInt("PopulateTrackedConds.GID"), data[2]));
         }
     } catch (Exception e) { Randomizer.Error("PopulateTrackedConds", e); }
 }
Example #6
0
 public static void OnLoad(int slot)
 {
     try {
         if (slot != CurrentSlot)
         {
             // slot swap
             CurrentSlot = slot;
             Data        = new SaveData(slot);
         }
         Data.Load();
     } catch (Exception e) { Randomizer.Error("OnLoad", e); }
 }
 public static void Update()
 {
     try {
         discord?.RunCallbacks();
     } catch (Exception e) {
         if (e is NullReferenceException)
         {
             return;
         }
         Randomizer.Error("Discord callbacks", e, false);
     }
 }
Example #8
0
 public static void Init()
 {
     try {
         if (File.Exists(targetsFile))
         {
             using (var sr = new StreamReader(targetsFile))
                 using (JsonReader reader = new JsonTextReader(sr)) {
                     var serializer = new JsonSerializer();
                     relevantRVAs = serializer.Deserialize <Dictionary <String, UInt64> >(reader);
                 }
         }
     } catch (Exception e) { Randomizer.Error("RVAFinder.Init", e); }
 }
 public static void OnTeleporterActivated(String identifier)
 {
     try {
         if (identifier == "kwoloksCavernSaveRoomA")
         {
             (HintsController.CurrentZone == ZoneType.Glades ? TeleporterType.Glades : TeleporterType.Hollow).p().Grant(true);
         }
         else
         {
             TpsByID[identifier].p().Grant(true);
         }
     }
     catch (Exception e) { Randomizer.Error($"OTA, key of {identifier}", e); }
 }
 public static void Write()
 {
     try {
         Last = new TrackData();
         if (File.Exists(trackFilePath))
         {
             File.Delete(trackFilePath);
         }
         using (var sw = new StreamWriter(trackFilePath))
             sw.Write(JsonConvert.SerializeObject(Last));
         IgnoreUpdateFrames = 5;
     } catch (IOException) {
         // that's fine
     } catch (Exception e) {
         Randomizer.Error("TrackFile.Write:", e); // less fine!
     }
 }
 public static void OnNewGame(int slot)
 {
     try {
         // overwrite the message log TODO: save a backup maybe?
         File.WriteAllText(Randomizer.MessageLog, "");
         SeedController.ReadSeed();
         UberStateController.NeedsNewGameInit = true;
         UberStateController.UberStates.Clear();
         UberStateController.TickingUberStates.Clear();
         AHK.OnNewGame();
         SaveController.NewGame(slot);
         BonusItemController.Refresh();
         Client.Connect();
     }
     catch (Exception e) {
         Randomizer.Error("OnNewGame", e);
     }
 }
 public TrackData()
 {
     try {
         keystones   = Randomizer.Memory.Keystones;
         spiritLight = Randomizer.Memory.Experience;
         ore         = Randomizer.Memory.Ore;
         skills      = SaveController.Data.SkillsFound.Select((AbilityType type) => trackName(type)).ToHashSet();
         upgraded    = SaveController.Data.OpherUpgraded.Keys.Select((AbilityType type) => $"{type.GetDescription().Replace(" ", "")}").ToHashSet();
         if (SaveController.HasAbility(AbilityType.DamageUpgrade1) && SaveController.HasAbility(AbilityType.DamageUpgrade2))
         {
             upgraded.Add(trackName(AbilityType.DamageUpgrade1));
         }
         events      = SaveController.Data.WorldEvents.Select((QuestEventType type) => type.GetDescription()).ToHashSet();
         teleporters = Teleporter.TeleporterStates.Keys.Where((TeleporterType t) => (new Teleporter(t)).Has()).Select((TeleporterType t) => t.GetDescription()).ToHashSet();
     }
     catch (Exception e) {
         Randomizer.Error("TrackData()", e);
     }
 }
 public static void Initialize()
 {
     Disabled = AHK.IniFlag("DisableNetcode");
     if (Disabled)
     {
         Randomizer.Log("Netcode disabled, skipping discord init", false, "DEBUG");
         return;
     }
     if (InitRunning)
     {
         Randomizer.Log("Init running already, skipping", false, "DEBUG");
         return;
     }
     InitRunning = true;
     new Thread(() => {
         try {
             if (discord == null)
             {
                 discord = new Discord.Discord(CLIENT_ID, (UInt64)CreateFlags.Default);
             }
             if (UserManager == null)
             {
                 UserManager = discord.GetUserManager();
             }
             if (ApplicationManager == null)
             {
                 ApplicationManager = discord.GetApplicationManager();
             }
             Initialized = true;
             if (GetUser() != null)
             {
                 Randomizer.Log("User already known, skipping rest of discord init", false, "DEBUG");
                 Randomizer.Client.Connect();
                 return;
             }
             discord.SetLogHook(LogLevel.Debug, (level, message) => Randomizer.Log($"discord: {message}", level.CompareTo(LogLevel.Info) > 0, level.ToString()));
             UserManager.OnCurrentUserUpdate += DiscordInitComplete;
         } catch (Exception e) {
             Randomizer.Error("DiscInitThread", e);
         }
         InitRunning = false;
     }).Start();
 }
Example #14
0
 public static void Update()
 {
     try {
         if (needWrite)
         {
             if (File.Exists(targetsFile))
             {
                 File.Delete(targetsFile);
             }
             using (var sw = new StreamWriter(targetsFile))
                 using (JsonWriter writer = new JsonTextWriter(sw)) {
                     writer.Formatting = Formatting.Indented;
                     var serializer = new JsonSerializer();
                     serializer.Serialize(writer, relevantRVAs);
                 }
             needWrite = false;
         }
     }
     catch (Exception e) { Randomizer.Error("RVAFinder.Update", e); }
 }
 public static void Update()
 {
     if (InterOp.get_game_state() == GameState.Game)
     {
         DontTrackYet = false;
     }
     if (IgnoreUpdateFrames > 0)
     {
         IgnoreUpdateFrames--;
         return;
     }
     try {
         if (Last == null || Last.ore != InterOp.get_ore() || Last.spiritLight != InterOp.get_experience())
         {
             Write();
         }
     } catch (Exception e) {
         Randomizer.Error("Track.Update", e);
         Randomizer.Log($"Last: {Last}", false);
     }
 }
Example #16
0
 public static bool HaveHintForZone(ZoneType zone)
 {
     try {
         if (zone == ZoneType.Void)
         {
             return(false);
         }
         if (ZoneToState.TryGetValue(zone, out UberState state))
         {
             return(state.GetValue().Bool);
         }
         else
         {
             return(false);
         }
     }
     catch (Exception e) {
         Randomizer.Error("Hints.HaveHintForZone", e, false);
         return(false);
     }
 }
        public static void Update()
        {
            try {
                if (NeedsNewGameInit)
                {
                    NewGameInit();
                }

                if (!SkipListeners)
                {
                    // We do ToArray here so we can change the hashset while we are looping.
                    foreach (var state in TickingUberStates.ToArray())
                    {
                        // Maybe change this to use our own cache lookup?
                        var value = InterOp.get_uber_state_value(state.GroupID, state.ID);
                        InterOp.set_uber_state_value(state.GroupID, state.ID, value + 1);
                    }
                }

                if (FullSyncNextUpdate)
                {
                    FullSyncNextUpdate = false;
                    Randomizer.Client.SendBulk(SyncedUberStates.Where(uid => uid.State() != null).ToDictionary(uid => uid, (uid) => uid.State().ValueAsFloat()));
                    var bad = SyncedUberStates.Where(uid => uid.State() == null).ToList();
                    foreach (var baduid in bad)
                    {
                        SyncedUberStates.Remove(baduid);
                    }
                }
                while (Randomizer.Client.UberStateQueue.TryTake(out var stateUpdate))
                {
                    var(id, val) = stateUpdate.FromNet();
                    if (id.State().ValueAsFloat() != val)
                    {
                        InterOp.set_uber_state_value(id.GroupID, id.ID, val);
                    }
                }
            }
            catch (Exception e) { Randomizer.Error("USC.Update", e, false); }
        }
Example #18
0
        public static void Tick()
        {
            var signal = Engine.ExecFunction("Tick");

            if (signal != null && signal != "none")
            {
                Engine.SetVar("signal", "none");
                HandleSignal(signal);
            }
            FramesTillUnlockReload = Math.Max(0, FramesTillUnlockReload - 1);
            if (FramesTillNextSend > 0)
            {
                FramesTillNextSend--;
            }
            else
            {
                if (CanPrint)
                {
                    Current            = MessageQueue.Peek();
                    FramesTillNextSend = Current.Frames;
                    try {
                        if (Current.Clear)
                        {
                            InterOp.clear_visible_hints();
                        }
                        InterOp.display_hint(Current.Text, Current.Frames / 60f, Current.Pos, Current.Mute);
                        if (IniFlag("LogOnPrint"))
                        {
                            Randomizer.Log($"Sending {Current.Text} for {Current.Frames} ({MessageQueue.Count} remaining in queue)", false);
                        }
                        MessageQueue.Dequeue();
                    } catch (Exception e) { Randomizer.Error("AHK.sendMsg", e, false); }
                }
                else
                {
                    Current = null;
                }
            }
        }
Example #19
0
 public static ulong rvaLookup(string sig, bool fromDump = false)
 {
     if (fromDump)
     {
         if (dumpRVAs.Count == 0)
         {
             populateAllRVAs();
             relevantRVAs.Clear();
         }
         if (dumpRVAs.ContainsKey(sig))
         {
             relevantRVAs[sig] = dumpRVAs[sig];
             needWrite         = true;
         }
     }
     if (!relevantRVAs.ContainsKey(sig))
     {
         Randomizer.Error("rvaLookup", new Exception($"Address not found for {sig}"));
         return(0);
     }
     return(relevantRVAs[sig]);
 }
Example #20
0
 public static void OnLoad(int slot, int backupSlot = -1)
 {
     try {
         if (slot != CurrentSlot)
         {
             if (Randomizer.InputUnlockCallback != null)
             {
                 AHK.Print("Warning: Callback overwritten on slot change!", 240);
                 Randomizer.InputUnlockCallback = null;
             }
             // slot swap
             CurrentSlot = slot;
             Data        = new SaveData(slot);
         }
         UberStateController.SkipListenersNextUpdate = true;
         Data.Load(backupSlot);
         if (DidWeJustDie)
         {
             InterOp.magic_function();
         }
     }
     catch (Exception e) { Randomizer.Error("SaveCont.OnLoad", e); }
 }
Example #21
0
        public static void Tick()
        {
            var signal = Engine.ExecFunction("Tick");

            if (signal != null && signal != "none")
            {
                Engine.SetVar("signal", "none");
                switch (signal)
                {
                case "reload":
                    if (FramesTillUnlockReload == 0)
                    {
                        FramesTillNextSend = 0;
                        SeedController.ReadSeed();
                        Randomizer.Memory.OnInit();
                        if (Randomizer.Memory.GameState == Memory.GameState.Game)
                        {
                            PsuedoLocs.RELOAD_SEED.Pickup().Grant();
                        }
                        FramesTillUnlockReload = 60;
                    }
                    break;

                case "lastPickup":
                    FramesTillNextSend = 1; // the only reason this isn't = 0 is that spamming this could get really annoying
                    MessageQueue.Enqueue(Last);
                    break;

                case "hintMessage":
                    HintsController.ShowHintMessage();
                    break;

                case "dev":
                    Randomizer.Dev = !Randomizer.Dev;
                    Randomizer.Log($"Dev: {Randomizer.Dev}");
                    break;

                case "exitapp":
                    Environment.Exit(Environment.ExitCode);
                    break;

                case "toggleDebug":
                    Randomizer.Memory.Debug = !Randomizer.Memory.Debug;
                    Print($"Debug {(Randomizer.Memory.Debug ? "enabled" : "disabled")}", toMessageLog: false);
                    break;

                case "toggleCursorLock":
                    Print($"Cursor Lock {(InterOp.toggle_cursorlock() ? "enabled" : "disabled")}", toMessageLog: false);
                    break;

                case "test1":
                    PsuedoLocs.BINDING_ONE.Pickup().Grant();
                    break;

                case "test2":
                    PsuedoLocs.BINDING_TWO.Pickup().Grant();
                    break;

                case "test3":
                    PsuedoLocs.BINDING_THREE.Pickup().Grant();
                    break;

                case "test4":
                    Print("magic", 180, false);
                    InterOp.magic_function();
                    break;

                case "test5":
                    tpCheatToggle = !tpCheatToggle;
                    Print($"TPCheat {(tpCheatToggle ? "enabled" : "disabled")}");
                    break;

                default:
                    Randomizer.Log($"Recieved unknown signal {signal}");
                    break;
                }
            }
            FramesTillUnlockReload = Math.Max(0, FramesTillUnlockReload - 1);
            if (FramesTillNextSend > 0)
            {
                FramesTillNextSend--;
            }
            else
            {
                if (CanPrint)
                {
                    Current            = MessageQueue.Peek();
                    FramesTillNextSend = Current.Frames;
                    try {
                        InterOp.clear_visible_hints();
                        InterOp.display_hint(InterOp.Util.getIl2cppStringPointer(Current.Text), Current.Frames / 60f);
                        if (IniFlag("LogOnPrint"))
                        {
                            Randomizer.Log($"Sending {Current.Text} for {Current.Frames} ({MessageQueue.Count} remaining in queue)", false);
                        }
                        MessageQueue.Dequeue();
                    } catch (Exception e) { Randomizer.Error("AHK.sendMsg", e, false); }
                }
                else
                {
                    Current = null;
                }
            }
        }
        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;
        }
        public static void Tick()
        {
            var signal = Engine.ExecFunction("Tick");

            if (signal != null && signal != "none")
            {
                Engine.SetVar("signal", "none");
                switch (signal)
                {
                case "reload":
                    if (FramesTillUnlockReload == 0)
                    {
                        iniFlagCache.Clear();
                        FramesTillNextSend = 0;
                        Randomizer.Client.Connect();
                        SeedController.ReadSeed();
                        if (InterOp.get_game_state() == GameState.Game)
                        {
                            PsuedoLocs.RELOAD_SEED.OnCollect();
                        }
                        FramesTillUnlockReload = 120;
                    }
                    break;

                case "lastPickup":
                    FramesTillNextSend = 1; // the only reason this isn't = 0 is that spamming this could get really annoying
                    MessageQueue.Enqueue(Last);
                    break;

                case "hintMessage":
                    HintsController.ProgressWithHints();
                    break;

                case "dev":
                    Randomizer.Dev = !Randomizer.Dev;
                    Randomizer.Log($"Dev: {Randomizer.Dev}");
                    break;

                case "exitapp":
                    Environment.Exit(Environment.ExitCode);
                    break;

                case "toggleDebug":
                    InterOp.set_debug_controls(!InterOp.get_debug_controls());
                    Print($"Debug {(InterOp.get_debug_controls() ? "enabled" : "disabled")}", toMessageLog: false);
                    break;

                case "toggleCursorLock":
                    Print($"Cursor Lock {(InterOp.toggle_cursorlock() ? "enabled" : "disabled")}", toMessageLog: false);
                    break;

                case "test1":
                    PsuedoLocs.BINDING_ONE.OnCollect();
                    break;

                case "test2":
                    PsuedoLocs.BINDING_TWO.OnCollect();
                    break;

                case "test3":
                    PsuedoLocs.BINDING_THREE.OnCollect();
                    break;

                case "test4":
                    if (SeedController.HasInternalSpoilers)
                    {
                        UberSet.Bool(34543, 11226, true);
                        Print("spoiler unlocked", toMessageLog: false);
                    }
                    break;

                case "test5":
                    tpCheatToggle = !tpCheatToggle;
                    Print($"TPCheat {(tpCheatToggle ? "enabled" : "disabled")}", toMessageLog: false);
                    break;

                case "printcoords":
                    var pos = InterOp.get_position();
                    Print($"{pos.X}, {pos.Y}", toMessageLog: false);
                    break;

                case "namespoilertoggle":
                    MapController.NameLabels = !MapController.NameLabels;
                    Print($"Loc name labels {(MapController.NameLabels ? "enabled" : "disabled")}", toMessageLog: false);
                    break;

                default:
                    Randomizer.Log($"Recieved unknown signal {signal}");
                    break;
                }
            }
            FramesTillUnlockReload = Math.Max(0, FramesTillUnlockReload - 1);
            if (FramesTillNextSend > 0)
            {
                FramesTillNextSend--;
            }
            else
            {
                if (CanPrint)
                {
                    Current            = MessageQueue.Peek();
                    FramesTillNextSend = Current.Frames;
                    try {
                        InterOp.clear_visible_hints();
                        InterOp.display_hint(Current.Text, Current.Frames / 60f, Current.Pos);
                        if (IniFlag("LogOnPrint"))
                        {
                            Randomizer.Log($"Sending {Current.Text} for {Current.Frames} ({MessageQueue.Count} remaining in queue)", false);
                        }
                        MessageQueue.Dequeue();
                    } catch (Exception e) { Randomizer.Error("AHK.sendMsg", e, false); }
                }
                else
                {
                    Current = null;
                }
            }
        }
        public static void ResolveUberStateChange(UberState state, UberValue old)
        {
            try {
                UberId key = state.GetUberId();
                if (!UberStates.TryGetValue(key, out UberState oldState))
                {
                    oldState       = state.Clone();
                    oldState.Value = old;
                    UberStates.Add(key, oldState);
                }

                UberValue value = state.Value;
                if (value.Int == old.Int)
                {
                    return;
                }

                var oldValFmt = old.FmtVal(state.Type); // get this now because we overwrite the value by reference
                if (ShouldRevert(state))
                {
                    Randomizer.Log($"Reverting state change of {state.Name} from {oldValFmt} to {state.FmtVal()}", false);
                    oldState.Write();
                    return;
                }

                HandleSpecial(state);
                UberStates[key].Value = state.Value;
                var  pos   = InterOp.get_position();
                bool found = false;
                if (value.Int > 0)
                {
                    var id = state.GetUberId();
                    if (SkipUberStateMapCount.GetOrElse(key, 0) > 0)
                    {
                        var p = id.toCond().Pickup().Concat(id.toCond(state.ValueAsInt()).Pickup());
                        if (p.NonEmpty)
                        {
                            SkipUberStateMapCount[key] -= 1;
                            Randomizer.Log($"Suppressed granting {p} from {id}={state.ValueAsInt()}. Will suppress {SkipUberStateMapCount[key]} more times", false, "DEBUG");
                            return;
                        }
                    }
                    found = SeedController.OnUberState(state);
                }

                if (SyncedUberStates.Contains(key))
                {
                    Randomizer.Client.SendUpdate(key, state.ValueAsFloat());
                }

                BonusItemController.OnUberState(state);
                var zone = ZoneType.Void;
                if (InterOp.get_game_state() == GameState.Game)
                {
                    zone = InterOp.get_player_area().toZone();
                }
                if (!NeedsNewGameInit && (value.Int == 0 || !found) && !(state.GroupName == "statsUberStateGroup" || state.GroupName == "achievementsGroup" || state.GroupID == 8 || state.GroupID == 10))
                {
                    Randomizer.Debug($"State change: {state.GroupName}.{state.Name} ({state.GroupID}|{state.ID}) {state.Type} {oldValFmt}->{state.FmtVal()} at ({Math.Round(pos.X)}, {Math.Round(pos.Y)}) in {zone}");
                }
                //Randomizer.Debug($"{state.GroupName}.{state.Name}, {state.GroupID}, {state.ID}, {state.Type}, {oldValFmt}, {state.FmtVal()}, {zone}, {Math.Round(pos.X)},{Math.Round(pos.Y)}");
            }
            catch (Exception e) {
                Randomizer.Error($"USC.Update {state}", e);
            }
        }