• 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.

Stellaris Dev Diary #232 - 3.2 Balance Changes and Performance Improvements

Hello everyone, today we would like to tease you with some of the upcoming changes coming with the 3.2 "Herbert" patch, named after Sci-Fi author Frank Herbert, which we will release along with the Aquatic Species Pack.

For Balance Changes, we have the following changes in store for you.
  • Functional Architecture and Constructobot: Reduced the free building slots granted from 2 to 1.
  • Agrarian Idyll empires now get one planet building slot per four Agricultural districts built.
  • Reduced the ship upkeep cost modifier for clone army admirals to 5/10/20% based on their decisions.
  • Ruins of Shallash arc site no longer has a chance of giving quite as much unity as defeating an endgame crisis.
  • You can no longer use planet killer weapons on primitives inside your borders if you lack the appropriate primitive interference policies.
  • Pops working the Livestock job now have 10% less political power.
  • A lot of anomalies were rewarding 3 Society Research deposits, there is now more variety.
  • Made Awakened Fallen Empires use Traditions (but not Ascension Perks).
  • Several productivity-improving technologies are now no longer of dubious benefit, as their upkeep (and production) effects now only apply to jobs actually primarily producing resources.
  • Nerve Stapled Hivemind pops can no longer perform complex drone jobs.
  • Reduced the amount of jobs added by Leisure Arcology Districts to bring them into line with other Ecumenopolis districts.
  • Ion cannons are no longer free to maintain, and have an upkeep cost of 8 energy.
  • Necro-Hives:
    • Cut Necrophage pop assembly penalty to 50% from 75%
    • Made pop output modifiers (positive and negative) no longer apply to hive minds.
    • Made the -50% organic upkeep also apply to energy, for photosynthesis.
    • Devouring Swarm Necrophages now spawn with extra infrastructure to account for the lack of chambers of elevation.
Of note here is the Functional Architecture change, we are aware that the extra building slot was the main draw of the civic but it was also way over-represented even after the initial release-hype

While not a pre-planned balance pass like we did for the Lem patch, we still found a few places to tweak and adjust and we will continue to do that in future patches.

...and now, handing over to Caligula Caesar for a look at some performance improvements and moddability topic.

A Look at Script Performance
Hi! You are probably used to me writing lengthy prose about new moddability and scripting language features. This time, we only have a few things to show off in that regard, but there are nevertheless some cool, technical things I can speak about.

Knowing the script language pretty well, I always found the performance impacts of our scripts to be a big unknown to me. Was what I was adding going to mess with performance? Well, I could do plenty of guessing as to how to script most efficiently, and general concepts of programming such as early outs do apply. But how big was the difference? And how much can we save by identifying inefficient scripts and improving them?

Moah had made some progress on porting the EU4 script profiler over to Stellaris as a pet project some time ago. The only problem was, its information was quite incomplete (since it needs a lot of tags added in many places of the code, basically everywhere where an effect or trigger is called). It was also pretty hard to read the information presented. But now, with the Custodians initiative, the time had come to see what we could do with this.

After a bit of (very tedious) work to make the information all-encompassing, systematic and readable, I let the game run on a Huge galaxy with a few extra boosts to the AI - 0.75 research costs, 1.25 habitable planets - and ran it a year with the script profiler enabled. Then, issues could be found. I’ve attached two versions of this output: one as it was in one of the early runs - so before coverage was comprehensive (notably, triggered modifiers and economic tables are missing), but also before any optimisation work was done - and one as it is now, in the 3.2 beta. (Note that the figures for how long it spent on each object is massively inflated by my having run the game in unoptimised debug mode with the profiler turned on)

Now, I must state in advance that we aren’t able to release the script profiler to the public with the 3.2 update for technical reasons: running the game with it makes the game about 50% slower, so we need to work out a way to be able to turn it - and its full performance impact - on and off at will. (At the moment, it is hidden behind compiler flags that are not available to the public). But we definitely hope that we’ll be able to release it to modders in the future.

Early Gains
The first big finding was that the game is repeatedly recalculating certain game rules a large number of times per pop each day, which was having a disproportionate impact on performance. The biggest culprit was “can_vote_in_democratic_election”, which it turned out was checked on every pop in the country every day for each pop faction while they were calculating their support value. Yes, you are reading this right: the imperialist faction would check whether each pop in the entire country was allowed to vote, then the prosperity faction would do the same, and the imperialist one, and so on… These cases were fixed by making use of daily caching: the pops will now calculate the result once per day (or, in the case of species_has_happiness, once per species in a country each day), and other places in the code can simply refer back to that result. Furthermore, pop factions’ support calculations were optimised so that the total by which they were dividing their support could be calculated once per country, rather than once per faction.

On the script side, by parsing various of the top hits, we noticed a few easily-optimised bits of script. First off, graygoo.555 was trying to fire a surprising number of times for an event that should come into play only when the Gray Goo are active (which they weren’t). It turns out that this was because it was missing “is_triggered_only”, so it was trying to fire on all planets every day! Similarly, a number of test events were scripted in a similar way, but with “always = no” as their trigger so they’d never fire. They made a small but nevertheless noticeable impact on performance, so they had to go.

