diff options
-rw-r--r-- | web/pw-server/package.json | 4 | ||||
-rw-r--r-- | web/pw-server/src/lib/components/OutputPane.svelte | 143 | ||||
-rw-r--r-- | web/pw-server/src/lib/components/PlayerLog.svelte | 152 | ||||
-rw-r--r-- | web/pw-server/src/lib/components/Visualizer.svelte | 13 | ||||
-rw-r--r-- | web/pw-server/src/lib/constants.ts | 11 | ||||
-rw-r--r-- | web/pw-server/src/routes/editor.svelte | 1 | ||||
-rw-r--r-- | web/pw-server/src/routes/matches/[match_id].svelte | 42 | ||||
-rw-r--r-- | web/pw-server/svelte.config.js | 8 |
8 files changed, 216 insertions, 158 deletions
diff --git a/web/pw-server/package.json b/web/pw-server/package.json index 474f38c..604a820 100644 --- a/web/pw-server/package.json +++ b/web/pw-server/package.json @@ -26,7 +26,7 @@ "prettier": "^2.4.1", "prettier-plugin-svelte": "^2.4.0", "sass": "^1.49.7", - "svelte": "^3.44.0", + "svelte": "^3.52.0", "svelte-check": "^2.2.6", "svelte-preprocess": "^4.9.4", "tslib": "^2.3.1", @@ -41,7 +41,7 @@ "dayjs": "^1.10.7", "planetwars-rs": "file:../planetwars-rs/pkg", "pw-visualizer": "file:../pw-visualizer", - "svelte-select": "^4.4.7" + "svelte-select": "^5.0.0-beta.31" }, "type": "module" } diff --git a/web/pw-server/src/lib/components/OutputPane.svelte b/web/pw-server/src/lib/components/OutputPane.svelte index 91ffadc..68c5c3b 100644 --- a/web/pw-server/src/lib/components/OutputPane.svelte +++ b/web/pw-server/src/lib/components/OutputPane.svelte @@ -1,157 +1,22 @@ <script lang="ts"> - import { parsePlayerLog, PlayerLog } from "$lib/log_parser"; + import PlayerLog from "./PlayerLog.svelte"; export let matchLog: string; - let playerLog: PlayerLog; - - let showRawStderr = false; - - const PLURAL_MAP = { - dispatch: "dispatches", - ship: "ships", - }; - - function pluralize(num: number, word: string): string { - if (num == 1) { - return `1 ${word}`; - } else { - return `${num} ${PLURAL_MAP[word]}`; - } - } - - $: if (matchLog) { - playerLog = parsePlayerLog(1, matchLog); - } else { - playerLog = []; - } </script> -<div class="output"> +<div class="output-pane"> <h3 class="output-header">Player log</h3> - {#if showRawStderr} - <div class="output-text stderr-text"> - {playerLog.flatMap((turn) => turn.stderr).join("\n")} - </div> - {:else} - <div class="output-text"> - {#each playerLog as logTurn, i} - <div class="turn"> - <div class="turn-header"> - <span class="turn-header-text">Turn {i}</span> - {#if logTurn.action?.type === "dispatches"} - {pluralize(logTurn.action.dispatches.length, "dispatch")} - {:else if logTurn.action?.type === "timeout"} - <span class="turn-error">timeout</span> - {:else if logTurn.action?.type === "bad_command"} - <span class="turn-error">invalid command</span> - {/if} - </div> - {#if logTurn.action?.type === "dispatches"} - <div class="dispatches-container"> - {#each logTurn.action.dispatches as dispatch} - <div class="dispatch"> - <div class="dispatch-text"> - {pluralize(dispatch.ship_count, "ship")} from {dispatch.origin} to {dispatch.destination} - </div> - {#if dispatch.error} - <span class="dispatch-error">{dispatch.error}</span> - {/if} - </div> - {/each} - </div> - {:else if logTurn.action?.type === "bad_command"} - <div class="bad-command-container"> - <div class="bad-command-text">{logTurn.action.command}</div> - <div class="bad-command-error">Parse error: {logTurn.action.error}</div> - </div> - {/if} - {#if logTurn.stderr.length > 0} - <div class="stderr-header">stderr</div> - <div class="stderr-text-box"> - {#each logTurn.stderr as stdErrMsg} - <div class="stderr-text">{stdErrMsg}</div> - {/each} - </div> - {/if} - </div> - {/each} - </div> - {/if} + <PlayerLog {matchLog} playerId={1} /> </div> <style lang="scss"> - .output { + .output-pane { width: 100%; overflow-y: scroll; background-color: rgb(41, 41, 41); padding: 15px; } - .turn { - margin: 16px 4px; - } - - .output-text { - color: #ccc; - } - - .turn-header { - display: flex; - justify-content: space-between; - } - - .turn-header-text { - color: #eee; - font-size: 14px; - font-weight: 600; - text-transform: uppercase; - } - - .turn-error { - color: red; - } - - .dispatch { - display: flex; - justify-content: space-between; - } - - .dispatch-error { - color: red; - } - - .bad-command-container { - border-left: 1px solid red; - margin-left: 4px; - padding-left: 8px; - } - - .bad-command-text { - font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; - padding-bottom: 4px; - } - - .bad-command-error { - color: whitesmoke; - } - - .stderr-text { - // font-family: monospace; - font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; - white-space: pre-wrap; - } - - .stderr-header { - color: #eee; - padding-top: 4px; - } - - .stderr-text-box { - border-left: 1px solid #ccc; - margin-left: 4px; - padding-left: 8px; - } - .output-header { color: #eee; padding-bottom: 20px; diff --git a/web/pw-server/src/lib/components/PlayerLog.svelte b/web/pw-server/src/lib/components/PlayerLog.svelte new file mode 100644 index 0000000..52cae8e --- /dev/null +++ b/web/pw-server/src/lib/components/PlayerLog.svelte @@ -0,0 +1,152 @@ +<script lang="ts"> + import { parsePlayerLog, PlayerLog } from "$lib/log_parser"; + + export let matchLog: string; + export let playerId: number; + + let playerLog: PlayerLog; + + let showRawStderr = false; + + const PLURAL_MAP = { + dispatch: "dispatches", + ship: "ships", + }; + + function pluralize(num: number, word: string): string { + if (num == 1) { + return `1 ${word}`; + } else { + return `${num} ${PLURAL_MAP[word]}`; + } + } + + $: if (matchLog) { + playerLog = parsePlayerLog(playerId, matchLog); + } else { + playerLog = []; + } +</script> + +<div class="output"> + {#if showRawStderr} + <div class="output-text stderr-text"> + {playerLog.flatMap((turn) => turn.stderr).join("\n")} + </div> + {:else} + <div class="output-text"> + {#each playerLog as logTurn, i} + <div class="turn"> + <div class="turn-header"> + <span class="turn-header-text">Turn {i}</span> + {#if logTurn.action?.type === "dispatches"} + {pluralize(logTurn.action.dispatches.length, "dispatch")} + {:else if logTurn.action?.type === "timeout"} + <span class="turn-error">timeout</span> + {:else if logTurn.action?.type === "bad_command"} + <span class="turn-error">invalid command</span> + {/if} + </div> + {#if logTurn.action?.type === "dispatches"} + <div class="dispatches-container"> + {#each logTurn.action.dispatches as dispatch} + <div class="dispatch"> + <div class="dispatch-text"> + {pluralize(dispatch.ship_count, "ship")} from {dispatch.origin} to {dispatch.destination} + </div> + {#if dispatch.error} + <span class="dispatch-error">{dispatch.error}</span> + {/if} + </div> + {/each} + </div> + {:else if logTurn.action?.type === "bad_command"} + <div class="bad-command-container"> + <div class="bad-command-text">{logTurn.action.command}</div> + <div class="bad-command-error">Parse error: {logTurn.action.error}</div> + </div> + {/if} + {#if logTurn.stderr.length > 0} + <div class="stderr-header">stderr</div> + <div class="stderr-text-box"> + {#each logTurn.stderr as stdErrMsg} + <div class="stderr-text">{stdErrMsg}</div> + {/each} + </div> + {/if} + </div> + {/each} + </div> + {/if} +</div> + +<style lang="scss"> + .output { + background-color: rgb(41, 41, 41); + } + + .turn { + margin: 16px 4px; + } + + .output-text { + color: #ccc; + } + + .turn-header { + display: flex; + justify-content: space-between; + } + + .turn-header-text { + color: #eee; + font-size: 14px; + font-weight: 600; + text-transform: uppercase; + } + + .turn-error { + color: red; + } + + .dispatch { + display: flex; + justify-content: space-between; + } + + .dispatch-error { + color: red; + } + + .bad-command-container { + border-left: 1px solid red; + margin-left: 4px; + padding-left: 8px; + } + + .bad-command-text { + font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; + padding-bottom: 4px; + } + + .bad-command-error { + color: whitesmoke; + } + + .stderr-text { + // font-family: monospace; + font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; + white-space: pre-wrap; + } + + .stderr-header { + color: #eee; + padding-top: 4px; + } + + .stderr-text-box { + border-left: 1px solid #ccc; + margin-left: 4px; + padding-left: 8px; + } +</style> diff --git a/web/pw-server/src/lib/components/Visualizer.svelte b/web/pw-server/src/lib/components/Visualizer.svelte index 8517a39..4fe8ab4 100644 --- a/web/pw-server/src/lib/components/Visualizer.svelte +++ b/web/pw-server/src/lib/components/Visualizer.svelte @@ -2,24 +2,13 @@ import { onDestroy, onMount } from "svelte"; import * as visualizer from "pw-visualizer"; import init_wasm_module from "planetwars-rs"; + import { PLAYER_COLORS } from "$lib/constants"; export let matchLog = null; export let matchData: object; // match object as returned by api let initialized = false; - const PLAYER_COLORS = [ - "#ff8000", - "#0080ff", - "#ff6693", - "#3fcb55", - "#cbc33f", - "#cf40e9", - "#ff3f0d", - "#1beef0", - "#0dc5ff", - ]; - onMount(async () => { await init_wasm_module(); diff --git a/web/pw-server/src/lib/constants.ts b/web/pw-server/src/lib/constants.ts new file mode 100644 index 0000000..208bf09 --- /dev/null +++ b/web/pw-server/src/lib/constants.ts @@ -0,0 +1,11 @@ +export const PLAYER_COLORS = [ + "#ff8000", + "#0080ff", + "#ff6693", + "#3fcb55", + "#cbc33f", + "#cf40e9", + "#ff3f0d", + "#1beef0", + "#0dc5ff", +]; diff --git a/web/pw-server/src/routes/editor.svelte b/web/pw-server/src/routes/editor.svelte index ff8232c..ee4eef9 100644 --- a/web/pw-server/src/routes/editor.svelte +++ b/web/pw-server/src/routes/editor.svelte @@ -12,7 +12,6 @@ import { debounce } from "$lib/utils"; import SubmitPane from "$lib/components/SubmitPane.svelte"; import OutputPane from "$lib/components/OutputPane.svelte"; - import BotName from "./bots/[bot_name].svelte"; enum ViewMode { Editor, diff --git a/web/pw-server/src/routes/matches/[match_id].svelte b/web/pw-server/src/routes/matches/[match_id].svelte index 11d6ee3..25438ad 100644 --- a/web/pw-server/src/routes/matches/[match_id].svelte +++ b/web/pw-server/src/routes/matches/[match_id].svelte @@ -22,6 +22,9 @@ <script lang="ts"> import { onMount } from "svelte"; import Visualizer from "$lib/components/Visualizer.svelte"; + import PlayerLog from "$lib/components/PlayerLog.svelte"; + import Select from "svelte-select"; + import { PLAYER_COLORS } from "$lib/constants"; export let matchLog: string | undefined; export let matchData: object; @@ -30,13 +33,35 @@ const apiClient = new ApiClient(); matchLog = await apiClient.getText(`/api/matches/${matchData["id"]}/log`); }); + + let selectedPlayer; + + $: matchPlayerSelectItems = matchData["players"].map((player: any, index: number) => ({ + color: PLAYER_COLORS[index], + value: index, + playerId: index + 1, // stoopid player number + 1 + label: player["bot_name"], + })); </script> <div class="container"> <Visualizer {matchLog} {matchData} /> + <div class="output-pane"> + <div class="player-select"> + <Select items={matchPlayerSelectItems} clearable={false} bind:value={selectedPlayer}> + <div slot="item" let:item> + <span style:color={item.color}>{item.label}</span> + </div> + </Select> + </div> + <div class="player-log"> + <PlayerLog {matchLog} playerId={selectedPlayer?.playerId} /> + </div> + </div> </div> <style lang="scss"> + @use "src/styles/variables"; .container { display: flex; // these are needed for making the visualizer fill the screen. @@ -44,4 +69,21 @@ flex-grow: 1; overflow: hidden; } + + .player-select { + padding: 20px; + } + + .player-log { + padding: 15px; + overflow-y: scroll; + } + + .output-pane { + width: 600px; + // overflow: hidden; + display: flex; + flex-direction: column; + background-color: variables.$bg-color; + } </style> diff --git a/web/pw-server/svelte.config.js b/web/pw-server/svelte.config.js index fe396f9..02b4db9 100644 --- a/web/pw-server/svelte.config.js +++ b/web/pw-server/svelte.config.js @@ -12,13 +12,13 @@ const config = { preprocess: [ sveltePreprocess(), mdsvex({ - extensions: ['.md'], + extensions: [".md"], layout: { - docs: 'src/routes/docs/doc.svelte', - } + docs: "src/routes/docs/doc.svelte", + }, }), ], - extensions: ['.svelte', '.md'], + extensions: [".svelte", ".md"], kit: { adapter: adapter(), |