• We have updated our Community Code of Conduct. Please read through the new rules for the forum that are an integral part of Paradox Interactive’s User Agreement.

TwiddleFactor

Second Lieutenant
38 Badges
Aug 17, 2010
190
92
  • Crusader Kings II: Charlemagne
  • Knights of Honor
  • Sword of the Stars
  • Sengoku
  • Rome Gold
  • Europa Universalis: Rome
  • Europa Universalis IV: Res Publica
  • Heir to the Throne
  • Europa Universalis IV: Call to arms event
  • Europa Universalis IV: Wealth of Nations
  • Europa Universalis IV: Conquest of Paradise
  • Divine Wind
  • Crusader Kings II
  • Crusader Kings II: Legacy of Rome
  • Crusader Kings II: The Old Gods
  • Crusader Kings II: Rajas of India
  • Crusader Kings II: The Republic
  • Crusader Kings II: Sons of Abraham
  • Crusader Kings II: Sword of Islam
  • Europa Universalis III
  • Europa Universalis III Complete
  • Imperator: Rome
  • Europa Universalis IV
  • Imperator: Rome Deluxe Edition
  • Crusader Kings II: Holy Fury
  • Crusader Kings II: Jade Dragon
  • Crusader Kings Complete
  • Imperator: Rome - Magna Graecia
  • Crusader Kings II: Monks and Mystics
  • Crusader Kings II: Reapers Due
  • Crusader Kings II: Conclave
  • Crusader Kings II: Horse Lords
  • Crusader Kings II: Way of Life
  • Mount & Blade: Warband
  • 500k Club
  • Rome: Vae Victis
  • Europa Universalis III Complete
  • Europa Universalis III Complete
Recently I have been thinking about maintenance events and how the various mods handle them. I wanted to discuss an approach I have used in the past as well and hear what others have to say as well.


Maintenance Events

So in case you aren't sure what I am talking about, what I mean by maintenance events is the events that are run periodically to check the status of various aspects of a mod, modify and update variables used by the mod, etc. These kinds of events are often necessary when a mod aims to add new features to CK2.


How Maintenance Events are Usually Handled

The most common way of handling maintenance events - as far as I know - is to use a character that is always hanging around and fire an on_action pulse event (e.g. on_yearly_pulse). In early modding people often used the catholic pope, as there is always a pope no matter what happens and so he can be relied on to always be available for events. The problem with the pope though, is that when he dies in the middle of an event chain, it can cause many issues with the associated events.

To overcome these issues, many mods started using immortal characters that could not interact with the rest of the world. The Isis character in HIP? is an example of this method of handling maintenance events. The character can't interact with anyone in the game, but acts as an immortal titled character always available for maintenance events.


Using Provinces for Maintenance Events

Now with the discussion of how maintenance events are usually handled covered, I would like to briefly touch on how I have handled maintenance events in the modding I have done.

In the most complex mod that I have made, a large number of additional variables are added to every settlement in the game, and I needed to have a maintenance event to update the values yearly. At first I used the player character and a yearly pulse, but that had a host of issues that I am sure other modders have run into before as well.

I also didn't want to go through the trouble of adding an immortal character, and I wanted the event to fire again after exactly 365 days, so in the end I decided to try something new. On the first launch of the game, I used an on_startup event to then launch a recurring event with a delay of 365 days for province 1. Below is a simple outline of what happens:

On Startup : event mod.1 is fired

mod.1's immediate block is

immediate = {
1 = {
province_event = { id = mod.2 }
}
}