The opinion modifier triggered_opinion_galactic_community_in_breach was taking up more performance than any other opinion modifier, by a distance, which seemed a bit strange. It turned out this could be fixed by a slight change in order in the triggers: it was checking “is_in_breach_of_any” before verifying Galcom membership - which sounds like it wouldn’t be a big issue, but that trigger then checks the triggers for the breach conditions of all passed resolutions, so it is in effect a lot of triggers in one. Simply swapping the order had very positive results, here.

Finally, the event crime.1 (somehow the second most costly event in the early version) was a similar case, but a lot more complicated. The main problem here was the following piece of script:

Code:
OR = {
            AND = {
                count_owned_pop = {
                    limit = {
                        is_shackled_robot = no
                        is_unemployed = yes
                        NOR = {
                            has_living_standard = { type = living_standard_utopian }
                            has_living_standard = { type = living_standard_good }
                            has_living_standard = { type = living_standard_shared_burden }
                        }
                    }
                    count > 3
                }
                owner = { is_gestalt = no }
            }
            AND = {
                count_owned_pop = {
                    limit = {
                        is_unemployed = yes
                        NOT = { has_living_standard = { type = living_standard_organic_trophy } }
                    }
                    count > 10
                }
                owner = { is_gestalt = yes }
            }
        }
This is quite inefficient, and large benefits could be found in applying the principle of early outs. “Count_owned_pop” is a relatively expensive way of calculating anything, because a lot of efficiency is lost in converting script into code and working out the results of this, so on a planet with 80 pops, it is looping through each of those and checking a set of triggers on each of those. Unfortunately, because of the ordering, it would do this twice per day on each planet which did not have 3 unemployed pops on it:
  • The event is checking the triggers every day on each inhabited planet. Or at least often. 44,000 times in a year, to be exact.
  • It does not verify that there are unemployed pops on the planet before working out what kinds of unemployed pops there are. Which means that the OR will return false on both count_owned_pop sections, which consequently means it is checking both. Adding “num_unemployed > 3” near the start had big benefits
  • It would check the number of unemployed pops relevant to non-gestalts and only after that check the country was not gestalt. By swapping the gestalt check to the start, it means it will only ever be trying one of the count_owned_pop loops.
A new, more efficient version of the trigger was therefore this:

Code:
num_unemployed > 3 #early out before the expensive count_owned_pop to come
        OR = {
            AND = {
                owner = { is_gestalt = no }
                count_owned_pop = {
                    limit = {
                        is_unemployed = yes
                        is_shackled_robot = no
                        NOR = {
                            has_living_standard = { type = living_standard_utopian }
                            has_living_standard = { type = living_standard_good }
                            has_living_standard = { type = living_standard_shared_burden }
 
                        }
                    }
                    count > 3
                }
            }
            AND = {
                owner = { is_gestalt = yes }
                count_owned_pop = {
                    limit = {
                        is_unemployed = yes
                        NOT = { has_living_standard = { type = living_standard_organic_trophy } }
                    }
                    count > 10
                }
            }
        }

Gains from Further Analysis
This was some cool stuff to fix, but beyond this, simply looking at the list became a bit harder to yield significant savings. Enter spreadsheeting! We pasted the results into a spreadsheet and, a few formulas later, and a nice pivot table to give us some breakdowns along the lines of “what is the total impact of all jobs”, or “what is the impact of the potential trigger of pop factions”

script profiler 1.png

Picture shows values after performance improvements

This allowed us to pinpoint a few more things. Firstly, ai_resource_production was causing an absurdly high performance cost from a rather small number of hits. The culprit, here, turned out to be that the “planet_resource_compare” trigger (used mainly here) was incredibly expensive. The problem was that it was recalculating the resource output of all resources on the planet, basically (including by seeing what each pop was producing!). It turned out to be possible to mitigate this somewhat (to about 75%) by making it selectively recalculate the production of the relevant resource, but this was still quite expensive for a trigger, so we also cut down on its use a bit. I suggest modders not overuse it either.

Another thing we saw was that, not unexpectedly, jobs were quite expensive. Specifically their weights and their “possible” checks. We have some ideas to save time on the weights that we aren’t ready to speak about yet (they emerged too late in 3.2 development to be considered for the patch, because they are relatively likely to need some iteration), but we found a way of making the “possible” triggers cheaper. Basically, every seven days, a pop would recalculate its job cache, at which point it will check whether it is allowed to work each job, and if so, calculate its weight. But most jobs have fairly standard “possible” triggers that check the first part - specifically, there is a shared set of triggers between, respectively, worker, specialist, ruler and drone jobs. It turned out that very significant improvements (to the degree of almost two thirds) were possible by having the pop calculate these four triggers first, then loop through the jobs and simply match the result to the right job.

(Note to modders: the format looks a bit different now. If you used the scripted triggers worker/specialist/complex_specialist/ruler/drone_job_check_trigger, you will now need to define e.g. “possible_precalc = can_fill_ruler_job”. And if you changed them, you will need to change the new versions of them in game_rules)

Finally, although the game rules optimisations had already fixed several performance issues with pop factions, there were a few more spots where they could be improved. The first was whether a faction should exist at all: it turned out that both the script and the code was checking whether there were 5 pops that could join the faction, just that the code wasn’t checking this anymore after the faction was formed. Obviously, this wasn’t ideal, so the script check (being the slower) was removed, and the code check amended to account for shrinking pop factions becoming invalid.

