add "fix player count" option to editor
All checks were successful
CI / checks (push) Successful in 32s

This commit is contained in:
Robin Appelman 2026-09-10 00:26:44 +02:00
commit 26c3cd67ae
4 changed files with 156 additions and 98 deletions

8
package-lock.json generated
View file

@ -5,7 +5,7 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"@demostf/edit": "0.2.0", "@demostf/edit": "0.2.1",
"@demostf/tf-demos-viewer": "^0.3.3", "@demostf/tf-demos-viewer": "^0.3.3",
"@lutaok/solid-modal": "^0.1.1", "@lutaok/solid-modal": "^0.1.1",
"@solid-primitives/autofocus": "^0.0.111", "@solid-primitives/autofocus": "^0.0.111",
@ -18,9 +18,9 @@
} }
}, },
"node_modules/@demostf/edit": { "node_modules/@demostf/edit": {
"version": "0.2.0", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@demostf/edit/-/edit-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@demostf/edit/-/edit-0.2.1.tgz",
"integrity": "sha512-s9wk3QVm+aTpMhIyfdGIHRm5qHp7FQ1dq/Jn0fms+lXsB1xY3wgjfWH+5gwRjjo/Dd3UMNM0o3atjO2uh+CxOQ==" "integrity": "sha512-0wtwe717/iyO8awK2iZV2gUopZxl/ApBq1w3h9sDfPdI+58CBUMFlIItJMuWtpis8VfiHoQRR3l1qmTe49+5vQ=="
}, },
"node_modules/@demostf/tf-demos-viewer": { "node_modules/@demostf/tf-demos-viewer": {
"version": "0.3.3", "version": "0.3.3",

View file

@ -1,6 +1,6 @@
{ {
"dependencies": { "dependencies": {
"@demostf/edit": "0.2.0", "@demostf/edit": "0.2.1",
"@demostf/tf-demos-viewer": "^0.3.3", "@demostf/tf-demos-viewer": "^0.3.3",
"@lutaok/solid-modal": "^0.1.1", "@lutaok/solid-modal": "^0.1.1",
"@solid-primitives/autofocus": "^0.0.111", "@solid-primitives/autofocus": "^0.0.111",

View file

@ -1,106 +1,163 @@
import {Duration, formatDuration} from '../components/Duration'; import { Duration, formatDuration } from "../components/Duration";
import Element = JSX.Element; import Element = JSX.Element;
import {Section} from "../components/Section"; import { Section } from "../components/Section";
import MultiRangeSlider from "../components/MultiSlider"; import MultiRangeSlider from "../components/MultiSlider";
import {DemoHead} from "../header"; import { DemoHead } from "../header";
import {createSignal} from "solid-js"; import { createSignal } from "solid-js";
import {downloadBuffer, edit} from "./tools"; import { downloadBuffer, edit } from "./tools";
export interface EditPageProps { export interface EditPageProps {
header: DemoHead; header: DemoHead;
demoData: ArrayBuffer; demoData: ArrayBuffer;
setDropText: (string) => void, setDropText: (string) => void;
name: string, name: string;
} }
export const Editor = (props: EditPageProps) => { export const Editor = (props: EditPageProps) => {
let demoInfo: any[] | Element = []; let demoInfo: any[] | Element = [];
demoInfo = ( demoInfo = (
<div className="demo-info"> <div className="demo-info">
{props.header.map} {props.header.map}
<Duration className="time" <Duration className="time" duration={Math.floor(props.header.duration)} />
duration={Math.floor(props.header.duration)}/> </div>
</div> );
);
const [loading, setLoading] = createSignal(false); const [loading, setLoading] = createSignal(false);
const [cutFrom, setCutFrom] = createSignal(0); const [cutFrom, setCutFrom] = createSignal(0);
const [cutTo, setCutTo] = createSignal(props.header.ticks); const [cutTo, setCutTo] = createSignal(props.header.ticks);
const intervalPerTick = props.header.duration / props.header.ticks; const intervalPerTick = props.header.duration / props.header.ticks;
const [unlockPov, setUnlockPov] = createSignal(false); const [unlockPov, setUnlockPov] = createSignal(false);
const [fixPlayerCount, setFixPlayerCount] = createSignal(true);
const editCb = async () => { const editCb = async () => {
try { try {
setLoading(true); setLoading(true);
(props.setDropText)("Editing..."); props.setDropText("Editing...");
await doEdit(props.name, props.demoData, props.header, unlockPov(), cutFrom(), cutTo()); await doEdit(
(props.setDropText)(props.name); props.name,
setLoading(false); props.demoData,
} catch (e) { props.header,
(props.setDropText)("Error: " + e.toString()); unlockPov(),
setLoading(false); fixPlayerCount(),
} cutFrom(),
cutTo(),
);
props.setDropText(props.name);
setLoading(false);
} catch (e) {
props.setDropText("Error: " + e.toString());
setLoading(false);
} }
};
return ( return (
<div> <div>
{demoInfo} {demoInfo}
<Section title="Unlock camera"> <Section title="Unlock camera">
<ul key={1}> <ul key={1}>
<li key={1}>Unlocks the camera in pov demos, allowing free movement as if it were an stv demo.</li> <li key={1}>
<li key={2}>When the player respawns the camera will be moved.</li> Unlocks the camera in pov demos, allowing free movement as if it
<li key={3}>As pov demos only contain data near the player, far away players might freeze, teleport or otherwise behave weirdly.</li> were an stv demo.
</ul> </li>
<p key={2}> <li key={2}>When the player respawns the camera will be moved.</li>
<input type="checkbox" id="pov-unlock" <li key={3}>
checked={unlockPov() ? "checked" : null} As pov demos only contain data near the player, far away players
onChange={() => { might freeze, teleport or otherwise behave weirdly.
setUnlockPov(!unlockPov()) </li>
}}/> </ul>
<label htmlFor="pov-unlock">Unlock camara for pov demo</label> <p key={2}>
</p> <input
</Section> type="checkbox"
<Section title="Cut demo"> id="pov-unlock"
<ul key={1}> checked={unlockPov() ? "checked" : null}
<li key={1}>Cuts the demo file to the selected tick range.</li> onChange={() => {
<li key={2}>Cutting demos is experimental, resulting demo files might crash, have broken animations or have other issues.</li> setUnlockPov(!unlockPov());
<li key={3}>Changing the specific cut range can sometimes work around issues with broken demos.</li> }}
</ul> />
<p key={2}> <label htmlFor="pov-unlock">Unlock camara for pov demo</label>
<MultiRangeSlider </p>
min={0} </Section>
max={props.header.ticks} <Section title="Cut demo">
onChange={(min, max) => { <ul key={1}>
if (min !== cutFrom() || max !== cutTo()) { <li key={1}>Cuts the demo file to the selected tick range.</li>
setCutFrom(min); <li key={2}>
setCutTo(max); Cutting demos is experimental, resulting demo files might crash,
} have broken animations or have other issues.
}} </li>
labelFn={(ticks) => formatDuration(ticks * intervalPerTick)} <li key={3}>
/> Changing the specific cut range can sometimes work around issues
</p> with broken demos.
</Section> </li>
<Section title="Process"> </ul>
<p key={1}> <p key={2}>
<button onClick={editCb} <MultiRangeSlider
className="pure-button pure-button-primary" min={0}
disabled={loading() ? "disabled" : null}> max={props.header.ticks}
{() => loading() ? 'Processing...' : 'Edit'} onChange={(min, max) => {
</button> if (min !== cutFrom() || max !== cutTo()) {
</p> setCutFrom(min);
</Section> setCutTo(max);
</div> }
); }}
labelFn={(ticks) => formatDuration(ticks * intervalPerTick)}
/>
</p>
</Section>
<Section title="Fix player count">
<ul key={1}>
<li key={1}>
The 2023-07-25 update increased the max player count from 32 to 100,
this broke playing some demos from before the update.
</li>
<li key={2}>
This option attempts to make demos from before the update
compatible.
</li>
<li key={3}>Using it on newer demos should have no effect.</li>
</ul>
<p key={2}>
<input
type="checkbox"
id="fix-player-count"
checked={fixPlayerCount() ? "checked" : null}
onChange={() => {
setFixPlayerCount(!fixPlayerCount());
}}
/>
<label htmlFor="fix-player-count">Fix player count</label>
</p>
</Section>
<Section title="Process">
<p key={1}>
<button
onClick={editCb}
className="pure-button pure-button-primary"
disabled={loading() ? "disabled" : null}
>
{() => (loading() ? "Processing..." : "Edit")}
</button>
</p>
</Section>
</div>
);
};
async function doEdit(
name: string,
data: ArrayBuffer,
header: DemoHead,
unlockPov: boolean,
fixPlayerCount: boolean,
from: number,
to: number,
) {
let options = {
unlock_pov: unlockPov,
fix_player_count: fixPlayerCount,
cut: from > 0 || to < header.ticks ? { from, to } : undefined,
};
console.log(options);
const edited = await edit(data, options);
downloadBuffer(edited, name.replace(".dem", "_edited.dem"));
} }
async function doEdit(name: string, data: ArrayBuffer, header: DemoHead, unlockPov: boolean, from: number, to: number) {
let options = {
unlock_pov: unlockPov,
cut: (from > 0 || to < header.ticks) ? {from, to} : undefined,
}
console.log(options);
const edited = await edit(data, options);
downloadBuffer(edited, name.replace('.dem', '_edited.dem'));
}

View file

@ -1,5 +1,6 @@
export interface EditOptions { export interface EditOptions {
unlock_pov: boolean, unlock_pov: boolean,
fix_player_count: boolean,
cut?: TickRange, cut?: TickRange,
} }