diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae3577a4..a4704ce9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,4 +21,3 @@ jobs: with: name: cypress-images path: cypress/integration/__image_snapshots__/ - if-no-files-found: ignore diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ccfc7311..17e718ca 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,8 +13,15 @@ jobs: run: npm i -g pnpm - run: pnpm install - run: pnpm build - # todo use nohup and official action? - # - run: pnpm prod-start & pnpm test:cypress + - uses: cypress-io/github-action@v5 + with: + install: false + start: pnpm prod-start + - uses: actions/upload-artifact@v3 + if: failure() + with: + name: cypress-images + path: cypress/integration/__image_snapshots__/ - uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 1a3c5c10..23b666f8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ public .env.local Thumbs.db build +localSettings.mjs dist .DS_Store .idea/ diff --git a/.vscode/launch.json b/.vscode/launch.json index bdc14789..7b9f4733 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,6 +7,7 @@ "address": "localhost", "name": "Attach Chrome", "request": "attach", + // comment if using webpack "pathMapping": { "/": "${workspaceFolder}/dist" }, @@ -24,10 +25,13 @@ }, { // not recommended as in most cases it will slower as it launches from extension host so it slows down extension host, not sure why - "type": "msedge", - "name": "Launch Edge", + "type": "chrome", + "name": "Launch Chrome", "request": "launch", "url": "http://localhost:8080/", + "pathMapping": { + "/": "${workspaceFolder}/dist" + }, "outFiles": [ "${workspaceFolder}/dist/**/*.js", // "!${workspaceFolder}/dist/**/*vendors*", diff --git a/cypress/integration/index.spec.ts b/cypress/integration/index.spec.ts index 41f8e96d..53505b3c 100644 --- a/cypress/integration/index.spec.ts +++ b/cypress/integration/index.spec.ts @@ -3,15 +3,18 @@ it('Loads & renders singleplayer', () => { // todo use ` } - onBtnClick () { + onBtnClick (e) { playSound('click_stereo.mp3') - this.dispatchEvent(new window.CustomEvent('pmui-click')) + this.dispatchEvent(new window.CustomEvent('pmui-click', { detail: e, })) } } diff --git a/src/menus/components/food_bar.js b/src/menus/components/food_bar.js index 17bdcccb..7f71ad0f 100644 --- a/src/menus/components/food_bar.js +++ b/src/menus/components/food_bar.js @@ -1,4 +1,5 @@ -const { LitElement, html, css } = require('lit') +const { LitElement, html, css, unsafeCSS } = require('lit') +const { guiIcons1_17_1 } = require('../hud') class FoodBar extends LitElement { static get styles () { @@ -19,7 +20,7 @@ class FoodBar extends LitElement { .food { width: 9px; height: 9px; - background-image: url('textures/1.17.1/gui/icons.png'), url('textures/1.17.1/gui/icons.png'); + background-image: url('${unsafeCSS(guiIcons1_17_1)}'), url('${unsafeCSS(guiIcons1_17_1)}'); background-size: 256px, 256px; background-position: var(--bg-x) var(--bg-y), var(--bg-x) var(--bg-y); margin-left: -1px; diff --git a/src/menus/components/health_bar.js b/src/menus/components/health_bar.js index fdbc3b8c..a6dacef8 100644 --- a/src/menus/components/health_bar.js +++ b/src/menus/components/health_bar.js @@ -1,4 +1,5 @@ -const { LitElement, html, css } = require('lit') +const { LitElement, html, css, unsafeCSS } = require('lit') +const { guiIcons1_17_1 } = require('../hud') function getEffectClass (effect) { switch (effect.id) { @@ -49,7 +50,7 @@ class HealthBar extends LitElement { .heart { width: 9px; height: 9px; - background-image: url('textures/1.17.1/gui/icons.png'), url('textures/1.17.1/gui/icons.png'); + background-image: url('${unsafeCSS(guiIcons1_17_1)}'), url('${unsafeCSS(guiIcons1_17_1)}'); background-size: 256px, 256px; background-position: var(--bg-x) var(--bg-y), var(--bg-x) var(--bg-y); margin-left: -1px; diff --git a/src/menus/components/hotbar.js b/src/menus/components/hotbar.js index d347b4f9..32b78606 100644 --- a/src/menus/components/hotbar.js +++ b/src/menus/components/hotbar.js @@ -1,7 +1,9 @@ -const { LitElement, html, css } = require('lit') +const { LitElement, html, css, unsafeCSS } = require('lit') const invsprite = require('../../invsprite.json') const { isGameActive } = require('../../globalState') +const widgetsTexture = require('minecraft-assets/minecraft-assets/data/1.16.4/gui/widgets.png') + class Hotbar extends LitElement { static get styles () { return css` @@ -12,7 +14,7 @@ class Hotbar extends LitElement { transform: translate(-50%); width: 182px; height: 22px; - background: url("textures/1.16.4/gui/widgets.png"); + background: url("${unsafeCSS(widgetsTexture)}"); background-size: 256px; } @@ -22,7 +24,7 @@ class Hotbar extends LitElement { top: -1px; width: 24px; height: 24px; - background: url("textures/1.16.4/gui/widgets.png"); + background: url("${unsafeCSS(widgetsTexture)}"); background-size: 256px; background-position-y: -22px; } diff --git a/src/menus/components/slider.js b/src/menus/components/slider.js index 25ced293..bb72f9f2 100644 --- a/src/menus/components/slider.js +++ b/src/menus/components/slider.js @@ -1,4 +1,6 @@ -const { LitElement, html, css } = require('lit') +const { LitElement, html, css, unsafeCSS } = require('lit') + +const widgetsGui = require('minecraft-assets/minecraft-assets/data/1.17.1/gui/widgets.png') class Slider extends LitElement { static get styles () { @@ -39,7 +41,7 @@ class Slider extends LitElement { left: 0; width: 50%; height: 20px; - background: url('textures/1.17.1/gui/widgets.png'); + background: url('${unsafeCSS(widgetsGui)}'); background-size: 256px; background-position-y: var(--txrV); z-index: -1; @@ -54,7 +56,7 @@ class Slider extends LitElement { left: 50%; width: 50%; height: 20px; - background: url('textures/1.17.1/gui/widgets.png'); + background: url('${unsafeCSS(widgetsGui)}'); background-size: 256px; background-position-x: calc(-200px + 100%); background-position-y: var(--txrV); diff --git a/src/menus/hud.js b/src/menus/hud.js index f465b50e..fa82f86f 100644 --- a/src/menus/hud.js +++ b/src/menus/hud.js @@ -1,10 +1,13 @@ //@ts-check -const { LitElement, html, css } = require('lit') +const { LitElement, html, css, unsafeCSS } = require('lit') const { isMobile } = require('./components/common') const { showModal, miscUiState } = require('../globalState') const { options, watchValue } = require('../optionsStorage') const { getGamemodeNumber } = require('../utils') +export const guiIcons1_17_1 = require('minecraft-assets/minecraft-assets/data/1.17.1/gui/icons.png') +export const guiIcons1_16_4 = require('minecraft-assets/minecraft-assets/data/1.16.4/gui/icons.png') + class Hud extends LitElement { static get styles () { return css` @@ -21,7 +24,7 @@ class Hud extends LitElement { .crosshair { width: 16px; height: 16px; - background: url('textures/1.17.1/gui/icons.png'); + background: url('${unsafeCSS(guiIcons1_17_1)}'); background-size: 256px; position: absolute; top: 50%; @@ -49,7 +52,7 @@ class Hud extends LitElement { transform: translate(-50%); width: 182px; height: 5px; - background-image: url('textures/1.16.4/gui/icons.png'); + background-image: url('${unsafeCSS(guiIcons1_16_4)}'); background-size: 256px; background-position-y: -64px; } @@ -57,7 +60,7 @@ class Hud extends LitElement { .xp-bar { width: 182px; height: 5px; - background-image: url('textures/1.17.1/gui/icons.png'); + background-image: url('${unsafeCSS(guiIcons1_17_1)}'); background-size: 256px; background-position-y: -69px; } diff --git a/src/menus/loading_or_error_screen.js b/src/menus/loading_or_error_screen.js index dfbf3660..9519179e 100644 --- a/src/menus/loading_or_error_screen.js +++ b/src/menus/loading_or_error_screen.js @@ -2,8 +2,9 @@ const { LitElement, html, css } = require('lit') const { commonCss } = require('./components/common') const { addPanoramaCubeMap } = require('../panorama') -const { hideModal, activeModalStacks, activeModalStack, replaceActiveModalStack } = require('../globalState') +const { hideModal, activeModalStacks, activeModalStack, replaceActiveModalStack, miscUiState } = require('../globalState') const { guessProblem } = require('../guessProblem') +const { fsState } = require('../loadFolder') class LoadingErrorScreen extends LitElement { static get styles () { @@ -84,6 +85,14 @@ class LoadingErrorScreen extends LitElement { } document.getElementById('play-screen').style.display = 'block' addPanoramaCubeMap() + }}> { + if (!confirm('Are you sure you want to delete all local world content?')) return + for (const key of Object.keys(localStorage)) { + if (/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/g.test(key) || key === '/') { + localStorage.removeItem(key) + } + } + window.location.reload() }}> window.location.reload()} pmui-label="Full Reload" pmui-width="200px">` : '' } diff --git a/src/menus/pause_screen.js b/src/menus/pause_screen.js index 120859b5..a2290d06 100644 --- a/src/menus/pause_screen.js +++ b/src/menus/pause_screen.js @@ -1,8 +1,11 @@ +//@ts-check const { LitElement, html, css } = require('lit') const { openURL } = require('./components/common') const { hideCurrentModal, showModal } = require('../globalState') const { fsState } = require('../loadFolder') const { subscribe } = require('valtio') +const { saveWorld } = require('../builtinCommands') +const { notification } = require('./notification') class PauseScreen extends LitElement { static get styles () { @@ -67,16 +70,10 @@ class PauseScreen extends LitElement { openURL('https://discord.gg/4Ucm684Fq3')}> showModal(document.getElementById('options-screen'))}> - { + { if (window.singlePlayerServer) { - for (const player of window.singlePlayerServer.players) { - const worlds = [singlePlayerServer.overworld] - for (const world of worlds) { - await world.storageProvider.close() - } - await player.save() - singlePlayerServer.quit() - } + await saveWorld() + singlePlayerServer.quit() } bot._client.emit('end') }}> diff --git a/src/menus/title_screen.js b/src/menus/title_screen.js index 204f0f86..93c6e1ff 100644 --- a/src/menus/title_screen.js +++ b/src/menus/title_screen.js @@ -1,8 +1,13 @@ -const { openWorldDirectory } = require('../browserfs') +const { openWorldDirectory, openWorldZip } = require('../browserfs') const { showModal } = require('../globalState') const { fsState } = require('../loadFolder') const { openURL } = require('./components/common') -const { LitElement, html, css } = require('lit') +const { LitElement, html, css, unsafeCSS } = require('lit') + +const mcImage = require('minecraft-assets/minecraft-assets/data/1.17.1/gui/title/minecraft.png') + +// const SUPPORT_WORLD_LOADING = !!window.showDirectoryPicker +const SUPPORT_WORLD_LOADING = true class TitleScreen extends LitElement { static get styles () { @@ -18,7 +23,7 @@ class TitleScreen extends LitElement { position: absolute; top: 0; left: 0; - background-image: url('textures/1.17.1/gui/title/minecraft.png'); + background-image: url('${unsafeCSS(mcImage)}'); background-size: 256px; width: 155px; height: 44px; @@ -29,7 +34,7 @@ class TitleScreen extends LitElement { position: absolute; top: 0; left: 155px; - background-image: url('textures/1.17.1/gui/title/minecraft.png'); + background-image: url('${unsafeCSS(mcImage)}'); background-size: 256px; width: 155px; height: 44px; @@ -125,7 +130,9 @@ class TitleScreen extends LitElement { this.versionStatus = '' this.versionTitle = '' this.isOutdated = false - if (process.env.NODE_ENV !== 'development') { + if (process.env.NODE_ENV === 'development') { + this.versionStatus = '(dev)' + } else { fetch('./version.txt').then(async (f) => { if (f.status === 404) return const contents = await f.text() @@ -148,16 +155,42 @@ class TitleScreen extends LitElement { } diff --git a/src/styles.css b/src/styles.css index 7f2b0a59..726d4aef 100644 --- a/src/styles.css +++ b/src/styles.css @@ -26,7 +26,7 @@ html { position: absolute; top: 0; left: 0; - background: url('textures/1.17.1/gui/options_background.png'), rgba(0, 0, 0, 0.7); + background: url('minecraft-assets/minecraft-assets/data/1.17.1/gui/options_background.png'), rgba(0, 0, 0, 0.7); background-size: 16px; background-repeat: repeat; width: 100%; diff --git a/src/utils.js b/src/utils.js index cf129d24..30c0d41c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,8 @@ //@ts-check import { activeModalStack, miscUiState } from './globalState' import { notification } from './menus/notification' +import * as crypto from 'crypto' +import UUID from 'uuid-1345' export const goFullscreen = async (doToggle = false) => { if (!document.fullscreenElement) { @@ -116,3 +118,17 @@ export const getGamemodeNumber = (bot) => { export const isCypress = () => { return localStorage.cypress === 'true' } + +// https://github.com/PrismarineJS/node-minecraft-protocol/blob/cf1f67117d586b5e6e21f0d9602da12e9fcf46b6/src/server/login.js#L170 +function javaUUID (s) { + const hash = crypto.createHash('md5') + hash.update(s, 'utf8') + const buffer = hash.digest() + buffer[6] = (buffer[6] & 0x0f) | 0x30 + buffer[8] = (buffer[8] & 0x3f) | 0x80 + return buffer +} + +export function nameToMcOfflineUUID (name) { + return (new UUID(javaUUID('OfflinePlayer:' + name))).toString() +}