As for this particular script, you can with some coding perhaps test your particular situation.
You see, I wrote a special 'atomic' function that tries to reserve some X opcodes. This is important for things like
detecting new companies and making objects for them (which can be used by the rest of the script to give them goals,
and the new story book pages, etc.).
Code: Select all
function Atomic(n){
if(n > GSController.GetOpsTillSuspend () + 1){GSController.Sleep(1);}
// Uncomment this to test if it works (spammy!)
//Log.Info("OPS: "+GSController.GetOpsTillSuspend (), Log.LVL_INFO);
}
Now by far and wide the two most important things that will slow down cityBuilder are the amount of towns, and the amount of cargoes you keep track of for each town.
Armed with these ideas you could try to 'measure' the amount of operations codes needed for a 'small' map, in the range of 10-20 towns. Make sure the towns are large enough to have a full requirement set, because (very) small towns are processed considerably faster. Set the maximum opcodes to a high value. Use the GetOpsTillSuspend function twice, at the beginning and end of the main loop, and print the results to console. The result is the amount of opcodes needed for your map. Divide it by the number of towns and we have (approximately) the amount of opcodes per town. There's some amount of overhead included, which makes this somewhat of a conservative estimate.
Note that I won't do the work for you, there's many settings combinations you can have for this script, and they do affect the code paths that are taken. If you put on a lot of logging/news settings and a lot of the special requirements, the script is slower than when you turn all this stuff off.
How can I calculate how large my map can be?
We know there are 28 days in the shortest month, and thus 2072 ticks in this month. If you have your opcode limit at N, we can thus at most use 2072 * N opcodes in the main loop each month.
So, if you want to use citybuilder with a very large map, you can use this to calculate how many towns you can have. Let's do an example:
Suppose that citybuilder uses 50,000 opcodes per town. If we have our limit at 10,000, you can have 414 towns before you will run into problems. Because we would like things to go smoothly in any scenario, and having more users can pose some extra overhead, I recommend keeping town count below 75% of this value, or below 300 in this case. The reality is actually pretty simple, the opcode count scales pretty much linearly with the amount of towns you can have before problems occur.
What problems will occur?
Well, the game loop will sort of fail. Instead of running each month, a month transition occurs half-way. This means the game will effectively 'skip' a month.
It may be an idea to build in some sort of mechanism to detect occurrences and resolve this. My question would be what the best mechanism is in this case. Do we want to force the game to pause, do we want to throw an error or do we want to throw a warning? The error guarantees the script to be well-behaved but of course can ruin your day when your server effectively crashes (well, the game continues, but the script stops).
How does opcode affect the server?
Now what is meant by 'opcode' is actually one instance of a squirrel function or one API call to openTTD, e.g. a script opcode. At the very technical level, an opcode is an instruction for the CPU from the x86 set. And at an even lower level there are differences still per opcode how many instruction cycles per opcode are needed (or opcodes per instruction cycle). Those CPU opcodes result from compiled C code, which in turn is invoked by the squirrel code. Thus 10,000 squirrel calls may be somewhere in the range of about 250,000 to many millions of actual CPU instructions. Some operations like
Code: Select all
function foo(i, j){return i + j; }
are really simple (we assume that the result of compute is actually used). Other operations may be more complicated:
Code: Select all
function bar(i){return GSCompany.ResolveCompanyID(i);}
Generally speaking, the opcode limit as defined by default is a very conservative limit so that the openTTD game script engine will give you minimal overhead when it is added on top of the already existing other game functions, even in worst case scenarios where a script writer writes a script that tries to call mostly resource-intensive operations. Now if you set the limit too high for your machine to handle, you may observe noticeable lag in your game (the clients will have to wait for the server to synchronize). If openTTD uses up high amounts of CPU power to run then you can set this number lower to use less.
Be careful though, premature optimization is never good. First identify if indeed this setting is relevant to system usage. There may be other issues with large maps (pathfinding a large amount of vehicles on a complex network) that can be the cause of your problem.
Some extra info
By the way, a map size of 4kx4k is considered insanely huge by openttd standards. While your users will rarely build as dense a network as is commonly used at openttdcoop it probably won't work when you have high town/industry density. Tile count isn't really the biggest issue, it's vehicle/track count. Pathfinding is going to cost you around O(N * ln(N)), and we run that for some V vehicles, it becomes O(V * N * ln(N)). (N would be the amount of 'vertices' in the pathfinding graph, which initially starts at two. Each network has its own graph. Each railway intersection possibility in a net adds one vertex for a dead-end, and half a vertex for a connected piece. A signal 'block' also adds a vertex).
Because pathfinding generally tends to be the most consuming resource, you might want to look into putting ships back to the default TTD pathfinder. This means the ships become 'stupid' and people have to steer them using a lot of buoys, but has the advantage that the pathfinding is much faster. The reason for the ships specifically having this problem is because the 'network' the ships run on has a lot of vertices, ships can go in a lot of possible routes and it isn't easy finding the best possible route from A to B like with planes (who just go straight all the time). Alternatively, try forbidding any use of ships at all.
On the other hand, if you only include very little towns and industries, then you just have a very sparse map, where networks will not need a lot of intersections.