a decent release (#225)
This commit is contained in:
commit
c8416261c6
37 changed files with 421 additions and 187 deletions
3
.github/workflows/next-deploy.yml
vendored
3
.github/workflows/next-deploy.yml
vendored
|
|
@ -20,6 +20,9 @@ jobs:
|
|||
run: npm install --global vercel pnpm@9.0.4
|
||||
- name: Pull Vercel Environment Information
|
||||
run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
|
||||
- name: Write Release Info
|
||||
run: |
|
||||
echo "{\"latestTag\": \"$(git rev-parse --short $GITHUB_SHA)\"}" > assets/release.json
|
||||
- name: Build Project Artifacts
|
||||
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
|
||||
- run: pnpm build-storybook
|
||||
|
|
|
|||
11
.github/workflows/publish.yml
vendored
11
.github/workflows/publish.yml
vendored
|
|
@ -20,6 +20,13 @@ jobs:
|
|||
- run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
|
||||
- run: node scripts/replaceFavicon.mjs ${{ secrets.FAVICON_MAIN }}
|
||||
# will install + build to .vercel/output/static
|
||||
- name: Get Release Info
|
||||
run: pnpx zardoy-release empty --skip-github
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: releaseInfo
|
||||
- name: Write Release Info
|
||||
run: echo '${{ toJson(steps.releaseInfo.outputs) }}' > assets/release.json
|
||||
- run: vercel build --token=${{ secrets.VERCEL_TOKEN }} --prod
|
||||
- run: pnpm build-storybook
|
||||
- name: Copy playground files
|
||||
|
|
@ -47,6 +54,10 @@ jobs:
|
|||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: .vercel/output/static
|
||||
force_orphan: true
|
||||
- name: Set publishing config
|
||||
run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}"
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- run: pnpm tsx scripts/buildNpmReact.ts ${{ steps.release.outputs.tag }}
|
||||
if: steps.release.outputs.tag
|
||||
env:
|
||||
|
|
|
|||
|
|
@ -4,6 +4,15 @@
|
|||
<meta name="darkreader-lock">
|
||||
<script>
|
||||
window.startLoad = Date.now()
|
||||
// g663 fix: forbid change of string prototype
|
||||
Object.defineProperty(String.prototype, 'format', {
|
||||
writable: false,
|
||||
configurable: false
|
||||
});
|
||||
Object.defineProperty(String.prototype, 'replaceAll', {
|
||||
writable: false,
|
||||
configurable: false
|
||||
});
|
||||
</script>
|
||||
<!-- // #region initial loader -->
|
||||
<script async>
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"express": "^4.18.2",
|
||||
"filesize": "^10.0.12",
|
||||
"flying-squid": "npm:@zardoy/flying-squid@^0.0.44",
|
||||
"flying-squid": "npm:@zardoy/flying-squid@^0.0.47",
|
||||
"fs-extra": "^11.1.1",
|
||||
"google-drive-browserfs": "github:zardoy/browserfs#google-drive",
|
||||
"jszip": "^3.10.1",
|
||||
|
|
|
|||
30
pnpm-lock.yaml
generated
30
pnpm-lock.yaml
generated
|
|
@ -119,8 +119,8 @@ importers:
|
|||
specifier: ^10.0.12
|
||||
version: 10.0.12
|
||||
flying-squid:
|
||||
specifier: npm:@zardoy/flying-squid@^0.0.44
|
||||
version: '@zardoy/flying-squid@0.0.44(encoding@0.1.13)'
|
||||
specifier: npm:@zardoy/flying-squid@^0.0.47
|
||||
version: '@zardoy/flying-squid@0.0.47(encoding@0.1.13)'
|
||||
fs-extra:
|
||||
specifier: ^11.1.1
|
||||
version: 11.1.1
|
||||
|
|
@ -350,7 +350,7 @@ importers:
|
|||
version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0)
|
||||
mineflayer:
|
||||
specifier: github:zardoy/mineflayer
|
||||
version: https://codeload.github.com/zardoy/mineflayer/tar.gz/519acba455d3387414b81fcaa12e550dcdc4f88e(encoding@0.1.13)
|
||||
version: https://codeload.github.com/zardoy/mineflayer/tar.gz/ece6755d94931116924874d9f55bc024998cc1ae(encoding@0.1.13)
|
||||
mineflayer-pathfinder:
|
||||
specifier: ^2.4.4
|
||||
version: 2.4.4
|
||||
|
|
@ -3395,8 +3395,8 @@ packages:
|
|||
resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==}
|
||||
engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'}
|
||||
|
||||
'@zardoy/flying-squid@0.0.44':
|
||||
resolution: {integrity: sha512-Co1fmBU1FkDD3WxiMzIZ4z2pYKFVeLGfdWJT2qQg0eQ9kP064NteOlI5sNNBjMqedNsZSlQdHzT+jkgcFnaGsQ==}
|
||||
'@zardoy/flying-squid@0.0.47':
|
||||
resolution: {integrity: sha512-VUtOqPGZ/20tQEjRLFpbz0taoTMi0GgoUM7002wn8RjuVmowg0pMUjdy0YwcFPGla8z1sOwjsF9cOtU4hQ8pUg==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
|
|
@ -4052,6 +4052,10 @@ packages:
|
|||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chalk@5.3.0:
|
||||
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
|
||||
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
|
||||
|
||||
change-case@4.1.2:
|
||||
resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==}
|
||||
|
||||
|
|
@ -6799,8 +6803,8 @@ packages:
|
|||
resolution: {integrity: sha512-wSchhS59hK+oPs8tFg847H82YEvxU7zYKdDKj4e5FVo3CxJ74eXJVT+JcFwEvoqFO7kXiQlhJITxEvO13GOSKA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/519acba455d3387414b81fcaa12e550dcdc4f88e:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/519acba455d3387414b81fcaa12e550dcdc4f88e}
|
||||
mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/ece6755d94931116924874d9f55bc024998cc1ae:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/ece6755d94931116924874d9f55bc024998cc1ae}
|
||||
version: 4.23.0
|
||||
engines: {node: '>=18'}
|
||||
|
||||
|
|
@ -7517,7 +7521,7 @@ packages:
|
|||
|
||||
prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05}
|
||||
version: 1.17.1
|
||||
version: 1.19.0
|
||||
|
||||
prismarine-chat@1.10.1:
|
||||
resolution: {integrity: sha512-XukYcuueuhDxzEXG7r8BZyt6jOObrPPB4JESCgb+/XenB9nExoSHF8eTQWWj8faKPLqm1dRQaYwFJlNBlJZJUw==}
|
||||
|
|
@ -13415,11 +13419,11 @@ snapshots:
|
|||
'@types/emscripten': 1.39.8
|
||||
tslib: 1.14.1
|
||||
|
||||
'@zardoy/flying-squid@0.0.44(encoding@0.1.13)':
|
||||
'@zardoy/flying-squid@0.0.47(encoding@0.1.13)':
|
||||
dependencies:
|
||||
'@tootallnate/once': 2.0.0
|
||||
chalk: 5.3.0
|
||||
change-case: 4.1.2
|
||||
colors: 1.4.0
|
||||
diamond-square: https://codeload.github.com/zardoy/diamond-square/tar.gz/cfaad2d1d5909fdfa63c8cc7bc05fb5e87782d71
|
||||
emit-then: 2.0.0
|
||||
exit-hook: 2.2.1
|
||||
|
|
@ -13525,7 +13529,7 @@ snapshots:
|
|||
|
||||
agent-base@7.1.0:
|
||||
dependencies:
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
debug: 4.3.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -14268,6 +14272,8 @@ snapshots:
|
|||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
chalk@5.3.0: {}
|
||||
|
||||
change-case@4.1.2:
|
||||
dependencies:
|
||||
camel-case: 4.1.2
|
||||
|
|
@ -17821,7 +17827,7 @@ snapshots:
|
|||
- encoding
|
||||
- supports-color
|
||||
|
||||
mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/519acba455d3387414b81fcaa12e550dcdc4f88e(encoding@0.1.13):
|
||||
mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/ece6755d94931116924874d9f55bc024998cc1ae(encoding@0.1.13):
|
||||
dependencies:
|
||||
minecraft-data: 3.78.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/590dc33fed2100e77ef58e7db716dfc45eb61159(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13)
|
||||
|
|
|
|||
|
|
@ -281,9 +281,14 @@ export class BasePlaygroundScene {
|
|||
|
||||
addKeyboardShortcuts () {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'KeyR' && !e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
||||
this.controls?.reset()
|
||||
this.resetCamera()
|
||||
if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
||||
if (e.code === 'KeyR') {
|
||||
this.controls?.reset()
|
||||
this.resetCamera()
|
||||
}
|
||||
if (e.code === 'KeyE') {
|
||||
worldView?.setBlockStateId(this.targetPos, this.world.getBlockStateId(this.targetPos))
|
||||
}
|
||||
}
|
||||
})
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
|
|
@ -297,6 +302,9 @@ export class BasePlaygroundScene {
|
|||
})
|
||||
|
||||
const updateKeys = () => {
|
||||
if (pressedKeys.has('ControlLeft') || pressedKeys.has('MetaLeft')) {
|
||||
return
|
||||
}
|
||||
// if (typeof viewer === 'undefined') return
|
||||
// Create a vector that points in the direction the camera is looking
|
||||
const direction = new THREE.Vector3(0, 0, 0)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import * as scenes from './scenes'
|
|||
|
||||
const qsScene = new URLSearchParams(window.location.search).get('scene')
|
||||
const Scene: typeof BasePlaygroundScene = qsScene ? scenes[qsScene] : scenes.main
|
||||
playgroundGlobalUiState.scenes = ['main', 'railsCobweb', 'floorRandom', 'lightingStarfield', 'transparencyIssue', 'entities', 'frequentUpdates']
|
||||
playgroundGlobalUiState.scenes = ['main', 'railsCobweb', 'floorRandom', 'lightingStarfield', 'transparencyIssue', 'entities', 'frequentUpdates', 'slabsOptimization']
|
||||
playgroundGlobalUiState.selected = qsScene ?? 'main'
|
||||
|
||||
const scene = new Scene()
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ export { default as transparencyIssue } from './transparencyIssue'
|
|||
export { default as rotationIssue } from './rotationIssue'
|
||||
export { default as entities } from './entities'
|
||||
export { default as frequentUpdates } from './frequentUpdates'
|
||||
export { default as slabsOptimization } from './slabsOptimization'
|
||||
|
|
|
|||
15
prismarine-viewer/examples/scenes/slabsOptimization.ts
Normal file
15
prismarine-viewer/examples/scenes/slabsOptimization.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { BasePlaygroundScene } from '../baseScene'
|
||||
|
||||
export default class extends BasePlaygroundScene {
|
||||
expectedNumberOfFaces = 30
|
||||
|
||||
setupWorld () {
|
||||
this.addWorldBlock(0, 1, 0, 'stone_slab')
|
||||
this.addWorldBlock(0, 0, 0, 'stone')
|
||||
this.addWorldBlock(0, -1, 0, 'stone_slab', { type: 'top', waterlogged: false })
|
||||
this.addWorldBlock(0, -1, -1, 'stone_slab', { type: 'top', waterlogged: false })
|
||||
this.addWorldBlock(0, -1, 1, 'stone_slab', { type: 'top', waterlogged: false })
|
||||
this.addWorldBlock(-1, -1, 0, 'stone_slab', { type: 'top', waterlogged: false })
|
||||
this.addWorldBlock(1, -1, 0, 'stone_slab', { type: 'top', waterlogged: false })
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ export const appAndRendererSharedConfig = () => defineConfig({
|
|||
},
|
||||
server: {
|
||||
htmlFallback: false,
|
||||
publicDir: false,
|
||||
// publicDir: false,
|
||||
headers: {
|
||||
// enable shared array buffer
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import stevePng from 'mc-assets/dist/other-textures/latest/entity/player/wide/st
|
|||
import { NameTagObject } from 'skinview3d/libs/nametag'
|
||||
import { flat, fromFormattedString } from '@xmcl/text-component'
|
||||
import mojangson from 'mojangson'
|
||||
import { snakeCase } from 'change-case'
|
||||
import * as Entity from './entity/EntityMesh'
|
||||
import { WalkingGeneralSwing } from './entity/animations'
|
||||
import externalTexturesJson from './entity/externalTextures.json'
|
||||
|
|
@ -63,11 +64,13 @@ const addNametag = (entity, options, mesh) => {
|
|||
// todo cleanup
|
||||
const nametags = {}
|
||||
|
||||
const isFirstUpperCase = (str) => str.charAt(0) === str.charAt(0).toUpperCase()
|
||||
|
||||
function getEntityMesh (entity, scene, options, overrides) {
|
||||
if (entity.name) {
|
||||
try {
|
||||
// https://github.com/PrismarineJS/prismarine-viewer/pull/410
|
||||
const entityName = entity.name.toLowerCase()
|
||||
const entityName = (isFirstUpperCase(entity.name) ? snakeCase(entity.name) : entity.name).toLowerCase()
|
||||
const e = new Entity.EntityMesh('1.16.4', entityName, scene, overrides)
|
||||
|
||||
if (e.mesh) {
|
||||
|
|
@ -340,7 +343,6 @@ export class Entities extends EventEmitter {
|
|||
}
|
||||
|
||||
update (entity: import('prismarine-entity').Entity & { delete?; pos }, overrides) {
|
||||
console.log('entity', entity)
|
||||
const isPlayerModel = entity.name === 'player'
|
||||
if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') {
|
||||
overrides.texture = `textures/1.16.4/entity/${entity.name === 'zombie_villager' ? 'zombie_villager/zombie_villager.png' : `zombie/${entity.name}.png`}`
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ function setSectionDirty (pos, value = true) {
|
|||
const softCleanup = () => {
|
||||
// clean block cache and loaded chunks
|
||||
world = new World(world.config.version)
|
||||
globalThis.world = world
|
||||
}
|
||||
|
||||
const handleMessage = data => {
|
||||
|
|
@ -75,8 +76,10 @@ const handleMessage = data => {
|
|||
}
|
||||
|
||||
if (data.config) {
|
||||
if (data.type === 'mesherData' && allDataReady) {
|
||||
world = undefined as any // reset models
|
||||
if (data.type === 'mesherData' && world) {
|
||||
// reset models
|
||||
world.blockCache = {}
|
||||
world.erroredBlockModel = undefined
|
||||
}
|
||||
|
||||
world ??= new World(data.config.version)
|
||||
|
|
|
|||
|
|
@ -196,13 +196,53 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ
|
|||
}
|
||||
}
|
||||
|
||||
let needRecompute = false
|
||||
const identicalCull = (currentElement: BlockElement, neighbor: Block, direction: Vec3) => {
|
||||
const dirStr = `${direction.x},${direction.y},${direction.z}`
|
||||
const lookForOppositeSide = {
|
||||
'0,1,0': 'down',
|
||||
'0,-1,0': 'up',
|
||||
'1,0,0': 'east',
|
||||
'-1,0,0': 'west',
|
||||
'0,0,1': 'south',
|
||||
'0,0,-1': 'north',
|
||||
}[dirStr]!
|
||||
const elemCompareForm = {
|
||||
'0,1,0': (e: BlockElement) => `${e.from[0]},${e.from[2]}:${e.to[0]},${e.to[2]}`,
|
||||
'0,-1,0': (e: BlockElement) => `${e.to[0]},${e.to[2]}:${e.from[0]},${e.from[2]}`,
|
||||
'1,0,0': (e: BlockElement) => `${e.from[2]},${e.from[1]}:${e.to[2]},${e.to[1]}`,
|
||||
'-1,0,0': (e: BlockElement) => `${e.to[2]},${e.to[1]}:${e.from[2]},${e.from[1]}`,
|
||||
'0,0,1': (e: BlockElement) => `${e.from[1]},${e.from[2]}:${e.to[1]},${e.to[2]}`,
|
||||
'0,0,-1': (e: BlockElement) => `${e.to[1]},${e.to[2]}:${e.from[1]},${e.from[2]}`,
|
||||
}[dirStr]!
|
||||
const elementEdgeValidator = {
|
||||
'0,1,0': (e: BlockElement) => currentElement.from[1] === 0 && e.to[2] === 16,
|
||||
'0,-1,0': (e: BlockElement) => currentElement.from[1] === 0 && e.to[2] === 16,
|
||||
'1,0,0': (e: BlockElement) => currentElement.from[0] === 0 && e.to[1] === 16,
|
||||
'-1,0,0': (e: BlockElement) => currentElement.from[0] === 0 && e.to[1] === 16,
|
||||
'0,0,1': (e: BlockElement) => currentElement.from[2] === 0 && e.to[0] === 16,
|
||||
'0,0,-1': (e: BlockElement) => currentElement.from[2] === 0 && e.to[0] === 16,
|
||||
}[dirStr]!
|
||||
const useVar = 0
|
||||
const models = neighbor.models?.map(m => m[useVar] ?? m[0]) ?? []
|
||||
// TODO we should support it! rewrite with optimizing general pipeline
|
||||
if (models.some(m => m.x || m.y || m.z)) return
|
||||
for (const model of models) {
|
||||
for (const element of model.elements ?? []) {
|
||||
// todo check alfa on texture
|
||||
if (element.faces[lookForOppositeSide]?.cullface && elemCompareForm(currentElement) === elemCompareForm(element) && elementEdgeValidator(element)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let needSectionRecomputeOnChange = false
|
||||
|
||||
function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: boolean, attr: MesherGeometryOutput, globalMatrix: any, globalShift: any, block: Block, biome: string) {
|
||||
const position = cursor
|
||||
// const key = `${position.x},${position.y},${position.z}`
|
||||
// if (!globalThis.allowedBlocks.includes(key)) return
|
||||
const cullIfIdentical = block.name.includes('glass')
|
||||
const cullIfIdentical = block.name.includes('glass') || block.name.includes('ice')
|
||||
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const face in element.faces) {
|
||||
|
|
@ -211,12 +251,12 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO:
|
|||
const dir = matmul3(globalMatrix, elemFaces[face].dir)
|
||||
|
||||
if (eFace.cullface) {
|
||||
const neighbor = world.getBlock(cursor.plus(new Vec3(...dir)))
|
||||
const neighbor = world.getBlock(cursor.plus(new Vec3(...dir)), blockProvider, {})
|
||||
if (neighbor) {
|
||||
if (cullIfIdentical && neighbor.type === block.type) continue
|
||||
if (!neighbor.transparent && isCube(neighbor)) continue
|
||||
if (cullIfIdentical && neighbor.stateId === block.stateId) continue
|
||||
if (!neighbor.transparent && (isCube(neighbor) || identicalCull(element, neighbor, new Vec3(...dir)))) continue
|
||||
} else {
|
||||
needRecompute = true
|
||||
needSectionRecomputeOnChange = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -310,6 +350,8 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO:
|
|||
}
|
||||
|
||||
let light = 1
|
||||
const { smoothLighting } = world.config
|
||||
// const smoothLighting = true
|
||||
if (doAO) {
|
||||
const dx = pos[0] * 2 - 1
|
||||
const dy = pos[1] * 2 - 1
|
||||
|
|
@ -321,14 +363,25 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO:
|
|||
const side2 = world.getBlock(cursor.offset(...side2Dir))
|
||||
const corner = world.getBlock(cursor.offset(...cornerDir))
|
||||
|
||||
let cornerLightResult = 15
|
||||
// eslint-disable-next-line no-constant-condition, sonarjs/no-gratuitous-expressions
|
||||
if (/* world.config.smoothLighting */false) { // todo fix
|
||||
const side1Light = world.getLight(cursor.plus(new Vec3(...side1Dir)), true)
|
||||
const side2Light = world.getLight(cursor.plus(new Vec3(...side2Dir)), true)
|
||||
const cornerLight = world.getLight(cursor.plus(new Vec3(...cornerDir)), true)
|
||||
let cornerLightResult = baseLight * 15
|
||||
|
||||
if (smoothLighting) {
|
||||
const dirVec = new Vec3(...dir)
|
||||
const getVec = (v: Vec3) => {
|
||||
for (const coord of ['x', 'y', 'z']) {
|
||||
if (Math.abs(dirVec[coord]) > 0) v[coord] = 0
|
||||
}
|
||||
return v.plus(dirVec)
|
||||
}
|
||||
const side1LightDir = getVec(new Vec3(...side1Dir))
|
||||
const side1Light = world.getLight(cursor.plus(side1LightDir))
|
||||
const side2DirLight = getVec(new Vec3(...side2Dir))
|
||||
const side2Light = world.getLight(cursor.plus(side2DirLight))
|
||||
const cornerLightDir = getVec(new Vec3(...cornerDir))
|
||||
const cornerLight = world.getLight(cursor.plus(cornerLightDir))
|
||||
// interpolate
|
||||
cornerLightResult = (side1Light + side2Light + cornerLight) / 3
|
||||
const lights = [side1Light, side2Light, cornerLight, baseLight * 15]
|
||||
cornerLightResult = lights.reduce((acc, cur) => acc + cur, 0) / lights.length
|
||||
}
|
||||
|
||||
const side1Block = world.shouldMakeAo(side1) ? 1 : 0
|
||||
|
|
@ -344,7 +397,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO:
|
|||
}
|
||||
|
||||
if (!needTiles) {
|
||||
attr.colors.push(baseLight * tint[0] * light, baseLight * tint[1] * light, baseLight * tint[2] * light)
|
||||
attr.colors.push(tint[0] * light, tint[1] * light, tint[2] * light)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -391,7 +444,6 @@ const invisibleBlocks = new Set(['air', 'cave_air', 'void_air', 'barrier'])
|
|||
const isBlockWaterlogged = (block: Block) => block.getProperties().waterlogged === true || block.getProperties().waterlogged === 'true'
|
||||
|
||||
let unknownBlockModel: BlockModelPartsResolved
|
||||
let erroredBlockModel: BlockModelPartsResolved
|
||||
export function getSectionGeometry (sx, sy, sz, world: World) {
|
||||
let delayedRender = [] as Array<() => void>
|
||||
|
||||
|
|
@ -421,7 +473,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) {
|
|||
for (cursor.y = sy; cursor.y < sy + 16; cursor.y++) {
|
||||
for (cursor.z = sz; cursor.z < sz + 16; cursor.z++) {
|
||||
for (cursor.x = sx; cursor.x < sx + 16; cursor.x++) {
|
||||
const block = world.getBlock(cursor)!
|
||||
let block = world.getBlock(cursor, blockProvider, attr)!
|
||||
if (!invisibleBlocks.has(block.name)) {
|
||||
const highest = attr.highestBlocks[`${cursor.x},${cursor.z}`]
|
||||
if (!highest || highest.y < cursor.y) {
|
||||
|
|
@ -459,6 +511,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) {
|
|||
if (block.models && JSON.stringify(block._originalProperties) !== JSON.stringify(block._properties)) {
|
||||
// recompute models
|
||||
block.models = undefined
|
||||
block = world.getBlock(cursor, blockProvider, attr)!
|
||||
}
|
||||
} else {
|
||||
block._properties = block._originalProperties ?? block._properties
|
||||
|
|
@ -481,37 +534,6 @@ export function getSectionGeometry (sx, sy, sz, world: World) {
|
|||
if (block.name !== 'water' && block.name !== 'lava' && !invisibleBlocks.has(block.name)) {
|
||||
// cache
|
||||
let { models } = block
|
||||
if (block.models === undefined) {
|
||||
const props = block.getProperties()
|
||||
try {
|
||||
// fixme
|
||||
if (world.preflat) {
|
||||
if (block.name === 'cobblestone_wall') {
|
||||
props.up = 'true'
|
||||
for (const key of ['north', 'south', 'east', 'west']) {
|
||||
const val = props[key]
|
||||
if (val === 'false' || val === 'true') {
|
||||
props[key] = val === 'true' ? 'low' : 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
models = blockProvider.getAllResolvedModels0_1({
|
||||
name: block.name,
|
||||
properties: props,
|
||||
}, world.preflat)! // fixme! this is a hack (also need a setting for all versions)
|
||||
if (!models.length) {
|
||||
console.debug('[mesher] block to render not found', block.name, props)
|
||||
models = null
|
||||
}
|
||||
} catch (err) {
|
||||
models ??= erroredBlockModel
|
||||
console.error(`Critical assets error. Unable to get block model for ${block.name}[${JSON.stringify(props)}]: ` + err.message, err.stack)
|
||||
attr.hadErrors = true
|
||||
}
|
||||
}
|
||||
block.models = models ?? null
|
||||
|
||||
models ??= unknownBlockModel
|
||||
|
||||
|
|
@ -607,7 +629,6 @@ export const setBlockStatesData = (blockstatesModels, blocksAtlas: any, _needTil
|
|||
globalThis.blockProvider = blockProvider
|
||||
if (useUnknownBlockModel) {
|
||||
unknownBlockModel = blockProvider.getAllResolvedModels0_1({ name: 'unknown', properties: {} })
|
||||
erroredBlockModel = blockProvider.getAllResolvedModels0_1({ name: 'errored', properties: {} })
|
||||
}
|
||||
|
||||
needTiles = _needTiles
|
||||
|
|
|
|||
|
|
@ -119,34 +119,12 @@ function renderElement (element: BlockElement, doAO: boolean, attr, globalMatrix
|
|||
|
||||
let light = 1
|
||||
if (doAO) {
|
||||
const dx = pos[0] * 2 - 1
|
||||
const dy = pos[1] * 2 - 1
|
||||
const dz = pos[2] * 2 - 1
|
||||
const cornerDir = matmul3(globalMatrix, [dx, dy, dz])
|
||||
const side1Dir = matmul3(globalMatrix, [dx * mask1[0], dy * mask1[1], dz * mask1[2]])
|
||||
const side2Dir = matmul3(globalMatrix, [dx * mask2[0], dy * mask2[1], dz * mask2[2]])
|
||||
// const side1 = world.getBlock(cursor.offset(...side1Dir))
|
||||
// const side2 = world.getBlock(cursor.offset(...side2Dir))
|
||||
// const corner = world.getBlock(cursor.offset(...cornerDir))
|
||||
|
||||
const cornerLightResult = 15
|
||||
// if (/* world.config.smoothLighting */false) { // todo fix
|
||||
// const side1Light = world.getLight(cursor.plus(new Vec3(...side1Dir)), true)
|
||||
// const side2Light = world.getLight(cursor.plus(new Vec3(...side2Dir)), true)
|
||||
// const cornerLight = world.getLight(cursor.plus(new Vec3(...cornerDir)), true)
|
||||
// // interpolate
|
||||
// cornerLightResult = (side1Light + side2Light + cornerLight) / 3
|
||||
// }
|
||||
|
||||
// const side1Block = world.shouldMakeAo(side1) ? 1 : 0
|
||||
// const side2Block = world.shouldMakeAo(side2) ? 1 : 0
|
||||
// const cornerBlock = world.shouldMakeAo(corner) ? 1 : 0
|
||||
const side1Block = 0
|
||||
const side2Block = 0
|
||||
const cornerBlock = 0
|
||||
|
||||
// TODO: correctly interpolate ao light based on pos (evaluate once for each corner of the block)
|
||||
|
||||
const ao = (side1Block && side2Block) ? 0 : (3 - (side1Block + side2Block + cornerBlock))
|
||||
// todo light should go upper on lower blocks
|
||||
light = (ao + 1) / 4 * (cornerLightResult / 15)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { BlockNames } from '../../../../../src/mcDataTypes'
|
||||
import { setup } from './mesherTester'
|
||||
|
||||
const addPositions = [
|
||||
|
|
@ -10,8 +11,10 @@ const addPositions = [
|
|||
[[0, 0, -1], 'stone'],
|
||||
] as const
|
||||
|
||||
const { mesherWorld, getGeometry, pos, mcData } = setup('1.18.1', addPositions as any)
|
||||
const { mesherWorld, getGeometry, pos, mcData } = setup('1.21.1', addPositions as any)
|
||||
|
||||
// mesherWorld.setBlockStateId(pos, mcData.blocksByName.soul_sand.defaultState)
|
||||
// mesherWorld.setBlockStateId(pos, 712)
|
||||
// mesherWorld.setBlockStateId(pos, mcData.blocksByName.stone_slab.defaultState)
|
||||
mesherWorld.setBlockStateId(pos, 11_225)
|
||||
|
||||
// console.log(getGeometry().centerTileNeighbors)
|
||||
console.log(getGeometry().centerTileNeighbors)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export class World {
|
|||
blockCache = {}
|
||||
biomeCache: { [id: number]: mcData.Biome }
|
||||
preflat: boolean
|
||||
erroredBlockModel?: BlockModelPartsResolved
|
||||
|
||||
constructor (version) {
|
||||
this.Chunk = Chunks(version) as any
|
||||
|
|
@ -47,6 +48,8 @@ export class World {
|
|||
}
|
||||
|
||||
getLight (pos: Vec3, isNeighbor = false, skipMoreChecks = false, curBlockName = '') {
|
||||
// for easier testing
|
||||
if (!(pos instanceof Vec3)) pos = new Vec3(...pos as [number, number, number])
|
||||
const { enableLighting, skyLight } = this.config
|
||||
if (!enableLighting) return 15
|
||||
// const key = `${pos.x},${pos.y},${pos.z}`
|
||||
|
|
@ -70,8 +73,10 @@ export class World {
|
|||
this.getLight(pos.offset(1, 0, 0), undefined, true),
|
||||
this.getLight(pos.offset(-1, 0, 0), undefined, true)
|
||||
].filter(x => x !== 2)
|
||||
const min = Math.min(...lights)
|
||||
result = min
|
||||
if (lights.length) {
|
||||
const min = Math.min(...lights)
|
||||
result = min
|
||||
}
|
||||
}
|
||||
if (isNeighbor && result === 2) result = 15 // TODO
|
||||
return result
|
||||
|
|
@ -108,7 +113,7 @@ export class World {
|
|||
return this.getColumn(Math.floor(pos.x / 16) * 16, Math.floor(pos.z / 16) * 16)
|
||||
}
|
||||
|
||||
getBlock (pos: Vec3): WorldBlock | null {
|
||||
getBlock (pos: Vec3, blockProvider?, attr?): WorldBlock | null {
|
||||
// for easier testing
|
||||
if (!(pos instanceof Vec3)) pos = new Vec3(...pos as [number, number, number])
|
||||
const key = columnKey(Math.floor(pos.x / 16) * 16, Math.floor(pos.z / 16) * 16)
|
||||
|
|
@ -122,8 +127,7 @@ export class World {
|
|||
const stateId = column.getBlockStateId(locInChunk)
|
||||
|
||||
if (!this.blockCache[stateId]) {
|
||||
const b = column.getBlock(locInChunk)
|
||||
//@ts-expect-error
|
||||
const b = column.getBlock(locInChunk) as unknown as WorldBlock
|
||||
b.isCube = isCube(b.shapes)
|
||||
this.blockCache[stateId] = b
|
||||
Object.defineProperty(b, 'position', {
|
||||
|
|
@ -132,7 +136,6 @@ export class World {
|
|||
}
|
||||
})
|
||||
if (this.preflat) {
|
||||
//@ts-expect-error
|
||||
b._properties = {}
|
||||
|
||||
const namePropsStr = legacyJson.blocks[b.type + ':' + b.metadata] || findClosestLegacyBlockFallback(b.type, b.metadata, pos)
|
||||
|
|
@ -145,7 +148,6 @@ export class World {
|
|||
if (!isNaN(val)) val = parseInt(val, 10)
|
||||
return [key, val]
|
||||
}))
|
||||
//@ts-expect-error
|
||||
b._properties = newProperties
|
||||
}
|
||||
}
|
||||
|
|
@ -153,6 +155,40 @@ export class World {
|
|||
}
|
||||
|
||||
const block = this.blockCache[stateId]
|
||||
|
||||
if (block.models === undefined && blockProvider) {
|
||||
if (!attr) throw new Error('attr is required')
|
||||
const props = block.getProperties()
|
||||
try {
|
||||
// fixme
|
||||
if (this.preflat) {
|
||||
if (block.name === 'cobblestone_wall') {
|
||||
props.up = 'true'
|
||||
for (const key of ['north', 'south', 'east', 'west']) {
|
||||
const val = props[key]
|
||||
if (val === 'false' || val === 'true') {
|
||||
props[key] = val === 'true' ? 'low' : 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block.models = blockProvider.getAllResolvedModels0_1({
|
||||
name: block.name,
|
||||
properties: props,
|
||||
}, this.preflat)! // fixme! this is a hack (also need a setting for all versions)
|
||||
if (!block.models!.length) {
|
||||
console.debug('[mesher] block to render not found', block.name, props)
|
||||
block.models = null
|
||||
}
|
||||
} catch (err) {
|
||||
this.erroredBlockModel ??= blockProvider.getAllResolvedModels0_1({ name: 'errored', properties: {} })
|
||||
block.models ??= this.erroredBlockModel
|
||||
console.error(`Critical assets error. Unable to get block model for ${block.name}[${JSON.stringify(props)}]: ` + err.message, err.stack)
|
||||
attr.hadErrors = true
|
||||
}
|
||||
}
|
||||
|
||||
if (block.name === 'flowing_water') block.name = 'water'
|
||||
if (block.name === 'flowing_lava') block.name = 'lava'
|
||||
// block.position = loc // it overrides position of all currently loaded blocks
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ export class Viewer {
|
|||
}
|
||||
|
||||
setBlockStateId (pos: Vec3, stateId: number) {
|
||||
if (!this.world.loadedChunks[`${Math.floor(pos.x / 16)},${Math.floor(pos.z / 16)}`]) {
|
||||
if (!this.world.loadedChunks[`${Math.floor(pos.x / 16) * 16},${Math.floor(pos.z / 16) * 16}`]) {
|
||||
console.warn('[should be unreachable] setBlockStateId called for unloaded chunk', pos)
|
||||
}
|
||||
this.world.setBlockStateId(pos, stateId)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,15 @@ const buildingVersion = new Date().toISOString().split(':')[0]
|
|||
|
||||
const dev = process.env.NODE_ENV === 'development'
|
||||
|
||||
let releaseTag
|
||||
let releaseChangelog
|
||||
|
||||
if (fs.existsSync('./assets/release.json')) {
|
||||
const releaseJson = JSON.parse(fs.readFileSync('./assets/release.json', 'utf8'))
|
||||
releaseTag = releaseJson.latestTag
|
||||
releaseChangelog = releaseJson.changelog?.replace(/<!-- bump-type:[\w]+ -->/, '')
|
||||
}
|
||||
|
||||
// base options are in ./prismarine-viewer/rsbuildSharedConfig.ts
|
||||
const appConfig = defineConfig({
|
||||
html: {
|
||||
|
|
@ -47,7 +56,9 @@ const appConfig = defineConfig({
|
|||
'process.env.MAIN_MENU_LINKS': JSON.stringify(process.env.MAIN_MENU_LINKS),
|
||||
'process.env.GITHUB_URL':
|
||||
JSON.stringify(`https://github.com/${process.env.GITHUB_REPOSITORY || `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}`}`),
|
||||
'process.env.DEPS_VERSIONS': JSON.stringify({})
|
||||
'process.env.DEPS_VERSIONS': JSON.stringify({}),
|
||||
'process.env.RELEASE_TAG': JSON.stringify(releaseTag),
|
||||
'process.env.RELEASE_CHANGELOG': JSON.stringify(releaseChangelog),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
|
|
@ -80,6 +91,9 @@ const appConfig = defineConfig({
|
|||
fs.copyFileSync('./assets/playground.html', './dist/playground.html')
|
||||
fs.copyFileSync('./assets/manifest.json', './dist/manifest.json')
|
||||
fs.copyFileSync('./assets/loading-bg.jpg', './dist/loading-bg.jpg')
|
||||
if (fs.existsSync('./assets/release.json')) {
|
||||
fs.copyFileSync('./assets/release.json', './dist/release.json')
|
||||
}
|
||||
const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8'))
|
||||
if (dev) {
|
||||
configJson.defaultProxy = ':8080'
|
||||
|
|
|
|||
|
|
@ -148,6 +148,13 @@ fs.promises.readdir(path.resolve(__dirname, '../src/react')).then(async (files)
|
|||
fs.copyFileSync(path.resolve(__dirname, '../README.NPM.MD'), path.resolve(__dirname, '../dist-npm/README.md'))
|
||||
|
||||
if (version !== '0.0.0-dev') {
|
||||
execSync('npm publish', { cwd: path.resolve(__dirname, '../dist-npm') })
|
||||
execSync('npm publish', {
|
||||
cwd: path.resolve(__dirname, '../dist-npm'),
|
||||
env: {
|
||||
...process.env,
|
||||
NPM_TOKEN: process.env.NPM_TOKEN,
|
||||
NODE_AUTH_TOKEN: process.env.NPM_TOKEN
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ const versionToNumber = (ver) => {
|
|||
return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// if not included here (even as {}) will not be bundled & accessible!
|
||||
const compressedOutput = false
|
||||
// if not included here (even as {}) will not be bundled & accessible!
|
||||
// const dataTypeBundling = {
|
||||
// protocol: {
|
||||
// // ignoreRemoved: true,
|
||||
|
|
@ -57,6 +57,18 @@ const dataTypeBundling = {
|
|||
},
|
||||
blocks: {
|
||||
arrKey: 'name',
|
||||
processData (current, prev) {
|
||||
for (const block of current) {
|
||||
if (block.transparent) {
|
||||
const forceOpaque = block.name.includes('shulker_box') || block.name.match(/^double_.+_slab\d?$/) || ['melon_block', 'lit_pumpkin', 'lit_redstone_ore', 'lit_furnace'].includes(block.name)
|
||||
|
||||
const prevBlock = prev?.find(x => x.name === block.name);
|
||||
if (forceOpaque || (prevBlock && !prevBlock.transparent)) {
|
||||
block.transparent = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ignoreRemoved: true,
|
||||
// genChanges (source, diff) {
|
||||
// const diffs = {}
|
||||
|
|
@ -164,6 +176,7 @@ for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) {
|
|||
diffSources[dataType] = new JsonOptimizer(config.arrKey, config.ignoreChanges, config.ignoreRemoved)
|
||||
}
|
||||
try {
|
||||
config.processData?.(dataRaw, previousData[dataType])
|
||||
diffSources[dataType].recordDiff(version, dataRaw)
|
||||
injectCode = `restoreDiff(sources, ${JSON.stringify(dataType)}, ${JSON.stringify(version)})`
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const displayClientChat = (text: string) => {
|
|||
})
|
||||
return
|
||||
}
|
||||
bot._client.write('chat', {
|
||||
bot._client.emit('chat', {
|
||||
message: JSON.stringify(message),
|
||||
position: 0,
|
||||
sender: 'minecraft:chat'
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ browserfs.install(window)
|
|||
const defaultMountablePoints = {
|
||||
'/world': { fs: 'LocalStorage' }, // will be removed in future
|
||||
'/data': { fs: 'IndexedDB' },
|
||||
'/resourcepack': { fs: 'InMemory' }, // temporary storage for currently loaded resource pack
|
||||
}
|
||||
browserfs.configure({
|
||||
fs: 'MountableFileSystem',
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ let lastCommandTrigger = null as { command: string, time: number } | null
|
|||
const secondActionActivationTimeout = 300
|
||||
const secondActionCommands = {
|
||||
'general.jump' () {
|
||||
// if (bot.game.gameMode === 'spectator') return
|
||||
toggleFly()
|
||||
},
|
||||
'general.forward' () {
|
||||
|
|
@ -475,7 +476,7 @@ export const f3Keybinds = [
|
|||
// TODO!
|
||||
if (resourcePackState.resourcePackInstalled || loadedGameState.usingServerResourcePack) {
|
||||
showNotification('Reloading textures...')
|
||||
await completeTexturePackInstall('default', 'default')
|
||||
await completeTexturePackInstall('default', 'default', loadedGameState.usingServerResourcePack)
|
||||
}
|
||||
},
|
||||
mobileTitle: 'Reload Textures'
|
||||
|
|
@ -645,6 +646,17 @@ export const onBotCreate = () => {
|
|||
}
|
||||
allowFlying = !!(flags & 4)
|
||||
})
|
||||
const gamemodeCheck = () => {
|
||||
if (bot.game.gameMode === 'spectator') {
|
||||
toggleFly(true, false)
|
||||
}
|
||||
}
|
||||
bot.on('game', () => {
|
||||
gamemodeCheck()
|
||||
})
|
||||
bot.on('login', () => {
|
||||
gamemodeCheck()
|
||||
})
|
||||
}
|
||||
|
||||
const standardAirborneAcceleration = 0.02
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export const customCommandsConfig = {
|
|||
}
|
||||
],
|
||||
handler ([setting, action, value]) {
|
||||
if (action === 'toggle') {
|
||||
if (action === 'toggle' || action === undefined) {
|
||||
const value = options[setting]
|
||||
const config = tryFindOptionConfig(setting)
|
||||
if (config && 'values' in config && config.values) {
|
||||
|
|
|
|||
18
src/index.ts
18
src/index.ts
|
|
@ -10,6 +10,7 @@ import { onGameLoad } from './inventoryWindows'
|
|||
import { supportedVersions } from 'minecraft-protocol'
|
||||
import protocolMicrosoftAuth from 'minecraft-protocol/src/client/microsoftAuth'
|
||||
import microsoftAuthflow from './microsoftAuthflow'
|
||||
import nbt from 'prismarine-nbt'
|
||||
|
||||
import 'core-js/features/array/at'
|
||||
import 'core-js/features/promise/with-resolvers'
|
||||
|
|
@ -387,9 +388,9 @@ async function connect (connectOptions: ConnectOptions) {
|
|||
Object.assign(serverOptions, connectOptions.serverOverridesFlat ?? {})
|
||||
window._LOAD_MC_DATA() // start loading data (if not loaded yet)
|
||||
const downloadMcData = async (version: string) => {
|
||||
if (connectOptions.authenticatedAccount && versionToNumber(version) < versionToNumber('1.19.4')) {
|
||||
if (connectOptions.authenticatedAccount && (versionToNumber(version) < versionToNumber('1.19.4') || versionToNumber(version) >= versionToNumber('1.21'))) {
|
||||
// todo support it (just need to fix .export crash)
|
||||
throw new Error('Microsoft authentication is only supported in 1.19.4 and above (at least for now)')
|
||||
throw new Error('Microsoft authentication is only supported on 1.19.4 - 1.20.6 (at least for now)')
|
||||
}
|
||||
|
||||
// todo expose cache
|
||||
|
|
@ -504,6 +505,7 @@ async function connect (connectOptions: ConnectOptions) {
|
|||
sessionServer: authData?.sessionEndpoint?.toString(),
|
||||
auth: connectOptions.authenticatedAccount ? async (client, options) => {
|
||||
authData!.setOnMsaCodeCallback(options.onMsaCode)
|
||||
authData?.setConnectingVersion(client.version)
|
||||
//@ts-expect-error
|
||||
client.authflow = authData!.authFlow
|
||||
try {
|
||||
|
|
@ -634,8 +636,16 @@ async function connect (connectOptions: ConnectOptions) {
|
|||
bot.on('error', handleError)
|
||||
|
||||
bot.on('kicked', (kickReason) => {
|
||||
console.log('User was kicked!', kickReason)
|
||||
setLoadingScreenStatus(`The Minecraft server kicked you. Kick reason: ${typeof kickReason === 'object' ? JSON.stringify(kickReason) : kickReason}`, true)
|
||||
console.log('You were kicked!', kickReason)
|
||||
let kickReasonString = typeof kickReason === 'string' ? kickReason : JSON.stringify(kickReason)
|
||||
let kickReasonFormatted = undefined as undefined | Record<string, any>
|
||||
if (typeof kickReason === 'object') {
|
||||
try {
|
||||
kickReasonFormatted = nbt.simplify(kickReason)
|
||||
kickReasonString = ''
|
||||
} catch {}
|
||||
}
|
||||
setLoadingScreenStatus(`The Minecraft server kicked you. Kick reason: ${kickReasonString}`, true, undefined, undefined, kickReasonFormatted)
|
||||
destroyAll()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -54,9 +54,22 @@ export const onGameLoad = (onLoad) => {
|
|||
|
||||
PrismarineItem = PItem(version)
|
||||
|
||||
const mapWindowType = (type: string, inventoryStart: number) => {
|
||||
if (type === 'minecraft:container') {
|
||||
if (inventoryStart === 45 - 9 * 4) return 'minecraft:generic_9x1'
|
||||
if (inventoryStart === 45 - 9 * 3) return 'minecraft:generic_9x2'
|
||||
if (inventoryStart === 45 - 9 * 2) return 'minecraft:generic_9x3'
|
||||
if (inventoryStart === 45 - 9) return 'minecraft:generic_9x4'
|
||||
if (inventoryStart === 45) return 'minecraft:generic_9x5'
|
||||
if (inventoryStart === 45 + 9) return 'minecraft:generic_9x6'
|
||||
}
|
||||
return type
|
||||
}
|
||||
|
||||
bot.on('windowOpen', (win) => {
|
||||
if (implementedContainersGuiMap[win.type]) {
|
||||
openWindow(implementedContainersGuiMap[win.type])
|
||||
const implementedWindow = implementedContainersGuiMap[mapWindowType(win.type as string, win.inventoryStart)]
|
||||
if (implementedWindow) {
|
||||
openWindow(implementedWindow)
|
||||
} else if (options.unimplementedContainers) {
|
||||
openWindow('ChestWin')
|
||||
} else {
|
||||
|
|
@ -205,7 +218,7 @@ export const getItemNameRaw = (item: Pick<import('prismarine-item').Item, 'nbt'>
|
|||
const customName = itemNbt.display?.Name
|
||||
if (!customName) return
|
||||
try {
|
||||
const parsed = mojangson.simplify(mojangson.parse(customName))
|
||||
const parsed = customName.startsWith('{') && customName.endsWith('}') ? mojangson.simplify(mojangson.parse(customName)) : fromFormattedString(customName)
|
||||
if (parsed.extra) {
|
||||
return parsed as Record<string, any>
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export const getProxyDetails = async (proxyBaseUrl: string) => {
|
|||
|
||||
export default async ({ tokenCaches, proxyBaseUrl, setProgressText = (text) => { }, setCacheResult, connectingServer }) => {
|
||||
let onMsaCodeCallback
|
||||
let connectingVersion = ''
|
||||
// const authEndpoint = 'http://localhost:3000/'
|
||||
// const sessionEndpoint = 'http://localhost:3000/session'
|
||||
let authEndpoint: URL | undefined
|
||||
|
|
@ -39,7 +40,8 @@ export default async ({ tokenCaches, proxyBaseUrl, setProgressText = (text) => {
|
|||
body: JSON.stringify({
|
||||
...tokenCaches,
|
||||
// important to set this param and not fake it as auth server might reject the request otherwise
|
||||
connectingServer
|
||||
connectingServer,
|
||||
connectingServerVersion: connectingVersion
|
||||
}),
|
||||
}).then(async response => {
|
||||
if (!response.ok) {
|
||||
|
|
@ -80,6 +82,9 @@ export default async ({ tokenCaches, proxyBaseUrl, setProgressText = (text) => {
|
|||
})
|
||||
if (!window.crypto && !isPageSecure()) throw new Error('Crypto API is available only in secure contexts. Be sure to use https!')
|
||||
const restoredData = await restoreData(result)
|
||||
if (!restoredData?.certificates?.profileKeys?.privatePEM) {
|
||||
throw new Error(`Authentication server issue: it didn't return auth data. Most probably because the auth request was rejected by the end authority and retrying won't help until the issue is resolved.`)
|
||||
}
|
||||
restoredData.certificates.profileKeys.private = restoredData.certificates.profileKeys.privatePEM
|
||||
return restoredData
|
||||
}
|
||||
|
|
@ -89,6 +94,9 @@ export default async ({ tokenCaches, proxyBaseUrl, setProgressText = (text) => {
|
|||
sessionEndpoint,
|
||||
setOnMsaCodeCallback (callback) {
|
||||
onMsaCodeCallback = callback
|
||||
},
|
||||
setConnectingVersion (version) {
|
||||
connectingVersion = version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ export const guiOptionsScheme: {
|
|||
dayCycleAndLighting: {
|
||||
text: 'Day Cycle',
|
||||
},
|
||||
// smoothLighting: {},
|
||||
smoothLighting: {},
|
||||
newVersionsLighting: {
|
||||
text: 'Lighting in newer versions',
|
||||
},
|
||||
|
|
@ -164,7 +164,7 @@ export const guiOptionsScheme: {
|
|||
}
|
||||
if (choice === 'Enable') {
|
||||
options.enabledResourcepack = name
|
||||
await completeTexturePackInstall(name, name)
|
||||
await completeTexturePackInstall(name, name, false)
|
||||
return
|
||||
}
|
||||
if (choice === 'Uninstall') {
|
||||
|
|
|
|||
|
|
@ -10,31 +10,30 @@ export default ({
|
|||
hideDots = false,
|
||||
lastStatus = '',
|
||||
backAction = undefined as undefined | (() => void),
|
||||
description = '',
|
||||
description = '' as string | JSX.Element,
|
||||
actionsSlot = null as React.ReactNode | null,
|
||||
children
|
||||
}) => {
|
||||
const [loadingDots, setLoadingDots] = useState('')
|
||||
const [loadingDotIndex, setLoadingDotIndex] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
const statusRunner = async () => {
|
||||
const timer = async (ms) => new Promise((resolve) => { setTimeout(resolve, ms) })
|
||||
|
||||
const load = async () => {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
setLoadingDotIndex(i => (i + 1) % 4)
|
||||
await timer(500) // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
}
|
||||
|
||||
void load()
|
||||
}
|
||||
|
||||
void statusRunner()
|
||||
}, [])
|
||||
|
||||
const statusRunner = async () => {
|
||||
const array = ['.', '..', '...', '']
|
||||
|
||||
const timer = async (ms) => new Promise((resolve) => { setTimeout(resolve, ms) })
|
||||
|
||||
const load = async () => {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
for (let i = 0; true; i = (i + 1) % array.length) {
|
||||
setLoadingDots(array[i])
|
||||
await timer(500) // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
}
|
||||
|
||||
void load()
|
||||
}
|
||||
|
||||
return (
|
||||
<Screen
|
||||
|
|
@ -48,8 +47,17 @@ export default ({
|
|||
>
|
||||
{status}
|
||||
</span>
|
||||
{isError || hideDots ? '' : loadingDots}
|
||||
<p className={styles['potential-problem']}>{description}</p>
|
||||
<div style={{ display: 'inline-flex', gap: '1px', }} hidden={hideDots || isError}>
|
||||
{
|
||||
[...'...'].map((dot, i) => {
|
||||
return <span
|
||||
key={i} style={{
|
||||
visibility: loadingDotIndex <= i ? 'hidden' : 'visible',
|
||||
}}>{dot}</span>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<p className={styles.description}>{description}</p>
|
||||
<p className={styles['last-status']}>{lastStatus ? `Last status: ${lastStatus}` : lastStatus}</p>
|
||||
</>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import Button from './Button'
|
|||
import { AuthenticatedAccount, updateAuthenticatedAccountData, updateLoadedServerData } from './ServersListProvider'
|
||||
import { showOptionsModal } from './SelectOption'
|
||||
import LoadingChunks from './LoadingChunks'
|
||||
import MessageFormatted from './MessageFormatted'
|
||||
import MessageFormattedString from './MessageFormattedString'
|
||||
|
||||
const initialState = {
|
||||
status: '',
|
||||
|
|
@ -25,7 +27,8 @@ const initialState = {
|
|||
hideDots: false,
|
||||
loadingChunksData: null as null | Record<string, string>,
|
||||
loadingChunksDataPlayerChunk: null as null | { x: number, z: number },
|
||||
isDisplaying: false
|
||||
isDisplaying: false,
|
||||
minecraftJsonMessage: null as null | Record<string, any>
|
||||
}
|
||||
export const appStatusState = proxy(initialState)
|
||||
export const resetAppStatusState = () => {
|
||||
|
|
@ -37,7 +40,7 @@ export const lastConnectOptions = {
|
|||
}
|
||||
|
||||
export default () => {
|
||||
const { isError, lastStatus, maybeRecoverable, status, hideDots, descriptionHint, loadingChunksData, loadingChunksDataPlayerChunk } = useSnapshot(appStatusState)
|
||||
const { isError, lastStatus, maybeRecoverable, status, hideDots, descriptionHint, loadingChunksData, loadingChunksDataPlayerChunk, minecraftJsonMessage } = useSnapshot(appStatusState)
|
||||
const { active: replayActive } = useSnapshot(packetsReplaceSessionState)
|
||||
|
||||
const isOpen = useIsModalActive('app-status')
|
||||
|
|
@ -95,7 +98,11 @@ export default () => {
|
|||
isError={isError || appStatusState.status === ''} // display back button if status is empty as probably our app is errored
|
||||
hideDots={hideDots}
|
||||
lastStatus={lastStatus}
|
||||
description={displayAuthButton ? '' : (isError ? guessProblem(status) : '') || descriptionHint}
|
||||
description={<>{
|
||||
displayAuthButton ? '' : (isError ? guessProblem(status) : '') || descriptionHint
|
||||
}{
|
||||
minecraftJsonMessage && <MessageFormattedString message={minecraftJsonMessage} />
|
||||
}</>}
|
||||
backAction={maybeRecoverable ? () => {
|
||||
resetAppStatusState()
|
||||
miscUiState.gameLoaded = false
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ interface Props {
|
|||
mapsProvider?: string
|
||||
versionStatus?: string
|
||||
versionTitle?: string
|
||||
onVersionClick?: () => void
|
||||
onVersionStatusClick?: () => void
|
||||
bottomRightLinks?: string
|
||||
versionText?: string
|
||||
onVersionTextClick?: () => void
|
||||
}
|
||||
|
||||
const httpsRegex = /^https?:\/\//
|
||||
|
|
@ -33,9 +35,11 @@ export default ({
|
|||
githubAction,
|
||||
linksButton,
|
||||
openFileAction,
|
||||
versionText,
|
||||
onVersionTextClick,
|
||||
versionStatus,
|
||||
versionTitle,
|
||||
onVersionClick,
|
||||
onVersionStatusClick,
|
||||
bottomRightLinks
|
||||
}: Props) => {
|
||||
if (!bottomRightLinks?.trim()) bottomRightLinks = undefined
|
||||
|
|
@ -109,13 +113,16 @@ export default ({
|
|||
</div>
|
||||
|
||||
<div className={styles['bottom-info']}>
|
||||
<span
|
||||
title={`${versionTitle} (click to reload)`}
|
||||
onClick={onVersionClick}
|
||||
className={styles['product-info']}
|
||||
>
|
||||
Prismarine Web Client {versionStatus}
|
||||
</span>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<span style={{ fontSize: 10, color: 'gray' }} onClick={onVersionTextClick}>{versionText}</span>
|
||||
<span
|
||||
title={`${versionTitle} (click to reload)`}
|
||||
onClick={onVersionStatusClick}
|
||||
className={styles['product-info']}
|
||||
>
|
||||
Prismarine Web Client {versionStatus}
|
||||
</span>
|
||||
</div>
|
||||
<span className={styles['product-description']}>
|
||||
<div className={styles['product-link']}>
|
||||
{linksParsed?.map(([name, link], i, arr) => {
|
||||
|
|
|
|||
|
|
@ -65,8 +65,11 @@ export default () => {
|
|||
setVersionStatus(`(${isLatest ? 'latest' : 'new version available'}${mainMenuState.serviceWorkerLoaded ? ' - Available Offline' : ''})`)
|
||||
}
|
||||
subscribe(mainMenuState, upStatus)
|
||||
upStatus()
|
||||
setVersionTitle(`Loaded: ${process.env.BUILD_VERSION}. Remote: ${contents}`)
|
||||
}, () => { })
|
||||
}, () => {
|
||||
setVersionStatus('(offline)')
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
|
@ -113,10 +116,14 @@ export default () => {
|
|||
mapsProvider={mapsProviderUrl}
|
||||
versionStatus={versionStatus}
|
||||
versionTitle={versionTitle}
|
||||
onVersionClick={async () => {
|
||||
onVersionStatusClick={async () => {
|
||||
setVersionStatus('(reloading)')
|
||||
await refreshApp()
|
||||
}}
|
||||
onVersionTextClick={async () => {
|
||||
openGithub('/releases')
|
||||
}}
|
||||
versionText={process.env.RELEASE_TAG}
|
||||
/>
|
||||
</div>}
|
||||
</Transition>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
.potential-problem {
|
||||
.description {
|
||||
color: #808080;
|
||||
word-break: break-all;
|
||||
padding: 0 10px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.last-status {
|
||||
|
|
|
|||
3
src/react/appStatus.module.css.d.ts
vendored
3
src/react/appStatus.module.css.d.ts
vendored
|
|
@ -1,10 +1,9 @@
|
|||
// This file is automatically generated.
|
||||
// Please do not change this file!
|
||||
interface CssExports {
|
||||
description: string;
|
||||
'last-status': string;
|
||||
lastStatus: string;
|
||||
'potential-problem': string;
|
||||
potentialProblem: string;
|
||||
}
|
||||
declare const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { options } from './optionsStorage'
|
|||
import { showOptionsModal } from './react/SelectOption'
|
||||
import { appStatusState } from './react/AppStatusProvider'
|
||||
import { appReplacableResources, resourcesContentOriginal } from './generated/resources'
|
||||
import { loadedGameState } from './globalState'
|
||||
|
||||
export const resourcePackState = proxy({
|
||||
resourcePackInstalled: false,
|
||||
|
|
@ -24,9 +25,14 @@ const getLoadedImage = async (url: string) => {
|
|||
return img
|
||||
}
|
||||
|
||||
const texturePackBasePath2 = '/data/resourcePacks/'
|
||||
const texturePackBasePath = '/data/resourcePacks/'
|
||||
export const uninstallTexturePack = async (name = 'default') => {
|
||||
const basePath = texturePackBasePath2 + name
|
||||
if (await existsAsync('/resourcepack/pack.mcmeta')) {
|
||||
await removeFileRecursiveAsync('/resourcepack')
|
||||
loadedGameState.usingServerResourcePack = false
|
||||
}
|
||||
const basePath = texturePackBasePath + name
|
||||
if (!(await existsAsync(basePath))) return
|
||||
await removeFileRecursiveAsync(basePath)
|
||||
options.enabledResourcepack = null
|
||||
await updateTexturePackInstalledState()
|
||||
|
|
@ -35,7 +41,7 @@ export const uninstallTexturePack = async (name = 'default') => {
|
|||
export const getResourcePackNames = async () => {
|
||||
// TODO
|
||||
try {
|
||||
return { [await fs.promises.readFile(join(texturePackBasePath2, 'default', 'name.txt'), 'utf8')]: true }
|
||||
return { [await fs.promises.readFile(join(texturePackBasePath, 'default', 'name.txt'), 'utf8')]: true }
|
||||
} catch (err) {
|
||||
return {}
|
||||
}
|
||||
|
|
@ -47,7 +53,7 @@ export const fromTexturePackPath = (path) => {
|
|||
|
||||
export const updateTexturePackInstalledState = async () => {
|
||||
try {
|
||||
resourcePackState.resourcePackInstalled = await existsAsync(texturePackBasePath2 + 'default')
|
||||
resourcePackState.resourcePackInstalled = await existsAsync(texturePackBasePath + 'default')
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
|
@ -58,31 +64,36 @@ export const installTexturePackFromHandle = async () => {
|
|||
// await completeTexturePackInstall()
|
||||
}
|
||||
|
||||
export const installTexturePack = async (file: File | ArrayBuffer, displayName = file['name'], name = 'default') => {
|
||||
export const installTexturePack = async (file: File | ArrayBuffer, displayName = file['name'], name = 'default', isServer = false) => {
|
||||
const installPath = isServer ? '/resourcepack/' : texturePackBasePath + name
|
||||
try {
|
||||
await uninstallTexturePack(name)
|
||||
} catch (err) {
|
||||
}
|
||||
const showLoader = !isServer
|
||||
const status = 'Installing resource pack: copying all files'
|
||||
setLoadingScreenStatus(status)
|
||||
|
||||
if (showLoader) {
|
||||
setLoadingScreenStatus(status)
|
||||
}
|
||||
// extract the zip and write to fs every file in it
|
||||
const zip = new JSZip()
|
||||
const zipFile = await zip.loadAsync(file)
|
||||
if (!zipFile.file('pack.mcmeta')) throw new Error('Not a resource pack: missing /pack.mcmeta')
|
||||
const basePath = texturePackBasePath2 + name
|
||||
await mkdirRecursive(basePath)
|
||||
await mkdirRecursive(installPath)
|
||||
|
||||
const allFilesArr = Object.entries(zipFile.files)
|
||||
.filter(([path]) => !path.startsWith('.') && !path.startsWith('_') && !path.startsWith('/')) // ignore dot files and __MACOSX
|
||||
let done = 0
|
||||
const upStatus = () => {
|
||||
setLoadingScreenStatus(`${status} ${Math.round(done / allFilesArr.length * 100)}%`)
|
||||
if (showLoader) {
|
||||
setLoadingScreenStatus(`${status} ${Math.round(done / allFilesArr.length * 100)}%`)
|
||||
}
|
||||
}
|
||||
const createdDirs = new Set<string>()
|
||||
const copyTasks = [] as Array<Promise<void>>
|
||||
await Promise.all(allFilesArr.map(async ([path, file]) => {
|
||||
// ignore dot files and __MACOSX
|
||||
if (path.startsWith('.') || path.startsWith('_') || path.startsWith('/')) return
|
||||
const writePath = join(basePath, path)
|
||||
const writePath = join(installPath, path)
|
||||
if (path.endsWith('/')) return
|
||||
const dir = dirname(writePath)
|
||||
if (!createdDirs.has(dir)) {
|
||||
|
|
@ -93,26 +104,32 @@ export const installTexturePack = async (file: File | ArrayBuffer, displayName =
|
|||
await Promise.all(copyTasks)
|
||||
copyTasks.length = 0
|
||||
}
|
||||
const promise = fs.promises.writeFile(writePath, Buffer.from(await file.async('arraybuffer')))
|
||||
const promise = fs.promises.writeFile(writePath, Buffer.from(await file.async('arraybuffer')) as any)
|
||||
copyTasks.push(promise)
|
||||
await promise
|
||||
done++
|
||||
upStatus()
|
||||
}))
|
||||
console.log('done')
|
||||
await completeTexturePackInstall(displayName, name)
|
||||
await completeTexturePackInstall(displayName, name, isServer)
|
||||
}
|
||||
|
||||
// or enablement
|
||||
export const completeTexturePackInstall = async (displayName: string, name: string) => {
|
||||
const basePath = texturePackBasePath2 + name
|
||||
await fs.promises.writeFile(join(basePath, 'name.txt'), displayName, 'utf8')
|
||||
export const completeTexturePackInstall = async (displayName: string | undefined, name: string, isServer: boolean) => {
|
||||
const basePath = isServer ? '/resourcepack/' : texturePackBasePath + name
|
||||
if (displayName) {
|
||||
await fs.promises.writeFile(join(basePath, 'name.txt'), displayName, 'utf8')
|
||||
}
|
||||
|
||||
await updateTextures()
|
||||
setLoadingScreenStatus(undefined)
|
||||
showNotification('Texturepack installed & enabled')
|
||||
await updateTexturePackInstalledState()
|
||||
options.enabledResourcepack = name
|
||||
if (isServer) {
|
||||
loadedGameState.usingServerResourcePack = true
|
||||
} else {
|
||||
options.enabledResourcepack = name
|
||||
}
|
||||
}
|
||||
|
||||
const existsAsync = async (path) => {
|
||||
|
|
@ -138,8 +155,8 @@ const getSizeFromImage = async (filePath: string) => {
|
|||
}
|
||||
|
||||
export const getActiveTexturepackBasePath = async () => {
|
||||
if (await existsAsync('/data/resourcePacks/server/pack.mcmeta')) {
|
||||
return '/data/resourcePacks/server'
|
||||
if (await existsAsync('/resourcepack/pack.mcmeta')) {
|
||||
return '/resourcepack'
|
||||
}
|
||||
const { enabledResourcepack } = options
|
||||
// const enabledResourcepack = 'default'
|
||||
|
|
@ -262,28 +279,42 @@ const prepareBlockstatesAndModels = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const downloadAndUseResourcePack = async (url) => {
|
||||
console.log('downloadAndUseResourcePack', url)
|
||||
const downloadAndUseResourcePack = async (url: string): Promise<void> => {
|
||||
console.log('Downloading server resource pack', url)
|
||||
const response = await fetch(url)
|
||||
const resourcePackData = await response.arrayBuffer()
|
||||
showNotification('Installing resource pack...')
|
||||
installTexturePack(resourcePackData, undefined, undefined, true).catch((err) => {
|
||||
console.error(err)
|
||||
showNotification('Failed to install resource pack: ' + err.message)
|
||||
})
|
||||
}
|
||||
|
||||
export const onAppLoad = () => {
|
||||
customEvents.on('gameLoaded', () => {
|
||||
customEvents.on('mineflayerBotCreated', () => {
|
||||
// todo also handle resourcePack
|
||||
bot._client.on('resource_pack_send', async (packet) => {
|
||||
const handleResourcePackRequest = async (packet) => {
|
||||
if (options.serverResourcePacks === 'never') return
|
||||
const promptMessage = 'promptMessage' in packet ? JSON.stringify(packet.promptMessage) : 'Do you want to use server resource pack?'
|
||||
const promptMessage = ('promptMessage' in packet && packet.promptMessage) ? JSON.stringify(packet.promptMessage) : 'Do you want to use server resource pack?'
|
||||
// TODO!
|
||||
const hash = 'hash' in packet ? packet.hash : '-'
|
||||
const forced = 'forced' in packet ? packet.forced : false
|
||||
const choice = options.serverResourcePacks === 'always'
|
||||
? true
|
||||
: await showOptionsModal(promptMessage, ['Download & Install'], {
|
||||
: await showOptionsModal(promptMessage, ['Download & Install (recommended)', 'Pretend Installed (not recommended)'], {
|
||||
cancel: !forced
|
||||
})
|
||||
if (!choice) return
|
||||
await downloadAndUseResourcePack(packet.url)
|
||||
bot.acceptResourcePack()
|
||||
})
|
||||
if (choice === 'Download & Install (recommended)') {
|
||||
await downloadAndUseResourcePack(packet.url).catch((err) => {
|
||||
console.error(err)
|
||||
showNotification('Failed to download resource pack: ' + err.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
bot._client.on('resource_pack_send', handleResourcePackRequest)
|
||||
bot._client.on('add_resource_pack' as any, handleResourcePackRequest)
|
||||
})
|
||||
|
||||
subscribe(resourcePackState, () => {
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ export const isMajorVersionGreater = (ver1: string, ver2: string) => {
|
|||
}
|
||||
|
||||
let ourLastStatus: string | undefined = ''
|
||||
export const setLoadingScreenStatus = function (status: string | undefined | null, isError = false, hideDots = false, fromFlyingSquid = false) {
|
||||
export const setLoadingScreenStatus = function (status: string | undefined | null, isError = false, hideDots = false, fromFlyingSquid = false, minecraftJsonMessage?: Record<string, any>) {
|
||||
// null can come from flying squid, should restore our last status
|
||||
if (status === null) {
|
||||
status = ourLastStatus
|
||||
|
|
@ -152,6 +152,7 @@ export const setLoadingScreenStatus = function (status: string | undefined | nul
|
|||
appStatusState.isError = isError
|
||||
appStatusState.lastStatus = isError ? appStatusState.status : ''
|
||||
appStatusState.status = status
|
||||
appStatusState.minecraftJsonMessage = minecraftJsonMessage ?? null
|
||||
}
|
||||
|
||||
// doesn't support snapshots
|
||||
|
|
@ -185,8 +186,8 @@ export const reloadChunks = async () => {
|
|||
await worldView.updatePosition(bot.entity.position, true)
|
||||
}
|
||||
|
||||
export const openGithub = () => {
|
||||
window.open(process.env.GITHUB_URL, '_blank')
|
||||
export const openGithub = (addUrl = '') => {
|
||||
window.open(`${process.env.GITHUB_URL}${addUrl}`, '_blank')
|
||||
}
|
||||
|
||||
export const resolveTimeout = async (promise, timeout = 10_000) => {
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ export const watchOptionsAfterViewerInit = () => {
|
|||
viewer.world.displayStats = o.renderDebug === 'advanced'
|
||||
})
|
||||
|
||||
// viewer.world.mesherConfig.smoothLighting = options.smoothLighting
|
||||
viewer.world.mesherConfig.smoothLighting = false // todo not supported for now
|
||||
viewer.world.mesherConfig.smoothLighting = options.smoothLighting
|
||||
subscribeKey(options, 'smoothLighting', () => {
|
||||
viewer.world.mesherConfig.smoothLighting = options.smoothLighting;
|
||||
(viewer.world as WorldRendererThree).rerenderAllChunks()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue