mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-24 23:56:51 +00:00
Refactor generator to prevent duplicate reloading
This commit is contained in:
@@ -9,7 +9,7 @@ import { BiomeSourcePreview, DecoratorPreview, DensityFunctionPreview, NoisePrev
|
||||
export const HasPreview = ['dimension', 'worldgen/density_function', 'worldgen/noise', 'worldgen/noise_settings', 'worldgen/configured_feature', 'worldgen/placed_feature']
|
||||
|
||||
type PreviewPanelProps = {
|
||||
model: DataModel | null,
|
||||
model: DataModel | undefined,
|
||||
version: VersionId,
|
||||
id: string,
|
||||
shown: boolean,
|
||||
|
||||
@@ -52,8 +52,8 @@ interface Editor {
|
||||
|
||||
type SourcePanelProps = {
|
||||
name: string,
|
||||
model: DataModel | null,
|
||||
blockStates: BlockStateRegistry | null,
|
||||
model: DataModel | undefined,
|
||||
blockStates: BlockStateRegistry | undefined,
|
||||
doCopy?: number,
|
||||
doDownload?: number,
|
||||
doImport?: number,
|
||||
|
||||
@@ -7,8 +7,8 @@ import type { BlockStateRegistry, VersionId } from '../../services'
|
||||
|
||||
type TreePanelProps = {
|
||||
version: VersionId,
|
||||
model: DataModel | null,
|
||||
blockStates: BlockStateRegistry | null,
|
||||
model: DataModel | undefined,
|
||||
blockStates: BlockStateRegistry | undefined,
|
||||
onError: (message: string) => unknown,
|
||||
}
|
||||
export function Tree({ version, model, blockStates, onError }: TreePanelProps) {
|
||||
|
||||
@@ -58,14 +58,14 @@ export const BiomeSourcePreview = ({ model, data, shown, version }: PreviewProps
|
||||
onLeave() {
|
||||
setFocused(undefined)
|
||||
},
|
||||
}, [state, scale, configuredSeed, layers])
|
||||
}, [version, state, scale, configuredSeed, layers])
|
||||
|
||||
useEffect(() => {
|
||||
if (shown) {
|
||||
res.current = type === 'multi_noise' ? 4 : 1
|
||||
redraw()
|
||||
}
|
||||
}, [state, scale, configuredSeed, layers, shown])
|
||||
}, [version, state, scale, configuredSeed, layers, shown])
|
||||
|
||||
const changeScale = (newScale: number) => {
|
||||
offset.current[0] = offset.current[0] * scale / newScale
|
||||
|
||||
@@ -11,6 +11,8 @@ export const DecoratorPreview = ({ data, version, shown }: PreviewProps) => {
|
||||
const [scale, setScale] = useState(4)
|
||||
const [seed, setSeed] = useState(randomSeed())
|
||||
|
||||
const state = JSON.stringify(data)
|
||||
|
||||
const { canvas, redraw } = useCanvas({
|
||||
size() {
|
||||
return [scale * 16, scale * 16]
|
||||
@@ -18,14 +20,13 @@ export const DecoratorPreview = ({ data, version, shown }: PreviewProps) => {
|
||||
async draw(img) {
|
||||
decorator(data, img, { seed, version, size: [scale * 16, 128, scale * 16] })
|
||||
},
|
||||
})
|
||||
}, [version, state, seed])
|
||||
|
||||
const state = JSON.stringify(data)
|
||||
useEffect(() => {
|
||||
if (shown) {
|
||||
redraw()
|
||||
}
|
||||
}, [state, scale, seed, shown])
|
||||
}, [version, state, scale, seed, shown])
|
||||
|
||||
return <>
|
||||
<div class="controls preview-controls">
|
||||
|
||||
@@ -36,7 +36,7 @@ export const DensityFunctionPreview = ({ data, shown, version }: PreviewProps) =
|
||||
onLeave() {
|
||||
setFocused(undefined)
|
||||
},
|
||||
}, [state, seed])
|
||||
}, [version, state, seed])
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollInterval.current) {
|
||||
@@ -51,7 +51,7 @@ export const DensityFunctionPreview = ({ data, shown, version }: PreviewProps) =
|
||||
}, 100) as any
|
||||
}
|
||||
}
|
||||
}, [state, seed, shown, autoScroll])
|
||||
}, [version, state, seed, shown, autoScroll])
|
||||
|
||||
return <>
|
||||
<div class="controls preview-controls">
|
||||
|
||||
@@ -26,13 +26,13 @@ export const NoisePreview = ({ data, shown, version }: PreviewProps) => {
|
||||
offset.current[1] = offset.current[1] + dy * 256
|
||||
redraw()
|
||||
},
|
||||
}, [state, scale, seed])
|
||||
}, [version, state, scale, seed])
|
||||
|
||||
useEffect(() => {
|
||||
if (shown) {
|
||||
redraw()
|
||||
}
|
||||
}, [state, scale, seed, shown])
|
||||
}, [version, state, scale, seed, shown])
|
||||
|
||||
const changeScale = (newScale: number) => {
|
||||
offset.current[0] = offset.current[0] * scale / newScale
|
||||
|
||||
@@ -62,7 +62,7 @@ export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) =>
|
||||
}
|
||||
})()
|
||||
}
|
||||
}, [state, seed, shown, biome, biomeScale, biomeDepth, autoScroll])
|
||||
}, [version, state, seed, shown, biome, biomeScale, biomeDepth, autoScroll])
|
||||
|
||||
const allBiomes = useMemo(() => CachedCollections?.get('worldgen/biome') ?? [], [version])
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { Inputs } from 'preact/hooks'
|
||||
import { useEffect } from 'preact/hooks'
|
||||
import type { AsyncState } from './useAsyncFn'
|
||||
import type { AsyncCancel, AsyncState } from './useAsyncFn'
|
||||
import { useAsyncFn } from './useAsyncFn'
|
||||
|
||||
export function useAsync<T>(
|
||||
fn: () => Promise<T>,
|
||||
export function useAsync<R>(
|
||||
fn: () => Promise<R | typeof AsyncCancel>,
|
||||
inputs: Inputs = [],
|
||||
): AsyncState<T> {
|
||||
const [state, callback] = useAsyncFn<T, () => Promise<T>>(fn, inputs, { loading: true })
|
||||
initialState: AsyncState<R> = { loading: true },
|
||||
): AsyncState<R> {
|
||||
const [state, callback] = useAsyncFn<R, () => Promise<R | typeof AsyncCancel>>(fn, inputs, initialState)
|
||||
|
||||
useEffect(() => {
|
||||
callback()
|
||||
|
||||
@@ -20,11 +20,13 @@ export type AsyncState<T> = {
|
||||
value: T,
|
||||
}
|
||||
|
||||
export function useAsyncFn<R, T extends (...args: any[]) => Promise<R>>(
|
||||
export const AsyncCancel = Symbol('async-cancel')
|
||||
|
||||
export function useAsyncFn<R, T extends (...args: any[]) => Promise<R | typeof AsyncCancel>>(
|
||||
fn: T,
|
||||
inputs: Inputs = [],
|
||||
initialState: AsyncState<R> = { loading: false },
|
||||
): [AsyncState<R>, (...args: Parameters<T>) => Promise<R | undefined>] {
|
||||
): [AsyncState<R>, (...args: Parameters<T>) => Promise<R | typeof AsyncCancel | undefined>] {
|
||||
const [state, setState] = useState<AsyncState<R>>(initialState)
|
||||
const isMounted = useRef<boolean>(false)
|
||||
const lastCallId = useRef(0)
|
||||
@@ -34,7 +36,7 @@ export function useAsyncFn<R, T extends (...args: any[]) => Promise<R>>(
|
||||
return () => isMounted.current = false
|
||||
}, [])
|
||||
|
||||
const callback = useCallback((...args: Parameters<T>): Promise<R | undefined> => {
|
||||
const callback = useCallback((...args: Parameters<T>): Promise<R | typeof AsyncCancel | undefined> => {
|
||||
const callId = ++lastCallId.current
|
||||
if (!state.loading) {
|
||||
setState(prev => ({ ...prev, loading: true }))
|
||||
@@ -42,7 +44,7 @@ export function useAsyncFn<R, T extends (...args: any[]) => Promise<R>>(
|
||||
|
||||
return fn(...args).then(
|
||||
value => {
|
||||
if (isMounted.current && callId === lastCallId.current) {
|
||||
if (isMounted.current && callId === lastCallId.current && value !== AsyncCancel) {
|
||||
setState({ value, loading: false })
|
||||
}
|
||||
return value
|
||||
|
||||
@@ -25,6 +25,7 @@ export function useSearchParam(param: string): [string | undefined, (value: stri
|
||||
|
||||
const changeValue = useCallback((newValue: string | undefined, replace?: boolean) => {
|
||||
if (newValue !== value) {
|
||||
setValue(newValue)
|
||||
const params = new URLSearchParams(location.search)
|
||||
if (newValue === undefined || newValue.length === 0) {
|
||||
params.delete(param)
|
||||
|
||||
@@ -5,9 +5,9 @@ import config from '../../config.json'
|
||||
import { Analytics } from '../Analytics'
|
||||
import { Ad, Btn, BtnMenu, ErrorPanel, HasPreview, Octicon, PreviewPanel, SearchList, SourcePanel, TextInput, Tree } from '../components'
|
||||
import { useLocale, useProject, useTitle, useVersion } from '../contexts'
|
||||
import { useActiveTimeout, useModel, useSearchParam } from '../hooks'
|
||||
import { AsyncCancel, useActiveTimeout, useAsync, useModel, useSearchParam } from '../hooks'
|
||||
import { getOutput } from '../schema/transformOutput'
|
||||
import type { BlockStateRegistry, VersionId } from '../services'
|
||||
import type { VersionId } from '../services'
|
||||
import { checkVersion, fetchPreset, getBlockStates, getCollections, getModel, getSnippet, shareSnippet, SHARE_KEY } from '../services'
|
||||
import { Store } from '../Store'
|
||||
import { cleanUrl, deepEqual, getGenerator } from '../Utils'
|
||||
@@ -50,35 +50,6 @@ export function Generator({}: Props) {
|
||||
const [currentPreset, setCurrentPreset] = useSearchParam('preset')
|
||||
const [sharedSnippetId, setSharedSnippetId] = useSearchParam(SHARE_KEY)
|
||||
const ignoreChange = useRef(false)
|
||||
useEffect(() => {
|
||||
if (model && currentPreset) {
|
||||
loadPreset(currentPreset).then(preset => {
|
||||
ignoreChange.current = true
|
||||
model.reset(DataModel.wrapLists(preset), false)
|
||||
setSharedSnippetId(undefined)
|
||||
})
|
||||
} else if (model && sharedSnippetId) {
|
||||
getSnippet(sharedSnippetId).then(s => loadSnippet(model, s))
|
||||
}
|
||||
}, [currentPreset, sharedSnippetId])
|
||||
|
||||
const loadSnippet = (model: DataModel, snippet: any) => {
|
||||
if (snippet.version && snippet.version !== version) {
|
||||
changeVersion(snippet.version, false)
|
||||
}
|
||||
if (snippet.type && snippet.type !== gen.id) {
|
||||
const snippetGen = config.generators.find(g => g.id === snippet.type)
|
||||
if (snippetGen) {
|
||||
route(`${cleanUrl(snippetGen.url)}?${SHARE_KEY}=${snippet.id}`)
|
||||
}
|
||||
}
|
||||
if (snippet.show_preview && !previewShown) {
|
||||
setPreviewShown(true)
|
||||
setSourceShown(false)
|
||||
}
|
||||
model.reset(DataModel.wrapLists(snippet.data), false)
|
||||
}
|
||||
|
||||
const backup = useMemo(() => Store.getBackup(gen.id), [gen.id])
|
||||
|
||||
const loadBackup = () => {
|
||||
@@ -87,27 +58,51 @@ export function Generator({}: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
const [model, setModel] = useState<DataModel | null>(null)
|
||||
const [blockStates, setBlockStates] = useState<BlockStateRegistry | null>(null)
|
||||
useEffect(() => {
|
||||
setError(null)
|
||||
setModel(null)
|
||||
getBlockStates(version)
|
||||
.then(b => setBlockStates(b))
|
||||
getModel(version, gen.id)
|
||||
.then(async m => {
|
||||
Analytics.setGenerator(gen.id)
|
||||
if (currentPreset) {
|
||||
const preset = await loadPreset(currentPreset)
|
||||
m.reset(DataModel.wrapLists(preset), false)
|
||||
} else if (sharedSnippetId) {
|
||||
const snippet = await getSnippet(sharedSnippetId)
|
||||
loadSnippet(m, snippet)
|
||||
const { value } = useAsync(async () => {
|
||||
let data: unknown = undefined
|
||||
if (currentPreset && sharedSnippetId) {
|
||||
setSharedSnippetId(undefined)
|
||||
return AsyncCancel
|
||||
}
|
||||
if (currentPreset) {
|
||||
data = await loadPreset(currentPreset)
|
||||
} else if (sharedSnippetId) {
|
||||
const snippet = await getSnippet(sharedSnippetId)
|
||||
let cancel = false
|
||||
if (snippet.version && snippet.version !== version) {
|
||||
changeVersion(snippet.version, false)
|
||||
cancel = true
|
||||
}
|
||||
if (snippet.type && snippet.type !== gen.id) {
|
||||
const snippetGen = config.generators.find(g => g.id === snippet.type)
|
||||
if (snippetGen) {
|
||||
route(`${cleanUrl(snippetGen.url)}?${SHARE_KEY}=${snippet.id}`)
|
||||
cancel = true
|
||||
}
|
||||
setModel(m)
|
||||
})
|
||||
.catch(e => { console.error(e); setError(e) })
|
||||
}, [version, gen.id])
|
||||
}
|
||||
if (cancel) {
|
||||
return AsyncCancel
|
||||
}
|
||||
if (snippet.show_preview && !previewShown) {
|
||||
setPreviewShown(true)
|
||||
setSourceShown(false)
|
||||
}
|
||||
data = snippet.data
|
||||
}
|
||||
const [model, blockStates] = await Promise.all([
|
||||
getModel(version, gen.id),
|
||||
getBlockStates(version),
|
||||
])
|
||||
if (data) {
|
||||
ignoreChange.current = true
|
||||
model.reset(DataModel.wrapLists(data), false)
|
||||
}
|
||||
Analytics.setGenerator(gen.id)
|
||||
return { model, blockStates }
|
||||
}, [gen.id, version, sharedSnippetId, currentPreset])
|
||||
|
||||
const model = value?.model
|
||||
const blockStates = value?.blockStates
|
||||
|
||||
const [dirty, setDirty] = useState(false)
|
||||
useModel(model, () => {
|
||||
|
||||
@@ -60,6 +60,7 @@ export class Deepslate {
|
||||
}
|
||||
|
||||
public generateChunks(minX: number, width: number, biome = 'unknown') {
|
||||
minX = Math.floor(minX)
|
||||
if (!this.settingsCache) {
|
||||
throw new Error('Tried to generate chunks before settings are loaded')
|
||||
}
|
||||
@@ -108,6 +109,8 @@ export class Deepslate {
|
||||
}
|
||||
|
||||
public getBlockState(x: number, y: number) {
|
||||
x = Math.floor(x)
|
||||
y = Math.floor(y)
|
||||
const chunk = this.chunksCache.find(c => this.d.ChunkPos.minBlockX(c.pos) <= x && this.d.ChunkPos.maxBlockX(c.pos) >= x)
|
||||
return chunk?.getBlockState(this.d.BlockPos.create(x, y, Deepslate.Z))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user