commit 09360bf4a90de0d80aa16cdd9a22e71c351944ae Author: Funey Date: Wed Jan 6 14:18:45 2021 +0000 code import diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..6e88443 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a18b849 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Ignore node modules. +/node_modules + +# Ignore Java version tracker. +/java.json + +# Ignore cape cache/override. +/c_cache +/c_override + +# Ignore skin cache/override. +/s_cache +/s_override + +# Ignore MinecraftResources. +/resources.xml \ No newline at end of file diff --git a/defaults/.DS_Store b/defaults/.DS_Store new file mode 100644 index 0000000..f436952 Binary files /dev/null and b/defaults/.DS_Store differ diff --git a/defaults/default_skin.png b/defaults/default_skin.png new file mode 100755 index 0000000..7cfa08a Binary files /dev/null and b/defaults/default_skin.png differ diff --git a/index.js b/index.js new file mode 100644 index 0000000..c217ba8 --- /dev/null +++ b/index.js @@ -0,0 +1,560 @@ +'use strict'; + +// ActionMC legacy skins server. +// Virtue 2021 + +const path = require('path'); +const fs = require('fs'); +const request = require('sync-request'); + +function downloadFileSync(url) { + return require('child_process') + .execFileSync('curl', ['--silent', '-L', url]); // remove options {encoding: 'utf8'} +} + +// Variables. + +// This is where the server stores caches of Mojang requests. +const Skin_CacheLocation = path.join(__dirname, "s_cache"); +const Cape_CacheLocation = path.join(__dirname, "c_cache"); + +// This defines the amount of time an item should be cached for. +const Skin_CacheTime = 24; // 24 hours by default. +const Cape_CacheTime = 48; // 48 hours by default (capes don't change as much) + +// This is where the server stores overrides - if a user has an entry in +// these folders, their Mojang request will be overriden with the contents +// of the file present in the override location. +const Skin_OverrideLocation = path.join(__dirname, "s_override"); +const Cape_OverrideLocation = path.join(__dirname, "c_override"); + +// This is where the resource.xml file is stored - this tells a client +// where to find certain resources - may fix some sounds in-game. +const Resources_XML_Location = path.join(__dirname, "resources.xml") + +// This is where the server stores default skins - if a user has no +// skin, their Mojang request will be overridden with the contents +// of the default file. + +const Skin_DefaultFile = path.join(__dirname, 'defaults', 'default_skin.png') +const Cape_DefaultFile = path.join(__dirname, 'defaults', 'default_cape.png') + +// This is where the information for the Java poll is stored. This +// must be a JSON file. + +const Java_JSON = path.join(__dirname, "java.json"); + +// ACTUAL CODE BEYOND THIS POINT \\ + +// Initialise variables +// Time until cache clear for stats api. +var Skin_CacheWipeTime = Date.now() + (3600000 * Skin_CacheTime); +var Cape_CacheWipeTime = Date.now() + (3600000 * Cape_CacheTime); + +// Actual distance between cache clear. +var Skin_CacheWipeDis = 3600000 * Skin_CacheTime; +var Cape_CacheWipeDis = 3600000 * Cape_CacheTime; + +// Previous. + +var Skin_PreviousCacheWipeTime = Date.now(); +var Cape_PreviousCacheWipeTime = Date.now(); + +// Do pre-initialisation folder checks. +if (!fs.existsSync(Skin_CacheLocation)) { + console.log('[WARN] Skin cache directory does not exist, creating...'); + try { + fs.mkdirSync(Skin_CacheLocation); + console.log('[INFO] Skin cache directory created successfully.') + } catch (exc) { + console.log(exc) + console.log('[ERR] Failure creating skin cache directory.') + process.exit(1); + } +} + +if (!fs.existsSync(Cape_CacheLocation)) { + console.log('[WARN] Cape cache directory does not exist, creating...'); + try { + fs.mkdirSync(Cape_CacheLocation); + console.log('[INFO] Cape cache directory created successfully.') + } catch (exc) { + console.log(exc) + console.log('[ERR] Failure creating Cape cache directory.') + process.exit(1); + } +} + +if (!fs.existsSync(Skin_OverrideLocation)) { + console.log('[WARN] Skin override directory does not exist, creating...'); + try { + fs.mkdirSync(Skin_OverrideLocation); + console.log('[INFO] Skin override directory created successfully.') + } catch (exc) { + console.log(exc) + console.log('[ERR] Failure creating skin override directory.') + process.exit(1); + } +} + +if (!fs.existsSync(Cape_OverrideLocation)) { + console.log('[WARN] Cape override directory does not exist, creating...'); + try { + fs.mkdirSync(Cape_OverrideLocation); + console.log('[INFO] Cape override directory created successfully.') + } catch { + console.log('[ERR] Failure creating cape override directory.') + process.exit(1); + } +} + +if (!fs.existsSync(Resources_XML_Location)) { + console.log('[WARN] Resources XML does not exist. Obtaining...'); + try { + let dl = downloadFileSync('http://s3.amazonaws.com/MinecraftResources'); + fs.writeFileSync(Resources_XML_Location, dl); + console.log('[INFO] Resources XML created successfully!') + } catch { + console.log('[ERR] Failure creating resources XML.'); + process.exit(1); + } +} + +// Initialise functions. + +// add_to_java_poll +// Purpose: Adds a new entry into the Java version data. +// Returns: nothing. + +function add_to_java_poll(java_useragent) { + var tmp_json; + + if (fs.existsSync(Java_JSON)) { + tmp_json = require(Java_JSON); + } else { + tmp_json = {}; + } + + if (tmp_json[java_useragent]) { + tmp_json[java_useragent] += 1; + } else { + tmp_json[java_useragent] = 1; + } + + try { + fs.writeFileSync(Java_JSON, JSON.stringify(tmp_json)); + } catch (exc) { + // oh poop. + } +} + +// get_uuid +// Purpose: Gets a UUID of a player. +// Returns: UUID of player if they exist, false if error occured. + +function get_uuid(username) { + let reqBody = '["' + username + '"]'; + let req = request('POST', 'https://api.mojang.com/profiles/minecraft', {body: reqBody}); + + // Have response now. + let response = JSON.parse(req.getBody('utf8')); + if (response.error) { + // error obtaining user. + return false; + } + + // we have the user, get their UUID. + return response[0]["id"]; +} + +// check_cache_override +// Purpose: Checks the cache and override directories for the user. +// Returns: 0 for not cached, 1 for cached and 2 for override. + +function check_cache_override(type, name) { + // type can either be skin or cape. + if (type == "skin") { + if (fs.existsSync(path.join(Skin_OverrideLocation, name + '.png'))) { + // user has an entry in the override location. + return 2; + } + + if (fs.existsSync(path.join(Skin_CacheLocation, name + '.png'))) { + // user has an entry in the cache location. + return 1; + } + + // if execution of subroutine continues, no cache entry exists. + return 0; + } else if (type == "cape") { + if (fs.existsSync(path.join(Cape_OverrideLocation, name + '.png'))) { + // user has an entry in the override location. + return 2; + } + + if (fs.existsSync(path.join(Cape_CacheLocation, name + '.png'))) { + // user has an entry in the cache location. + return 1; + } + + // if execution of subroutine continues, no cache entry exists. + return 0; + } +} + +// get_skin_from_mojang +// Purpose: Get a skin from Mojang. +// Returns: true if skin obtained, false if skin failed. + +function get_skin_from_mojang(uuid, name) { + let req = request('GET', 'https://sessionserver.mojang.com/session/minecraft/profile/' + uuid); + + // have response now. + let response = JSON.parse(req.getBody('utf8')); + + if (response.error) { + // error obtaining profile. + return false; + } + + // now parse for the base64 encoded json. + let b64json = response["properties"][0]["value"]; + let b64buffer = new Buffer(b64json, 'base64'); + b64json = b64buffer.toString('ascii'); + b64json = JSON.parse(b64json); + + // now we have a download link. + let download_url = b64json["textures"]["SKIN"]['url']; + let skin_file = downloadFileSync(download_url); + + + + // write to file. + try { + let write_time = Date.now(); + let writing = fs.writeFileSync(path.join(Skin_CacheLocation, name + '.png'), skin_file); + + return true; + } catch (exc) { + console.error(exc); + return false; + } + +} + +// get_cape_from_mojang +// Purpose: Get a cape from Mojang. +// Returns: true if cape obtained, false if cape failed. + +function get_cape_from_mojang(uuid, name) { + let req = request('GET', 'https://sessionserver.mojang.com/session/minecraft/profile/' + uuid); + + // have response now. + let response = JSON.parse(req.getBody('utf8')); + + if (response.error) { + // error obtaining profile. + return false; + } + + // now parse for the base64 encoded json. + let b64json = response["properties"][0]["value"]; + let b64buffer = new Buffer(b64json, 'base64'); + b64json = b64buffer.toString('ascii'); + b64json = JSON.parse(b64json); + + // now we have a download link. + let download_url = b64json["textures"]["CAPE"]['url']; + let skin_file = downloadFileSync(download_url); + + + + // write to file. + try { + let write_time = Date.now(); + let writing = fs.writeFileSync(path.join(Cape_CacheLocation, name + ".png"), skin_file); + + return true; + } catch (exc) { + console.error(exc); + return false; + } + +} + +// get_skin +// Purpose: Get the skin location of the user to serve. +// Returns: location to skin file if exists, location to default skin if not. + +function get_skin(uname) { + let cache = check_cache_override('skin', uname); + + if (cache == 1) { + // present in cache - serve user from there + return path.join(Skin_CacheLocation, uname + '.png') + } else if (cache == 2) { + // present in OVERRIDE - serve user from there. + return path.join(Skin_OverrideLocation, uname + '.png') + } else { + // presume user does not exist in cache. try to fetch beforehand though. + try { + // get uuid + let uuid = get_uuid(uname); + // get skin from mojang. + let skin_get = get_skin_from_mojang(uuid, uname); + if (!skin_get) { + // failed. return default skin path. + return Skin_DefaultFile; + } else { + // user is now in cache. + return path.join(Skin_CacheLocation, uname + '.png') + } + } catch { + // error occured somewhere. + return Skin_DefaultFile; + } + } +} + +// get_cape +// Purpose: Get the cape location of the user to serve. +// Returns: location to cape file if exists, location to default cape if not. + +function get_cape(uname) { + let cache = check_cache_override('cape', uname); + + if (cache == 1) { + // present in cache - serve user from there + return path.join(Cape_CacheLocation, uname + '.png') + } else if (cache == 2) { + // present in OVERRIDE - serve user from there. + return path.join(Cape_OverrideLocation, uname + '.png') + } else { + // presume user does not exist in cache. try to fetch beforehand though. + try { + // get uuid + let uuid = get_uuid(uname); + // get skin from mojang. + let skin_get = get_cape_from_mojang(uuid, uname); + if (!skin_get) { + // failed. return default skin path. + return Cape_DefaultFile; + } else { + // user is now in cache. + return path.join(Cape_CacheLocation, uname + '.png') + } + } catch { + // error occured somewhere. + return Cape_DefaultFile; + } + } +} + +// rm_dir +// Purpose: removes all files in a directory. +// Returns: nothing. + +function rm_dir(dirPath) { + try { var files = fs.readdirSync(dirPath); } + catch(e) { return; } + if (files.length > 0) + for (var i = 0; i < files.length; i++) { + var filePath = dirPath + '/' + files[i]; + if (fs.statSync(filePath).isFile()) + fs.unlinkSync(filePath); + else + rmDir(filePath); + } + }; + +// Initialise cache-wiping functions. + +// Skin. +setInterval(() => { + console.log('[CACHE] Wiping skins cache...'); + rm_dir(Skin_CacheLocation); + Skin_PreviousCacheWipeTime = Skin_CacheWipeTime + Skin_CacheWipeTime = Date.now() + (3600000 * Skin_CacheTime); + console.log('[CACHE] Skin cache wipe completed.'); +}, Skin_CacheWipeDis) + +// Cape. +setInterval(() => { + console.log('[CACHE] Wiping capes cache...'); + rm_dir(Cape_CacheLocation); + Cape_PreviousCacheWipeTime = Cape_CacheWipeTime + Cape_CacheWipeTime = Date.now() + (3600000 * Cape_CacheTime); + console.log('[CACHE] Cape cache wipe completed.'); +}, Cape_CacheWipeDis) + +// Do server initial cache wipe. +console.log('[CACHE] Server startup - wiping skin cache...'); +rm_dir(Skin_CacheLocation); +console.log('[CACHE] Skin cache wipe completed.'); +console.log('[CACHE] Server startup - wiping cape cache...'); +rm_dir(Cape_CacheLocation); +console.log('[CACHE] Cape cache wipe completed.') + +// Initialise express. +const express = require('express') +const app = express() +const port = 80 + +// Initialise static folders. +app.use(express.static('static')) +app.use('/MinecraftResources', express.static('resources')) + +// Initialise routers. + +// GET /MinecraftSkins/:username +// Paramters: +// username: username of character to get skin for +// Returns: +// skin.png + +app.get('/MinecraftSkins/:username', async (req,res) => { + let parsed_username = req.params.username.slice(0,-4); + console.log('[GET] Cape: ' + parsed_username); + console.log(parsed_username) + let location = get_skin(parsed_username); + res.sendFile(location); +}) + +// GET /MinecraftCloaks/:username +// Paramters: +// username: username of character to get cape for +// Returns: +// cape.png + +app.get('/MinecraftCloaks/:username', async (req,res) => { + let parsed_username = req.params.username.slice(0,-4); + console.log('[GET] Cloak: ' + parsed_username); + let location = get_cape(parsed_username); + res.sendFile(location); +}) + +// GET /statistics +// No parameters. +// Returns: +// - information from the stats API but neater and user-friendly. + +app.get('/statistics', async (req,res) => { + let skin_cacheCount = fs.readdirSync(Skin_CacheLocation).length; + let skin_overrideCount = fs.readdirSync(Skin_OverrideLocation).length; + let cape_cacheCount = fs.readdirSync(Cape_CacheLocation).length; + let cape_overrideCount = fs.readdirSync(Cape_OverrideLocation).length; + + let skin_wipeTime = new Date(Skin_CacheWipeTime); + let skin_prevWipeTime = new Date(Skin_PreviousCacheWipeTime); + let cape_wipeTime = new Date(Cape_CacheWipeTime); + let cape_prevWipeTime = new Date(Cape_PreviousCacheWipeTime); + let result = `

