Split g_TemplateData into per-player cache

Previously, all templates were stored in the same cache. This was
problematic because the data returned by the GuiInterface depends on
the player passed (since it applies player modifications). The reason
this didn't cause a lot of issues is because most of the time
GetTemplateData is called without the player parameter, in which case
the viewed player is always fallen back to. In a few other cases the
viewed player is even passed directly to it, which has the same effect.

Creating one cache for each player is the best way to ensure the
stored template data is consistent and what one would expect.

Also, this allows us to stop having to reset the cache when the viewed
player is changed.
This commit is contained in:
Vantha 2026-02-12 17:39:27 +01:00 committed by Vantha
parent a2befad973
commit 15f150341a
No known key found for this signature in database
GPG key ID: 3F5D02FA4D3E8E74
2 changed files with 31 additions and 31 deletions

View file

@ -226,13 +226,23 @@ function GetEntityState(entId)
*/
function GetTemplateData(templateName, player)
{
if (!(templateName in g_TemplateData))
const targetPlayer = player || g_ViewedPlayer;
let cache = g_TemplateData[targetPlayer];
if (!cache)
{
const template = Engine.GuiInterfaceCall("GetTemplateData", { "templateName": templateName, "player": player });
translateObjectKeys(template, ["specific", "generic", "tooltip"]);
g_TemplateData[templateName] = deepfreeze(template);
cache = {};
g_TemplateData[targetPlayer] = cache;
}
return g_TemplateData[templateName];
let templateData = cache[templateName];
if (!templateData)
{
templateData = Engine.GuiInterfaceCall("GetTemplateData", { "templateName": templateName, "player": targetPlayer });
translateObjectKeys(templateData, ["specific", "generic", "tooltip"]);
deepfreeze(templateData);
cache[templateName] = templateData;
}
return templateData;
}
function GetTechnologyData(technologyName, civ)
@ -285,7 +295,6 @@ async function init(initData, hotloadData)
g_DiplomacyColors = new DiplomacyColors();
g_PlayerViewControl = new PlayerViewControl();
g_PlayerViewControl.registerViewedPlayerChangeHandler(g_DiplomacyColors.updateDisplayedPlayerColors.bind(g_DiplomacyColors));
g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates);
g_DiplomacyColors.registerDiplomacyColorsChangeHandler(g_PlayerViewControl.rebuild.bind(g_PlayerViewControl));
g_PauseControl = new PauseControl();
g_PlayerViewControl.registerPreViewedPlayerChangeHandler(removeStatusBarDisplay);
@ -495,16 +504,6 @@ function initializeMusic()
}
function resetTemplates()
{
// Update GUI and clear player-dependent cache
g_TemplateData = {};
Engine.GuiInterfaceCall("ResetTemplateModified");
// TODO: do this more selectively
onSimulationUpdate();
}
/**
* Returns true if the player with that ID is in observermode.
*/
@ -660,15 +659,16 @@ function onTick()
function onSimulationUpdate()
{
g_EntityStates = {};
g_SimState = undefined;
// Templates change depending on technologies and auras, so they have to be reloaded after such a change.
// g_TechnologyData data never changes, so it shouldn't be deleted.
g_EntityStates = {};
if (Engine.GuiInterfaceCall("IsTemplateModified"))
{
g_TemplateData = {};
Engine.GuiInterfaceCall("ResetTemplateModified");
}
g_SimState = undefined;
const players = Engine.GuiInterfaceCall("GetPlayersWithModifiedTemplates");
for (const player of players)
g_TemplateData[player] = {};
if (players.size)
Engine.GuiInterfaceCall("ResetPlayersWithModifiedTemplates");
// Some changes may require re-rendering the selection.
if (Engine.GuiInterfaceCall("IsSelectionDirty"))

View file

@ -36,7 +36,7 @@ GuiInterface.prototype.Init = function()
this.entsRallyPointsDisplayed = [];
this.entsWithAuraAndStatusBars = new Set();
this.enabledVisualRangeOverlayTypes = {};
this.templateModified = {};
this.playersWithModifiedTemplates = new Set();
this.selectionDirty = {};
this.obstructionSnap = new ObstructionSnap();
};
@ -713,18 +713,18 @@ GuiInterface.prototype.GetNeededResources = function(player, data)
*/
GuiInterface.prototype.OnTemplateModification = function(msg)
{
this.templateModified[msg.player] = true;
this.playersWithModifiedTemplates.add(msg.player);
this.selectionDirty[msg.player] = true;
};
GuiInterface.prototype.IsTemplateModified = function(player)
GuiInterface.prototype.GetPlayersWithModifiedTemplates = function(player)
{
return this.templateModified[player] || false;
return this.playersWithModifiedTemplates;
};
GuiInterface.prototype.ResetTemplateModified = function()
GuiInterface.prototype.ResetPlayersWithModifiedTemplates = function()
{
this.templateModified = {};
this.playersWithModifiedTemplates.clear();
};
/**
@ -2132,8 +2132,8 @@ GuiInterface.prototype.exposedFunctions = {
"GetTraderNumber": 1,
"GetTradingGoods": 1,
"IsTemplateModified": 1,
"ResetTemplateModified": 1,
"GetPlayersWithModifiedTemplates": 1,
"ResetPlayersWithModifiedTemplates": 1,
"IsSelectionDirty": 1,
"ResetSelectionDirty": 1
};