The second was deciding whether a pop should belong to a faction: even though almost all factions only allow pops matching their ethos, the filter by ethos is quite late. By putting it much earlier - in code, before the script is even checked at all (with an override in case this isn’t desired, e.g. for technologist robots) - this massively cut down the costs of this particular calculation.

Finally, a number of their demands - checked each day per faction - were quite exorbitant. By changing the ordering and using equivalent but cheaper checks (e.g. any_owned_species instead of any_owned_pop). This, too, had a significant impact, so that the script footprint of pop factions (excluding game rules they use) was reduced by about two thirds.

Further Performance Topics
It is my hope that this work will be felt in the form of a bit less late game slowdown. My tests would indicate that this was a success, though it’s very hard to quantify by how much. It was however work that was solely focused on the script performance footprint, so there’s plenty of other things for us to look at! The job is never over, when it comes to performance, and hopefully we’ll have time to make further improvements for 3.3.

For example, I have heard a few complaints about UI lag in the late game, which might be improved slightly in a few interfaces as a result of the performance work, but this work didn’t focus on UIs. It is certainly true that some of them are not as fast as we would like them to be. Particular ones in this regard are the planet view, the species view and the colonisation selection menu, and we are looking at options to speed them up. (And, indeed, if anyone can think of any others, it would be useful for you to point them out!)

Moddability Improvements
I can’t really do a dev diary without talking about a few moddability improvements, so here they are. As I said, we don’t have that much this time, but there’s a few things that people might enjoy trying out:
  • There’s now a create_nebula effect. Although it’s best used during galaxy generation, since the visual effects on the galaxy map won’t refresh during the game.
  • Decisions can now have on_queued and on_unqueued effects.
  • Terraforming now uses the Megacorp economy system. Meaning, the costs are configurable resource tables, and you can make economic_unit modifiers to apply to them.
  • There’s a species_gender trigger that checks what gender the species’ leaders can be
  • You can now define custom tooltips for systems that you see when you mouse over them on the galactic map
  • There’s now on_actions for on_capital_changed and on_planet_class_changed
  • For Traditions, the “possible” trigger of its adopt tradition will now show in the tooltip for adopting it if it fails. I’m told that you can also now make the tradition categories have a container where you add a gridbox.

There’s also a couple of things that modders will have to update (aside from the terraforming, as mentioned):
  • any/count/every/random_planet_army now refers to all armies on the planet, not just all owned by the owner of the planet
  • Set_starbase_building/module and the remove variants are now consistent in starting at slot 1, rather than “set” starting at 0 and “remove” at 1.
  • In component_templates, valid_for_country is now a trigger rather than a weights field
  • Fire_on_action had some issues where you defined scopes with “prev” and “from”, those no longer exist.
As a final moddability note, for anyone who misses the meaty dev diaries with far-reaching moddability changes, not to worry! Anyone that has played around with the script of our newer games will know that there’s a lot more potential in our scripting language. There’s some cool stuff in the works, though I can’t at this stage say what exactly or in which patch it’ll be.

I am also, as last time, attaching the script docs to the dev diary, so that you can see any changes I forgot to mention. Also, any modders who are interested in early access to the 3.2 Update, for the purposes of getting your mods updated, you can sign up here: https://pdxint.at/3bZbVJN
 

Attachments

  • profiling_summary after.log
    695,8 KB · Views: 0
  • profiling_summary before.log
    465,4 KB · Views: 0
  • effects.log
    181 KB · Views: 0
  • localizations.log
    4,4 KB · Views: 0
  • modifiers.log
    332,5 KB · Views: 0
  • scopes.log
    8,1 KB · Views: 0
  • triggers.log
    128,8 KB · Views: 0
Last edited by a moderator:
  • 95Like
  • 25Love
  • 25
  • 4
  • 2
Reactions:
I've worked with reinforcements quite a bit for this patch with the help of a few other custodians.
The goal has been to not only fix the recent issues with it, but also to improve the feature overall to ensure that it simulates the process it automates well enough that players don't feel that constructing reinforcements manually is worth it.

That being said, there will still be cases where the ships are spawned at the shipyard, but they should be less common and when they do occurr, they should cause less frustration. The reason why we can't eliminate this completely is because there needs to be a safe path for the consutructed fleet to get to the one requesting reinforcements, or else it would be exploitable and it wouldn't simulate the manual reinforcement process. A lot can happen between the time when you order the reinforcement and the time when they finish construction and attempt to make their way to the target fleet and if it's no longer possible to get to that fleet safely, we will spawn the ship at the shipyard and leave the problem of getting the ship to the target fleet in the hands of the player.

I'm glad to hear it's being worked on. If the issue with the new ships dividing into new fleets of one ship each is cause the target fleets unable to be reached safely, wouldn't it be an option to let the player delay the ship's arriving until the fleet is in a safe location, or allow them to manually tell the game to spawn the ships at the shipyard if they're desperate for firepower.

Although to be honest, for me it's more of an issue cause of the UI and the annoyance of having to delete each ship/fleet one at a time. if you just had all ships that do this group together into one ad-hoc fleet formation, that'd solve a good 75% of my problems.
 
  • 1Like
