Im struggling to check if there is at least a minute between two date times. I created a game in c# and have limited a part of my game to once a minute every time this command is
executed it runs a void
The problem is that it does it even if it hasn't been a minute?
public void _CheckIfBeenAMinute
{
string TimeStamp;
using (IQueryAdapter dbClient = SilverwaveEnvironment.GetDatabaseManager().getQueryreactor())
{
dbClient.setQuery("SELECT game_timestamp FROM users WHERE id=" + Session.Id + "");
TimeStamp = dbClient.getString();
}
DateTime TimeStamp_Converted = Convert.ToDateTime(TimeStamp);
if (TimeStamp_Converted > DateTime.UtcNow.AddMinutes(-1))
{
//It has been a minuted...
//But the problem is, i it hasnt been it still does this?
this.SendMessage("You have reached your limit today");
return;
}
}
EDIT: I have decided to use timespan. But when I try to get the seconds of the timespan after it has reached 60 it resets?
Try
if ((DateTime.UtcNow - TimeStamp_Converted).TotalMinutes > 1)
It should be:
if (TimeStamp_Converted < DateTime.UtcNow.AddMinutes(-1))
Related
So I'm using Hangfire schedule jobs that send out reminders to users. Whenever I set the Job to run every monday for example at 14h00. If I do this in the morning say at 09h00. The first execution, should go off at 14h00. Problem is what seems to be happening is when I look in the dashboard it says Next execution is in 7 days.
Heres the code :
public class ReminderTimeService : IReminderTimeService
{
private readonly IRecurringJobManager _recurringJobClient;
public ReminderTimeService(
IRecurringJobManager recurringJobClient)
{
_recurringJobClient = recurringJobClient;
}
public void ScheduleJobs(List<ReminderTime> reminderTimeList)
{
foreach (var reminder in reminderTimeList)
{
_recurringJobClient.AddOrUpdate<IProactiveMessageService>(
// Job Id
$"send-status-set-reminder-{reminder.Day.Substring(0, 3)}-
{reminder.StartTime.Split(":")[0]}-{reminder.StartTime.Split(":")[1]}",
// Service to run work
c => c.SendStatusSetReminder(),
// Repeat weekly
GetWeekCronExpression(reminder.Day, reminder.StartTime),
// Use local time
TimeZoneInfo.Local);
}
private DayOfWeek GetDayOfTheWeek(string day)
{
switch (day)
{
case "Monday":
return DayOfWeek.Monday;
case "Tuesday":
return DayOfWeek.Tuesday;
case "Wednesday":
return DayOfWeek.Wednesday;
case "Thursday":
return DayOfWeek.Thursday;
case "Friday":
return DayOfWeek.Friday;
default:
return DayOfWeek.Monday;
}
}
private string GetWeekCronExpression(string day, string time)
{
var hour = Convert.ToInt16(time.Split(":")[0]);
var minute = Convert.ToInt16(time.Split(":")[1]);
var weekStartExp = Cron.Weekly(GetDayOfTheWeek(day), hour, minute);
return weekStartExp;
}
}
}
My guess is theres something I'm fundamentally not understanding or theres a bug with Hangfire.
In any case I'm happy to just get a work around.
Thanks
Maybe there is a timezone problem and your hour:minute pair is slightly in the past. I just created a recurring job with manual parameters of Cron.Weekly() and in the dashboard it says, it will be perform in two hours (as expected).
Maybe you should hover over the string In 7 days on the dashboard, then you'll see the exact time. Check if it is your expected time and check with the debugger what comes within the StartTime property in your reminder object.
If all of these don't help, would be my last rescue to also add an ordinary job by calling jobClient.Enqueue() with the desired time.
Code for a command for a private server for a game I am "developing".
internal class LotteryCommand : Command
{
public LotteryCommand()
: base("lottery", 1)
{
}
//lottery can be used once every 24h, just to attract users making them want to get on at least once a day!
protected override bool Process(Player player, RealmTime time, string[] args)
{
Random rand = new Random();
string name = player.Name;
string lottonum = rand.Next(0, 100).ToString();
//50 50 chance!
if (int.Parse(lottonum) > 49)
{
player.Manager.Database.DoActionAsync(db =>
{
player.Credits = db.UpdateCredit(player.Client.Account, +5000);
player.UpdateCount++;
});
}
foreach (Client i in player.Manager.Clients.Values)
{
i.SendPacket(new TextPacket
{
BubbleTime = 0,
Stars = -1,
Name = "Lottery - " + name,
Text = "rolled a " + lottonum
});
}
return true;
}
}
can anyone tell me what to add if i wanted the command only to be used once per 24 hours?
for those who cant tell/wanna know, its /lottery, and will say "Lottery - Player has rolled a #" and if the # is 50 or higher, the player who used the command will win 5k gold (credits) and if below, they dont win anything.
i just want it to where it can only be used once every 24 hours.
Whenever a player runs that command you can insert a timestamp into the database. At the beginning of the command method, put a bit of code to check the database for that timestamp and see if it is within the last 24 hours. If it is, the break out of the method or put some functionality to alert the user they have played to often.
You can create another method called CanPlay that returns a bool, and pass it the user and the current timestamp. Use that method to determine if the user can play.
As always, im quite the noob, as im sure you will see from both my code and question. For practice im currently writing an Xamarin.Android app for a game called Eve Online. People there mine resources from planets to make cash. These mines have to be reset at different intervals, and the real pros can have up to 30 characters doing it. Each character can have 5 planets, usually there are at least 2 mines (extractors) on each. So there could be 300 timers going on.
In my app you save your characters in an sqlite db, and every hour a intentservice runs through the API and checks your times and if their expired or not. This is how i do that:
public async Task PullPlanets(long KeyID, long CharacterID, string VCode, string CharName)
{
XmlReader lesern = XmlReader.Create("https://api.eveonline.com/char/PlanetaryColonies.xml.aspx?keyID=" + KeyID + "&vCode=" + VCode + "&characterID=" + CharacterID);
while (lesern.Read())
{
long planet = 0;
string planetName;
planet = Convert.ToInt64(lesern.GetAttribute("planetID"));
planetName = lesern.GetAttribute("planetName");
if ((planet != 0) && (planetName != null))
{
planets.Add(planet);
planetNames.Add(planetName);
await GetExpirationTimes(CharName, planet, planetName, KeyID, CharacterID, VCode);
}
}
lesern.Close ();
}
public async Task GetExpirationTimes(string CharName, long planetID, string planetName, long KeyID, long CharacterID, string VCode)
{
string planet = planetID.ToString();
XmlReader lesern = XmlReader.Create("https://api.eveonline.com/char/PlanetaryPins.xml.aspx?keyID=" + KeyID + "&vCode=" + VCode + "&characterID=" + CharacterID + "&planetID=" + planet);
while (lesern.Read())
{
string expTime;
expTime = lesern.GetAttribute("expiryTime");
if ((expTime != null) && (expTime != "0001-01-01 00:00:00"))
{
allInfo.Add (new AllInfo (CharName, planetName, Convert.ToDateTime (expTime)));
}
}
lesern.Close ();
SendOrderedBroadcast (stocksIntent, null);
}
}
After this, it sends the times back to my Activity, where they get added to an extractor. It seems to work pretty fine, although ive only been able to test with 2 characters with a total of 14 extractors so far. An alarmmanger in activity calls the service every hour, and it sends a notification. When user opens the activity, it pulls the list from service, sorts it, and displays it. I would welcome input on if this is the way to do it.
I do see a problem in the horizon, though. The Eve API blocks if an app surpases 30 API-calls per second. Im pretty sure someone with 30 characters would do that. So, im wondering if i should add something to delay each call if a certain number is passed? This is how i call the first XML call.
var table = db.Table<CharsList> ();
foreach (var e in table) {
long KeyIDOut = Convert.ToInt64(e.KeyID);
long CharIDOut = Convert.ToInt64(e.CharacterID);
string VCodeOut = e.VCode.ToString();
string navnOut = e.Name.ToString();
PullPlanets(KeyIDOut, CharIDOut, VCodeOut, navnOut);
}
CheckTimes ();
}
Is it viable to add a
if (table.Count > 10) {
foreach (var e in table) {
//start the first characters call
Thread.Sleep(100)
}
The service is intentservice and not on UI thread. I guess this would bring the calls under 30 a sec, but i have never used Thread.Sleep and fear what else could happen in my code. Are there other things that could help me not blow the limit? Can this code handle 300 extractors?
I believe you are generally right in your approach. I had to do a similar thing for a reddit client I was writing, except their limits is once a second or so.
The only problem I see with your setup is that assume that Thread.Sleep does sleep for the amount of time you give it. Spurious wakeups are possible in some cases, so what I would suggest is that you give it a smaller value, save the last time you accessed the service and then put a loop around the sleep call that terminates once enough time has passed.
Finally if you are going to be firing up a lot of intent services for a relatively short amount of work, you might want to have a normal service with a thread to handle the work - that way it will only have to be created once but it is still of the UI thread.
I am well aware this question has been asked for vb.net however there is none for c# and I have been struggling with this code for about 3 weeks now and am stuck on this point.
I need to get the total duration of time a user has spent on an application. So far I have tried using appstart and append time spans however I get 00:00:00 I know why i am getting this result however I do not know how to solve my problem and I am at my wits end. So can anyone explain to me how one goes about calculating the total amount of time a window is open and saving that information all in real time.
DateTime appStart = new DateTime();
DateTime appStart = new DateTime();
TimeSpan Duration;
DateTime now = DateTime.Now;
string time = now.ToString();
const int nChars = 256;
int handle = 0;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
string strbuff = Buff.ToString();
appstart = DateTime.Now();
#region insert statement
try
{
var with = cnSave;
if (with.State == ConnectionState.Open)
with.Close();
with.ConnectionString = cnString;
with.Open();
string strQRY = "Insert Into [Log] values ('" + strbuff + "', '" + time + "', '" + Processing + "')";
OleDbCommand cmd = new OleDbCommand(strQRY, cnSave);
try
{
cmd.ExecuteNonQuery();
}
catch (Exception)
{
}
}
finally { }
#endregion
ActivityTimer.Start();
Processing = "Working";
}
This is not the full application nor is it what it looks like at the moment that is on a different PC and I havent uploaded that yet but this more or less sums up what the application does I run most of the codes from timers and well Like I said I am stuck.
The logic of what I am trying to do.
An end user starts Notepad.
my applications records the time that notepad is in focus or is the
active window-
end user opens or switches to a new application like Ms Words.
my applications records the time that the user switched or closed notepad and calculates the difference between these two times and I get the total duration and save this information into a database.
and so on and so forth.
Why don't you just record DateTime.Now when the user starts the application or loads a form you are interested in. Then, at any time you can just check the difference between current DateTime.Now and the recorded one to see how long they have been used it? This seems pretty obvious? Have I missed something?
So...
AppStart or Form you are interested loads etc...
Global.TimeStarted = DateTime.Now;
...
Some arbitrary time or they close the app etc...
var usingAppFor = DateTime.Now - Global.TimeStarted;
I have used Global but in your architecture store it somewhere it makes sense. You get the general idea though.
As a base I'd use:
class SubscribedProgram
{
public DateTime StartTime { get; set; }
public TimeSpan TotalTime { get; set; }
public object LinkedProgram { get; set; }
public SubscribedProgram(object linkedProgram)
{
this.LinkedProgram = linkedProgram;
}
public void programActivated()
{
this.StartTime = DateTime.Now;
}
public void programDeactivated()
{
// If this was the first deactivation, set totalTime
if (this.TotalTime == TimeSpan.MinValue) { this.TotalTime = DateTime.Now.Subtract(this.StartTime); }
// If this is not the first calculation, add older totalTime too
else { this.TotalTime = this.TotalTime + (DateTime.Now.Subtract(this.StartTime)); }
}
}
Then somehow you need to monitor each followed programs "Activate" and "Deactivate"-events. Each time some of these events are triggered, you need to read the program, find the linked SubscribedProgram-object, and run it's corresponding method (like programActivated()).
It has to be trivial, but I just cannot get through it.
I have to limit amount of tasks (let's say connections, emails sent or clicks in the button) per amount of time. So e.g. I can send 1000 emails per hour.
How can I do that in c#? I don't know and don't care how much time each operation will take. I just want to make sure that for last hour, only 1000 will be executed.
class EventLimiter
{
Queue<DateTime> requestTimes;
int maxRequests;
TimeSpan timeSpan;
public EventLimiter(int maxRequests, TimeSpan timeSpan)
{
this.maxRequests = maxRequests;
this.timeSpan = timeSpan;
requestTimes = new Queue<DateTime>(maxRequests);
}
private void SynchronizeQueue()
{
while ((requestTimes.Count > 0) && (requestTimes.Peek().Add(timeSpan) < DateTime.UtcNow))
requestTimes.Dequeue();
}
public bool CanRequestNow()
{
SynchronizeQueue();
return requestTimes.Count < maxRequests;
}
public void EnqueueRequest()
{
while (!CanRequestNow())
Thread.Sleep(requestTimes.Peek().Add(timeSpan).Subtract(DateTime.UtcNow));
// Was: System.Threading.Thread.Sleep(1000);
requestTimes.Enqueue(DateTime.UtcNow);
}
}
Assuming a rolling hour window:
Maintain a list of when actions were done.
Each time you want to do your action, remove all in the list not within the hour.
If there are fewer than 1000 then do the action and add a record to your list.
Assuming hourly:
Create a proxy method and a variable that is incremented for every action, and reduced to zero on the hour.
Do your action if the counter is < 1000.
The above solution looked fine. Here is my trimmed down version:
public class EmailRateHelper
{
private int _requestsPerInterval;
private Queue<DateTime> _history;
private TimeSpan _interval;
public EmailRateHelper()
: this(30, new TimeSpan(0, 1, 0)) { }
public EmailRateHelper(int requestsPerInterval, TimeSpan interval)
{
_requestsPerInterval = requestsPerInterval;
_history = new Queue<DateTime>();
_interval = interval;
}
public void SleepAsNeeded()
{
DateTime now = DateTime.Now;
_history.Enqueue(now);
if (_history.Count >= _requestsPerInterval)
{
var last = _history.Dequeue();
TimeSpan difference = now - last;
if (difference < _interval)
{
System.Threading.Thread.Sleep(_interval - difference);
}
}
}
}
You can use Rx extensions (How to use the new BufferWithTimeOrCount in Rx that returns IObservable<IObservable<T>> instead of IObservable<IList<T>>), but I would implement the buffering manually by adding an appropriate proxy object.
You may also consider storing {action, time, user} information in a database and get number of actions in a last hour fomr the DB (or similar persisted storager) if you need to handle Application pool restarts / crashes. Otherwise clever user may circumvent your in-memory protection with overloading your server.
You can create a persistent counter for every user. Every time you receive a request (for sending an email) you need to check the value of the counter and the date of the counter creation.
If the count is greater than the limit you refuse the request
If the date is older than an hour you reset the counter and set the new creation date
If the date is correct and the count is under the limit you increase the counter
Only in the last two cases the request is executed.