From a279792fca1c23dfa2e45e6fbf92c31965770c06 Mon Sep 17 00:00:00 2001 From: vlado Date: Fri, 3 Jul 2026 21:07:21 +0200 Subject: [PATCH 1/5] feat: remove /vnw abuse --- src/commands/general.ts | 2 +- src/frameworks/commands/requirements.ts | 6 +++++- src/index.ts | 6 +++++- src/utils.ts | 22 ++++++++++++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/commands/general.ts b/src/commands/general.ts index 695f05dc..d9cc0b4b 100644 --- a/src/commands/general.ts +++ b/src/commands/general.ts @@ -785,7 +785,7 @@ Please stop attacking and [lime]build defenses[] first!` .on("player vote change", (t, player) => Call.sendMessage(`VNW: ${player.name} [white] has voted on skipping [accent]${t.session!.data}[white] wave(s). [green]${t.currentVotes()}[white] votes, [green]${t.requiredVotes()}[white] required.`)) .on("player vote removed", (t, player) => Call.sendMessage(`VNW: ${player.name} [white] has left. [green]${t.currentVotes()}[white] votes, [green]${t.requiredVotes()}[white] required.`)) }), - requirements: [Req.cooldown(3000), Req.integerRange("waves", 1, 15), Req.mode("survival", "testsrv"), Req.gameRunning], + requirements: [Req.cooldown(3000), Req.integerRange("waves", 1, 15), Req.canVnw(), Req.mode("survival", "testsrv"), Req.gameRunning], async handler({sender, args: {waves}, data:{manager}}){ //Disable narrowing, this is async diff --git a/src/frameworks/commands/requirements.ts b/src/frameworks/commands/requirements.ts index 5dd718d2..d2fc7905 100644 --- a/src/frameworks/commands/requirements.ts +++ b/src/frameworks/commands/requirements.ts @@ -8,7 +8,7 @@ import { fail } from "/frameworks/commands/errors"; import { PermType } from "/frameworks/commands/perm"; import type { FishCommandHandlerData } from "/frameworks/commands/types"; import { FishPlayer } from "/players"; -import { formatModeName } from "/utils"; +import { formatModeName, outputMessage, vnwCondition } from "/utils"; export const Req = { mode: (...modes:GamemodeName[]) => () => @@ -53,4 +53,8 @@ export const Req = { Req.integer(argName)({args}) && (args[argName] == undefined || args[argName] > 0 || fail(`${argName} must be positive`)), + canVnw: + (message = "You can only do that when all units from previous waves are dead.", + message1 = "Stop spamming vnw!",) => ({sender}:{sender:FishPlayer}) => + vnwCondition.check() || fail(Math.random() < 0.9 ? message: message1) }; diff --git a/src/index.ts b/src/index.ts index 2a932ae9..dbdf56af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ import { PartialMapRun } from "/maps"; import { loadPacketHandlers } from "/packetHandlers"; import { FishPlayer } from "/players"; import * as timers from "/timers"; -import { addToTileHistory, fishCommandsRootDirPath, formatTimeRelative, matchFilter, processChat, restartNow, serverRestartLoop } from "/utils"; +import { addToTileHistory, fishCommandsRootDirPath, formatTimeRelative, matchFilter, processChat, restartNow, serverRestartLoop, vnwCondition } from "/utils"; const { Menu } = menus; @@ -326,6 +326,10 @@ Events.on(EventType.PlayEvent, () => { fishState.startTime = Date.now(); }); +Events.on(EventType.UnitDestroyEvent, vnwCondition.removeUnit); +Events.on(EventType.UnitDrownEvent, vnwCondition.removeUnit); +Events.on(EventType.WaveEvent, vnwCondition.onWaveStart); + Events.on(EventType.AdminRequestEvent, e => { if(e.action == Packets.AdminAction.wave){ const fishP = FishPlayer.get(e.player); diff --git a/src/utils.ts b/src/utils.ts index bc9220ab..44274334 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -475,6 +475,28 @@ export function skipWaves(requestedWaves: number, runIntermediateWaves: boolean) } } +export const vnwCondition = { + waveUnits: [] as number[], + addUnit(unitId:number){ + this.waveUnits.push(unitId) + }, + removeUnit(e:EventType){ + let index = vnwCondition.waveUnits.indexOf(e.unit.id); + vnwCondition.waveUnits.splice(index,1); + }, + onWaveStart(){ + Groups.unit.each((unit) => { + + if (unit.team==getEnemyTeam()) vnwCondition.addUnit(unit.id) + }); + + }, + check(){ + if (this.waveUnits.length==0) return true + else return false + }, +} + export function logHTrip(player:FishPlayer, name:string, message?:string){ Log.warn(`&yPlayer &b"${player.cleanedName}"&y (&b${player.uuid}&y/&b${player.ip()}&y) tripped &c${name}&y` + (message ? `: ${message}` : "")); From f754b7a46d1da3edbf0da0824b6f5a27913fca64 Mon Sep 17 00:00:00 2001 From: vlado Date: Sat, 4 Jul 2026 11:18:08 +0200 Subject: [PATCH 2/5] fix: simplify vnw requirements checking --- src/commands/general.ts | 6 +++--- src/frameworks/commands/requirements.ts | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/commands/general.ts b/src/commands/general.ts index d9cc0b4b..9c0711cf 100644 --- a/src/commands/general.ts +++ b/src/commands/general.ts @@ -14,7 +14,7 @@ import { FishEvents, fishPlugin, fishState, ipPortPattern, recentWhispers, tileH import { FMap, PartialMapRun } from "/maps"; import { FishPlayer } from "/players"; import { Rank, RoleFlag } from "/ranks"; -import { formatTime, formatTimeRelative, getColor, logAction, nearbyEnemyTile, neutralGameover, skipWaves, teleportPlayer } from "/utils"; +import { formatTime, formatTimeRelative, getColor, logAction, nearbyEnemyTile, neutralGameover, skipWaves, teleportPlayer, vnwCondition } from "/utils"; import { VoteManager } from "/votes"; export const commands = commandList({ @@ -785,9 +785,9 @@ Please stop attacking and [lime]build defenses[] first!` .on("player vote change", (t, player) => Call.sendMessage(`VNW: ${player.name} [white] has voted on skipping [accent]${t.session!.data}[white] wave(s). [green]${t.currentVotes()}[white] votes, [green]${t.requiredVotes()}[white] required.`)) .on("player vote removed", (t, player) => Call.sendMessage(`VNW: ${player.name} [white] has left. [green]${t.currentVotes()}[white] votes, [green]${t.requiredVotes()}[white] required.`)) }), - requirements: [Req.cooldown(3000), Req.integerRange("waves", 1, 15), Req.canVnw(), Req.mode("survival", "testsrv"), Req.gameRunning], + requirements: [Req.cooldown(3000), Req.integerRange("waves", 1, 15), Req.mode("survival", "testsrv"), Req.gameRunning], async handler({sender, args: {waves}, data:{manager}}){ - + if (!vnwCondition.check()) fail("You can only do that when all units from previous waves are dead.") //Disable narrowing, this is async if(!manager.session as boolean){ waves ??= await Menu.menu( diff --git a/src/frameworks/commands/requirements.ts b/src/frameworks/commands/requirements.ts index d2fc7905..e06d4b2f 100644 --- a/src/frameworks/commands/requirements.ts +++ b/src/frameworks/commands/requirements.ts @@ -53,8 +53,5 @@ export const Req = { Req.integer(argName)({args}) && (args[argName] == undefined || args[argName] > 0 || fail(`${argName} must be positive`)), - canVnw: - (message = "You can only do that when all units from previous waves are dead.", - message1 = "Stop spamming vnw!",) => ({sender}:{sender:FishPlayer}) => - vnwCondition.check() || fail(Math.random() < 0.9 ? message: message1) + }; From 1932862d6ba9190a21f16f28600df734d3d3585d Mon Sep 17 00:00:00 2001 From: vlado Date: Sat, 4 Jul 2026 11:49:05 +0200 Subject: [PATCH 3/5] fix lint check issues --- src/commands/general.ts | 2 +- src/index.ts | 6 +++--- src/utils.ts | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/commands/general.ts b/src/commands/general.ts index 9c0711cf..ced2358e 100644 --- a/src/commands/general.ts +++ b/src/commands/general.ts @@ -787,7 +787,7 @@ Please stop attacking and [lime]build defenses[] first!` }), requirements: [Req.cooldown(3000), Req.integerRange("waves", 1, 15), Req.mode("survival", "testsrv"), Req.gameRunning], async handler({sender, args: {waves}, data:{manager}}){ - if (!vnwCondition.check()) fail("You can only do that when all units from previous waves are dead.") + if (!vnwCondition.check()) fail("You can only do that when all units from previous waves are dead."); //Disable narrowing, this is async if(!manager.session as boolean){ waves ??= await Menu.menu( diff --git a/src/index.ts b/src/index.ts index dbdf56af..db31dbd8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -326,9 +326,9 @@ Events.on(EventType.PlayEvent, () => { fishState.startTime = Date.now(); }); -Events.on(EventType.UnitDestroyEvent, vnwCondition.removeUnit); -Events.on(EventType.UnitDrownEvent, vnwCondition.removeUnit); -Events.on(EventType.WaveEvent, vnwCondition.onWaveStart); +Events.on(EventType.UnitDestroyEvent, (e) => vnwCondition.removeUnit(e)); +Events.on(EventType.UnitDrownEvent, (e) => vnwCondition.removeUnit(e)); +Events.on(EventType.WaveEvent, () => vnwCondition.onWaveStart()); Events.on(EventType.AdminRequestEvent, e => { if(e.action == Packets.AdminAction.wave){ diff --git a/src/utils.ts b/src/utils.ts index 44274334..2b2fb0f0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -478,24 +478,24 @@ export function skipWaves(requestedWaves: number, runIntermediateWaves: boolean) export const vnwCondition = { waveUnits: [] as number[], addUnit(unitId:number){ - this.waveUnits.push(unitId) + this.waveUnits.push(unitId); }, removeUnit(e:EventType){ - let index = vnwCondition.waveUnits.indexOf(e.unit.id); + const index = vnwCondition.waveUnits.indexOf(e.unit.id); vnwCondition.waveUnits.splice(index,1); }, onWaveStart(){ Groups.unit.each((unit) => { - if (unit.team==getEnemyTeam()) vnwCondition.addUnit(unit.id) + if (unit.team==getEnemyTeam()) vnwCondition.addUnit(unit.id); }); }, check(){ - if (this.waveUnits.length==0) return true - else return false - }, -} + if (this.waveUnits.length==0) return true; + else return false; + } +}; export function logHTrip(player:FishPlayer, name:string, message?:string){ From beabc0ca5a2197f1589cee4f213d8fa8a3939a1c Mon Sep 17 00:00:00 2001 From: vlado Date: Sat, 4 Jul 2026 16:02:02 +0200 Subject: [PATCH 4/5] fix: add requested changes --- src/frameworks/commands/requirements.ts | 2 +- src/index.ts | 2 -- src/utils.ts | 21 ++++++--------------- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/frameworks/commands/requirements.ts b/src/frameworks/commands/requirements.ts index e06d4b2f..87dafab6 100644 --- a/src/frameworks/commands/requirements.ts +++ b/src/frameworks/commands/requirements.ts @@ -8,7 +8,7 @@ import { fail } from "/frameworks/commands/errors"; import { PermType } from "/frameworks/commands/perm"; import type { FishCommandHandlerData } from "/frameworks/commands/types"; import { FishPlayer } from "/players"; -import { formatModeName, outputMessage, vnwCondition } from "/utils"; +import { formatModeName } from "/utils"; export const Req = { mode: (...modes:GamemodeName[]) => () => diff --git a/src/index.ts b/src/index.ts index db31dbd8..7789fd26 100644 --- a/src/index.ts +++ b/src/index.ts @@ -326,8 +326,6 @@ Events.on(EventType.PlayEvent, () => { fishState.startTime = Date.now(); }); -Events.on(EventType.UnitDestroyEvent, (e) => vnwCondition.removeUnit(e)); -Events.on(EventType.UnitDrownEvent, (e) => vnwCondition.removeUnit(e)); Events.on(EventType.WaveEvent, () => vnwCondition.onWaveStart()); Events.on(EventType.AdminRequestEvent, e => { diff --git a/src/utils.ts b/src/utils.ts index 2b2fb0f0..c1097757 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -476,24 +476,15 @@ export function skipWaves(requestedWaves: number, runIntermediateWaves: boolean) } export const vnwCondition = { - waveUnits: [] as number[], - addUnit(unitId:number){ - this.waveUnits.push(unitId); - }, - removeUnit(e:EventType){ - const index = vnwCondition.waveUnits.indexOf(e.unit.id); - vnwCondition.waveUnits.splice(index,1); - }, + waveUnits: [] as Unit[], onWaveStart(){ - Groups.unit.each((unit) => { - - if (unit.team==getEnemyTeam()) vnwCondition.addUnit(unit.id); - }); - + let units = Groups.unit.copy(new Seq()); + let enemyTeam = getEnemyTeam(); + this.waveUnits = units.select(u => u.team === enemyTeam).toArray(); }, check(){ - if (this.waveUnits.length==0) return true; - else return false; + if (this.waveUnits.some(u => !u.dead)) return false; + return true; } }; From eac90181e34500c0710dd058a8e4b8df6c9bd3b1 Mon Sep 17 00:00:00 2001 From: vlado Date: Sat, 4 Jul 2026 17:25:10 +0200 Subject: [PATCH 5/5] fix: make event for /vnw logic fire only on survival --- src/commands/general.ts | 1 + src/index.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/commands/general.ts b/src/commands/general.ts index ced2358e..55b77238 100644 --- a/src/commands/general.ts +++ b/src/commands/general.ts @@ -787,6 +787,7 @@ Please stop attacking and [lime]build defenses[] first!` }), requirements: [Req.cooldown(3000), Req.integerRange("waves", 1, 15), Req.mode("survival", "testsrv"), Req.gameRunning], async handler({sender, args: {waves}, data:{manager}}){ + if (!vnwCondition.check()) fail("You can only do that when all units from previous waves are dead."); //Disable narrowing, this is async if(!manager.session as boolean){ diff --git a/src/index.ts b/src/index.ts index 7789fd26..30ecf439 100644 --- a/src/index.ts +++ b/src/index.ts @@ -326,7 +326,9 @@ Events.on(EventType.PlayEvent, () => { fishState.startTime = Date.now(); }); -Events.on(EventType.WaveEvent, () => vnwCondition.onWaveStart()); +Events.on(EventType.WaveEvent, () => { + if (Vars.state.rules.mode().name() === "survival") vnwCondition.onWaveStart(); +}); Events.on(EventType.AdminRequestEvent, e => { if(e.action == Packets.AdminAction.wave){