What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

FixIdle - Thread Safe version of the Unstucker.

I have this problem with idle in town and bot doesn't take tp back after stash interaction. But this is not caused by this very plugin.


Dev, pls check if there is a problem with your plugin and Artemis plugin. because as i mentione dabove the bot idles on start postion sometimes (Barb).
 
thread safety comes into play if you are trying to multithread something that is not thread safe. db runs the entire bot in a single thread therefore thread safety is not a concern, unless you try to spawn a new thread or kill the existing one from a plugin. good thought though. calling a thread.sleep is not an issue as when the bot is 'stuck' it isn't doing anything useful anyway. it's the same as pausing the bot.

This exactely!

The first Unstucker is working pretty good with the moving-around adjustment, BUT if the bot manages to get stuck so badly that he cant even move you can still get stuck in the game. Thats why i modified the first Unstucker and kinda merged both ideas together by using a counter for move tries.
If the bot is idle inside town that seems to be a whole other problem and the Unstucker will just move the bot around in there without leaving the game - this may still need some work.

The thread-savety part does not seem to be a huge problem imo and if there is no proper api-support (i dont see one either) for that sort of thing thats the only way to get the moving-around part working properly i guess. Although i did not test this code very well - so DONT take this to be working perfectly.

Code:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using Zeta;
using Zeta.Common;
using Zeta.CommonBot;
using Zeta.Common.Plugins;
using Zeta.Internals;
using Zeta.Internals.Actors;
using Zeta.Internals.Service;

namespace Eax.Plugins
{
    public class Unstucker : IPlugin
    {
        private bool IsRestarting { get; set; }
        private bool IsChecking { get; set; }
	private int MoveTries { get; set; }
	private int MoveTriesMax { get; set; }
        private DateTime LastCheckTime { get; set; }
        private DateTime LastLogTime { get; set; }
        private List<Vector3> LoggedPositions { get; set; }

        public string Author { get { return "eax"; } }
        public string Description { get { return "Unstucks you when you're stuck."; } }
        public string Name { get { return "Unstucker v" + Version.ToString(); } }
        public Version Version { get { return new Version(1, 8); } }
        public Window DisplayWindow { get { return null; } }

        private void Log(string message)
        {
            Logging.Write(string.Format("[{0}] {1}", Name, message));
        }

        private static float GetMaxDistanceTraveled(List<Vector3> positions)
        {
            float max = 0F;
            for (int i = 0; i < positions.Count - 1; ++i)
                for (int j = 1; j < positions.Count; ++j)
                    max = Math.Max(max, Math.Abs(positions[i].Distance(positions[j])));
            return max;
        }

        public void OnInitialize()
        {
            IsRestarting = false;
            IsChecking = false;
            LastCheckTime = DateTime.Now;
            LastLogTime = DateTime.Now;
	    MoveTries = 0;
            MoveTriesMax = 5;
            LoggedPositions = new List<Vector3>();
        }

	public void RemoveAllBehavior()
        {
	   ProfileManager.CurrentProfileBehavior.ResetCachedDone();
	}
	
        public void OnPulse()
        {
            // if we're not in game and not in the process of restarting, do nothing
            if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid)
            {
                LastCheckTime = DateTime.Now;
                LastLogTime = DateTime.Now;
                LoggedPositions.Clear();
                return;
            }

	    // Did I invoke Restart and finally got to the Town?	
	    if( ZetaDia.Me.IsInTown && IsRestarting )
	    {
		// Leave the game and reset everything
		Log( "Leaving the game." );
	        Clear();
		ZetaDia.Service.Games.LeaveGame();
		return;
	    }

            // if it's been 4 seconds since we've logged a position, then we log a new position
            if (DateTime.Now.Subtract(LastLogTime).TotalSeconds > 4)
            {
                LastLogTime = DateTime.Now;
                LoggedPositions.Add(ZetaDia.Actors.Me.Position);
            }

            // if it's been 30 seconds since we've last evaluated the logged positions, evaluate the logged positions
            if (!IsChecking && (LoggedPositions.Count > 5) && (DateTime.Now.Subtract(LastCheckTime).TotalSeconds > 30))
            {
                // we want to prevent this section from being executed twice - can that even happen with one thread?
                IsChecking = true;

                var rnd = new Random();
                // if our person has not traveled 10 yards or whatever in the last 30 seconds, we're stuck
                if (GetMaxDistanceTraveled(LoggedPositions) < 10f)
                {
                    Log("Seems the bot just got stuck.");
		    IsRestarting = true;
		    
                    var v = ZetaDia.Me.Position; 
                    var oldPosition = v; 
                    do {
                        if (MoveTries > MoveTriesMax)
			{
			    Log("Random moving was unsuccessful - moving to town and restarting bot.");
			    Log( "Using Town Portal." );
		
			    RemoveAllBehavior();
			    ZetaDia.Me.UseTownPortal();
			    return;
			}	    
                        v.X = (DateTime.Now.Millisecond % 2 == 0) ? v.X + rnd.Next(500, 6500) : v.X - rnd.Next(500, 6500); 
                        v.Y = (DateTime.Now.Millisecond % 2 == 0) ? v.Y + rnd.Next(500, 6500) : v.Y - rnd.Next(500, 6500); 
                        v.Z = (DateTime.Now.Millisecond % 2 == 0) ? v.Z + rnd.Next(500, 6500) : v.Z - rnd.Next(500, 6500); 

                        Log("Moving Char randomly to: " + v.ToString()); 
                        ZetaDia.Me.UsePower(SNOPower.Walk, v, ZetaDia.Me.WorldDynamicId, 2, -1); 
			            MoveTries++;	
                        Thread.Sleep(rnd.Next(1000, 3000)); // Is there a better way via api?
                    } while (ZetaDia.Me.Position == oldPosition);
                    IsRestarting = false;
                }               
		Clear();
            }
        }
	
