-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Separate AI and player logic #4426
Separate AI and player logic #4426
Conversation
Would this cause issues with the ScenarioInfo.Options.AIReplacement option? I've never used it so I don't know if it actually works in faf. |
It does not, it is taken into account. If that is enabled, then the game is considered to 'have AIs' |
I think making the AI not load when there are no AI's is a wonderful thing. Can elaborate what you mean about controlling the imports though? If there's an AI in the game, then the AI files are going to get loaded no matter what, so I don't see a point in delaying their loading. If there's unused imports that load the AI files when there are no AI's, but you're thinking that removing them will cause compatibility issues, don't; utilizing the AI when there is none would be an error anyway, and any mod that transparently enables the AI would have those files imported somewhere else regardless. We either need to redo the AI (which completely breaks mods compatibility) or only try to fix it--and I'm pretty sure we're leaning towards the former since the latter isn't feasible. |
Using
Without it, the game loads in:
Data is gathered by starting the game from the executable and loading in the map |
Campaign doesnt use these files either. Base templates are created in the base manager using data from map_save file. Engineers platoons are loaded by the base manager. And any attack templates are defined in the mission script and go through OPAI |
I can remove the exception for campaign. But how certain are you of this? |
I might be able to export data from the vscode extension to make a graph of the file imports. Would that be useful? |
Campaign needs some basic AI logic running, but it surely doesn't use these large with with base or builder templates |
It would definitely be interesting. Would that be dynamic? |
I'm going to merge this in. @speed2CZ if you want to remove the use of these files in campaign too, all you'll need to change is this: https://github.com/FAForever/fa/blob/deploy/fafdevelop/lua/simInit.lua#L92-L96 I don't have the time at the moment to take that into account, as it would require quite a bit of testing as I'm not familiar with the co-op code that much. |
@relent0r / @maudlin27 / @Azraeel / @Uveso as this touches the foundation of AIs - please make sure your AIs still run as expected. I've tested the base game AI, and that one works. |
Alright, thanks |
And note to self: I'm not sure if we need these files. In particular the |
Its a lookaround table, in the default AI it doesn't do much special beyond the spiral look around for locations to build from a point. But when custom templates are used its useful for having certain units only be built within a certain set of positions. examples of how I use them currently.
I've been meaning to learn how to rotate them so I could get things like a firebase with PD at the front and aa at the back but I haven't done it yet. Loud does it. |
Anyway this seems to have broken my AI's ability to import files. I'm not sure if its this exact PR as I didn't check the branch on its own but its me doing the latest pull on fafdevelop. e.g g:\code\fa\lua\system\import.lua(155): Error importing '/lua/ai/aibuildstructures.lua' |
I see, I'll look at it 👍 |
Does |
@relent0r can you provide a full log? |
Sure |
I think it is because I removed an import. |
|
@relent0r We found it 😄 tldr: add these two lines at the very top of your local AIUtils = import("/lua/ai/aiutilities.lua")
local AIAttackUtils = import("/lua/ai/aiattackutilities.lua") long version: you have a nasty circular dependency with an immediate use on that dependency 😄 , allow me to show you the stack trace. The loop starts in local RNGBeginSession = import('/mods/RNGAI/lua/FlowAI/framework/mapping/Mapping.lua').BeginSession Which produces this sequence of imports: DEBUG: Touched module ' /mods/RNGAI/lua/FlowAI/framework/mapping/Mapping.lua '
DEBUG: Loading module ' /mods/rngai/lua/flowai/framework/mapping/mapping.lua '# <-- start loading mapping.lua
DEBUG: Touched module ' /lua/AI/aiattackutilities.lua ' # marked
DEBUG: Loading module ' /lua/ai/aiattackutilities.lua '
INFO: Hooked /lua/ai/aiattackutilities.lua with /mods/rngai/hook/lua/ai/aiattackutilities.lua
DEBUG: Touched module ' /lua/sim/scenarioutilities.lua '
DEBUG: Touched module ' /lua/ai/aiutilities.lua '
DEBUG: Loading module ' /lua/ai/aiutilities.lua '
INFO: Hooked /lua/ai/aiutilities.lua with /mods/rngai/hook/lua/ai/aiutilities.lua
DEBUG: Touched module ' /lua/buildingtemplates.lua '
DEBUG: Touched module ' /lua/unittemplates.lua '
DEBUG: Touched module ' /lua/sim/scenarioutilities.lua '
DEBUG: Touched module ' /lua/utilities.lua '
DEBUG: Loading module ' /lua/utilities.lua '
DEBUG: Touched module ' /lua/ai/aiattackutilities.lua '
DEBUG: Touched module ' /lua/sim/buff.lua '
DEBUG: Loading module ' /lua/sim/buff.lua '
DEBUG: Touched module ' /lua/ai/sorianutilities.lua ' # marked
DEBUG: Loading module ' /lua/ai/sorianutilities.lua '
DEBUG: Touched module ' /lua/ai/aiutilities.lua ' # marked
DEBUG: Touched module ' /lua/ai/aiattackutilities.lua '
DEBUG: Touched module ' /lua/utilities.lua '
DEBUG: Touched module ' /lua/mods.lua '
DEBUG: Touched module ' /lua/ai/sorianlang.lua '
DEBUG: Loading module ' /lua/ai/sorianlang.lua '
DEBUG: Touched module ' /lua/ai/aibehaviors.lua ' # marked
DEBUG: Loading module ' /lua/ai/aibehaviors.lua '
INFO: Hooked /lua/ai/aibehaviors.lua with /mods/rngai/hook/lua/ai/aibehaviors.lua
DEBUG: Touched module ' /lua/ai/aiutilities.lua '
DEBUG: Touched module ' /lua/utilities.lua '
DEBUG: Touched module ' /lua/ai/aibuildstructures.lua '
DEBUG: Loading module ' /lua/ai/aibuildstructures.lua '
INFO: Hooked /lua/ai/aibuildstructures.lua with /mods/rngai/hook/lua/ai/aibuildstructures.lua
DEBUG: Touched module ' /lua/basetemplates.lua '
DEBUG: Touched module ' /lua/buildingtemplates.lua '
DEBUG: Touched module ' /lua/utilities.lua '
DEBUG: Touched module ' /lua/ai/aiutilities.lua '
DEBUG: Touched module ' /lua/sim/scenarioutilities.lua '
DEBUG: Touched module ' /lua/ai/aiattackutilities.lua ' # marked
DEBUG: Touched module ' /lua/scenariotriggers.lua '
DEBUG: Loading module ' /lua/scenariotriggers.lua '
DEBUG: Touched module ' /lua/sim/scenarioutilities.lua '
WARNING: [aibuildstructures.lua, line:771] * RNGAI: offset aibuildstructures.lua
DEBUG: Touched module ' /mods/RNGAI/lua/AI/RNGUtilities.lua '# <-- loads in mapping.lua too
DEBUG: Loading module ' /mods/rngai/lua/ai/rngutilities.lua '
DEBUG: Touched module ' /lua/ai/AIUtilities.lua ' # 1.
DEBUG: Touched module ' /lua/sim/ScenarioUtilities.lua ' # 2.
DEBUG: Touched module ' /lua/AI/aiattackutilities.lua ' # 3. Note that the last file (after that it crashes) is local AIUtils = import('/lua/ai/AIUtilities.lua') -- 1.
local ScenarioUtils = import('/lua/sim/ScenarioUtilities.lua') -- 2.
local AIAttackUtils = import('/lua/AI/aiattackutilities.lua') -- 3.
local MAP = import('/mods/RNGAI/lua/FlowAI/framework/mapping/Mapping.lua').GetMap() -- <-- note this line Everything works, until we call And that is a circular dependency! Then the question: why didn't this show before this pull request? Well, that is because of these lines: Imports various builders, and those builders occasionally import one of the files that I've marked in the stack trace. Effectively they broke the circular dependency because they change the order of loading. And by adding these to the very top of your hook of local AIUtils = import("/lua/ai/aiutilities.lua")
local AIAttackUtils = import("/lua/ai/aiattackutilities.lua") |
@relent0r Consider using lazyimport :) |
Lazy import would not have prevented this particular situation. It is trying to import: local RNGBeginSession = import('/mods/RNGAI/lua/FlowAI/framework/mapping/Mapping.lua').BeginSession Which ends up importing: local MAP = import('/mods/RNGAI/lua/FlowAI/framework/mapping/Mapping.lua').GetMap() Because of the immediate access, lazy import would not have helped. @relent0r lazy import is something we introduced last week. To me the only actual use case is to prevent importing files that would not be touched in some situation. For example, we can prevent loading the This idea only applies when the code base is generic, like the base repository. In your case you likely want to use your imports or you would not have imported it. Lazy import is no 'concrete' solution either, as pointed out at the start of this post. Circular dependencies is an issue across all languages and all libraries. Last week alone I managed to produce one at work too, but in that case the application told me that I had a circular dependency. Our best bet here is to add a better warning that indicates that you have a circular dependency too. Besides that, there's nothing you can do against it besides being careful when you import something that you end up using immediately - those are prime candidates for circular dependencies during loading as we've seen here. |
BeginSessionCommonArmy() is broken. teams missing. |
@Garanas did you mean to make |
No, I just messed up 😄 ! |
Reduces the amount of memory used in non-ai games by 5 - 10 megabytes.
While watching Relent0r work on the AI I noticed that there are many - many tables that the AI requires to function. That is fine. But these tables are also loaded when there is no need for them (no AIs, not campaign, etc).
We're talking about files like these:
We're talking about tens of thousands of tables - they should not be in memory when they are not required. The idea of this pull request is to make sure that these files do not get loaded unless there is need for them. Sadly though, there are many files that import each other and somewhere at the end of it there is always some reference to these files. Especially the base template file is strangely nested into some files.
@Hdt80bro I'd like your thoughts on this one. Should we try and combat the import madness, or shall we just assign nil to them and 'cull' them if we detect that there is no need for them?
Note that we detect the need of an AI with this value:
So we know for sure when the files can be culled or not.