I have to implement cron jobs in to my MMO browser game. Its like in Travian game - you build something, and wait few minutes or hours.. But the problem is, that cron job shedulers made to be sheduled every second/minute/day etc.. But in my game i need to shedule thousands of the same jobs, which must run separately and after some unique time to be released. So far i am unlucky with that idea. I am using Node.js and I just tried so many node shedulers, they seem are not supposed to have thousands of jobs.. I can run few unique only. I trough maybe then i can make like - cron job, which runs every 1 second and checks the Redis DB for any finished proccess. But that sounds crazy.. if i have few jobs only and they are in 24 hous only, my server checks that every 1 second.. Any ideas what should i do?
So far, the best option would be to remove cron jobs. I was doing some my processes without cron jobs. But other processes were really hard to calculate, and it was much easier with cron jobs. But i trough deeper and i think it is possible to calculate eveyrthing and simulate everything without cron jobs.
For example, the user has ressources inside Stats model. And i have hourly ressource income there. If the user wants to build anything, for example, traps, i have to recalculate the user ressources and save new value, set the new updatedAt. This is how i am updating my user ressources (inside Stats model), when some user wants to build a trap.
Stats.findOne({user_id: user_id}).exec(function (err, newstats){
var time_in_ms = (new Date()).getTime() - new Date(newstats.updatedAt).getTime();
var ressources_per_second = (newstats.base_income + newstats.field_income) / 60 / 60;
var current_ressources = newstats.resources + parseInt(time_in_ms / 1000 * ressources_per_second) - traps_price;
if(current_ressources < traps_price) return res.ok();
Stats.update(newstats.id, {resources: current_ressources});
});
Answer
Do lazy evaluation of player build progression.
Whenever you need to retrieve the state of a player from your database for any reason, check how old that state is, progress the time which elapsed since the last save, and then put it back into the database.
For example: The current timestamp is 2017-06-15 15:49:23
. You want to know how many windmills player 16433531 has. You retrieve the state from the database and it looks something like this:
{
last_update: "2017-06-15 12:48:03",
windmills: 12,
active_tasks: [
{ task: "build_windmill", time_left: "0:23:12" },
{ task: "recruit_swordman", time_left: "14:48:33" }
]
}
You check the last_update
timestamp and notice that this state is 3 hours, 1 minute and 20 seconds old. So you advance it by 3 hours, 1 minute and 20 seconds by simulating their progress during his timespan. During that time, the windmill they are currently building is finished. So afterwards your state looks like this:
{
last_update: "2017-06-15 15:49:23",
windmills: 13,
active_tasks: [
{ task: "recruit_swordman", time_left: "11:47:13" }
]
}
Use this for whatever you are doing right now and also put it back into the database.
The advantage of this method over regular cronjobs is that you no longer waste time with updating the states of any players which are of no interest for anyone right now. So your total server load only depends on your current active users, not your total registered users. Considering that most online games accumulate a huge amount of dead accounts over their running history, this will really save you a lot of server capacity.
Also, updating longer timespans in bulk is likely faster than doing many frequent updates because they allow you to make a lot of optimizations. When a player has an income of 2 gold per second and you need to process 3000 seconds of game time, then incrementing their gold once by 6000 requires far less CPU time than incrementing it 3000 times by 2. And that doesn't account for the huge overhead the cron job scheduling system will cause for running a job every second.
No comments:
Post a Comment