From 0576a70479d96e0364a92eb753647c635c2fc506 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Wed, 28 May 2025 15:38:51 -0700 Subject: [PATCH 01/20] fix alternate view regression (#937) ## Description: when pressing space for alternate view, the ships did not change color. This was cased by incorrect sprite caching. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: --- src/client/graphics/SpriteLoader.ts | 2 +- src/client/graphics/layers/UnitLayer.ts | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/client/graphics/SpriteLoader.ts b/src/client/graphics/SpriteLoader.ts index 7fb1ab852..4bc552bc1 100644 --- a/src/client/graphics/SpriteLoader.ts +++ b/src/client/graphics/SpriteLoader.ts @@ -128,7 +128,7 @@ export const getColoredSprite = ( const territoryColor = customTerritoryColor ?? theme.territoryColor(owner); const borderColor = customBorderColor ?? theme.borderColor(owner); const spawnHighlightColor = theme.spawnHighlightColor(); - const key = `${unit.type()}-${owner.id()}`; + const key = `${unit.type()}-${owner.id()}-${customTerritoryColor}-${customBorderColor}`; if (coloredSpriteCache.has(key)) { return coloredSpriteCache.get(key)!; diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 5ec98850c..8501610f8 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -4,7 +4,6 @@ import { ClientID } from "../../../core/Schemas"; import { Theme } from "../../../core/configuration/Config"; import { UnitType } from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; -import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; import { BezenhamLine } from "../../../core/utilities/Line"; import { @@ -16,6 +15,7 @@ import { MoveWarshipIntentEvent } from "../../Transport"; import { TransformHandler } from "../TransformHandler"; import { Layer } from "./Layer"; +import { GameUpdateType } from "../../../core/game/GameUpdates"; import { getColoredSprite, isSpriteReady, @@ -70,8 +70,11 @@ export class UnitLayer implements Layer { if (this.myPlayer === null) { this.myPlayer = this.game.playerByClientID(this.clientID); } + const unitIds = this.game + .updatesSinceLastTick() + ?.[GameUpdateType.Unit]?.map((unit) => unit.id); - this.updateUnitsSprites(); + this.updateUnitsSprites(unitIds ?? []); } init() { @@ -202,7 +205,7 @@ export class UnitLayer implements Layer { this.transportShipTrailCanvas.width = this.game.width(); this.transportShipTrailCanvas.height = this.game.height(); - this.updateUnitsSprites(); + this.updateUnitsSprites(this.game.units().map((unit) => unit.id())); this.unitToTrail.forEach((trail, unit) => { for (const t of trail) { @@ -218,10 +221,9 @@ export class UnitLayer implements Layer { }); } - private updateUnitsSprites() { - const unitsToUpdate = this.game - .updatesSinceLastTick() - ?.[GameUpdateType.Unit]?.map((unit) => this.game.unit(unit.id)) + private updateUnitsSprites(unitIds: number[]) { + const unitsToUpdate = unitIds + ?.map((id) => this.game.unit(id)) .filter((unit) => unit !== undefined); if (unitsToUpdate) { From 15519b95c8fc79a8d9b166c034ab02f5be0c72cd Mon Sep 17 00:00:00 2001 From: evanpelle Date: Wed, 28 May 2025 17:26:13 -0700 Subject: [PATCH 02/20] fix warship targetting range (#938) ## Description: A warship refactor caused a regressions where warships could attack at any distance. Also refactored & simplified the trade ship logic. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: --- src/core/execution/WarshipExecution.ts | 59 +++++++++++++++++--------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/core/execution/WarshipExecution.ts b/src/core/execution/WarshipExecution.ts index f5400c74d..43233bd91 100644 --- a/src/core/execution/WarshipExecution.ts +++ b/src/core/execution/WarshipExecution.ts @@ -79,28 +79,45 @@ export class WarshipExecution implements Execution { const hasPort = this.warship.owner().units(UnitType.Port).length > 0; const patrolRangeSquared = this.mg.config().warshipPatrolRange() ** 2; - const ships = this.mg - .nearbyUnits( - this.warship.patrolTile()!, - this.mg.config().warshipTargettingRange(), - [UnitType.TransportShip, UnitType.Warship, UnitType.TradeShip], - ) - .filter( - ({ unit }) => - unit.owner() !== this.warship.owner() && - unit !== this.warship && - !unit.owner().isFriendly(this.warship.owner()) && - !this.alreadySentShell.has(unit) && - (unit.type() !== UnitType.TradeShip || - (hasPort && - this.mg.euclideanDistSquared(this.warship.tile(), unit.tile()) <= - patrolRangeSquared && - unit.targetUnit()?.owner() !== this.warship.owner() && - !unit.targetUnit()?.owner().isFriendly(this.warship.owner()) && - unit.isSafeFromPirates() !== true)), - ); + const ships = this.mg.nearbyUnits( + this.warship.tile()!, + this.mg.config().warshipTargettingRange(), + [UnitType.TransportShip, UnitType.Warship, UnitType.TradeShip], + ); + const potentialTargets: { unit: Unit; distSquared: number }[] = []; + for (const { unit, distSquared } of ships) { + if ( + unit.owner() === this.warship.owner() || + unit === this.warship || + unit.owner().isFriendly(this.warship.owner()) || + this.alreadySentShell.has(unit) + ) { + continue; + } + if (unit.type() === UnitType.TradeShip) { + if ( + !hasPort || + unit.isSafeFromPirates() || + unit.targetUnit()?.owner() === this.warship.owner() || // trade ship is coming to my port + unit.targetUnit()?.owner().isFriendly(this.warship.owner()) // trade ship is coming to my ally + ) { + continue; + } + if ( + this.mg.euclideanDistSquared( + this.warship.patrolTile()!, + unit.tile(), + ) > patrolRangeSquared + ) { + // Prevent warship from chasing trade ship that is too far away from + // the patrol tile to prevent warships from wandering around the map. + continue; + } + } + potentialTargets.push({ unit: unit, distSquared }); + } - return ships.sort((a, b) => { + return potentialTargets.sort((a, b) => { const { unit: unitA, distSquared: distA } = a; const { unit: unitB, distSquared: distB } = b; From 3511bb0eb488d24ce3ec7898dc922196534261a4 Mon Sep 17 00:00:00 2001 From: Mason Schmidgall <13247733+spicydll@users.noreply.github.com> Date: Wed, 28 May 2025 22:00:31 -0500 Subject: [PATCH 03/20] Add instructional overlay message during spawn phase (#934) ## Description: My first game, I was embarrassingly confused about the spawn phase. I looked for where my nation spawned for something like 3 minutes before I realized I needed to actually click a location at the beginning. Therefore, my first contribution is to add a simple UI message during the spawn phase that will hopefully prevent anyone else from making the same mistake. I have implemented this as an overlay layer that displays at the top and center of the screen during spawn phase. ## UI Screenshots Spawn phase message ![image](https://github.com/user-attachments/assets/1d07bc51-e7eb-47d4-9ad6-8ef06404b40a) ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: spicydll --- src/client/graphics/GameRenderer.ts | 10 +++++ src/client/graphics/layers/HeadsUpMessage.ts | 46 ++++++++++++++++++++ src/client/index.html | 5 +++ 3 files changed, 61 insertions(+) create mode 100644 src/client/graphics/layers/HeadsUpMessage.ts diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 901ff166e..6fef871c0 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -13,6 +13,7 @@ import { ControlPanel } from "./layers/ControlPanel"; import { EmojiTable } from "./layers/EmojiTable"; import { EventsDisplay } from "./layers/EventsDisplay"; import { FxLayer } from "./layers/FxLayer"; +import { HeadsUpMessage } from "./layers/HeadsUpMessage"; import { Layer } from "./layers/Layer"; import { Leaderboard } from "./layers/Leaderboard"; import { MultiTabModal } from "./layers/MultiTabModal"; @@ -172,6 +173,14 @@ export function createRenderer( } playerTeamLabel.game = game; + const headsUpMessage = document.querySelector( + "heads-up-message", + ) as HeadsUpMessage; + if (!(headsUpMessage instanceof HeadsUpMessage)) { + console.error("heads-up message not found"); + } + headsUpMessage.game = game; + const unitInfoModal = document.querySelector( "unit-info-modal", ) as UnitInfoModal; @@ -220,6 +229,7 @@ export function createRenderer( topBar, playerPanel, playerTeamLabel, + headsUpMessage, unitInfoModal, multiTabModal, ]; diff --git a/src/client/graphics/layers/HeadsUpMessage.ts b/src/client/graphics/layers/HeadsUpMessage.ts new file mode 100644 index 000000000..8dd68d59a --- /dev/null +++ b/src/client/graphics/layers/HeadsUpMessage.ts @@ -0,0 +1,46 @@ +import { LitElement, html } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { GameView } from "../../../core/game/GameView"; +import { Layer } from "./Layer"; + +@customElement("heads-up-message") +export class HeadsUpMessage extends LitElement implements Layer { + public game: GameView; + + @state() + private isVisible = false; + + createRenderRoot() { + return this; + } + + init() { + this.isVisible = true; + this.requestUpdate(); + } + + tick() { + if (!this.game.inSpawnPhase()) { + this.isVisible = false; + this.requestUpdate(); + } + } + + render() { + if (!this.isVisible) { + return html``; + } + + return html` +
e.preventDefault()} + > + Choose a starting location +
+ `; + } +} diff --git a/src/client/index.html b/src/client/index.html index 3e15ba515..dc635b838 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -299,6 +299,11 @@ +
+ +
From 1167ac80d74bb6fd963b28a38d8d844448917e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9odore=20L=C3=A9on?= Date: Thu, 29 May 2025 05:01:17 +0200 Subject: [PATCH 04/20] Add test coverage script (#929) ## Description: Added a command "npm run test:coverage" This could be added to the CI/CD to check if a pull request induced regressions, and if it added propers tests. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: theodoreleon.aetarax --- .gitignore | 1 + jest.config.ts | 10 ++++++++++ package.json | 1 + 3 files changed, 12 insertions(+) diff --git a/.gitignore b/.gitignore index 90ce6e054..0cbd366d8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build/ node_modules/ out/ static/ +coverage/ TODO.txt resources/images/.DS_Store resources/.DS_Store diff --git a/jest.config.ts b/jest.config.ts index 02f05ddd8..bb15e1770 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -17,4 +17,14 @@ export default { }, transformIgnorePatterns: ["node_modules/(?!(node:)/)"], preset: "ts-jest/presets/default-esm", + collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"], + coverageThreshold: { + global: { + branches: 0, + functions: 0, + lines: 0, + statements: 0, + }, + }, + coverageReporters: ["text", "lcov", "html"], }; diff --git a/package.json b/package.json index 2e2d02381..eedac982d 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dev": "cross-env GAME_ENV=dev concurrently \"npm run start:client\" \"npm run start:server-dev\"", "tunnel": "npm run build-prod && npm run start:server", "test": "jest", + "test:coverage": "jest --coverage", "format": "prettier --ignore-unknown --write .", "lint": "eslint", "lint:fix": "eslint --fix", From 4443459bc520278ecb06830b25981a48c3ef6a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9odore=20L=C3=A9on?= Date: Thu, 29 May 2025 05:01:51 +0200 Subject: [PATCH 05/20] Added two checkboxes to the default pull request template (#930) ## Description: Add the following checks to the pull request template : - [ ] I have added relevant tests to the test directory - [ ] I process any text displayed to the user through translateText() and i've added it to the en.json file/i ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I have added relevant tests to the test directory - [x] I process any text displayed to the user through translateText() and i've added it to the en.json file - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: theodoreleon.aetarax --- .github/PULL_REQUEST_TEMPLATE.md | 2 ++ .github/workflows/pr-description.yml | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ce91d5ba5..c7c8c0124 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,6 +3,8 @@ ## Please complete the following: - [ ] I have added screenshots for all UI updates +- [ ] I process any text displayed to the user through translateText() and I've added it to the en.json file +- [ ] I have added relevant tests to the test directory - [ ] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [ ] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index 864bca7a8..e38ba5037 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -24,9 +24,11 @@ jobs: errors.push('❌ Missing or short `## Description:` section.'); } - // Check all three boxes are checked + // Check all five boxes are checked const requiredBoxes = [ /- \[x\] I have added screenshots for all UI updates/i, + /- \[x\] I process any text displayed to the user through translateText\(\) and I\'ve added it to the en\.json file/i, + /- \[x\] I have added relevant tests to the test directory/i, /- \[x\] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced/i, /- \[x\] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors/i ]; From 46252176d81ada9fbb10a42c5dd9a6909b2d520e Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 29 May 2025 09:34:27 -0700 Subject: [PATCH 06/20] update privacy policy for playwire ads --- resources/privacy-policy.html | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/resources/privacy-policy.html b/resources/privacy-policy.html index 121539400..efb7b2f78 100644 --- a/resources/privacy-policy.html +++ b/resources/privacy-policy.html @@ -59,7 +59,7 @@

Privacy Policy

-

Last Updated: April 29, 2025

+

Last Updated: May 29, 2025

