Sped up large battles and trimmed snapshot traffic
- The bombardment overlay was rebuilt every animation frame, and each barrage scanned the whole army to find its target (O(strikes x units)). It now redraws at most at the renderer's 24 fps, reuses its SVG/DOM nodes, and resolves garrisons from one per-snapshot tile index, so a high-refresh display no longer makes a big battle worse. - Route truncation had the same shape: every step of every route rescanned every unit. The snapshot now buckets the army by tile once. - Idle units no longer have identical left/top rewritten every frame, and the whole-map fog buffer is rebuilt less often. - Snapshot traffic: cityStats and visible are delta-encoded like territory and population, city stats carry a content-hash version, tile ethnicity is memoised, and a city's economy is derived once per broadcast instead of twice. A per-peer civ change now forces a full snapshot, since the visible set is per-viewer.
This commit is contained in:
@@ -12,6 +12,10 @@ export const serializationMethods = {
|
||||
},
|
||||
|
||||
serializeShared() {
|
||||
// Built once and shared by every viewer. Its content hash doubles as the
|
||||
// version that lets the server omit it from a delta when nothing moved, so
|
||||
// a city panel is not re-sent on every 10 Hz broadcast.
|
||||
const cityStats = this._serializeCityStats();
|
||||
return {
|
||||
seed: this.seed,
|
||||
// The world-generation config the seed was generated with. The browser
|
||||
@@ -48,7 +52,7 @@ export const serializationMethods = {
|
||||
tileGdpPenalties: this._serializeTileValues(this.tileGdpPenalty),
|
||||
tileBattleGdpDeficits: this._serializeTileValues(this.tileBattleGdpDeficit),
|
||||
civStats: this._serializeCivStats(),
|
||||
cityStats: this._serializeCityStats(),
|
||||
cityStats: cityStats,
|
||||
budgets: this._serializeCivValues(this.budgets),
|
||||
culture: this._serializeCivValues(this.culture),
|
||||
// Each nation's share of the world's cultural output (zero-sum) and the
|
||||
@@ -102,6 +106,8 @@ export const serializationMethods = {
|
||||
ethnicity: this._ethnicityVersion,
|
||||
improvements: this._improvementVersion,
|
||||
warfare: this._tileImprovementVersion,
|
||||
cityStats: hashCityStats(cityStats),
|
||||
visible: this._visibleVersion,
|
||||
},
|
||||
};
|
||||
},
|
||||
@@ -319,7 +325,7 @@ export const serializationMethods = {
|
||||
approval: economy.approval,
|
||||
income: economy.income,
|
||||
// The region's own income and building upkeep, for the city budget tab.
|
||||
budget: this.cityBudgetBreakdown(city),
|
||||
budget: this.cityBudgetBreakdown(city, economy),
|
||||
// The ethnicity of the region's people, for the circle graph.
|
||||
ethnicMakeup: this.getCityEthnicMakeup(city),
|
||||
};
|
||||
@@ -509,8 +515,17 @@ export const serializationMethods = {
|
||||
},
|
||||
|
||||
// Sparse [x, y, [[ethnicity, share, trend], ...]] for every populated tile,
|
||||
// where trend is the net migration over the last day.
|
||||
// where trend is the net migration over the last day. Memoised against the
|
||||
// ethnicity version and the current migration delta store: the shares only
|
||||
// move when politics does, so re-walking every populated tile on every 10 Hz
|
||||
// broadcast was pure waste (the delta wire format drops it anyway).
|
||||
_serializeTileEthnicity() {
|
||||
const cache = this._serializedTileEthnicityCache;
|
||||
if (cache &&
|
||||
cache.version === this._ethnicityVersion &&
|
||||
cache.deltas === this._migrationDeltas.tile) {
|
||||
return cache.value;
|
||||
}
|
||||
const result = [];
|
||||
for (const [k, shares] of this.tileEthnicity) {
|
||||
const coords = parseKey(k);
|
||||
@@ -526,6 +541,11 @@ export const serializationMethods = {
|
||||
if (entries.length === 0) continue;
|
||||
result.push([coords.x, coords.y, entries]);
|
||||
}
|
||||
this._serializedTileEthnicityCache = {
|
||||
version: this._ethnicityVersion,
|
||||
deltas: this._migrationDeltas.tile,
|
||||
value: result,
|
||||
};
|
||||
return result;
|
||||
},
|
||||
|
||||
@@ -606,6 +626,19 @@ function roundTo(value, digits) {
|
||||
return Math.round(value * factor) / factor;
|
||||
}
|
||||
|
||||
// A cheap, stable fingerprint of the serialised city stats. Comparing it lets
|
||||
// the server treat cityStats like the other versioned collections without
|
||||
// having to hook every economy-affecting mutation (pillage, battle scars,
|
||||
// approval drift, research) individually.
|
||||
function hashCityStats(stats) {
|
||||
const text = JSON.stringify(stats);
|
||||
let hash = 2166136261;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
hash = Math.imul(hash ^ text.charCodeAt(i), 16777619);
|
||||
}
|
||||
return `${text.length}:${(hash >>> 0).toString(36)}`;
|
||||
}
|
||||
|
||||
// A Map<ethnicity, number> as sorted [ethnicity, roundedNumber] pairs, dropping
|
||||
// zero entries.
|
||||
function mapToEntries(map) {
|
||||
|
||||
Reference in New Issue
Block a user