Reactions:
The reason why we can't eliminate this completely is because there needs to be a safe path for the consutructed fleet to get to the one requesting reinforcements, or else it would be exploitable and it wouldn't simulate the manual reinforcement process. A lot can happen between the time when you order the reinforcement and the time when they finish construction and attempt to make their way to the target fleet and if it's no longer possible to get to that fleet safely, we will spawn the ship at the shipyard and leave the problem of getting the ship to the target fleet in the hands of the player.

How about we ignore all that and simply make the proccess convenient and functional?

Stellaris warfare is enough of a chore without having a random enemy corvette disrupt hundreds of naval cap worth of reinforcements. This is exactle the issue why people think manual reinforcements are worth it.
 
  • 1Like
Reactions:
Ive experienced an issue with my low end gpu where character portraits slow down the game alot alot. Ive reported it and others have too i think. Any plants to fix this? if its not reported i can make one.
 
Hello everyone, today we would like to tease you with some of the upcoming changes coming with the 3.2 "Herbert" patch, named after Sci-Fi author Frank Herbert, which we will release along with the Aquatic Species Pack.

For Balance Changes, we have the following changes in store for you.
  • Functional Architecture and Constructobot: Reduced the free building slots granted from 2 to 1.
  • Agrarian Idyll empires now get one planet building slot per four Agricultural districts built.
  • Reduced the ship upkeep cost modifier for clone army admirals to 5/10/20% based on their decisions.
  • Ruins of Shallash arc site no longer has a chance of giving quite as much unity as defeating an endgame crisis.
  • You can no longer use planet killer weapons on primitives inside your borders if you lack the appropriate primitive interference policies.
  • Pops working the Livestock job now have 10% less political power.
  • A lot of anomalies were rewarding 3 Society Research deposits, there is now more variety.
  • Made Awakened Fallen Empires use Traditions (but not Ascension Perks).
  • Several productivity-improving technologies are now no longer of dubious benefit, as their upkeep (and production) effects now only apply to jobs actually primarily producing resources.
  • Nerve Stapled Hivemind pops can no longer perform complex drone jobs.
  • Reduced the amount of jobs added by Leisure Arcology Districts to bring them into line with other Ecumenopolis districts.
  • Ion cannons are no longer free to maintain, and have an upkeep cost of 8 energy.
  • Necro-Hives:
    • Cut Necrophage pop assembly penalty to 50% from 75%
    • Made pop output modifiers (positive and negative) no longer apply to hive minds.
    • Made the -50% organic upkeep also apply to energy, for photosynthesis.
    • Devouring Swarm Necrophages now spawn with extra infrastructure to account for the lack of chambers of elevation.
Of note here is the Functional Architecture change, we are aware that the extra building slot was the main draw of the civic but it was also way over-represented even after the initial release-hype

While not a pre-planned balance pass like we did for the Lem patch, we still found a few places to tweak and adjust and we will continue to do that in future patches.

...and now, handing over to Caligula Caesar for a look at some performance improvements and moddability topic.

A Look at Script Performance
Hi! You are probably used to me writing lengthy prose about new moddability and scripting language features. This time, we only have a few things to show off in that regard, but there are nevertheless some cool, technical things I can speak about.

Knowing the script language pretty well, I always found the performance impacts of our scripts to be a big unknown to me. Was what I was adding going to mess with performance? Well, I could do plenty of guessing as to how to script most efficiently, and general concepts of programming such as early outs do apply. But how big was the difference? And how much can we save by identifying inefficient scripts and improving them?

Moah had made some progress on porting the EU4 script profiler over to Stellaris as a pet project some time ago. The only problem was, its information was quite incomplete (since it needs a lot of tags added in many places of the code, basically everywhere where an effect or trigger is called). It was also pretty hard to read the information presented. But now, with the Custodians initiative, the time had come to see what we could do with this.

After a bit of (very tedious) work to make the information all-encompassing, systematic and readable, I let the game run on a Huge galaxy with a few extra boosts to the AI - 0.75 research costs, 1.25 habitable planets - and ran it a year with the script profiler enabled. Then, issues could be found. I’ve attached two versions of this output: one as it was in one of the early runs - so before coverage was comprehensive (notably, triggered modifiers and economic tables are missing), but also before any optimisation work was done - and one as it is now, in the 3.2 beta. (Note that the figures for how long it spent on each object is massively inflated by my having run the game in unoptimised debug mode with the profiler turned on)

Now, I must state in advance that we aren’t able to release the script profiler to the public with the 3.2 update for technical reasons: running the game with it makes the game about 50% slower, so we need to work out a way to be able to turn it - and its full performance impact - on and off at will. (At the moment, it is hidden behind compiler flags that are not available to the public). But we definitely hope that we’ll be able to release it to modders in the future.

Early Gains
The first big finding was that the game is repeatedly recalculating certain game rules a large number of times per pop each day, which was having a disproportionate impact on performance. The biggest culprit was “can_vote_in_democratic_election”, which it turned out was checked on every pop in the country every day for each pop faction while they were calculating their support value. Yes, you are reading this right: the imperialist faction would check whether each pop in the entire country was allowed to vote, then the prosperity faction would do the same, and the imperialist one, and so on… These cases were fixed by making use of daily caching: the pops will now calculate the result once per day (or, in the case of species_has_happiness, once per species in a country each day), and other places in the code can simply refer back to that result. Furthermore, pop factions’ support calculations were optimised so that the total by which they were dividing their support could be calculated once per country, rather than once per faction.

On the script side, by parsing various of the top hits, we noticed a few easily-optimised bits of script. First off, graygoo.555 was trying to fire a surprising number of times for an event that should come into play only when the Gray Goo are active (which they weren’t). It turns out that this was because it was missing “is_triggered_only”, so it was trying to fire on all planets every day! Similarly, a number of test events were scripted in a similar way, but with “always = no” as their trigger so they’d never fire. They made a small but nevertheless noticeable impact on performance, so they had to go.

The opinion modifier triggered_opinion_galactic_community_in_breach was taking up more performance than any other opinion modifier, by a distance, which seemed a bit strange. It turned out this could be fixed by a slight change in order in the triggers: it was checking “is_in_breach_of_any” before verifying Galcom membership - which sounds like it wouldn’t be a big issue, but that trigger then checks the triggers for the breach conditions of all passed resolutions, so it is in effect a lot of triggers in one. Simply swapping the order had very positive results, here.

Finally, the event crime.1 (somehow the second most costly event in the early version) was a similar case, but a lot more complicated. The main problem here was the following piece of script:

Code:
OR = {
            AND = {
                count_owned_pop = {
                    limit = {
                        is_shackled_robot = no
                        is_unemployed = yes
                        NOR = {
                            has_living_standard = { type = living_standard_utopian }
                            has_living_standard = { type = living_standard_good }
                            has_living_standard = { type = living_standard_shared_burden }
                        }
                    }
                    count > 3
                }
                owner = { is_gestalt = no }
            }
            AND = {
                count_owned_pop = {
                    limit = {
                        is_unemployed = yes
                        NOT = { has_living_standard = { type = living_standard_organic_trophy } }
                    }
                    count > 10
                }
                owner = { is_gestalt = yes }
            }
        }
This is quite inefficient, and large benefits could be found in applying the principle of early outs. “Count_owned_pop” is a relatively expensive way of calculating anything, because a lot of efficiency is lost in converting script into code and working out the results of this, so on a planet with 80 pops, it is looping through each of those and checking a set of triggers on each of those. Unfortunately, because of the ordering, it would do this twice per day on each planet which did not have 3 unemployed pops on it:
  • The event is checking the triggers every day on each inhabited planet. Or at least often. 44,000 times in a year, to be exact.
  • It does not verify that there are unemployed pops on the planet before working out what kinds of unemployed pops there are. Which means that the OR will return false on both count_owned_pop sections, which consequently means it is checking both. Adding “num_unemployed > 3” near the start had big benefits
  • It would check the number of unemployed pops relevant to non-gestalts and only after that check the country was not gestalt. By swapping the gestalt check to the start, it means it will only ever be trying one of the count_owned_pop loops.
A new, more efficient version of the trigger was therefore this:

Code:
num_unemployed > 3 #early out before the expensive count_owned_pop to come
        OR = {
            AND = {
                owner = { is_gestalt = no }
                count_owned_pop = {
                    limit = {
                        is_unemployed = yes
                        is_shackled_robot = no
                        NOR = {
                            has_living_standard = { type = living_standard_utopian }
                            has_living_standard = { type = living_standard_good }
                            has_living_standard = { type = living_standard_shared_burden }
 
                        }
                    }
                    count > 3
                }
            }
            AND = {
                owner = { is_gestalt = yes }
                count_owned_pop = {
                    limit = {
                        is_unemployed = yes
                        NOT = { has_living_standard = { type = living_standard_organic_trophy } }
                    }
                    count > 10
                }
            }
        }

Gains from Further Analysis
This was some cool stuff to fix, but beyond this, simply looking at the list became a bit harder to yield significant savings. Enter spreadsheeting! We pasted the results into a spreadsheet and, a few formulas later, and a nice pivot table to give us some breakdowns along the lines of “what is the total impact of all jobs”, or “what is the impact of the potential trigger of pop factions”

View attachment 772431
Picture shows values after performance improvements

This allowed us to pinpoint a few more things. Firstly, ai_resource_production was causing an absurdly high performance cost from a rather small number of hits. The culprit, here, turned out to be that the “planet_resource_compare” trigger (used mainly here) was incredibly expensive. The problem was that it was recalculating the resource output of all resources on the planet, basically (including by seeing what each pop was producing!). It turned out to be possible to mitigate this somewhat (to about 75%) by making it selectively recalculate the production of the relevant resource, but this was still quite expensive for a trigger, so we also cut down on its use a bit. I suggest modders not overuse it either.

Another thing we saw was that, not unexpectedly, jobs were quite expensive. Specifically their weights and their “possible” checks. We have some ideas to save time on the weights that we aren’t ready to speak about yet (they emerged too late in 3.2 development to be considered for the patch, because they are relatively likely to need some iteration), but we found a way of making the “possible” triggers cheaper. Basically, every seven days, a pop would recalculate its job cache, at which point it will check whether it is allowed to work each job, and if so, calculate its weight. But most jobs have fairly standard “possible” triggers that check the first part - specifically, there is a shared set of triggers between, respectively, worker, specialist, ruler and drone jobs. It turned out that very significant improvements (to the degree of almost two thirds) were possible by having the pop calculate these four triggers first, then loop through the jobs and simply match the result to the right job.