This Privacy Policy describes Our policies and procedures on the @@ -598,6 +598,18 @@

  • By email: openfrontio@gmail.com
  • +

    Advertising

    +

    + All or partial advertising on this Website or App is managed by Playwire + LLC. If Playwire publisher advertising services are used, Playwire LLC may + collect and use certain aggregated and anonymized data for advertising + purposes. To learn more about the types of data collected, how data is + used and your choices as a user, please visit + https://www.playwire.com/privacy-policy. +

    + From 535df61ffd594505cca8a11e08dc3e42e3457b44 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 29 May 2025 11:07:04 -0700 Subject: [PATCH 08/20] add robots.txt --- resources/robots.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 resources/robots.txt diff --git a/resources/robots.txt b/resources/robots.txt new file mode 100644 index 000000000..14267e903 --- /dev/null +++ b/resources/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / \ No newline at end of file From 5e7bfb2708e267127565278130ec8b7851eddaad Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 29 May 2025 11:33:42 -0700 Subject: [PATCH 09/20] add steam wishlist link on win modal --- resources/lang/en.json | 3 ++- src/client/graphics/layers/WinModal.ts | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index 9d492f0a4..7f4bd92d0 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -359,7 +359,8 @@ "you_won": "You Won!", "other_won": "{player} has won!", "exit": "Exit Game", - "keep": "Keep Playing" + "keep": "Keep Playing", + "wishlist": "Wishlist on Steam!" }, "leaderboard": { "title": "Leaderboard", diff --git a/src/client/graphics/layers/WinModal.ts b/src/client/graphics/layers/WinModal.ts index ce0739e50..ce705513d 100644 --- a/src/client/graphics/layers/WinModal.ts +++ b/src/client/graphics/layers/WinModal.ts @@ -148,7 +148,24 @@ export class WinModal extends LitElement implements Layer { } innerHtml() { - return html``; + return html`

    + + ${translateText("win_modal.wishlist")} + +

    `; } show() { From 32d746768e942230a65001ca34ad9dc6b7a2a535 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 29 May 2025 14:57:58 -0700 Subject: [PATCH 10/20] Fix slow singleplayer timer (#943) ## Description: The LocalServer was counting 100ms between turns, causing the timer to run slow (100ms + turn execution time), it now checks 100ms from the start of the previous turn. I've noticed it still runs a tad slow (1-2 seconds slow after 1 minute), but it's much better than before. ## Please complete the following: - [ ] I have added screenshots for all UI updates - [ ] I process any text displayed to the user through translateText() and I've added it to the en.json file - [ ] I have added relevant tests to the test directory - [ ] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [ ] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: --- src/client/LocalServer.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/client/LocalServer.ts b/src/client/LocalServer.ts index 4d7be1a1c..c3982510f 100644 --- a/src/client/LocalServer.ts +++ b/src/client/LocalServer.ts @@ -30,7 +30,7 @@ export class LocalServer { private allPlayersStats: AllPlayersStats = {}; private turnsExecuted = 0; - private lastTurnCompletedTime = 0; + private turnStartTime = 0; private turnCheckInterval: NodeJS.Timeout; @@ -47,9 +47,10 @@ export class LocalServer { if ( this.isReplay || Date.now() > - this.lastTurnCompletedTime + - this.lobbyConfig.serverConfig.turnIntervalMs() + this.turnStartTime + this.lobbyConfig.serverConfig.turnIntervalMs() ) { + this.turnStartTime = Date.now(); + // End turn on the server means the client will start processing the turn. this.endTurn(); } } @@ -140,11 +141,13 @@ export class LocalServer { } } + // This is so the client can tell us when it finished processing the turn. public turnComplete() { this.turnsExecuted++; - this.lastTurnCompletedTime = Date.now(); } + // endTurn in this context means the server has collected all the intents + // and will send the turn to the client. private endTurn() { if (this.paused) { return; From 9471fdaf1f60ad14b601a5b80e4ba8d4f16938e0 Mon Sep 17 00:00:00 2001 From: falc <76709589+falcolnic@users.noreply.github.com> Date: Fri, 30 May 2025 00:16:35 +0200 Subject: [PATCH 11/20] improved perfomance of PseudoRandom (#933) ## Description: Added XorShift algo for better random number generation ## Please complete the following: - [x] I have added screenshots for all UI updates - [ ] I have added relevant tests to the test directory - [x] I process any text displayed to the user through translateText() and i've added it to the en.json file - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: @qqkedsi ![Screenshot from 2025-05-29 00-25-20](https://github.com/user-attachments/assets/7e748c7f-3bc2-4275-8ffd-9adf3a224064) --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/core/PseudoRandom.ts | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/core/PseudoRandom.ts b/src/core/PseudoRandom.ts index daf27497f..406cfb243 100644 --- a/src/core/PseudoRandom.ts +++ b/src/core/PseudoRandom.ts @@ -9,6 +9,9 @@ export class PseudoRandom { private c: number = 12345; private state: number; + private static readonly POW36_8 = Math.pow(36, 8); // Pre-compute 36^8 + private static readonly INV_2_32 = 1 / 4294967296; // 1 / 2^32 for float conversion + constructor(seed: number) { // Initialize the XorShift state with seed this.state0 = seed | 0; // Force to 32-bit integer with bitwise OR @@ -48,6 +51,13 @@ export class PseudoRandom { return (this.state0 + this.state1) | 0; } + /** + * Optimized version that directly returns unsigned 32-bit integer + */ + private _nextUInt32(): number { + return this._nextIntInternal() >>> 0; + } + /** * Generates the next pseudorandom number. * @returns A number between 0 (inclusive) and 1 (exclusive). @@ -55,7 +65,7 @@ export class PseudoRandom { next(): number { // Get a 32-bit integer and convert to [0,1) range // Using >>> 0 to get unsigned interpretation (positive number) - const int = this._nextIntInternal() >>> 0; + const int = this._nextUInt32(); // Update the state variable to maintain compatibility with original interface this.state = int % this.m; @@ -64,25 +74,33 @@ export class PseudoRandom { return this.state / this.m; } + /** + * Optimized version for internal use - directly converts to [0,1) without state update + */ + private _nextFloat(): number { + return this._nextUInt32() * PseudoRandom.INV_2_32; + } + /** * Generates a random integer between min (inclusive) and max (exclusive). */ nextInt(min: number, max: number): number { - return Math.floor(this.next() * (max - min) + min); + // keep max exclusive, min inclusive – round down to get an int + return Math.floor(this._nextFloat() * (max - min)) + min; } /** * Generates a random float between min (inclusive) and max (exclusive). */ nextFloat(min: number, max: number): number { - return this.next() * (max - min) + min; + return this._nextFloat() * (max - min) + min; } /** * Generates a random ID (8 characters, alphanumeric). */ nextID(): string { - return this.nextInt(0, Math.pow(36, 8)) // 36^8 possibilities + return Math.floor(this._nextFloat() * PseudoRandom.POW36_8) // 36^8 possibilities .toString(36) // Convert to base36 (0-9 and a-z) .padStart(8, "0"); // Ensure 8 chars by padding with zeros } @@ -94,25 +112,25 @@ export class PseudoRandom { if (arr.length === 0) { throw new Error("array must not be empty"); } - return arr[this.nextInt(0, arr.length)]; + return arr[Math.floor(this._nextFloat() * arr.length)]; } /** * Returns true with probability 1/odds. */ chance(odds: number): boolean { - return this.nextInt(0, odds) === 0; + return Math.floor(this._nextFloat() * odds) === 0; } /** * Returns a shuffled copy of the array using Fisher-Yates algorithm. */ shuffleArray(array: T[]): T[] { - for (let i = array.length - 1; i >= 0; i--) { - const j = this.nextInt(0, i + 1); - [array[i], array[j]] = [array[j], array[i]]; + const result = [...array]; + for (let i = result.length - 1; i >= 0; i--) { + const j = Math.floor(this._nextFloat() * (i + 1)); + [result[i], result[j]] = [result[j], result[i]]; } - - return array; + return result; } } From fff0f00e226dfe02f183b84a64762c68975761af Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Thu, 29 May 2025 19:13:35 -0400 Subject: [PATCH 12/20] Change deploy concurrency group (#944) ## Description: Change deploy concurrency group ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com> --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3d32eb87a..58fd0819a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -37,7 +37,7 @@ on: permissions: {} concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.event_name == 'workflow_dispatch' && inputs.target_host || 'staging' }} cancel-in-progress: false jobs: From a1a4b22351197d0377b198da9261073784089f6e Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Thu, 29 May 2025 19:47:28 -0400 Subject: [PATCH 13/20] Set singleplayer gitCommit in the client (#945) ## Description: Set singleplayer gitCommit in the client ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors --------- Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/core/Schemas.ts | 4 +++- src/core/Util.ts | 2 +- src/server/Worker.ts | 20 ++++++++++++++------ webpack.config.js | 7 +++++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index fb67b0bad..c7030515d 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -446,10 +446,12 @@ export const GameEndInfoSchema = GameStartInfoSchema.extend({ }); export type GameEndInfo = z.infer; +const GitCommitSchema = z.string().regex(/^[0-9a-fA-F]{40}$/); + export const AnalyticsRecordSchema = z.object({ info: GameEndInfoSchema, version: z.literal("v0.0.2"), - gitCommit: z.string(), + gitCommit: GitCommitSchema, }); export type AnalyticsRecord = z.infer; diff --git a/src/core/Util.ts b/src/core/Util.ts index b518f5f1a..d78f7091d 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -195,7 +195,7 @@ export function createGameRecord( ): GameRecord { const duration = Math.floor((end - start) / 1000); const version = "v0.0.2"; - const gitCommit = ""; + const gitCommit = process.env.GIT_COMMIT ?? "unknown"; const num_turns = allTurns.length; const turns = allTurns.filter( (t) => t.intents.length !== 0 || t.hash !== undefined, diff --git a/src/server/Worker.ts b/src/server/Worker.ts index 5ff087242..e9fb2131a 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -5,10 +5,16 @@ import ipAnonymize from "ip-anonymize"; import path from "path"; import { fileURLToPath } from "url"; import { WebSocket, WebSocketServer } from "ws"; +import { z } from "zod/v4"; import { GameEnv } from "../core/configuration/Config"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; import { GameType } from "../core/game/Game"; -import { ClientMessageSchema, GameConfig, GameRecord } from "../core/Schemas"; +import { + ClientMessageSchema, + GameConfig, + GameRecord, + GameRecordSchema, +} from "../core/Schemas"; import { archive, readGameRecord } from "./Archive"; import { Client } from "./Client"; import { GameManager } from "./GameManager"; @@ -241,13 +247,15 @@ export function startWorker() { app.post( "/api/archive_singleplayer_game", gatekeeper.httpHandler(LimiterType.Post, async (req, res) => { - const gameRecord: GameRecord = req.body; - - if (!gameRecord) { - log.info("game record not found in request"); - res.status(404).json({ error: "Game record not found" }); + const result = GameRecordSchema.safeParse(req.body); + if (!result.success) { + const error = z.prettifyError(result.error); + log.info(error); + res.status(400).json({ error }); return; } + + const gameRecord: GameRecord = result.data; archive(gameRecord); res.json({ success: true, diff --git a/webpack.config.js b/webpack.config.js index 4b4096141..3f6d28936 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,3 +1,4 @@ +import { execSync } from "child_process"; import CopyPlugin from "copy-webpack-plugin"; import ESLintPlugin from "eslint-webpack-plugin"; import HtmlWebpackPlugin from "html-webpack-plugin"; @@ -8,6 +9,9 @@ import webpack from "webpack"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +const gitCommit = + process.env.GIT_COMMIT ?? execSync("git rev-parse HEAD").toString().trim(); + export default async (env, argv) => { const isProduction = argv.mode === "production"; @@ -116,9 +120,8 @@ export default async (env, argv) => { "process.env.WEBSOCKET_URL": JSON.stringify( isProduction ? "" : "localhost:3000", ), - }), - new webpack.DefinePlugin({ "process.env.GAME_ENV": JSON.stringify(isProduction ? "prod" : "dev"), + "process.env.GIT_COMMIT": JSON.stringify(gitCommit), }), new CopyPlugin({ patterns: [ From 5b42e746a06c55fd227a7bafa71ccdafa45ef578 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 29 May 2025 17:14:49 -0700 Subject: [PATCH 14/20] generate unique env file for each deployment to prevent conflicts --- deploy.sh | 10 +++++++--- update.sh | 28 +++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/deploy.sh b/deploy.sh index 48c9cc5f5..dd8210778 100755 --- a/deploy.sh +++ b/deploy.sh @@ -171,8 +171,12 @@ if [ $? -ne 0 ]; then exit 1 fi +# Generate a random filename for the environment file to prevent conflicts +# when multiple deployments are happening at the same time. +ENV_FILE="${REMOTE_UPDATE_PATH}/${SUBDOMAIN}-${RANDOM}.env" + ssh -i $SSH_KEY $REMOTE_USER@$SERVER_HOST "chmod +x $REMOTE_UPDATE_SCRIPT && \ -cat > $REMOTE_UPDATE_PATH/.env << 'EOL' +cat > $ENV_FILE << 'EOL' GAME_ENV=$ENV ENV=$ENV HOST=$HOST @@ -192,8 +196,8 @@ OTEL_ENDPOINT=$OTEL_ENDPOINT BASIC_AUTH_USER=$BASIC_AUTH_USER BASIC_AUTH_PASS=$BASIC_AUTH_PASS EOL -chmod 600 $REMOTE_UPDATE_PATH/.env && \ -$REMOTE_UPDATE_SCRIPT" +chmod 600 $ENV_FILE && \ +$REMOTE_UPDATE_SCRIPT $ENV_FILE" if [ $? -ne 0 ]; then echo "❌ Failed to execute update script on server." diff --git a/update.sh b/update.sh index 91c516346..761c353f9 100755 --- a/update.sh +++ b/update.sh @@ -2,12 +2,25 @@ # update.sh - Script to update Docker container on Hetzner server # Called by deploy.sh after uploading Docker image to Docker Hub -# Load environment variables if .env exists -if [ -f /home/openfront/.env ]; then - echo "Loading environment variables from .env file..." - export $(grep -v '^#' /home/openfront/.env | xargs) +# Check if environment file is provided +if [ $# -ne 1 ]; then + echo "Error: Environment file path is required" + echo "Usage: $0 " + exit 1 fi +ENV_FILE="$1" + +# Check if environment file exists +if [ ! -f "$ENV_FILE" ]; then + echo "Error: Environment file '$ENV_FILE' not found" + exit 1 +fi + +# Load environment variables from the provided file +echo "Loading environment variables from $ENV_FILE..." +export $(grep -v '^#' "$ENV_FILE" | xargs) + echo "======================================================" echo "🔄 UPDATING SERVER: ${HOST} ENVIRONMENT" echo "======================================================" @@ -47,7 +60,7 @@ fi echo "Starting new container for ${HOST} environment..." docker run -d \ --restart="${RESTART}" \ - --env-file /home/openfront/.env \ + --env-file "$ENV_FILE" \ --name "${CONTAINER_NAME}" \ "${DOCKER_IMAGE}" @@ -60,6 +73,11 @@ if [ $? -eq 0 ]; then docker image prune -a -f docker container prune -f echo "Cleanup complete." + + # Remove the environment file + echo "Removing environment file ${ENV_FILE}..." + rm -f "$ENV_FILE" + echo "Environment file removed." else echo "Failed to start container" exit 1 From 682918732a7d178ecc47ca7ba64a59be9729955a Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 29 May 2025 17:19:55 -0700 Subject: [PATCH 15/20] remove ads.txt --- resources/ads.txt | 999 ---------------------------------------------- 1 file changed, 999 deletions(-) delete mode 100644 resources/ads.txt diff --git a/resources/ads.txt b/resources/ads.txt deleted file mode 100644 index a5b191562..000000000 --- a/resources/ads.txt +++ /dev/null @@ -1,999 +0,0 @@ -ownerdomain=openfront.io -managerdomain=adinplay.com -#V 01.04.2025 VH -#V - - -#----------------------------------------------------------------------------# -# . # -# .o8 # -# oooo ooo .ooooo. ooo. .oo. .oooo. .o888oo oooo oooo .oooo.o # -# `88. .8' d88' `88b `888P"Y88b `P )88b 888 `888 `888 d88( "8 # -# `88..8' 888ooo888 888 888 .oP"888 888 888 888 `"Y88b. # -# `888' 888 . 888 888 d8( 888 888 . 888 888 o. )88b # -# `8' `Y8bod8P' o888o o888o `Y888""8o "888" `V88V"V8P' 8""888P' # -# # -# The leading advertising solution for gaming and entertainment # -# # -# To become a publisher or advertise please contact info@venatus.com # -# # -#----------------------------------------------------------------------------# -adagio.io, 1090, DIRECT -rubiconproject.com, 19116, RESELLER, 0bfd66d529a55807 -pubmatic.com, 159110, RESELLER, 5d62403b186f2ace -lijit.com, 367236, RESELLER, fafdf38b16bf6b2b -improvedigital.com, 1790, RESELLER -triplelift.com, 13482, RESELLER, 6c33edb13117fd86 -rubiconproject.com, 12186, RESELLER, 0bfd66d529a55807 -video.unrulymedia.com, 5672421953199218469, RESELLER -amxrtb.com, 105199358, DIRECT -amxrtb.com, 105199778, DIRECT -sharethrough.com, a6a34444, RESELLER, d53b998a7bd4ecd2 -appnexus.com, 12290, RESELLER -pubmatic.com, 158355, RESELLER, 5d62403b186f2ace -rubiconproject.com, 23844, RESELLER, 0bfd66d529a55807 -openx.com, 559680764, RESELLER, 6a698e2ec38604c6 -adform.com, 2767, RESELLER -adyoulike.com, c1314a52de718f3c214c00173d2994f9, DIRECT -pubmatic.com, 160925, RESELLER, 5d62403b186f2ace -aps.amazon.com,70247b00-ff8f-4016-b3ab-8344daf96e09,DIRECT -aniview.com, 5f2063121d82c82557194737, RESELLER, 78b21b97965ec3f8 -aniview.com, 643f8e74688b10f72307cc24, DIRECT, 78b21b97965ec3f8 -google.com, pub-6346866704322274, RESELLER, f08c47fec0942fa0 -pubmatic.com, 160993, RESELLER, 5d62403b186f2ace -rubiconproject.com, 13918, RESELLER, 0bfd66d529a55807 -google.com, pub-5717092533913515, RESELLER, f08c47fec0942fa0 -gannett.com, 22652678936, RESELLER -richaudience.com, 1ru8dKmJJV, RESELLER -sharethrough.com, zLsEa05k, RESELLER, d53b998a7bd4ecd2 -aps.amazon.com, 1ad7261b-91ea-4b6f-b9e9-b83522205b75, RESELLER -pubmatic.com, 161335, RESELLER, 5d62403b186f2ace -openx.com, 556532676, RESELLER, 6a698e2ec38604c6 -mediago.io, 045ac24b888bcf59a09731e7f0f2084f, RESELLER -blockthrough.com, 5643766199222272, DIRECT -criteo.com, B-062405, DIRECT, 9fac4a4a87c2a44f -themediagrid.com, CVQXOH, DIRECT, 35d5010d7789b49d -freewheel.tv, 211121, DIRECT -freewheel.tv, 211129-524565, DIRECT -freewheel.tv, 211129-169843, DIRECT -google.com, pub-5781531207509232, DIRECT, f08c47fec0942fa0 -google.com, pub-5781531207509232, RESELLER, f08c47fec0942fa0 -google.com, pub-2553634189837243, RESELLER, f08c47fec0942fa0 -gumgum.com, 13385, RESELLER, ffdef49475d318a9 -gumgum.com, 14302, RESELLER, ffdef49475d318a9 -rubiconproject.com, 23434, RESELLER, 0bfd66d529a55807 -pubmatic.com, 157897, RESELLER, 5d62403b186f2ace -indexexchange.com, 183921, DIRECT, 50b1c356f2c5c8fc -indexexchange.com, 193067, DIRECT, 50b1c356f2c5c8fc -indexexchange.com, 194127, DIRECT, 50b1c356f2c5c8fc -indexexchange.com, 205972, RESELLER, 50b1c356f2c5c8fc -Blis.com,33,RESELLER,61453ae19a4b73f4 -conversantmedia.com,40881,RESELLER,03113cd04947736d -insticator.com,843c9a44-60ea-4342-8ad4-68f894283b3e,DIRECT,b3511ffcafb23a32 -sharethrough.com,Q9IzHdvp,DIRECT,d53b998a7bd4ecd2 -rubiconproject.com,17062,RESELLER,0bfd66d529a55807 -risecodes.com,6124caed9c7adb0001c028d8,DIRECT -pubmatic.com,95054,DIRECT,5d62403b186f2ace -openx.com,558230700,RESELLER,6a698e2ec38604c6 -video.unrulymedia.com,136898039,RESELLER -lijit.com,257618,RESELLER,fafdf38b16bf6b2b -minutemedia.com,01garg96c88b,RESELLER -appnexus.com,3695,RESELLER,f5ab79cb980f11d1 -kargo.com, 8688, DIRECT -kueez.com,e5b6208bc94ed2d5788e1e4c1cf5452e, DIRECT -rubiconproject.com, 16920, RESELLER, 0bfd66d529a55807 -openx.com, 557564833, RESELLER, 6a698e2ec38604c6 -lijit.com, 407406, RESELLER, fafdf38b16bf6b2b #SOVRN -appnexus.com, 8826,RESELLER, f5ab79cb980f11d1 -Media.net,8CU4JTRF9, RESELLER -rubiconproject.com, 13762, RESELLER, 0bfd66d529a55807 -media.net, 8CU8ARTF8, DIRECT -Media.net, 8CU198XI2, DIRECT -themediagrid.com, LTW57M, DIRECT, 35d5010d7789b49d -ogury.com, 086233d2-e8a8-44fc-907b-f0752e1c85de, DIRECT -appnexus.com, 11470, RESELLER -openx.com, 542378302, RESELLER, 6a698e2ec38604c6 -openx.com, 540134228, RESELLER, 6a698e2ec38604c6 -openx.com, 537144009, RESELLER, 6a698e2ec38604c6 -openx.com, 560557013, RESELLER, 6a698e2ec38604c6 -optidigital.com,p230,DIRECT -pubmatic.com,158939,RESELLER,5d62403b186f2ace -rubiconproject.com,20336,RESELLER,0bfd66d529a55807 -smartadserver.com,3379,RESELLER,060d053dcf45cbf3 -triplelift.com,8183,RESELLER,6c33edb13117fd86 -the-ozone-project.com, ozoneven0005, DIRECT -openx.com, 540731760, RESELLER, 6a698e2ec38604c6 -pubmatic.com, 160557, RESELLER, 5d62403b186f2ace -themediagrid.com, WF71T3, DIRECT, 35d5010d7789b49d -Yahoo.com, 60170, DIRECT, e1a5b5b6e3255540 -pubmatic.com, 159234, RESELLER, 5d62403b186f2ace -pubmatic.com, 160552, RESELLER, 5d62403b186f2ace -pubmatic.com, 159401, RESELLER, 5d62403b186f2ace -pubmatic.com, 165533, RESELLER, 5d62403b186f2ace -richaudience.com, 1XvIoD5o0S, DIRECT -pubmatic.com, 81564, DIRECT, 5d62403b186f2ace -pubmatic.com, 156538, DIRECT, 5d62403b186f2ace -appnexus.com, 8233, DIRECT -rubiconproject.com, 13510, DIRECT -risecodes.com, 5fa94677b2db6a00015b22a9, DIRECT -pubmatic.com, 160295, RESELLER, 5d62403b186f2ace -xandr.com, 14082, RESELLER -rubiconproject.com, 23876, RESELLER, 0bfd66d529a55807 -sharethrough.com, 5926d422, RESELLER, d53b998a7bd4ecd2 -yieldmo.com, 2754490424016969782, RESELLER -media.net, 8CUQ6928Q, RESELLER -onetag.com, 69f48c2160c8113, RESELLER -amxrtb.com, 105199691, RESELLER -openx.com, 537140488, RESELLER, 6a698e2ec38604c6 -video.unrulymedia.com, 335119963, RESELLER -seedtag.com, 5aa6c80640c9e209009721e0, DIRECT -xandr.com, 4009, DIRECT, f5ab79cb980f11d1 -rubiconproject.com, 17280, DIRECT, 0bfd66d529a55807 -smartadserver.com, 3050, DIRECT -lijit.com, 397546, DIRECT, fafdf38b16bf6b2b -sharethrough.com, 31c129df, DIRECT, d53b998a7bd4ecd2 -sharethrough.com, awx1H4AI, RESELLER, d53b998a7bd4ecd2 -smaato.com, 1100055690, DIRECT, 07bcf65f187117b4 -smaato.com, 1100049216, DIRECT, 07bcf65f187117b5 -rubiconproject.com, 24600, RESELLER, 0bfd66d529a55807 -pubmatic.com, 156177, RESELLER, 5d62403b186f2ace -smartadserver.com, 3490, DIRECT -smartadserver.com, 4016, DIRECT -smartadserver.com, 4074, DIRECT -sovrn.com, 237754, DIRECT, fafdf38b16bf6b2b -lijit.com, 237754, DIRECT, fafdf38b16bf6b2b -lijit.com, 506352, DIRECT, fafdf38b16bf6b2b -teads.tv, 23348, DIRECT, 15a9c44f6d26cbe1 -triplelift.com, 6059, RESELLER, 6c33edb13117fd86 -video.unrulymedia.com, 985572675, DIRECT -video.unrulymedia.com, 985572675, RESELLER -sharethrough.com, 6qlnf8SY, RESELLER, d53b998a7bd4ecd2 -appnexus.com, 12986, RESELLER, f5ab79cb980f11d1 -improvedigital.com, 1069, RESELLER -pubmatic.com, 158056, RESELLER -Weborama.nl, 10714, DIRECT -adwmg.com, 101261, DIRECT, c9688a22012618e7 -google.com, pub-8622186303703569, DIRECT, f08c47fec0942fa0 -freewheel.tv, 1604590, DIRECT -freewheel.tv, 1604595, DIRECT -pubmatic.com, 156512, DIRECT -indexexchange.com, 183753, DIRECT -wunderkind.co, 6438, DIRECT -wunderkind.co, 6449, DIRECT -criteo.com, B-068503, DIRECT -appnexus.com, 806, DIRECT, f5ab79cb980f11d1 -appnexus.com,1908,RESELLER,f5ab79cb980f11d1 -adinplay.com, FTB, DIRECT -venatus.com, 67f90df66f43edab7e84d165, DIRECT - -################################## -# AdinPlay.com ads.txt - 2025-04-16 -################################## - -adinplay.com, OFI, DIRECT - -#Google -google.com, pub-3282547114800347, RESELLER, f08c47fec0942fa0 - -#Appnexus -appnexus.com, 8631, RESELLER, f5ab79cb980f11d1 - -#Index -indexexchange.com, 186547, RESELLER, 50b1c356f2c5c8fc -indexexchange.com, 187218, RESELLER, 50b1c356f2c5c8fc -indexexchange.com, 177754, RESELLER, 50b1c356f2c5c8fc -indexexchange.com, 196862, RESELLER, 50b1c356f2c5c8fc -indexexchange.com, 207014, RESELLER, 50b1c356f2c5c8fc - - -#Pulsepoint -contextweb.com, 561767, RESELLER, 89ff185a4c4e857c - -#Pubmatic -pubmatic.com, 156975, RESELLER, 5d62403b186f2ace -pubmatic.com, 156857, RESELLER, 5d62403b186f2ace -pubmatic.com, 162231, RESELLER, 5d62403b186f2ace - -#OpenX -openx.com, 540164985, RESELLER, 6a698e2ec38604c6 -openx.com, 540010967, RESELLER, 6a698e2ec38604c6 -openx.com, 540182293, RESELLER, 6a698e2ec38604c6 -openx.com, 556894440, RESELLER, 6a698e2ec38604c6 - - -#Sovrn -sovrn.com, 268781, RESELLER, fafdf38b16bf6b2b -lijit.com, 268781, RESELLER, fafdf38b16bf6b2b -lijit.com, 268781-eb, DIRECT, fafdf38b16bf6b2b -appnexus.com, 1360, RESELLER, f5ab79cb980f11d1 -openx.com, 538959099, RESELLER, 6a698e2ec38604c6 -openx.com, 539924617, RESELLER, 6a698e2ec38604c6 -pubmatic.com, 137711, RESELLER, 5d62403b186f2ace -pubmatic.com, 156212, RESELLER, 5d62403b186f2ace -rubiconproject.com, 17960, RESELLER, 0bfd66d529a55807 -sovrn.com, 264160, RESELLER, fafdf38b16bf6b2b -lijit.com, 264160, RESELLER, fafdf38b16bf6b2b -lijit.com, 264160-eb, DIRECT, fafdf38b16bf6b2b -smartadserver.com, 4125, RESELLER -sharethrough.com,7144eb80,RESELLER - - -#Oath -coxmt.com, 2000067907202, RESELLER -pubmatic.com, 156377, RESELLER, 5d62403b186f2ace #banner -pubmatic.com, 156078, RESELLER, 5d62403b186f2ace #banner -pubmatic.com, 155967, RESELLER, 5d62403b186f2ace #banner -openx.com, 537143344, RESELLER, 6a698e2ec38604c6 -indexexchange.com, 175407, RESELLER, 50b1c356f2c5c8fc - -#Rhythmone -rhythmone.com, 1432377581,DIRECT, a670c89d4a324e47 -rhythmone.com, 665259327, DIRECT, a670c89d4a324e47 -rhythmone.com, 2451244104, RESELLER, a670c89d4a324e47 -video.unrulymedia.com, 2451244104, RESELLER -video.unrulymedia.com, 1432377581, DIRECT - - -#Gumgum -aolcloud.net,9904,RESELLER -appnexus.com,1001,DIRECT,f5ab79cb980f11d1 -appnexus.com,2758,RESELLER,f5ab79cb980f11d1 -appnexus.com,3135,DIRECT,f5ab79cb980f11d1 -bidtellect.com,1407,RESELLER,1c34aa2d85d45e93 -contextweb.com,558355,RESELLER,89ff185a4c4e857c -openx.com,537120563,DIRECT,6a698e2ec38604c6 -openx.com,537149485,RESELLER,6a698e2ec38604c6 -google.com,pub-9557089510405422,DIRECT,f08c47fec0942fa0 -google.com,pub-3848273848634341,RESELLER,f08c47fec0942fa0 -google.com, pub-7861278482560604, RESELLER, f08c47fec0942fa0 -rhythmone.com,78519861,RESELLER, a670c89d4a324e47 -outbrain.com,01a755b08c8c22b15d46a8b753ab6955d4,RESELLER -appnexus.com,7597,RESELLER,f5ab79cb980f11d1 -openx.com,540003333,RESELLER,6a698e2ec38604c6 -33across.com,0013300001r0t9mAAA,RESELLER - - -#Amazon -aps.amazon.com,53b902f9-cf9c-4605-aec3-2c8ce65042b8,DIRECT -gumgum.com,13543,DIRECT,ffdef49475d318a9 -appnexus.com,8631,DIRECT,f5ab79cb980f11d1 -indexexchange.com,196862,DIRECT,50b1c356f2c5c8fc -pubmatic.com,160006,RESELLER,5d62403b186f2ace -pubmatic.com,160096,RESELLER,5d62403b186f2ace -rubiconproject.com,18020,RESELLER,0bfd66d529a55807 -pubmatic.com,162231,DIRECT,5d62403b186f2ace -appnexus.com,1908,RESELLER,f5ab79cb980f11d1 -smaato.com,1100044650,RESELLER,07bcf65f187117b4 -ad-generation.jp,12474,RESELLER,7f4ea9029ac04e53 -districtm.io,100962,RESELLER,3fd707be9c4527c3 -yieldmo.com,2719019867620450718,RESELLER -appnexus.com,3663,RESELLER,f5ab79cb980f11d1 -rhythmone.com,1654642120,RESELLER,a670c89d4a324e47 -yahoo.com,55029,RESELLER,e1a5b5b6e3255540 -gumgum.com,14141,RESELLER,ffdef49475d318a9 -admanmedia.com,726,RESELLER -emxdgt.com,2009,RESELLER,1e1d41537f7cad7f -appnexus.com,1356,RESELLER,f5ab79cb980f11d1 -contextweb.com,562541,RESELLER,89ff185a4c4e857c -themediagrid.com,JTQKMP,RESELLER,35d5010d7789b49d -sovrn.com,375328,RESELLER,fafdf38b16bf6b2b -lijit.com,375328,RESELLER,fafdf38b16bf6b2b -beachfront.com,14804,RESELLER,e2541279e8e2ca4d -improvedigital.com,2050,RESELLER -mintegral.com,10043,RESELLER,0aeed750c80d6423 -sonobi.com,7f5fa520f8,RESELLER,d1a215d9eb5aee9e -openx.com,556894440,DIRECT,6a698e2ec38604c6 -onetag.com,7683ebe7bee7969,DIRECT -media.net,8CUZ1MK22,RESELLER -sharethrough.com,buaxQzOE,DIRECT,d53b998a7bd4ecd2 -smartadserver.com,4571,DIRECT,060d053dcf45cbf3 -mediago.io,045ac24b888bcf59a09731e7f0f2084f,RESELLER -adyoulike.com,7463c359225e043c111036d7a29affa5,RESELLER -minutemedia.com,01gya4708ddm,RESELLER -visiblemeasures.com,1052,RESELLER -undertone.com,4205,RESELLER,d954590d0cb265b9 -admedia.com,AM1601,RESELLER,ae6c32151e71f19d -triplelift.com,8472,DIRECT,6c33edb13117fd86 -kargo.com,8824,RESELLER -start.io,123111883,RESELLER -connectad.io,455,RESELLER,85ac85a30c93b3e5 - -# 33Across -rubiconproject.com, 16414, RESELLER, 0bfd66d529a55807 #33Across #hb #tag -rubiconproject.com, 21642, RESELLER, 0bfd66d529a55807 #33Across #hb #tag #viewable -rubiconproject.com, 21434, RESELLER, 0bfd66d529a55807 #33Across #tag #ebda -rubiconproject.com, 21720, RESELLER, 0bfd66d529a55807 #33Across EU #hb #tag -pubmatic.com, 156423, RESELLER, 5d62403b186f2ace #33Across #hb #tag -pubmatic.com, 158136, RESELLER, 5d62403b186f2ace #33Across EU #hb #tag -pubmatic.com, 158569, RESELLER, 5d62403b186f2ace #33Across #tag #ebda -appnexus.com, 10239, RESELLER, f5ab79cb980f11d1 #33Across #hb #tag #viewable -appnexus.com, 1001, RESELLER, f5ab79cb980f11d1 #33Across #tag -appnexus.com, 3135, RESELLER, f5ab79cb980f11d1 #33Across #tag -openx.com, 537120563, RESELLER, 6a698e2ec38604c6 #33Across #hb #tag -openx.com, 539392223, RESELLER, 6a698e2ec38604c6 #33Across #tag #ebda -openx.com, 540995201, RESELLER, 6a698e2ec38604c6 #33Across #hb #tag #viewable -adtech.com, 12094, RESELLER #33Across #hb #tag -adtech.com, 9993, RESELLER #33Across #tag -aol.com, 47594, RESELLER, e1a5b5b6e3255540 #33Across #hb #tag #viewable -yahoo.com, 55188, DIRECT, e1a5b5b6e3255540 #33Across #tag #ebda -advangelists.com, 8d3bba7425e7c98c50f52ca1b52d3735, RESELLER, 60d26397ec060f98 #33Across #hb #tag -sonobi.com, a416546bb7, RESELLER, d1a215d9eb5aee9e #33Across #tag #ebda -indexexchange.com, 190966, RESELLER, 50b1c356f2c5c8fc #33Across #tag #ebda -indexexchange.com, 183635, RESELLER, 50b1c356f2c5c8fc #33Across #hb #tag #viewable -google.com, pub-9557089510405422, RESELLER, f08c47fec0942fa0 #33Across #tag - -#Rubiconproject -rubiconproject.com, 15636, RESELLER, 0bfd66d529a55807 - -#LockerDome -lockerdome.com, 11908041977355520, DIRECT - -#Yield Nexus -yieldnexus.com, 1, DIRECT -ssp.ynxs.io, 185, DIRECT -appnexus.com, 10617, RESELLER, f5ab79cb980f11d1 -appnexus.com, 9393, RESELLER, f5ab79cb980f11d1 -advertising.com, 25034, RESELLER -sonobi.com, 783272317b, RESELLER, d1a215d9eb5aee9e -indexexchange.com, 186684,RESELLER, 50b1c356f2c5c8fc - -#CPM -appnexus.com, 9624, RESELLER, f5ab79cb980f11d1 -adtech.com, 11506, RESELLER -yahoo.com, 56896, RESELLER -pubmatic.com, 156078, RESELLER, 5d62403b186f2ace -advertising.com, 25218, RESELLER #video, US -beachfront.com, 9065, RESELLER -contextweb.com, 559969, RESELLER, 89ff185a4c4e857c -indexexchange.com, 189455, RESELLER, 50b1c356f2c5c8fc -advertising.com, 28320, RESELLER -richaudience.com, NtMZGaQQTT, RESELLER -adform.com, 1942, RESELLER -adform.com, 1941, RESELLER -adtech.com, 4687, RESELLER -aerserv.com, 2750, RESELLER, 2ce496b9f80eb9fa -aol.com, 27093, RESELLER -aol.com, 46658, RESELLER -aolcloud.net, 4687, RESELLER -appnexus.com, 2928, RESELLER, f5ab79cb980f11d1 -contextweb.com, 560520, RESELLER, 89ff185a4c4e857c -google.com, pub-9115524111147081, RESELLER, f08c47fec0942fa0 -google.com, pub-4673227357197067, RESELLER, f08c47fec0942fa0 -indexexchange.com, 179394, RESELLER, 50b1c356f2c5c8fc -lijit.com, 249425, RESELLER, fafdf38b16bf6b2b -cpmstar.com, 49818, RESELLER -mobfox.com, 74240, RESELLER -mobfox.com, 45499, RESELLER -openx.com, 539625136, RESELLER, 6a698e2ec38604c6 -smaato.com, 1100037086, RESELLER -smaato.com, 1100000579, RESELLER -sovrn.com, 249425, RESELLER, fafdf38b16bf6b2b -openx.com, 541079309, RESELLER, 6a698e2ec38604c6 -openx.com, 541166421, RESELLER, 6a698e2ec38604c6 -contextweb.com, 562263, RESELLER, 89ff185a4c4e857c -districtm.io, 102015, RESELLER, 3fd707be9c4527c3 -lkqd.net, 304, RESELLER, 59c49fa9598a0117 -lkqd.com, 304, RESELLER, 59c49fa9598a0117 -advertising.com, 2694, RESELLER -google.com, pub-5781531207509232, RESELLER, f08c47fec0942fa0 -appnexus.com, 806, RESELLER, f5ab79cb980f11d1 -freewheel.tv, 211121, RESELLER -freewheel.tv, 211129, RESELLER -indexexchange.com, 183921, RESELLER, 50b1c356f2c5c8fc -openx.com, 540134228, RESELLER, 6a698e2ec38604c6 -openx.com, 540634629, RESELLER, 6a698e2ec38604c6 -pubmatic.com, 156715, RESELLER, 5d62403b186f2ace -rubiconproject.com, 13762, RESELLER, 0bfd66d529a55807 -smartadserver.com, 3490, RESELLER -springserve.com, 550, RESELLER, a24eb641fc82e93d -beachfront.com, 4969, RESELLER, e2541279e8e2ca4d -advertising.com, 26282, RESELLER -pubmatic.com, 157310, RESELLER, 5d62403b186f2ace -rhythmone.com, 2968119028, RESELLER, a670c89d4a324e47 -contextweb.com, 561910, RESELLER, 89ff185a4c4e857c -openx.com, 540226160, RESELLER, 6a698e2ec38604c6 -openx.com, 540255318, RESELLER, 6a698e2ec38604c6 -ssp.ynxs.io, 185, RESELLER -tremorhub.com, hpwve, RESELLER, 1a4e959a1b50034a -telaria.com, hpwve, RESELLER, 1a4e959a1b50034a -video.unrulymedia.com, UNRX-PUB-29dad46b-9bec-43c7-b950-c59d09cc8c71, RESELLER -video.unrulymedia.com, 985572675, RESELLER -rhythmone.com, 2864567592, RESELLER, a670c89d4a324e47 -vidoomy.com, 51019, RESELLER -aol.com, 22762, RESELLER -freewheel.tv, 872257, RESELLER -openx.com, 540804929, RESELLER, 6a698e2ec38604c6 -emxdgt.com, 1495, RESELLER, 1e1d41537f7cad7f - -#Rubicon -rubiconproject.com, 23042, RESELLER, 0bfd66d529a55807 -rubiconproject.com, 23044, RESELLER, 0bfd66d529a55807 - - -#AMX - -amxrtb.com, 105199469, RESELLER -appnexus.com, 12290, RESELLER, f5ab79cb980f11d1 -appnexus.com, 11786, RESELLER, f5ab79cb980f11d1 -indexexchange.com, 191503, RESELLER, 50b1c356f2c5c8fc -lijit.com, 260380, RESELLER, fafdf38b16bf6b2b -sovrn.com, 260380, RESELLER, fafdf38b16bf6b2b -pubmatic.com, 158355, RESELLER, 5d62403b186f2ace -appnexus.com, 9393, RESELLER, f5ab79cb980f11d1 #Video #Display -appnexus.com, 11924, RESELLER, f5ab79cb980f11d1 - -#Kueez -kueez.com, fe46d13305ce1b89f18a84c52275b7fe, DIRECT -appnexus.com, 8826, RESELLER -rubiconproject.com, 16920, RESELLER -openx.com, 557564833, RESELLER -lijit.com, 407406, RESELLER -media.net, 8cu4jtrf9, RESELLER -pubmatic.com, 162110, RESELLER -sharethrough.com, n98xdzel, RESELLER -33across.com, 0010b00002odu4haax, RESELLER -yieldmo.com, 3133660606033240149, RESELLER -onetag.com, 6e053d779444c00, RESELLER -video.unrulymedia.com, 3486482593, RESELLER -sonobi.com, 4c4fba1717, RESELLER -smartadserver.com, 4288, RESELLER -zetaglobal.com, 108, RESELLER -improvedigital.com, 2106, RESELLER -loopme.com, 11576, RESELLER -themediagrid.com, uot45z, RESELLER - - -#Aniview - -aniview.com, 606c5af8b82e996ca965f498, RESELLER, 78b21b97965ec3f8 -advertising.com, 23089, RESELLER -appnexus.com, 12637, RESELLER, f5ab79cb980f11d1 -appnexus.com, 9382, RESELLER, f5ab79cb980f11d1 -synacor.com, 82171, RESELLER, e108f11b2cdf7d5b -pubmatic.com, 156344, RESELLER, 5d62403b186f2ace -rubiconproject.com, 13344, RESELLER, 0bfd66d529a55807 -indexexchange.com, 191740, RESELLER, 50b1c356f2c5c8fc -conversantmedia.com, 100195, DIRECT, 03113cd04947736d -appnexus.com, 4052, RESELLER, f5ab79cb980f11d1 -contextweb.com, 561998, RESELLER, 89ff185a4c4e857c -pubmatic.com, 158100, RESELLER, 5d62403b186f2ace -yahoo.com, 55771, RESELLER, e1a5b5b6e3255540 -onetag.com, 57e618150c70d90, DIRECT -google.com, pub-3769010358500643, RESELLER, f08c47fec0942fa0 -video.unrulymedia.com, 3350674472, DIRECT -rhythmone.com, 3350674472, DIRECT, a670c89d4a324e47 -google.com, pub-4586415728471297, RESELLER, f08c47fec0942fa0 -google.com, pub-3565385483761681, DIRECT, f08c47fec0942fa0 -google.com, pub-5717092533913515, RESELLER, f08c47fec0942fa0 -smartadserver.com, 2786, DIRECT -improvedigital.com, 1147, DIRECT -google.com, pub-2930805104418204, RESELLER, f08c47fec0942fa0 -google.com, pub-4903453974745530, RESELLER, f08c47fec0942fa0 -richaudience.com, 1ru8dKmJJV, DIRECT -advertising.com, 7574, RESELLER -appnexus.com, 8233, RESELLER, f5ab79cb980f11d1 -pubmatic.com, 81564, RESELLER, 5d62403b186f2ace -pubmatic.com, 156538, RESELLER, 5d62403b186f2ace -rubiconproject.com, 13510, RESELLER, 0bfd66d529a55807 -smartadserver.com, 2640, RESELLER -smartadserver.com, 2441, RESELLER -yahoo.com, 57857, RESELLER, e1a5b5b6e3255540 -undertone.com, 4077, DIRECT -appnexus.com, 2234, RESELLER, f5ab79cb980f11d1 -rubiconproject.com, 22412, RESELLER, 0bfd66d529a55807 -advertising.com, 28650, RESELLER -pubmatic.com, 160318, RESELLER, 5d62403b186f2ace -pubmatic.com, 160319, RESELLER, 5d62403b186f2ace -appnexus.com, 10112, RESELLER, f5ab79cb980f11d1 -google.com, pub-0679975395820445, RESELLER, f08c47fec0942fa0 -google.com, pub-9936969251765866, RESELLER, f08c47fec0942fa0 - -#Fluct -adingo.jp, 25262, RESELLER -pubmatic.com, 156313, RESELLER, 5d62403b186f2ace -appnexus.com, 7044, RESELLER, f5ab79cb980f11d1 -pubmatic.com, 158060, RESELLER, 5d62403b186f2ace - -#Conversant -conversantmedia.com, 100106, RESELLER, 03113cd04947736d -lijit.com, 411121, RESELLER, fafdf38b16bf6b2b #SOVRN -admanmedia.com, 2050, RESELLER -Appnerve.com, 187287, RESELLER -rubiconproject.com, 23644, RESELLER, 0bfd66d529a55807 - - -#OneTag -onetag.com, 7683ebe7bee7969, RESELLER -onetag.com, 7683ebe7bee7969-OB, RESELLER -appnexus.com, 13099, RESELLER, f5ab79cb980f11d1 -yahoo.com, 58905, RESELLER, e1a5b5b6e3255540 -rubiconproject.com, 11006, RESELLER, 0bfd66d529a55807 -smartadserver.com, 4111, RESELLER - -#Media.net -media.net, 8CUEHU9Y5, RESELLER -openx.com, 537100188, RESELLER, 6a698e2ec38604c6 -pubmatic.com, 159463, RESELLER, 5d62403b186f2ace -emxdgt.com, 1759, RESELLER, 1e1d41537f7cad7f -google.com, pub-7439041255533808, RESELLER, f08c47fec0942fa0 -rubiconproject.com, 19396, RESELLER, 0bfd66d529a55807 -onetag.com, 5d49f482552c9b6, RESELLER -sonobi.com, 83729e979b, RESELLER -33across.com, 0010b00002cGp2AAAS, RESELLER, bbea06d9c4d2853c -rhythmone.com, 3611299104, RESELLER, a670c89d4a324e47 -districtm.io, 100600, RESELLER -lemmatechnologies.com, 399, RESELLER, 7829010c5bebd1fb #LEMMA -e-planning.net,ec771b05828a67fa,RESELLER,c1ba615865ed87b2 -google.com, pub-9685734445476814, RESELLER, f08c47fec0942fa0 - -#EMX Digital -emxdgt.com, 2345, RESELLER, 1e1d41537f7cad7f - - -#The MediaGrid -themediagrid.com, B8ZEVT, RESELLER, 35d5010d7789b49d -themediagrid.com, 3W8S2K, RESELLER, 35d5010d7789b49d - -#triplelift -triplelift.com, 12900, RESELLER, 6c33edb13117fd86 -triplelift.com, 12900-EB, DIRECT, 6c33edb13117fd86 -triplelift.com, 13897, DIRECT, 6c33edb13117fd86 - -#Sharethrough - -sharethrough.com, buaxQzOE, RESELLER, d53b998a7bd4ecd2 -sharethrough.com, jvyAFD6e, DIRECT, d53b998a7bd4ecd2 -pubmatic.com, 156557, RESELLER, 5d62403b186f2ace -rubiconproject.com, 18694, RESELLER, 0bfd66d529a55807 -openx.com, 540274407, RESELLER, 6a698e2ec38604c6 -33across.com, 0013300001kQj2HAAS, RESELLER, bbea06d9c4d2853c -smaato.com, 1100047713, RESELLER, 07bcf65f187117b4 -yahoo.com, 59531, RESELLER, e1a5b5b6e3255540 -smartadserver.com, 4342, RESELLER -smartadserver.com, 4012, RESELLER - - -#V 15.01.2024 PH - -#------------------------------------------------------------------------------------------------------ -adagio.io, 1090, DIRECT # Adagio_0_6 -rubiconproject.com, 19116, RESELLER, 0bfd66d529a55807 # Adagio_0_6 -pubmatic.com, 159110, RESELLER, 5d62403b186f2ace # Adagio_0_6 -improvedigital.com, 1790, RESELLER # Adagio_0_6 -indexexchange.com, 194558, RESELLER # Adagio_0_6 -richaudience.com, 1BTOoaD22a, DIRECT # Adagio_0_6 -33across.com, 0015a00002oUk4aAAC, DIRECT, bbea06d9c4d2853c # Adagio_0_6 -appnexus.com, 10239, RESELLER, f5ab79cb980f11d1 # Adagio_0_6 -rubiconproject.com, 16414, RESELLER, 0bfd66d529a55807 # Adagio_0_6 -lijit.com, 367236, RESELLER, fafdf38b16bf6b2b # Adagio_0_6 -e-planning.net, 83c06e81531537f4, RESELLER, c1ba615865ed87b2 # Adagio_0_6 -amxrtb.com, 105199358, DIRECT # AdaptMX_1_6&7 -indexexchange.com, 191503, RESELLER # AdaptMX_1_6&7 -appnexus.com, 11786, RESELLER # AdaptMX_1_6&7 -appnexus.com, 12290, RESELLER # AdaptMX_1_6&7 -pubmatic.com, 158355, RESELLER, 5d62403b186f2ace # AdaptMX_1_6&7 -advertising.com, 28305, RESELLER # AdaptMX_1_6&7 -rubiconproject.com, 23844, RESELLER, 0bfd66d529a55807 # AdaptMX_1_6&7 -openx.com, 559680764, RESELLER, 6a698e2ec38604c6 # AdaptMX_1_6&7 -adform.com, 2767, RESELLER # Adform_0_6&7 -adyoulike.com, c1314a52de718f3c214c00173d2994f9, DIRECT # AdYouLike_0_6 -pubmatic.com, 160925, RESELLER, 5d62403b186f2ace # AdYouLike_0_6 -rubiconproject.com, 20736, RESELLER, 0bfd66d529a55807 # AdYouLike_0_6 -appnexus.com, 7664, RESELLER # AdYouLike_0_6 -aps.amazon.com,70247b00-ff8f-4016-b3ab-8344daf96e09,DIRECT # Amazon_3_6&7 -ad-generation.jp,12474,RESELLER # Amazon_3_6&7 -aniview.com, 5f2063121d82c82557194737, RESELLER, 78b21b97965ec3f8 # Aniview -aniview.com, 643f8e74688b10f72307cc24, DIRECT, 78b21b97965ec3f8 # Aniview -google.com, pub-6346866704322274, RESELLER, f08c47fec0942fa0 # Aniview -pubmatic.com, 160993, RESELLER, 5d62403b186f2ace # Aniview -rubiconproject.com, 13918, RESELLER, 0bfd66d529a55807 # Aniview -google.com, pub-5717092533913515, RESELLER, f08c47fec0942fa0 # Aniview -gannett.com, 22652678936, RESELLER # Aniview -richaudience.com, 1ru8dKmJJV, RESELLER # Aniview -appnexus.com, 12637, RESELLER, f5ab79cb980f11d1 # Aniview -google.com, pub-3565385483761681, RESELLER, f08c47fec0942fa0 # Aniview -sharethrough.com, zLsEa05k, RESELLER, d53b998a7bd4ecd2 # Aniview -aps.amazon.com, 1ad7261b-91ea-4b6f-b9e9-b83522205b75, RESELLER # Aniview -pubmatic.com, 161335, RESELLER, 5d62403b186f2ace # Aniview -google.com, pub-7734005103835923, RESELLER, f08c47fec0942fa0 # Aniview -openx.com, 559611024, RESELLER, 6a698e2ec38604c6 # Aniview -yieldlab.net, 495507, DIRECT # Aniview -blockthrough.com, 5643766199222272, DIRECT # Blockthrough -appnexus.com, 6979, RESELLER # Blockthrough -indexexchange.com, 194341, RESELLER, 50b1c356f2c5c8fc # Blockthrough -pubmatic.com, 160377, RESELLER, 5d62403b186f2ace # Blockthrough -rubiconproject.com, 23718, RESELLER, 0bfd66d529a55807 # Blockthrough -onetag.com, 75804861b76a852, DIRECT # Blockthrough -amxrtb.com, 105199664, DIRECT # Blockthrough -criteo.com, B-062405, DIRECT, 9fac4a4a87c2a44f # Criteo_0_6&7 -themediagrid.com, CVQXOH, DIRECT, 35d5010d7789b49d # Criteo_0_6&7 -cpmstar.com, 53615, DIRECT # CPMSTAR -rhythmone.com,1838093862,DIRECT,a670c89d4a324e47 # CPMSTAR -video.unrulymedia.com, 1838093862, DIRECT # CPMSTAR -pubmatic.com, 160251, DIRECT, 5d62403b186f2ace # CPMSTAR -pubmatic.com, 161595, DIRECT, 5d62403b186f2ace # CPMSTAR -rubiconproject.com, 23330, DIRECT, 0bfd66d529a55807 # CPMSTAR -conversantmedia.com, 41150, DIRECT, 03113cd04947736d # Epsilon -adingo.jp, 24379, DIRECT # Fluct_1_6&7 -freewheel.tv, 211121, DIRECT # Freewheel_0_7 -freewheel.tv, 211129, RESELLER # Freewheel_0_7 -google.com, pub-5781531207509232, RESELLER, f08c47fec0942fa0 # Google_AdX_6&7 -google.com, pub-2553634189837243, RESELLER, f08c47fec0942fa0 # Google_AdX_6&7 -gumgum.com, 13385, RESELLER, ffdef49475d318a9 # GumGum_JP_0_9_6 -gumgum.com, 14302, RESELLER, ffdef49475d318a9 # GumGum_JP_0_9_6 -improvedigital.com, 1012, DIRECT # Improve_0_6&7 -improvedigital.com, 1640, RESELLER # Improve_1_6 -improvedigital.com, 2114, RESELLER # Improve_kids_1_6&7 -indexexchange.com, 183921, DIRECT, 50b1c356f2c5c8fc # Index Exchange_0_6&7 -indexexchange.com, 188416, DIRECT, 50b1c356f2c5c8fc # Index Exchange_1_6&7 -indexexchange.com, 193067, DIRECT, 50b1c356f2c5c8fc # Index Exchange_2_6&7 -indexexchange.com, 194127, DIRECT, 50b1c356f2c5c8fc # Index Exchange_7&4_6&7 -indexexchange.com, 205972, RESELLER, 50b1c356f2c5c8fc # Index Exchange_Oz -indexexchange.com, 206870, RESELLER, 50b1c356f2c5c8fc # Index_EasyConnect -iion.io, 10133, DIRECT # iion -kargo.com, 8688, DIRECT # Kargo_0_6 -rubiconproject.com, 17902, RESELLER, 0bfd66d529a55807 # Magnite_1_6&7 -rubiconproject.com, 13762, RESELLER, 0bfd66d529a55807 # Magnite_0&2_6&7 -telaria.com,hpwve,RESELLER,1a4e959a1b50034a # Magnite_Streaming -tremorhub.com,hpwve,RESELLER,1a4e959a1b50034a # Magnite_Streaming -media.net, 8CU8ARTF8, DIRECT # Media.net -Media.net, 8CU5786QK, DIRECT # Media.net -themediagrid.com, LTW57M, DIRECT, 35d5010d7789b49d # MediaGrid_2_6&7 -minutemedia.com, 01gerz6y43ck, RESELLER # MinuteMedia_0_6 -pubmatic.com, 161683, RESELLER, 5d62403b186f2ace # MinuteMedia_0_6 -appnexus.com, 8381, RESELLER # MinuteMedia_0_6 -triplelift.com, 6030, RESELLER, 6c33edb13117fd86 # MinuteMedia_0_6 -33across.com, 0013300001jlr99AAA, RESELLER, bbea06d9c4d2853c # MinuteMedia_0_6 -nobid.io, 22629800915, DIRECT # Nobid_0_6 -sonobi.com, 7ad1b9f952, RESELLER, d1a215d9eb5aee9e # Nobid_0_6 -xandr.com, 12701, RESELLER, f5ab79cb980f11d1 # Nobid_0_6 -lijit.com, 273657, DIRECT, fafdf38b16bf6b2b # Nobid_0_6 -onetag.com, 694e68b73971b58, DIRECT # Nobid_0_6 -yahoo.com, 57872, RESELLER # Nobid_0_6 -sharethrough.com, UvcAx8IL, DIRECT, d53b998a7bd4ecd2 # Nobid_0_6 -ogury.com, 086233d2-e8a8-44fc-907b-f0752e1c85de, DIRECT # Ogury_0_6 -appnexus.com, 11470, RESELLER # Ogury_0_6 -openx.com, 537144009, RESELLER, 6a698e2ec38604c6 # OpenX_0_6 -openx.com, 540134228, RESELLER, 6a698e2ec38604c6 # OpenX_0_7 -openx.com, 540368327, RESELLER, 6a698e2ec38604c6 # OpenX_1_6&7 -openx.com, 542378302, RESELLER, 6a698e2ec38604c6 # OpenX_2_6&7 -the-ozone-project.com, ozoneven0005, DIRECT # Ozone_0_6 -appnexus.com, 9979, RESELLER # Ozone_0_6 -openx.com, 540731760, RESELLER, 6a698e2ec38604c6 # Ozone_0_6 -adform.com, 2657, RESELLER, 9f5210a2f0999e32 # Ozone_0_6 -pubmatic.com, 160557, RESELLER, 5d62403b186f2ace # Ozone_0_6 -themediagrid.com, WF71T3, DIRECT, 35d5010d7789b49d # Ozone_0_6 -pgamssp.com, 634dc90283fff00f005151f2, DIRECT # PGAM_0_7 -freewheel.tv, 1489202, RESELLER # PGAM_0_7 -freewheel.tv, 1488706, RESELLER # PGAM_0_7 -video.unrulymedia.com, 5921144960123684292, RESELLER # PGAM_0_7 -appnexus.com, 9291, RESELLER # PGAM_0_7 -pubmatic.com, 162623, RESELLER, 5d62403b186f2ace # PGAM_0_7 -primis.tech, 31136, DIRECT, b6b21d256ef43532 # Primis -pubmatic.com, 156595, RESELLER, 5d62403b186f2ace # Primis -google.com, pub-1320774679920841, RESELLER, f08c47fec0942fa0 # Primis -openx.com, 540258065, RESELLER, 6a698e2ec38604c6 # Primis -rubiconproject.com, 20130, RESELLER, 0bfd66d529a55807 # Primis -freewheel.tv, 19133, RESELLER, 74e8e47458f74754 # Primis -smartadserver.com, 3436, RESELLER, 060d053dcf45cbf3 # Primis -indexexchange.com, 191923, RESELLER, 50b1c356f2c5c8fc # Primis -adform.com, 2078, RESELLER # Primis -Media.net, 8CU695QH7, RESELLER # Primis -video.unrulymedia.com, 2338962694, RESELLER # Primis -sharethrough.com, flUyJowI, RESELLER, d53b998a7bd4ecd2 # Primis -triplelift.com, 8210, RESELLER, 6c33edb13117fd86 # Primis -yahoo.com, 59260, RESELLER # Primis -pubmatic.com, 159234, RESELLER, 5d62403b186f2ace # PubMatic_0_6&7 -pubmatic.com, 158940, RESELLER, 5d62403b186f2ace # PubMatic_1_6&7 -pubmatic.com, 160552, RESELLER, 5d62403b186f2ace # PubMatic_4_7 -pubmatic.com, 159401, RESELLER, 5d62403b186f2ace # PubMatic_2_6&7 -pubmatic.com, 163598, RESELLER, 5d62403b186f2ace # Pubmatic_OW -richaudience.com, 1XvIoD5o0S, DIRECT # Rich Audience_0_6&7 -risecodes.com, 5fa94677b2db6a00015b22a9, DIRECT # Rise -pubmatic.com, 160295, RESELLER, 5d62403b186f2ace # Rise -xandr.com, 14082, RESELLER # Rise -rubiconproject.com, 23876, RESELLER, 0bfd66d529a55807 # Rise -media.net, 8CUQ6928Q, RESELLER # Rise_Temp -sharethrough.com, 5926d422, RESELLER, d53b998a7bd4ecd2 # Rise_Temp -sharethrough.com, 31c129df, DIRECT, d53b998a7bd4ecd2 # Sharethrough_0_6&7 -sharethrough.com, Ip2TfKpa, DIRECT, d53b998a7bd4ecd2 # Sharethrough_1_6&7 -smartadserver.com, 2161, RESELLER # Showheroes_7_8 -appnexus.com, 8833, RESELLER, f5ab79cb980f11d1 # Showheroes_7_8 -smartadserver.com, 3668, RESELLER # Showheroes_7_8 -freewheel.tv, 1003361, DIRECT # Showheroes_7_8 -pubmatic.com, 156695, DIRECT, 5d62403b186f2ace # Showheroes_7_8 -showheroes.com, 6829, RESELLER # Showheroes_7_8 -smartadserver.com, 3490, DIRECT # Smart AdServer_0&1&2_6&7 -smartadserver.com, 3490-OB, DIRECT, 060d053dcf45cbf3 # Smart AdServer_0&1&2_6&7 -smartadserver.com, 4016, DIRECT # Smart AdServer_0&1&2_6&7 -smartadserver.com, 4074, DIRECT # Smart AdServer_0&1&2_6&7 -smaato.com, 1100055690, DIRECT, 07bcf65f187117b4 # Smaato -smaato.com, 1100004890, DIRECT, 07bcf65f187117b4 # Smaato -sonobi.com, 116da9d98c, DIRECT, d1a215d9eb5aee9e # Sonobi_0_6&7 -sonobi.com, e017850301, DIRECT, d1a215d9eb5aee9e # Sonobi_4_7 -sovrn.com, 237754, DIRECT, fafdf38b16bf6b2b # Sovrn_0&1&2_6&7 -lijit.com, 237754, DIRECT, fafdf38b16bf6b2b # Sovrn_0&1&2_6&7 -lijit.com, 237754-eb, DIRECT, fafdf38b16bf6b2b # Sovrn_1_6&7 -taboola.com,1422403,DIRECT,c228e6794e811952 # Taboola_6_8 -triplelift.com, 6059, DIRECT, 6c33edb13117fd86 # Triplelift_0&2_6&7 -triplelift.com, 6059-EB, DIRECT, 6c33edb13117fd86 # Triplelift_0&2_6&7 -video.unrulymedia.com, 985572675, DIRECT # Unruly_0&2_7 -rhythmone.com, 2864567592, DIRECT, a670c89d4a324e47 # Unruly_0&2_7 -xandr.com, 13799, RESELLER # Unruly -sharethrough.com, 6qlnf8SY, RESELLER, d53b998a7bd4ecd2 # Unruly -vidazoo.com, 655c85dc63ceeb606a0f365f, DIRECT, b6ada874b4d7d0b2 # Vidazoo -pubmatic.com, 159988, RESELLER, 5d62403b186f2ace # Vidazoo -rubiconproject.com, 17130, RESELLER, 0bfd66d529a55807 # Vidazoo -pubmatic.com, 156512, DIRECT # Wunderkind -indexexchange.com, 183753, DIRECT # Wunderkind -wunderkind.co, 6438, DIRECT # Wunderkind -wunderkind.co, 6449, DIRECT # Wunderkind -criteo.com, B-068503, DIRECT # Wunderkind -appnexus.com, 806, DIRECT, f5ab79cb980f11d1 # Xandr_0&2_6&7 -appnexus.com,1908,RESELLER,f5ab79cb980f11d1 # Xandr_0&2_6&7 - - -#Equativ - -smartadserver.com, 4571, RESELLER, 060d053dcf45cbf3 -smartadserver.com, 4571-OB, RESELLER, 060d053dcf45cbf3 -smartadserver.com, 4016, RESELLER, 060d053dcf45cbf3 #Global -smartadserver.com, 4012, RESELLER, 060d053dcf45cbf3 #EUR -smartadserver.com, 4071, RESELLER, 060d053dcf45cbf3 #USD -smartadserver.com, 4073, RESELLER, 060d053dcf45cbf3 #BRL -smartadserver.com, 4074, RESELLER, 060d053dcf45cbf3 #MXN -smartadserver.com, 4247, RESELLER, 060d053dcf45cbf3 #CAD -smartadserver.com, 4228, RESELLER, 060d053dcf45cbf3 #USD_CTV -pubmatic.com, 156439, RESELLER, 5d62403b186f2ace -pubmatic.com, 154037, RESELLER, 5d62403b186f2ace -rubiconproject.com, 16114, RESELLER, 0bfd66d529a55807 -openx.com, 537149888, RESELLER, 6a698e2ec38604c6 -appnexus.com, 3703, RESELLER, f5ab79cb980f11d1 -loopme.com, 5679, RESELLER, 6c8d5f95897a5a3b -xad.com, 958, RESELLER, 81cbf0a75a5e0e9a -video.unrulymedia.com, 2564526802, RESELLER -smaato.com, 1100044045, RESELLER, 07bcf65f187117b4 -pubnative.net, 1006576, RESELLER, d641df8625486a7b -verve.com, 15503, RESELLER, 0c8f5958fc2d6270 -adyoulike.com, b4bf4fdd9b0b915f746f6747ff432bde, RESELLER, 4ad745ead2958bf7 -axonix.com, 57264, RESELLER -admanmedia.com, 43, RESELLER -sharethrough.com, OAW69Fon, RESELLER, d53b998a7bd4ecd2 -contextweb.com, 560288, RESELLER, 89ff185a4c4e857c - -#nobid - -nobid.io, 22931676975, DIRECT -xandr.com, 11429, RESELLER, f5ab79cb980f11d1 -sharethrough.com, aRE1degH, RESELLER, d53b998a7bd4ecd2 -sonobi.com, 7ad1b9f952, RESELLER, d1a215d9eb5aee9e -sharethrough.com, UvcAx8IL, RESELLER, d53b998a7bd4ecd2 -amxrtb.com, 105199579, RESELLER -yahoo.com,49648,RESELLER -rubiconproject.com, 24434, RESELLER, 0bfd66d529a55807 -minutemedia.com, 01gerz67grgj, RESELLER -pubmatic.com, 161683, RESELLER, 5d62403b186f2ace -appnexus.com, 8381, RESELLER, f5ab79cb980f11d1 -triplelift.com, 6030, RESELLER, 6c33edb13117fd86 -sonobi.com, 37fbaf262c, RESELLER, d1a215d9eb5aee9e -openx.com, 540780517, RESELLER, 6a698e2ec38604c6 -rubiconproject.com, 17598, RESELLER, 0bfd66d529a55807 -indexexchange.com, 196326, RESELLER, 50b1c356f2c5c8fc -yahoo.com, 59407, RESELLER, e1a5b5b6e3255540 -sharethrough.com, xz7QjFBY, RESELLER, d53b998a7bd4ecd2 -inmobi.com,8f261ace12c3486ba2e0d2011cd97976,RESELLER,83e75a7ae333ca9d -risecodes.com, 63ea59eef828de0001cf1773, RESELLER -inmobi.com, 9e311c7a68e94888aac7fbb4272381e2, RESELLER, 83e75a7ae333ca9d -video.unrulymedia.com, 1352466146, RESELLER -yahoo.com, 59261, RESELLER, e1a5b5b6e3255540 -gumgum.com, 13926, RESELLER, ffdef49475d318a9 -onetag.com, 694e68b73971b58, RESELLER -lijit.com, 273657, RESELLER, fafdf38b16bf6b2b -sovrn.com, 273657, RESELLER, fafdf38b16bf6b2b -mediafuse.com, 389, RESELLER -appnexus.com, 9538, RESELLER, f5ab79cb980f11d1 -yahoo.com, 57872, RESELLER -video.unrulymedia.com, 2997140015, RESELLER -indexexchange.com, 182257, RESELLER, 50b1c356f2c5c8fc -152media.info,152M374,RESELLER -appnexus.com, 3153, RESELLER, f5ab79cb980f11d1 -#media.net_serverside_displayvideo -media.net, 8CUV34PJ4, DIRECT -sharethrough.com, koRtppYA, RESELLER, d53b998a7bd4ecd2 -video.unrulymedia.com, 699546687, RESELLER -lijit.com, 264726, RESELLER, fafdf38b16bf6b2b -onetag.com, 765b4e6bb9c8438, RESELLER -amxrtb.com, 105199663, RESELLER -yieldmo.com, 2954622693783052507, RESELLER -loopme.com, 11556, RESELLER, 6c8d5f95897a5a3b -Contextweb.com, 562963, RESELLER, 89ff185a4c4e857c -zeta.com, 591, RESELLER -disqus.com, 591, RESELLER -admanmedia.com, 953, RESELLER -smartadserver.com, 4106, RESELLER, 060d053dcf45cbf3 -imds.tv, 82302, RESELLER, ae6c32151e71f19d -improvedigital.com, 2073, RESELLER -betweendigital.com, 44808, RESELLER -adyoulike.com, 53264963677efeda057eef7db2cb305f, RESELLER -freewheel.tv,1577878,RESELLER -freewheel.tv,1577888,RESELLER -dxkulture.com, 9533, DIRECT, 259726033fc4df0c -dxkulture.com, 0098, DIRECT, 259726033fc4df0c -adswizz.com,dxkulture,DIRECT -adswizz.com,651,DIRECT -pubmatic.com,164751,RESELLER,5d62403b186f2ace -rubiconproject.com,26094,DIRECT,0bfd66d529a55807 -zetaglobal.net,790,DIRECT -ssp.disqus.com,790,DIRECT -video.unrulymedia.com,946176315,RESELLER -video.unrulymedia.com, 347774562, RESELLER -rubiconproject.com, 15268, RESELLER, 0bfd66d529a55807 -pubmatic.com, 159277, RESELLER - -#AdaptMX - -amxrtb.com, 105199723, DIRECT -appnexus.com, 12290, RESELLER -pubmatic.com, 161527, RESELLER -rubiconproject.com, 23844, RESELLER - - - -# Adagio -adagio.io, 1361, RESELLER -# Adagio - Magnite -rubiconproject.com, 19116, RESELLER, 0bfd66d529a55807 -# Adagio - Pubmatic -pubmatic.com, 159110, RESELLER, 5d62403b186f2ace -# Adagio - Improve Digital -improvedigital.com, 1790, RESELLER -# Adagio - Onetag -onetag.com, 6b859b96c564fbe, RESELLER -appnexus.com, 13099, RESELLER -pubmatic.com, 161593, RESELLER, 5d62403b186f2ace -# Adagio - Index Exchange -indexexchange.com, 194558, RESELLER -# Adagio - 33Across -33across.com, 0015a00002oUk4aAAC, RESELLER, bbea06d9c4d2853c -yahoo.com, 57289, RESELLER, e1a5b5b6e3255540 -appnexus.com, 10239, RESELLER, f5ab79cb980f11d1 -rubiconproject.com, 16414, RESELLER, 0bfd66d529a55807 -pubmatic.com, 156423, RESELLER, 5d62403b186f2ace -rubiconproject.com, 21642, RESELLER, 0bfd66d529a55807 -conversantmedia.com, 100141, RESELLER -indexexchange.com, 191973, RESELLER, 50b1c356f2c5c8fc -triplelift.com, 12503, RESELLER, 6c33edb13117fd86 -insticator.com, 4ec3ed85-2830-4174-9f7f-f545620598b9, RESELLER -sharethrough.com, Q9IzHdvp, RESELLER, d53b998a7bd4ecd2 -admanmedia.com, 2216, RESELLER -connectad.io, 456, RESELLER, 85ac85a30c93b3e5 -# Adagio - Equativ -smartadserver.com, 3554, RESELLER -# Adagio - Sovrn -lijit.com, 367236, RESELLER, fafdf38b16bf6b2b -# Adagio - Freewheel -freewheel.tv, 1568036, RESELLER -freewheel.tv, 1568041, RESELLER -# Adagio - OpenX -openx.com, 558899373, RESELLER, 6a698e2ec38604c6 -# Adagio - Triplelift -triplelift.com, 13482, RESELLER, 6c33edb13117fd86 -# Adagio - E-Planning -e-planning.net, 83c06e81531537f4, RESELLER, c1ba615865ed87b2 -pubmatic.com, 156631, RESELLER, 5d62403b186f2ace -openx.com, 541031350, RESELLER, 6a698e2ec38604c6 -rubiconproject.com, 12186, RESELLER, 0bfd66d529a55807 -# Adagio - Nexxen -video.unrulymedia.com, 5672421953199218469, RESELLER - -#Freewheel - -freewheel.tv, 1598995, RESELLER -freewheel.tv, 1599004, RESELLER - - -#Pgam - -pgamssp.com, 64661fa49d522e327b0a8b84, DIRECT -freewheel.tv, 1489202, RESELLER -freewheel.tv, 1488706, RESELLER -rubiconproject.com, 24852, RESELLER, 0bfd66d529a55807 -pubmatic.com, 162623, RESELLER, 5d62403b186f2ace -video.unrulymedia.com, 5921144960123684292, RESELLER -appnexus.com, 9291, RESELLER, f5ab79cb980f11d1 - - -#Sonobi - -sonobi.com, 3ee2ca3952, RESELLER, d1a215d9eb5aee9e - - -#Rich Audience - -richaudience.com, kWVs0vbyki, RESELLER -appnexus.com, 2928, DIRECT, f5ab79cb980f11d1 -smartadserver.com, 1999, RESELLER, 060d053dcf45cbf3 - -#Ozone - -the-ozone-project.com, OZONEAIP0001, DIRECT -appnexus.com, 9979, RESELLER, f5ab79cb980f11d1 -openx.com, 540731760, RESELLER, 6a698e2ec38604c6 -adform.com, 2657, RESELLER, 9f5210a2f0999e32 -pubmatic.com, 160557, RESELLER, 5d62403b186f2ace -indexexchange.com, 206233, RESELLER, 50b1c356f2c5c8fc -themediagrid.com, 1J3ZI6, DIRECT, 35d5010d7789b49d -themediagrid.com, WF71T3, DIRECT, 35d5010d7789b49d - -# OptiDigital -optidigital.com,p345,RESELLER -pubmatic.com,158939,RESELLER,5d62403b186f2ace -rubiconproject.com,20336,RESELLER,0bfd66d529a55807 -smartadserver.com,3379,RESELLER,060d053dcf45cbf3 -criteo.com,B-060926,RESELLER,9fac4a4a87c2a44f -themediagrid.com,3ETIX5,RESELLER,35d5010d7789b49d -triplelift.com,8183,RESELLER,6c33edb13117fd86 -appnexus.com,12190,RESELLER,f5ab79cb980f11d1 -onetag.com,806eabb849d0326,RESELLER -rtbhouse.com,mSu1piUSmB9TF4AQDGk4,RESELLER -33across.com,001Pg00000HMy0YIAT,RESELLER,bbea06d9c4d2853c -e-planning.net,a76893b96338e7e9,RESELLER,c1ba615865ed87b2 -appnexus.com,15941,RESELLER,f5ab79cb980f11d1 -video.unrulymedia.com,731539260,RESELLER - -#Rise -risecodes.com, 643813aab7212c00011c3f28, DIRECT -pubmatic.com, 160295, RESELLER, 5d62403b186f2ace -xandr.com, 14082, RESELLER -rubiconproject.com, 23876, RESELLER, 0bfd66d529a55807 -sharethrough.com, 5926d422, RESELLER, d53b998a7bd4ecd2 -media.net, 8CUQ6928Q, RESELLER -sonobi.com, 4a289cdd79, RESELLER, d1a215d9eb5aee9e -video.unrulymedia.com, 335119963, RESELLER -contextweb.com,562615,RESELLER,89ff185a4c4e857c -onetag.com, 69f48c2160c8113, RESELLER -33across.com, 0010b00002Xbn7QAAR, RESELLER, bbea06d9c4d2853c -yieldmo.com, 2754490424016969782, RESELLER -openx.com, 537140488, RESELLER, 6a698e2ec38604c6 -lijit.com, 405318, RESELLER, fafdf38b16bf6b2b -themediagrid.com, 4DQHAP, RESELLER, 35d5010d7789b49d -loopme.com, 11362, RESELLER, 6c8d5f95897a5a3b -amxrtb.com, 105199691, RESELLER -smartadserver.com, 4284, RESELLER -adform.com, 3119, RESELLER, 9f5210a2f0999e32 -smaato.com, 1100057444, RESELLER, 07bcf65f187117b4 -adyoulike.com, 78afbc34fac571736717317117dfa247, RESELLER - -#Block -blockthrough.com, 5130683165442048, DIRECT -pubmatic.com, 160377, RESELLER, 5d62403b186f2ace -rubiconproject.com, 23718, RESELLER, 0bfd66d529a55807 -appnexus.com, 6979, RESELLER -lijit.com, 251666, RESELLER, fafdf38b16bf6b2b -lijit.com, 251666-eb, RESELLER, fafdf38b16bf6b2b -video.unrulymedia.com, 2444764291, RESELLER -contextweb.com, 558511, RESELLER -krushmedia.com, AJxF6R572a9M6CaTvK, RESELLER -criteo.com, 8990, RESELLER -smartadserver.com, 4485, RESELLER, 060d053dcf45cbf3 -smartadserver.com, 4485-OB, RESELLER, 060d053dcf45cbf3 -Contextweb.com, 562926, RESELLER, 89ff185a4c4e857c - -# VT Amazon TAM - -aps.amazon.com,70247b00-ff8f-4016-b3ab-8344daf96e09,DIRECT -indexexchange.com, 193067, DIRECT, 50b1c356f2c5c8fc -triplelift.com, 6059, DIRECT, 6c33edb13117fd86 -sharethrough.com, 31c129df, DIRECT, d53b998a7bd4ecd2 -appnexus.com, 806, DIRECT, f5ab79cb980f11d1 -risecodes.com, 5fa94677b2db6a00015b22a9, DIRECT -minutemedia.com, 01gerz6y43ck, RESELLER -themediagrid.com, LTW57M, DIRECT, 35d5010d7789b49d -vidazoo.com, 655c85dc63ceeb606a0f365f, DIRECT, b6ada874b4d7d0b2 -smartadserver.com, 3490, DIRECT - -################################## -# AdinPlay.com ads.txt - 2025-04-16 -################################## - -venatus.com, OFI, DIRECT \ No newline at end of file From 32f5723c72213c92d19eb31877d7023a317e1ca6 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Thu, 29 May 2025 20:53:18 -0400 Subject: [PATCH 16/20] Simplify bots retaliation logic (#946) ## Description: Simplify bots retaliation logic. Do not counter-attack before reaching the trigger ratio. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com> --- src/core/execution/BotExecution.ts | 1 - src/core/execution/FakeHumanExecution.ts | 1 - src/core/execution/utils/BotBehavior.ts | 16 ++++++++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/core/execution/BotExecution.ts b/src/core/execution/BotExecution.ts index 3b0354810..84d46768c 100644 --- a/src/core/execution/BotExecution.ts +++ b/src/core/execution/BotExecution.ts @@ -79,7 +79,6 @@ export class BotExecution implements Execution { } this.behavior.forgetOldEnemies(); - this.behavior.checkIncomingAttacks(); const enemy = this.behavior.selectRandomEnemy(); if (!enemy) return; if (!this.bot.sharesBorderWith(enemy)) return; diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index af71e77c2..4fa3ec7cd 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -262,7 +262,6 @@ export class FakeHumanExecution implements Execution { throw new Error("not initialized"); } this.behavior.forgetOldEnemies(); - this.behavior.checkIncomingAttacks(); this.behavior.assistAllies(); const enemy = this.behavior.selectEnemy(); if (!enemy) return; diff --git a/src/core/execution/utils/BotBehavior.ts b/src/core/execution/utils/BotBehavior.ts index afbb53331..e68a0c775 100644 --- a/src/core/execution/utils/BotBehavior.ts +++ b/src/core/execution/utils/BotBehavior.ts @@ -52,7 +52,7 @@ export class BotBehavior { } } - checkIncomingAttacks() { + private checkIncomingAttacks() { // Switch enemies if we're under attack const incomingAttacks = this.player.incomingAttacks(); if (incomingAttacks.length > 0) { @@ -109,6 +109,11 @@ export class BotBehavior { } } + // Retaliate against incoming attacks + if (this.enemy === null) { + this.checkIncomingAttacks(); + } + // Select the most hated player if (this.enemy === null) { const mostHated = this.player.allRelationsSorted()[0]; @@ -145,8 +150,15 @@ export class BotBehavior { this.enemy = neighbor; this.enemyUpdated = this.game.ticks(); } + } - // Select a traitor as an enemy + // Retaliate against incoming attacks + if (this.enemy === null) { + this.checkIncomingAttacks(); + } + + // Select a traitor as an enemy + if (this.enemy === null) { const traitors = this.player .neighbors() .filter((n) => n.isPlayer() && n.isTraitor()) as Player[]; From be2e64c85c08e42fb2934bf8444cf8d7c96d7dbb Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 29 May 2025 19:18:34 -0700 Subject: [PATCH 17/20] put meta changes in news modal --- resources/lang/en.json | 3 +++ src/client/NewsModal.ts | 39 +++++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index 7f4bd92d0..886b68fd6 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -20,6 +20,9 @@ "advertise": "Advertise", "wiki": "Wiki" }, + "news": { + "title": "Version 23 released!" + }, "help_modal": { "hotkeys": "Hotkeys", "table_key": "Key", diff --git a/src/client/NewsModal.ts b/src/client/NewsModal.ts index 94c8116bf..13ddb67bb 100644 --- a/src/client/NewsModal.ts +++ b/src/client/NewsModal.ts @@ -12,6 +12,10 @@ export class NewsModal extends LitElement { }; static styles = css` + :host { + display: block; + } + .news-container { max-height: 60vh; overflow-y: auto; @@ -24,10 +28,20 @@ export class NewsModal extends LitElement { .news-content { color: #ddd; line-height: 1.5; - background: rgba(255, 255, 255, 0.05); + background: rgba(0, 0, 0, 0.6); border-radius: 8px; padding: 1rem; } + + .news-content a { + color: #4a9eff !important; + text-decoration: underline !important; + transition: color 0.2s ease; + } + + .news-content a:hover { + color: #6fb3ff !important; + } `; render() { @@ -36,7 +50,24 @@ export class NewsModal extends LitElement {
    -
    INSERT NEWS HERE
    +
    +

    Main things to note:

    +
    +
      +
    • Workers reproduce faster than troops.
    • +
    • Defense = troops divided how much land you have.
    • +
    • Attacking troops count toward your population limit.
    • +
    +
    +
    + See full changelog + here. +
    @@ -58,8 +89,4 @@ export class NewsModal extends LitElement { private close() { this.modalEl?.close(); } - - createRenderRoot() { - return this; // light DOM - } } From ca0a848f9fe2a8b55871b12753b40111b9805000 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 29 May 2025 19:44:53 -0700 Subject: [PATCH 18/20] show news button --- src/client/Main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/Main.ts b/src/client/Main.ts index 22bd98b65..8fdc0cfdb 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -75,7 +75,7 @@ class Client { } // Comment out to show news button. - newsButton.hidden = true; + // newsButton.hidden = true; const langSelector = document.querySelector( "lang-selector", From 91b19c1f097c976b01913931d95279dbbc0dee21 Mon Sep 17 00:00:00 2001 From: Drills Kibo <59177241+drillskibo@users.noreply.github.com> Date: Fri, 30 May 2025 05:55:03 +0200 Subject: [PATCH 19/20] Add close label (#949) ## Description: Add a label to the close button on News modal. Closes [#947](https://github.com/openfrontio/OpenFrontIO/issues/947) ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: `drillskibo` ![image](https://github.com/user-attachments/assets/35500890-a30a-4481-aa73-802152325eef) --- resources/lang/en.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/lang/en.json b/resources/lang/en.json index 886b68fd6..e75ecdb6e 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -5,6 +5,9 @@ "svg": "uk_us_flag", "lang_code": "en" }, + "common": { + "close": "Close" + }, "main": { "title": "OpenFront (ALPHA)", "join_discord": "Join the Discord!", From 44e7b4990d57b1d9e578d41071259c17031dc77b Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Fri, 30 May 2025 02:12:03 -0400 Subject: [PATCH 20/20] Remove ClientID from GameRenderer (#878) ## Description: GameView provides a `myPlayer()` implementation. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors --- src/client/ClientGameRunner.ts | 7 +----- src/client/graphics/GameRenderer.ts | 15 +++--------- src/client/graphics/layers/ChatDisplay.ts | 6 ++--- src/client/graphics/layers/ControlPanel.ts | 2 -- src/client/graphics/layers/EventsDisplay.ts | 24 +++++++++---------- src/client/graphics/layers/Leaderboard.ts | 9 +------ src/client/graphics/layers/NameLayer.ts | 15 +----------- .../graphics/layers/PlayerInfoOverlay.ts | 17 +++---------- src/client/graphics/layers/RadialMenu.ts | 4 +--- src/client/graphics/layers/TeamStats.ts | 2 -- src/client/graphics/layers/UILayer.ts | 2 -- src/client/graphics/layers/UnitLayer.ts | 23 +++++------------- 12 files changed, 29 insertions(+), 97 deletions(-) diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index df11c56c8..67d899f22 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -142,12 +142,7 @@ export async function createClientGame( consolex.log("going to init path finder"); consolex.log("inited path finder"); const canvas = createCanvas(); - const gameRenderer = createRenderer( - canvas, - gameView, - eventBus, - lobbyConfig.clientID, - ); + const gameRenderer = createRenderer(canvas, gameView, eventBus); consolex.log( `creating private game got difficulty: ${lobbyConfig.gameStartInfo.config.difficulty}`, diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 6fef871c0..ad114234c 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -1,6 +1,5 @@ import { consolex } from "../../core/Consolex"; import { EventBus } from "../../core/EventBus"; -import { ClientID } from "../../core/Schemas"; import { GameView } from "../../core/game/GameView"; import { GameStartingModal } from "../GameStartingModal"; import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler"; @@ -38,7 +37,6 @@ export function createRenderer( canvas: HTMLCanvasElement, game: GameView, eventBus: EventBus, - clientID: ClientID, ): GameRenderer { const transformHandler = new TransformHandler(game, eventBus, canvas); @@ -71,7 +69,6 @@ export function createRenderer( if (!emojiTable || !(leaderboard instanceof Leaderboard)) { consolex.error("EmojiTable element not found in the DOM"); } - leaderboard.clientID = clientID; leaderboard.eventBus = eventBus; leaderboard.game = game; @@ -79,7 +76,6 @@ export function createRenderer( if (!emojiTable || !(teamStats instanceof TeamStats)) { consolex.error("EmojiTable element not found in the DOM"); } - teamStats.clientID = clientID; teamStats.eventBus = eventBus; teamStats.game = game; @@ -87,7 +83,6 @@ export function createRenderer( if (!(controlPanel instanceof ControlPanel)) { consolex.error("ControlPanel element not found in the DOM"); } - controlPanel.clientID = clientID; controlPanel.eventBus = eventBus; controlPanel.uiState = uiState; controlPanel.game = game; @@ -100,7 +95,6 @@ export function createRenderer( } eventsDisplay.eventBus = eventBus; eventsDisplay.game = game; - eventsDisplay.clientID = clientID; const chatDisplay = document.querySelector("chat-display") as ChatDisplay; if (!(chatDisplay instanceof ChatDisplay)) { @@ -108,7 +102,6 @@ export function createRenderer( } chatDisplay.eventBus = eventBus; chatDisplay.game = game; - chatDisplay.clientID = clientID; const playerInfo = document.querySelector( "player-info-overlay", @@ -117,7 +110,6 @@ export function createRenderer( consolex.error("player info overlay not found"); } playerInfo.eventBus = eventBus; - playerInfo.clientID = clientID; playerInfo.transform = transformHandler; playerInfo.game = game; @@ -201,10 +193,10 @@ export function createRenderer( new TerrainLayer(game, transformHandler), new TerritoryLayer(game, eventBus), structureLayer, - new UnitLayer(game, eventBus, clientID, transformHandler), + new UnitLayer(game, eventBus, transformHandler), new FxLayer(game), - new UILayer(game, eventBus, clientID, transformHandler), - new NameLayer(game, transformHandler, clientID), + new UILayer(game, eventBus, transformHandler), + new NameLayer(game, transformHandler), eventsDisplay, chatDisplay, buildMenu, @@ -212,7 +204,6 @@ export function createRenderer( eventBus, game, transformHandler, - clientID, emojiTable as EmojiTable, buildMenu, uiState, diff --git a/src/client/graphics/layers/ChatDisplay.ts b/src/client/graphics/layers/ChatDisplay.ts index e4f376e18..f6940ae0a 100644 --- a/src/client/graphics/layers/ChatDisplay.ts +++ b/src/client/graphics/layers/ChatDisplay.ts @@ -9,7 +9,6 @@ import { GameUpdateType, } from "../../../core/game/GameUpdates"; import { GameView } from "../../../core/game/GameView"; -import { ClientID } from "../../../core/Schemas"; import { onlyImages } from "../../../core/Util"; import { Layer } from "./Layer"; @@ -24,7 +23,6 @@ interface ChatEvent { export class ChatDisplay extends LitElement implements Layer { public eventBus: EventBus; public game: GameView; - public clientID: ClientID; private active: boolean = false; @@ -61,7 +59,7 @@ export class ChatDisplay extends LitElement implements Layer { onDisplayMessageEvent(event: DisplayMessageUpdate) { if (event.messageType !== MessageType.CHAT) return; - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if ( event.playerID !== null && (!myPlayer || myPlayer.smallID() !== event.playerID) @@ -90,7 +88,7 @@ export class ChatDisplay extends LitElement implements Layer { if (messages) { for (const msg of messages) { if (msg.messageType === MessageType.CHAT) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if ( msg.playerID !== null && (!myPlayer || myPlayer.smallID() !== msg.playerID) diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index 0ec35f9af..19d2bd02c 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -3,7 +3,6 @@ import { customElement, state } from "lit/decorators.js"; import { translateText } from "../../../client/Utils"; import { EventBus } from "../../../core/EventBus"; import { GameView } from "../../../core/game/GameView"; -import { ClientID } from "../../../core/Schemas"; import { AttackRatioEvent } from "../../InputHandler"; import { SendSetTargetTroopRatioEvent } from "../../Transport"; import { renderNumber, renderTroops } from "../../Utils"; @@ -13,7 +12,6 @@ import { Layer } from "./Layer"; @customElement("control-panel") export class ControlPanel extends LitElement implements Layer { public game: GameView; - public clientID: ClientID; public eventBus: EventBus; public uiState: UIState; diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index f95f4d84b..0ccba3800 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -23,7 +23,6 @@ import { TargetPlayerUpdate, UnitIncomingUpdate, } from "../../../core/game/GameUpdates"; -import { ClientID } from "../../../core/Schemas"; import { CancelAttackIntentEvent, CancelBoatIntentEvent, @@ -66,7 +65,6 @@ interface Event { export class EventsDisplay extends LitElement implements Layer { public eventBus: EventBus; public game: GameView; - public clientID: ClientID; private active: boolean = false; private events: Event[] = []; @@ -184,7 +182,7 @@ export class EventsDisplay extends LitElement implements Layer { renderLayer(): void {} onDisplayMessageEvent(event: DisplayMessageUpdate) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if ( event.playerID !== null && (!myPlayer || myPlayer.smallID() !== event.playerID) @@ -202,7 +200,7 @@ export class EventsDisplay extends LitElement implements Layer { } onDisplayChatEvent(event: DisplayChatMessageUpdate) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if ( event.playerID === null || !myPlayer || @@ -230,7 +228,7 @@ export class EventsDisplay extends LitElement implements Layer { } onAllianceRequestEvent(update: AllianceRequestUpdate) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if (!myPlayer || update.recipientID !== myPlayer.smallID()) { return; } @@ -282,7 +280,7 @@ export class EventsDisplay extends LitElement implements Layer { } onAllianceRequestReplyEvent(update: AllianceRequestReplyUpdate) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if (!myPlayer || update.request.requestorID !== myPlayer.smallID()) { return; } @@ -303,7 +301,7 @@ export class EventsDisplay extends LitElement implements Layer { } onBrokeAllianceEvent(update: BrokeAllianceUpdate) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if (!myPlayer) return; const betrayed = this.game.playerBySmallID(update.betrayedID) as PlayerView; @@ -341,7 +339,7 @@ export class EventsDisplay extends LitElement implements Layer { } onAllianceExpiredEvent(update: AllianceExpiredUpdate) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if (!myPlayer) return; const otherID = @@ -365,7 +363,7 @@ export class EventsDisplay extends LitElement implements Layer { onTargetPlayerEvent(event: TargetPlayerUpdate) { const other = this.game.playerBySmallID(event.playerID) as PlayerView; - const myPlayer = this.game.playerByClientID(this.clientID) as PlayerView; + const myPlayer = this.game.myPlayer() as PlayerView; if (!myPlayer || !myPlayer.isFriendly(other)) return; const target = this.game.playerBySmallID(event.targetID) as PlayerView; @@ -380,13 +378,13 @@ export class EventsDisplay extends LitElement implements Layer { } emitCancelAttackIntent(id: string) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if (!myPlayer) return; this.eventBus.emit(new CancelAttackIntentEvent(myPlayer.id(), id)); } emitBoatCancelIntent(id: number) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if (!myPlayer) return; this.eventBus.emit(new CancelBoatIntentEvent(id)); } @@ -406,7 +404,7 @@ export class EventsDisplay extends LitElement implements Layer { } onEmojiMessageEvent(update: EmojiUpdate) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if (!myPlayer) return; const recipient = @@ -441,7 +439,7 @@ export class EventsDisplay extends LitElement implements Layer { } onUnitIncomingEvent(event: UnitIncomingUpdate) { - const myPlayer = this.game.playerByClientID(this.clientID); + const myPlayer = this.game.myPlayer(); if (!myPlayer || myPlayer.smallID() !== event.playerID) { return; diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts index fb22697b4..2bcca0e8d 100644 --- a/src/client/graphics/layers/Leaderboard.ts +++ b/src/client/graphics/layers/Leaderboard.ts @@ -4,7 +4,6 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { translateText } from "../../../client/Utils"; import { EventBus, GameEvent } from "../../../core/EventBus"; import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; -import { ClientID } from "../../../core/Schemas"; import { renderNumber } from "../../Utils"; import { Layer } from "./Layer"; @@ -36,7 +35,6 @@ export class GoToUnitEvent implements GameEvent { @customElement("leader-board") export class Leaderboard extends LitElement implements Layer { public game: GameView | null = null; - public clientID: ClientID | null = null; public eventBus: EventBus | null = null; players: Entry[] = []; @@ -66,12 +64,7 @@ export class Leaderboard extends LitElement implements Layer { private updateLeaderboard() { if (this.game === null) throw new Error("Not initialized"); - if (this.clientID === null) { - return; - } - const myPlayer = - this.game.playerViews().find((p) => p.clientID() === this.clientID) ?? - null; + const myPlayer = this.game.myPlayer(); const sorted = this.game .playerViews() diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index 32d924fcc..2f8c36a3d 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -8,7 +8,6 @@ import shieldIcon from "../../../../resources/images/ShieldIconBlack.svg"; import targetIcon from "../../../../resources/images/TargetIcon.svg"; import traitorIcon from "../../../../resources/images/TraitorIcon.svg"; import { PseudoRandom } from "../../../core/PseudoRandom"; -import { ClientID } from "../../../core/Schemas"; import { Theme } from "../../../core/configuration/Config"; import { AllPlayers, Cell, nukeTypes, UnitType } from "../../../core/game/Game"; import { GameView, PlayerView } from "../../../core/game/GameView"; @@ -47,14 +46,12 @@ export class NameLayer implements Layer { private nukeRedIconImage: HTMLImageElement; private shieldIconImage: HTMLImageElement; private container: HTMLDivElement; - private myPlayer: PlayerView | null = null; private firstPlace: PlayerView | null = null; private theme: Theme = this.game.config().theme(); constructor( private game: GameView, private transformHandler: TransformHandler, - private clientID: ClientID, ) { this.traitorIconImage = new Image(); this.traitorIconImage.src = traitorIcon; @@ -314,7 +311,7 @@ export class NameLayer implements Layer { ".player-icons", ) as HTMLDivElement; const iconSize = Math.min(render.fontSize * 1.5, 48); - const myPlayer = this.getPlayer(); + const myPlayer = this.game.myPlayer(); // Crown icon const existingCrown = iconsDiv.querySelector('[data-icon="crown"]'); @@ -520,14 +517,4 @@ export class NameLayer implements Layer { } return icon; } - - private getPlayer(): PlayerView | null { - if (this.myPlayer !== null) { - return this.myPlayer; - } - this.myPlayer = - this.game.playerViews().find((p) => p.clientID() === this.clientID) ?? - null; - return this.myPlayer; - } } diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index 394669f81..4ee411891 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -11,7 +11,6 @@ import { } from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; -import { ClientID } from "../../../core/Schemas"; import { MouseMoveEvent } from "../../InputHandler"; import { renderNumber, renderTroops } from "../../Utils"; import { TransformHandler } from "../TransformHandler"; @@ -42,9 +41,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer { @property({ type: Object }) public game!: GameView; - @property({ type: String }) - public clientID!: ClientID; - @property({ type: Object }) public eventBus!: EventBus; @@ -137,13 +133,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer { this.requestUpdate(); } - private myPlayer(): PlayerView | null { - if (!this.game) { - return null; - } - return this.game.playerByClientID(this.clientID); - } - private getRelationClass(relation: Relation): string { switch (relation) { case Relation.Hostile: @@ -175,7 +164,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer { } private renderPlayerInfo(player: PlayerView) { - const myPlayer = this.myPlayer(); + const myPlayer = this.game.myPlayer(); const isFriendly = myPlayer?.isFriendly(player); let relationHtml: TemplateResult | null = null; const attackingTroops = player @@ -275,8 +264,8 @@ export class PlayerInfoOverlay extends LitElement implements Layer { private renderUnitInfo(unit: UnitView) { const isAlly = - (unit.owner() === this.myPlayer() || - this.myPlayer()?.isFriendly(unit.owner())) ?? + (unit.owner() === this.game.myPlayer() || + this.game.myPlayer()?.isFriendly(unit.owner())) ?? false; return html` diff --git a/src/client/graphics/layers/RadialMenu.ts b/src/client/graphics/layers/RadialMenu.ts index db79c507e..4df72f104 100644 --- a/src/client/graphics/layers/RadialMenu.ts +++ b/src/client/graphics/layers/RadialMenu.ts @@ -16,7 +16,6 @@ import { } from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; import { GameView, PlayerView } from "../../../core/game/GameView"; -import { ClientID } from "../../../core/Schemas"; import { CloseViewEvent, ContextMenuEvent, @@ -98,7 +97,6 @@ export class RadialMenu implements Layer { private eventBus: EventBus, private g: GameView, private transformHandler: TransformHandler, - private clientID: ClientID, private emojiTable: EmojiTable, private buildMenu: BuildMenu, private uiState: UIState, @@ -121,7 +119,7 @@ export class RadialMenu implements Layer { return; } const tile = this.g.ref(clickedCell.x, clickedCell.y); - const p = this.g.playerByClientID(this.clientID); + const p = this.g.myPlayer(); if (p === null) { return; } diff --git a/src/client/graphics/layers/TeamStats.ts b/src/client/graphics/layers/TeamStats.ts index b671becb9..e62152f13 100644 --- a/src/client/graphics/layers/TeamStats.ts +++ b/src/client/graphics/layers/TeamStats.ts @@ -3,7 +3,6 @@ import { customElement, state } from "lit/decorators.js"; import { EventBus } from "../../../core/EventBus"; import { GameMode } from "../../../core/game/Game"; import { GameView, PlayerView } from "../../../core/game/GameView"; -import { ClientID } from "../../../core/Schemas"; import { renderNumber } from "../../Utils"; import { Layer } from "./Layer"; @@ -18,7 +17,6 @@ interface TeamEntry { @customElement("team-stats") export class TeamStats extends LitElement implements Layer { public game: GameView; - public clientID: ClientID; public eventBus: EventBus; teams: TeamEntry[] = []; diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index ce91ea7be..e8b8a54a0 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -1,6 +1,5 @@ import { Colord } from "colord"; import { EventBus } from "../../../core/EventBus"; -import { ClientID } from "../../../core/Schemas"; import { Theme } from "../../../core/configuration/Config"; import { UnitType } from "../../../core/game/Game"; import { GameView, UnitView } from "../../../core/game/GameView"; @@ -35,7 +34,6 @@ export class UILayer implements Layer { constructor( private game: GameView, private eventBus: EventBus, - private clientID: ClientID, private transformHandler: TransformHandler, ) { this.theme = game.config().theme(); diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 8501610f8..03afa99c9 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -1,10 +1,9 @@ import { colord, Colord } from "colord"; import { EventBus } from "../../../core/EventBus"; -import { ClientID } from "../../../core/Schemas"; import { Theme } from "../../../core/configuration/Config"; import { UnitType } from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; -import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; +import { GameView, UnitView } from "../../../core/game/GameView"; import { BezenhamLine } from "../../../core/utilities/Line"; import { AlternateViewEvent, @@ -40,8 +39,6 @@ export class UnitLayer implements Layer { private alternateView = false; - private myPlayer: PlayerView | null = null; - private oldShellTile = new Map(); private transformHandler: TransformHandler; @@ -55,7 +52,6 @@ export class UnitLayer implements Layer { constructor( private game: GameView, private eventBus: EventBus, - private clientID: ClientID, transformHandler: TransformHandler, ) { this.theme = game.config().theme(); @@ -67,9 +63,6 @@ export class UnitLayer implements Layer { } tick() { - if (this.myPlayer === null) { - this.myPlayer = this.game.playerByClientID(this.clientID); - } const unitIds = this.game .updatesSinceLastTick() ?.[GameUpdateType.Unit]?.map((unit) => unit.id); @@ -98,18 +91,13 @@ export class UnitLayer implements Layer { } const clickRef = this.game.ref(cell.x, cell.y); - // Make sure we have the current player - if (this.myPlayer === null) { - this.myPlayer = this.game.playerByClientID(this.clientID); - } - // Only select warships owned by the player return this.game .units(UnitType.Warship) .filter( (unit) => unit.isActive() && - unit.owner() === this.myPlayer && // Only allow selecting own warships + unit.owner() === this.game.myPlayer() && // Only allow selecting own warships this.game.manhattanDist(unit.tile(), clickRef) <= this.WARSHIP_SELECTION_RADIUS, ) @@ -256,13 +244,14 @@ export class UnitLayer implements Layer { } private relationship(unit: UnitView): Relationship { - if (this.myPlayer === null) { + const myPlayer = this.game.myPlayer(); + if (myPlayer === null) { return Relationship.Enemy; } - if (this.myPlayer === unit.owner()) { + if (myPlayer === unit.owner()) { return Relationship.Self; } - if (this.myPlayer.isFriendly(unit.owner())) { + if (myPlayer.isFriendly(unit.owner())) { return Relationship.Ally; } return Relationship.Enemy;