重写更新逻辑(50%,需要另外的服务器支持增量更新)

This commit is contained in:
nonameShijian 2024-03-29 23:39:16 +08:00
parent 612d0231b3
commit bd7cb202b7
5 changed files with 782 additions and 348 deletions

View File

@ -5572,3 +5572,20 @@ div[data-decoration="bronze"]::after{
* {
scrollbar-width: none;
}
/* 更新进度条 */
progress.progress {
width: 75%;
height: 10px;
border: 2px solid;
border-radius: 15px;
vertical-align: middle;
-webkit-appearance: none;
}
progress.progress::-webkit-progress-bar {
background: rgb(239, 239, 239);
border-radius: 0.2rem;
}
progress.progress::-webkit-progress-value {
border-radius: 0.2rem;
background: rgb(0, 117, 255);
}

View File

@ -73,7 +73,6 @@ declare type Dialog = import('../../noname/library/index.js').Dialog;
declare type GameEvent = import('../../noname/library/index.js').GameEvent;
declare type GameEventPromise = import('../../noname/library/index.js').GameEventPromise;
declare type Player = import('../../noname/library/index.js').Player;
declare type VCard = import('../../noname/library/index.js').VCard;
declare type Control = import('../../noname/library/index.js').Control;
declare type Video = import('../../noname/game/index.js').Video;
@ -85,6 +84,27 @@ declare type Sex = 'male' | 'female' | 'dobule' | 'none';
declare type Character = [Sex, string, number | string, string[], string[]] | [Sex, string, number | string, string[]];
declare type Select = [number, number];
declare interface progress extends HTMLDivElement {
/** 获取标题 */
getTitle: () => string;
/** 更改标题 */
setTitle: (title: string) => void;
/** 获取显示的文件名 */
getFileName: () => string;
/** 更改显示的文件名 */
setFileName: (title: string) => void;
/** 获取进度*/
getProgressValue: () => number;
/** 更改进度*/
setProgressValue: (value: number) => void;
/** 获取下载文件总数 */
getProgressMax: () => number;
/** 修改下载文件总数 */
setProgressMax: (max: number) => void;
/** 通过数组自动解析文件名 */
autoSetFileNameFromArray: (fileNameList: string[]) => void;
}
/**
*
*/
@ -269,7 +289,7 @@ declare interface importModeConfig {
*/
characterSort?: SMap<SMap<string[]>>;
/** 卡牌(主要是放些该模式下特有的卡牌) */
card?: SMap<ExCardData>;
card?: SMap<Card>;
/**
*
*/

View File

@ -19,7 +19,7 @@ export function canUseHttpProtocol() {
// 如果是http了就不用
if (location.protocol.startsWith('http')) return false;
// 首次启动不更新(即还没进行过新手教程)
if (!lib.config.new_tutorial) return false;
if (!config.get('new_tutorial')) return false;
if (typeof nonameInitialized == 'string') {
// 手机端
if (window.cordova) {
@ -134,18 +134,6 @@ export async function boot() {
setWindowListener();
const promiseErrorHandler = await setOnError();
// 无名杀更新日志
if (window.noname_update) {
Reflect.set(lib, 'version', window.noname_update.version);
lib.changeLog = window.noname_update.changeLog;
if (window.noname_update.players) {
lib.changeLog.push('players://' + JSON.stringify(window.noname_update.players));
}
if (window.noname_update.cards) {
lib.changeLog.push('cards://' + JSON.stringify(window.noname_update.cards));
}
delete window.noname_update;
}
// 确认手机端平台
const noname_inited = localStorage.getItem('noname_inited');
if (noname_inited && noname_inited !== 'nodejs') {
@ -498,6 +486,39 @@ export async function boot() {
}
delete _status.htmlbg;
// 无名杀更新日志
if (window.noname_update) {
Reflect.set(lib, 'version', window.noname_update.version);
// 更全面的更新内容
if (config.get(`version_description_v${window.noname_update.version}`)) {
try {
const description = config.get(`version_description_v${window.noname_update.version}`);
const html = String.raw;
lib.changeLog.push(
html`
<div style="position: relative;width:50px;height:50px;border-radius:50px;background-image:url('${description.author.avatar_url}');background-size:cover;vertical-align:middle;"></div>
${description.author.login}于${description.published_at}发布
`.trim(),
description.body.replaceAll('\n', '<br/>')
);
} catch (e) {
console.error(e);
lib.changeLog.push(...window.noname_update.changeLog);
}
}
// 原更新内容
else {
lib.changeLog.push(...window.noname_update.changeLog);
}
if (window.noname_update.players) {
lib.changeLog.push('players://' + JSON.stringify(window.noname_update.players));
}
if (window.noname_update.cards) {
lib.changeLog.push('cards://' + JSON.stringify(window.noname_update.cards));
}
delete window.noname_update;
}
// 虽然但是我就暴露个import应该没啥问题
Reflect.set(window, 'game', {
import: game.import.bind(null)
@ -715,6 +736,7 @@ async function loadConfig() {
Reflect.set(lib, 'config', Reflect.get(window, 'config'));
Reflect.set(lib, 'configOL', {});
Reflect.deleteProperty(window, 'config');
let result;
if (localStorage.getItem(`${lib.configprefix}nodb`))
Reflect.set(window, 'nodb', true);

418
noname/library/update.js Normal file
View File

@ -0,0 +1,418 @@
import { ui } from '../../noname.js';
// https://github.com/libccy/noname/archive/refs/tags/v1.10.10.zip
/**
* HTTP响应头中的Rate Limit相关信息
* X-RateLimit-Limit: 请求总量限制
* X-RateLimit-Remaining: 剩余请求次数
* X-RateLimit-Reset: 限制重置时间UTC时间戳
*/
/** @type { HeadersInit } */
const defaultHeaders = {
'Accept': 'application/vnd.github.v3+json',
// 根据GitHub API的要求添加适当的认证头信息
// 如果公共仓库则无需认证私有仓库需提供token
// 'Authorization': `Bearer ${YOUR_GITHUB_PERSONAL_ACCESS_TOKEN}`
};
const defaultResponse = response => {
const limit = response.headers.get("X-RateLimit-Limit");
const remaining = response.headers.get("X-RateLimit-Remaining");
const reset = response.headers.get("X-RateLimit-Reset");
console.log(`请求总量限制`, limit);
console.log(`剩余请求次数`, remaining);
console.log(`限制重置时间`, (new Date(reset * 1000)).toLocaleString());
};
/**
* 字节转换
* @param { number } limit
*/
export function parseSize(limit) {
let size = "";
if (limit < 1 * 1024) {
// 小于1KB则转化成B
size = limit.toFixed(2) + "B"
} else if (limit < 1 * 1024 * 1024) {
// 小于1MB则转化成KB
size = (limit / 1024).toFixed(2) + "KB"
} else if (limit < 1 * 1024 * 1024 * 1024) {
// 小于1GB则转化成MB
size = (limit / (1024 * 1024)).toFixed(2) + "MB"
} else {
// 其他转化成GB
size = (limit / (1024 * 1024 * 1024)).toFixed(2) + "GB"
}
// 转成字符串
let sizeStr = size + "";
// 获取小数点处的索引
let index = sizeStr.indexOf(".");
// 获取小数点后两位的值
let dou = sizeStr.slice(index + 1, 2);
// 判断后两位是否为00如果是则删除00
if (dou == "00") {
return sizeStr.slice(0, index) + sizeStr.slice(index + 3, 2);
}
return size;
};
/**
* 对比版本号
* @param { string } ver1
* @param { string } ver2
* @returns { -1 | 0 | 1 }
*/
export function checkVersion(ver1, ver2) {
if (typeof ver1 !== 'string') ver1 = String(ver1);
if (typeof ver2 !== 'string') ver2 = String(ver2);
// 移除 'v' 开头
if (ver1.startsWith('v')) ver1 = ver1.slice(1);
if (ver2.startsWith('v')) ver2 = ver2.slice(1);
// 验证版本号格式
if (/[^0-9.-]/i.test(ver1) || /[^0-9.-]/i.test(ver2)) {
throw new Error('Invalid characters found in the version numbers');
}
/** @param { string } str */
function* walk(str) {
let part = '';
for (const char of str) {
if (char === '.' || char === '-') {
if (part) yield Number(part);
part = '';
} else {
part += char;
}
}
if (part) yield Number(part);
}
const iterator1 = walk(ver1);
const iterator2 = walk(ver2);
while (true) {
const iter1 = iterator1.next();
const iter2 = iterator2.next();
let { value: item1 } = iter1;
let { value: item2 } = iter2;
// 如果任意一个迭代器已经没有剩余值将该值视为0
item1 = item1 === undefined ? 0 : item1;
item2 = item2 === undefined ? 0 : item2;
if (isNaN(item1) || isNaN(item2)) {
throw new Error('Non-numeric part found in the version numbers');
} else if (item1 > item2) {
return 1;
} else if (item1 < item2) {
return -1;
} else {
if (iter1.done && iter2.done) break;
}
}
// 若正常遍历结束,说明版本号相等
return 0;
};
/**
*
* 获取指定仓库的tags
* @param { Object } options
* @param { string } [options.username = 'libccy'] 仓库拥有者
* @param { string } [options.repository = 'noname'] 仓库名称
* @param { string } [options.accessToken] 身份令牌
* @returns { Promise<{ commit: { sha: string, url: string }, name: string, node_id: string, tarball_url: string, zipball_url: string }[]> }
*
* @example
* ```js
* getRepoTags().then(tags => {
* console.log("All tags:", tags.map(tag => tag.name));
* // 获取最新tag假设按时间顺序排列最新tag在数组首位
* const latestTag = tags[0].name;
* console.log("Latest tag:", latestTag);
* });
* ```
*/
export async function getRepoTags(options = { username: 'libccy', repository: 'noname' }) {
const { username = 'libccy', repository = 'noname', accessToken } = options;
const headers = Object.assign({}, defaultHeaders);
if (accessToken) {
headers['Authorization'] = `token ${accessToken}`;
}
const url = `https://api.github.com/repos/${username}/${repository}/tags`;
const response = await fetch(url, { headers });
defaultResponse(response);
if (response.ok) {
const data = await response.json();
return data;
} else {
throw new Error(`Error fetching tags: ${response.statusText}`);
}
};
/**
* 获取指定仓库的指定tags的描述
* @param { string } tagName tag名称
* @param { Object } options
* @param { string } [options.username = 'libccy'] 仓库拥有者
* @param { string } [options.repository = 'noname'] 仓库名称
* @param { string } [options.accessToken] 身份令牌
* @example
* ```js
* getRepoTagDescription('v1.10.10')
* .then(description => console.log(description))
* .catch(error => console.error('Failed to fetch description:', error));
* ```
*/
export async function getRepoTagDescription(tagName, options = { username: 'libccy', repository: 'noname' }) {
const { username = 'libccy', repository = 'noname', accessToken } = options;
const headers = Object.assign({}, defaultHeaders);
if (accessToken) {
headers['Authorization'] = `token ${accessToken}`;
}
const apiUrl = `https://api.github.com/repos/${username}/${repository}/releases/tags/${tagName}`;
const response = await fetch(apiUrl, { headers });
defaultResponse(response);
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
const releaseData = await response.json();
// console.log(releaseData);
// 从json里拿我们需要的
return {
/** @type { { browser_download_url: string, content_type: string, name: string, size: number }[] } tag额外上传的素材包 */
assets: releaseData.assets,
author: {
/** @type { string } 用户名 */
login: releaseData.author.login,
/** @type { string } 用户头像地址 */
avatar_url: releaseData.author.avatar_url,
/** @type { string } 用户仓库地址 */
html_url: releaseData.author.html_url,
},
/** @type { string } tag描述 */
body: releaseData.body,
// created_at: (new Date(releaseData.created_at)).toLocaleString(),
/** @type { string } tag页面 */
html_url: releaseData.html_url,
/** @type { string } tag名称 */
name: releaseData.name,
/** 发布日期 */
published_at: (new Date(releaseData.published_at)).toLocaleString(),
/** @type { string } 下载地址 */
zipball_url: releaseData.zipball_url,
};
};
/**
*
* 获取仓库指定分支和指定目录内的所有文件和目录
* @param { string } [path = ''] 路径名称(可放参数)
* @param { string } [branch = ''] 仓库分支名称
* @param { Object } options
* @param { string } [options.username = 'libccy'] 仓库拥有者
* @param { string } [options.repository = 'noname'] 仓库名称
* @param { string } [options.accessToken] 身份令牌
* @returns { Promise<{ download_url: string, name: string, path: string, sha: string, size: number, type: 'file' } | { download_url: null, name: string, path: string, sha: string, size: 0, type: 'dir' }> }
* @example
* ```js
* getRepoFilesList()
* .then(files => console.log(files))
* .catch(error => console.error('Failed to fetch files:', error));
* ```
*/
export async function getRepoFilesList(path = '', branch, options = { username: 'libccy', repository: 'noname' }) {
const { username = 'libccy', repository = 'noname', accessToken } = options;
const headers = Object.assign({}, defaultHeaders);
if (accessToken) {
headers['Authorization'] = `token ${accessToken}`;
}
let url = `https://api.github.com/repos/${username}/${repository}/contents/${path}`;
if (typeof branch == 'string' && branch.length > 0) {
const searchParams = new URLSearchParams(new URL(url).search.slice(1));
if (searchParams.has('ref')) {
throw new TypeError(`设置了branch参数后不应在path参数内拼接ref`);
}
searchParams.append('ref', branch);
url = searchParams.toString();
}
const response = await fetch(url, { headers });
defaultResponse(response);
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
const data = await response.json();
console.log(data);
// 处理响应数据,返回文件列表
return data.map(({ download_url, name, path, sha, size, type }) => ({
download_url,
name,
path,
sha,
size,
type
}));
};
/**
* 请求一个文件而不是直接储存为文件
* @param { string } url
* @param { (receivedBytes: number, total?:number, filename?: string) => void } [onProgress]
* @example
* ```js
* await getRepoTagDescription('v1.10.10').then(({ zipball_url }) => request(zipball_url));
* ```
*/
export async function request(url, onProgress) {
const response = await fetch(url, {
// 告诉服务器我们期望得到范围请求的支持
headers: { 'Range': 'bytes=0-' },
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// @ts-ignore
let total = parseInt(response.headers.get('Content-Length'), 10);
// 如果服务器未返回Content-Length则无法准确计算进度
// @ts-ignore
if (isNaN(total)) total = null;
// @ts-ignore
const reader = response.body.getReader();
let filename;
try {
// @ts-ignore
filename = response.headers.get('Content-Disposition').split(';')[1].split('=')[1];
} catch {}
let receivedBytes = 0;
let chunks = [];
while (true) {
// 使用ReadableStream来获取部分数据并计算进度
const { done, value } = await reader.read();
if (done) {
break;
}
chunks.push(value);
receivedBytes += value.length;
if (typeof onProgress == 'function') {
if (total) {
const progress = (receivedBytes / total) * 100;
onProgress(receivedBytes, progress, filename);
} else {
onProgress(receivedBytes, void 0, filename);
}
}
}
// 合并chunks并转换为Blob
const blob = new Blob(chunks);
// 仅做演示打印已合并的Blob大小
console.log(`Download completed. Total size: ${ parseSize(blob.size) }.`);
return blob;
};
/**
*
* @param { string } [title]
* @param { string | number } [max]
* @param { string } [fileName]
* @param { string | number } [value]
* @returns { progress }
*/
export function createProgress(title, max, fileName, value) {
/** @type { progress } */
// @ts-ignore
const parent = ui.create.div(ui.window, {
textAlign: 'center',
width: '300px',
height: '150px',
left: 'calc(50% - 150px)',
top: 'auto',
bottom: 'calc(50% - 75px)',
zIndex: '10',
boxShadow: 'rgb(0 0 0 / 40 %) 0 0 0 1px, rgb(0 0 0 / 20 %) 0 3px 10px',
backgroundImage: 'linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4))',
borderRadius: '8px',
overflow: 'hidden scroll'
});
// 可拖动
parent.className = 'dialog';
const container = ui.create.div(parent, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%'
});
container.ontouchstart = ui.click.dialogtouchStart;
container.ontouchmove = ui.click.touchScroll;
// @ts-ignore
container.style.WebkitOverflowScrolling = 'touch';
parent.ontouchstart = ui.click.dragtouchdialog;
const caption = ui.create.div(container, '', title, {
position: 'relative',
paddingTop: '8px',
fontSize: '20px'
});
ui.create.node('br', container);
const tip = ui.create.div(container, {
position: 'relative',
paddingTop: '8px',
fontSize: '20px',
width: '100%'
});
const file = ui.create.node('span', tip, '', fileName);
file.style.width = file.style.maxWidth = '100%';
ui.create.node('br', tip);
const index = ui.create.node('span', tip, '', String(value || '0'));
ui.create.node('span', tip, '', '/');
const maxSpan = ui.create.node('span', tip, '', String(max || '未知'));
ui.create.node('br', container);
const progress = ui.create.node('progress.progress', container);
progress.setAttribute('value', value || '0');
progress.setAttribute('max', max);
parent.getTitle = () => caption.innerText;
parent.setTitle = title => caption.innerHTML = title;
parent.getFileName = () => file.innerText;
parent.setFileName = name => file.innerHTML = name;
parent.getProgressValue = () => progress.value;
parent.setProgressValue = value => progress.value = index.innerHTML = value;
parent.getProgressMax = () => progress.max;
parent.setProgressMax = max => progress.max = maxSpan.innerHTML = max;
parent.autoSetFileNameFromArray = fileNameList => {
if (fileNameList.length > 2) {
parent.setFileName(fileNameList.slice(0, 2).concat(`......等${fileNameList.length - 2}个文件`).join('<br/>'));
} else if (fileNameList.length == 2) {
parent.setFileName(fileNameList.join('<br/>'));
} else if (fileNameList.length == 1) {
parent.setFileName(fileNameList[0]);
} else {
parent.setFileName('当前没有正在下载的文件');
}
};
return parent;
};

View File

@ -15,8 +15,17 @@ import {
clickMenuItem,
createMenu,
createConfig
} from "../index.js";
import { ui, game, get ,ai ,lib, _status } from "../../../../../noname.js";
} from '../index.js';
import { ui, game, get, ai, lib, _status } from "../../../../../noname.js";
import {
parseSize,
checkVersion,
getRepoTags,
getRepoTagDescription,
getRepoFilesList,
request,
createProgress
} from "../../../../library/update.js"
export const otherMenu = function (connectMenu) {
if (connectMenu) return;
@ -108,263 +117,229 @@ export const otherMenu = function (connectMenu) {
var li1 = document.createElement('li');
var li2 = document.createElement('li');
var li3 = document.createElement('li');
const trimURL = url => {
const updateURLS = lib.updateURLS;
for (const key in updateURLS) {
const updateURL = updateURLS[key];
if (url == updateURL) return lib.configMenu.general.config.update_link.item[key];
}
let index = url.indexOf('://');
if (index != -1) url = url.slice(index + 3);
index = url.indexOf('/');
if (index != -1) url = url.slice(0, index);
if (url.length > 15) {
const list = url.split('.');
if (list.length > 1) list.shift();
url = list.join('.');
}
if (url.length > 15) {
const list = url.split('.');
if (list.length > 1) list.pop();
url = list.join('.');
}
return url;
};
// const trimURL = url => {
// const updateURLS = lib.updateURLS;
// for (const key in updateURLS) {
// const updateURL = updateURLS[key];
// if (url == updateURL) return lib.configMenu.general.config.update_link.item[key];
// }
// let index = url.indexOf('://');
// if (index != -1) url = url.slice(index + 3);
// index = url.indexOf('/');
// if (index != -1) url = url.slice(0, index);
// if (url.length > 15) {
// const list = url.split('.');
// if (list.length > 1) list.shift();
// url = list.join('.');
// }
// if (url.length > 15) {
// const list = url.split('.');
// if (list.length > 1) list.pop();
// url = list.join('.');
// }
// return url;
// };
li1.innerHTML = '游戏版本:' + lib.version + '<p style="margin-top:8px;white-space:nowrap"></p>';
li2.innerHTML = '素材版本:' + (lib.config.asset_version || '无') + '<p style="margin-top:8px"></p>';
li3.innerHTML = '更新地址:<span>' + trimURL(lib.config.updateURL || lib.updateURL) + '</span><p style="margin-top:8px"></p>';
// li3.innerHTML = '更新地址:<span>' + trimURL(lib.config.updateURL || lib.updateURL) + '</span><p style="margin-top:8px"></p>';
li3.style.whiteSpace = 'nowrap';
li3.style.display = 'none';// coding
var button1, button2, button3, button4, button5;
var checkVersionButton, checkAssetButton, checkDevVersionButton/*, button4, button5*/;
game.checkForUpdate = function (forcecheck, dev) {
if (!dev && button1.disabled) {
game.checkForUpdate = async function (forcecheck, dev) {
if (!dev && checkVersionButton.disabled) {
return;
}
else if (dev && button3.disabled) {
return;
}
else if (!game.download) {
alert('此版本不支持游戏内更新,请手动更新');
else if (dev && checkDevVersionButton.disabled) {
return;
}
else {
if (dev) {
button3.innerHTML = '正在检查更新';
checkDevVersionButton.innerHTML = '正在检查更新';
}
else {
button1.innerHTML = '正在检查更新';
}
button3.disabled = true;
button1.disabled = true;
var goupdate = function (files, update) {
lib.version = update.version;
if (update.dev && !lib.config.debug) {
dev = 'nodev';
}
lib.init.req('game/source.js', function () {
try {
eval(this.responseText);
if (!window.noname_source_list) {
throw ('err');
}
}
catch (e) {
alert('更新地址有误');
console.log(e);
return;
checkVersionButton.innerHTML = '正在检查更新';
}
var updates = window.noname_source_list;
delete window.noname_source_list;
if (Array.isArray(files)) {
files.add('game/update.js');
var files2 = [];
for (var i = 0; i < files.length; i++) {
var str = files[i].indexOf('*');
if (str != -1) {
str = files[i].slice(0, str);
files.splice(i--, 1);
for (var j = 0; j < updates.length; j++) {
if (updates[j].startsWith(str)) {
files2.push(updates[j]);
}
}
}
}
updates = files.concat(files2);
}
for (var i = 0; i < updates.length; i++) {
if (updates[i].startsWith('theme/') && !updates[i].includes('.css')) {
updates.splice(i--, 1);
}
else if (updates[i].startsWith('node_modules/') && !update.node) {
updates.splice(i--, 1);
}
checkDevVersionButton.disabled = true;
checkVersionButton.disabled = true;
function refresh() {
checkVersionButton.disabled = false;
checkVersionButton.innerHTML = '检查游戏更新';
checkDevVersionButton.disabled = false;
checkDevVersionButton.innerHTML = '更新到开发版';
}
if (!ui.arena.classList.contains('menupaused')) {
ui.click.configMenu();
ui.click.menuTab('其它');
}
var p = button1.parentNode;
button1.remove();
button3.remove();
var span = document.createElement('span');
var n1 = 0;
var n2 = updates.length;
span.innerHTML = '正在下载文件(' + n1 + '/' + n2 + '';
p.appendChild(span);
var finish = function () {
span.innerHTML = '游戏更新完毕(' + n1 + '/' + n2 + '';
p.appendChild(document.createElement('br'));
var button = document.createElement('button');
button.innerHTML = '重新启动';
button.onclick = game.reload;
button.style.marginTop = '8px';
p.appendChild(button);
};
game.multiDownload(updates, function () {
n1++;
span.innerHTML = '正在下载文件(' + n1 + '/' + n2 + '';
}, function (e) {
game.print('下载失败:' + e.source);
}, function () {
setTimeout(finish, 500);
}, null, dev);
}, function () {
alert('更新地址有误');
}, true);
};
lib.init.req('game/update.js', function () {
try {
eval(this.responseText);
if (!window.noname_update) {
throw ('err');
}
}
catch (e) {
alert('更新地址有误');
console.log(e);
return;
}
var update = window.noname_update;
delete window.noname_update;
if (forcecheck === false) {
if (update.version == lib.config.check_version) {
return;
}
}
game.saveConfig('check_version', update.version);
var goon = true;
if (!dev) {
if (update.version.includes('beta') || update.version == lib.version) {
goon = false;
getRepoTags()
.then(tags => tags.filter(tag => tag.name != 'v1998')[0])
.then(tag => {
game.saveConfig('check_version', tag.name.slice(1));
if (typeof lib.config['version_description_' + tag.name] == 'object') {
/** @type { ReturnType<import('../../../../library/update.js').getRepoTagDescription> } */
const description = lib.config['version_description_' + tag.name];
return description;
}
else return getRepoTagDescription(tag.name);
})
.then(description => {
// 保存版本信息
if (typeof lib.config['version_description_' + description.name] != 'object') {
game.saveConfig('version_description_' + description.name, description);
}
const versionResult = checkVersion(lib.version, description.name);
if (versionResult === 0) {
// forcecheck: 为false的时候是自动检测更新的调用
if (forcecheck === false || !confirm('版本已是最新,是否强制更新?')) {
refresh();
return;
}
}
if (goon) {
var files = null;
var version = lib.version;
if (Array.isArray(update.dev) && dev) {
files = update.dev;
}
else if (Array.isArray(update.files) && update.update && !dev) {
var version1 = version.split('.');
var version2 = update.update.split('.');
for (var i = 0; i < version1.length && i < version2.length; i++) {
if (version2[i] > version1[i]) {
files = false; break;
}
else if (version1[i] > version2[i]) {
files = update.files.slice(0); break;
}
}
if (files === null) {
if (version1.length >= version2.length) {
files = update.files.slice(0);
}
}
}
var str;
if (dev) {
str = '开发版仅供测试使用,可能存在风险,是否确定更新?';
}
else {
str = '有新版本' + update.version + '可用,是否下载?';
}
const str = versionResult > 0 ? (`有新版本${description.name}可用,是否下载?`) : (`本地版本${ lib.version }高于或等于github版本${description.name},是否强制下载?`);
const str2 = description.body;
if (navigator.notification && navigator.notification.confirm) {
var str2;
if (dev) {
str2 = str;
str = '更新到开发版';
}
else {
str2 = update.changeLog[0];
for (var i = 1; i < update.changeLog.length; i++) {
if (update.changeLog[i].indexOf('://') == -1) {
str2 += '' + update.changeLog[i];
}
}
}
navigator.notification.confirm(
str2,
function (index) {
if (index == 1) {
goupdate(files, update);
}
else {
button1.disabled = false;
button1.innerHTML = '检查游戏更新';
button3.disabled = false;
button3.innerHTML = '更新到开发版';
download(description);
}
else refresh();
},
str,
['确定', '取消']
);
}
else {
if (confirm(str)) {
goupdate(files, update);
if (confirm(str + '\n' + str2)) {
download(description);
}
else {
button1.disabled = false;
button1.innerHTML = '检查游戏更新';
button3.disabled = false;
button3.innerHTML = '更新到开发版';
else refresh();
}
})
.catch(e => {
alert('获取更新失败: ' + e);
refresh();
});
} else {
if (confirm('将要下载dev版本的完整包是否继续?')) {
download({
name: 'noname-PR-Branch',
assets: [],
zipball_url: 'https://ghproxy.cc/https://github.com/libccy/noname/archive/PR-Branch.zip'
});
}
}
function download(description) {
const progress = createProgress('正在更新' + description.name, 1, description.name + '.zip');
let unZipProgress;
let url = description.zipball_url;
if (Array.isArray(description.assets) && description.assets.length > 0) {
const coreZipData = description.assets.find(v => v.name == 'noname.core.zip');
if (coreZipData && confirm(`检测到该版本(${description.name})有离线包资源,是否改为下载离线包资源?否则将下载完整包资源`)) {
url = 'https://ghproxy.cc/' + coreZipData.browser_download_url;
}
else {
alert('当前版本已是最新');
button1.disabled = false;
button1.innerHTML = '检查游戏更新';
button3.disabled = false;
button3.innerHTML = '更新到开发版';
}
}, function () {
if (forcecheck === false) {
return;
request(url, (receivedBytes, total, filename) => {
if (typeof filename == 'string') {
progress.setFileName(filename);
}
let received = 0, max = 0;
if (total) {
max = +(total / (1024 * 1024)).toFixed(1)
} else {
max = 1000;
}
received = +(receivedBytes / (1024 * 1024)).toFixed(1);
if (received > max) max = received;
progress.setProgressMax(max);
progress.setProgressValue(received);
}).then(async blob => {
progress.remove();
await import('../../../../../game/jszip.js');
const zip = new window.JSZip().load(await blob.arrayBuffer());
const entries = Object.entries(zip.files);
let root;
const hiddenFileFlags = ['.', '_'];
unZipProgress = createProgress('正在解压' + progress.getFileName(), entries.length);
let i = 0;
for (const [key, value] of entries) {
// 第一个是文件夹的话,就是根文件夹
if (i == 0 && value.dir && !description.name.includes('noname.core.zip')) {
root = key;
}
unZipProgress.setProgressValue(i++);
const fileName = typeof root == 'string' && key.startsWith(root) ? key.replace(root, '') : key;
if (hiddenFileFlags.includes(fileName[0])) continue;
if (value.dir) {
await game.promises.createDir(fileName);
continue;
}
unZipProgress.setFileName(fileName);
const [path, name] = [fileName.split('/').slice(0, -1).join('/'), fileName.split('/').slice(-1).join('/')];
game.print(`${fileName}(${i}/${entries.length})`);
await game.promises.writeFile(value.asArrayBuffer(), path, name)
.catch(async e => {
// 特殊处理
if (name == 'noname-server.exe' && e.message.includes('resource busy or locked') && location.protocol.startsWith('http')) {
if (typeof window.require == 'function' &&
typeof window.process == 'object' &&
typeof window.__dirname == 'string') {
return new Promise((resolve, reject) => {
const cp = require('child_process');
cp.exec(`taskkill /IM noname-server.exe /F`, e => {
if (e) reject(e);
else game.promises.writeFile(value.asArrayBuffer(), path, name).then(() => {
cp.exec(`start /b ${__dirname}\\noname-server.exe -platform=electron`, () => { });
function loadURL() {
let myAbortController = new AbortController();;
let signal = myAbortController.signal;
setTimeout(() => myAbortController.abort(), 2000);
fetch(`http://localhost:8089/app.html`, { signal })
.then(({ ok }) => {
if (ok) resolve(null);
else throw new Error('fetch加载失败');
})
.catch(() => loadURL());
}
loadURL();
}).catch(reject);
});
});
}
} else throw e;
});
}
unZipProgress.remove();
// await import('../../../../../game/update.js');
// if (Array.isArray(window.noname_asset_list)) {
// game.saveConfig('asset_version', window.noname_asset_list[0]);
// delete window.noname_asset_list;
// }
if (confirm('更新完成,是否重启?')) {
game.reload();
}
refresh();
}).catch(e => {
if (progress.parentNode) progress.remove();
if (unZipProgress && unZipProgress.parentNode) unZipProgress.remove();
refresh();
throw e;
});
}
alert('连接失败');
button1.disabled = false;
button1.innerHTML = '检查游戏更新';
button3.disabled = false;
button3.innerHTML = '更新到开发版';
}, true);
}
};
game.checkForAssetUpdate = function (type) {
if (button2.disabled) {
return alert('暂不支持更新素材,请点击检查游戏更新按钮下载完整包');
if (checkAssetButton.disabled) {
return;
}
else if (game.download) {
button2.innerHTML = '正在检查更新';
button2.disabled = true;
checkAssetButton.innerHTML = '正在检查更新';
checkAssetButton.disabled = true;
lib.init.req('game/asset.js', function () {
try {
eval(this.responseText);
@ -455,12 +430,12 @@ export const otherMenu = function (connectMenu) {
game.print(updates);
game.saveConfig('asset_version', asset_version);
alert('素材已是最新');
button2.disabled = false;
button2.innerHTML = '检查素材更新';
checkAssetButton.disabled = false;
checkAssetButton.innerHTML = '检查素材更新';
return;
}
var p = button2.parentNode;
button2.remove();
var p = checkAssetButton.parentNode;
checkAssetButton.remove();
var span = document.createElement('span');
span.style.whiteSpace = 'nowrap';
var n1 = 0;
@ -515,8 +490,8 @@ export const otherMenu = function (connectMenu) {
game.checkFileList(updates, proceed);
}, function () {
alert('连接失败');
button2.disabled = false;
button2.innerHTML = '检查素材更新';
checkAssetButton.disabled = false;
checkAssetButton.innerHTML = '检查素材更新';
}, true);
}
else {
@ -524,19 +499,19 @@ export const otherMenu = function (connectMenu) {
}
};
button1 = document.createElement('button');
button1.innerHTML = '检查游戏更新';
button1.onclick = game.checkForUpdate;
li1.lastChild.appendChild(button1);
checkVersionButton = document.createElement('button');
checkVersionButton.innerHTML = '检查游戏更新';
checkVersionButton.onclick = game.checkForUpdate;
li1.lastChild.appendChild(checkVersionButton);
button3 = document.createElement('button');
button3.innerHTML = '更新到开发版';
button3.style.marginLeft = '5px';
button3.onclick = function () {
checkDevVersionButton = document.createElement('button');
checkDevVersionButton.innerHTML = '更新到开发版';
checkDevVersionButton.style.marginLeft = '5px';
checkDevVersionButton.onclick = function () {
game.checkForUpdate(null, true);
};
// if(lib.config.dev){
// li1.lastChild.appendChild(button3);
// li1.lastChild.appendChild(checkDevVersionButton);
// }
(function () {
@ -572,53 +547,53 @@ export const otherMenu = function (connectMenu) {
ui.updateUpdate();
}());
button4 = document.createElement('button');
button4.innerHTML = '设置更新地址';
button4.onclick = function () {
game.prompt('设置更新地址', function (str) {
if (str) {
game.saveConfig('updateURL', str);
li3.querySelector('span').innerHTML = trimURL(str);
button5.style.display = '';
button6.style.display = 'none';
}
});
};
// button4 = document.createElement('button');
// button4.innerHTML = '设置更新地址';
// button4.onclick = function () {
// game.prompt('设置更新地址', function (str) {
// if (str) {
// game.saveConfig('updateURL', str);
// li3.querySelector('span').innerHTML = trimURL(str);
// button5.style.display = '';
// button6.style.display = 'none';
// }
// });
// };
// li3.lastChild.appendChild(button4);
var button6 = document.createElement('button');
button6.innerHTML = '设为备用镜像';
button6.style.display = 'none';// coding
// var button6 = document.createElement('button');
// button6.innerHTML = '设为备用镜像';
// button6.style.display = 'none';// coding
// button6.style.marginLeft='5px';
button6.onclick = function () {
game.saveConfig('updateURL', lib.mirrorURL);
button5.style.display = '';
button6.style.display = 'none';
li3.querySelector('span').innerHTML = trimURL(lib.mirrorURL);
};
li3.lastChild.appendChild(button6);
// button6.onclick = function () {
// game.saveConfig('updateURL', lib.mirrorURL);
// // button5.style.display = '';
// button6.style.display = 'none';
// li3.querySelector('span').innerHTML = trimURL(lib.mirrorURL);
// };
// li3.lastChild.appendChild(button6);
button5 = document.createElement('button');
button5.innerHTML = '设为默认镜像';
// button5 = document.createElement('button');
// button5.innerHTML = '设为默认镜像';
// button5.style.marginLeft='5px';
button5.onclick = function () {
game.saveConfig('updateURL');
button5.style.display = 'none';
button6.style.display = '';
li3.querySelector('span').innerHTML = trimURL(lib.updateURL);
};
li3.lastChild.appendChild(button5);
if (!lib.config.updateURL) {
button5.style.display = 'none';
}
else {
button6.style.display = 'none';
}
// button5.onclick = function () {
// game.saveConfig('updateURL');
// button5.style.display = 'none';
// button6.style.display = '';
// li3.querySelector('span').innerHTML = trimURL(lib.updateURL);
// };
// li3.lastChild.appendChild(button5);
// if (!lib.config.updateURL) {
// button5.style.display = 'none';
// }
// else {
// button6.style.display = 'none';
// }
button2 = document.createElement('button');
button2.innerHTML = '检查素材更新';
button2.onclick = game.checkForAssetUpdate;
li2.lastChild.appendChild(button2);
checkAssetButton = document.createElement('button');
checkAssetButton.innerHTML = '检查素材更新';
checkAssetButton.onclick = game.checkForAssetUpdate;
li2.lastChild.appendChild(checkAssetButton);
var span1 = ui.create.div('.config.more', '选项 <div>&gt;</div>');
span1.style.fontSize = 'small';
@ -626,39 +601,27 @@ export const otherMenu = function (connectMenu) {
span1.toggle = function () {
if (!this.classList.toggle('on')) {
game.saveConfig('asset_toggle_off', true);
span2.style.display = 'none';
span2_br.style.display = 'none';
span2_check.style.display = 'none';
span3.style.display = 'none';
span3_br.style.display = 'none';
span3_check.style.display = 'none';
span4.style.display = 'none';
span4_br.style.display = 'none';
span4_check.style.display = 'none';
span5.style.display = 'none';
span5_br.style.display = 'none';
span5_check.style.display = 'none';
span6.style.display = 'none';
span6_br.style.display = 'none';
span6_check.style.display = 'none';
[
span2, span2_br, span2_check,
span3, span3_br, span3_check,
span4, span4_br, span4_check,
span5, span5_br, span5_check,
span6, span6_br, span6_check,
].forEach(item => HTMLDivElement.prototype.css.call(item, {
display: 'none'
}));
}
else {
game.saveConfig('asset_toggle_off');
span2.style.display = '';
span2_br.style.display = '';
span2_check.style.display = '';
span3.style.display = '';
span3_br.style.display = '';
span3_check.style.display = '';
span4.style.display = '';
span4_br.style.display = '';
span4_check.style.display = '';
span5.style.display = '';
span5_br.style.display = '';
span5_check.style.display = '';
span6.style.display = '';
span6_br.style.display = '';
span6_check.style.display = '';
[
span2, span2_br, span2_check,
span3, span3_br, span3_check,
span4, span4_br, span4_check,
span5, span5_br, span5_check,
span6, span6_br, span6_check,
].forEach(item => HTMLDivElement.prototype.css.call(item, {
display: ''
}));
}
};
span1.listen(span1.toggle);
@ -752,21 +715,15 @@ export const otherMenu = function (connectMenu) {
};
li2.lastChild.appendChild(span6_check);
span2.style.display = 'none';
span2_br.style.display = 'none';
span2_check.style.display = 'none';
span3.style.display = 'none';
span3_br.style.display = 'none';
span3_check.style.display = 'none';
span4.style.display = 'none';
span4_br.style.display = 'none';
span4_check.style.display = 'none';
span5.style.display = 'none';
span5_br.style.display = 'none';
span5_check.style.display = 'none';
span6.style.display = 'none';
span6_br.style.display = 'none';
span6_check.style.display = 'none';
[
span2, span2_br, span2_check,
span3, span3_br, span3_check,
span4, span4_br, span4_check,
span5, span5_br, span5_check,
span6, span6_br, span6_check,
].forEach(item => HTMLDivElement.prototype.css.call(item, {
display: 'none'
}));
ul.appendChild(li1);
ul.appendChild(li2);