        private void Clear()
        {
	    MoveTries = 0;
	    IsChecking = false;
	    LastCheckTime = DateTime.Now;
	    LastLogTime = DateTime.Now;
	    LoggedPositions.Clear();
        }

        public void OnShutdown()
        {
        }

        public void OnEnabled()
        {
            Log("Enabled.");
        }

        public void OnDisabled()
        {
            Log("Disabled.");
        }
        
        public bool Equals(IPlugin other)
        {
            return (other.Name == Name) && (other.Version == Version);
        }
    }
}

All credit goes to the original guy writing this (eax, Nuls), i just modified the MoveTries-Part to leave the game again and fiddled around with the timings a bit.

EDIT: Wrong code the first time, got it mixed up with a backup, sorry.
 
Last edited:
09:11:10.430 N] Creating new game, Params: <Act:A3 Difficulty:Nightmare Quest:87700 QuestStep:-1 ResumeFromSave:True IsPrivate:True>
[09:11:21.008 N] [FixIdle] Using Town Portal
[09:11:27.811 N] Path with 5 hops received (partial: False)
[09:11:33.813 N] Creating new game, Params: <Act:A3 Difficulty:Nightmare Quest:87700 QuestStep:-1 ResumeFromSave:True IsPrivate:True>
[09:11:38.352 N] [FixIdle] Using Town Portal
[09:11:48.190 N] Path with 5 hops received (partial: False)
[09:11:54.207 N] Creating new game, Params: <Act:A3 Difficulty:Nightmare Quest:87700 QuestStep:-1 ResumeFromSave:True IsPrivate:True>
[09:11:58.839 N] [FixIdle] Using Town Portal
[09:12:07.070 N] Path with 5 hops received (partial: False)
[09:12:13.071 N] Creating new game, Params: <Act:A3 Difficulty:Nightmare Quest:87700 QuestStep:-1 ResumeFromSave:True IsPrivate:True>

kinda weird .. Dunno what happened there
 
there should be a timeout for the bot to take the portal
sometimes the but just keeps moving and cant use the portal
 
Last edited:
Edit. Bit stuck still issues.

Can you be more specific?

there should be a timeout for the bot to take the portal
sometimes the but just keeps moving and cant use the portal

If you are talking about my edited version: The Thread.Sleep() should handle that, actually. If its not working for you try to increase the timings. If that wont work i dont have any clue why not.

I edited this again and removed checking inside town because i had some game leaves while identifying items. Had one unhandled stuck in town so far and another stuck in the game where i dont know why the plugin didnt work, cant reproduce it though.

Code:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using Zeta;
using Zeta.Common;
using Zeta.CommonBot;
using Zeta.Common.Plugins;
using Zeta.Internals;
using Zeta.Internals.Actors;
using Zeta.Internals.Service;

namespace Eax.Plugins
{
    public class Unstucker : IPlugin
    {
        private bool IsRestarting { get; set; }
        private bool IsChecking { get; set; }
	private int MoveTries { get; set; }
	private int MoveTriesMax { get; set; }
        private DateTime LastCheckTime { get; set; }
        private DateTime LastLogTime { get; set; }
        private List<Vector3> LoggedPositions { get; set; }

        public string Author { get { return "eax"; } }
        public string Description { get { return "Unstucks you when you're stuck."; } }
        public string Name { get { return "Unstucker v" + Version.ToString(); } }
        public Version Version { get { return new Version(1, 8); } }
        public Window DisplayWindow { get { return null; } }

        private void Log(string message)
        {
            Logging.Write(string.Format("[{0}] {1}", Name, message));
        }

        private static float GetMaxDistanceTraveled(List<Vector3> positions)
        {
            float max = 0F;
            for (int i = 0; i < positions.Count - 1; ++i)
                for (int j = 1; j < positions.Count; ++j)
                    max = Math.Max(max, Math.Abs(positions[i].Distance(positions[j])));
            return max;
        }

        public void OnInitialize()
        {
            IsRestarting = false;
            IsChecking = false;
            LastCheckTime = DateTime.Now;
            LastLogTime = DateTime.Now;
	    MoveTries = 0;
            MoveTriesMax = 5;
            LoggedPositions = new List<Vector3>();
        }

	public void RemoveAllBehavior()
        {
	   ProfileManager.CurrentProfileBehavior.ResetCachedDone();
	}
	
        public void OnPulse()
        {
	    // if we're not in game and not in the process of restarting, do nothing
            if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid || (ZetaDia.Me.IsInTown && !IsRestarting))
            {
                LastCheckTime = DateTime.Now;
                LastLogTime = DateTime.Now;
                LoggedPositions.Clear();
                return;
            }
	    
	    // Did I invoke Restart and finally got to the Town?	
	    if( ZetaDia.Me.IsInTown && IsRestarting )
	    {
		// Leave the game and reset everything
		Log( "Leaving the game." );
	        Clear();
		ZetaDia.Service.Games.LeaveGame();
		return;
	    }

            // if it's been 4 seconds since we've logged a position, then we log a new position
            if (DateTime.Now.Subtract(LastLogTime).TotalSeconds > 4)
            {
                LastLogTime = DateTime.Now;
                LoggedPositions.Add(ZetaDia.Actors.Me.Position);
            }

            // if it's been 30 seconds since we've last evaluated the logged positions, evaluate the logged positions
            if (!IsChecking && (LoggedPositions.Count > 5) && (DateTime.Now.Subtract(LastCheckTime).TotalSeconds > 30))
            {
                // we want to prevent this section from being executed twice - can that even happen with one thread?
                IsChecking = true;

                var rnd = new Random();
                // if our person has not traveled 10 yards or whatever in the last 30 seconds, we're stuck
                if (GetMaxDistanceTraveled(LoggedPositions) < 10f)
                {
                    Log("Seems the bot just got stuck.");
		    IsRestarting = true;
		    
                    var v = ZetaDia.Me.Position; 
                    var oldPosition = v; 
                    do {
                        if (MoveTries > MoveTriesMax)
			{
			    Log("Random moving was unsuccessful - moving to town and restarting bot.");
			    Log( "Using Town Portal." );
		
			    RemoveAllBehavior();
			    ZetaDia.Me.UseTownPortal();
			    return;
			}	    
                        v.X = (DateTime.Now.Millisecond % 2 == 0) ? v.X + rnd.Next(500, 6500) : v.X - rnd.Next(500, 6500); 
                        v.Y = (DateTime.Now.Millisecond % 2 == 0) ? v.Y + rnd.Next(500, 6500) : v.Y - rnd.Next(500, 6500); 
                        v.Z = (DateTime.Now.Millisecond % 2 == 0) ? v.Z + rnd.Next(500, 6500) : v.Z - rnd.Next(500, 6500); 

                        Log("Moving Char randomly to: " + v.ToString()); 
                        ZetaDia.Me.UsePower(SNOPower.Walk, v, ZetaDia.Me.WorldDynamicId, 2, -1); 
			MoveTries++;	
                        Thread.Sleep(rnd.Next(2000, 4000)); // Is there a better way via api?
                    } while (ZetaDia.Me.Position == oldPosition);
                    IsRestarting = false;
                }               
		Clear();
            }
        }
	
        private void Clear()
        {
	    MoveTries = 0;
	    IsChecking = false;
	    LastCheckTime = DateTime.Now;
	    LastLogTime = DateTime.Now;
	    LoggedPositions.Clear();
        }

        public void OnShutdown()
        {
        }

        public void OnEnabled()
        {
            Log("Enabled.");
        }

        public void OnDisabled()
        {
            Log("Disabled.");
        }
        
        public bool Equals(IPlugin other)
        {
            return (other.Name == Name) && (other.Version == Version);
        }
    }
}

Keep testing them codez ;)
 
09:11:10.430 N] Creating new game, Params: <Act:A3 Difficulty:Nightmare Quest:87700 QuestStep:-1 ResumeFromSave:True IsPrivate:True>
[09:11:21.008 N] [FixIdle] Using Town Portal
[09:11:27.811 N] Path with 5 hops received (partial: False)
[09:11:33.813 N] Creating new game, Params: <Act:A3 Difficulty:Nightmare Quest:87700 QuestStep:-1 ResumeFromSave:True IsPrivate:True>
[09:11:38.352 N] [FixIdle] Using Town Portal
[09:11:48.190 N] Path with 5 hops received (partial: False)
[09:11:54.207 N] Creating new game, Params: <Act:A3 Difficulty:Nightmare Quest:87700 QuestStep:-1 ResumeFromSave:True IsPrivate:True>
[09:11:58.839 N] [FixIdle] Using Town Portal
[09:12:07.070 N] Path with 5 hops received (partial: False)
[09:12:13.071 N] Creating new game, Params: <Act:A3 Difficulty:Nightmare Quest:87700 QuestStep:-1 ResumeFromSave:True IsPrivate:True>

kinda weird .. Dunno what happened there

Same here, not working very good.
 
The plugin seems to think you're stuck when the bot is waiting around for a couple seconds to go use the stash/sell. It keeps leaving TPing out and leaving the game before the bot can use the stash.
 
Is it possible to add some sort of tolerance to the current position check? Or take a few samples? I am stuck on core at times in this area: imgur: the simple image sharer.

Since the bot runs back and forth a few paces, the plugin doesn't trigger.
 
After stuck I did not see how plugin works. Just nothing happened during the 30s-1m (.
 
Back
Top