Jump to content
MakeWebGames

Cronless Crons


Dayo

Recommended Posts

Im a big fan of cronless crons and have been pushing this idea for 10 years now! (see here) Ive had a few people ask if i can make this easier to work with in GLv2 so i have created a simple Cron class that will hopefully make life easier and give you a better understanding of cronless crons.

Cron Class

<?php

	class Cron {

		public function __construct($cronName, $cronType, $interval, $maxRepetitions = 1000) {
			global $db, $user;
			$this->db = $db;
			$this->user = $user;
			$this->settings = new Settings();
			$this->cronName = $cronName;
			$this->cronType = $cronType;
			$this->maxRepetitions = $maxRepetitions;
			$this->interval = $interval;
		}

		public function updateLastRun() {

			if (!$this->getRepetitionCount()) return $this; 

			$key = "cron-" . $this->cronName;
			$newTime = time() - (time() % $this->interval);

			if ($this->cronType == "user" && $this->user) {
				$time = $this->user->updateTimer($key, $newTime);
			} else if ($this->cronType == "system") {
				$time = $this->settings->update($key, $newTime);
			}
			
			return $this;
		}

		public function getRepetitionCount () {
			$key = "cron-" . $this->cronName;
				
			if ($this->cronType == "user" && $this->user) {
				$time = $this->user->getTimer($key, false);
			} else if ($this->cronType == "system") {
				$time = $this->settings->loadSetting($key);
			}

			$count =  floor((time() - $time) / $this->interval);

			return $count > $this->maxRepetitions?$this->maxRepetitions:$count;

		}

		public function getCronDates() {

			$key = "cron-" . $this->cronName;
				
			if ($this->cronType == "user" && $this->user) {
				$time = $user->getTimer($key);
			} else if ($this->cronType == "system") {
				$time = $this->settings->loadSetting($key);
			}

			$count =  floor((time() - intval($time)) / $this->interval);
			if ($count > $this->maxRepetitions) {
				$count = $this->maxRepetitions;
			}

			if (!$count) return array();

			$start = time() - time() % $this->interval;

			$dates = array();

			while ($count) {
				$dates[] = $start - ($this->interval * ($count - 1));
				$count--;
			}

			return $dates;
		}

	}

Usage Examples (moduleExample.hooks.php)

<?php

	new Hook("userInformation", function ($user) {
		global $page;

		if ($user) {
			$oneMinCron = new Cron("addUserEnergy", "user", 60, $this->user->info->US_maxEnergy);    
			$howManyTimesTheCronWouldHaveRun = $oneMinCron->getRepetitionCount();
			$newEnergy = $howManyTimesTheCronWouldHaveRun * 2 + $this->user->info->US_maxEnergy;
			if ($newEnergy > $this->user->info->US_maxEnergy) {
				$newEnergy = $this->user->info->US_maxEnergy;
			}
			$this->user->set("US_maxEnergy", $newEnergy);
			$oneMinCron->updateLastRun();
		}

		$systemCron = new Cron("everyHour", "system", 3600, 24);
		$datesAndTimeThatTheCronWouldHaveRan = $systemCron->getCronDates();
		foreach ($datesAndTimeThatTheCronWouldHaveRan as $date) {
			/* do something here */
		}
		$systemCron->updateLastRun();

	});

 

Documentation

__construct($cronName, $cronType, $interval, $maxRepetitions)

  • $cronName - A unique name given to this cron, this is used to identify it in the database
  • $cronType - This can be "user" or "system"
    • user - This cron will only run when the user is active and playing the game, if they have been AFK for a period of time it will run the cron as many times as needed.(capped by the maxRepetitions)
    • system - This cron is run when there is any activity on the game, it can be a logged in user or someone visiting the login page, if it has not been run for a while it will run as many times as needed (capped by the maxRepetitions)
  • $interval - How often should the cron run (seconds)
  • $maxRepetitions - The maximum times this cron will run

getRepetitionCount()

  • Returns how many times the cron would have ran if it was running away in the background

getCronDates()

  • Same as getRepetitionCount but instead of returning a numerical value it will return an array of dates and times when the cron would have ran.

updateLastRun

  • Updates the database storing when the cron was last run. If the cron was not run 

 

 

 

  • Like 6
Link to comment
Share on other sites

I love the idea of cronless crons, as long as you don't randomly lag a user out for ~30 seconds performing some crazy intense logic as they're the first visitor in a few days. This is a nice simple implementation though! Thanks for sharing. 

I'd still promote deferring heavy, long running operations to an actual crontab.

I remember this whole idea came around partially due to everyone using hosting which didn't support crons lower than 15 minutes, so neat to see it's coming back (shameless plug: even though MWG hosting supports minute crons).

Link to comment
Share on other sites

36 minutes ago, Dave said:

I love the idea of cronless crons, as long as you don't randomly lag a user out for ~30 seconds performing some crazy intense logic as they're the first visitor in a few days. This is a nice simple implementation though! Thanks for sharing. 

Yeah that's why i like to separate user crons and system crons, as well as adding limits to how many times a cron can run. Plus features should be designed in a way so that if a game has no activity for 100 days does it really need to run 100 days worth of crons or can it run the past week.

36 minutes ago, Dave said:

I remember this whole idea came around partially due to everyone using hosting which didn't support crons lower than 15 minutes, so neat to see it's coming back (shameless plug: even though MWG hosting supports minute crons).

Yeah back in the days when people would find an illegal copy of McCodes and run it on a free host 😛

  • Like 1
  • Haha 1
Link to comment
Share on other sites

9 hours ago, KyleMassacre said:

If you were targeting mainly only higher versions of PHP you could provide a callback to you methods you defined to run whatever you wish in that callback x number of times automatically. 

I am already adding the ability for that for when i build it directly into the core engine, it will support both the callback method and the methods above

  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...