scriptsdk / CSharp-ScriptSDK-Legacy

ScriptSDK is an advanced extension written in .NET. The framework is based on a local tcp socket protocol library written by maxwell and extend the provided architecture by additional methods and handlers.
GNU General Public License v3.0
9 stars 11 forks source link

Feature request: Check if Stealth is running #5

Open Bosek opened 8 years ago

Bosek commented 8 years ago

It would be great if there is any method to find out, if there is running Stealth. Now it seems like the only way is something like:

var isRunning = false;
var checkThread = new Thread(() =>
{
    // ScriptSDK is obviously missing reliable way to detect if Stealth is running or not
    // This is ugly way, but I don't see any other option
    try
    {
        // Now call any random method to make ScriptSDK send request to Stealth
        // and possibly throw an exception if it fails
        GameClient.GetClient().GetInfo();
        isRunning = true;
    }
    catch (Exception) { }
});
watchThread = new Thread(() =>
{
    try
    {
        setEnabled(stealthButton, false);

        checkThread.Start();
        if (!checkThread.Join(TimeSpan.FromSeconds(2)) || !isRunning)
            checkThread.Abort();
        else
            setEnabled(stealthButton);
    }
    catch (ThreadAbortException)
    {
        checkThread.Abort();
    }
});

Since I don't want to block WinForms window while checking Stealth, I have to use two different threads.

Please, can you make something like StealthAPI.Stealth.IsRunning to find out, if the connection is OK?

Maybe even test if there is two-way connection possible since I found out that if you run elevated(with admin role) ScriptSDK app, but Stealth is not elevated, you can send request and ScriptSDK will block thread forever, probably waiting for answer(which will not come).

Crome696 commented 8 years ago

Did you tried out to just check for the process itself? I mean you could just make a timer and check process list every here and there. a handshake between client and api is not planned so far. There are workaround ways maybe.

Bosek commented 8 years ago

Yeah, there are only workarounds. It's not needed to make handshaking I guess. Just something that will maybe ping Stealth? I haven't dived into your code to see how exactly it works, but it would be great to have something like event for Stealth open/close itself. I was trying to make somethink more fancy and this was a little nightmare for me. Especially because Stealth sometimes crashes and I don't want to lose script progress.

My scenario: I have mining script. When I first come to the mine, I go around all edges to make some 2d map of mine(I can then save or load the map from txt). When I hit play, script is mining every piece in the mine. Since the mine can have about 800 tiles, it takes some time to mine it all When the Stealth crashes, all my progress is lost because it immediatelly crashes my app(app can't know what happened). See how it looks: http://imgur.com/a/rc7Ft

Of course I can make something like progress saving every second to separate file. I just thought It would be nice to share the idea about possible improvement. ScriptSDK is really good, It just needs some polishment :)

But keep up with good work sir! ScriptSDK already helped me A LOT

Crome696 commented 8 years ago

What you need is a so called Watchguard. 1) You need an application which purpose is to check Thread of Stealth and Script. 2) Your script need to store some data so it would be able to store where it has been before the script crashed 3) Your watchguard should observe Stealth script and the Client. Whenever client dies out, it kills process of stealth remaining process and script and then restart Stealth and script. The script could read process from a config you write while handle the script and work further. Try some winapi functions to get handles that should help your watchguard to identify if its a dead or alive handle.

Crome696 commented 8 years ago

So there is no 100% way to validate the client itself, what i could do is writing a SDK Routine, but i would almost say its much better if the user can decide what way he want to check it up.

Feedback please

drabadan commented 8 years ago

It's because of infinite loop in Stealth constructor, i think i can fix that in a way it will wait 30 secs and then or throw an error.

Bosek commented 7 years ago

Routine would be great since it is very repetitive to write some kind of watcher for almost every project.

Crome696 commented 6 years ago

It´s an old thread but I´m still open for ideas about a watcher..

Bosek commented 6 years ago

I am using something like this in my projects:

public delegate void StealthStatus();
public delegate void MovementStatus(Point3D position);
public static class StealthWatcher
{
    static SemaphoreSlim positionSemaphore = new SemaphoreSlim(0);
    static SemaphoreSlim connectedSemaphore = new SemaphoreSlim(0);
    static Thread watcherThread;

    public static bool IsWatching { get; private set; }
    public static bool Connected { get; private set; }
    public static Point3D Position { get; private set; } = Point3D.Zero;
    public static bool TrackMovement { get; set; } = true;
    public static event StealthStatus OnConnected;
    public static event StealthStatus OnDisconnected;
    public static event MovementStatus OnMove;

    public static void Setup()
    {
        if (watcherThread != null)
            return;
        watcherThread = new Thread(() =>
        {
            GameClient gameClient = null;
            while (true)
            {
                try
                {
                    var connected = Stealth.Client.GetConnectedStatus();
                    if (connected && !Connected)
                    {
                        gameClient = GameClient.GetClient();
                        Connected = true;
                        OnConnected?.Invoke();
                        connectedSemaphore.Release();
                    }
                    else if (!connected && Connected)
                    {
                        Connected = false;
                        OnDisconnected?.Invoke();
                    }
                    if (Connected && TrackMovement)
                    {
                        var position = gameClient.Player.Location;
                        if (position != Position && position != Point3D.Zero)
                        {
                            Position = position;
                            OnMove?.Invoke(position);
                            positionSemaphore.Release();
                        }
                    }
                }
                catch (Exception)
                {
                    if (Connected)
                    {
                        Connected = false;
                        OnDisconnected?.Invoke();
                    }
                    else
                    {
                        Thread.Sleep(900);
                    }
                    // TODO: Some exception handling maybe?
                }
                finally
                {
                    Thread.Sleep(100);
                }
            }
        });
    }

    public static void Start()
    {
        if (IsWatching)
            return;
        if (watcherThread == null)
            Setup();
        watcherThread.Start();
        IsWatching = true;
    }

    public static void Stop()
    {
        if (!IsWatching)
            return;
        if (watcherThread == null)
            return;
        if (watcherThread.IsAlive)
            watcherThread.Abort();
        watcherThread = null;
        IsWatching = false;
    }

    public static void WaitOnConnect(int msTimeout = -1)
    {
        connectedSemaphore.Wait(msTimeout);
    }

    public static void WaitOnMove(int msTimeout = -1)
    {
        positionSemaphore.Wait(msTimeout);
    }
}

I guess it could be optimized, maybe refactored and it uses movement tracking too, which can be removed if wanted. Just an idea. The thing is, that this behaviour is needed almost everytime I script something to be at least a bit foolproof(to share with other ppl) so I basically copy&paste this piece of code in every project. I am just not sure if it is something you would like / isn't too much overkill.