mirror of
https://github.com/d07RiV/diabloweb.git
synced 2025-05-11 13:57:21 +00:00
updates
This commit is contained in:
parent
ca67365fe7
commit
1c7c0684c7
@ -72,7 +72,9 @@ module.exports = {
|
||||
appBuild: resolveApp('build'),
|
||||
appPublic: resolveApp('public'),
|
||||
appHtml: resolveApp('public/index.html'),
|
||||
appHtmlStorage: resolveApp('public/storage.html'),
|
||||
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
||||
appStorageJs: resolveModule(resolveApp, 'src/storage'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appTsConfig: resolveApp('tsconfig.json'),
|
||||
|
@ -128,25 +128,28 @@ module.exports = function(webpackEnv) {
|
||||
: isEnvDevelopment && 'cheap-module-source-map',
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
entry: [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
isEnvDevelopment &&
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
].filter(Boolean),
|
||||
entry: {
|
||||
main: [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
isEnvDevelopment &&
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
].filter(Boolean),
|
||||
storage: paths.appStorageJs,
|
||||
},
|
||||
output: {
|
||||
// The build folder.
|
||||
path: isEnvProduction ? paths.appBuild : undefined,
|
||||
@ -500,6 +503,7 @@ module.exports = function(webpackEnv) {
|
||||
{},
|
||||
{
|
||||
inject: true,
|
||||
chunks: ['main'],
|
||||
template: paths.appHtml,
|
||||
},
|
||||
isEnvProduction
|
||||
@ -520,6 +524,33 @@ module.exports = function(webpackEnv) {
|
||||
: undefined
|
||||
)
|
||||
),
|
||||
new HtmlWebpackPlugin(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
inject: true,
|
||||
chunks: ['storage'],
|
||||
filename: 'storage.html',
|
||||
template: paths.appHtmlStorage,
|
||||
},
|
||||
isEnvProduction
|
||||
? {
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
),
|
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
isEnvProduction &&
|
||||
|
5992
package-lock.json
generated
5992
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -64,7 +64,8 @@
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
"build": "node scripts/build.js",
|
||||
"test": "node scripts/test.js"
|
||||
"test": "node scripts/test.js",
|
||||
"deploy": "gh-pages -d build"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
@ -131,8 +132,10 @@
|
||||
"react-app"
|
||||
]
|
||||
},
|
||||
"homepage": "https://d07riv.github.io/diabloweb",
|
||||
"devDependencies": {
|
||||
"exports-loader": "^0.7.0",
|
||||
"gh-pages": "^2.0.1",
|
||||
"node-sass": "^4.12.0",
|
||||
"worker-loader": "^2.0.0"
|
||||
}
|
||||
|
BIN
public/icon-192.png
Normal file
BIN
public/icon-192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
public/icon-512.png
Normal file
BIN
public/icon-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
@ -6,10 +6,20 @@
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
"display": "fullscreen",
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#000000"
|
||||
}
|
||||
|
9
public/storage.html
Normal file
9
public/storage.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>DIABLO Connector</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
35
src/App.js
35
src/App.js
@ -46,7 +46,7 @@ class App extends React.Component {
|
||||
touchMods = [false, false, false, false, false, false];
|
||||
touchBelt = [-1, -1, -1, -1, -1, -1];
|
||||
|
||||
fs = create_fs(this);
|
||||
fs = create_fs(true);
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -344,6 +344,11 @@ class App extends React.Component {
|
||||
this.touchButton = touchOther;
|
||||
if (touchOther) {
|
||||
this.setTouchMod(touchOther.index, true);
|
||||
if (touchOther.index === TOUCH_MOVE) {
|
||||
this.setTouchMod(TOUCH_RMB, false);
|
||||
} else if (touchOther.index === TOUCH_RMB) {
|
||||
this.setTouchMod(TOUCH_MOVE, false);
|
||||
}
|
||||
delete this.panPos;
|
||||
} else if (touches.length === 2) {
|
||||
const x = (touches[1].clientX + touches[0].clientX) / 2, y = (touches[1].clientY + touches[0].clientY) / 2;
|
||||
@ -406,6 +411,10 @@ class App extends React.Component {
|
||||
const {x, y} = this.mousePos(prevTc);
|
||||
this.game("DApi_Mouse", 2, 1, this.eventMods(e), x, y);
|
||||
this.game("DApi_Mouse", 2, 2, this.eventMods(e), x, y);
|
||||
|
||||
if (this.touchMods[TOUCH_RMB] && (!this.touchButton || this.touchButton.index !== TOUCH_RMB)) {
|
||||
this.setTouchButton(TOUCH_RMB, false);
|
||||
}
|
||||
}
|
||||
if (!document.fullscreenElement) {
|
||||
this.element.requestFullscreen();
|
||||
@ -469,22 +478,20 @@ class App extends React.Component {
|
||||
This is a web port of the original Diablo game, based on source code reconstructed by
|
||||
GalaXyHaXz and devilution team: <Link href="https://github.com/diasurgical/devilution">https://github.com/diasurgical/devilution</Link>
|
||||
</p>
|
||||
<form>
|
||||
<p>
|
||||
If you own the original game, you can drop the original DIABDAT.MPQ onto this page or click the button below to start playing.
|
||||
The game can be purchased from <Link href="https://www.gog.com/game/diablo">GoG</Link>.
|
||||
</p>
|
||||
{!has_spawn && (
|
||||
<p>
|
||||
If you own the original game, you can drop the original DIABDAT.MPQ onto this page (or <label htmlFor="loadFile" className="link" onClick={this.download}>click here</label>)
|
||||
to start playing. The game can be purchased from <Link href="https://www.gog.com/game/diablo">GoG</Link>.
|
||||
</p>
|
||||
<input accept=".mpq" type="file" id="loadFile" style={{display: "none"}} onChange={this.parseFile}/>
|
||||
</form>
|
||||
{has_spawn ? (
|
||||
<span className="startButton" onClick={() => this.start()}>Play Shareware</span>
|
||||
) : (
|
||||
<p>
|
||||
Or you can download and play the shareware version instead (50MB download). <i>The site has lately been under too much stress due to users downloading the shareware
|
||||
package, so instead you will need to <a href="https://d07riv.github.io/diabloweb/public/spawn.mpq">download it from GitHub</a>, then drop it on the page (or upload
|
||||
by <label htmlFor="loadFile" className="link" onClick={this.download}>clicking here</label>).</i>
|
||||
Or you can play the shareware version for free (50MB download).
|
||||
</p>
|
||||
)}
|
||||
<form>
|
||||
<label htmlFor="loadFile" className="startButton">Select MPQ</label>
|
||||
<input accept=".mpq" type="file" id="loadFile" style={{display: "none"}} onChange={this.parseFile}/>
|
||||
</form>
|
||||
<span className="startButton" onClick={() => this.start()}>Play Shareware</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -79,7 +79,7 @@ body, #root, .App {
|
||||
text-align: center;
|
||||
background: #000;
|
||||
p {
|
||||
margin: 12px 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.startButton {
|
||||
display: inline-block;
|
||||
@ -88,6 +88,8 @@ body, #root, .App {
|
||||
font-size: 2em;
|
||||
padding: 4px 18px;
|
||||
cursor: pointer;
|
||||
margin-top: 6px;
|
||||
width: 90%;
|
||||
&:hover {
|
||||
background-color: #111;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//import axios from 'axios';
|
||||
import axios from 'axios';
|
||||
|
||||
const SpawnSize = 50274091;
|
||||
|
||||
@ -12,13 +12,12 @@ export default async function load_spawn(api, fs) {
|
||||
file = null;
|
||||
}
|
||||
if (!file) {
|
||||
throw Error("Invalid spawn.mpq size.");
|
||||
/*const spawn = await axios.request({
|
||||
const spawn = await axios.request({
|
||||
url: '/spawn.mpq',
|
||||
responseType: 'arraybuffer',
|
||||
onDownloadProgress: e => {
|
||||
if (api.onProgress) {
|
||||
api.onProgress({type: 'spawn', loaded: e.loaded, total: e.total || SpawnSize});
|
||||
api.onProgress({text: 'Downloading...', loaded: e.loaded, total: e.total || SpawnSize});
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
@ -30,7 +29,7 @@ export default async function load_spawn(api, fs) {
|
||||
}
|
||||
const data = new Uint8Array(spawn.data);
|
||||
fs.files.set('spawn.mpq', data);
|
||||
fs.update('spawn.mpq', data);*/
|
||||
fs.update('spawn.mpq', data.slice());
|
||||
}
|
||||
return fs;
|
||||
}
|
||||
|
43
src/fs.js
43
src/fs.js
@ -1,22 +1,63 @@
|
||||
import IdbKvStore from 'idb-kv-store';
|
||||
|
||||
export default async function create_fs() {
|
||||
const importStorage = () => new Promise((resolve, reject) => {
|
||||
let done = false;
|
||||
const frame = document.createElement('iframe');
|
||||
window.addEventListener('message', ({data}) => {
|
||||
if (data.method === 'storage' && !done) {
|
||||
done = true;
|
||||
resolve(data.files);
|
||||
frame.contentWindow.postMessage({method: 'clear'}, '*');
|
||||
}
|
||||
});
|
||||
frame.addEventListener('load', () => {
|
||||
frame.contentWindow.postMessage({method: 'transfer'}, '*');
|
||||
});
|
||||
frame.addEventListener('error', () => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
frame.src = "https://diablo.rivsoft.net/storage.html";
|
||||
frame.style.display = "none";
|
||||
document.body.appendChild(frame);
|
||||
setTimeout(() => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
resolve(null);
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
export default async function create_fs(load) {
|
||||
try {
|
||||
const store = new IdbKvStore('diablo_fs');
|
||||
const files = new Map();
|
||||
for (let [name, data] of Object.entries(await store.json())) {
|
||||
files.set(name, data);
|
||||
}
|
||||
if (load) {
|
||||
const files = await importStorage();
|
||||
if (files) {
|
||||
for (let [name, data] of files) {
|
||||
files.set(name, data);
|
||||
store.set(name, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
files,
|
||||
update: (name, data) => store.set(name, data),
|
||||
delete: name => store.remove(name),
|
||||
clear: () => store.clear(),
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
files: new Map(),
|
||||
update: () => Promise.resolve(),
|
||||
delete: () => Promise.resolve(),
|
||||
clear: () => Promise.resolve(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './reset.css';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
|
||||
serviceWorker.register();
|
||||
|
135
src/serviceWorker.js
Normal file
135
src/serviceWorker.js
Normal file
@ -0,0 +1,135 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl, config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl, config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
12
src/storage.js
Normal file
12
src/storage.js
Normal file
@ -0,0 +1,12 @@
|
||||
import create_fs from './fs';
|
||||
|
||||
const fs = create_fs();
|
||||
window.addEventListener('message', ({data, source}) => {
|
||||
if (data.method === 'transfer') {
|
||||
fs.then(({files}) => {
|
||||
source.postMessage({method: 'storage', files}, '*');
|
||||
});
|
||||
} else if (data.method === 'clear') {
|
||||
fs.then(({clear}) => clear());
|
||||
}
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user