(Note to modders: the format looks a bit different now. If you used the scripted triggers worker/specialist/complex_specialist/ruler/drone_job_check_trigger, you will now need to define e.g. “possible_precalc = can_fill_ruler_job”. And if you changed them, you will need to change the new versions of them in game_rules)

Finally, although the game rules optimisations had already fixed several performance issues with pop factions, there were a few more spots where they could be improved. The first was whether a faction should exist at all: it turned out that both the script and the code was checking whether there were 5 pops that could join the faction, just that the code wasn’t checking this anymore after the faction was formed. Obviously, this wasn’t ideal, so the script check (being the slower) was removed, and the code check amended to account for shrinking pop factions becoming invalid.

The second was deciding whether a pop should belong to a faction: even though almost all factions only allow pops matching their ethos, the filter by ethos is quite late. By putting it much earlier - in code, before the script is even checked at all (with an override in case this isn’t desired, e.g. for technologist robots) - this massively cut down the costs of this particular calculation.

Finally, a number of their demands - checked each day per faction - were quite exorbitant. By changing the ordering and using equivalent but cheaper checks (e.g. any_owned_species instead of any_owned_pop). This, too, had a significant impact, so that the script footprint of pop factions (excluding game rules they use) was reduced by about two thirds.

Further Performance Topics
It is my hope that this work will be felt in the form of a bit less late game slowdown. My tests would indicate that this was a success, though it’s very hard to quantify by how much. It was however work that was solely focused on the script performance footprint, so there’s plenty of other things for us to look at! The job is never over, when it comes to performance, and hopefully we’ll have time to make further improvements for 3.3.

For example, I have heard a few complaints about UI lag in the late game, which might be improved slightly in a few interfaces as a result of the performance work, but this work didn’t focus on UIs. It is certainly true that some of them are not as fast as we would like them to be. Particular ones in this regard are the planet view, the species view and the colonisation selection menu, and we are looking at options to speed them up. (And, indeed, if anyone can think of any others, it would be useful for you to point them out!)

Moddability Improvements
I can’t really do a dev diary without talking about a few moddability improvements, so here they are. As I said, we don’t have that much this time, but there’s a few things that people might enjoy trying out:
  • There’s now a create_nebula effect. Although it’s best used during galaxy generation, since the visual effects on the galaxy map won’t refresh during the game.
  • Decisions can now have on_queued and on_unqueued effects.
  • Terraforming now uses the Megacorp economy system. Meaning, the costs are configurable resource tables, and you can make economic_unit modifiers to apply to them.
  • There’s a species_gender trigger that checks what gender the species’ leaders can be
  • You can now define custom tooltips for systems that you see when you mouse over them on the galactic map
  • There’s now on_actions for on_capital_changed and on_planet_class_changed
  • For Traditions, the “possible” trigger of its adopt tradition will now show in the tooltip for adopting it if it fails. I’m told that you can also now make the tradition categories have a container where you add a gridbox.

There’s also a couple of things that modders will have to update (aside from the terraforming, as mentioned):
  • any/count/every/random_planet_army now refers to all armies on the planet, not just all owned by the owner of the planet
  • Set_starbase_building/module and the remove variants are now consistent in starting at slot 1, rather than “set” starting at 0 and “remove” at 1.
  • In component_templates, valid_for_country is now a trigger rather than a weights field
  • Fire_on_action had some issues where you defined scopes with “prev” and “from”, those no longer exist.
As a final moddability note, for anyone who misses the meaty dev diaries with far-reaching moddability changes, not to worry! Anyone that has played around with the script of our newer games will know that there’s a lot more potential in our scripting language. There’s some cool stuff in the works, though I can’t at this stage say what exactly or in which patch it’ll be.

