2

so I have a system where you can set to call a taxi (this is a game application), and this taxi takes 10 seconds to come. The issue is I also have a canceltaxi function and I need to know how I can stop a System.Threading.Timer because when they order a taxi, cancel it after lets say 8 seconds then straight after they order another taxi, that taxi takes 2 seconds to come not 10 so it still uses the old taxi timer, how do I stop it?

I have tried this code but still it doesnt stop.. I call this void when I want to cancel it.

public void StopTaxiTimer()
        {
            taxiTimerInstance.Dispose();
            taxiTimerInstance = null;
            this.Dispose();
        }

Full class:

using log4net;
using Plus.Communication.Packets.Outgoing.Rooms.Chat;
using Plus.HabboHotel.GameClients;
using Plus.HabboHotel.Roleplay.Instance;
using Plus.HabboHotel.Rooms;
using System;
using System.Threading;

namespace Plus.HabboHotel.Roleplay.Timers
{
    public sealed class TaxiTimer : IDisposable
    {
        private static readonly ILog myLogger = LogManager.GetLogger("Plus.HabboHotel.Roleplay.Timers.DeathTimer");

        private Timer taxiTimerInstance;
        private uint timerTimeSeconds;
        private RoleplayInstance roleplayInstance;

        public TaxiTimer(RoleplayInstance roleplayInstance)
        {
            Console.WriteLine("Setup TaxiTimer for " + roleplayInstance.GetSession().GetHabbo().Username + " (" + roleplayInstance.TaxiWaitTimeSeconds + " seconds)");
            this.timerTimeSeconds = roleplayInstance.TaxiWaitTimeSeconds;
            this.roleplayInstance = roleplayInstance;
            this.taxiTimerInstance = new Timer(new TimerCallback(this.OnTimerElapsed), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
        }

        public void OnTimerElapsed(object Obj)
        {
            try
            {
                if (taxiTimerInstance == null)
                    return;

                if (roleplayInstance == null || !roleplayInstance.CalledTaxi || roleplayInstance.GetSession() == null || roleplayInstance.GetSession().GetHabbo() == null)
                    return;

                GameClient gameSession = roleplayInstance.GetSession();

                if (roleplayInstance.TaxiWaitTimeSeconds < 1)
                {
                    Room currentRoom = gameSession.GetHabbo().CurrentRoom;
                    if (currentRoom == null)
                        return;

                    RoomUser roomUser = currentRoom.GetRoomUserManager().GetRoomUserByHabbo(gameSession.GetHabbo().Id);
                    if (roomUser == null)
                        return;

                    roleplayInstance.CalledTaxi = false;
                    currentRoom.SendMessage(new ShoutComposer(roomUser.VirtualId, "*Gets transported to my destination*", 0, roomUser.LastBubble));
                    gameSession.GetHabbo().PrepareRoom(roleplayInstance.TaxiRoomId, string.Empty);
                }
                else
                {
                    roleplayInstance.TaxiWaitTimeSeconds--;
                }
            }
            catch (Exception ex)
            {
                myLogger.Error(ex.Message);
                myLogger.Error(ex.StackTrace);
            }
        }

        public void StopTaxiTimer()
        {
            taxiTimerInstance.Dispose();
            taxiTimerInstance = null;
            this.Dispose();
        }

        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }
    }
}

On CallTaxi:

roleplayInstance.TaxiWaitTimeSeconds = Convert.ToUInt32(PlusEnvironment.GetRPManager().GetSettings().GetSettingValueByKey("roleplay_taxi_wait_seconds"));
                    roleplayInstance.TaxiRoomId = goingTo.RoomId;
                    roleplayInstance.TaxiTimer = new HabboHotel.Roleplay.Timers.TaxiTimer(roleplayInstance);
Liam Hardy
  • 155
  • 4
  • 13
  • Try `taxiTimerInstance.Change(Timeout.Infinite, Timeout.Infinite);` You'll then have to reset it back to 10 seconds on the next order. https://msdn.microsoft.com/en-us/library/yz1c7148(v=vs.110).aspx – TyCobb Oct 10 '16 at 21:24
  • Does this answer your question? [Reliably stop System.Threading.Timer?](https://stackoverflow.com/questions/6379541/reliably-stop-system-threading-timer) – Michael Freidgeim Nov 21 '20 at 22:39

4 Answers4

4

I need to know how I can stop a System.Threading.Timer because when they order a taxi, cancel it after lets say 8 seconds then straight after they order another taxi, that taxi takes 2 seconds to come not 10 so it still uses the old taxi timer, how do I stop it?

Take a step back. You don't have to worry about canceling a timer if you never make one.

You are describing an asynchronous workflow with cancellation. C# and the .NET framework already has this feature, so use it rather than trying to roll your own.

Make an async workflow method that awaits a Task.Delay task that takes a CancellationToken. The continuation of the delay is the arrival of the taxi; the cancellation method causes the task to fail by canceling the token.

There is plenty of documentation on this mechanism, so start reading it. A good place to start is here:

https://msdn.microsoft.com/en-us/library/dd997364

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
1

There seems to be a lot of cruft going on in your code. There's a few warning signs that I see.

You're not implementing IDisposable correctly. It should be more like this:

public class ExampleDisposable : IDisposable 
{
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing) 
        {
            // free managed resources
        }
        // free native resources if there are any.
    }
}

You've also used the event raising method naming convention for the event handler. Methods for raising the events are normally called On* and the method for handling those events are *. So, in your case, instead of public void OnTimerElapsed(object Obj) it should be public void TimerElapsed(object Obj) by convention.

Also, you are catching an exception with catch (Exception ex). That's just a bad anti-pattern. Have a read of Eric Lippert's Vexing Exceptions.

Now, finally, I'd suggest that you avoid this kind of class anyway. Use Microsoft's Reactive framework instead (NuGet "System.Reactive").

Then you can write this:

Subject<RoleplayInstance> requestTaxi = new Subject<RoleplayInstance>();

IDisposable subscription =
    requestTaxi
        .Select(ri =>
            ri == null
            ? Observable.Never<RoleplayInstance>()
            : Observable
                .Timer(TimeSpan.FromSeconds((double)ri.TaxiWaitTimeSeconds))
                .Select(n => ri))
        .Switch()
        .Subscribe(ri =>
        {
            GameClient gameSession = ri.GetSession();
            Room currentRoom = gameSession.GetHabbo().CurrentRoom;
            RoomUser roomUser = currentRoom.GetRoomUserManager().GetRoomUserByHabbo(gameSession.GetHabbo().Id);
            currentRoom.SendMessage(new ShoutComposer(roomUser.VirtualId, "*Gets transported to my destination*", 0, roomUser.LastBubble));
            gameSession.GetHabbo().PrepareRoom(roleplayInstance.TaxiRoomId, string.Empty);
        });

Now when you want to call for a taxi you just write requestTaxi.OnNext(roleplayInstance);. If you call that a second time before the taxi is called the event automatically restarts. If you call it with requestTaxi.OnNext(null); then it cancels any current request, but is still ready to handle a new one later.

If you are shutting down entirely you call subscription.Dispose();. All just neater I think.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
1

Did you read the tooltips on the Timer.Change method? Try this:

timer.Change(Timeout.Infinite, Timeout.Infinite);
Chris
  • 1,101
  • 8
  • 13
  • Worked for me. I had a method which is executed every second which displays message to console. I wanted it to stop executing after few seconds. I did this and it stopped. – Ziggler Feb 15 '19 at 00:48
1
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_timer = null;
Brian Rice
  • 3,107
  • 1
  • 35
  • 53