mod.2 recalls itself while also firing the actual maintenance event (mod.3(

immediate = {
ROOT = {
repeat_event = { id = mod.2 days = 365 }
province_event = { id = mod.3 }
}
}

The use of the repeat_event command ensures that the scope chain doesn't grow each time the event is run, and I believe that calling the repeat from mod.2 prevents any local variables created in mod.3 from persisting after each loop (I may be wrong about this though).

I have used this method for quite some time in my mod, and haven't noticed any serious issues as of yet. I've run observe games and played for nearly 300 years with this method being used, and haven't noticed any save game bloat or sudden failure of the maintenance event. As far as I can tell it seems to be working well.


Discussion

So, to my fellow modders, what is your opinion on this? Is the use of a province to call a recurring maintenance event safe? Am I being naive and there are some nuances of the way the CK2 engine works that makes this dangerous and unreliable? Or are there other reasons why the use of immortal characters is better for handling maintenance events?

If there are alternative ways of firing a maintenance event that you know of, please mention them as well. I don't know if this has been debated in the past, but I am interested to hear what others think and what they feel is the best way to handle maintenance events.
 
I don't have time to write a super detailed response atm, but I believe your approach is quite sensible and in theory superior than relying on some special character. Provinces are, after all, static and not removable during gameplay.
I've actually used a similar approach for an old personal mod of mine, and it was working fine as well. The only downside to it might be a slight performance loss compared to the character variant, because in order to fire the repeat_event I was told the game engine has to continuously check whether the specified amount of time has passed; so it is checking whether to fire the repeat_event every day. In contrast to that, most character based maintenance events are fired by on_actions which are periodically called by the game engine without separate checks for elapsed time.
However, given that you are using just a single maintenance event set up this way, I think it's safe to say that the performance overhead is minimal and negligible.
What could become another problem is if you fired a bunch of events from your repeating event, because they would all fire on the same day which could cause a perceptible slowdown or hiccup (I had this in my setup in the beginning). There's ofc ways around this by spreading out and delaying called events, but it's not perfect because then you will amplify the first issue (the engine having to check each day).
However, this can also be a benefit because while regular on-action events are spread around the year to minimize lag spikes, the precisely repeating maintenance event allows to actually call events in equally spaced intervals. Which might be desirable if you e.g. always want to call a Christmas celebration on the 24th or 25th, instead of "randomly at some point in November or December".

In conclusion, I think your approach is quite sensible and if well handled, can work just fine for many situations - including any that requires a precisely repeating event that is occurring exactly on the same date each year.
Still, it might be desirably to have Paradox add an official on_action that is fired on a province, which could replace the current vanilla method and would be superior to it in any way. Because well, provinces are truly static and the logical choice (instead of the Pope or some other character).
 
Thank you for the detailed response.

The maintenance event in my mod itself modifies variables in every holding in the game, and as you supposed, this does cause a large lag spike on the day it fires in my case. The spike is mostly caused by a single bottleneck in the update though, so unfortunately separating different parts of the update into different days didn't help.

As an alternative, I was thinking of actually getting the maintenance event to fire on different days for different provinces. I did a proof of concept of this by creating a province event with an MTTH that calls the recurring maintenance event for provinces individually. Once the MTTH event fires once for a given province, it is then prevented from firing again by a flag on the province. The method seemed to work just fine, and reasonably randomized the start days of the recurring event for the different provinces.

At the time though, I didn't know about the performance difference between the on_action events and delayed events. That is very interesting, because with the new method I have 1000 something individual repeat loops (one for every province) waiting to be called. In my test it was playable, but I didn't compare the speed to the old method.

The problem I have with on_yearly_pulse events is that there is too much variability in when they actually fire. If you are trying to update variables periodically, it can be undesirable if the timing is too random. I suppose a solution to this would be to have it fire more frequently but have the variables experience a smaller change, but as far as I know there is no on_monthly_pulse or something similar (correct me if I am wrong).

More on_action events would definitely help as well. I will make the suggestion in the suggestions thread.
 
as far as I know there is no on_monthly_pulse or something similar (correct me if I am wrong).

AFAIK, events that are not is_triggered_only = yes are checked every 20 days for whether or not they trigger, with the chance of triggering at a given check controlled by the MTTH block (and the trigger block, of course). This setting is controlled by the define EVENT_PROCESS_OFFSET. This should come close to your on_monthly_pulse.
 
With the Pope method, if your event needs an event chain with a delay, you can avoid issues with the Pope dying by firing an intermediary event on a province with a delay, then having that immediately call an event for the current Pope. This way, if the Pope dies, the second part will just fire your the new Pope.
Of course, you will have to design your event to make sure you aren't using the Pope to store any variables or flags, since they will get lost if he dies.

This method would also work for Ancestor Veneraton to allow religious heads to become venerated ancestors, since it results in the event firing on their successor instead of their corpse.
 
AFAIK, events that are not is_triggered_only = yes are checked every 20 days for whether or not they trigger, with the chance of triggering at a given check controlled by the MTTH block (and the trigger block, of course). This setting is controlled by the define EVENT_PROCESS_OFFSET. This should come close to your on_monthly_pulse.

Are MTTH events really only checked eery 20 days? I thought they were checked every day and therefore it was best to avoid their use for performance reasons. I suppose it might not be any different from using the recurring event if those are also checked every day. With the MTTH, it would have to evaluate the probability every time it runs though, so that would add more overhead than just checking the days elapsed.

With the Pope method, if your event needs an event chain with a delay, you can avoid issues with the Pope dying by firing an intermediary event on a province with a delay, then having that immediately call an event for the current Pope. This way, if the Pope dies, the second part will just fire your the new Pope.
Of course, you will have to design your event to make sure you aren't using the Pope to store any variables or flags, since they will get lost if he dies.

This method would also work for Ancestor Veneraton to allow religious heads to become venerated ancestors, since it results in the event firing on their successor instead of their corpse.

That is definitely true, but it still has the same lag spike issue when being used for maintenance events that fire in every province.
 
Are MTTH events really only checked eery 20 days? I thought they were checked every day and therefore it was best to avoid their use for performance reasons. I suppose it might not be any different from using the recurring event if those are also checked every day. With the MTTH, it would have to evaluate the probability every time it runs though, so that would add more overhead than just checking the days elapsed.

Pretty sure they're checked every 20 days, or whatever the define is set to (e.g. I know some "performance" mods increase that number, so MTTH events are checked less often when running them). You should try to avoid their use if your MTTH is much greater than 20 days because you don't usually gain much from checking once every 20 days instead of once at a random point per year compared to the performance loss (18 checks per year for MTTH vs. 1 check per year for on_yearly_pulse). The ability to group random events into blocks within on_yearly_pulse also helps avoid situations where multiple, unrelated, random events fire at the same time. However, if you really do want your events to be able to happen multiple times per year akin to an "on_monthly_pulse" sort of thing, MTTH events are kind of your only option besides manually firing events.
 
One alternative to using MTTH for a monthly pulse is to create a yearly pulse event that fires 12 events, each at a one month interval. The date of the yearly event can drift a bit, so you may want to queue up about 13 events, then have the yearly event cancel those queued events before queuing up new ones. This should be both less performance intensive and more reliable than MTTH.
 
I think this talk of MTTH and on_actions is slightly missing the point.
The whole point (imo) is: Could you use the old "Pope system"? And if not - why do you want to change it?

There's a number of answers to that last question, including these two:
1) You want to be save from bugs that might arise from the Pope dying unexpectedly or disappearing, causing problems.
In that case, you could switch to using MTTH events, or different on_actions, or offmap powers, or whatever.​
2) You want to have the maintenance events spread out in exactly equal intervals, without random variations.
In this case, you would be best advised to use the OP post's idea of creating a chain of repeating province events. Because every other solution, be it MTTH or on-action, is inherently variable and event call times slightly randomized.​

I also want to note that there's probably a reason why MTTH has been mostly phased out from usage in vanilla. Afaik it is inherently less efficient compared to on_actions, and thus likely not a good alternative performance-wise compared to using repeating events.
And as for repeating province events, one last note is that instead of using repeating events on every province, you could use just one that is repeating, and scope to any_province from that event when the time comes. This might produce a more pronounced short lag spike at the benefit of a smoother gameplay for the rest of the year - but it would have to be tested to be sure.

But still the discussion itself is quite a good one, as it shows up the multitude of options that are available to us. Different situations might just require different approaches.