Skip to content

Commit

Permalink
fixes synchronization for automappers
Browse files Browse the repository at this point in the history
  • Loading branch information
k2d222 committed Nov 24, 2024
1 parent caa7bd0 commit 13a1050
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 115 deletions.
2 changes: 1 addition & 1 deletion client/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export class WebSocketServer extends EventDispatcher<Recv> implements Server {

// we predict an ok response from the server and dispatch right away.
// if the server replies with err(), or if the requests are out of order,
// the history will take care of resyncing.
// the history will take care of resyncing by undoing dirty predictions.
this.dispatch(type as any, content, promise.then())

this.socket.send(message)
Expand Down
1 change: 1 addition & 0 deletions client/src/ui/lib/AutomapperConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

60 changes: 27 additions & 33 deletions client/src/ui/lib/automapper.svelte
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
<script lang="ts">
import type { TilesLayer } from '../../twmap/tilesLayer'
import { lint as lintAutomapper, LintLevel, lintToString } from '../../twmap/automap'
import type { AutomapperConfig } from '../../twmap/mapdir'
import { lint as lintAutomapper, LintLevel } from '../../twmap/automap'
import { showError, showInfo, showWarning } from './dialog'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import { server, automappers } from '../global'
import type { Recv } from 'src/server/protocol'
import { createEventDispatcher } from 'svelte'
import { server } from '../global'
export let layer: TilesLayer
export let automapper: AutomapperConfig | null
export let configs: string[]
let dispatch = createEventDispatcher<{ change: number }>()
$: configs = $automappers[layer.image?.name + '.rules']?.configs ?? []
let dispatch = createEventDispatcher<{ change: AutomapperConfig }>()
async function onFileChange(e: Event) {
const input = e.target as HTMLInputElement
if (input.files === null || input.files.length === 0) return
const file = input.files[0]
const name = file.name.replace(/.rules$/, '')
const str = await file.text()
// const newRules = parseAutomapper(str) ?? []
Expand All @@ -32,57 +29,54 @@
}
try {
await $server.query('create/automapper', [file.name, str])
const res = await $server.query('create/automapper', [file.name, str])
if (res.length !== 0) {
showWarning(
`Automapper has ${res.length} issues(s). Check the autommapper tab for more details.`
)
}
} catch (e) {
showError('Saving failed: ' + e)
return
}
showInfo(`Uploaded '${name}'.`, 'closable')
showInfo(`Uploaded '${file.name}'.`)
}
async function onConfig() {
dispatch('change', layer.automapper.config)
}
function onSync([_g, _l, e]: Recv['edit/layer']) {
if ('automapper_config' in e || 'image' in e) layer = layer
dispatch('change', automapper)
}
onMount(() => {
$server.on('edit/layer', onSync)
})
onDestroy(() => {
$server.off('edit/layer', onSync)
})
</script>

<div class="edit-automapper">
<label>
Upload rules
<input type="file" placeholder="upload rules…" accept=".rules" on:change={onFileChange} />
<input type="file" placeholder="upload rules…" accept=".rules,.rpp" on:change={onFileChange} />
</label>

<label>
Config
<select bind:value={layer.automapper.config} on:change={onConfig}>
<option value={-1}>None</option>
<select bind:value={automapper.config} on:change={onConfig}>
<option value={-1} selected={automapper.config === -1 || automapper.config === null}>
None
</option>
{#each configs as conf, i}
<option value={i}>#{i} {conf}</option>
<option value={i} selected={automapper.config === i}>#{i} {conf}</option>
{/each}
{#if layer.automapper.config >= configs.length}
<option value={layer.automapper.config}>#{layer.automapper.config} (missing)</option>
{#if automapper.config >= configs.length}
<option value={automapper.config} selected>
#{automapper.config} (missing)
</option>
{/if}
</select>
</label>

<label>
Seed
<input type="number" bind:value={layer.automapper.seed} on:change={onConfig} />
<input type="number" bind:value={automapper.seed} on:change={onConfig} />
</label>
<label>
Automatic
<input type="checkbox" bind:checked={layer.automapper.automatic} on:change={onConfig} />
<input type="checkbox" bind:checked={automapper.automatic} on:change={onConfig} />
</label>
</div>
46 changes: 32 additions & 14 deletions client/src/ui/lib/editLayer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import type { Envelope } from '../../twmap/map'
import { type FormInputEvent, layerKind } from './util'
import { TilesLayerFlags } from '../../twmap/types'
import type { Group } from '../../twmap/group'
import { AnyTilesLayer, TilesLayer, GameLayer } from '../../twmap/tilesLayer'
import { QuadsLayer } from '../../twmap/quadsLayer'
import { showInfo, showError, clearDialog } from '../lib/dialog'
Expand All @@ -23,14 +24,28 @@
import type * as Info from '../../twmap/types'
import type * as MapDir from '../../twmap/mapdir'
import Number from './number.svelte'
import type { RenderLayer } from '../../gl/renderLayer'
import type { RenderGroup } from '../../gl/renderGroup'
export let g: number, l: number
let rlayer: RenderLayer | null
let rgroup: RenderGroup | null
let layer: (TilesLayer | QuadsLayer) | null
let group: Group | null
$: rgroup = g === -1 ? null : $rmap.groups[g]
$: rlayer = l === -1 ? null : rgroup.layers[l]
$: group = rgroup === null ? null : rgroup.group
$: layer = rlayer === null ? null : (rlayer.layer as TilesLayer | QuadsLayer)
let amCfgs: string[]
$: if (syncImg && $syncImg !== null) {
amCfgs = $automappers[$syncImg.name + '.rules']?.configs ?? []
} else {
amCfgs = []
}
function clamp(cur: number, min: number, max: number) {
return Math.min(Math.max(min, cur), max)
}
Expand Down Expand Up @@ -75,11 +90,11 @@
}
}
function amCfgName(img: string, cfg: number | null): string {
if (cfg === null) {
function amCfgName(cfgs: string[], cfg: number | null): string {
if (cfg === null || cfg === -1) {
return 'None'
} else {
return $automappers[img + '.rules']?.configs?.at(cfg) ?? `#${cfg} (missing)`
return cfgs.at(cfg) ?? `#${cfg} (missing)`
}
}
Expand Down Expand Up @@ -112,7 +127,7 @@
}
// new image from gallery
else {
const [name, size] = e.detail
const [name, _size] = e.detail
const url = externalImageUrl(name)
const embed = await showInfo('Do you wish to embed this image?', 'yesno')
if (embed) {
Expand Down Expand Up @@ -175,7 +190,7 @@
let syncColorEnvOff: Writable<number>
let syncImgs: Readable<Image[]>
let syncImg: Readable<Image | null>
let syncAmCfg: Readable<number | null>
let syncAmCfg: Readable<MapDir.AutomapperConfig>
let syncColEnvs: Readable<Envelope[]>
$: syncGroup = sync($server, g, {
Expand Down Expand Up @@ -277,9 +292,9 @@
},
])
$: if (layer && layer instanceof TilesLayer) {
syncAmCfg = read($server, layer.automapper.config === -1 ? null : layer.automapper.config, {
syncAmCfg = read($server, layer.automapper, {
query: 'edit/layer',
match: [g, l, { automapper_config: { config: pick } }],
match: [g, l, { automapper_config: pick }],
})
}
$: if (layer && layer instanceof TilesLayer) {
Expand Down Expand Up @@ -325,18 +340,17 @@
// $rmap.automapLayer(g, l, conf, tlayer.automapper.seed)
// }, 5000)
}
async function onAutomapperChange() {
async function onAutomapperChange(cfg: MapDir.AutomapperConfig) {
if (!layer) return
const automapper = (layer as TilesLayer).automapper
await $server.query('edit/layer', [
g,
l,
{
type: layerKind(layer),
automapper_config: {
config: automapper.config === -1 ? null : automapper.config,
seed: automapper.seed,
automatic: automapper.automatic,
config: cfg.config,
seed: cfg.seed,
automatic: cfg.automatic,
},
},
])
Expand Down Expand Up @@ -404,15 +418,19 @@
</span>
<input
type="button"
value={amCfgName($syncImg?.name, $syncAmCfg)}
value={amCfgName(amCfgs, $syncAmCfg.config)}
disabled={$syncImg === null}
on:click={() => (automapperOpen = true)}
/>
</label>
<ComposedModal bind:open={automapperOpen} size="sm" selectorPrimaryFocus=".bx--modal-close">
<ModalHeader title="Automapper" />
<ModalBody hasForm>
<AutomapperPicker {layer} on:change={onAutomapperChange} />
<AutomapperPicker
automapper={$syncAmCfg}
configs={amCfgs}
on:change={e => onAutomapperChange(e.detail)}
/>
</ModalBody>
</ComposedModal>
<button
Expand Down
42 changes: 7 additions & 35 deletions client/src/ui/lib/editor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,30 @@
import { LayerType } from '../../twmap/types'
import { AnyTilesLayer, GameLayer } from '../../twmap/tilesLayer'
import { onMount, onDestroy } from 'svelte'
import { server, rmap, selected, automappers, anim, peers } from '../global'
import { server, rmap, selected, anim, peers } from '../global'
import TreeView from './treeView.svelte'
import { showError } from './dialog'
import EnvelopeEditor from './envelopeEditor.svelte'
import * as Editor from './editor'
import { px2vw, rem2px } from './util'
import { px2vh, px2vw, rem2px } from './util'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import LayerEditor from './editLayer.svelte'
import GroupEditor from './editGroup.svelte'
import MapEditor from './mapEditor.svelte'
import { Add as CreateGroupIcon } from 'carbon-icons-svelte'
import { Button } from 'carbon-components-svelte'
import { navigate } from 'svelte-routing'
import { dataToTiles, tilesLayerFlagsToLayerKind } from '../../server/convert'
import * as Actions from '../actions'
import { viewport } from '../../gl/global'
import Fence from './fence.svelte'
import type { AutomapperKind, Recv, Tiles } from '../../server/protocol'
import type { Recv, Tiles } from '../../server/protocol'
// split panes
let layerPaneSize = px2vw(rem2px(10))
let propsPaneSize = px2vw(rem2px(10))
let envPaneSize = px2vw(rem2px(10))
let layerPaneSize = Math.min(33, px2vw(rem2px(15)))
let propsPaneSize = Math.min(33, px2vw(rem2px(15)))
let envPaneSize = Math.min(33, px2vh(rem2px(10)))
let lastLayerPaneSize = layerPaneSize
let lastPropsPaneSize = propsPaneSize
let lastEnvPaneSize = 20
let lastEnvPaneSize = envPaneSize
let closedPaneThreshold = px2vw(rem2px(2))
// computed (readonly)
Expand Down Expand Up @@ -83,39 +81,16 @@
$rmap.editTile({ g, l, x, y, ...tile })
}
}
function serverOnDeleteAutomapper(e: Recv['delete/automapper']) {
delete $automappers[e]
$automappers = $automappers
}
function serverOnUploadAutomapper([name, file]: Recv['create/automapper']) {
const kind = name.slice(name.lastIndexOf('.') + 1) as AutomapperKind
const image = name.slice(0, name.lastIndexOf('.'))
$automappers[name] = {
name,
image,
file,
kind,
}
$automappers = $automappers
}
async function onServerClosed() {
await showError('You have been disconnected from the server.')
navigate('/')
}
let signalLoaded: () => void
let loadSignal: Promise<void> = new Promise(resolve => {
signalLoaded = resolve
})
onMount(() => {
$selected = [$rmap.map.physicsLayerIndex(GameLayer)]
$server.socket.addEventListener('close', onServerClosed, { once: true })
$server.on('users', serverOnUsers)
$server.on('edit/tiles', serverOnEditTiles)
$server.on('edit/automap', serverOnApplyAutomapper)
$server.on('delete/automapper', serverOnDeleteAutomapper)
$server.on('create/automapper', serverOnUploadAutomapper)
$server.query('get/users', undefined).then(u => ($peers = u))
viewport.canvas.addEventListener('mouseenter', onHoverCanvas)
Expand All @@ -124,12 +99,9 @@
})
onDestroy(() => {
$server.socket.removeEventListener('error', onServerClosed)
$server.off('users', serverOnUsers)
$server.off('edit/tiles', serverOnEditTiles)
$server.off('edit/automap', serverOnApplyAutomapper)
$server.off('delete/automapper', serverOnDeleteAutomapper)
$server.off('create/automapper', serverOnUploadAutomapper)
viewport.canvas.removeEventListener('mouseenter', onHoverCanvas)
})
Expand Down
4 changes: 2 additions & 2 deletions client/src/ui/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ export function rem2px(rem: number) {
return parseFloat(window.getComputedStyle(document.documentElement).fontSize) * rem
}
export function px2vw(px: number) {
return (px / window.screen.width) * 100
return (px / window.innerWidth) * 100
}
export function px2vh(px: number) {
return (px / window.screen.height) * 100
return (px / window.innerHeight) * 100
}
Loading

0 comments on commit 13a1050

Please sign in to comment.