Statistics

+

Skins

+

Skins currently cached: ${skin_cacheCount}

+

Skins currently overriden: ${skin_overrideCount}

+

Time of next skin cache wipe: ${skin_wipeTime.toUTCString()}

+

Time of previous skin cache wipe: ${skin_prevWipeTime.toUTCString()}

+

Capes

+

Capes currently cached: ${cape_cacheCount}

+

Capes currently overridden: ${cape_overrideCount}

+

Time of next cape cache wipe: ${cape_wipeTime.toUTCString()}

+

Time of previous cape cache wipe: ${cape_prevWipeTime.toUTCString()}


+

For Java statistics, see the Java statistics page.

` + + res.send(result) + +}) + +// GET /statistics/java +// No parameters +// Returns: +// - Java version info from the stats API but neater and user-friendly. + +app.get('/statistics/java', async (req,res) => { + let result = '

Versions of Java that have used this skin server:

' + var javajson; + if (fs.existsSync(Java_JSON)) { + javajson = require(Java_JSON) + } else { + javajson = {} + } + for (const i in javajson) { + result += `

${i}: ${javajson[i]}

` + } + result += '

To see other statistics, see the main statistics page.

' + res.send(result) +}) + +// GET /statistics/api/java +// No parameters. +// Returns: +// - Java versions that have "phoned home" on MC client startup. + +app.get('/statisticss/api/java', async (req,res) => { + var javajson; + if (fs.existsSync(Java_JSON)) { + javajson = require(Java_JSON) + } else { + javajson = {} + } + + res.json(javajson) +}); + + + +// GET /statistics/api/skins +// No parameteers +// Returns: +// - amount of skins cached +// - amount of skins with overrides +// - epoch timestamp of cache wipe time +// - epoch timestamp of previous cache wipe time + +app.get('/statistics/api/skins', async (req,res) => { + let skin_CacheCount = fs.readdirSync(Skin_CacheLocation).length; + let skin_OverrideCount = fs.readdirSync(Skin_OverrideLocation).length; + res.contentType('application/json') + res.json({cached: skin_CacheCount, overridden: skin_OverrideCount, cache_wipe_time: Skin_CacheWipeTime, previous_cache_wipe_time: Skin_PreviousCacheWipeTime}); +}) + +// GET /statistics/api/capes +// No parameteers +// Returns: +// - amount of capes cached +// - amount of capes with overrides +// - epoch timestamp of cache wipe time +// - epoch timestamp of previous cache wipe time + +app.get('/statistics/api/capes', async (req,res) => { + let skin_CacheCount = fs.readdirSync(Cape_CacheLocation).length; + let skin_OverrideCount = fs.readdirSync(Cape_OverrideLocation).length; + res.contentType('application/json') + res.json({cached: skin_CacheCount, overridden: skin_OverrideCount, cache_wipe_time: Cape_CacheWipeTime, previous_cache_wipe_time: Cape_PreviousCacheWipeTime}); +}) + +// GET /MinecraftResources +// No parameters +// Returns: +// - XML file containing location of resources. +// This can be also used to track amount of clients using skin server, +// but this shouldn't be relied upon. + +// Client can be tracked by User Agent. +// If the first four characters of their user agent are "Java", then +// it is likely that they are a MC client. + +app.get('/MinecraftResources', async (req,res) => { + if (req.headers['user-agent'].slice(0,4) == "Java") { + console.log('[INFO] New client made connection to server!'); + add_to_java_poll(req.headers['user-agent']); + } + res.sendFile(Resources_XML_Location); +}) + +// Listen on defined port. +app.listen(port, () => { + console.log(`[HTTP] ActionMC skin server listening on port ${port}.`) +}) + +// EOF \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f0618d2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,586 @@ +{ + "name": "amc-skins", + "version": "2.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0=", + "requires": { + "@types/node": "*" + } + }, + "@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "10.17.50", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.50.tgz", + "integrity": "sha512-vwX+/ija9xKc/z9VqMCdbf4WYcMTGsI0I/L/6shIF3qXURxZOhPQlPRHtjTpiNhAwn0paMJzlOQqw6mAGEQnTA==" + }, + "@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "download-file-sync": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/download-file-sync/-/download-file-sync-1.0.4.tgz", + "integrity": "sha1-0+PFQ/g29BA5RVuQNMcuNVsDYBk=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" + }, + "http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "requires": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "requires": { + "@types/node": "^10.0.3" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" + }, + "mime-types": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "requires": { + "mime-db": "1.45.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha1-juqz5U+laSD+Fro493+iGqzC104=" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "requires": { + "asap": "~2.0.6" + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "requires": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + } + }, + "sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "requires": { + "get-port": "^3.1.0" + } + }, + "then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "requires": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + } + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6c13c42 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "amc-skins", + "version": "2.0.1", + "description": "Legacy skins server for ActionMC.", + "main": "index.js", + "scripts": { + "test": "node ." + }, + "author": "Virtue ", + "license": "ISC", + "dependencies": { + "download-file-sync": "^1.0.4", + "express": "^4.17.1", + "sync-request": "^6.1.0" + } +} diff --git a/resources/README_VERY_IMPORTANT.txt b/resources/README_VERY_IMPORTANT.txt new file mode 100644 index 0000000..ea4241b --- /dev/null +++ b/resources/README_VERY_IMPORTANT.txt @@ -0,0 +1,8 @@ +Place contents of resources folder in .minecraft here. +This is required for some older versions of Minecraft. + +Location of .minecraft: + +Windows: %appdata%\.minecraft +Linux: ~/.minecraft +macOS: ~/Libary/Application Support/minecraft diff --git a/static/README.md b/static/README.md new file mode 100644 index 0000000..a93edb0 --- /dev/null +++ b/static/README.md @@ -0,0 +1,19 @@ +# Theta Skins Server +A replacement for the now-removed legacy Minecraft Skins API, as well as providing a few fixes for older versions. + +# Run Instructions +- Clone the repository. +- Run ``npm install`` +- Populate the "resources" folder with the contents of `.minecraft/resources` (follow the readme file in the folder for more information) +- Start the program with ``npm run test`` + +The program will automatically make directories and XML files. + +# How to Access + +Append the following string to your hosts file: +``` + s3.amazonaws.com +``` + +Be sure to disable this after playing though, as a lot of the internet relies on Amazon S3 services and this server cannot provide all of them. \ No newline at end of file diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..5f20a18 --- /dev/null +++ b/static/index.html @@ -0,0 +1,2 @@ +

Theta Skin Server

+

Your website goes here!

\ No newline at end of file