Debugging with Visual Studio for in game commands

Hello,

First - thanks to you for such an interesting tool. I’m trying to automate some of routines, although I’m familiar with coding I have absolutely no experiecnce with C# and Visual Studio (VS). I was looking at Sanderling and A-Bot source code and got 2 questions:

  1. I’ve managed to build and run Sanderling in debug mode in VS, but unfortunately, actual automation scripts are sort of imported during runtime there, and I had no luck in debugging those from VS. Can you guide me on how can I make automation script integrated into Sanderling so I can debug it from VS?
  2. A-Bot allowed me to place breakpoints in the middle of automation code and I was able to see evaluated measurments inside VS on the fly (this is just what I’m looking for). But it seems underlying Sanderling API is different than the latest release version. Is that so?

Thanks in advance.

Welcome @NastyGeek!

No problem, to debug from VS, expand the sample project from the Sanderling repository at Sanderling/src/Sanderling/Sanderling.Sample.Read at 70ce068d1672896a3a9b6aa4f0bc3f1a2d05f0bb · Arcitectus/Sanderling · GitHub

Just add a call to your code in there.

Why different? What difference are you seeing?

Thanks for the quick reply.

Indeed after the second look seems like underlying measurments are the same.
I’ve tried Sanderling.Sample.Read, as far as I understand this is a minimalistic program that boots Sanderling and checks for measurememnts, didn’t figure out what exactly getline.cs is for, seems it’s some sort of console input processing…

Anyway, with ABot pointing to latest Sanderling, I’m going to try work from there, cause as mentioned - the main thing I’m looking for is being able to debug right inside the automation script.

Hi @Viir I’m trying to adopt A-Bot, and have a problem toggling keyboard short-keys, I’m trying to stop the ship using this code but nothing happens:

 MotionParamExtension.KeyboardPressCombined(new[] {
	WindowsInput.Native.VirtualKeyCode.LCONTROL,
	WindowsInput.Native.VirtualKeyCode.SPACE
});

Could you guide me on that?

After half a day of digging I came to conclusion the task loop you’ve coded is beyond me. As for question above - I noticed you have 2 types of “tasks” one are <IBotTask> Component oriented others are <MotionParam> Effects and the letter are those that can help with UI interaction. But the logic of execution flow of the public BotStepResult Step(BotStepInput input) is blowing my mind, right now I’m trying to improve Retreat logic to make it repeat the way I do it in-game (click Citadel, Align, wait for drones, warp out) that’s what I come up so far, but thing is - it keeps clicking on citadel overview item over and over again… My changes to Retreat.cs main method:

		public IEnumerable<IBotTask> Component
		{
			get
			{
				var memoryMeasurement = Bot?.MemoryMeasurementAtTime?.Value;

				if (!memoryMeasurement.ManeuverStartPossible())
					yield break;

				// Select citadel in overview
				var citadelName = Bot?.ConfigSerialAndStruct.Value?.RetreatCitadel ?? "TEST-NAME";
				var CitadelEntry = memoryMeasurement?.WindowOverview?.FirstOrDefault()?.ListView?.Entry?.Where(entry => entry?.Name == citadelName)?.First();
				if (CitadelEntry != null)
				{
					yield return new ElementClickTask { bot = Bot, element = CitadelEntry };
				}
				else
				{
					yield break;
				}
				

				// Click Align at item UI
				var alignIcon = memoryMeasurement?.WindowSelectedItemView?.FirstOrDefault()?.Sprite?.First();
				if (alignIcon != null)
				{
					yield return new ElementClickTask { bot = Bot, element = alignIcon };

					// TODO turn off AB

					// wait for drones, or do urgent warp out
					if (enemyOnGrid(memoryMeasurement) || dronesInSpace(memoryMeasurement) < 1)
					{
						// Click Dock at item UI
						var dockIcon = memoryMeasurement?.WindowSelectedItemView?.FirstOrDefault()?.Sprite?.ElementAt(2);
						if (dockIcon != null)
						{
							yield return new ElementClickTask { bot = Bot, element = dockIcon };
						}
					}
				}


				/*
				var retreatBookmark = Bot?.ConfigSerialAndStruct.Value?.RetreatBookmark;

				yield return new MenuPathTask
				{
					RootUIElement = memoryMeasurement?.InfoPanelCurrentSystem?.ListSurroundingsButton,
					Bot = Bot,
					ListMenuListPriorityEntryRegexPattern = new[] { new[] { retreatBookmark }, new[] { @"dock", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },
				};
				*/
			}
		}

and this is how I changed SaveShip.cs main method (I’m trying to test Retreat, and want it to proceed to this step right after undocking):

		public IEnumerable<IBotTask> Component
		{
			get
			{
				var memoryMeasurement = Bot?.MemoryMeasurementAtTime?.Value;

				var charIsLocatedInHighsec = 500 < memoryMeasurement?.InfoPanelCurrentSystem?.SecurityLevelMilli;

				var setLocalChatWindowCandidate =
					memoryMeasurement?.WindowChatChannel
					?.Where(window => window?.Caption?.RegexMatchSuccessIgnoreCase(@"local") ?? false)
					?.ToArray();

				if (1 < setLocalChatWindowCandidate?.Length)
					yield return new DiagnosticTask
					{
						MessageText = CannotIdentifyLocalChatWindowDiagnosticText,
					};

				var localChatWindow = setLocalChatWindowCandidate?.FirstOrDefault();

				if (null == localChatWindow)
					yield return new DiagnosticTask
					{
						MessageText = LocalChatWindowNotFoundDiagnosticText,
					};

				var sessionDurationSufficient = AllowRoamSessionDurationMin <= memoryMeasurement?.SessionDurationRemaining;

				// testing new retreat
				var retreatTask = new RetreatTask
				{
					Bot = Bot,
				};
				yield return retreatTask;

				/*

				if (sessionDurationSufficient && (charIsLocatedInHighsec || ChatIsClean(localChatWindow)))
				{
					AllowRoam = true;
					AllowAnomalyEnter = AllowAnomalyEnterSessionDurationMin <= memoryMeasurement?.SessionDurationRemaining;
					yield break;
				}

				yield return new RetreatTask
				{
					Bot = Bot,
				};
				*/
			}
		}

And this is a new task I’ve to do the clicking:

	public class ElementClickTask : IBotTask
	{
		public Bot bot;

		public Interface.MemoryStruct.IUIElement element;

		public IEnumerable<IBotTask> Component => null;

		public IEnumerable<MotionParam> Effects
		{
			get
			{
				yield return element?.MouseMove();

				yield return element?.MouseClick(MouseButtonIdEnum.Left);
			}
		}
	}

To implement the toggle, make sure that this effect description is propagated through to the root of the task tree.
The code you gave creates a description of an effect, what you need to do next is tell the bot that it should perform this effect.
I looked into the source code of A-Bot to find an example of how this is done. I used text search with the term KeyboardPressCombined.
This way, I found the code at

As we can see in this sample, the return value from KeyboardPressCombined is returned via the Effects property. This way, it is propagated through the task tree.

The simplest way of making sure the effect reaches the tree is to implement it right in the root. Replace the RootTaskListComponentDefault method with following code:

IEnumerable<IBotTask> RootTaskListComponentDefault()
{
	var theEffect =
		MotionParamExtension.KeyboardPressCombined(new[] {
			WindowsInput.Native.VirtualKeyCode.LCONTROL,
			WindowsInput.Native.VirtualKeyCode.SPACE
		});

	yield return new BotTask { Effects = new[] { theEffect } };
}

Then you will see the bot performing this effect described by the code you posted.

Does this make sense?