-
Notifications
You must be signed in to change notification settings - Fork 707
Fix nation relation exploit 🔧 #2523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
9c1f7b4
543d0bb
f3d4ab2
ed71be8
1ce1eae
90dff31
6a83b35
c1674bc
1fe3878
5820e13
54fb232
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,18 @@ | ||
| import { Execution, Game, Gold, Player, PlayerID } from "../game/Game"; | ||
| import { | ||
| Difficulty, | ||
| Execution, | ||
| Game, | ||
| Gold, | ||
| Player, | ||
| PlayerID, | ||
| } from "../game/Game"; | ||
| import { PseudoRandom } from "../PseudoRandom"; | ||
| import { toInt } from "../Util"; | ||
|
|
||
| export class DonateGoldExecution implements Execution { | ||
| private recipient: Player; | ||
| private random: PseudoRandom; | ||
| private mg: Game; | ||
|
|
||
| private active = true; | ||
| private gold: Gold; | ||
|
|
@@ -16,8 +26,13 @@ export class DonateGoldExecution implements Execution { | |
| } | ||
|
|
||
| init(mg: Game, ticks: number): void { | ||
| this.mg = mg; | ||
| this.random = new PseudoRandom(mg.ticks()); | ||
|
|
||
| if (!mg.hasPlayer(this.recipientID)) { | ||
| console.warn(`DonateExecution recipient ${this.recipientID} not found`); | ||
| console.warn( | ||
| `DonateGoldExecution recipient ${this.recipientID} not found`, | ||
| ); | ||
| this.active = false; | ||
| return; | ||
| } | ||
|
|
@@ -32,7 +47,10 @@ export class DonateGoldExecution implements Execution { | |
| this.sender.canDonateGold(this.recipient) && | ||
| this.sender.donateGold(this.recipient, this.gold) | ||
| ) { | ||
| this.recipient.updateRelation(this.sender, 50); | ||
| // Prevent players from just buying a good relation by sending 1% gold. Instead, a minimum is needed, and it's random. | ||
| if (this.gold >= BigInt(this.getMinGoldForRelationUpdate())) { | ||
| this.recipient.updateRelation(this.sender, 50); | ||
| } | ||
| } else { | ||
| console.warn( | ||
| `cannot send gold from ${this.sender.name()} to ${this.recipient.name()}`, | ||
|
|
@@ -41,6 +59,18 @@ export class DonateGoldExecution implements Execution { | |
| this.active = false; | ||
| } | ||
|
|
||
| getMinGoldForRelationUpdate(): number { | ||
|
||
| const { difficulty } = this.mg.config().gameConfig(); | ||
| if (difficulty === Difficulty.Easy) return this.random.nextInt(0, 25_000); | ||
|
||
| if (difficulty === Difficulty.Medium) | ||
| return this.random.nextInt(25_000, 50_000); | ||
| if (difficulty === Difficulty.Hard) | ||
| return this.random.nextInt(50_000, 125_000); | ||
| if (difficulty === Difficulty.Impossible) | ||
| return this.random.nextInt(125_000, 250_000); | ||
| return 0; | ||
| } | ||
|
|
||
| isActive(): boolean { | ||
| return this.active; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| import { Execution, Game, Player, PlayerID } from "../game/Game"; | ||
| import { Difficulty, Execution, Game, Player, PlayerID } from "../game/Game"; | ||
| import { PseudoRandom } from "../PseudoRandom"; | ||
|
|
||
| export class DonateTroopsExecution implements Execution { | ||
| private recipient: Player; | ||
| private random: PseudoRandom; | ||
| private mg: Game; | ||
|
|
||
| private active = true; | ||
|
|
||
|
|
@@ -12,8 +15,13 @@ export class DonateTroopsExecution implements Execution { | |
| ) {} | ||
|
|
||
| init(mg: Game, ticks: number): void { | ||
| this.mg = mg; | ||
| this.random = new PseudoRandom(mg.ticks()); | ||
FloPinguin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (!mg.hasPlayer(this.recipientID)) { | ||
| console.warn(`DonateExecution recipient ${this.recipientID} not found`); | ||
| console.warn( | ||
| `DonateTroopExecution recipient ${this.recipientID} not found`, | ||
| ); | ||
| this.active = false; | ||
| return; | ||
| } | ||
|
|
@@ -31,7 +39,10 @@ export class DonateTroopsExecution implements Execution { | |
| this.sender.canDonateTroops(this.recipient) && | ||
| this.sender.donateTroops(this.recipient, this.troops) | ||
| ) { | ||
| this.recipient.updateRelation(this.sender, 50); | ||
| // Prevent players from just buying a good relation by sending 1% troops. Instead, a minimum is needed, and it's random. | ||
| if (this.troops >= this.getMinTroopsForRelationUpdate()) { | ||
| this.recipient.updateRelation(this.sender, 50); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else { | ||
| console.warn( | ||
| `cannot send troops from ${this.sender} to ${this.recipient}`, | ||
|
|
@@ -40,6 +51,40 @@ export class DonateTroopsExecution implements Execution { | |
| this.active = false; | ||
| } | ||
|
|
||
| getMinTroopsForRelationUpdate(): number { | ||
|
||
| const { difficulty } = this.mg.config().gameConfig(); | ||
|
|
||
| // ~7.7k - ~9.1k troops (for 100k troops) | ||
| if (difficulty === Difficulty.Easy) | ||
| return this.random.nextInt( | ||
| this.sender.troops() / 13, | ||
|
||
| this.sender.troops() / 11, | ||
| ); | ||
|
|
||
| // ~9.1k - ~11.1k troops (for 100k troops) | ||
| if (difficulty === Difficulty.Medium) | ||
| return this.random.nextInt( | ||
| this.sender.troops() / 11, | ||
| this.sender.troops() / 9, | ||
| ); | ||
|
|
||
| // ~11.1k - ~14.3k troops (for 100k troops) | ||
| if (difficulty === Difficulty.Hard) | ||
| return this.random.nextInt( | ||
| this.sender.troops() / 9, | ||
| this.sender.troops() / 7, | ||
| ); | ||
|
|
||
| // ~14.3k - ~20k troops (for 100k troops) | ||
| if (difficulty === Difficulty.Impossible) | ||
| return this.random.nextInt( | ||
| this.sender.troops() / 7, | ||
| this.sender.troops() / 5, | ||
| ); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| isActive(): boolean { | ||
| return this.active; | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.