<Nexus>
Traveler of the Multiverse
So what is ID allocation limit? Can it be increased?
8K.So what is ID allocation limit? Can it be increased?
#define FLTA_ID_SHIFT (13)
#define FLTA_MAX_BUCKETS (1 << FLTA_ID_SHIFT)
AI_updateRouteToCity
are next, but getting mid-game turn times under control is not going to be possible for 10x Huge. The AI stuff will just need a whole rework. Or I extend the parallel unit update implementation to cover most AI, but... ehh... Maybe I should just find the biggest playable map size that doesn't require herculean parallel AI efforts.Wow! That's a lot. I guess we run out of city names much earlier.
Just wanted to ask about that.Maybe I should just find the biggest playable map size that doesn't require herculean parallel AI efforts.
/* advc.opt: In a test over a few turns on a Giant map with 36 civs,
the mean list size at the start of processNode was 80, and the
maximal list size 333. In a longer test on a Huge map with 18 civs,
reserving memory for 128 nodes was faster than for either 32, 200 or 256
(power of 2 does seem to help). K-Mod didn't reserve any memory. */
/* ^Looks like K-Mod has already tried a heap (std: priority_queue). Or maybe
it was abandoned w/o a test. Would have to re-heap after recalculateHeuristics.
And the functor should take m_stepMetric.getMaxPath() as a contructor argument
so that nodes within that limit can receive absolute priority. But, given the
modest number of open nodes, I doubt that a heap will be worthwhile. */
The nerdy stuff. Open set size goes up to around 2300 for 1040x640 on turn 520. I have tried different types of heaps. Currently using a quaternary heap in an array. Popping is the problem, so I think it's slow because it has to bubble down an element from the top to the bottom. And it does it for each active element in the SIMD vector.Let me start out by saying that I’m very impressed by your efforts and skill.
... before diving straight into the nerdy stuff:
What are the average- and worst-case sizes of your open list?
I’m asking because the K-Mod Pathfinder (refactored and improved by @f1rpo for the Advanced Civ Mod: https://github.com/f1rpo/AdvCiv/blob/1.12/CvGameCoreDLL/KmodPathFinder.h
) stores its open list in a std::vector. That means that the two (or three depending on your viewpoint) critical open list operations have the complexity:
1) Finding the minimum element requires a linear scan (O(n)). Quite a bit worse than the heap but extremely cache friendly.
2) Appending a new element is a constant-time push_back (O(1)).
3) Removing an element is just swapping with and popping the last element (ok since the list is not ordered and we're already committed to a linear scan)
I'm curious what numbers you are seeing. Also note this comment:
However, it very likely that you are seeing much worse numbers considering your 1000x1000 map! Still it would be interesting to know the inflection point of a heap vs. a vector with respect to the open list.
See this as well
Finally, the linear find-min scan could itself be sped up by using branchless AVX2 code, effectively pushing the heap-vs-vector inflection point even higher. Have you (or anyone) actually measured where that sweet spot lies?
CvPlotGroup::recalculatePlots
has started to show up on the profiler, at about 55% CPU time on the main thread. Turn times are reaching ~20s at turn 536.Limit!? Maybe there could be a warning message.Maybe limit the game to a map size that won't exceed the limits?
co_await
updated pathfinding plot info when a pending plot shows up at the top of the open set. This may be insanely complex to implement.But to Firaxis' credit, they did try local updates. But in the case of disconnect, it may recount all plots in the group. And that group might include the whole ocean, I assume.if i could suggest one thing that may need to be murdered, dissected and rebuilt, it's the plotgroup system (which is the system handling which tiles are connected for resource access and trade purposes).
From what i've had to interact with, it seems very innefficient, with groups being completely deleted and rebuilt tile by tile each time there's an effect that impacts it ( pillaged road or improvement, discovered resources, ....)
(storage of the network is the key part to make that work, as of now, it's just a list of all the tiles in the group, without better understanding of the links between them)
CvPlotGroup
will store a list of block plot group indices. But, you'd still need to loop over plots in case of split/join, to call CvPlot::setPlotGroup
. This could possibly be threaded. Or further accelerated by counting bonuses block-wise (so, plots would have a CvBlockPlotGroup*
and that will have a CvPlotGroup*
).CvCity::changeNumBonuses
has complex logic that can't just be refactored away. Mechanical redesign though could do all city bonus updates during a single parallel city update, maybe, possibly. At least for AI, things that invalidate plot groups and things that depend on city bonuses could be separated within the turn.Don't we have the source code for PakBuild somewhere? I will admit I never bothered to look into it because we have PakBuild and mods becomes less stable when using fpk files. Better to just release mods with all the files "unpacked".armed with the actual unpacked files (thanks to PakBuild) I just couldn't figure out the data structure of those .fpk archives in their entirety.
Never say never, but I find it unlikely. On top of the workload in doing so, remember how people got upset when they added support for multiplayer through steam. The problem was not the new feature, but that it stopped being network compatible with non-steam versions and it became less mod compatible. Kind of hard to make a remaster, which is modern and at the same time supports the old mods. The backlash was big enough for them to release the previous version as a beta in steam, so they certainly noticed. It's possible Firaxis will see a remaster as expensive and it just won't sell because people get upset about changes. Also from a pure business perspective, it might be more cost effective to produce random DLCs for the newest version.Maybe Firaxis is going to release a remaster on the 20th anniversary of Civilization 4 in October? Well, guess that too falls under wishful thinking...
I can confirm that there are indeed engineers in this thread (yes plural) and while I'm not aware of any confirmed non-engineers, I have to say that I don't know the education level of most people. Personally I'm not too concerned with titles. Skills are more important and that's certainly present here.(seriously are all of you guys professional software engineers??)
...and load time dramatically increases.Better to just release mods with all the files "unpacked".
I get to see what Civ4 in Godot would be like!a Godot project
Yes, you just need to load a NIF. Sounds like a Fun thing to do.A lot of assets are provided via the .fpk files
CIV4MainMenus.xml
.main()
, init your CvInterface
implementation, and within your "world view", show the most basic representation of plots that shows it works. I think this is mostly a giant heightmap actually, pieced together from lots of little textures. And you'd have one global light. You'll zoom out, and say it finally works.We'll have to do it ourselves. Combine the DLL of Civ4 and the assets of the prettiest Civ game. It will be beautiful.It's possible Firaxis will see a remaster as expensive and it just won't sell because people get upset about changes.
It's the same with Colonization, but at the same time We The People won't work when packed. I had issues with Medieval Conquest too (another colo mod) back when it had the files packed. Usually problems means some people have consistent crashes prior to the main menu and making the game in general more prone to random crashes. Sure slow startup isn't great, but it's better than crashing....and load time dramatically increases.
Maybe it's not this way with Col but it is with Civ4. Just try loading Quot Capita Majesty mod. It takes around 15 minutesto get to the main menu.
I did a reimplementation in Medieval Conquest years ago because there is no plot group support in the colonization exe. It's actually not that hard to do, through I admit my goal was to get it working and not try for my current performance goals.Anyway... I've got a plot groups reimplementation going finally. Need to integrate it into the DLL and test it.
It probably needs some sort of "translation layer". I wrote a python file to try to get an overview of what python expects when getting an input from the exe. Not all features mentioned in this file are available in pedia when opened from the main menu, but they are if opened after loading a game. Overall it's not a good system, so the goal should likely be to make a new system and add something to make it backward compatible to avoid having to rewrite all UI in all mods.In-game UI will be something else. How will you do that. The DLL and scripts have expectations. The DLL wants controls that trigger specific actions, it reads modifier key state. And scripts handle screens, and build screen UIs.
Take a look here: https://github.com/NicholasMoser/Naruto-GNT-Modding/blob/main/gnt4/docs/file_formats/fpk.md
While the game is different, from what I understand it's the same fpk format.
I've tried googling and found two or three community-made "Pakking" Tools for CivIV, but none with source code as far as I can tell.Don't we have the source code for PakBuild somewhere? I will admit I never bothered to look into it because we have PakBuild and mods becomes less stable when using fpk files. Better to just release mods with all the files "unpacked".
From what I understand you can pretty much do anything with Godot. There seems to be a virtual filesystem, where resources can be imported via Drag&Drop but also via script during runtime: "res://" (read-only, during runtime) and "user://" (write-access during runtime).And hook up a VFS into however Godot loads resources I guess. Can you memory-map the FPKs?
Thank you so much! That list helped immensely! With that I could finally understand the structure for the most part:FPK format is:
- u32 version?
- u32 magic
- u8 unk
- u32 numFiles
- for each file,
- char[u32] path ROT-1
- u8[align path to 4 bytes] padding
- FILETIME (I think)
- u32 size
- u32 file address
Yep, that's the step I'm currently working on but struggling.Then you need NIFs. Without actual DLL integration, you'd just be testing NIFs I suppose. With animations.
That's definitely a goal for a later phase. For phase 1 I want to show that we can load official assets during runtime and spawn 3D objects in the game world, like a tech-prototype in Godot to show people that it's doable. It's going to take a while but I'm very motivated and I'm spending my entire free time on it.With DLL integration, you could maybe do what I did. Just load a game straight away insidemain()
, init yourCvInterface
implementation, and within your "world view", show the most basic representation of plots that shows it works. I think this is mostly a giant heightmap actually, pieced together from lots of little textures. And you'd have one global light. You'll zoom out, and say it finally works.
That's good to know and once we can load all the official assets and draw all kinds of 3D objects we can start merging those two worlds together!In-game UI will be something else. How will you do that. The DLL and scripts have expectations. The DLL wants controls that trigger specific actions, it reads modifier key state. And scripts handle screens, and build screen UIs.
I think so too, although the theme files .thm for Scaleform are all there. It would take quite a while to figure out what every config entry does, so a main menu based on just Godot would be far quicker but in the end, if we wanted to achieve 100% compability with all the existing mods that modify the menu, we would have to understand Scaleform and load the values from the theme files anyway, so that mods could overwrite them (I know that C2C does this for example)Main menu UI wouldn't be so annoying. It's definited in the application. So I suppose you'd just do whatever the normal Godot UI stuff is.
Maybe it has to do with the compression or the way PakBuild works? I've noticed differences in the 'signature' part of .fpk archives that were created by using PakBuild versus the offical ones. Maybe there's a bug hidden somewhere? Creating single file .fpk archives via PakBuild's CLI helped me greatly with understanding them but that is probably no news for you since you've posted in the exact same thread where I learned this from hahaIt's the same with Colonization, but at the same time We The People won't work when packed. I had issues with Medieval Conquest too (another colo mod) back when it had the files packed. Usually problems means some people have consistent crashes prior to the main menu and making the game in general more prone to random crashes. Sure slow startup isn't great, but it's better than crashing.
100% agree!It probably needs some sort of "translation layer". I wrote a python file to try to get an overview of what python expects when getting an input from the exe. Not all features mentioned in this file are available in pedia when opened from the main menu, but they are if opened after loading a game. Overall it's not a good system, so the goal should likely be to make a new system and add something to make it backward compatible to avoid having to rewrite all UI in all mods.
For NIF support i'm found:
* Editor (https://github.com/niftools/nifskope)
* C++ lib (https://github.com/niftools/niflib)
* Python lib (https://github.com/niftools/pyffi)
* Pprogram code OpenMW - They also imitate Gamebryo work (https://gitlab.com/OpenMW/openmw)
I entertain that thought too. You could probably create an exact UI replication if you build your own UI system and use the theme files. And make it scalable. This is basically making your own CEGUI. I'm not quite sure how it works exactly though.I think so too, although the theme files .thm for Scaleform are all there.
Yes, that's what an engine reimplementation would have to do. It would need a NIF loader. And NIFs are not simply meshes. They are parts of a full scene. They have materials, animations, lights, particle systems, a camera, and maybe other things.So yeah I guess actually using the .nif files in Godot is going to take a while
Looking around, it seems people claim PakBuild is released by Firaxis themselves as a service to mod creators. Google's AI states the source code has never been released and sure enough there is no trace of it.I've tried googling and found two or three community-made "Pakking" Tools for CivIV, but none with source code as far as I can tell.
While being able to read the format would be good, we could consider the option to include PakBuild and hence extract fpk files prior to reading files using a new engine. This means it's a less critical part compared to reading/converting the graphics. If we need to unpack fpk files anyway, it would be a fair argument that converting graphics to a modern format using external tools would be an option, through it is important to remember that we can't distribute vanilla meaning any conversion will have to be scripted for the end user. Being able to read the files and convert in memory at startup for both would be preferable, which means we shouldn't drop that goal. It's more like it's a matter of priorities.
We should likely do it in this order because if a step can't be done, then the following steps becomes pointless.
- load converted graphics
- load original graphics and convert on load
- extract fpk at startup
PlayerTradeNetworkSystem
is the top-most class. It takes cities, bonuses, CvPlot::isTradeNetwork
and CvPlot::isTradeNetworkConnected
, and feeds them into a generic TradeNetworkSystem
.TradeNetworkSystem
is a blockwise dynamic connectivity implementation.TradeNetworkCore
.TradeNetworkCore
is a dumb class, or the bonus distribution class, that simply stores the BPGs, CPGs, bonuses, and cities. It at least tries to minimise city updates given the membership changes.CvPlot::getPlotGroup
when using the new system.ENABLE_PlayerTradeNetworkSystem
currently.DIVERGENCE_CHECKPOINT
macro for even more logging, and the ability to change logging frequency and range. This allows you to efficiently narrow in on the source of a bug.ENABLE_GAMECOREDLL_FIXES
, that fixes a whole bunch of bugs:YIELD_PRODUCTION
forgot to update commerce rates (presumably, when producing commerce).GET_PLAYER(getOwnerINLINE()).calculateInflationRate()
, which depends on game turn and era. So I added updates when those change.CvCity::init/kill
forgot to update city maintenance of the master, when city maintenance depends on vassal city count.CvPlayer::acquireCity
intentionally skips some plot group updates. I added a few more in to at least satisfy log comparisons.!isTradeNetwork
. The plot group path finder will still count the plot because the start node is always valid.CvTeam::setOpenBorders
invalidates CvPlot::isCity
but was missing a plot group update.CvPlot::isTradeNetworkConnected
is not symmetric, but the vanilla plot group system appears to assume it is symmetric (an undirected graph instead of a directed graph). This is the biggest mechanical change:bool CvPlot::isTradeNetworkConnected(const CvPlot* pPlot, TeamTypes eTeam) const
{
// ...
#if ENABLE_GAMECOREDLL_FIXES
if (!isOwned() || !pPlot->isOwned())
#else
if (!isOwned())
#endif
{
if (!isRevealed(eTeam, false) || !(pPlot->isRevealed(eTeam, false)))
{
return false;
}
}
// ...
CvPlotGroup::recalculatePlots
took 38.14% of main thread time. T532 took 22.3s.CvPlotGroup::recalculatePlots
took 50% of main thread time. T532 took 26.1s.705 cities (25 barbarian)
17324/50633 plots owned on biggest land area (34%)
CvPlayer::updatePlotGroups
takes 11.6% of main thread time now. All those whole-map invalidations. Almost all time in there is actually spent just updating plot properties, and it's multi-threaded. So what else do you do... It seems though that my fixes are causing CvPlayer::acquireCity
to invalidate the whole map more than once. I'll have to see if that's necessary.CvRandomEventInterface.applyInfluenza2
does a non-deterministic sort, so it selected different cities on different runs. So that's now been patched. And once again, the Huge/Normal map produces consistent results.718 cities (21 barbarian)
18438/50633 plots owned on biggest land area (36%)
The header starts atMy immediate problem though is that .nif files can be hugely complex inside and deciphering all of that would take ages for me alone. There is indeed a lot of spread out information about .nif files out there but I couldn't find an easily digestible source. The best I have is the niftools-project. (https://www.niftools.org/) but that's where my lack in software developing experience makes things difficult. They have different libraries full of functions that deal with .nif files for every major game that uses them in all their different versions (https://github.com/niftools/nifxml/blob/master/nif.xml - line 172) but the documentation assumes you already know a considerable amount about the topic.
<struct name="Header" module="NiMain">
in the XML, I think. The header contains the class types, and then you start deserialising them after the header. And then once you've deserialised, you then link up the pointers. Lookup <basic name="Ptr"
and <basic name="Ref"
. If in doubt, use nifskope to check what fields a class has. The "footer" of the file contains a list of roots, normally one.