From 260ba892c80d1424bcb484aa2b310e0d082abe3b Mon Sep 17 00:00:00 2001 From: tobspr Date: Mon, 18 May 2020 16:08:33 +0200 Subject: [PATCH] Properly process belt dependencies and fix items not travelling linear on belts --- src/js/core/config.js | 5 +- src/js/game/components/belt.js | 17 +- src/js/game/dynamic_tickrate.js | 12 +- src/js/game/systems/belt.js | 228 ++++++---- src/js/game/time/game_time.js | 15 +- src/js/game/upgrades.js | 32 +- src/js/game_analytics.d.ts | 737 -------------------------------- 7 files changed, 199 insertions(+), 847 deletions(-) delete mode 100644 src/js/game_analytics.d.ts diff --git a/src/js/core/config.js b/src/js/core/config.js index 3e56c156..9bf7847a 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -23,7 +23,7 @@ export const globalConfig = { statisticsGraphSlices: 100, analyticsSliceDurationSeconds: 10, - minimumTickRate: 30, + minimumTickRate: 25, maximumTickRate: 500, // Map @@ -71,7 +71,7 @@ export const globalConfig = { debug: { /* dev:start */ - // fastGameEnter: true, + fastGameEnter: true, noArtificialDelays: true, // disableSavegameWrite: true, showEntityBounds: false, @@ -85,6 +85,7 @@ export const globalConfig = { allBuildingsUnlocked: true, upgradesNoCost: true, disableUnlockDialog: true, + // framePausesBetweenTicks: 40, // testTranslations: true, /* dev:end */ }, diff --git a/src/js/game/components/belt.js b/src/js/game/components/belt.js index 3f890b9b..dbca2ec5 100644 --- a/src/js/game/components/belt.js +++ b/src/js/game/components/belt.js @@ -60,7 +60,7 @@ export class BeltComponent extends Component { /** * Returns if the belt can currently accept an item from the given direction */ - canAcceptNewItem() { + canAcceptNewItem(leftoverProgress = 0.0) { const firstItem = this.sortedItems[0]; if (!firstItem) { return true; @@ -73,8 +73,19 @@ export class BeltComponent extends Component { * Pushes a new item to the belt * @param {BaseItem} item */ - takeNewItem(item) { - this.sortedItems.unshift([0, item]); + takeNewItem(item, leftoverProgress = 0.0) { + if (G_IS_DEV) { + assert( + this.sortedItems.length === 0 || + leftoverProgress <= this.sortedItems[0][0] - globalConfig.itemSpacingOnBelts + 0.001, + "Invalid leftover: " + + leftoverProgress + + " items are " + + this.sortedItems.map(item => item[0]) + ); + assert(leftoverProgress < 1.0, "Invalid leftover: " + leftoverProgress); + } + this.sortedItems.unshift([leftoverProgress, item]); } /** diff --git a/src/js/game/dynamic_tickrate.js b/src/js/game/dynamic_tickrate.js index bfa5d42d..f70a084e 100644 --- a/src/js/game/dynamic_tickrate.js +++ b/src/js/game/dynamic_tickrate.js @@ -14,15 +14,11 @@ export class DynamicTickrate { constructor(root) { this.root = root; - this.setTickRate(120); - this.currentTickStart = null; this.capturedTicks = []; this.averageTickDuration = 0; - // Exposed - this.deltaSeconds = 0; - this.deltaMs = 0; + this.setTickRate(60); } /** @@ -40,14 +36,14 @@ export class DynamicTickrate { * Increases the tick rate marginally */ increaseTickRate() { - this.setTickRate(Math_round(Math_min(globalConfig.maximumTickRate, this.currentTickRate * 1.1))); + this.setTickRate(Math_round(Math_min(globalConfig.maximumTickRate, this.currentTickRate * 1.2))); } /** * Decreases the tick rate marginally */ decreaseTickRate() { - this.setTickRate(Math_round(Math_min(globalConfig.maximumTickRate, this.currentTickRate * 0.9))); + this.setTickRate(Math_round(Math_max(globalConfig.minimumTickRate, this.currentTickRate * 0.8))); } /** @@ -57,7 +53,7 @@ export class DynamicTickrate { assert(this.currentTickStart === null, "BeginTick called twice"); this.currentTickStart = performanceNow(); - if (this.capturedTicks.length > this.currentTickRate * 4) { + if (this.capturedTicks.length > this.currentTickRate * 2) { // Take only a portion of the ticks this.capturedTicks.sort(); this.capturedTicks.splice(0, 10); diff --git a/src/js/game/systems/belt.js b/src/js/game/systems/belt.js index 9ecf8ba1..cd228bce 100644 --- a/src/js/game/systems/belt.js +++ b/src/js/game/systems/belt.js @@ -12,9 +12,14 @@ import { gMetaBuildingRegistry } from "../../core/global_registries"; import { MetaBeltBaseBuilding } from "../buildings/belt_base"; import { defaultBuildingVariant } from "../meta_building"; import { GameRoot } from "../root"; +import { createLogger } from "../../core/logging"; const BELT_ANIM_COUNT = 6; +const logger = createLogger("belt"); + +/** @typedef {Array<{ entity: Entity, followUp: Entity }>} BeltCache */ + export class BeltSystem extends GameSystemWithFilter { constructor(root) { super(root, [BeltComponent]); @@ -26,7 +31,7 @@ export class BeltSystem extends GameSystemWithFilter { [enumDirection.left]: Loader.getSprite("sprites/belt/left_0.png"), [enumDirection.right]: Loader.getSprite("sprites/belt/right_0.png"), }; - /** + /**b * @type {Object.>} */ this.beltAnimations = { @@ -58,6 +63,11 @@ export class BeltSystem extends GameSystemWithFilter { this.root.signals.entityAdded.add(this.updateSurroundingBeltPlacement, this); this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this); + + this.cacheNeedsUpdate = true; + + /** @type {BeltCache} */ + this.beltCache = []; } /** @@ -74,6 +84,10 @@ export class BeltSystem extends GameSystemWithFilter { return; } + if (entity.components.Belt) { + this.cacheNeedsUpdate = true; + } + const metaBelt = gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding); // Compute affected area @@ -98,6 +112,7 @@ export class BeltSystem extends GameSystemWithFilter { ); targetStaticComp.rotation = rotation; metaBelt.updateVariants(targetEntity, rotationVariant, defaultBuildingVariant); + this.cacheNeedsUpdate = true; } } } @@ -110,97 +125,155 @@ export class BeltSystem extends GameSystemWithFilter { } /** - * Updates a given entity + * Finds the follow up entity for a given belt. Used for building the dependencies * @param {Entity} entity - * @param {Set} processedEntities */ - updateBelt(entity, processedEntities) { - if (processedEntities.has(entity.uid)) { - return; - } - - processedEntities.add(entity.uid); - - // Divide by item spacing on belts since we use throughput and not speed - const beltSpeed = - this.root.hubGoals.getBeltBaseSpeed() * - this.root.dynamicTickrate.deltaSeconds * - globalConfig.itemSpacingOnBelts; - const beltComp = entity.components.Belt; + findFollowUpEntity(entity) { const staticComp = entity.components.StaticMapEntity; - const items = beltComp.sortedItems; + const beltComp = entity.components.Belt; - if (items.length === 0) { - // Fast out for performance + const followUpDirection = staticComp.localDirectionToWorld(beltComp.direction); + const followUpVector = enumDirectionToVector[followUpDirection]; + + const followUpTile = staticComp.origin.add(followUpVector); + const followUpEntity = this.root.map.getTileContent(followUpTile); + + if (followUpEntity) { + const followUpBeltComp = followUpEntity.components.Belt; + if (followUpBeltComp) { + return followUpEntity; + } + } + + return null; + } + + /** + * Adds a single entity to the cache + * @param {Entity} entity + * @param {BeltCache} cache + * @param {Set} visited + */ + computeSingleBeltCache(entity, cache, visited) { + // Check for double visit + if (visited.has(entity.uid)) { return; } + visited.add(entity.uid); - const ejectorComp = entity.components.ItemEjector; - let maxProgress = 1; - - // When ejecting, we can not go further than the item spacing since it - // will be on the corner - if (ejectorComp.isAnySlotEjecting()) { - maxProgress = 1 - globalConfig.itemSpacingOnBelts; - } else { - // Find follow up belt to make sure we don't clash items - const followUpDirection = staticComp.localDirectionToWorld(beltComp.direction); - const followUpVector = enumDirectionToVector[followUpDirection]; - - const followUpTile = staticComp.origin.add(followUpVector); - const followUpEntity = this.root.map.getTileContent(followUpTile); - - if (followUpEntity) { - const followUpBeltComp = followUpEntity.components.Belt; - if (followUpBeltComp) { - // Update follow up belt first - this.updateBelt(followUpEntity, processedEntities); - - const spacingOnBelt = followUpBeltComp.getDistanceToFirstItemCenter(); - maxProgress = Math_min(1, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt); - } - } + const followUp = this.findFollowUpEntity(entity); + if (followUp) { + // Process followup first + this.computeSingleBeltCache(followUp, cache, visited); } - let speedMultiplier = 1; - if (beltComp.direction !== enumDirection.top) { - // Shaped belts are longer, thus being quicker - speedMultiplier = 1.41; + cache.push({ entity, followUp }); + } + + computeBeltCache() { + logger.log("Updating belt cache"); + + let cache = []; + let visited = new Set(); + for (let i = 0; i < this.allEntities.length; ++i) { + this.computeSingleBeltCache(this.allEntities[i], cache, visited); } + assert( + cache.length === this.allEntities.length, + "Belt cache mismatch: Has " + cache.length + " entries but should have " + this.allEntities.length + ); - for (let itemIndex = items.length - 1; itemIndex >= 0; --itemIndex) { - const itemAndProgress = items[itemIndex]; - - const newProgress = itemAndProgress[0] + speedMultiplier * beltSpeed; - if (newProgress >= 1.0) { - // Try to give this item to a new belt - const freeSlot = ejectorComp.getFirstFreeSlot(); - - if (freeSlot === null) { - // So, we don't have a free slot - damned! - itemAndProgress[0] = 1.0; - maxProgress = 1 - globalConfig.itemSpacingOnBelts; - } else { - // We got a free slot, remove this item and keep it on the ejector slot - if (!ejectorComp.tryEject(freeSlot, itemAndProgress[1])) { - assert(false, "Ejection failed"); - } - items.splice(itemIndex, 1); - maxProgress = 1; - } - } else { - itemAndProgress[0] = Math_min(newProgress, maxProgress); - maxProgress = itemAndProgress[0] - globalConfig.itemSpacingOnBelts; - } - } + this.beltCache = cache; } update() { - const processedEntities = new Set(); + if (this.cacheNeedsUpdate) { + this.cacheNeedsUpdate = false; + this.computeBeltCache(); + } - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - this.updateBelt(entity, processedEntities); + for (let i = 0; i < this.beltCache.length; ++i) { + const { entity, followUp } = this.beltCache[i]; + + // Divide by item spacing on belts since we use throughput and not speed + const beltSpeed = + this.root.hubGoals.getBeltBaseSpeed() * + this.root.dynamicTickrate.deltaSeconds * + globalConfig.itemSpacingOnBelts; + const beltComp = entity.components.Belt; + const items = beltComp.sortedItems; + + if (items.length === 0) { + // Fast out for performance + continue; + } + + const ejectorComp = entity.components.ItemEjector; + let maxProgress = 1; + + // When ejecting, we can not go further than the item spacing since it + // will be on the corner + if (ejectorComp.isAnySlotEjecting()) { + maxProgress = 1 - globalConfig.itemSpacingOnBelts; + } else { + // Otherwise our progress depends on the follow up + if (followUp) { + const spacingOnBelt = followUp.components.Belt.getDistanceToFirstItemCenter(); + maxProgress = Math_min(2, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt); + } + } + + let speedMultiplier = 1; + if (beltComp.direction !== enumDirection.top) { + // Shaped belts are longer, thus being quicker + speedMultiplier = 1.41; + } + + // Not really nice. haven't found the reason for this yet. + if (items.length > 2 / globalConfig.itemSpacingOnBelts) { + logger.error("Fixing broken belt:", entity, items); + beltComp.sortedItems = []; + } + + for (let itemIndex = items.length - 1; itemIndex >= 0; --itemIndex) { + const progressAndItem = items[itemIndex]; + + progressAndItem[0] = Math_min(maxProgress, progressAndItem[0] + speedMultiplier * beltSpeed); + + if (progressAndItem[0] >= 1.0) { + if (followUp) { + const followUpBelt = followUp.components.Belt; + if (followUpBelt.canAcceptNewItem()) { + followUpBelt.takeNewItem(progressAndItem[1], progressAndItem[0] - 1.0); + items.splice(itemIndex, 1); + } else { + // Well, we couldn't really take it to a follow up belt, keep it at + // max progress + progressAndItem[0] = 1.0; + maxProgress = 1 - globalConfig.itemSpacingOnBelts; + } + } else { + // Try to give this item to a new belt + const freeSlot = ejectorComp.getFirstFreeSlot(); + if (freeSlot === null) { + // So, we don't have a free slot - damned! + progressAndItem[0] = 1.0; + maxProgress = 1 - globalConfig.itemSpacingOnBelts; + } else { + // We got a free slot, remove this item and keep it on the ejector slot + if (!ejectorComp.tryEject(freeSlot, progressAndItem[1])) { + assert(false, "Ejection failed"); + } + items.splice(itemIndex, 1); + // Do not override max progress at all + // maxProgress = 1; + } + } + } else { + // We just moved this item forward, so determine the maximum progress of other items + maxProgress = progressAndItem[0] - globalConfig.itemSpacingOnBelts; + } + } } } @@ -212,7 +285,6 @@ export class BeltSystem extends GameSystemWithFilter { drawChunk(parameters, chunk) { if (parameters.zoomLevel < globalConfig.mapChunkOverviewMinZoom) { return; - 1; } const speedMultiplier = this.root.hubGoals.getBeltBaseSpeed(); diff --git a/src/js/game/time/game_time.js b/src/js/game/time/game_time.js index f83cbe79..4bcc6869 100644 --- a/src/js/game/time/game_time.js +++ b/src/js/game/time/game_time.js @@ -112,10 +112,14 @@ export class GameTime extends BasicSerializableObject { } // Check for too big pile of updates -> reduce it to 1 - const maxLogicSteps = Math_max( + let maxLogicSteps = Math_max( 3, (this.speed.getMaxLogicStepsInQueue() * this.root.dynamicTickrate.currentTickRate) / 60 ); + if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) { + maxLogicSteps *= 1 + globalConfig.debug.framePausesBetweenTicks; + } + if (this.logicTimeBudget > this.root.dynamicTickrate.deltaMs * maxLogicSteps) { // logger.warn("Skipping logic time steps since more than", maxLogicSteps, "are in queue"); this.logicTimeBudget = this.root.dynamicTickrate.deltaMs * maxLogicSteps; @@ -132,9 +136,14 @@ export class GameTime extends BasicSerializableObject { const speedAtStart = this.root.time.getSpeed(); + let effectiveDelta = this.root.dynamicTickrate.deltaMs; + if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) { + effectiveDelta += globalConfig.debug.framePausesBetweenTicks * this.root.dynamicTickrate.deltaMs; + } + // Update physics & logic - while (this.logicTimeBudget >= this.root.dynamicTickrate.deltaMs) { - this.logicTimeBudget -= this.root.dynamicTickrate.deltaMs; + while (this.logicTimeBudget >= effectiveDelta) { + this.logicTimeBudget -= effectiveDelta; if (!updateMethod()) { // Gameover happened or so, do not update anymore diff --git a/src/js/game/upgrades.js b/src/js/game/upgrades.js index 08f246c7..4260f7f9 100644 --- a/src/js/game/upgrades.js +++ b/src/js/game/upgrades.js @@ -16,19 +16,19 @@ export const UPGRADES = { }, { required: [{ shape: "CpCpCpCp", amount: 15000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 40000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 40000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: finalGameShape, amount: 150000 }], - improvement: 4, + improvement: 5, excludePrevious: true, }, ], @@ -46,19 +46,19 @@ export const UPGRADES = { }, { required: [{ shape: "ScScScSc", amount: 20000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 40000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 40000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: finalGameShape, amount: 150000 }], - improvement: 4, + improvement: 5, excludePrevious: true, }, ], @@ -76,19 +76,19 @@ export const UPGRADES = { }, { required: [{ shape: "CgScScCg", amount: 25000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 40000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 40000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: finalGameShape, amount: 150000 }], - improvement: 4, + improvement: 5, excludePrevious: true, }, ], @@ -106,19 +106,19 @@ export const UPGRADES = { }, { required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 30000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 40000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 40000 }], - improvement: 4, + improvement: 2, }, { required: [{ shape: finalGameShape, amount: 150000 }], - improvement: 4, + improvement: 5, excludePrevious: true, }, ], diff --git a/src/js/game_analytics.d.ts b/src/js/game_analytics.d.ts deleted file mode 100644 index 3324213e..00000000 --- a/src/js/game_analytics.d.ts +++ /dev/null @@ -1,737 +0,0 @@ -declare module gameanalytics { - enum EGAErrorSeverity { - Undefined = 0, - Debug = 1, - Info = 2, - Warning = 3, - Error = 4, - Critical = 5, - } - enum EGAProgressionStatus { - Undefined = 0, - Start = 1, - Complete = 2, - Fail = 3, - } - enum EGAResourceFlowType { - Undefined = 0, - Source = 1, - Sink = 2, - } - module http { - enum EGAHTTPApiResponse { - NoResponse = 0, - BadResponse = 1, - RequestTimeout = 2, - JsonEncodeFailed = 3, - JsonDecodeFailed = 4, - InternalServerError = 5, - BadRequest = 6, - Unauthorized = 7, - UnknownResponseCode = 8, - Ok = 9, - Created = 10, - } - } - module events { - enum EGASdkErrorCategory { - Undefined = 0, - EventValidation = 1, - Database = 2, - Init = 3, - Http = 4, - Json = 5, - } - enum EGASdkErrorArea { - Undefined = 0, - BusinessEvent = 1, - ResourceEvent = 2, - ProgressionEvent = 3, - DesignEvent = 4, - ErrorEvent = 5, - InitHttp = 9, - EventsHttp = 10, - ProcessEvents = 11, - AddEventsToStore = 12, - } - enum EGASdkErrorAction { - Undefined = 0, - InvalidCurrency = 1, - InvalidShortString = 2, - InvalidEventPartLength = 3, - InvalidEventPartCharacters = 4, - InvalidStore = 5, - InvalidFlowType = 6, - StringEmptyOrNull = 7, - NotFoundInAvailableCurrencies = 8, - InvalidAmount = 9, - NotFoundInAvailableItemTypes = 10, - WrongProgressionOrder = 11, - InvalidEventIdLength = 12, - InvalidEventIdCharacters = 13, - InvalidProgressionStatus = 15, - InvalidSeverity = 16, - InvalidLongString = 17, - DatabaseTooLarge = 18, - DatabaseOpenOrCreate = 19, - JsonError = 25, - FailHttpJsonDecode = 29, - FailHttpJsonEncode = 30, - } - enum EGASdkErrorParameter { - Undefined = 0, - Currency = 1, - CartType = 2, - ItemType = 3, - ItemId = 4, - Store = 5, - FlowType = 6, - Amount = 7, - Progression01 = 8, - Progression02 = 9, - Progression03 = 10, - EventId = 11, - ProgressionStatus = 12, - Severity = 13, - Message = 14, - } - } -} -export declare var EGAErrorSeverity: typeof gameanalytics.EGAErrorSeverity; -export declare var EGAProgressionStatus: typeof gameanalytics.EGAProgressionStatus; -export declare var EGAResourceFlowType: typeof gameanalytics.EGAResourceFlowType; -declare module gameanalytics { - module logging { - class GALogger { - private static readonly instance; - private infoLogEnabled; - private infoLogVerboseEnabled; - private static debugEnabled; - private static readonly Tag; - private constructor(); - static setInfoLog(value: boolean): void; - static setVerboseLog(value: boolean): void; - static i(format: string): void; - static w(format: string): void; - static e(format: string): void; - static ii(format: string): void; - static d(format: string): void; - private sendNotificationMessage; - } - } -} -declare module gameanalytics { - module utilities { - class GAUtilities { - static getHmac(key: string, data: string): string; - static stringMatch(s: string, pattern: RegExp): boolean; - static joinStringArray(v: Array, delimiter: string): string; - static stringArrayContainsString(array: Array, search: string): boolean; - private static readonly keyStr; - static encode64(input: string): string; - static decode64(input: string): string; - static timeIntervalSince1970(): number; - static createGuid(): string; - private static s4; - } - } -} -declare module gameanalytics { - module validators { - import EGASdkErrorCategory = gameanalytics.events.EGASdkErrorCategory; - import EGASdkErrorArea = gameanalytics.events.EGASdkErrorArea; - import EGASdkErrorAction = gameanalytics.events.EGASdkErrorAction; - import EGASdkErrorParameter = gameanalytics.events.EGASdkErrorParameter; - class ValidationResult { - category: EGASdkErrorCategory; - area: EGASdkErrorArea; - action: EGASdkErrorAction; - parameter: EGASdkErrorParameter; - reason: string; - constructor( - category: EGASdkErrorCategory, - area: EGASdkErrorArea, - action: EGASdkErrorAction, - parameter: EGASdkErrorParameter, - reason: string - ); - } - class GAValidator { - static validateBusinessEvent( - currency: string, - amount: number, - cartType: string, - itemType: string, - itemId: string - ): ValidationResult; - static validateResourceEvent( - flowType: EGAResourceFlowType, - currency: string, - amount: number, - itemType: string, - itemId: string, - availableCurrencies: Array, - availableItemTypes: Array - ): ValidationResult; - static validateProgressionEvent( - progressionStatus: EGAProgressionStatus, - progression01: string, - progression02: string, - progression03: string - ): ValidationResult; - static validateDesignEvent(eventId: string): ValidationResult; - static validateErrorEvent(severity: EGAErrorSeverity, message: string): ValidationResult; - static validateSdkErrorEvent( - gameKey: string, - gameSecret: string, - category: EGASdkErrorCategory, - area: EGASdkErrorArea, - action: EGASdkErrorAction - ): boolean; - static validateKeys(gameKey: string, gameSecret: string): boolean; - static validateCurrency(currency: string): boolean; - static validateEventPartLength(eventPart: string, allowNull: boolean): boolean; - static validateEventPartCharacters(eventPart: string): boolean; - static validateEventIdLength(eventId: string): boolean; - static validateEventIdCharacters(eventId: string): boolean; - static validateAndCleanInitRequestResponse( - initResponse: { - [key: string]: any; - }, - configsCreated: boolean - ): { - [key: string]: any; - }; - static validateBuild(build: string): boolean; - static validateSdkWrapperVersion(wrapperVersion: string): boolean; - static validateEngineVersion(engineVersion: string): boolean; - static validateUserId(uId: string): boolean; - static validateShortString(shortString: string, canBeEmpty: boolean): boolean; - static validateString(s: string, canBeEmpty: boolean): boolean; - static validateLongString(longString: string, canBeEmpty: boolean): boolean; - static validateConnectionType(connectionType: string): boolean; - static validateCustomDimensions(customDimensions: Array): boolean; - static validateResourceCurrencies(resourceCurrencies: Array): boolean; - static validateResourceItemTypes(resourceItemTypes: Array): boolean; - static validateDimension01(dimension01: string, availableDimensions: Array): boolean; - static validateDimension02(dimension02: string, availableDimensions: Array): boolean; - static validateDimension03(dimension03: string, availableDimensions: Array): boolean; - static validateArrayOfStrings( - maxCount: number, - maxStringLength: number, - allowNoValues: boolean, - logTag: string, - arrayOfStrings: Array - ): boolean; - static validateClientTs(clientTs: number): boolean; - } - } -} -declare module gameanalytics { - module device { - class NameValueVersion { - name: string; - value: string; - version: string; - constructor(name: string, value: string, version: string); - } - class NameVersion { - name: string; - version: string; - constructor(name: string, version: string); - } - class GADevice { - private static readonly sdkWrapperVersion; - private static readonly osVersionPair; - static readonly buildPlatform: string; - static readonly deviceModel: string; - static readonly deviceManufacturer: string; - static readonly osVersion: string; - static readonly browserVersion: string; - static sdkGameEngineVersion: string; - static gameEngineVersion: string; - private static connectionType; - static touch(): void; - static getRelevantSdkVersion(): string; - static getConnectionType(): string; - static updateConnectionType(): void; - private static getOSVersionString; - private static runtimePlatformToString; - private static getBrowserVersionString; - private static getDeviceModel; - private static getDeviceManufacturer; - private static matchItem; - } - } -} -declare module gameanalytics { - module threading { - class TimedBlock { - readonly deadline: Date; - block: () => void; - readonly id: number; - ignore: boolean; - async: boolean; - running: boolean; - private static idCounter; - constructor(deadline: Date); - } - } -} -declare module gameanalytics { - module threading { - interface IComparer { - compare(x: T, y: T): number; - } - class PriorityQueue { - _subQueues: { - [key: number]: Array; - }; - _sortedKeys: Array; - private comparer; - constructor(priorityComparer: IComparer); - enqueue(priority: number, item: TItem): void; - private addQueueOfPriority; - peek(): TItem; - hasItems(): boolean; - dequeue(): TItem; - private dequeueFromHighPriorityQueue; - } - } -} -declare module gameanalytics { - module store { - enum EGAStoreArgsOperator { - Equal = 0, - LessOrEqual = 1, - NotEqual = 2, - } - enum EGAStore { - Events = 0, - Sessions = 1, - Progression = 2, - } - class GAStore { - private static readonly instance; - private static storageAvailable; - private static readonly MaxNumberOfEntries; - private eventsStore; - private sessionsStore; - private progressionStore; - private storeItems; - private static readonly StringFormat; - private static readonly KeyFormat; - private static readonly EventsStoreKey; - private static readonly SessionsStoreKey; - private static readonly ProgressionStoreKey; - private static readonly ItemsStoreKey; - private constructor(); - static isStorageAvailable(): boolean; - static isStoreTooLargeForEvents(): boolean; - static select( - store: EGAStore, - args?: Array<[string, EGAStoreArgsOperator, any]>, - sort?: boolean, - maxCount?: number - ): Array<{ - [key: string]: any; - }>; - static update( - store: EGAStore, - setArgs: Array<[string, any]>, - whereArgs?: Array<[string, EGAStoreArgsOperator, any]> - ): boolean; - static delete(store: EGAStore, args: Array<[string, EGAStoreArgsOperator, any]>): void; - static insert( - store: EGAStore, - newEntry: { - [key: string]: any; - }, - replace?: boolean, - replaceKey?: string - ): void; - static save(gameKey: string): void; - static load(gameKey: string): void; - static setItem(gameKey: string, key: string, value: string): void; - static getItem(gameKey: string, key: string): string; - private static getStore; - } - } -} -declare module gameanalytics { - module state { - class GAState { - private static readonly CategorySdkError; - private static readonly MAX_CUSTOM_FIELDS_COUNT; - private static readonly MAX_CUSTOM_FIELDS_KEY_LENGTH; - private static readonly MAX_CUSTOM_FIELDS_VALUE_STRING_LENGTH; - static readonly instance: GAState; - private constructor(); - private userId; - static setUserId(userId: string): void; - private identifier; - static getIdentifier(): string; - private initialized; - static isInitialized(): boolean; - static setInitialized(value: boolean): void; - sessionStart: number; - static getSessionStart(): number; - private sessionNum; - static getSessionNum(): number; - private transactionNum; - static getTransactionNum(): number; - sessionId: string; - static getSessionId(): string; - private currentCustomDimension01; - static getCurrentCustomDimension01(): string; - private currentCustomDimension02; - static getCurrentCustomDimension02(): string; - private currentCustomDimension03; - static getCurrentCustomDimension03(): string; - private gameKey; - static getGameKey(): string; - private gameSecret; - static getGameSecret(): string; - private availableCustomDimensions01; - static getAvailableCustomDimensions01(): Array; - static setAvailableCustomDimensions01(value: Array): void; - private availableCustomDimensions02; - static getAvailableCustomDimensions02(): Array; - static setAvailableCustomDimensions02(value: Array): void; - private availableCustomDimensions03; - static getAvailableCustomDimensions03(): Array; - static setAvailableCustomDimensions03(value: Array): void; - private availableResourceCurrencies; - static getAvailableResourceCurrencies(): Array; - static setAvailableResourceCurrencies(value: Array): void; - private availableResourceItemTypes; - static getAvailableResourceItemTypes(): Array; - static setAvailableResourceItemTypes(value: Array): void; - private build; - static getBuild(): string; - static setBuild(value: string): void; - private useManualSessionHandling; - static getUseManualSessionHandling(): boolean; - private _isEventSubmissionEnabled; - static isEventSubmissionEnabled(): boolean; - sdkConfigCached: { - [key: string]: any; - }; - private configurations; - private remoteConfigsIsReady; - private remoteConfigsListeners; - initAuthorized: boolean; - clientServerTimeOffset: number; - configsHash: string; - abId: string; - static getABTestingId(): string; - abVariantId: string; - static getABTestingVariantId(): string; - private defaultUserId; - private setDefaultId; - static getDefaultId(): string; - sdkConfigDefault: { - [key: string]: string; - }; - sdkConfig: { - [key: string]: any; - }; - static getSdkConfig(): { - [key: string]: any; - }; - private progressionTries; - static readonly DefaultUserIdKey: string; - static readonly SessionNumKey: string; - static readonly TransactionNumKey: string; - private static readonly Dimension01Key; - private static readonly Dimension02Key; - private static readonly Dimension03Key; - static readonly SdkConfigCachedKey: string; - static isEnabled(): boolean; - static setCustomDimension01(dimension: string): void; - static setCustomDimension02(dimension: string): void; - static setCustomDimension03(dimension: string): void; - static incrementSessionNum(): void; - static incrementTransactionNum(): void; - static incrementProgressionTries(progression: string): void; - static getProgressionTries(progression: string): number; - static clearProgressionTries(progression: string): void; - static setKeys(gameKey: string, gameSecret: string): void; - static setManualSessionHandling(flag: boolean): void; - static setEnabledEventSubmission(flag: boolean): void; - static getEventAnnotations(): { - [key: string]: any; - }; - static getSdkErrorEventAnnotations(): { - [key: string]: any; - }; - static getInitAnnotations(): { - [key: string]: any; - }; - static getClientTsAdjusted(): number; - static sessionIsStarted(): boolean; - private static cacheIdentifier; - static ensurePersistedStates(): void; - static calculateServerTimeOffset(serverTs: number): number; - static validateAndCleanCustomFields(fields: { - [id: string]: any; - }): { - [id: string]: any; - }; - static validateAndFixCurrentDimensions(): void; - static getConfigurationStringValue(key: string, defaultValue: string): string; - static isRemoteConfigsReady(): boolean; - static addRemoteConfigsListener(listener: { onRemoteConfigsUpdated: () => void }): void; - static removeRemoteConfigsListener(listener: { onRemoteConfigsUpdated: () => void }): void; - static getRemoteConfigsContentAsString(): string; - static populateConfigurations(sdkConfig: { [key: string]: any }): void; - } - } -} -declare module gameanalytics { - module tasks { - class SdkErrorTask { - private static readonly MaxCount; - private static readonly countMap; - private static readonly timestampMap; - static execute(url: string, type: string, payloadData: string, secretKey: string): void; - } - } -} -declare module gameanalytics { - module http { - import EGASdkErrorCategory = gameanalytics.events.EGASdkErrorCategory; - import EGASdkErrorArea = gameanalytics.events.EGASdkErrorArea; - import EGASdkErrorAction = gameanalytics.events.EGASdkErrorAction; - import EGASdkErrorParameter = gameanalytics.events.EGASdkErrorParameter; - class GAHTTPApi { - static readonly instance: GAHTTPApi; - private protocol; - private hostName; - private version; - private remoteConfigsVersion; - private baseUrl; - private remoteConfigsBaseUrl; - private initializeUrlPath; - private eventsUrlPath; - private useGzip; - private static readonly MAX_ERROR_MESSAGE_LENGTH; - private constructor(); - requestInit( - configsHash: string, - callback: ( - response: EGAHTTPApiResponse, - json: { - [key: string]: any; - } - ) => void - ): void; - sendEventsInArray( - eventArray: Array<{ - [key: string]: any; - }>, - requestId: string, - callback: ( - response: EGAHTTPApiResponse, - json: { - [key: string]: any; - }, - requestId: string, - eventCount: number - ) => void - ): void; - sendSdkErrorEvent( - category: EGASdkErrorCategory, - area: EGASdkErrorArea, - action: EGASdkErrorAction, - parameter: EGASdkErrorParameter, - reason: string, - gameKey: string, - secretKey: string - ): void; - private static sendEventInArrayRequestCallback; - private static sendRequest; - private static initRequestCallback; - private createPayloadData; - private processRequestResponse; - private static sdkErrorCategoryString; - private static sdkErrorAreaString; - private static sdkErrorActionString; - private static sdkErrorParameterString; - } - } -} -declare module gameanalytics { - module events { - class GAEvents { - private static readonly CategorySessionStart; - private static readonly CategorySessionEnd; - private static readonly CategoryDesign; - private static readonly CategoryBusiness; - private static readonly CategoryProgression; - private static readonly CategoryResource; - private static readonly CategoryError; - private static readonly MaxEventCount; - private constructor(); - static addSessionStartEvent(): void; - static addSessionEndEvent(): void; - static addBusinessEvent( - currency: string, - amount: number, - itemType: string, - itemId: string, - cartType: string, - fields: { - [id: string]: any; - } - ): void; - static addResourceEvent( - flowType: EGAResourceFlowType, - currency: string, - amount: number, - itemType: string, - itemId: string, - fields: { - [id: string]: any; - } - ): void; - static addProgressionEvent( - progressionStatus: EGAProgressionStatus, - progression01: string, - progression02: string, - progression03: string, - score: number, - sendScore: boolean, - fields: { - [id: string]: any; - } - ): void; - static addDesignEvent( - eventId: string, - value: number, - sendValue: boolean, - fields: { - [id: string]: any; - } - ): void; - static addErrorEvent( - severity: EGAErrorSeverity, - message: string, - fields: { - [id: string]: any; - } - ): void; - static processEvents(category: string, performCleanUp: boolean): void; - private static processEventsCallback; - private static cleanupEvents; - private static fixMissingSessionEndEvents; - private static addEventToStore; - private static updateSessionStore; - private static addDimensionsToEvent; - private static addFieldsToEvent; - private static resourceFlowTypeToString; - private static progressionStatusToString; - private static errorSeverityToString; - } - } -} -declare module gameanalytics { - module threading { - class GAThreading { - private static readonly instance; - readonly blocks: PriorityQueue; - private readonly id2TimedBlockMap; - private static runTimeoutId; - private static readonly ThreadWaitTimeInMs; - private static ProcessEventsIntervalInSeconds; - private keepRunning; - private isRunning; - private constructor(); - static createTimedBlock(delayInSeconds?: number): TimedBlock; - static performTaskOnGAThread(taskBlock: () => void, delayInSeconds?: number): void; - static performTimedBlockOnGAThread(timedBlock: TimedBlock): void; - static scheduleTimer(interval: number, callback: () => void): number; - static getTimedBlockById(blockIdentifier: number): TimedBlock; - static ensureEventQueueIsRunning(): void; - static endSessionAndStopQueue(): void; - static stopEventQueue(): void; - static ignoreTimer(blockIdentifier: number): void; - static setEventProcessInterval(interval: number): void; - private addTimedBlock; - private static run; - private static startThread; - private static getNextBlock; - private static processEventQueue; - } - } -} -declare module gameanalytics { - class GameAnalytics { - private static initTimedBlockId; - static methodMap: { - [id: string]: (...args: any[]) => void; - }; - static init(): void; - static gaCommand(...args: any[]): void; - static configureAvailableCustomDimensions01(customDimensions?: Array): void; - static configureAvailableCustomDimensions02(customDimensions?: Array): void; - static configureAvailableCustomDimensions03(customDimensions?: Array): void; - static configureAvailableResourceCurrencies(resourceCurrencies?: Array): void; - static configureAvailableResourceItemTypes(resourceItemTypes?: Array): void; - static configureBuild(build?: string): void; - static configureSdkGameEngineVersion(sdkGameEngineVersion?: string): void; - static configureGameEngineVersion(gameEngineVersion?: string): void; - static configureUserId(uId?: string): void; - static initialize(gameKey?: string, gameSecret?: string): void; - static addBusinessEvent( - currency?: string, - amount?: number, - itemType?: string, - itemId?: string, - cartType?: string - ): void; - static addResourceEvent( - flowType?: EGAResourceFlowType, - currency?: string, - amount?: number, - itemType?: string, - itemId?: string - ): void; - static addProgressionEvent( - progressionStatus?: EGAProgressionStatus, - progression01?: string, - progression02?: string, - progression03?: string, - score?: any - ): void; - static addDesignEvent(eventId: string, value?: any): void; - static addErrorEvent(severity?: EGAErrorSeverity, message?: string): void; - static setEnabledInfoLog(flag?: boolean): void; - static setEnabledVerboseLog(flag?: boolean): void; - static setEnabledManualSessionHandling(flag?: boolean): void; - static setEnabledEventSubmission(flag?: boolean): void; - static setCustomDimension01(dimension?: string): void; - static setCustomDimension02(dimension?: string): void; - static setCustomDimension03(dimension?: string): void; - static setEventProcessInterval(intervalInSeconds: number): void; - static startSession(): void; - static endSession(): void; - static onStop(): void; - static onResume(): void; - static getRemoteConfigsValueAsString(key: string, defaultValue?: string): string; - static isRemoteConfigsReady(): boolean; - static addRemoteConfigsListener(listener: { onRemoteConfigsUpdated: () => void }): void; - static removeRemoteConfigsListener(listener: { onRemoteConfigsUpdated: () => void }): void; - static getRemoteConfigsContentAsString(): string; - static getABTestingId(): string; - static getABTestingVariantId(): string; - private static internalInitialize; - private static newSession; - private static startNewSessionCallback; - private static resumeSessionAndStartQueue; - private static isSdkReady; - } -} -declare var GameAnalyticsCommand: typeof gameanalytics.GameAnalytics.gaCommand; -export declare var GameAnalytics: typeof gameanalytics.GameAnalytics; -export default GameAnalytics;