I am also, as last time, attaching the script docs to the dev diary, so that you can see any changes I forgot to mention. Also, any modders who are interested in early access to the 3.2 Update, for the purposes of getting your mods updated, you can sign up here: https://pdxint.at/3bZbVJN
What is the total number of biotrophies you can put in leisure districts now on say a size 18 ecu? How many trophy jobs are being lost by this change? I didn't even try relic servitors yet cause I was busy trying all the plantoid and humanoid additions, and it's already nerfed :(
 
Yes, you are reading this right: the imperialist faction would check whether each pop in the entire country was allowed to vote, then the prosperity faction would do the same, and the imperialist one, and so on…
Reminds me of that time Crusader Kings was choking because the game kept checking every single living character to see if they could be castrated.
 
Last edited:
  • 8Haha
  • 2Like
Reactions:
Hoping that one aspect to the ship reinforcement fix. Is that if the ships can't safely get to the main fleet, the will at least form up into a single fleet to deal with for later. This also means if they arrived from multiple shipyards, the game will have the ones from the furthest shipyards travel to the closest shipyard of the targeted fleet.
 
What is the total number of biotrophies you can put in leisure districts now on say a size 18 ecu? How many trophy jobs are being lost by this change? I didn't even try relic servitors yet cause I was busy trying all the plantoid and humanoid additions, and it's already nerfed :(
To be fair, it says Leisure districts, and doesn't mention Rogue Servitors or Bio-Trophies, so it could be that the Sanctuary archologies remain unchanged and still add +15 Bio-Trophies each

Still, a clarification would be nice.

(Also, reupping my earlier suggestion to make Servitors capable of pacifying Amoebas and buffing Habitat Sanctuary districts from +5 to +10 Bio-Trophies)
 
Last edited:
  • 3
  • 2Like
  • 2Love
Reactions:
To be fair, it says Leisure districts, and doesn't mention Rogue Servitors or Bio-Trophies, so it could be that the Sanctuary archologies remain unchanged and still add +30 Bio-Trophies each

Still, a clarification would be nice.

(Also, reupping my earlier suggestion to make Servitors capable of pacifying Amoebas and buffing Habitat Sanctuary districts from +5 to +10 Bio-Trophies)
true! thanks for that haha, i got confused for some reason
 
I'm glad to hear it's being worked on. If the issue with the new ships dividing into new fleets of one ship each is cause the target fleets unable to be reached safely, wouldn't it be an option to let the player delay the ship's arriving until the fleet is in a safe location, or allow them to manually tell the game to spawn the ships at the shipyard if they're desperate for firepower.

Although to be honest, for me it's more of an issue cause of the UI and the annoyance of having to delete each ship/fleet one at a time. if you just had all ships that do this group together into one ad-hoc fleet formation, that'd solve a good 75% of my problems.
That's an interesting suggestion, but I see a few issues with a solution like that. The first one would be that players wouldn't necessarily understand why the reinforcements aren't arriving and that we would need to add even more notifications.

It also opens up for some baiting exploits where you can have a weak fleet baiting a hostile AI or player that could instantly get very big and turn the table fight around because they had deliberately let a big amount of ships hover around in MIA waiting for a clear path. You could instead go for not starting the MIA timer until there is a clear path, but then you would be overriding player control, as if they had manual control, they could move the fleets towards each other and merge the fleets faster than the automatic reinforcements could.

I think what we have gone for now should solve the problem well enough as only truly unreachable fleets should have their reinforcements spawn at the shipyard.
Be sure to let us know if you don't agree after you've had a chance to play with it though!
 
  • 6
  • 1Like
Reactions:
How about we ignore all that and simply make the proccess convenient and functional?

Stellaris warfare is enough of a chore without having a random enemy corvette disrupt hundreds of naval cap worth of reinforcements. This is exactle the issue why people think manual reinforcements are worth it.
We did consider this. It is indeed a balance between accuracy & fairness vs convenience.

We felt that just allowing the reinforcement process to cheat would be the "easy way out" here and we would rather first attempt to make it work as best as we can without cheating and if players still feel it's not good enough, we will again consider dropping the conditions of a safe path in favour of making reinforcements feel better for the player.

The specific case you describe should be handled by the changes in 3.2.
If you still feel that it doesn't work well enough, be sure to let us know after you've tried it out :)
 
  • 6
  • 4Like
Reactions:
I've worked with reinforcements quite a bit for this patch with the help of a few other custodians.
The goal has been to not only fix the recent issues with it, but also to improve the feature overall to ensure that it simulates the process it automates well enough that players don't feel that constructing reinforcements manually is worth it.

That being said, there will still be cases where the ships are spawned at the shipyard, but they should be less common and when they do occurr, they should cause less frustration. The reason why we can't eliminate this completely is because there needs to be a safe path for the consutructed fleet to get to the one requesting reinforcements, or else it would be exploitable and it wouldn't simulate the manual reinforcement process. A lot can happen between the time when you order the reinforcement and the time when they finish construction and attempt to make their way to the target fleet and if it's no longer possible to get to that fleet safely, we will spawn the ship at the shipyard and leave the problem of getting the ship to the target fleet in the hands of the player.
The exploit is that reinforcing ships go MIA and MIA ships cannot be seen on sensors, so fleets can suddenly dramatically increase in power without warning. As long as it remains that way there will be exploits.

Simplest solution: let MIA reinforcing fleets ignore hostiles, but let all empires see that a fleet has e.g. +10 MIA battleships approaching so the reinforcements aren't invisible and quite so exploity.

If ships are created and moved into position manually then an opponent can see them on sensors and intercept them before they merge into a threatening force. If you use the fleet manager then those reinforcements are now invisible to sensors... I imagine the potential exploit would be that you could hide 1 corvette fleets around and have one decoy fleet reinforce and suddenly turn into a full fleet without warning, so you have to treat each lone ship as a potenial doomstack fleet and whichever fleet you try to attack can cancel the reinforcements to waste the time of the enemy... but that's entirely caused by reinforcing fleets going MIA for performance reasons. If the MIA behaviour isn't changing then there will always be potential explots with the reinforcement mechanic that cannot be prevented. i.e. to perform the exploit you'd just have to maintain a safe path to each of those 1 corvette decoy fleets, so it'd still let you perform annoying surprise attacks before getting jump drive technology if you have enough shipyards to quickly reinforce those potential fleets and don't mind waiting a few years.

I suppose one way to prevent fleets wasting their MIA time and failing back to the shipyard without reverting the newer MIA behaviour of reinforcements would be to have the reinforcements wait until there is a safe path before joining the fleet. Perhaps staying MIA for a little while longer (max 12 months?), with a new little warning next to MIA icon that they can't return to normal space until the fleet moves to friendly territory - but that could cause more exploits as MIA fleets don't seem to count towards threat/relative power, so you could park a couple of single corvette fleets in a hostile (mining drone) system, the AI would think you have no fleet and guarantee your independence and feel very protective towards you for a few years and then at the onset of war have those single corvette fleets instantly turn into full fleets when they leave the system (depending on how long the ships waited in MIA space, or conversely an AI could get all their fleets stuck in MIA limbo and beg to be a protectorate because they feel so weak)... so not good I suppose if you're avoiding exploits... so something else then.

Perhaps instead fleets could just indicate incoming reinforcements via their icon (perhaps glowing as a warning with +30 corvette symbol, +20 destroyer, +10 cruiser, +5 battleship on the icon) so there's an indication that someone is trying to perform some 1-ship-fleet hidden-reinforcement shenanigans rather than the fleet suddenly getting dramatically stronger. Then you can let the MIA ships arrive even if there are hostiles in between. (Though I'm not sure how often any of that actually happens anyway).

