cleanup, better resource pack support (#173)
This commit is contained in:
parent
067d4b527f
commit
6bac74b6c1
10 changed files with 114 additions and 4890 deletions
|
|
@ -135,7 +135,9 @@ export class Viewer {
|
|||
let yOffset = this.playerHeight
|
||||
if (this.isSneaking) yOffset -= 0.3
|
||||
|
||||
if (this.world instanceof WorldRendererThree) this.world.camera = cam as THREE.PerspectiveCamera
|
||||
if (this.world instanceof WorldRendererThree) {
|
||||
this.world.camera = cam as THREE.PerspectiveCamera
|
||||
}
|
||||
this.world.updateCamera(pos?.offset(0, yOffset, 0) ?? null, yaw, pitch)
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +151,7 @@ export class Viewer {
|
|||
|
||||
const audioLoader = new THREE.AudioLoader()
|
||||
const start = Date.now()
|
||||
audioLoader.loadAsync(path).then((buffer) => {
|
||||
void audioLoader.loadAsync(path).then((buffer) => {
|
||||
if (Date.now() - start > 500) return
|
||||
// play
|
||||
sound.setBuffer(buffer)
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@ import itemsAtlases from 'mc-assets/dist/itemsAtlases.json'
|
|||
import itemsAtlasLatest from 'mc-assets/dist/itemsAtlasLatest.png'
|
||||
import itemsAtlasLegacy from 'mc-assets/dist/itemsAtlasLegacy.png'
|
||||
import { AtlasParser } from 'mc-assets'
|
||||
import TypedEmitter from 'typed-emitter'
|
||||
import { dynamicMcDataFiles } from '../../buildMesherConfig.mjs'
|
||||
import { getResourcepackTiles } from '../../../src/resourcePack'
|
||||
import { toMajorVersion } from '../../../src/utils'
|
||||
import { buildCleanupDecorator } from './cleanupDecorator'
|
||||
import { defaultMesherConfig } from './mesher/shared'
|
||||
import { loadTexture } from './utils.web'
|
||||
import { loadJSON } from './utils'
|
||||
import { chunkPos } from './simpleUtils'
|
||||
|
||||
function mod (x, n) {
|
||||
|
|
@ -55,8 +53,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
sectionsOutstanding = new Map<string, number>()
|
||||
|
||||
@worldCleanup()
|
||||
renderUpdateEmitter = new EventEmitter()
|
||||
|
||||
renderUpdateEmitter = new EventEmitter() as unknown as TypedEmitter<{
|
||||
dirty (pos: Vec3, value: boolean): void
|
||||
update (/* pos: Vec3, value: boolean */): void
|
||||
textureDownloaded (): void
|
||||
}>
|
||||
customTexturesDataUrl = undefined as string | undefined
|
||||
currentTextureImage = undefined as any
|
||||
workers: any[] = []
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ import nbt from 'prismarine-nbt'
|
|||
import PrismarineChatLoader from 'prismarine-chat'
|
||||
import * as tweenJs from '@tweenjs/tween.js'
|
||||
import { BloomPass, RenderPass, UnrealBloomPass, EffectComposer, WaterPass, GlitchPass } from 'three-stdlib'
|
||||
import worldBlockProvider from 'mc-assets/dist/worldBlockProvider'
|
||||
import { renderSign } from '../sign-renderer'
|
||||
import { chunkPos, sectionPos } from './simpleUtils'
|
||||
import { WorldRendererCommon, WorldRendererConfig } from './worldrendererCommon'
|
||||
import { disposeObject } from './threeJsUtils'
|
||||
import { renderBlockThree } from './mesher/standaloneRenderer'
|
||||
|
||||
export class WorldRendererThree extends WorldRendererCommon {
|
||||
outputFormat = 'threeJs' as const
|
||||
|
|
@ -17,6 +19,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
signsCache = new Map<string, any>()
|
||||
starField: StarField
|
||||
cameraSectionPos: Vec3 = new Vec3(0, 0, 0)
|
||||
cameraGroup = new THREE.Group()
|
||||
|
||||
get tilesRendered () {
|
||||
return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0)
|
||||
|
|
@ -25,6 +28,8 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
constructor (public scene: THREE.Scene, public renderer: THREE.WebGLRenderer, public config: WorldRendererConfig) {
|
||||
super(config)
|
||||
this.starField = new StarField(scene)
|
||||
// this.initCameraGroup()
|
||||
// this.initHandObject()
|
||||
}
|
||||
|
||||
timeUpdated (newTime: number): void {
|
||||
|
|
|
|||
|
|
@ -23,11 +23,14 @@ fs.writeFileSync('./generated/minecraft-data-data.js', mcDataContents, 'utf8')
|
|||
// app resources
|
||||
|
||||
let headerImports = ''
|
||||
let resourcesContent = 'export const appReplacableResources: { [key: string]: { content: any, resourcePackPath: string, cssVar?: string, cssVarRepeat?: number } } = {\n'
|
||||
let resourcesContent = 'export const appReplacableResources: { [key in Keys]: { content: any, resourcePackPath: string, cssVar?: string, cssVarRepeat?: number } } = {\n'
|
||||
let resourcesContentOriginal = 'export const resourcesContentOriginal = {\n'
|
||||
const keys = [] as string[]
|
||||
|
||||
for (const resource of appReplacableResources) {
|
||||
const { path, ...rest } = resource
|
||||
const name = path.split('/').slice(-4).join('_').replace('.png', '').replaceAll('-', '_').replaceAll('.', '_')
|
||||
keys.push(name)
|
||||
headerImports += `import ${name} from '${path.replace('../node_modules/', '')}'\n`
|
||||
resourcesContent += `
|
||||
'${name}': {
|
||||
|
|
@ -35,10 +38,16 @@ for (const resource of appReplacableResources) {
|
|||
resourcePackPath: 'minecraft/textures/${path.slice(path.indexOf('other-textures/') + 'other-textures/'.length).split('/').slice(1).join('/')}',
|
||||
...${JSON.stringify(rest)}
|
||||
},
|
||||
`
|
||||
resourcesContentOriginal += `
|
||||
'${name}': ${name},
|
||||
`
|
||||
}
|
||||
|
||||
resourcesContent += '}'
|
||||
resourcesContent += '}\n'
|
||||
resourcesContent += `type Keys = ${keys.map(k => `'${k}'`).join(' | ')}\n`
|
||||
resourcesContentOriginal += '}\n'
|
||||
resourcesContent += resourcesContentOriginal
|
||||
|
||||
fs.mkdirSync('./src/generated', { recursive: true })
|
||||
fs.writeFileSync('./src/generated/resources.ts', headerImports + '\n' + resourcesContent, 'utf8')
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ import { startLocalServer, unsupportedLocalServerFeatures } from './createLocalS
|
|||
import defaultServerOptions from './defaultLocalServerOptions'
|
||||
import dayCycle from './dayCycle'
|
||||
|
||||
import { onAppLoad, resourcepackOnWorldLoad } from './resourcePack'
|
||||
import { onAppLoad, resourcepackReload } from './resourcePack'
|
||||
import { connectToPeer } from './localServerMultiplayer'
|
||||
import CustomChannelClient from './customClient'
|
||||
import { loadScript } from 'prismarine-viewer/viewer/lib/utils'
|
||||
|
|
@ -403,7 +403,7 @@ async function connect (connectOptions: ConnectOptions) {
|
|||
await loadScript(`./mc-data/${toMajorVersion(version)}.js`)
|
||||
miscUiState.loadedDataVersion = version
|
||||
try {
|
||||
await resourcepackOnWorldLoad(version)
|
||||
await resourcepackReload(version)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
const doContinue = confirm('Failed to apply texture pack. See errors in the console. Continue?')
|
||||
|
|
|
|||
|
|
@ -1,23 +1,8 @@
|
|||
import { proxy, subscribe } from 'valtio'
|
||||
import { showInventory } from 'minecraft-inventory-gui/web/ext.mjs'
|
||||
import InventoryGui from 'mc-assets/dist/other-textures/latest/gui/container/inventory.png'
|
||||
import ChestLikeGui from 'mc-assets/dist/other-textures/latest/gui/container/shulker_box.png'
|
||||
import LargeChestLikeGui from 'mc-assets/dist/other-textures/latest/gui/container/generic_54.png'
|
||||
import FurnaceGui from 'mc-assets/dist/other-textures/latest/gui/container/furnace.png'
|
||||
import CraftingTableGui from 'mc-assets/dist/other-textures/latest/gui/container/crafting_table.png'
|
||||
import DispenserGui from 'mc-assets/dist/other-textures/latest/gui/container/dispenser.png'
|
||||
import HopperGui from 'mc-assets/dist/other-textures/latest/gui/container/hopper.png'
|
||||
import HorseGui from 'mc-assets/dist/other-textures/latest/gui/container/horse.png'
|
||||
import VillagerGui from 'mc-assets/dist/other-textures/latest/gui/container/villager2.png'
|
||||
import EnchantingGui from 'mc-assets/dist/other-textures/latest/gui/container/enchanting_table.png'
|
||||
import AnvilGui from 'mc-assets/dist/other-textures/latest/gui/container/anvil.png'
|
||||
import BeaconGui from 'mc-assets/dist/other-textures/latest/gui/container/beacon.png'
|
||||
import WidgetsGui from 'mc-assets/dist/other-textures/latest/gui/widgets.png'
|
||||
|
||||
// import Dirt from 'mc-assets/dist/other-textures/latest/blocks/dirt.png'
|
||||
import { RecipeItem } from 'minecraft-data'
|
||||
import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils'
|
||||
import _itemsAtlases from 'prismarine-viewer/public/textures/items.json'
|
||||
import { flat, fromFormattedString } from '@xmcl/text-component'
|
||||
import mojangson from 'mojangson'
|
||||
import nbt from 'prismarine-nbt'
|
||||
|
|
@ -25,36 +10,18 @@ import { splitEvery, equals } from 'rambda'
|
|||
import PItem, { Item } from 'prismarine-item'
|
||||
import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer'
|
||||
import Generic95 from '../assets/generic_95.png'
|
||||
import { appReplacableResources } from './generated/resources'
|
||||
import { activeModalStack, hideCurrentModal, hideModal, miscUiState, showModal } from './globalState'
|
||||
import invspriteJson from './invsprite.json'
|
||||
import { options } from './optionsStorage'
|
||||
import { assertDefined, inGameError } from './utils'
|
||||
import { MessageFormatPart } from './botUtils'
|
||||
import { currentScaling } from './scaleInterface'
|
||||
import { descriptionGenerators, getItemDescription } from './itemsDescriptions'
|
||||
import { getItemDescription } from './itemsDescriptions'
|
||||
|
||||
const loadedImagesCache = new Map<string, HTMLImageElement>()
|
||||
const cleanLoadedImagesCache = () => {
|
||||
loadedImagesCache.delete('blocks')
|
||||
}
|
||||
export type BlockStates = Record<string, null | {
|
||||
variants: Record<string, {
|
||||
model: {
|
||||
elements: [{
|
||||
faces: {
|
||||
[face: string]: {
|
||||
texture: {
|
||||
u
|
||||
v
|
||||
su
|
||||
sv
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}>
|
||||
}>
|
||||
|
||||
let lastWindow: ReturnType<typeof showInventory>
|
||||
/** bot version */
|
||||
|
|
@ -143,22 +110,22 @@ export const onGameLoad = (onLoad) => {
|
|||
const getImageSrc = (path): string | HTMLImageElement => {
|
||||
assertDefined(viewer)
|
||||
switch (path) {
|
||||
case 'gui/container/inventory': return InventoryGui
|
||||
case 'gui/container/inventory': return appReplacableResources.latest_gui_container_inventory.content
|
||||
case 'blocks': return viewer.world.blocksAtlasParser!.latestImage
|
||||
case 'items': return viewer.world.itemsAtlasParser!.latestImage
|
||||
case 'gui/container/dispenser': return DispenserGui
|
||||
case 'gui/container/furnace': return FurnaceGui
|
||||
case 'gui/container/crafting_table': return CraftingTableGui
|
||||
case 'gui/container/shulker_box': return ChestLikeGui
|
||||
case 'gui/container/generic_54': return LargeChestLikeGui
|
||||
case 'gui/container/dispenser': return appReplacableResources.latest_gui_container_dispenser.content
|
||||
case 'gui/container/furnace': return appReplacableResources.latest_gui_container_furnace.content
|
||||
case 'gui/container/crafting_table': return appReplacableResources.latest_gui_container_crafting_table.content
|
||||
case 'gui/container/shulker_box': return appReplacableResources.latest_gui_container_shulker_box.content
|
||||
case 'gui/container/generic_54': return appReplacableResources.latest_gui_container_generic_54.content
|
||||
case 'gui/container/generic_95': return Generic95
|
||||
case 'gui/container/hopper': return HopperGui
|
||||
case 'gui/container/horse': return HorseGui
|
||||
case 'gui/container/villager2': return VillagerGui
|
||||
case 'gui/container/enchanting_table': return EnchantingGui
|
||||
case 'gui/container/anvil': return AnvilGui
|
||||
case 'gui/container/beacon': return BeaconGui
|
||||
case 'gui/widgets': return WidgetsGui
|
||||
case 'gui/container/hopper': return appReplacableResources.latest_gui_container_hopper.content
|
||||
case 'gui/container/horse': return appReplacableResources.latest_gui_container_horse.content
|
||||
case 'gui/container/villager2': return appReplacableResources.latest_gui_container_villager2.content
|
||||
case 'gui/container/enchanting_table': return appReplacableResources.latest_gui_container_enchanting_table.content
|
||||
case 'gui/container/anvil': return appReplacableResources.latest_gui_container_anvil.content
|
||||
case 'gui/container/beacon': return appReplacableResources.latest_gui_container_beacon.content
|
||||
case 'gui/widgets': return appReplacableResources.other_textures_latest_gui_widgets.content
|
||||
}
|
||||
// empty texture
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
|
||||
|
|
|
|||
4812
src/invsprite.json
4812
src/invsprite.json
File diff suppressed because it is too large
Load diff
|
|
@ -62,7 +62,7 @@ export default () => {
|
|||
return
|
||||
}
|
||||
const upStatus = () => {
|
||||
setVersionStatus(`(${isLatest ? 'latest' : 'new version available'}) ${mainMenuState.serviceWorkerLoaded ? '(Available Offline)' : ''}`)
|
||||
setVersionStatus(`(${isLatest ? 'latest' : 'new version available'}${mainMenuState.serviceWorkerLoaded ? ' - Available Offline' : ''})`)
|
||||
}
|
||||
subscribe(mainMenuState, upStatus)
|
||||
setVersionTitle(`Loaded: ${process.env.BUILD_VERSION}. Remote: ${contents}`)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import { join, dirname, basename } from 'path'
|
||||
import fs from 'fs'
|
||||
import JSZip from 'jszip'
|
||||
import { proxy, ref } from 'valtio'
|
||||
import type { BlockStates } from './inventoryWindows'
|
||||
import { copyFilesAsync, copyFilesAsyncWithProgress, mkdirRecursive, removeFileRecursiveAsync } from './browserfs'
|
||||
import { proxy, subscribe } from 'valtio'
|
||||
import { mkdirRecursive, removeFileRecursiveAsync } from './browserfs'
|
||||
import { setLoadingScreenStatus } from './utils'
|
||||
import { showNotification } from './react/NotificationProvider'
|
||||
import { options } from './optionsStorage'
|
||||
import { showOptionsModal } from './react/SelectOption'
|
||||
import { appStatusState } from './react/AppStatusProvider'
|
||||
import { appReplacableResources } from './generated/resources'
|
||||
import { appReplacableResources, resourcesContentOriginal } from './generated/resources'
|
||||
|
||||
export const resourcePackState = proxy({
|
||||
resourcePackInstalled: false,
|
||||
currentTexturesDataUrl: undefined as string | undefined,
|
||||
currentTexturesBlockStates: undefined as BlockStates | undefined,
|
||||
})
|
||||
|
||||
const getLoadedImage = async (url: string) => {
|
||||
|
|
@ -111,9 +108,7 @@ export const completeTexturePackInstall = async (displayName: string, name: stri
|
|||
const basePath = texturePackBasePath2 + name
|
||||
await fs.promises.writeFile(join(basePath, 'name.txt'), displayName, 'utf8')
|
||||
|
||||
if (viewer?.world.active) {
|
||||
await updateTextures()
|
||||
}
|
||||
await updateTextures()
|
||||
setLoadingScreenStatus(undefined)
|
||||
showNotification('Texturepack installed & enabled')
|
||||
await updateTexturePackInstalledState()
|
||||
|
|
@ -290,11 +285,15 @@ export const onAppLoad = () => {
|
|||
bot.acceptResourcePack()
|
||||
})
|
||||
})
|
||||
|
||||
subscribe(resourcePackState, () => {
|
||||
if (!resourcePackState.resourcePackInstalled) return
|
||||
void updateAllReplacableTextures()
|
||||
})
|
||||
}
|
||||
|
||||
const setOtherTexturesCss = async () => {
|
||||
const updateAllReplacableTextures = async () => {
|
||||
const basePath = await getActiveTexturepackBasePath()
|
||||
// TODO! fallback to default
|
||||
const setCustomCss = async (path: string | null, varName: string, repeat = 1) => {
|
||||
if (path && await existsAsync(path)) {
|
||||
const contents = await fs.promises.readFile(path, 'base64')
|
||||
|
|
@ -304,10 +303,25 @@ const setOtherTexturesCss = async () => {
|
|||
document.body.style.setProperty(varName, '')
|
||||
}
|
||||
}
|
||||
const vars = Object.values(appReplacableResources).filter(x => x.cssVar)
|
||||
for (const { cssVar, cssVarRepeat, resourcePackPath } of vars) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await setCustomCss(`${basePath}/assets/${resourcePackPath}`, cssVar!, cssVarRepeat ?? 1)
|
||||
const setCustomPicture = async (key: string, path: string) => {
|
||||
let contents = resourcesContentOriginal[key]
|
||||
if (await existsAsync(path)) {
|
||||
const file = await fs.promises.readFile(path, 'base64')
|
||||
const dataUrl = `data:image/png;base64,${file}`
|
||||
contents = dataUrl
|
||||
}
|
||||
appReplacableResources[key].content = contents
|
||||
}
|
||||
const vars = Object.entries(appReplacableResources).filter(([, x]) => x.cssVar)
|
||||
for (const [key, { cssVar, cssVarRepeat, resourcePackPath }] of vars) {
|
||||
const resPath = `${basePath}/assets/${resourcePackPath}`
|
||||
if (cssVar) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await setCustomCss(resPath, cssVar, cssVarRepeat ?? 1)
|
||||
} else {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await setCustomPicture(key, resPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +332,7 @@ const updateTextures = async () => {
|
|||
const itemsFiles = Object.keys(viewer.world.itemsAtlases.latest.textures)
|
||||
const blocksData = await getResourcepackTiles('blocks', blocksFiles)
|
||||
const itemsData = await getResourcepackTiles('items', itemsFiles)
|
||||
await setOtherTexturesCss()
|
||||
await updateAllReplacableTextures()
|
||||
await prepareBlockstatesAndModels()
|
||||
viewer.world.customTextures = {}
|
||||
if (blocksData) {
|
||||
|
|
@ -338,6 +352,6 @@ const updateTextures = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
export const resourcepackOnWorldLoad = async (version) => {
|
||||
export const resourcepackReload = async (version) => {
|
||||
await updateTextures()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ export const appReplacableResources: Array<{
|
|||
cssVar?: string
|
||||
cssVarRepeat?: number
|
||||
}> = [
|
||||
// GUI
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/title/minecraft.png',
|
||||
cssVar: '--title-gui',
|
||||
|
|
@ -19,5 +20,42 @@ export const appReplacableResources: Array<{
|
|||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/bars.png',
|
||||
cssVar: '--bars-gui-atlas',
|
||||
}
|
||||
},
|
||||
// container
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/inventory.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/shulker_box.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/generic_54.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/furnace.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/crafting_table.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/dispenser.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/hopper.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/horse.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/villager2.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/enchanting_table.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/anvil.png',
|
||||
},
|
||||
{
|
||||
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/beacon.png',
|
||||
},
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue