Initial commit
This commit is contained in:
commit
cf9323beae
8
.gitignore
vendored
Executable file
8
.gitignore
vendored
Executable file
@ -0,0 +1,8 @@
|
||||
# Node
|
||||
node_modules/
|
||||
|
||||
# Development
|
||||
view/import/
|
||||
|
||||
# Misc
|
||||
.vscode/
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "subrepo/Xash3D-Emscripten"]
|
||||
path = subrepo/Xash3D-Emscripten
|
||||
url = https://github.com/iCrazyBlaze/Xash3D-Emscripten
|
58
assets/global.scss
Executable file
58
assets/global.scss
Executable file
@ -0,0 +1,58 @@
|
||||
.xash3d_terminal code {
|
||||
display: block;
|
||||
width: 60ch;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.xash3d_launcher {
|
||||
background: #333 var(--xash-trame);
|
||||
background-size: 4px;
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
:root {
|
||||
--mod-height: 36px;
|
||||
}
|
||||
|
||||
html {
|
||||
/* im not gonna write a preprocessor for the css so i'm refrencing an element here */
|
||||
background: #333 var(--trame);
|
||||
background-size: 4px;
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
.xash3d_launcher .mod {
|
||||
display: flex;
|
||||
padding: 1px;
|
||||
box-sizing: content-box;
|
||||
height: var(--mod-height)
|
||||
}
|
||||
|
||||
.xash3d_launcher .info {
|
||||
border-right: 1px solid #666;
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
margin: 2px 0 2px 5px;
|
||||
}
|
||||
|
||||
.xash3d_launcher .info h4 {
|
||||
font-size: 16px;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.xash3d_launcher .launch {
|
||||
width: var(--mod-height);
|
||||
height: var(--mod-height);
|
||||
box-sizing: content-box;
|
||||
background-image: url(play.svg);
|
||||
background-size: 20px 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.xash3d_launcher .launch:active {
|
||||
opacity: .6;
|
||||
}
|
BIN
assets/play.png
Normal file
BIN
assets/play.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 204 B |
BIN
dist/xash.asar
vendored
Executable file
BIN
dist/xash.asar
vendored
Executable file
Binary file not shown.
32
dist/xash.dev.js
vendored
Executable file
32
dist/xash.dev.js
vendored
Executable file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
/******/ (() => { // webpackBootstrap
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ "./src/index.js":
|
||||
/*!**********************!*\
|
||||
!*** ./src/index.js ***!
|
||||
\**********************/
|
||||
/***/ (() => {
|
||||
|
||||
eval("console.log('Hello, World!')\n\n//# sourceURL=webpack://xash93d/./src/index.js?");
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/
|
||||
/******/ // startup
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ // This entry module can't be inlined because the eval devtool is used.
|
||||
/******/ var __webpack_exports__ = {};
|
||||
/******/ __webpack_modules__["./src/index.js"]();
|
||||
/******/
|
||||
/******/ })()
|
||||
;
|
11
import.sh
Executable file
11
import.sh
Executable file
@ -0,0 +1,11 @@
|
||||
cd subrepo/Xash3D-Emscripten
|
||||
cp
|
||||
client.js
|
||||
icon.png
|
||||
menu.js
|
||||
mods.js
|
||||
server.js
|
||||
xash.html.mem
|
||||
xash.js
|
||||
|
||||
../../view/import/
|
BIN
mods/hldm-gz.asar
Executable file
BIN
mods/hldm-gz.asar
Executable file
Binary file not shown.
BIN
mods/hldm-unzip/hldm.data
Executable file
BIN
mods/hldm-unzip/hldm.data
Executable file
Binary file not shown.
314
mods/hldm-unzip/hldm.js
Executable file
314
mods/hldm-unzip/hldm.js
Executable file
File diff suppressed because one or more lines are too long
4
mods/hldm-unzip/manifest.json
Executable file
4
mods/hldm-unzip/manifest.json
Executable file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "HLDM",
|
||||
"entry": "hldm.js"
|
||||
}
|
BIN
mods/hldm.asar
Executable file
BIN
mods/hldm.asar
Executable file
Binary file not shown.
BIN
mods/hldm.asar.gz
Executable file
BIN
mods/hldm.asar.gz
Executable file
Binary file not shown.
BIN
mods/hldm/hldm.data
Executable file
BIN
mods/hldm/hldm.data
Executable file
Binary file not shown.
BIN
mods/hldm/hldm.js
Executable file
BIN
mods/hldm/hldm.js
Executable file
Binary file not shown.
4
mods/hldm/manifest.json
Executable file
4
mods/hldm/manifest.json
Executable file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "HLDM",
|
||||
"entry": "hldm.js"
|
||||
}
|
BIN
mods/uplink.asar
Executable file
BIN
mods/uplink.asar
Executable file
Binary file not shown.
5
mods/uplink/manifest.json
Executable file
5
mods/uplink/manifest.json
Executable file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Half-Life: Uplink",
|
||||
"id": "uplink",
|
||||
"entry": "uplink.js"
|
||||
}
|
BIN
mods/uplink/uplink.data
Executable file
BIN
mods/uplink/uplink.data
Executable file
Binary file not shown.
311
mods/uplink/uplink.js
Executable file
311
mods/uplink/uplink.js
Executable file
File diff suppressed because one or more lines are too long
8977
package-lock.json
generated
Executable file
8977
package-lock.json
generated
Executable file
File diff suppressed because it is too large
Load Diff
33
package.json
Executable file
33
package.json
Executable file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "xash93d",
|
||||
"version": "1.0.0",
|
||||
"description": "Xash3D-Emscripten Ported To Win93",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"init": "import.sh",
|
||||
"build": "webpack -c webpack.config.js && asar p view/ dist/xash.asar"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 1 version",
|
||||
">15%, not dead",
|
||||
"not IE <= 11"
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.6",
|
||||
"asar": "^3.0.3",
|
||||
"djv": "^2.1.4",
|
||||
"fflate": "^0.6.10",
|
||||
"node-sass": "^6.0.1",
|
||||
"pixel-art-2-svg": "^0.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.4",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"babel-loader": "^8.2.2",
|
||||
"webpack": "^5.33.2",
|
||||
"webpack-cli": "^4.6.0"
|
||||
},
|
||||
"author": "Dakedres",
|
||||
"license": "ISC"
|
||||
}
|
7
scripts/build.js
Executable file
7
scripts/build.js
Executable file
@ -0,0 +1,7 @@
|
||||
const files = [
|
||||
"xash.js",
|
||||
"mods.js",
|
||||
"server.js",
|
||||
"client.js",
|
||||
"menu.js"
|
||||
]
|
23
scripts/processAssets.js
Normal file
23
scripts/processAssets.js
Normal file
@ -0,0 +1,23 @@
|
||||
const path = require('path'),
|
||||
fs = require('fs').promises,
|
||||
pixel2svg = require('pixel-art-2-svg')
|
||||
|
||||
const assetsIn = path.join(__dirname, '../assets'),
|
||||
assetsOut = path.join(__dirname, '../view/assets')
|
||||
|
||||
const write = async (location, data) => {
|
||||
const filename = path.parse(location).name + '.svg',
|
||||
writeTo = path.join(assetsOut, filename)
|
||||
|
||||
await fs.writeFile(writeTo, data, 'utf-8')
|
||||
console.log(`Converted ${filename}`)
|
||||
}
|
||||
|
||||
fs.readdir(assetsIn)
|
||||
.then(files =>
|
||||
Promise.all(files.map(at => pixel2svg(path.join(assetsIn, at) )
|
||||
.then(data => write(at, data))
|
||||
.catch(console.error)
|
||||
))
|
||||
)
|
||||
.then(files => console.log(`Processed ${files.length} file(s)!`) )
|
97
src/App.js
Executable file
97
src/App.js
Executable file
@ -0,0 +1,97 @@
|
||||
const Instance = require('./Instance'),
|
||||
Launcher = require('./Launcher'),
|
||||
{ app } = require('./util/constants')
|
||||
|
||||
class App {
|
||||
constructor() {
|
||||
this.name = app.id
|
||||
this.categories = app.categories
|
||||
this.version = app.version
|
||||
this.sessions = []
|
||||
this.assets = {}
|
||||
this.bundle = $bundle.for('/')
|
||||
|
||||
let self
|
||||
|
||||
// Run acts as a proxy of sorts so we can retain access to
|
||||
// the app instance as the context
|
||||
this.exec = function(cfg) {
|
||||
self.run(cfg, this)
|
||||
}
|
||||
|
||||
const openAsset = (name, path) =>
|
||||
this.bundle.open(path, 'URL').then(url => {
|
||||
self.assets[name] = url
|
||||
})
|
||||
|
||||
// Fake filename so we can target it with CSS to work around
|
||||
// how app icons work
|
||||
this.icon = 'xash'
|
||||
|
||||
// Should probably just load all of "import/"
|
||||
// but there's not any ls function in abnt
|
||||
this.init()
|
||||
|
||||
this.config = {
|
||||
pauseOnLostFocus: true
|
||||
}
|
||||
|
||||
self = this
|
||||
}
|
||||
|
||||
async run(cfg, context) {
|
||||
const self = this,
|
||||
{ arg, cli } = context
|
||||
|
||||
console.log(context)
|
||||
|
||||
if(arg.arguments.length > 0) {
|
||||
let path = $fs.utils.resolvePath(arg.arguments[0])
|
||||
|
||||
if($fs.utils.exist(path) !== false) {
|
||||
this.launch(path)
|
||||
} else {
|
||||
cli.log.error(`Could not open path "${path}"`)
|
||||
}
|
||||
} else {
|
||||
new Launcher(this)
|
||||
}
|
||||
}
|
||||
|
||||
launch(path) {
|
||||
let sess = new Instance(this, path) //testing path
|
||||
|
||||
this.sessions.push(sess)
|
||||
}
|
||||
|
||||
cleanInstances() {
|
||||
const { sessions } = this
|
||||
|
||||
for(let i in sessions)
|
||||
if(sessions[i].closed)
|
||||
delete this.sessions[i]
|
||||
}
|
||||
|
||||
killAll() {
|
||||
for(let session of this.sessions)
|
||||
session.kill()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bundle.open('global.css', 'URL')
|
||||
.then($loader.css)
|
||||
|
||||
const assets = {
|
||||
"trame": "./half-trame.png",
|
||||
"play": "./assets/play.svg",
|
||||
"icon": "./import/icon.png"
|
||||
}
|
||||
|
||||
for(let name in assets) {
|
||||
this.bundle.open(assets[name], 'URL')
|
||||
.then(url => document.querySelector(':root').style.setProperty(`--xash3d-${name}`, `url(${url})`) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = App
|
55
src/Console.js
Executable file
55
src/Console.js
Executable file
@ -0,0 +1,55 @@
|
||||
class Console extends DocumentFragment {
|
||||
constructor(onCloseSignal) {
|
||||
super()
|
||||
|
||||
this.container = document.createElement('code')
|
||||
this.parent = undefined
|
||||
this.onCloseSignal = onCloseSignal
|
||||
super.append(this.container)
|
||||
}
|
||||
|
||||
_createLine(lines, classList) {
|
||||
for(let content of lines) {
|
||||
if(content == 'exit(0)')
|
||||
this.onCloseSignal()
|
||||
|
||||
let line = document.createElement('div')
|
||||
|
||||
if(classList) line.classList = classList
|
||||
this.container.appendChild(line)
|
||||
line.innerText = content
|
||||
}
|
||||
|
||||
this._tickParent()
|
||||
}
|
||||
|
||||
_tickParent() {
|
||||
if(this.parent) {
|
||||
let { parent } = this
|
||||
|
||||
parent.scrollTop = parent.scrollHeight
|
||||
}
|
||||
}
|
||||
|
||||
log(...lines) {
|
||||
return this._createLine(lines)
|
||||
}
|
||||
|
||||
error(...lines) {
|
||||
return this._createLine(lines, 'ui_log__red')
|
||||
}
|
||||
|
||||
attachTo(window) {
|
||||
let { body } = window.el
|
||||
|
||||
body.append(this)
|
||||
this.parent = body
|
||||
this._tickParent()
|
||||
}
|
||||
|
||||
unattach() {
|
||||
this.parent = undefined
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Console
|
166
src/EmulatedIDB.js
Normal file
166
src/EmulatedIDB.js
Normal file
@ -0,0 +1,166 @@
|
||||
const localBasePath = '.config/xash/saves',
|
||||
localExt = '.jso'
|
||||
|
||||
const callIfPresent = (func, args, fallback = false) => {
|
||||
if(typeof func === 'function') {
|
||||
return func(...args)
|
||||
} else if(fallback) {
|
||||
return fallback(...args)
|
||||
}
|
||||
}
|
||||
|
||||
class EmulatedIDB {
|
||||
constructor() {
|
||||
const self = this
|
||||
|
||||
const keyOnlyWrapper = original => key =>
|
||||
original(this.getRemoteKey(key) )
|
||||
|
||||
const valueKeyWrapper = original => (item, key) =>
|
||||
original(item, this.getRemoteKey(key) )
|
||||
|
||||
this.objectStoreOverrides = {
|
||||
get: keyOnlyWrapper,
|
||||
delete: keyOnlyWrapper,
|
||||
put: valueKeyWrapper,
|
||||
add: valueKeyWrapper,
|
||||
|
||||
clear: original => () => {
|
||||
let out = {}
|
||||
|
||||
this.getLocalRemoteKeys
|
||||
.then(keys => Promise.all(
|
||||
keys.map(key => localforage.removeItem(key))
|
||||
))
|
||||
.catch(err => {
|
||||
callIfPresent(out.onerror, [ err ], err => {
|
||||
throw err
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
callIfPresent(out.onsuccess, [ {} ])
|
||||
})
|
||||
|
||||
return out
|
||||
},
|
||||
|
||||
index: () => targetEntryKey => {
|
||||
function openKeyCursor() {
|
||||
let out = {}
|
||||
|
||||
self.getLocalRemoteKeys().then(async remoteKeys => {
|
||||
let files = await Promise.all(
|
||||
remoteKeys
|
||||
.map(async remoteKey => {
|
||||
let obj = await localforage.getItem(remoteKey)
|
||||
|
||||
return {
|
||||
key: obj[targetEntryKey],
|
||||
primaryKey: self.getLocalKey(remoteKey)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
let index = 0
|
||||
|
||||
const nextItem = () => {
|
||||
callIfPresent(out.onsuccess, [
|
||||
{ target: { result: makeCursor() } }
|
||||
])
|
||||
}
|
||||
|
||||
const makeCursor = () => {
|
||||
if(index < files.length)
|
||||
return Object.assign(files[index], {
|
||||
continue() {
|
||||
index++
|
||||
nextItem()
|
||||
}
|
||||
})
|
||||
else
|
||||
return null
|
||||
}
|
||||
|
||||
nextItem()
|
||||
})
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
return { openKeyCursor }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createMethodProxy(target, funcs) {
|
||||
return new Proxy(target, {
|
||||
get(target, prop) {
|
||||
console.log('IDB Proxy accessing:', prop)
|
||||
|
||||
if(funcs[prop])
|
||||
return funcs[prop](target[prop].bind(target), target)
|
||||
else
|
||||
return target[prop]
|
||||
},
|
||||
|
||||
set(target, prop, value) {
|
||||
// Ensure sure handlers like onsuccess and such work properly
|
||||
target[prop] = typeof value == 'function' ? value.bind(target) : value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getRemoteKey(key, includeExt = true) {
|
||||
return localBasePath + (includeExt ? key + localExt : key)
|
||||
}
|
||||
|
||||
isKeyLocal(key) {
|
||||
return key.startsWith(localBasePath) && key.endsWith(localExt)
|
||||
}
|
||||
|
||||
getLocalKey(key) {
|
||||
return key.slice(localBasePath.length, -localExt.length)
|
||||
}
|
||||
|
||||
getLocalRemoteKeys() {
|
||||
return localforage.keys()
|
||||
.then(keys =>
|
||||
keys.filter(key => this.isKeyLocal(key))
|
||||
)
|
||||
}
|
||||
|
||||
patch(IDBFS) {
|
||||
const self = this
|
||||
|
||||
IDBFS.getDB = function getDB(name, callback) {
|
||||
const dbEmulator = self.createMethodProxy(top.localforage._dbInfo.db, {
|
||||
transaction: original => (name, ...args) => {
|
||||
let out = original(['a'], ...args)
|
||||
|
||||
if(Array.isArray(name) && name[0] == IDBFS.DB_STORE_NAME)
|
||||
out = self.createMethodProxy(out, {
|
||||
objectStore: original => () =>
|
||||
self.createMethodProxy(original('a'), self.objectStoreOverrides)
|
||||
})
|
||||
|
||||
// if(Array.isArray(name) )
|
||||
// out = self.createMethodProxy(out, {
|
||||
// objectStore: original => () =>
|
||||
// self.createMethodProxy(original('a'), self.objectStoreOverrides)
|
||||
// })
|
||||
|
||||
|
||||
return out
|
||||
}
|
||||
})
|
||||
|
||||
callback(null, dbEmulator)
|
||||
}
|
||||
}
|
||||
|
||||
sync() {
|
||||
$explorer.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EmulatedIDB
|
148
src/Instance.js
Executable file
148
src/Instance.js
Executable file
@ -0,0 +1,148 @@
|
||||
const Console = require('./Console')
|
||||
const EmulatedIDB = require('./EmulatedIDB')
|
||||
const ModPackage = require('./ModPackage')
|
||||
const handleIframe = require('./util/handleIframe'),
|
||||
// gzip = require('./util/gzip'),
|
||||
promisify = require('./util/promisify')
|
||||
|
||||
const openAsync = promisify($file.open),
|
||||
{ Buffer } = le._apps.abnt
|
||||
|
||||
class Instance {
|
||||
constructor(app, modPath) {
|
||||
let self = this,
|
||||
bundleDir = app.bundle.for('/import/')
|
||||
|
||||
let width = 640 + 7 - 9,
|
||||
height = 480 + 28 - 30
|
||||
|
||||
const options = {
|
||||
title: 'Xash3D',
|
||||
// url: "data:text/plain,",
|
||||
url: app.bundle.openSync('./main.html', 'URL'),
|
||||
// icon: app.icon,
|
||||
|
||||
// Windows93 adds to these to compensate for title height and such,
|
||||
// but we want it to match the canvas resolution.
|
||||
// We want 647 x 508 on the window element
|
||||
width,
|
||||
height,
|
||||
minWidth: width,
|
||||
minHeight: height,
|
||||
bodyClass: 'xash3d_main',
|
||||
menu: [
|
||||
{
|
||||
name: 'Game',
|
||||
items: [
|
||||
{
|
||||
name: 'Open console',
|
||||
action: function() {
|
||||
self.openConsole()
|
||||
self.focusConsole()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
onready() {
|
||||
const { iframe } = self.window.el
|
||||
|
||||
iframe.contentWindow.instance = self
|
||||
handleIframe(iframe, '/import/')
|
||||
},
|
||||
onclose() {
|
||||
self.consoleWindow?.close()
|
||||
self.closed = true
|
||||
|
||||
app.cleanInstances()
|
||||
}
|
||||
}
|
||||
|
||||
this.consoleWindow
|
||||
this.window = $window(options)
|
||||
this.closed = false
|
||||
this.app = app
|
||||
this.assets = new Map()
|
||||
this.import = new Proxy(this.assets, {
|
||||
get(target, prop) {
|
||||
console.log(`LOADING "${prop}"`)
|
||||
|
||||
if( !target.has(prop) && bundleDir.access(prop) ) {
|
||||
target.set(prop, bundleDir.openSync(prop, 'URL') )
|
||||
}
|
||||
|
||||
return target.get(prop)
|
||||
}
|
||||
})
|
||||
this.emulatedIDB = new EmulatedIDB()
|
||||
this.arguments = []
|
||||
|
||||
this.package = this.loadPackage(modPath)
|
||||
.then(mod => {
|
||||
this.window.changeTitle(mod.manifest.name)
|
||||
|
||||
return mod
|
||||
})
|
||||
.catch(console.error)
|
||||
|
||||
this.console = new Console(() => this.onQuit())
|
||||
this.onCloseConsole
|
||||
|
||||
console.log('INSTANCE:', this)
|
||||
}
|
||||
|
||||
async loadPackage(path) {
|
||||
let buffer = await openAsync(path, 'ArrayBuffer'),
|
||||
mod = await ModPackage.unpack(buffer)
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
openConsole() {
|
||||
let self = this
|
||||
|
||||
if(this.consoleWindow)
|
||||
// Put focus on the window instead
|
||||
this.focusConsole()
|
||||
|
||||
const options = {
|
||||
title: 'Xash Console',
|
||||
bodyClass: 'ui_terminal xash3d_terminal',
|
||||
onready() {
|
||||
self.console.attachTo(self.consoleWindow)
|
||||
self.focusConsole()
|
||||
},
|
||||
onclose() {
|
||||
self.console.unattach()
|
||||
self.onCloseConsole?.call()
|
||||
}
|
||||
}
|
||||
|
||||
this.consoleWindow = $window(options)
|
||||
}
|
||||
|
||||
focusConsole() {
|
||||
this.consoleWindow.el.header.click()
|
||||
}
|
||||
|
||||
// Triggered when the user presses "quit" on the main menu
|
||||
onQuit() {
|
||||
if(this.consoleWindow) {
|
||||
this.onCloseConsole = () => {
|
||||
// Prevent .kill from trying to close the console
|
||||
this.consoleWindow = null
|
||||
this.kill()
|
||||
}
|
||||
} else {
|
||||
this.kill()
|
||||
}
|
||||
}
|
||||
|
||||
kill() {
|
||||
this.window?.close()
|
||||
this.consoleWindow?.close()
|
||||
delete this.assets // pls my memory
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Instance
|
121
src/Launcher.js
Normal file
121
src/Launcher.js
Normal file
@ -0,0 +1,121 @@
|
||||
const ModPackage = require('./ModPackage')
|
||||
const constants = require('./util/constants.json'),
|
||||
promisify = require('./util/promisify')
|
||||
|
||||
const create = (name, ...children) => {
|
||||
let classList = name.split('.')
|
||||
ele = document.createElement(classList.shift() || 'div')
|
||||
|
||||
if(classList)
|
||||
ele.classList = classList.join(' ')
|
||||
|
||||
children.forEach(child =>
|
||||
typeof(child) == 'string' ? ele.innerText += child : ele.appendChild(child)
|
||||
)
|
||||
|
||||
return ele
|
||||
}
|
||||
|
||||
const openAsync = promisify($file.open)
|
||||
|
||||
class Launcher {
|
||||
constructor(app) {
|
||||
const self = this
|
||||
|
||||
const options = {
|
||||
title: 'Xash3D Launcher',
|
||||
// html: app.bundle.openSync('./launcher.html', 'String'),
|
||||
bodyClass: 'skin_inset xash3d_launcher',
|
||||
width: 350,
|
||||
height: 400,
|
||||
// onready() {
|
||||
// const { iframe } = launcher.el,
|
||||
// win = iframe.contentWindow
|
||||
|
||||
// win.app = self
|
||||
// handleIframe(iframe)
|
||||
// },
|
||||
// TODO: move to actual mods folder
|
||||
onready() {
|
||||
console.log(this)
|
||||
self.modList = this.el.body.appendChild(create('ul') )
|
||||
self.loadMods()
|
||||
},
|
||||
footer: `
|
||||
<span style="display: inline-block; color: #555; padding: 3px 1px;">
|
||||
v${app.version}
|
||||
</span>
|
||||
<span style="float: right">
|
||||
<button onclick="$exe('${constants.paths.modPath}')">Open Mods Folder</button>
|
||||
</span>
|
||||
`
|
||||
}
|
||||
|
||||
this.app = app
|
||||
this.window = $window(options)
|
||||
}
|
||||
|
||||
async loadMods() {
|
||||
let { modPath } = constants.paths,
|
||||
files = $io.obj.getPath(window.le._files, modPath, '/'),
|
||||
out = []
|
||||
|
||||
console.log(files)
|
||||
|
||||
if(files) {
|
||||
for(let name in files)
|
||||
if(files[name] == 0 && $fs.utils.getExt(name) == 'asar') {
|
||||
let path = modPath + name,
|
||||
promise = openAsync(path, 'ArrayBuffer')
|
||||
.then(async buffer => ({
|
||||
name,
|
||||
path,
|
||||
size: buffer.byteLength,
|
||||
manifest: await ModPackage.unpack(buffer, true)
|
||||
.then(data => JSON.parse(data.manifestString) )
|
||||
}))
|
||||
|
||||
out.push(promise)
|
||||
}
|
||||
}
|
||||
|
||||
out = await Promise.all(out)
|
||||
out
|
||||
.map(mod => this.renderMod(mod))
|
||||
.map(ele => this.modList.appendChild(ele) )
|
||||
}
|
||||
|
||||
renderMod(mod) {
|
||||
// const mod = document.createElement('div'),
|
||||
// header = document.createElement('header'),
|
||||
// info = document.createElement('div')
|
||||
// modName = document.createElement('h5'),
|
||||
// modInfo = document.createElement('span'),
|
||||
// launch = document.createElement('div')
|
||||
|
||||
// header.appendChild(modName)
|
||||
// header.appendChild(modInfo)
|
||||
// mod.appendChild('header')
|
||||
|
||||
let self = this,
|
||||
launch,
|
||||
element = create('.mod.skin_outset',
|
||||
create('header',
|
||||
create('.info',
|
||||
create('h5', mod.manifest.name),
|
||||
create('span', `${mod.name} | ${parseInt(mod.size / 1_000_000)}mb`)
|
||||
),
|
||||
launch = create('.launch')
|
||||
)
|
||||
)
|
||||
|
||||
launch.onclick = () => {
|
||||
self.app.launch(mod.path)
|
||||
self.window.destroy()
|
||||
}
|
||||
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Launcher
|
18
src/ManifestParser.js
Executable file
18
src/ManifestParser.js
Executable file
@ -0,0 +1,18 @@
|
||||
const Ajv = require('ajv'),
|
||||
constants = require('./util/constants')
|
||||
|
||||
const validate = new ajv().compile(constants.manifestSchema)
|
||||
|
||||
class ManifestParser {
|
||||
constructor(manifestString) {
|
||||
let data = JSON.parse(manifestString),
|
||||
valid = validate(data)
|
||||
|
||||
if(!valid)
|
||||
throw new Error('Uh oh, stinky!')
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ManifestParser
|
74
src/ModPackage.js
Executable file
74
src/ModPackage.js
Executable file
@ -0,0 +1,74 @@
|
||||
const { gzip } = require('./util/gzip')
|
||||
|
||||
// I don't like using a loader in this way but I
|
||||
// guess this is punishment for putting off
|
||||
// putting AsarHandler in it's own project
|
||||
// and finishing FakeBuffer
|
||||
const { AsarHandler: Asar, Buffer } = le._apps.abnt
|
||||
|
||||
function toArrayBuffer(buffer) {
|
||||
let ab = new ArrayBuffer(buffer.length),
|
||||
view = new Uint8Array(ab)
|
||||
|
||||
for (let i = 0; i < buffer.length; ++i) {
|
||||
view[i] = buffer[i]
|
||||
}
|
||||
|
||||
return ab
|
||||
}
|
||||
|
||||
const textDecoder = new TextDecoder('utf-8')
|
||||
|
||||
class ModPackage {
|
||||
static async unpack(buffer, direct = false) {
|
||||
let asar = new Asar(buffer),
|
||||
manifestString = textDecoder.decode( asar.get('manifest.json') ),
|
||||
files = [ ...asar.contents ]
|
||||
|
||||
const decompress = path => new Promise((resolve, reject) => {
|
||||
// gzip(asar.get(path), (error, data) => {
|
||||
// console.log('GZIP: ', path, data)
|
||||
|
||||
// if(error)
|
||||
// reject(error)
|
||||
// else
|
||||
// resolve([ path, toArrayBuffer(data) ])
|
||||
// })
|
||||
|
||||
resolve([ path, asar.get(path) ])
|
||||
})
|
||||
|
||||
files.splice(files.indexOf('manifest.json'), 1)
|
||||
files = files.map(decompress)
|
||||
|
||||
files = await Promise.all(files)
|
||||
return direct ? { files, manifestString } : new this(new Map(files), manifestString)
|
||||
}
|
||||
|
||||
constructor(files, manifestString) {
|
||||
this.manifest = JSON.parse(manifestString)
|
||||
this.files = files
|
||||
this.cache = new Map()
|
||||
}
|
||||
|
||||
get(path) {
|
||||
if(this.cache.has(path))
|
||||
return this.cache.get(path)
|
||||
|
||||
let data = this.files.get(path),
|
||||
ext = $fs.utils.getExt(path),
|
||||
file = new Blob([ data ], { type: le._get.ext.mime[ext] })
|
||||
|
||||
this.cache.set(path, file)
|
||||
return file
|
||||
}
|
||||
|
||||
getURL(path) {
|
||||
let file = this.get(path),
|
||||
url = URL.createObjectURL(file)
|
||||
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ModPackage
|
24
src/emf/Opener.js
Normal file
24
src/emf/Opener.js
Normal file
@ -0,0 +1,24 @@
|
||||
const { emf } = require('../util/constants.json'),
|
||||
merge = require('../../util/merge')
|
||||
|
||||
class Opener {
|
||||
constructor() {
|
||||
this.name = emf.id
|
||||
this.silent = true
|
||||
this.categories = emf.categories
|
||||
this.accept = emf.ext
|
||||
|
||||
this.exec = (url, context) => {
|
||||
console.log(url, context)
|
||||
}
|
||||
}
|
||||
|
||||
patchSystem() {
|
||||
// Import to prevent race conditions
|
||||
const { _get } = window.le
|
||||
|
||||
window.le._get = merge(_get, emf.filetypePatch)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Opener
|
152
src/hidden_Instance.js
Executable file
152
src/hidden_Instance.js
Executable file
@ -0,0 +1,152 @@
|
||||
const Console = require('./Console')
|
||||
const ModPackage = require('./ModPackage')
|
||||
const handleIframe = require('./util/handleIframe'),
|
||||
gzip = require('./util/gzip'),
|
||||
promisify = require('./util/promisify')
|
||||
|
||||
const openAsync = promisify($file.open),
|
||||
{ Buffer } = le._apps.abnt
|
||||
|
||||
class Instance {
|
||||
constructor(app, modPath) {
|
||||
let self = this,
|
||||
bundleDir = app.bundle.for('/import/')
|
||||
|
||||
this.consoleWindow
|
||||
this.window
|
||||
this.mod
|
||||
this.closed = false
|
||||
this.app = app
|
||||
this.assets = new Map()
|
||||
this.import = new Proxy(this.assets, {
|
||||
get(target, prop) {
|
||||
console.log(`LOADING "${prop}"`)
|
||||
|
||||
if( !target.has(prop) && bundleDir.access(prop) ) {
|
||||
target.set(prop, bundleDir.openSync(prop, 'URL') )
|
||||
}
|
||||
|
||||
return target.get(prop)
|
||||
}
|
||||
})
|
||||
this.arguments = []
|
||||
this.console = new Console()
|
||||
|
||||
console.log('INSTANCE:', this)
|
||||
|
||||
// Post init
|
||||
|
||||
this.package = this.loadPackage(modPath)
|
||||
.then(mod => {
|
||||
this.mod = mod
|
||||
console.log('woo window,', this.window)
|
||||
|
||||
return mod
|
||||
})
|
||||
|
||||
this.package.catch(console.error)
|
||||
|
||||
this.openMain()
|
||||
}
|
||||
|
||||
async loadPackage(path) {
|
||||
const buffer = await openAsync(path, 'ArrayBuffer').then(ab => Buffer.from(ab)),
|
||||
mod = await ModPackage.unpack(buffer)
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
openMain() {
|
||||
// We'll need to shove it in a fragment to force it to load while hidden
|
||||
const iframe = document.createElement('iframe'),
|
||||
self = this
|
||||
|
||||
const options = {
|
||||
title: 'Xash3D',
|
||||
// url: "data:text/plain,",
|
||||
// icon: app.icon,
|
||||
// Windows93 adds to these to compensate for title height and such,
|
||||
// but we want it to match the canvas resolution.
|
||||
// We want 647 x 508 on the window element
|
||||
width: 640 + 7 - 9,
|
||||
height: 480 + 28 - 30,
|
||||
menu: [
|
||||
{
|
||||
name: 'Game',
|
||||
items: [
|
||||
{
|
||||
name: 'Open console',
|
||||
action: function() {
|
||||
self.openConsole()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
onready() {
|
||||
console.log('this:', this)
|
||||
this.el.body.appendChild(iframe)
|
||||
},
|
||||
onclose() {
|
||||
self.consoleWindow?.close()
|
||||
self.closed = true
|
||||
|
||||
app.cleanInstances()
|
||||
}
|
||||
}
|
||||
|
||||
iframe.style.display = 'none'
|
||||
iframe.src = this.app.bundle.openSync('./main.html', 'URL')
|
||||
|
||||
iframe.onload = () => {
|
||||
iframe.contentWindow.instance = self
|
||||
iframe.contentWindow.onmessage = event => {
|
||||
console.log(event)
|
||||
|
||||
if(event.data == 'loadingDone')
|
||||
self.window = $window(options)
|
||||
iframe.style.display = 'initial'
|
||||
}
|
||||
|
||||
handleIframe(iframe, '/import/')
|
||||
console.log('loadd')
|
||||
}
|
||||
|
||||
document.body.append(iframe)
|
||||
console.log(iframe)
|
||||
}
|
||||
|
||||
openConsole() {
|
||||
const self = this
|
||||
|
||||
if(this.consoleWindow)
|
||||
// Put focus on the window instead
|
||||
this.focusConsole()
|
||||
|
||||
const options = {
|
||||
title: 'Xash Console',
|
||||
bodyClass: 'ui_terminal xash3d_terminal',
|
||||
onready() {
|
||||
self.console.attachTo(self.consoleWindow)
|
||||
self.focusConsole()
|
||||
},
|
||||
onclose() {
|
||||
self.console.unattach()
|
||||
}
|
||||
}
|
||||
|
||||
this.consoleWindow = $window(options)
|
||||
}
|
||||
|
||||
focusConsole() {
|
||||
this.consoleWindow.el.header.click()
|
||||
}
|
||||
|
||||
kill() {
|
||||
this.window?.close()
|
||||
this.consoleWindow?.close()
|
||||
delete this.assets // pls my memory
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Instance
|
7
src/index.js
Executable file
7
src/index.js
Executable file
@ -0,0 +1,7 @@
|
||||
// https://icrazyblaze.github.io/Xash3D-Emscripten/xash.html
|
||||
// https://github.com/icrazyblaze/Xash3D-Emscripten
|
||||
|
||||
const App = require('./App'),
|
||||
{ app } = require('./util/constants')
|
||||
|
||||
le._apps[app.id] = new App()
|
31
src/util/Loader.js
Executable file
31
src/util/Loader.js
Executable file
@ -0,0 +1,31 @@
|
||||
// $loader alternative that can be bind to other scopes
|
||||
|
||||
class Loader {
|
||||
constructor(document = window.document) {
|
||||
this.document = document
|
||||
}
|
||||
|
||||
createElement(tag, resource, rel) {
|
||||
const executor = (resolve, reject) => {
|
||||
const { document } = this,
|
||||
element = document.createElement(tag)
|
||||
|
||||
element[tag === 'script' ? 'src' : 'href'] = resource
|
||||
element.rel = rel
|
||||
element.onload = () => resolve(element)
|
||||
document.head.appendChild(element)
|
||||
}
|
||||
|
||||
return new Promise(executor)
|
||||
}
|
||||
|
||||
script(src) {
|
||||
return this.createElement('script', src)
|
||||
}
|
||||
|
||||
css(href) {
|
||||
return this.createElement('link', href, 'stylesheet')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Loader
|
58
src/util/_handleIframe_preprocessor.js
Normal file
58
src/util/_handleIframe_preprocessor.js
Normal file
@ -0,0 +1,58 @@
|
||||
const Loader = require('./Loader')
|
||||
|
||||
const wrapProcessor = (preprocessor, path, handle) => async data => {
|
||||
let ext = $fs.utils.getExt(path),
|
||||
processed = await preprocessor(data, path).catch(console.error),
|
||||
blob = new Blob([ processed ], { type: le._get.ext.mime[ext] })
|
||||
|
||||
handle( URL.createObjectURL(blob) )
|
||||
}
|
||||
|
||||
const handleIframe = async (iframe, path = '/') => {
|
||||
const convert = (tag, from, to, preprocessor) => {
|
||||
let elements = iframe.contentDocument.querySelectorAll(tag)
|
||||
|
||||
for(let element of elements) {
|
||||
let original = element.getAttribute(from)
|
||||
|
||||
if(!original)
|
||||
continue
|
||||
|
||||
let handle = url => {
|
||||
element[to] = url
|
||||
}
|
||||
|
||||
$bundle.for(path).open(original, preprocessor ? 'String' : 'URL')
|
||||
.then(preprocessor ? wrapProcessor(preprocessor, path, handle) : handle)
|
||||
}
|
||||
}
|
||||
|
||||
convert('script', 'lsrc', 'src')
|
||||
convert('img', 'lsrc', 'src')
|
||||
convert('link', 'lhref', 'href', async (stylesheet, path) => {
|
||||
let matches = stylesheet.matchAll(/url\(("(.+?)"|'(.+?)'|(.+?))\)/g),
|
||||
fromIndex = 0,
|
||||
out = []
|
||||
|
||||
console.log(matches)
|
||||
|
||||
for(let match of matches) {
|
||||
let bundle = $bundle.for( $fs.utils.getFolderPath(path) )
|
||||
|
||||
console.log(bundle.access(match[2]))
|
||||
|
||||
out.concat([
|
||||
stylesheet.slice(fromIndex, match.index),
|
||||
'url("' + await bundle.open(match[2], 'URL') + '")'
|
||||
])
|
||||
|
||||
fromIndex = match.index + match[0].length
|
||||
}
|
||||
|
||||
console.log(out, fromIndex)
|
||||
|
||||
return out.join('')
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = handleIframe
|
71
src/util/constants.json
Executable file
71
src/util/constants.json
Executable file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"app": {
|
||||
"id": "xash",
|
||||
"categories": "Game",
|
||||
"version": "0.0.0b"
|
||||
},
|
||||
"emf": {
|
||||
"id": "emf",
|
||||
"ext": ".emf",
|
||||
"categories": "Utility"
|
||||
},
|
||||
"paths": {
|
||||
"modPath": "/a/.config/xash/mods/",
|
||||
"saves": "/a/.config/xash/saves/"
|
||||
},
|
||||
"manifestScheme": {
|
||||
"definitions": {},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://example.com/object1619767958.json",
|
||||
"title": "Root",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"entry"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$id": "#root/name",
|
||||
"title": "Name",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"Half-life Deathmatch"
|
||||
],
|
||||
"pattern": "^.*$"
|
||||
},
|
||||
"description": {
|
||||
"$id": "#root/description",
|
||||
"title": "Description",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"hurrr durr big p"
|
||||
],
|
||||
"pattern": "^.*$"
|
||||
},
|
||||
"entry": {
|
||||
"$id": "#root/entry",
|
||||
"title": "Entry",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"./hldm.js"
|
||||
],
|
||||
"pattern": "^.*$"
|
||||
},
|
||||
"compressed": {
|
||||
"$id": "#root/compressed",
|
||||
"title": "Compressed",
|
||||
"type": "boolean",
|
||||
"examples": [
|
||||
true
|
||||
],
|
||||
"default": true
|
||||
},
|
||||
"readme": {
|
||||
"$id": "#root/readme",
|
||||
"title": "Readme",
|
||||
"type": "string",
|
||||
"pattern": "^.*$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
src/util/gzip.js
Executable file
8
src/util/gzip.js
Executable file
@ -0,0 +1,8 @@
|
||||
// Require will import EVERYTHING no matter what I do,
|
||||
// so I'm making a fool of myself with this stupid
|
||||
// util script. Should've listened to robbie and
|
||||
// gotten used to ES6 syntax but noooo I had to be
|
||||
// a special snowflake with my stupid CJS garbage.
|
||||
import { gzip } from "fflate"
|
||||
|
||||
export { gzip }
|
24
src/util/handleIframe.js
Executable file
24
src/util/handleIframe.js
Executable file
@ -0,0 +1,24 @@
|
||||
const Loader = require('./Loader')
|
||||
|
||||
const handleIframe = async (iframe, path = '/') => {
|
||||
const convert = (tag, from, to) => {
|
||||
let elements = iframe.contentDocument.querySelectorAll(tag)
|
||||
|
||||
for(let element of elements) {
|
||||
let original = element.getAttribute(from)
|
||||
|
||||
if(!original)
|
||||
continue
|
||||
|
||||
$bundle.for(path).open(original, 'URL').then(url => {
|
||||
element[to] = url
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
convert('script', 'lsrc', 'src')
|
||||
convert('img', 'lsrc', 'src')
|
||||
convert('link', 'lhref', 'href')
|
||||
}
|
||||
|
||||
module.exports = handleIframe
|
17
src/util/promisify.js
Executable file
17
src/util/promisify.js
Executable file
@ -0,0 +1,17 @@
|
||||
const promisify = original => {
|
||||
const async = (...args) => {
|
||||
const executor = (resolve, reject) => {
|
||||
try {
|
||||
original(...args, resolve)
|
||||
} catch(error) {
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise(executor)
|
||||
}
|
||||
|
||||
return async
|
||||
}
|
||||
|
||||
module.exports = promisify
|
1
subrepo/Xash3D-Emscripten
Submodule
1
subrepo/Xash3D-Emscripten
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 0c2f84f9ae9855a7d4dc8692e8a11536fac6e1c0
|
BIN
test/hldm.asar
Executable file
BIN
test/hldm.asar
Executable file
Binary file not shown.
0
test/import.sh
Normal file
0
test/import.sh
Normal file
24
util/merge.js
Executable file
24
util/merge.js
Executable file
@ -0,0 +1,24 @@
|
||||
const merge = function self (...objects) {
|
||||
let [ left, right ] = objects
|
||||
|
||||
right = { ...right } // Prevent overwriting source
|
||||
|
||||
for(const key in right) {
|
||||
const source = right[key],
|
||||
target = left[key]
|
||||
|
||||
if(target && source instanceof Object)
|
||||
right[key] = self(target, source)
|
||||
}
|
||||
|
||||
const output = Object.assign(left, right)
|
||||
|
||||
if(objects.length > 2) {
|
||||
return self(output, ...objects.slice(2))
|
||||
} else {
|
||||
objects[0] = output
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = merge
|
63
view/_main.html
Executable file
63
view/_main.html
Executable file
@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="MobileOptimized" content="640"/>
|
||||
<meta name="HandheldFriendly" content="true"/>
|
||||
<title>Xash3D Emscripten Port</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: arial;
|
||||
margin: 0;
|
||||
padding: none;
|
||||
background-color: #555555;
|
||||
color: #f0b418;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onerror=alert(event);>
|
||||
<div class="emscripten_border">
|
||||
<canvas style="display:none" class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
||||
</div>
|
||||
|
||||
<div style="float:left;" id="status">Downloading...</div>
|
||||
|
||||
<div style=width:100%><span id='controls'>
|
||||
<a class="glow"><span><input type="checkbox" id="resize">Resize canvas</span></a>
|
||||
<a class="glow"><span><input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer </span></a>
|
||||
<span><input type="button" value="Fullscreen" onclick="Module.requestFullscreen(document.getElementById('pointerLock').checked, document.getElementById('resize').checked)"></span>
|
||||
</span></div>
|
||||
|
||||
<!-- <textarea id="output" rows="8"></textarea><div id="asyncDialog" style="float:left"></div> -->
|
||||
|
||||
<script type='text/javascript' lsrc='../init.js'></script>
|
||||
|
||||
<script>
|
||||
// Causes the app to freeze. Maybe make async?
|
||||
function loadingDone() {
|
||||
(function() {
|
||||
// var memoryInitializer = 'xash.html.mem';
|
||||
var memoryInitializer = 'xash.html.mem' // not gonna cuz locateFile will handle it
|
||||
if (typeof Module['locateFile'] === 'function') {
|
||||
memoryInitializer = Module['locateFile'](memoryInitializer);
|
||||
} else if (Module['memoryInitializerPrefixURL']) {
|
||||
memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer;
|
||||
}
|
||||
var xhr = Module['memoryInitializerRequest'] = new XMLHttpRequest();
|
||||
xhr.open('GET', memoryInitializer, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.send(null);
|
||||
})();
|
||||
|
||||
var script = document.createElement('script');
|
||||
// script.src = "xash.js";
|
||||
script.src = instance.import['xash.js'] // temp
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
1
view/assets/play.svg
Normal file
1
view/assets/play.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10" shape-rendering="crispEdges"><rect x="3" y="0" width="1" height="1" fill="#000000"/><rect x="4" y="0" width="1" height="1" fill="#000000"/><rect x="3" y="1" width="1" height="1" fill="#000000"/><rect x="4" y="1" width="1" height="1" fill="#000000"/><rect x="5" y="1" width="1" height="1" fill="#000000"/><rect x="3" y="2" width="1" height="1" fill="#000000"/><rect x="4" y="2" width="1" height="1" fill="#000000"/><rect x="5" y="2" width="1" height="1" fill="#000000"/><rect x="6" y="2" width="1" height="1" fill="#000000"/><rect x="3" y="3" width="1" height="1" fill="#000000"/><rect x="4" y="3" width="1" height="1" fill="#000000"/><rect x="5" y="3" width="1" height="1" fill="#000000"/><rect x="6" y="3" width="1" height="1" fill="#000000"/><rect x="7" y="3" width="1" height="1" fill="#000000"/><rect x="3" y="4" width="1" height="1" fill="#000000"/><rect x="4" y="4" width="1" height="1" fill="#000000"/><rect x="5" y="4" width="1" height="1" fill="#000000"/><rect x="6" y="4" width="1" height="1" fill="#000000"/><rect x="7" y="4" width="1" height="1" fill="#000000"/><rect x="8" y="4" width="1" height="1" fill="#000000"/><rect x="3" y="5" width="1" height="1" fill="#000000"/><rect x="4" y="5" width="1" height="1" fill="#000000"/><rect x="5" y="5" width="1" height="1" fill="#000000"/><rect x="6" y="5" width="1" height="1" fill="#000000"/><rect x="7" y="5" width="1" height="1" fill="#000000"/><rect x="8" y="5" width="1" height="1" fill="#000000"/><rect x="3" y="6" width="1" height="1" fill="#000000"/><rect x="4" y="6" width="1" height="1" fill="#000000"/><rect x="5" y="6" width="1" height="1" fill="#000000"/><rect x="6" y="6" width="1" height="1" fill="#000000"/><rect x="7" y="6" width="1" height="1" fill="#000000"/><rect x="3" y="7" width="1" height="1" fill="#000000"/><rect x="4" y="7" width="1" height="1" fill="#000000"/><rect x="5" y="7" width="1" height="1" fill="#000000"/><rect x="6" y="7" width="1" height="1" fill="#000000"/><rect x="3" y="8" width="1" height="1" fill="#000000"/><rect x="4" y="8" width="1" height="1" fill="#000000"/><rect x="5" y="8" width="1" height="1" fill="#000000"/><rect x="3" y="9" width="1" height="1" fill="#000000"/><rect x="4" y="9" width="1" height="1" fill="#000000"/></svg>
|
After Width: | Height: | Size: 2.3 KiB |
3
view/bundle.config.json
Executable file
3
view/bundle.config.json
Executable file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"entry": "/dist/xash.js"
|
||||
}
|
208
view/dist/xash.js
vendored
Executable file
208
view/dist/xash.js
vendored
Executable file
File diff suppressed because one or more lines are too long
75
view/global.css
Normal file
75
view/global.css
Normal file
@ -0,0 +1,75 @@
|
||||
|
||||
|
||||
.xash3d_terminal code {
|
||||
display: block;
|
||||
width: 60ch;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.xash3d_launcher {
|
||||
--mod-height: 36px;
|
||||
|
||||
background: #333 var(--xash3d-trame);
|
||||
background-size: 4px;
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
.xash3d_launcher > ul {
|
||||
margin: 4px 2px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.xash3d_launcher .mod {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.xash3d_launcher .mod header {
|
||||
display: flex;
|
||||
padding: 1px;
|
||||
box-sizing: content-box;
|
||||
height: var(--mod-height)
|
||||
}
|
||||
|
||||
.xash3d_launcher .info {
|
||||
border-right: 1px solid #666;
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
margin: 2px 0 2px 5px;
|
||||
}
|
||||
|
||||
.xash3d_launcher .info h5 {
|
||||
font-size: 8px;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.xash3d_launcher .info span {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.xash3d_launcher .launch {
|
||||
width: var(--mod-height);
|
||||
height: var(--mod-height);
|
||||
box-sizing: content-box;
|
||||
background-image: var(--xash3d-play);
|
||||
background-size: 20px 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.xash3d_launcher .launch:active {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
span.ui_menu__item__ico > img[src="/c/sys/skins/w93/xash"],
|
||||
div.ui_icon__file img[src="/c/sys/skins/w93/xash"] {
|
||||
display: block;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background-image: var(--xash3d-icon);
|
||||
background-repeat: no-repeat;
|
||||
/* It's a little blurry and should really just be pixel art but whatever for now */
|
||||
background-size: 100%;
|
||||
}
|
BIN
view/half-trame.png
Normal file
BIN
view/half-trame.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 B |
394
view/init.js
Executable file
394
view/init.js
Executable file
@ -0,0 +1,394 @@
|
||||
// Heavily-modified version of the xash.html set-up script
|
||||
// from Xash3d-Emscripten
|
||||
|
||||
var statusElement = document.getElementById('status');
|
||||
var progressElement = document.getElementById('progress');
|
||||
var asyncDialog = document.getElementById('asyncDialog');
|
||||
var myerrorbuf = ''
|
||||
var myerrordate = new Date();
|
||||
var mounted = false;
|
||||
var gamedir = 'valve';
|
||||
var moduleCount = 0;
|
||||
//var mem = 150;
|
||||
var mfs;
|
||||
var zipSize;
|
||||
var modPackage;
|
||||
|
||||
// // make BrowserFS to work on ES5 browsers
|
||||
// if (!ArrayBuffer['isView']) {
|
||||
// ArrayBuffer.isView = function(a) {
|
||||
// return a !== null && typeof(a) === "object" && a['buffer'] instanceof ArrayBuffer;
|
||||
// };
|
||||
|
||||
// }
|
||||
|
||||
// showElement('optionsTitle', false);
|
||||
|
||||
// function prepareSelects()
|
||||
// {
|
||||
// var len = zipMods.length;
|
||||
// var select = document.getElementById('selectZip');
|
||||
// if( len )
|
||||
// {
|
||||
// showElement('zipHider', true);
|
||||
|
||||
// if(len > 1)
|
||||
// {
|
||||
// var links = '';
|
||||
// for(var i = 0; i < len; i++)
|
||||
// {
|
||||
// select.options[i] = new Option(zipMods[i][1], zipMods[i][0]);
|
||||
// links += '<br><a href="'+zipMods[i][0]+'">'+zipMods[i][1]+'</a>';
|
||||
// }
|
||||
// select.style.display = 'block';
|
||||
// document.getElementById('linksPlaceholder').innerHTML += links;
|
||||
// showElement('linksPlaceholder', true);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// document.getElementById('rZip').checked = false;
|
||||
// len = pkgMods.length;
|
||||
// select = document.getElementById('selectPkg');
|
||||
// if( len )
|
||||
// {
|
||||
// showElement('pkgHider', true);
|
||||
|
||||
// if(len > 1)
|
||||
// {
|
||||
// for(var i = 0; i < len; i++)
|
||||
// select.options[i] = new Option(pkgMods[i][1], pkgMods[i][0]);
|
||||
// select.style.display = 'block';
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// document.getElementById('rPackage').checked = false;
|
||||
|
||||
// if( !zipMods.length && !len )
|
||||
// {
|
||||
// document.getElementById('rLocalZip').checked = true;
|
||||
// showElement('rLocalZip', false);
|
||||
// }
|
||||
// }
|
||||
|
||||
try{mem = Math.round(window.location.hash.substring(1));}catch(e){};
|
||||
|
||||
function wrapPrint(func) {
|
||||
return (...args) => {
|
||||
if(args.length > 0)
|
||||
instance.console[func](...args)
|
||||
}
|
||||
}
|
||||
|
||||
var Module = {
|
||||
TOTAL_MEMORY: mem * 1024 * 1024,
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
// print: (function() {
|
||||
// var element = document.getElementById('output');
|
||||
// if (element) element.value = ''; // clear browser cache
|
||||
// return function(text) {
|
||||
// if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
// // These replacements are necessary if you render to raw HTML
|
||||
// //text = text.replace(/&/g, "&");
|
||||
// //text = text.replace(/</g, "<");
|
||||
// //text = text.replace(/>/g, ">");
|
||||
// //text = text.replace('\n', '<br>', 'g');
|
||||
// //console.log(text);
|
||||
// if(text)
|
||||
// myerrorbuf += text + '\n';
|
||||
// if (element) {
|
||||
// if(element.value.length > 65536)
|
||||
// element.value = element.value.substring(512) + myerrorbuf;
|
||||
// else
|
||||
// element.value += myerrorbuf;
|
||||
// element.scrollTop = element.scrollHeight; // focus on bottom
|
||||
// }
|
||||
// myerrorbuf = ''
|
||||
// };
|
||||
// })(),
|
||||
print: wrapPrint('log'),
|
||||
// printErr: function(text) {
|
||||
// if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
// if (0) { // XXX disabled for safety typeof dump == 'function') {
|
||||
// dump(text + '\n'); // fast, straight to the real console
|
||||
// } else {
|
||||
// if( myerrorbuf.length > 2048 )
|
||||
// myerrorbuf = 'some lines skipped\n'+ myerrorbuf.substring(512);
|
||||
// myerrorbuf += text + '\n';
|
||||
// if( new Date() - myerrordate > 3000 )
|
||||
// {
|
||||
// myerrordate = new Date();
|
||||
// Module.print();
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
printErr: wrapPrint('error'),
|
||||
canvas: (function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
|
||||
// application robust, you may want to override this behavior before shipping!
|
||||
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
|
||||
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
|
||||
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: function(text) {
|
||||
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
|
||||
if (text === Module.setStatus.text) return;
|
||||
if( new Date() - myerrordate > 3000 )
|
||||
{
|
||||
myerrordate = new Date();
|
||||
Module.print();
|
||||
}
|
||||
|
||||
statusElement.innerHTML = text;
|
||||
if( progressElement )
|
||||
{
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
|
||||
if(m)
|
||||
{
|
||||
var progress = Math.round(parseInt(m[2])*100/parseInt(m[4]));
|
||||
progressElement.style.color = progress > 5?'#303030':'#aaa000';
|
||||
progressElement.style.width = progressElement.innerHTML = ''+progress+'%';
|
||||
}
|
||||
showElement('progress1', !!m);
|
||||
}
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function(left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
if(left)
|
||||
Module.setStatus('Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')');
|
||||
},
|
||||
// Added:
|
||||
// This is needed for it to load the package's data from a URI
|
||||
// https://emscripten.org/docs/porting/files/packaging_files.html#changing-the-data-file-location
|
||||
locateFile(dataName) {
|
||||
return instance.import[dataName] || modPackage.getURL(dataName)
|
||||
}
|
||||
//
|
||||
};
|
||||
|
||||
function syncFS() {
|
||||
FS.syncfs(false, function(err) {
|
||||
instance.emulatedIDB.sync()
|
||||
Module.print('Saving IDBFS' + err ? `: ${err}` : '')
|
||||
})
|
||||
}
|
||||
|
||||
window.onerror = function(event) {
|
||||
if(mounted) {
|
||||
// console.error('Errored: ', event)
|
||||
syncFS()
|
||||
} if( (''+event).indexOf('SimulateInfiniteLoop') > 0 )
|
||||
return;
|
||||
var text = 'Exception thrown: ' + event;
|
||||
text = text.replace(/&/g, "&");
|
||||
text = text.replace(/</g, "<");
|
||||
text = text.replace(/>/g, ">");
|
||||
text = text.replace('\n', '<br>', 'g');
|
||||
Module.setStatus(text);
|
||||
Module.print('Exception thrown: ' + event);
|
||||
};
|
||||
|
||||
function haltRun()
|
||||
{
|
||||
}
|
||||
|
||||
var savedRun;
|
||||
|
||||
function radioChecked(id)
|
||||
{
|
||||
var r = document.getElementById('r'+id);
|
||||
if(r) return r.checked;
|
||||
return false;
|
||||
}
|
||||
|
||||
function showElement(id, show)
|
||||
{
|
||||
var e = document.getElementById(id);
|
||||
if(!e) return;
|
||||
e.style.display=show?'block':'none';
|
||||
}
|
||||
|
||||
Module.setStatus('Downloading...');
|
||||
|
||||
function startXash()
|
||||
{
|
||||
// showElement('loader1', false);
|
||||
// showElement('optionsTitle', false);
|
||||
// showElement('fSettings', false);
|
||||
setupFS();
|
||||
Module.arguments = instance.arguments;
|
||||
Module.run = run = savedRun;
|
||||
// if( radioChecked('Zip') )
|
||||
// fetchZIP(zipMods.length>1?document.getElementById('selectZip').value:zipMods[0][0], savedRun);
|
||||
// else if( (!zipMods.length && !pkgMods.length) || radioChecked('LocalZip') )
|
||||
// {
|
||||
// var reader = new FileReader();
|
||||
// reader.onload = function(){
|
||||
// mountZIP(reader.result);
|
||||
// Module.print("Loaded zip data");
|
||||
// savedRun();
|
||||
// };
|
||||
// reader.readAsArrayBuffer(document.getElementById('iZipFile').files[0]);
|
||||
// }
|
||||
// else if( radioChecked('Package') )
|
||||
// {
|
||||
var script = document.createElement('script');
|
||||
script.onload = savedRun;
|
||||
document.body.appendChild(script);
|
||||
// script.src = pkgMods.length>1?document.getElementById('selectPkg').value:pkgMods[0][0];
|
||||
// }
|
||||
|
||||
instance.package.then(data => {
|
||||
console.log('Loading package...')
|
||||
modPackage = data
|
||||
script.src = modPackage.getURL(modPackage.manifest.entry)
|
||||
console.log('PAYLOAD: ', script.src)
|
||||
})
|
||||
|
||||
showElement('canvas', true);
|
||||
|
||||
window.addEventListener("beforeunload", function (e) {
|
||||
var confirmationMessage = 'Leave the game?';
|
||||
|
||||
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
|
||||
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
|
||||
});
|
||||
|
||||
// Added
|
||||
document.addEventListener("onkeydown", function (e) {
|
||||
e = e || window.event;//Get event
|
||||
|
||||
if (!e.ctrlKey) return;
|
||||
|
||||
var code = e.which || e.keyCode;//Get key code
|
||||
|
||||
switch (code) {
|
||||
case 83://Block Ctrl+S
|
||||
case 87://Block Ctrl+W -- Not work in Chrome and new Firefox
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
if(instance.app.config.pauseOnLostFocus)
|
||||
document.addEventListener('mouseleave', function(event) {
|
||||
console.log('aaa')
|
||||
canvas.dispatchEvent(new KeyboardEvent({ key: 'Esc' }))
|
||||
})
|
||||
}
|
||||
|
||||
// function mountZIP(data)
|
||||
// {
|
||||
// var Buffer = BrowserFS.BFSRequire('buffer').Buffer;
|
||||
// mfs.mount('/zip', new BrowserFS.FileSystem.ZipFS(Buffer.from(data)));
|
||||
// FS.mount(new BrowserFS.EmscriptenFS(), {root:'/zip'}, '/rodir');
|
||||
// }
|
||||
|
||||
// function fetchZIP(packageName, cb)
|
||||
// {
|
||||
// var xhr = new XMLHttpRequest();
|
||||
// xhr.open('GET', packageName, true);
|
||||
// xhr.responseType = 'arraybuffer';
|
||||
|
||||
// xhr.onprogress = function(event) {
|
||||
// var url = packageName;
|
||||
// var size;
|
||||
// if (event.total) size = event.total;
|
||||
// else size = zipMods[document.getElementById('selectZip').selectedIndex][2];
|
||||
// if (event.loaded) {
|
||||
// var total = size;
|
||||
// var loaded = event.loaded;
|
||||
// var num = 0;
|
||||
// if (Module['setStatus']) Module['setStatus']('Downloading data... (' + loaded + '/' + total + ')');
|
||||
// } else if (!Module.dataFileDownloads) {
|
||||
// if (Module['setStatus']) Module['setStatus']('Downloading data...');
|
||||
// }
|
||||
// };
|
||||
// xhr.onerror = function(event) {
|
||||
// throw new Error("NetworkError");
|
||||
// }
|
||||
// xhr.onload = function(event) {
|
||||
// if (xhr.status == 200 || xhr.status == 304 || xhr.status == 206 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
|
||||
// mountZIP(xhr.response);
|
||||
// cb();
|
||||
// } else {
|
||||
// throw new Error(xhr.statusText + " : " + xhr.responseURL);
|
||||
// }
|
||||
// };
|
||||
// xhr.send(null);
|
||||
// }
|
||||
|
||||
let localForageOperations = []
|
||||
|
||||
function setupFS()
|
||||
{
|
||||
// TODO: Determined by the new "mod id"
|
||||
let fsPath = '/' + modPackage.manifest.id
|
||||
|
||||
FS.mkdir('/rodir');
|
||||
FS.mkdir(fsPath);
|
||||
|
||||
instance.emulatedIDB.patch(IDBFS)
|
||||
|
||||
FS.mount(IDBFS, {}, fsPath);
|
||||
FS.syncfs(true, function(err) {
|
||||
if(err)
|
||||
Module.print('Loading IDBFS: ' + err)
|
||||
})
|
||||
mounted = true;
|
||||
|
||||
FS.chdir(fsPath);
|
||||
}
|
||||
|
||||
function skipRun()
|
||||
{
|
||||
savedRun = run;
|
||||
Module.run = haltRun;
|
||||
run = haltRun;
|
||||
|
||||
Module.setStatus("Engine downloaded!");
|
||||
// showElement('loader1', false);
|
||||
// showElement('optionsTitle', true);
|
||||
|
||||
// if(window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB)
|
||||
// showElement('idbHider', true);
|
||||
// prepareSelects();
|
||||
// showElement('fSettings',true);
|
||||
|
||||
ENV.XASH3D_GAMEDIR = gamedir;
|
||||
ENV.XASH3D_RODIR = '/rodir'
|
||||
|
||||
function loadModule(name)
|
||||
{
|
||||
var script = document.createElement('script');
|
||||
script.onload = function() {
|
||||
moduleCount++
|
||||
|
||||
if(moduleCount == 3) {
|
||||
Module.setStatus("Scripts downloaded!")
|
||||
// We'll now wanna start xash right away
|
||||
startXash()
|
||||
}
|
||||
}
|
||||
document.body.appendChild(script);
|
||||
// script.src = name + ".js";
|
||||
script.src = instance.import[name + '.js']
|
||||
}
|
||||
|
||||
loadModule("server");
|
||||
loadModule("client");
|
||||
loadModule("menu");
|
||||
};
|
||||
|
||||
Module.preInit = [skipRun];
|
||||
Module.websocket = [];
|
||||
Module.websocket.url = 'wsproxy://the-swank.pp.ua:3000/'
|
||||
ENV = [];
|
||||
|
||||
loadingDone()
|
55
view/launcher.css
Normal file
55
view/launcher.css
Normal file
@ -0,0 +1,55 @@
|
||||
:root {
|
||||
--mod-height: 36px;
|
||||
}
|
||||
|
||||
html {
|
||||
/* im not gonna write a preprocessor for the css so i'm refrencing an element here */
|
||||
background: #333 var(--trame);
|
||||
background-size: 4px;
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
.mod {
|
||||
display: flex;
|
||||
padding: 1px;
|
||||
box-sizing: content-box;
|
||||
height: var(--mod-height)
|
||||
}
|
||||
|
||||
.mod .info {
|
||||
border-right: 1px solid #666;
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
margin: 2px 0 2px 5px;
|
||||
}
|
||||
|
||||
.info h4 {
|
||||
font-size: 16px;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.mod .button {
|
||||
width: var(--mod-height);
|
||||
height: var(--mod-height);
|
||||
box-sizing: content-box;
|
||||
background-image: var(--xash3d-play);
|
||||
background-size: 20px 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.button:before {
|
||||
content: " ";
|
||||
float: left;
|
||||
padding-top: 100%;
|
||||
}
|
||||
|
||||
.mod .button:active {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.version-info {
|
||||
color: #555;
|
||||
text-align: center;
|
||||
height: 16px;
|
||||
}
|
10
view/launcher.html
Normal file
10
view/launcher.html
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
<ul class="mods">
|
||||
<li class="skin_outset mod">
|
||||
<div class="info">
|
||||
<h4>Test</h4>
|
||||
<span>Data</span>
|
||||
</div>
|
||||
<div class="launch"></div>
|
||||
</li>
|
||||
</ul>
|
12
view/launcher.js
Normal file
12
view/launcher.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Load the trame and save it's url to the appropriate
|
||||
// css variable
|
||||
//
|
||||
// Gross we have to do it this way but it's whatever
|
||||
app.bundle.open('./half-trame.png', 'URL')
|
||||
.then(url => document.querySelector(':root').style.setProperty('--xash-trame', `url(${url})`) )
|
||||
|
||||
{
|
||||
const modList = document.getElementById('mods')
|
||||
|
||||
console.log(app)
|
||||
}
|
21
view/main.css
Executable file
21
view/main.css
Executable file
@ -0,0 +1,21 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background: #000;
|
||||
/* For some reason the canvas gets huge
|
||||
during loading so */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
height: 100vh;
|
||||
width: auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#status {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
color: #f0b418;
|
||||
background: rgba(0, 0, 0, .5);
|
||||
}
|
91
view/main.html
Executable file
91
view/main.html
Executable file
@ -0,0 +1,91 @@
|
||||
|
||||
<!-- Although this is in the root of the bundle, it is loaded into
|
||||
the import/ directory. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/c/sys/skins/w93.css" >
|
||||
<!-- <link rel="stylesheet" lhref="../main.css"> -->
|
||||
<style>
|
||||
html {
|
||||
/* For some reason the status messages cause the scrollbars to appear */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
height: 100vh;
|
||||
width: auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#status {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
color: #f0b418;
|
||||
background: rgba(0, 0, 0, .5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas style="display:none" class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
||||
<code id="status"></code>
|
||||
|
||||
<script>
|
||||
{
|
||||
// const { indexedDb } = window
|
||||
|
||||
// window.indexedDB = new Proxy(indexedDb, {
|
||||
// get(target, prop) {
|
||||
// if(prop == 'open') {
|
||||
// return function(...args) {
|
||||
// if(args[0] == 'UPLINK') {
|
||||
// console.log('Preventing IDB cache')
|
||||
// return {}
|
||||
// } else {
|
||||
// return indexedDb.open.call(this, ...args)
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// return indexedDb[prop]
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
// indexedDB.open('UPLINK', 1)
|
||||
}
|
||||
</script>
|
||||
<script type='text/javascript' lsrc='../init.js'></script>
|
||||
<script>
|
||||
// Causes the app to freeze. Maybe make async?
|
||||
function loadingDone() {
|
||||
(function() {
|
||||
// locateFile will handle the uri conversion on this one
|
||||
var memoryInitializer = 'xash.html.mem';
|
||||
if (typeof Module['locateFile'] === 'function') {
|
||||
memoryInitializer = Module['locateFile'](memoryInitializer);
|
||||
} else if (Module['memoryInitializerPrefixURL']) {
|
||||
memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer;
|
||||
}
|
||||
// Send it the request so it can handle the data
|
||||
var xhr = Module['memoryInitializerRequest'] = new XMLHttpRequest();
|
||||
xhr.open('GET', memoryInitializer, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.send(null);
|
||||
})();
|
||||
|
||||
var script = document.createElement('script');
|
||||
script.src = instance.import['xash.js'];
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
BIN
view/quarter-trame.png
Normal file
BIN
view/quarter-trame.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 108 B |
47
webpack.config.js
Executable file
47
webpack.config.js
Executable file
@ -0,0 +1,47 @@
|
||||
const path = require('path'),
|
||||
merge = require('./util/merge')
|
||||
|
||||
let env = /^--([a-z]+)$/i.exec(process.argv[2])
|
||||
env = env ? env[1] : 'dev'
|
||||
|
||||
const config = {
|
||||
entry: path.join(__dirname, 'src/index.js'),
|
||||
output: {
|
||||
// path: path.join(__dirname, 'dist/'),
|
||||
path: path.join(__dirname, 'view/dist'),
|
||||
filename: 'xash.js'
|
||||
},
|
||||
// module: {
|
||||
// rules: [
|
||||
// {
|
||||
// test: /\.m?js$/i,
|
||||
// exclude: /node_modules|bower_components/,
|
||||
// use: {
|
||||
// loader: 'babel-loader',
|
||||
// options: {
|
||||
// sourceType: 'unambiguous',
|
||||
// presets: [
|
||||
// '@babel/preset-env'
|
||||
// ],
|
||||
// plugins: [
|
||||
// // '@babel/plugin-transform-runtime',
|
||||
// // '@babel/plugin-proposal-class-properties'
|
||||
// '@babel/plugin-syntax-dynamic-import'
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
}
|
||||
|
||||
const environments = {
|
||||
'dev': {
|
||||
mode: 'development'
|
||||
},
|
||||
'prod': {
|
||||
mode: 'production'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = merge(config, environments[env])
|
Loading…
x
Reference in New Issue
Block a user