Skip to content

Commit 2fa705c

Browse files
authored
bug:: fix demo clamp (#238)
1 parent dbd9066 commit 2fa705c

1 file changed

Lines changed: 28 additions & 25 deletions

File tree

src/matches/clips/clips.service.ts

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,7 +1999,18 @@ export class ClipsService {
19991999
const tail = Math.round(tickRate * 3);
20002000
const CLUSTER_GAP_SECS = 10;
20012001
const clusterGapTicks = Math.round(tickRate * CLUSTER_GAP_SECS);
2002-
const clamp = (t: number) => Math.max(0, Math.min(t, totalTicks || t));
2002+
// Demo's total_ticks is the literal last observed tick — i.e. gameover.
2003+
// Playing a segment up to that tick triggers the engine's gameover
2004+
// transition and auto-closes the demo, breaking every subsequent job
2005+
// in the same batch. Hold segments a few seconds short of demo end.
2006+
const SAFETY_SECS = 3;
2007+
const safetyTicks = Math.round(tickRate * SAFETY_SECS);
2008+
const maxClipEnd = totalTicks > 0 ? totalTicks - safetyTicks : 0;
2009+
const clamp = (t: number) =>
2010+
Math.max(0, maxClipEnd > 0 ? Math.min(t, maxClipEnd) : t);
2011+
const MIN_SEGMENT_TICKS = Math.round(tickRate * 1.5);
2012+
const segmentIsViable = (s: { start_tick: number; end_tick: number }) =>
2013+
s.end_tick - s.start_tick >= MIN_SEGMENT_TICKS;
20032014

20042015
const clusterKills = (
20052016
ks: Array<{ tick: number }>,
@@ -2059,40 +2070,30 @@ export class ClipsService {
20592070
segments.push(...clusterKills(inRound));
20602071
}
20612072
} else if (preset === "best_round") {
2062-
let best: {
2063-
round: number;
2073+
// Build all viable candidates so that if the kill-leader round
2074+
// happens to be the final round (kills land past the safety
2075+
// margin), we naturally fall through to the next-best round
2076+
// instead of producing an empty/unrenderable clip.
2077+
const candidates: Array<{
20642078
count: number;
20652079
span: number;
2066-
start: number;
2067-
end: number;
2068-
} | null = null;
2080+
segs: Array<{ start_tick: number; end_tick: number }>;
2081+
}> = [];
20692082
for (const r of rounds) {
20702083
const inRound = myKills
20712084
.filter((k) => k.tick >= r.start_tick && k.tick <= r.end_tick)
20722085
.sort((a, b) => a.tick - b.tick);
20732086
const count = inRound.length;
20742087
if (count === 0) continue;
2088+
const segs = clusterKills(inRound).filter(segmentIsViable);
2089+
if (segs.length === 0) continue;
20752090
const span = count >= 2 ? inRound[count - 1].tick - inRound[0].tick : 0;
2076-
if (
2077-
!best ||
2078-
count > best.count ||
2079-
(count === best.count && span < best.span)
2080-
) {
2081-
best = {
2082-
round: r.round,
2083-
count,
2084-
span,
2085-
start: r.start_tick,
2086-
end: r.end_tick,
2087-
};
2088-
}
2091+
candidates.push({ count, span, segs });
20892092
}
2090-
if (best && best.count > 0) {
2091-
stats.bestRoundKills = best.count;
2092-
const inRound = myKills
2093-
.filter((k) => k.tick >= best!.start && k.tick <= best!.end)
2094-
.sort((a, b) => a.tick - b.tick);
2095-
segments = clusterKills(inRound);
2093+
candidates.sort((a, b) => b.count - a.count || a.span - b.span);
2094+
if (candidates.length > 0) {
2095+
stats.bestRoundKills = candidates[0].count;
2096+
segments = candidates[0].segs;
20962097
}
20972098
} else if (preset === "recap") {
20982099
segments = clusterKills(myKills);
@@ -2110,6 +2111,8 @@ export class ClipsService {
21102111
stats.usedFallback = true;
21112112
}
21122113

2114+
segments = segments.filter(segmentIsViable);
2115+
21132116
if (segments.length === 0) {
21142117
throw new Error(
21152118
`no clip-worthy moments for ${targetSteamId} in this match — preset "${preset}" produced zero segments and the player has no kills to fall back on`,

0 commit comments

Comments
 (0)