Lastly, if you're working out bugs/quirks/exploits with fleets moving around I'd love for FTL-inhibitors to not completely break the AI pathfinding. Currentlly I can't farm marauders for debris or lure enemies to suicide into my defences if I research the FTL-inhibition technology (which I eventually get anyway if I do any espionage)... marauders sometimes just get stuck... forever... unless they actually want to attack the station and not a target past the station.

Whatever solution is found I never, ever, want to have the game send ships MIA for many years (longer than it'd take to fly directly) only to have wasted all that time before dumping the ships back at a random selection of shipyards at the opposite end of my empire, each ship now in a seperate fleet that I have to round-up and merge together (I usually just delete them to save the hassle, sometimes they don't even show up in the fleet manager and the fleet manager buttons don't show up on the single-ship fleets for some arcane reason so I have to build ships manually anyway, or merge and split until they stop bugging out and the icons are active again)... if that's going to be the case even after you've fixed things... then sadly it hasn't been completely fixed.
 
  • 2Like
Reactions:
Pop output modifiers (positive and negative) no longer apply to hive minds? That's a huge change if I'm not mistaken. There are some very useful techs that give bonuses to those. Without them, the hive minds would be severely weakened if there won't be any substitute techs exclusive to hive minds. And what about productivity-boosting genetic traits?
 
  • 1
Reactions:
Pop output modifiers (positive and negative) no longer apply to hive minds? That's a huge change if I'm not mistaken. There are some very useful techs that give bonuses to those. Without them, the hive minds would be severely weakened if there won't be any substitute techs exclusive to hive minds. And what about productivity-boosting genetic traits?


This purely refers to the Resources from Ruler and Specialist Jobs +5%, and Resources from Worker Jobs −10% from the Necrophage trait.
 
  • 5
  • 3Like
Reactions:
We did consider this. It is indeed a balance between accuracy & fairness vs convenience.

We felt that just allowing the reinforcement process to cheat would be the "easy way out" here and we would rather first attempt to make it work as best as we can without cheating and if players still feel it's not good enough, we will again consider dropping the conditions of a safe path in favour of making reinforcements feel better for the player.

The specific case you describe should be handled by the changes in 3.2.
If you still feel that it doesn't work well enough, be sure to let us know after you've tried it out :)
Would it be possible to use the naval cost (information inherent to the class of ships) of each ship with a modifier to determine whether any potentially intercepting fleet has enough "naval mass" to stop a reinforcing fleet?

For example, let's use a modifier of two:

To disrupt one reinforcing corvette, you need at least two in its path. To disrupt one reinforcing battleship, you need 16 corvettes in its path. Civilian ships are always 0, while bases should always intercept fleets.

The modifier can be adjusted for balancing purposes.

Principally, the system could be built upon to e.g. add additional modifiers that are based on speed (the faster the ship, the more other ships are required for interception) or based on the existence of more advanced jump drives etc.. For prototyping purposes, the straightforward navel cost times modifier should be more than enough though.

On a slightly different note, it would be good to have reinforcing ships belonging to the same fleet gather first before trying to reinforce. Those fleets could be named like the original fleet with a RF prefix for example for easier identification. As always with these things, a toggle option would be great.
 
  • 2Like
Reactions:
... marauders sometimes just get stuck... forever... unless they actually want to attack the station and not a target past the station.

That issue happens when the marauder fleet is already on their way, and suddenly there is no longer any valid path towards their target. Usually this happens if they are on their way when the target (or enough other empires the fleet needs to path through) when the FTL inhibitor tech is finished. Fleets that are sent out afterwards have no trouble.
 
  • 2
Reactions:
Can i get a TL: DR, stupid version for the simpleton i am for performance? Is this lots better, kinda better or "time go buuuuuuuuuuuuuuuuuuuuh" slow?

Improving performance requires tools to tell you where the game is choking. They repurosed some tools they used in another game to work for Stellaris. In this patch, they fixed a lot of the biggest offenders, so you should see a pretty noticeable impact on lower end harder, especially late-game in larger galaxies. Expect further improvements in the future now that they have better tools.

I can't put hard numbers on it because of all the factors that go into a game's performance on a particular system. Maybe one of the dev replies clarified what results they got in their testing on systems like yours. (I scrolled past the necro-hive discussion and may have missed something. Seemed interesting, especially since there were a lot of gory details from the dev team. But time is limited.)
 
  • 2
Reactions: