need to release (#135)
This commit is contained in:
commit
0c032cdc57
36 changed files with 452 additions and 208 deletions
|
|
@ -1,15 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
import type { AppOptions } from '../../src/optionsStorage'
|
||||
|
||||
const cleanVisit = (url?) => {
|
||||
cy.clearLocalStorage()
|
||||
visit(url)
|
||||
}
|
||||
|
||||
const visit = (url = '/') => {
|
||||
window.localStorage.cypress = 'true'
|
||||
cy.visit(url)
|
||||
}
|
||||
import { setOptions, cleanVisit, visit } from './shared'
|
||||
|
||||
// todo use ssl
|
||||
|
||||
|
|
@ -31,14 +21,8 @@ const testWorldLoad = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const setOptions = (options: Partial<AppOptions>) => {
|
||||
cy.window().then(win => {
|
||||
Object.assign(win['options'], options)
|
||||
})
|
||||
}
|
||||
|
||||
it('Loads & renders singleplayer', () => {
|
||||
visit('/?singleplayer=1')
|
||||
cleanVisit('/?singleplayer=1')
|
||||
setOptions({
|
||||
localServerOptions: {
|
||||
generation: {
|
||||
|
|
@ -69,10 +53,3 @@ it('Loads & renders zip world', () => {
|
|||
cy.get('input[type="file"]').selectFile('cypress/superflat.zip', { force: true })
|
||||
testWorldLoad()
|
||||
})
|
||||
|
||||
it.skip('Performance test', () => {
|
||||
// select that world
|
||||
// from -2 85 24
|
||||
// await bot.loadPlugin(pathfinder.pathfinder)
|
||||
// bot.pathfinder.goto(new pathfinder.goals.GoalXZ(28, -28))
|
||||
})
|
||||
|
|
|
|||
15
cypress/e2e/shared.ts
Normal file
15
cypress/e2e/shared.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { AppOptions } from '../../src/optionsStorage'
|
||||
|
||||
export const cleanVisit = (url?) => {
|
||||
cy.clearLocalStorage()
|
||||
visit(url)
|
||||
}
|
||||
export const visit = (url = '/') => {
|
||||
window.localStorage.cypress = 'true'
|
||||
cy.visit(url)
|
||||
}
|
||||
export const setOptions = (options: Partial<AppOptions>) => {
|
||||
cy.window().then(win => {
|
||||
Object.assign(win['options'], options)
|
||||
})
|
||||
}
|
||||
|
|
@ -94,9 +94,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="react-root"></div>
|
||||
<div id="ui-root">
|
||||
<pmui-playscreen id="play-screen" style="display: none;"></pmui-playscreen>
|
||||
</div>
|
||||
<div id="ui-root"></div>
|
||||
<!-- inject script -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@
|
|||
"browserify-zlib": "^0.2.0",
|
||||
"buffer": "^6.0.3",
|
||||
"constants-browserify": "^1.0.0",
|
||||
"contro-max": "^0.1.7",
|
||||
"contro-max": "^0.1.8",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"cypress": "^10.11.0",
|
||||
"cypress-esbuild-preprocessor": "^1.0.2",
|
||||
|
|
|
|||
72
pnpm-lock.yaml
generated
72
pnpm-lock.yaml
generated
|
|
@ -268,8 +268,8 @@ importers:
|
|||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
contro-max:
|
||||
specifier: ^0.1.7
|
||||
version: 0.1.7(typescript@5.5.0-beta)
|
||||
specifier: ^0.1.8
|
||||
version: 0.1.8(typescript@5.5.0-beta)
|
||||
crypto-browserify:
|
||||
specifier: ^3.12.0
|
||||
version: 3.12.0
|
||||
|
|
@ -299,7 +299,7 @@ importers:
|
|||
version: 1.0.0
|
||||
minecraft-inventory-gui:
|
||||
specifier: github:zardoy/minecraft-inventory-gui#next
|
||||
version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5554c7ab0a74bce52aa5f5f04a48eb8d3b9ac65c(@types/react@18.2.20)(react@18.2.0)
|
||||
version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/200902aca941475e7feb610070e662b172a000b5(@types/react@18.2.20)(react@18.2.0)
|
||||
mineflayer:
|
||||
specifier: github:PrismarineJS/mineflayer
|
||||
version: https://codeload.github.com/PrismarineJS/mineflayer/tar.gz/5a544cf2547a6e0f1f17786962d77a33c661c02f(encoding@0.1.13)
|
||||
|
|
@ -3808,8 +3808,8 @@ packages:
|
|||
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
contro-max@0.1.7:
|
||||
resolution: {integrity: sha512-HIYF1Dl50tUyTKaDsX+mPMDv2OjleNMVedYuBTX0n1wKNm9WxjWu2w74ATjz/8fHVL9GgmziIxAlFStd2je6kg==}
|
||||
contro-max@0.1.8:
|
||||
resolution: {integrity: sha512-5SoeudO8Zzfj/gbFTDrMRFJny02+MY1lBtb2NyCNiBLtHAfvhWZxZs/Z3yJvKL2rY/qKUZs9gTQOIDygBcBrdw==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
convert-source-map@1.9.0:
|
||||
|
|
@ -6035,14 +6035,19 @@ packages:
|
|||
minecraft-folder-path@1.2.0:
|
||||
resolution: {integrity: sha512-qaUSbKWoOsH9brn0JQuBhxNAzTDMwrOXorwuRxdJKKKDYvZhtml+6GVCUrY5HRiEsieBEjCUnhVpDuQiKsiFaw==}
|
||||
|
||||
minecraft-inventory-gui@https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5554c7ab0a74bce52aa5f5f04a48eb8d3b9ac65c:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5554c7ab0a74bce52aa5f5f04a48eb8d3b9ac65c}
|
||||
minecraft-inventory-gui@https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/200902aca941475e7feb610070e662b172a000b5:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/200902aca941475e7feb610070e662b172a000b5}
|
||||
version: 1.0.1
|
||||
|
||||
minecraft-protocol@1.47.0:
|
||||
resolution: {integrity: sha512-IHL8faXLLIWv1O+2v2NgyKlooilu/OiSL9orI8Kqed/rZvVOrFPzs2PwMAYjpQX9gxLPhiSU19KqZ8CjfNuqhg==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc:
|
||||
resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc}
|
||||
version: 1.47.0
|
||||
engines: {node: '>=14'}
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/ccab9fb39681f3ebe0d264e2a3f833aa3c5a1ac7:
|
||||
resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/ccab9fb39681f3ebe0d264e2a3f833aa3c5a1ac7}
|
||||
version: 1.47.0
|
||||
|
|
@ -6717,6 +6722,11 @@ packages:
|
|||
prismarine-chat@1.9.1:
|
||||
resolution: {integrity: sha512-x7WWa5MNhiLZSO6tw+YyKpzquFZ+DNISVgiV6K3SU0GsishMXe+nto02WhF/4AuFerKdugm9u1d/r4C4zSkJOg==}
|
||||
|
||||
prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/eb39a905761a36f733a456110e6b49d655bf5c16:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/eb39a905761a36f733a456110e6b49d655bf5c16}
|
||||
version: 1.35.0
|
||||
engines: {node: '>=14'}
|
||||
|
||||
prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/f32234a724a5c2482ffbaf85edc5e91c7ab9b38f:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/f32234a724a5c2482ffbaf85edc5e91c7ab9b38f}
|
||||
version: 1.35.0
|
||||
|
|
@ -11936,11 +11946,11 @@ snapshots:
|
|||
flatmap: 0.0.3
|
||||
long: 5.2.3
|
||||
minecraft-data: 3.65.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/ccab9fb39681f3ebe0d264e2a3f833aa3c5a1ac7(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13)
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13)
|
||||
mkdirp: 2.1.6
|
||||
node-gzip: 1.1.2
|
||||
node-rsa: 1.1.1
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/f32234a724a5c2482ffbaf85edc5e91c7ab9b38f(minecraft-data@3.65.0)
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/eb39a905761a36f733a456110e6b49d655bf5c16(minecraft-data@3.65.0)
|
||||
prismarine-entity: 2.3.1
|
||||
prismarine-item: 1.14.0
|
||||
prismarine-nbt: 2.5.0
|
||||
|
|
@ -12839,7 +12849,7 @@ snapshots:
|
|||
|
||||
content-type@1.0.5: {}
|
||||
|
||||
contro-max@0.1.7(typescript@5.5.0-beta):
|
||||
contro-max@0.1.8(typescript@5.5.0-beta):
|
||||
dependencies:
|
||||
events: 3.3.0
|
||||
lodash-es: 4.17.21
|
||||
|
|
@ -13206,7 +13216,7 @@ snapshots:
|
|||
diamond-square@https://codeload.github.com/zardoy/diamond-square/tar.gz/915fce8e27fe8eb45464d89b9563956afa4f7687:
|
||||
dependencies:
|
||||
minecraft-data: 3.65.0
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/f32234a724a5c2482ffbaf85edc5e91c7ab9b38f(minecraft-data@3.65.0)
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/eb39a905761a36f733a456110e6b49d655bf5c16(minecraft-data@3.65.0)
|
||||
random-seed: 0.3.0
|
||||
vec3: 0.1.8
|
||||
|
||||
|
|
@ -15668,7 +15678,7 @@ snapshots:
|
|||
|
||||
minecraft-folder-path@1.2.0: {}
|
||||
|
||||
minecraft-inventory-gui@https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5554c7ab0a74bce52aa5f5f04a48eb8d3b9ac65c(@types/react@18.2.20)(react@18.2.0):
|
||||
minecraft-inventory-gui@https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/200902aca941475e7feb610070e662b172a000b5(@types/react@18.2.20)(react@18.2.0):
|
||||
dependencies:
|
||||
valtio: 1.11.2(@types/react@18.2.20)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -15700,6 +15710,31 @@ snapshots:
|
|||
- encoding
|
||||
- supports-color
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@types/readable-stream': 4.0.12
|
||||
aes-js: 3.1.2
|
||||
buffer-equal: 1.0.1
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
endian-toggle: 0.0.0
|
||||
lodash.get: 4.4.2
|
||||
lodash.merge: 4.6.2
|
||||
minecraft-data: 3.65.0
|
||||
minecraft-folder-path: 1.2.0
|
||||
node-fetch: 2.7.0(encoding@0.1.13)
|
||||
node-rsa: 0.4.2
|
||||
prismarine-auth: 2.4.2(encoding@0.1.13)
|
||||
prismarine-chat: 1.10.1
|
||||
prismarine-nbt: 2.5.0
|
||||
prismarine-realms: 1.3.2(encoding@0.1.13)
|
||||
protodef: 1.15.0
|
||||
readable-stream: 4.5.2
|
||||
uuid-1345: 1.0.2
|
||||
yggdrasil: 1.7.0(encoding@0.1.13)
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/ccab9fb39681f3ebe0d264e2a3f833aa3c5a1ac7(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@types/readable-stream': 4.0.12
|
||||
|
|
@ -16520,6 +16555,19 @@ snapshots:
|
|||
prismarine-nbt: 2.5.0
|
||||
prismarine-registry: 1.7.0
|
||||
|
||||
prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/eb39a905761a36f733a456110e6b49d655bf5c16(minecraft-data@3.65.0):
|
||||
dependencies:
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.65.0)(prismarine-registry@1.7.0)
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/ada4ec3fdfbbc1cc20ab01d0e23f0718a77cc1a0
|
||||
prismarine-nbt: 2.5.0
|
||||
prismarine-registry: 1.7.0
|
||||
smart-buffer: 4.2.0
|
||||
uint4: 0.1.2
|
||||
vec3: 0.1.8
|
||||
xxhash-wasm: 0.4.2
|
||||
transitivePeerDependencies:
|
||||
- minecraft-data
|
||||
|
||||
prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/f32234a724a5c2482ffbaf85edc5e91c7ab9b38f(minecraft-data@3.65.0):
|
||||
dependencies:
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.65.0)(prismarine-registry@1.7.0)
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
module.exports = {
|
||||
dispose3 (o) {
|
||||
o.geometry?.dispose()
|
||||
o.dispose?.()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
import * as THREE from 'three'
|
||||
import * as TWEEN from '@tweenjs/tween.js'
|
||||
import * as Entity from './entity/EntityMesh'
|
||||
import { dispose3 } from './dispose'
|
||||
import nbt from 'prismarine-nbt'
|
||||
import EventEmitter from 'events'
|
||||
import { PlayerObject, PlayerAnimation } from 'skinview3d'
|
||||
|
|
@ -14,10 +13,11 @@ import { NameTagObject } from 'skinview3d/libs/nametag'
|
|||
import { flat, fromFormattedString } from '@xmcl/text-component'
|
||||
import mojangson from 'mojangson'
|
||||
import externalTexturesJson from './entity/externalTextures.json'
|
||||
import { disposeObject } from './threeJsUtils'
|
||||
|
||||
export const TWEEN_DURATION = 50 // todo should be 100
|
||||
|
||||
function getUsernameTexture(username, { fontFamily = 'sans-serif' }) {
|
||||
function getUsernameTexture (username, { fontFamily = 'sans-serif' }) {
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) throw new Error('Could not get 2d context')
|
||||
|
|
@ -61,7 +61,7 @@ const addNametag = (entity, options, mesh) => {
|
|||
// todo cleanup
|
||||
const nametags = {}
|
||||
|
||||
function getEntityMesh(entity, scene, options, overrides) {
|
||||
function getEntityMesh (entity, scene, options, overrides) {
|
||||
if (entity.name) {
|
||||
try {
|
||||
// https://github.com/PrismarineJS/prismarine-viewer/pull/410
|
||||
|
|
@ -105,15 +105,15 @@ export class Entities extends EventEmitter {
|
|||
this.getItemUv = undefined
|
||||
}
|
||||
|
||||
clear() {
|
||||
clear () {
|
||||
for (const mesh of Object.values(this.entities)) {
|
||||
this.scene.remove(mesh)
|
||||
dispose3(mesh)
|
||||
disposeObject(mesh)
|
||||
}
|
||||
this.entities = {}
|
||||
}
|
||||
|
||||
setDebugMode(mode, /** @type {THREE.Object3D?} */entity = null) {
|
||||
setDebugMode (mode, /** @type {THREE.Object3D?} */entity = null) {
|
||||
this.debugMode = mode
|
||||
for (const mesh of entity ? [entity] : Object.values(this.entities)) {
|
||||
const boxHelper = mesh.children.find(c => c.name === 'debug')
|
||||
|
|
@ -125,14 +125,14 @@ export class Entities extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
setVisible(visible, /** @type {THREE.Object3D?} */entity = null) {
|
||||
setVisible (visible, /** @type {THREE.Object3D?} */entity = null) {
|
||||
this.visible = visible
|
||||
for (const mesh of entity ? [entity] : Object.values(this.entities)) {
|
||||
mesh.visible = visible
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
const dt = this.clock.getDelta()
|
||||
for (const entityId of Object.keys(this.entities)) {
|
||||
const playerObject = this.getPlayerObject(entityId)
|
||||
|
|
@ -142,7 +142,7 @@ export class Entities extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
getPlayerObject(entityId) {
|
||||
getPlayerObject (entityId) {
|
||||
/** @type {(PlayerObject & { animation?: PlayerAnimation }) | undefined} */
|
||||
const playerObject = this.entities[entityId]?.playerObject
|
||||
return playerObject
|
||||
|
|
@ -152,7 +152,7 @@ export class Entities extends EventEmitter {
|
|||
defaultSteveTexture
|
||||
|
||||
// true means use default skin url
|
||||
updatePlayerSkin(entityId, username, /** @type {string | true} */skinUrl, /** @type {string | true | undefined} */capeUrl = undefined) {
|
||||
updatePlayerSkin (entityId, username, /** @type {string | true} */skinUrl, /** @type {string | true | undefined} */capeUrl = undefined) {
|
||||
let playerObject = this.getPlayerObject(entityId)
|
||||
if (!playerObject) return
|
||||
// const username = this.entities[entityId].username
|
||||
|
|
@ -235,14 +235,14 @@ export class Entities extends EventEmitter {
|
|||
playerObject.cape.map = null
|
||||
}
|
||||
|
||||
function isCanvasBlank(canvas) {
|
||||
function isCanvasBlank (canvas) {
|
||||
return !canvas.getContext('2d')
|
||||
.getImageData(0, 0, canvas.width, canvas.height).data
|
||||
.some(channel => channel !== 0)
|
||||
}
|
||||
}
|
||||
|
||||
playAnimation(entityPlayerId, /** @type {'walking' | 'running' | 'oneSwing' | 'idle'} */animation) {
|
||||
playAnimation (entityPlayerId, /** @type {'walking' | 'running' | 'oneSwing' | 'idle'} */animation) {
|
||||
const playerObject = this.getPlayerObject(entityPlayerId)
|
||||
if (!playerObject) return
|
||||
|
||||
|
|
@ -262,14 +262,14 @@ export class Entities extends EventEmitter {
|
|||
|
||||
}
|
||||
|
||||
displaySimpleText(jsonLike) {
|
||||
displaySimpleText (jsonLike) {
|
||||
if (!jsonLike) return
|
||||
const parsed = typeof jsonLike === 'string' ? mojangson.simplify(mojangson.parse(jsonLike)) : nbt.simplify(jsonLike)
|
||||
const text = flat(parsed).map(x => x.text)
|
||||
return text.join('')
|
||||
}
|
||||
|
||||
update(/** @type {import('prismarine-entity').Entity & {delete?, pos}} */entity, overrides) {
|
||||
update (/** @type {import('prismarine-entity').Entity & {delete?, pos}} */entity, overrides) {
|
||||
let isPlayerModel = entity.name === 'player'
|
||||
if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') {
|
||||
isPlayerModel = true
|
||||
|
|
@ -456,7 +456,7 @@ export class Entities extends EventEmitter {
|
|||
if (e.additionalCleanup) e.additionalCleanup()
|
||||
this.emit('remove', entity)
|
||||
this.scene.remove(e)
|
||||
dispose3(e)
|
||||
disposeObject(e)
|
||||
// todo dispose textures as well ?
|
||||
delete this.entities[entity.id]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
const THREE = require('three')
|
||||
const { MeshLine, MeshLineMaterial } = require('three.meshline')
|
||||
const { dispose3 } = require('./dispose')
|
||||
|
||||
function getMesh (primitive, camera) {
|
||||
if (primitive.type === 'line') {
|
||||
|
|
@ -48,7 +47,7 @@ function getMesh (primitive, camera) {
|
|||
}
|
||||
|
||||
class Primitives {
|
||||
constructor (scene, camera) {
|
||||
constructor(scene, camera) {
|
||||
this.scene = scene
|
||||
this.camera = camera
|
||||
this.primitives = {}
|
||||
|
|
@ -57,7 +56,7 @@ class Primitives {
|
|||
clear () {
|
||||
for (const mesh of Object.values(this.primitives)) {
|
||||
this.scene.remove(mesh)
|
||||
dispose3(mesh)
|
||||
disposeObject(mesh)
|
||||
}
|
||||
this.primitives = {}
|
||||
}
|
||||
|
|
@ -65,7 +64,7 @@ class Primitives {
|
|||
update (primitive) {
|
||||
if (this.primitives[primitive.id]) {
|
||||
this.scene.remove(this.primitives[primitive.id])
|
||||
dispose3(this.primitives[primitive.id])
|
||||
disposeObject(this.primitives[primitive.id])
|
||||
delete this.primitives[primitive.id]
|
||||
}
|
||||
|
||||
|
|
|
|||
12
prismarine-viewer/viewer/lib/threeJsUtils.ts
Normal file
12
prismarine-viewer/viewer/lib/threeJsUtils.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import * as THREE from 'three';
|
||||
|
||||
export const disposeObject = (obj: THREE.Object3D) => {
|
||||
// not cleaning texture there as it might be used by other objects, but would be good to also do that
|
||||
if (obj instanceof THREE.Mesh) {
|
||||
obj.geometry?.dispose?.();
|
||||
obj.material?.dispose?.();
|
||||
}
|
||||
if (obj.children) {
|
||||
obj.children.forEach(disposeObject);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import * as THREE from 'three'
|
||||
import { Vec3 } from 'vec3'
|
||||
import nbt from 'prismarine-nbt'
|
||||
import { dispose3 } from './dispose'
|
||||
import PrismarineChatLoader from 'prismarine-chat'
|
||||
import { renderSign } from '../sign-renderer/'
|
||||
import { chunkPos, sectionPos } from './simpleUtils'
|
||||
import { WorldRendererCommon, WorldRendererConfig } from './worldrendererCommon'
|
||||
import * as tweenJs from '@tweenjs/tween.js'
|
||||
import { BloomPass, RenderPass, UnrealBloomPass, EffectComposer, WaterPass, GlitchPass } from 'three-stdlib'
|
||||
import { disposeObject } from './threeJsUtils'
|
||||
|
||||
export class WorldRendererThree extends WorldRendererCommon {
|
||||
outputFormat = 'threeJs' as const
|
||||
|
|
@ -62,7 +62,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
let object: THREE.Object3D = this.sectionObjects[data.key]
|
||||
if (object) {
|
||||
this.scene.remove(object)
|
||||
dispose3(object)
|
||||
disposeObject(object)
|
||||
delete this.sectionObjects[data.key]
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +263,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
const mesh = this.sectionObjects[key]
|
||||
if (mesh) {
|
||||
this.scene.remove(mesh)
|
||||
dispose3(mesh)
|
||||
disposeObject(mesh)
|
||||
}
|
||||
delete this.sectionObjects[key]
|
||||
}
|
||||
|
|
@ -277,11 +277,23 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
|
||||
class StarField {
|
||||
points?: THREE.Points
|
||||
private _enabled = true
|
||||
get enabled () {
|
||||
return this._enabled
|
||||
}
|
||||
set enabled (value) {
|
||||
this._enabled = value
|
||||
if (this.points) {
|
||||
this.points.visible = value
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private scene: THREE.Scene) {
|
||||
}
|
||||
|
||||
addToScene () {
|
||||
if (this.points || !this.enabled) return
|
||||
|
||||
const radius = 80
|
||||
const depth = 50
|
||||
const count = 7000
|
||||
|
|
@ -315,7 +327,6 @@ class StarField {
|
|||
material.blending = THREE.AdditiveBlending
|
||||
material.depthTest = false
|
||||
material.transparent = true
|
||||
// material.unifo
|
||||
|
||||
// Create points and add them to the scene
|
||||
this.points = new THREE.Points(geometry, material)
|
||||
|
|
@ -332,6 +343,7 @@ class StarField {
|
|||
if (this.points) {
|
||||
this.points.geometry.dispose();
|
||||
(this.points.material as THREE.Material).dispose();
|
||||
this.scene.remove(this.points)
|
||||
|
||||
this.points = undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ const warnings = new Set<string>()
|
|||
Promise.resolve().then(async () => {
|
||||
generateItemsAtlases()
|
||||
console.time('generateTextures')
|
||||
for (const version of mcAssets.versions as typeof mcAssets['versions']) {
|
||||
const versions = process.argv.includes('-l') ? [mcAssets.versions.at(-1)!] : mcAssets.versions
|
||||
for (const version of versions as typeof mcAssets['versions']) {
|
||||
// for debugging (e.g. when above is overridden)
|
||||
if (!mcAssets.versions.includes(version)) {
|
||||
if (!versions.includes(version)) {
|
||||
throw new Error(`Version ${version} is not supported by minecraft-assets`)
|
||||
}
|
||||
if (versionToNumber(version) < versionToNumber('1.13')) {
|
||||
|
|
@ -45,7 +46,7 @@ Promise.resolve().then(async () => {
|
|||
fs.copySync(assets.directory, path.resolve(texturesPath, version), { overwrite: true })
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(publicPath, 'supportedVersions.json'), '[' + mcAssets.versions.map(v => `"${v}"`).toString() + ']')
|
||||
fs.writeFileSync(path.join(publicPath, 'supportedVersions.json'), '[' + versions.map(v => `"${v}"`).toString() + ']')
|
||||
warnings.forEach(x => console.warn(x))
|
||||
console.timeEnd('generateTextures')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import Jimp from 'jimp'
|
||||
import minecraftData from 'minecraft-data'
|
||||
import prismarineRegistry from 'prismarine-registry'
|
||||
import { McAssets } from './modelsBuilder'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
|
@ -11,8 +10,6 @@ const twoTileTextures: string[] = []
|
|||
let currentImage: Jimp
|
||||
let currentBlockName: string
|
||||
let currentMcAssets: McAssets
|
||||
let isPreFlattening = false
|
||||
const postFlatenningRegistry = prismarineRegistry('1.13')
|
||||
const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url)))
|
||||
|
||||
type SidesType = {
|
||||
|
|
@ -24,9 +21,9 @@ type SidesType = {
|
|||
"down": string
|
||||
}
|
||||
|
||||
const getBlockStates = (name: string, postFlatenningName = name) => {
|
||||
const mcData = isPreFlattening ? postFlatenningRegistry : minecraftData(currentMcAssets.version)
|
||||
return mcData.blocksByName[isPreFlattening ? postFlatenningName : name]?.states
|
||||
const getBlockStates = (name: string) => {
|
||||
const mcData = minecraftData(currentMcAssets.version)
|
||||
return mcData.blocksByName[name]?.states
|
||||
}
|
||||
|
||||
export const addBlockCustomSidesModel = (name: string, sides: SidesType) => {
|
||||
|
|
@ -124,7 +121,7 @@ const handleShulkerBox = async (dataBase: string, match: RegExpExecArray) => {
|
|||
}
|
||||
|
||||
const handleSign = async (dataBase: string, match: RegExpExecArray) => {
|
||||
const states = getBlockStates(currentBlockName, currentBlockName === 'wall_sign' ? 'wall_sign' : 'sign')
|
||||
const states = getBlockStates(currentBlockName)
|
||||
if (!states) return
|
||||
|
||||
const [, signMaterial = ''] = match
|
||||
|
|
@ -375,17 +372,11 @@ const handleChest = async (dataBase: string, match: RegExpExecArray) => {
|
|||
if (modelName.endsWith('_left')) chestTextureName = `${chestTextureName}_left`
|
||||
if (modelName.endsWith('_right')) chestTextureName = `${chestTextureName}_right`
|
||||
|
||||
// reading latest version since the texture wasn't changed, but in pre-flatenning need custom mapping for doubled_chest
|
||||
const texture = path.join(currentMcAssets.directory, `../1.19.1/entity/chest/${chestTextureName}.png`)
|
||||
|
||||
currentImage = await Jimp.read(texture)
|
||||
|
||||
const model = structuredClone(chestModels[modelName])
|
||||
// todo < 1.9
|
||||
if (currentMcAssets.version === '1.8.8') {
|
||||
// doesn't have definition of block yet
|
||||
model.parent = undefined
|
||||
}
|
||||
model.textures.particle = particle
|
||||
const newModelName = `${currentBlockName}_${modelName}`
|
||||
for (const variant of blockStatesVariants) {
|
||||
|
|
@ -438,7 +429,6 @@ export const tryHandleBlockEntity = async (dataBase, blockName) => {
|
|||
|
||||
export const prepareMoreGeneratedBlocks = async (mcAssets: McAssets) => {
|
||||
const mcData = minecraftData(mcAssets.version)
|
||||
isPreFlattening = !mcData.supportFeature('blockStateId')
|
||||
const allTheBlocks = mcData.blocksArray.map(x => x.name)
|
||||
|
||||
currentMcAssets = mcAssets
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export const startWatchingHmr = () => {
|
|||
const mesherSharedPlugins = [
|
||||
{
|
||||
name: 'minecraft-data',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
build.onLoad({
|
||||
filter: /data[\/\\]pc[\/\\]common[\/\\]legacy.json$/,
|
||||
}, async (args) => {
|
||||
|
|
@ -59,7 +59,7 @@ const plugins = [
|
|||
...mesherSharedPlugins,
|
||||
{
|
||||
name: 'strict-aliases',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
build.onResolve({
|
||||
filter: /^minecraft-protocol$/,
|
||||
}, async ({ kind, resolveDir }) => {
|
||||
|
|
@ -110,7 +110,7 @@ const plugins = [
|
|||
},
|
||||
{
|
||||
name: 'data-assets',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
build.onResolve({
|
||||
filter: /.*/,
|
||||
}, async ({ path, ...rest }) => {
|
||||
|
|
@ -161,7 +161,7 @@ const plugins = [
|
|||
},
|
||||
{
|
||||
name: 'prevent-incorrect-linking',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
build.onResolve({
|
||||
filter: /.+/,
|
||||
}, async ({ resolveDir, path, importer, kind, pluginData }) => {
|
||||
|
|
@ -184,7 +184,7 @@ const plugins = [
|
|||
},
|
||||
{
|
||||
name: 'watch-notify',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
let count = 0
|
||||
let time
|
||||
let prevHash
|
||||
|
|
@ -234,7 +234,7 @@ const plugins = [
|
|||
},
|
||||
{
|
||||
name: 'esbuild-readdir',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
build.onResolve({
|
||||
filter: /^esbuild-readdir:.+$/,
|
||||
}, ({ resolveDir, path }) => {
|
||||
|
|
@ -262,7 +262,7 @@ const plugins = [
|
|||
},
|
||||
{
|
||||
name: 'esbuild-import-glob',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
build.onResolve({
|
||||
filter: /^esbuild-import-glob\(path:(.+),skipFiles:(.+)\)+$/,
|
||||
}, ({ resolveDir, path }) => {
|
||||
|
|
@ -292,7 +292,7 @@ const plugins = [
|
|||
},
|
||||
{
|
||||
name: 'fix-dynamic-require',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
build.onResolve({
|
||||
filter: /1\.14\/chunk/,
|
||||
}, async ({ resolveDir, path }) => {
|
||||
|
|
@ -321,7 +321,7 @@ const plugins = [
|
|||
},
|
||||
{
|
||||
name: 'react-displayname',
|
||||
setup (build) {
|
||||
setup(build) {
|
||||
build.onLoad({
|
||||
filter: /.tsx$/,
|
||||
}, async ({ path }) => {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ const fns = {
|
|||
async getAlias () {
|
||||
const aliasesRaw = process.env.ALIASES
|
||||
if (!aliasesRaw) throw new Error('No aliases found')
|
||||
const aliases = aliasesRaw.split('\n').map((x) => x.split('='))
|
||||
const aliases = aliasesRaw.split('\n').map((x) => x.trim().split('='))
|
||||
const githubActionsPull = process.env.PULL_URL?.split('/').at(-1)
|
||||
if (!githubActionsPull) throw new Error(`Not a pull request, got ${process.env.GITHUB_REF}`)
|
||||
const prNumber = githubActionsPull[1]
|
||||
if (!githubActionsPull) throw new Error(`Not a pull request, got ${process.env.PULL_URL}`)
|
||||
const prNumber = githubActionsPull
|
||||
const alias = aliases.find((x) => x[0] === prNumber)
|
||||
if (alias) {
|
||||
// set github output
|
||||
|
|
@ -18,7 +18,7 @@ const fns = {
|
|||
}
|
||||
}
|
||||
|
||||
function setOutput(key, value) {
|
||||
function setOutput (key, value) {
|
||||
// Temporary hack until core actions library catches up with github new recommendations
|
||||
const output = process.env['GITHUB_OUTPUT']
|
||||
fs.appendFileSync(output, `${key}=${value}${os.EOL}`)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { proxy, subscribe } from 'valtio'
|
|||
import { ControMax } from 'contro-max/build/controMax'
|
||||
import { CommandEventArgument, SchemaCommandInput } from 'contro-max/build/types'
|
||||
import { stringStartsWith } from 'contro-max/build/stringUtils'
|
||||
import { UserOverridesConfig } from 'contro-max/build/types/store'
|
||||
import { UserOverrideCommand, UserOverridesConfig } from 'contro-max/build/types/store'
|
||||
import { isGameActive, showModal, gameAdditionalState, activeModalStack, hideCurrentModal, miscUiState } from './globalState'
|
||||
import { goFullscreen, pointerLock, reloadChunks } from './utils'
|
||||
import { options } from './optionsStorage'
|
||||
|
|
@ -19,6 +19,7 @@ import { showOptionsModal } from './react/SelectOption'
|
|||
import widgets from './react/widgets'
|
||||
import { getItemFromBlock } from './botUtils'
|
||||
import { gamepadUiCursorState, moveGamepadCursorByPx } from './react/GamepadUiCursor'
|
||||
import { updateBinds } from './react/KeybindingsScreenProvider'
|
||||
|
||||
|
||||
export const customKeymaps = proxy(JSON.parse(localStorage.keymap || '{}')) as UserOverridesConfig
|
||||
|
|
@ -86,7 +87,7 @@ export const contro = new ControMax({
|
|||
window.controMax = contro
|
||||
export type Command = CommandEventArgument<typeof contro['_commandsRaw']>['command']
|
||||
|
||||
// updateCustomBinds()
|
||||
updateBinds(customKeymaps)
|
||||
|
||||
const updateDoPreventDefault = () => {
|
||||
controlOptions.preventDefault = miscUiState.gameLoaded && !activeModalStack.length
|
||||
|
|
@ -296,19 +297,18 @@ function cycleHotbarSlot (dir: 1 | -1) {
|
|||
bot.setQuickBarSlot(newHotbarSlot)
|
||||
}
|
||||
|
||||
// custom commands hamdler
|
||||
const customCommandsHandler = (buttonData: { code?: string, button?: string, state: boolean }) => {
|
||||
if (!buttonData.state || !isGameActive(true)) return
|
||||
// custom commands handler
|
||||
const customCommandsHandler = ({ command }) => {
|
||||
const [section, name] = command.split('.')
|
||||
if (!isGameActive(true) || section !== 'custom') return
|
||||
|
||||
const codeOrButton = buttonData.code ?? buttonData.button
|
||||
const inputType = buttonData.code ? 'keys' : 'gamepad'
|
||||
for (const value of Object.values(contro.userConfig!.custom ?? {})) {
|
||||
if (value[inputType]?.includes(codeOrButton!)) {
|
||||
customCommandsConfig[(value as CustomCommand).type].handler((value as CustomCommand).inputs)
|
||||
}
|
||||
if (contro.userConfig?.custom) {
|
||||
customCommandsConfig[(contro.userConfig.custom[name] as CustomCommand).type].handler(
|
||||
(contro.userConfig.custom[name] as CustomCommand).inputs
|
||||
)
|
||||
}
|
||||
}
|
||||
contro.on('pressedKeyOrButtonChanged', customCommandsHandler)
|
||||
contro.on('trigger', customCommandsHandler)
|
||||
|
||||
contro.on('trigger', ({ command }) => {
|
||||
const willContinue = !isGameActive(true)
|
||||
|
|
@ -651,6 +651,24 @@ window.addEventListener('keydown', (e) => {
|
|||
}
|
||||
})
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.code !== 'F2' || e.repeat || !isGameActive(true)) return
|
||||
e.preventDefault()
|
||||
const canvas = document.getElementById('viewer-canvas') as HTMLCanvasElement
|
||||
if (!canvas) return
|
||||
const link = document.createElement('a')
|
||||
link.href = canvas.toDataURL('image/png')
|
||||
const date = new Date()
|
||||
link.download = `screenshot ${date.toLocaleString().replaceAll('.', '-').replace(',', '')}.png`
|
||||
link.click()
|
||||
})
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.code !== 'F1' || e.repeat || !isGameActive(true)) return
|
||||
e.preventDefault()
|
||||
miscUiState.showUI = !miscUiState.showUI
|
||||
})
|
||||
|
||||
// #region experimental debug things
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'F11') {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ window.len = (obj) => Object.keys(obj).length
|
|||
window.inspectPacket = (packetName, full = false) => {
|
||||
const listener = (...args) => console.log('packet', packetName, full ? args : args[0])
|
||||
const attach = () => {
|
||||
bot?.on(packetName, listener)
|
||||
bot?._client.on(packetName, listener)
|
||||
}
|
||||
attach()
|
||||
customEvents.on('mineflayerBotCreated', attach)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ const updateAutoJump = () => {
|
|||
jumpOnAllEdges: options.autoParkour,
|
||||
// strictBlockCollision: true,
|
||||
})
|
||||
if (autoJump === bot.autoJumper.enabled) return
|
||||
if (autoJump) {
|
||||
bot.autoJumper.enable()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ export const miscUiState = proxy({
|
|||
wanOpened: false,
|
||||
/** wether game hud is shown (in playing state) */
|
||||
gameLoaded: false,
|
||||
showUI: true,
|
||||
loadedServerIndex: '',
|
||||
/** currently trying to load or loaded mc version, after all data is loaded */
|
||||
loadedDataVersion: null as string | null,
|
||||
|
|
|
|||
41
src/index.ts
41
src/index.ts
|
|
@ -108,6 +108,8 @@ let renderer: THREE.WebGLRenderer
|
|||
try {
|
||||
renderer = new THREE.WebGLRenderer({
|
||||
powerPreference: options.gpuPreference,
|
||||
preserveDrawingBuffer: true,
|
||||
logarithmicDepthBuffer: true,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
|
|
@ -142,24 +144,35 @@ new THREE.TextureLoader().load(itemsPng, (texture) => {
|
|||
viewer.entities.itemsTexture = texture
|
||||
// todo unify
|
||||
viewer.entities.getItemUv = (id) => {
|
||||
const name = loadedData.items[id]?.name
|
||||
const uv = itemsAtlases.latest.textures[name]
|
||||
if (!uv) {
|
||||
const variant = viewer.world.downloadedBlockStatesData[name]?.variants?.['']
|
||||
if (!variant) return
|
||||
const uvBlock = (Array.isArray(variant) ? variant[0] : variant).model?.elements?.[0]?.faces?.north.texture
|
||||
if (!uvBlock) return
|
||||
try {
|
||||
const name = loadedData.items[id]?.name
|
||||
const uv = itemsAtlases.latest.textures[name]
|
||||
if (!uv) {
|
||||
const variant = viewer.world.downloadedBlockStatesData[name]?.variants?.['']
|
||||
if (!variant) return
|
||||
const faces = (Array.isArray(variant) ? variant[0] : variant).model?.elements?.[0]?.faces
|
||||
const uvBlock = faces?.north?.texture ?? faces?.up?.texture ?? faces?.down?.texture ?? faces?.west?.texture ?? faces?.east?.texture ?? faces?.south?.texture
|
||||
if (!uvBlock) return
|
||||
return {
|
||||
...uvBlock,
|
||||
size: Math.abs(uvBlock.su),
|
||||
texture: viewer.world.material.map
|
||||
}
|
||||
}
|
||||
return {
|
||||
...uvBlock,
|
||||
size: Math.abs(uvBlock.su),
|
||||
...uv,
|
||||
size: itemsAtlases.latest.size,
|
||||
texture: viewer.entities.itemsTexture
|
||||
}
|
||||
} catch (err) {
|
||||
reportError?.(err)
|
||||
return {
|
||||
u: 0,
|
||||
v: 0,
|
||||
size: 16 / viewer.world.material.map!.image.width,
|
||||
texture: viewer.world.material.map
|
||||
}
|
||||
}
|
||||
return {
|
||||
...uv,
|
||||
size: itemsAtlases.latest.size,
|
||||
texture: viewer.entities.itemsTexture
|
||||
}
|
||||
}
|
||||
})
|
||||
viewer.entities.entitiesOptions = {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ export const guiOptionsScheme: {
|
|||
enableWarning: 'Enabling it will make chunks load ~4x slower',
|
||||
disabledDuringGame: true
|
||||
},
|
||||
starfieldRendering: {}
|
||||
},
|
||||
],
|
||||
main: [
|
||||
|
|
|
|||
|
|
@ -30,24 +30,7 @@ const defaultOptions = {
|
|||
touchButtonsSize: 40,
|
||||
touchButtonsOpacity: 80,
|
||||
touchButtonsPosition: 12,
|
||||
touchControlsPositions: {
|
||||
action: [
|
||||
70,
|
||||
85
|
||||
],
|
||||
sneak: [
|
||||
90,
|
||||
85
|
||||
],
|
||||
break: [
|
||||
70,
|
||||
65
|
||||
],
|
||||
jump: [
|
||||
90,
|
||||
65
|
||||
],
|
||||
} as Record<string, [number, number]>,
|
||||
touchControlsPositions: getDefaultTouchControlsPositions(),
|
||||
touchControlsType: 'classic' as 'classic' | 'joystick-buttons',
|
||||
gpuPreference: 'default' as 'default' | 'high-performance' | 'low-power',
|
||||
backgroundRendering: '20fps' as 'full' | '20fps' | '5fps',
|
||||
|
|
@ -59,6 +42,7 @@ const defaultOptions = {
|
|||
dayCycleAndLighting: true,
|
||||
loadPlayerSkins: true,
|
||||
lowMemoryMode: false,
|
||||
starfieldRendering: true,
|
||||
// antiAliasing: false,
|
||||
|
||||
showChunkBorders: false, // todo rename option
|
||||
|
|
@ -68,11 +52,14 @@ const defaultOptions = {
|
|||
excludeCommunicationDebugEvents: [],
|
||||
preventDevReloadWhilePlaying: false,
|
||||
numWorkers: 4,
|
||||
localServerOptions: {} as any,
|
||||
localServerOptions: {
|
||||
gameMode: 1
|
||||
} as any,
|
||||
preferLoadReadonly: false,
|
||||
disableLoadPrompts: false,
|
||||
guestUsername: 'guest',
|
||||
askGuestName: true,
|
||||
errorReporting: true,
|
||||
/** Actually might be useful */
|
||||
showCursorBlockInSpectator: false,
|
||||
renderEntities: true,
|
||||
|
|
@ -91,6 +78,27 @@ const defaultOptions = {
|
|||
wysiwygSignEditor: 'auto' as 'auto' | 'always' | 'never',
|
||||
}
|
||||
|
||||
function getDefaultTouchControlsPositions () {
|
||||
return {
|
||||
action: [
|
||||
70,
|
||||
85
|
||||
],
|
||||
sneak: [
|
||||
90,
|
||||
85
|
||||
],
|
||||
break: [
|
||||
70,
|
||||
65
|
||||
],
|
||||
jump: [
|
||||
90,
|
||||
65
|
||||
],
|
||||
} as Record<string, [number, number]>
|
||||
}
|
||||
|
||||
const qsOptionsRaw = new URLSearchParams(location.search).getAll('setting')
|
||||
export const qsOptions = Object.fromEntries(qsOptionsRaw.map(o => {
|
||||
const [key, value] = o.split(':')
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export default () => {
|
|||
if (items[0].match) items = items.map(i => i.match)
|
||||
}
|
||||
if (completeValue === '/') {
|
||||
if (!items[0].startsWith('/')) {
|
||||
if (!items[0]?.startsWith('/')) {
|
||||
// normalize
|
||||
items = items.map(item => `/${item}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,17 +8,19 @@ import styles from './createWorld.module.css'
|
|||
|
||||
// const worldTypes = ['default', 'flat', 'largeBiomes', 'amplified', 'customized', 'buffet', 'debug_all_block_states']
|
||||
const worldTypes = ['default', 'flat'/* , 'void' */]
|
||||
const gameModes = ['survival', 'creative'/* , 'adventure', 'spectator' */]
|
||||
|
||||
export const creatingWorldState = proxy({
|
||||
title: '',
|
||||
type: worldTypes[0],
|
||||
gameMode: gameModes[0],
|
||||
version: ''
|
||||
})
|
||||
|
||||
export default ({ cancelClick, createClick, customizeClick, versions, defaultVersion }) => {
|
||||
const [quota, setQuota] = useState('')
|
||||
|
||||
const { title, type, version } = useSnapshot(creatingWorldState)
|
||||
const { title, type, version, gameMode } = useSnapshot(creatingWorldState)
|
||||
useEffect(() => {
|
||||
creatingWorldState.version = defaultVersion
|
||||
void navigator.storage?.estimate?.().then(({ quota, usage }) => {
|
||||
|
|
@ -54,9 +56,15 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer
|
|||
<Button onClick={() => {
|
||||
const index = worldTypes.indexOf(type)
|
||||
creatingWorldState.type = worldTypes[index === worldTypes.length - 1 ? 0 : index + 1]
|
||||
}}>{type}</Button>
|
||||
<Button onClick={() => customizeClick()} disabled>
|
||||
}}>World Type: {type}</Button>
|
||||
{/* <Button onClick={() => customizeClick()} disabled>
|
||||
Customize
|
||||
</Button> */}
|
||||
<Button onClick={() => {
|
||||
const index = gameModes.indexOf(gameMode)
|
||||
creatingWorldState.gameMode = gameModes[index === gameModes.length - 1 ? 0 : index + 1]
|
||||
}}>
|
||||
Gamemode: {gameMode}
|
||||
</Button>
|
||||
</div>
|
||||
<div className='muted' style={{ fontSize: 8 }}>Default and other world types are WIP</div>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { hideCurrentModal, showModal } from '../globalState'
|
|||
import defaultLocalServerOptions from '../defaultLocalServerOptions'
|
||||
import { mkdirRecursive, uniqueFileNameFromWorldName } from '../browserfs'
|
||||
import CreateWorld, { WorldCustomize, creatingWorldState } from './CreateWorld'
|
||||
import { useIsModalActive } from './utilsApp'
|
||||
import { getWorldsPath } from './SingleplayerProvider'
|
||||
import { useIsModalActive } from './utilsApp'
|
||||
|
||||
export default () => {
|
||||
const activeCreate = useIsModalActive('create-world')
|
||||
|
|
@ -23,7 +23,7 @@ export default () => {
|
|||
}}
|
||||
createClick={async () => {
|
||||
// create new world
|
||||
const { title, type, version } = creatingWorldState
|
||||
const { title, type, version, gameMode } = creatingWorldState
|
||||
// todo display path in ui + disable if exist
|
||||
const savePath = await uniqueFileNameFromWorldName(title, getWorldsPath())
|
||||
await mkdirRecursive(savePath)
|
||||
|
|
@ -51,7 +51,8 @@ export default () => {
|
|||
levelName: title,
|
||||
version,
|
||||
generation,
|
||||
'worldFolder': savePath
|
||||
'worldFolder': savePath,
|
||||
gameMode: gameMode === 'survival' ? 0 : 1,
|
||||
},
|
||||
}))
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,25 @@
|
|||
.crosshair {
|
||||
z-index: -1;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--gui-icons);
|
||||
background-size: calc(256px * var(--crosshair-scale));
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
z-index: -1;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--gui-icons);
|
||||
background-size: calc(256px * var(--crosshair-scale));
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
image-rendering: pixelated;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.crosshair-indicator {
|
||||
z-index: -1;
|
||||
width: var(--crosshair-indicator-size);
|
||||
height: 1.5px;
|
||||
position: fixed;
|
||||
top: calc(50% + 8px);
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: lightgray;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,70 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import './Crosshair.css'
|
||||
import { proxy, useSnapshot } from 'valtio'
|
||||
import SharedHudVars from './SharedHudVars'
|
||||
|
||||
// todo move to mineflayer
|
||||
export const itemBeingUsed = proxy({
|
||||
name: null as string | null,
|
||||
hand: 0 as 0 | 1
|
||||
})
|
||||
|
||||
export default () => {
|
||||
const { name: usingItem, hand } = useSnapshot(itemBeingUsed)
|
||||
|
||||
const [displayIndicator, setDisplayIndicator] = useState(false)
|
||||
const [indicatorProgress, setIndicatorProgress] = useState(0)
|
||||
const [alternativeIndicator, setAlternativeIndicator] = useState(false)
|
||||
const boxMaxTimeMs = 1000
|
||||
// todo add sword indicator
|
||||
const indicatorSize = 20
|
||||
|
||||
useEffect(() => {
|
||||
bot.on('heldItemChanged' as any, () => {
|
||||
const displayBar = (item: import('prismarine-item').Item | null) => {
|
||||
const itemName = item?.name
|
||||
if (!itemName) return
|
||||
return loadedData.foodsArray.map((food) => food.name).includes(itemName) || itemName === 'bow' || itemName === 'shield' || itemName === 'crossbow'
|
||||
}
|
||||
setDisplayIndicator(displayBar(bot.heldItem) || displayBar(bot.inventory.slots[45]) || false)
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setAlternativeIndicator(usingItem === 'shield')
|
||||
if (!usingItem) return
|
||||
const startTime = Date.now()
|
||||
let maxTime = 0
|
||||
if (usingItem === 'bow' || usingItem === 'crossbow') {
|
||||
maxTime = 1000
|
||||
}
|
||||
const isFood = loadedData.foodsArray.some((food) => food.name === usingItem)
|
||||
if (isFood) {
|
||||
maxTime = 32 * 50
|
||||
}
|
||||
if (!maxTime) return
|
||||
|
||||
const id = setInterval(() => {
|
||||
const progress = (Date.now() - startTime) / boxMaxTimeMs
|
||||
if (progress >= 1) {
|
||||
clearInterval(id)
|
||||
} else {
|
||||
setIndicatorProgress(progress)
|
||||
}
|
||||
}, 1000 / 60)
|
||||
return () => {
|
||||
setIndicatorProgress(0)
|
||||
clearInterval(id)
|
||||
}
|
||||
}, [usingItem])
|
||||
|
||||
return <SharedHudVars>
|
||||
<div className='crosshair'/>
|
||||
<div className='crosshair' />
|
||||
{displayIndicator && <div className='crosshair-indicator' style={{
|
||||
//@ts-expect-error
|
||||
'--crosshair-indicator-size': `${indicatorSize}px`,
|
||||
borderLeft: `solid ${indicatorSize * indicatorProgress}px white`,
|
||||
backgroundColor: alternativeIndicator ? 'dodgerblue' : undefined,
|
||||
}} />}
|
||||
</SharedHudVars>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export default () => {
|
|||
},
|
||||
} as any)
|
||||
const { canvasManager } = inv
|
||||
inv.inventory.supportsOffhand = bot.supportFeature('doesntHaveOffHandSlot')
|
||||
inv.inventory.supportsOffhand = !bot.supportFeature('doesntHaveOffHandSlot')
|
||||
inv.pwindow.disablePicking = true
|
||||
|
||||
canvasManager.children[0].disableHighlight = true
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useState, useEffect, useRef, createContext, useContext } from 'react'
|
||||
import { UserOverridesConfig } from 'contro-max/build/types/store'
|
||||
import { ModifierOnlyKeys } from 'contro-max/build/types/keyCodes'
|
||||
import { contro as controEx } from '../controls'
|
||||
import { hideModal } from '../globalState'
|
||||
import triangle from './ps_icons/playstation_triangle_console_controller_gamepad_icon.svg'
|
||||
|
|
@ -96,22 +97,35 @@ export default (
|
|||
updateBindMap()
|
||||
}, [userConfig])
|
||||
|
||||
const updateBinding = (data) => {
|
||||
if (data.state === true || !awaitingInputType) return
|
||||
const updateBinding = (data: any) => {
|
||||
if ((!data.state && awaitingInputType) || !awaitingInputType) {
|
||||
setAwaitingInputType(null)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if ('code' in data) {
|
||||
if (data.state && [...contro.pressedKeys].includes(data.code)) return
|
||||
|
||||
if (data.code === 'Escape' || ['Mouse0', 'Mouse1', 'Mouse2'].includes(data.code)) {
|
||||
setAwaitingInputType(null)
|
||||
return
|
||||
}
|
||||
setBinding({ code: data.code, state: true }, groupName, actionName, buttonNum)
|
||||
const pressedModifiers = [...contro.pressedKeys].filter(
|
||||
key => /^(Meta|Control|Alt|Shift)?$/.test(key)
|
||||
)
|
||||
setBinding(
|
||||
{ code: pressedModifiers.length ? `${pressedModifiers[0]}+${data.code}` : data.code, state: true },
|
||||
groupName,
|
||||
actionName,
|
||||
buttonNum
|
||||
)
|
||||
}
|
||||
if ('button' in data) {
|
||||
contro.enabled = false
|
||||
void Promise.resolve().then(() => { contro.enabled = true })
|
||||
setBinding(data, groupName, actionName, buttonNum)
|
||||
}
|
||||
|
||||
setAwaitingInputType(null)
|
||||
}
|
||||
|
||||
const updateBindMap = () => {
|
||||
|
|
@ -368,8 +382,12 @@ const parseActionName = (action: string) => {
|
|||
const parseBindingName = (binding: string | undefined) => {
|
||||
if (!binding) return ''
|
||||
const cut = binding.replaceAll(/(Numpad|Digit|Key)/g, '')
|
||||
const parts = cut.split(/(?=[A-Z\d])/)
|
||||
return parts.reverse().join(' ')
|
||||
|
||||
const parts = cut.includes('+') ? cut.split('+') : [cut]
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
parts[i] = parts[i].split(/(?=[A-Z\d])/).reverse().join(' ')
|
||||
}
|
||||
return parts.join(' + ')
|
||||
}
|
||||
|
||||
const buttonsMap = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { openURL } from 'prismarine-viewer/viewer/lib/simpleUtils'
|
||||
import { haveDirectoryPicker } from '../utils'
|
||||
import { activeModalStack } from '../globalState'
|
||||
import styles from './mainMenu.module.css'
|
||||
import Button from './Button'
|
||||
import ButtonWithTooltip from './ButtonWithTooltip'
|
||||
|
|
@ -17,12 +18,24 @@ interface Props {
|
|||
mapsProvider?: string
|
||||
}
|
||||
|
||||
const refreshApp = async () => {
|
||||
const refreshApp = async (failedUpdate = false) => {
|
||||
const registration = await navigator.serviceWorker.getRegistration()
|
||||
await registration?.unregister()
|
||||
window.justReloaded = true
|
||||
sessionStorage.justReloaded = true
|
||||
window.location.reload()
|
||||
if (failedUpdate) {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(resolve, 2000)
|
||||
})
|
||||
}
|
||||
if (activeModalStack.length !== 0) return
|
||||
if (failedUpdate) {
|
||||
sessionStorage.justReloaded = false
|
||||
// try to force bypass cache
|
||||
location.search = '?update=true'
|
||||
} else {
|
||||
window.justReloaded = true
|
||||
sessionStorage.justReloaded = true
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
const httpsRegex = /^https?:\/\//
|
||||
|
|
@ -40,10 +53,10 @@ export default ({ connectToServerAction, mapsProvider, singleplayerAction, optio
|
|||
const contents = await f.text()
|
||||
const isLatest = contents === process.env.BUILD_VERSION
|
||||
if (!isLatest && sessionStorage.justReloaded) {
|
||||
// try to force bypass cache
|
||||
location.search = '?update=true'
|
||||
setVersionStatus('(force reloading, wait)')
|
||||
void refreshApp(true)
|
||||
return
|
||||
}
|
||||
sessionStorage.justReloaded = false
|
||||
setVersionStatus(`(${isLatest ? 'latest' : 'new version available'})`)
|
||||
setVersionTitle(`Loaded: ${process.env.BUILD_VERSION}. Remote: ${contents}`)
|
||||
}, () => { })
|
||||
|
|
|
|||
|
|
@ -186,6 +186,12 @@ const Inner = () => {
|
|||
}
|
||||
}, [serverEditScreen])
|
||||
|
||||
useDidUpdateEffect(() => {
|
||||
if (!isEditScreenModal) {
|
||||
setServerEditScreen(null)
|
||||
}
|
||||
}, [isEditScreenModal])
|
||||
|
||||
if (isEditScreenModal) {
|
||||
return <AddServerOrConnect
|
||||
defaults={{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export default () => {
|
|||
const [openActionBar, setOpenActionBar] = useState(false)
|
||||
|
||||
useMemo(() => {
|
||||
// todo move to mineflayer
|
||||
bot._client.on('set_title_text', (packet) => {
|
||||
setTitle(JSON.parse(packet.text))
|
||||
setOpenTitle(true)
|
||||
|
|
|
|||
|
|
@ -95,29 +95,33 @@ const InGameComponent = ({ children }) => {
|
|||
}
|
||||
|
||||
const InGameUi = () => {
|
||||
const { gameLoaded } = useSnapshot(miscUiState)
|
||||
const { gameLoaded, showUI } = useSnapshot(miscUiState)
|
||||
if (!gameLoaded) return
|
||||
|
||||
return <>
|
||||
<RobustPortal to={document.querySelector('#ui-root')}>
|
||||
{/* apply scaling */}
|
||||
<DeathScreenProvider />
|
||||
<DebugOverlay />
|
||||
<MobileTopButtons />
|
||||
<PlayerListOverlayProvider />
|
||||
<ChatProvider />
|
||||
<SoundMuffler />
|
||||
<TitleProvider />
|
||||
<ScoreboardProvider />
|
||||
<IndicatorEffectsProvider />
|
||||
<TouchAreasControlsProvider />
|
||||
<Crosshair />
|
||||
<div style={{ display: showUI ? 'block' : 'none' }}>
|
||||
<DeathScreenProvider />
|
||||
<DebugOverlay />
|
||||
<MobileTopButtons />
|
||||
<PlayerListOverlayProvider />
|
||||
<ChatProvider />
|
||||
<SoundMuffler />
|
||||
<TitleProvider />
|
||||
<ScoreboardProvider />
|
||||
<IndicatorEffectsProvider />
|
||||
<Crosshair />
|
||||
<TouchAreasControlsProvider />
|
||||
</div>
|
||||
|
||||
<PauseScreen />
|
||||
<XPBarProvider />
|
||||
<HudBarsProvider />
|
||||
<HotbarRenderApp />
|
||||
<BedTime />
|
||||
<div style={{ display: showUI ? 'block' : 'none' }}>
|
||||
<XPBarProvider />
|
||||
<HudBarsProvider />
|
||||
<BedTime />
|
||||
</div>
|
||||
{showUI && <HotbarRenderApp />}
|
||||
</RobustPortal>
|
||||
<PerComponentErrorBoundary>
|
||||
<SignEditorProvider />
|
||||
|
|
@ -125,7 +129,7 @@ const InGameUi = () => {
|
|||
</PerComponentErrorBoundary>
|
||||
<RobustPortal to={document.body}>
|
||||
{/* because of z-index */}
|
||||
<TouchControls />
|
||||
{showUI && <TouchControls />}
|
||||
<GlobalSearchInput />
|
||||
</RobustPortal>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -57,4 +57,9 @@ export const watchOptionsAfterViewerInit = () => {
|
|||
customEvents.on('gameLoaded', () => {
|
||||
viewer.world.mesherConfig.enableLighting = !bot.supportFeature('blockStateId') || options.newVersionsLighting
|
||||
})
|
||||
|
||||
watchValue(options, o => {
|
||||
if (!(viewer.world instanceof WorldRendererThree)) return
|
||||
viewer.world.starField.enabled = o.starfieldRendering
|
||||
})
|
||||
}
|
||||
|
|
|
|||
4
src/workerWorkaround.ts
Normal file
4
src/workerWorkaround.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
//@ts-nocheck
|
||||
// eslint-disable-next-line no-global-assign
|
||||
global = globalThis
|
||||
globalThis.window = globalThis
|
||||
|
|
@ -17,6 +17,7 @@ import { LineMaterial, Wireframe, LineSegmentsGeometry } from 'three-stdlib'
|
|||
import { hideCurrentModal, isGameActive, showModal } from './globalState'
|
||||
import { assertDefined } from './utils'
|
||||
import { options } from './optionsStorage'
|
||||
import { itemBeingUsed } from './react/Crosshair'
|
||||
|
||||
function getViewDirection (pitch, yaw) {
|
||||
const csPitch = Math.cos(pitch)
|
||||
|
|
@ -133,6 +134,9 @@ class WorldInteraction {
|
|||
}
|
||||
this.lastDugBlock = null
|
||||
})
|
||||
bot.on('heldItemChanged' as any, () => {
|
||||
itemBeingUsed.name = null
|
||||
})
|
||||
|
||||
const upLineMaterial = () => {
|
||||
const inCreative = bot.game.gameMode === 'creative'
|
||||
|
|
@ -191,16 +195,20 @@ class WorldInteraction {
|
|||
|
||||
// Place / interact / activate
|
||||
if (this.buttons[2] && this.lastBlockPlaced >= 4) {
|
||||
const activate = bot.heldItem && ['egg', 'fishing_rod', 'firework_rocket',
|
||||
'fire_charge', 'snowball', 'ender_pearl', 'experience_bottle', 'potion',
|
||||
'glass_bottle', 'bucket', 'water_bucket', 'lava_bucket', 'milk_bucket',
|
||||
'minecart', 'boat', 'tnt_minecart', 'chest_minecart', 'hopper_minecart',
|
||||
'command_block_minecart', 'armor_stand', 'lead', 'name_tag',
|
||||
//
|
||||
'writable_book', 'written_book', 'compass', 'clock', 'filled_map', 'empty_map', 'map',
|
||||
'shears', 'carrot_on_a_stick', 'warped_fungus_on_a_stick',
|
||||
'spawn_egg', 'trident', 'crossbow', 'elytra', 'shield', 'turtle_helmet',
|
||||
].includes(bot.heldItem.name)
|
||||
const activatableItems = (itemName: string) => {
|
||||
return ['egg', 'fishing_rod', 'firework_rocket',
|
||||
'fire_charge', 'snowball', 'ender_pearl', 'experience_bottle', 'potion',
|
||||
'glass_bottle', 'bucket', 'water_bucket', 'lava_bucket', 'milk_bucket',
|
||||
'minecart', 'boat', 'tnt_minecart', 'chest_minecart', 'hopper_minecart',
|
||||
'command_block_minecart', 'armor_stand', 'lead', 'name_tag',
|
||||
//
|
||||
'writable_book', 'written_book', 'compass', 'clock', 'filled_map', 'empty_map', 'map',
|
||||
'shears', 'carrot_on_a_stick', 'warped_fungus_on_a_stick',
|
||||
'spawn_egg', 'trident', 'crossbow', 'elytra', 'shield', 'turtle_helmet', 'bow', 'crossbow', 'bucket_of_cod',
|
||||
...loadedData.foodsArray.map((f) => f.name),
|
||||
].includes(itemName)
|
||||
}
|
||||
const activate = bot.heldItem && activatableItems(bot.heldItem.name)
|
||||
let stop = false
|
||||
if (!bot.controlState.sneak) {
|
||||
if (cursorBlock?.name === 'bed' || cursorBlock?.name.endsWith('_bed')) {
|
||||
|
|
@ -223,6 +231,7 @@ class WorldInteraction {
|
|||
})
|
||||
}
|
||||
}
|
||||
// todo placing with offhand
|
||||
if (cursorBlock && !activate && !stop) {
|
||||
const vecArray = [new Vec3(0, -1, 0), new Vec3(0, 1, 0), new Vec3(0, 0, -1), new Vec3(0, 0, 1), new Vec3(-1, 0, 0), new Vec3(1, 0, 0)]
|
||||
//@ts-expect-error
|
||||
|
|
@ -242,10 +251,21 @@ class WorldInteraction {
|
|||
}).catch(console.warn)
|
||||
}
|
||||
} else if (!stop) {
|
||||
bot.activateItem() // todo offhand
|
||||
const offhand = activate ? false : activatableItems(bot.inventory.slots[45]?.name ?? '')
|
||||
bot.activateItem(offhand) // todo offhand
|
||||
itemBeingUsed.name = (offhand ? bot.inventory.slots[45]?.name : bot.heldItem?.name) ?? null
|
||||
itemBeingUsed.hand = offhand ? 1 : 0
|
||||
}
|
||||
this.lastBlockPlaced = 0
|
||||
}
|
||||
// stop using activated item (cancel)
|
||||
if (itemBeingUsed.name && !this.buttons[2]) {
|
||||
itemBeingUsed.name = null
|
||||
// "only foods and bow can be deactivated" - not true, shields also can be deactivated and client always sends this
|
||||
// if (bot.heldItem && (loadedData.foodsArray.map((f) => f.name).includes(bot.heldItem.name) || bot.heldItem.name === 'bow')) {
|
||||
bot.deactivateItem()
|
||||
// }
|
||||
}
|
||||
|
||||
// Stop break
|
||||
if ((!this.buttons[0] && this.lastButtons[0]) || cursorChanged) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"useUnknownInCatchVariables": false,
|
||||
"skipLibCheck": true,
|
||||
"experimentalDecorators": true,
|
||||
"strictBindCallApply": true,
|
||||
// this the only options that allows smooth transition from js to ts (by not dropping types from js files)
|
||||
// however might need to consider includeing *only needed libraries* instead of using this
|
||||
"maxNodeModuleJsDepth": 1,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue