init: 2.B

This commit is contained in:
AncTe committed 2026-02-28 20:23:34 +08:00
commit 6bf32174d8
610 files changed
+151444

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
{
"name": "@motajs/common"
}
+2
View File
@@ -0,0 +1,2 @@
export * from './logger';
export * from './utils';
+99
View File
@@ -0,0 +1,99 @@
{
"error": {
"1": "Unexpected error when posting danmaku. Error info: $1",
"2": "Unexpected loading error in loading resource '$1/$2'. Error info: $3",
"3": "Syntax error in parsing CSS: Unexpected ':'. Col: $1. CSS string: '$2'",
"4": "Syntax error in parsing CSS: Unexpected ';'. Col: $1. CSS string: '$2'",
"5": "Syntax error in parsing CSS: Missing property name after '-'. Col: $1. CSS string: '$2'",
"6": "Syntax error in parsing CSS: Unexpected end of css, expecting ':'. Col: $1. CSS string: '$2'",
"7": "Syntax error in parsing CSS: Unexpected end of css, expecting property value. Col: $1. CSS string: '$2'",
"8": "Post danmaku with not allowed css. Info: $1",
"9": "Cannot initialize shader program. Error info: $1",
"10": "Cannot compile $1 shader. Error info: $2",
"11": "Cache depth cannot larger than 31.",
"12": "Cannot move while status is not 'moving'. Call 'readyMove' first.",
"13": "Cannot compile $1 shader. Error info: $2",
"14": "",
"15": "",
"16": "Cannot find log message for $1 code $2.",
"17": "Cannot use shader program for shader element that does not belong to it.",
"18": "Cannot delete shader program for shader element that does not belong to it.",
"19": "Cannot create MotaRenderer instance for nonexistent canvas.",
"20": "Cannot create render element for tag '$1', since there's no registration for it.",
"21": "Incorrect render prop type is delivered. key: '$1', expected type: '$2', delivered type: '$3'",
"22": "Incorrect props for custom tag. Please ensure you have delivered 'item' prop and other required props.",
"23": "Cannot get reader when fetching '$1'.",
"24": "Cannot decode source type of '$1', since there is no registered decoder for that type.",
"25": "Unknown audio type. Header: '$1'",
"26": "Uncaught error when fetching stream data from '$1'. Error info: $2.",
"1201": "Floor-damage extension needs 'floor-binder' extension as dependency."
},
"warn": {
"1": "Resource with type of 'none' is loaded.",
"2": "Repeat load of resource '$1/$2'.",
"3": "Unknown danmaku tag: $1",
"4": "Ignored a mismatched ']' in danmaku.",
"5": "Repeat post danmaku.",
"6": "Registered special danmaku element: $1.",
"7": "Unknown special danmaku element: '$1'.",
"8": "Incomplete render data is put. None will be filled to the lacked data.",
"9": "Data transfered is partially (or totally) out of range. Overflowed data will be ignored.",
"10": "Cannot resolve big image of enemy '$1'.",
"11": "Cannot resolve material $1. Material not exists.",
"12": "Cannot mark buffable with a non-number status. Key: '$1'.",
"13": "Cannot set buff of non-number status. Key: '$1'.",
"14": "Cannot add status of non-number status. Key: '$1'.",
"15": "Cannot get item of a non-item block on loc: $1,$2,$3.",
"16": "Override repeated state key: '$1'.",
"17": "Floor-damage extension needs 'floor-binder' extension as dependency.",
"18": "Uncaught error in posting like info for danmaku. Danmaku id: $1.",
"19": "Repeat light id: '$1'.",
"20": "Cannot apply animation to camera operation that does not belong to it.",
"21": "Cannot apply transition to camera operation that does not belong to it.",
"22": "There is already an active camera for delivered render item. Consider using 'Camera.for' or disable the active camera to avoid some exceptions.",
"23": "Render item with id of '$1' has already exists. Please avoid repeat id since it may cause issues when use 'getElementById'.",
"24": "Uniform block can only be used in glsl version es 300.",
"25": "Cannot activate weather since there's no weather named '$1'.",
"26": "Cannot set attribute when only element number specified. Use 'pointer' or 'pointerI' instead.",
"27": "Cannot vertex attribute integer point when specified as float. Use 'set' or 'pointer' instead.",
"28": "Redefinition of shader $1: '$2'",
"29": "Cannot define new texture since texture index is larger than max texture count.",
"30": "Cannot use indices named $1 since no definition for it. Please define it in advance.",
"31": "Cannot use indices since the indices instance is not belong to the program.",
"32": "Sub-image exceeds texture dimensions, auto adjusting size.",
"33": "Cannot modify MotaOffscreenCanvas2D that is freezed.",
"34": "Repeated render tag registration: '$1'.",
"35": "Cannot append child on plain render item, please ensure you have overrided 'appendChild' method in your own element.",
"36": "Cannot remove child on plain render item, please ensure you have overrided 'removeChild' method in your own element.",
"37": "Cannot execute 'requestSort' on plain render item, please ensure you have overrided 'requestSort' method in your own element.",
"38": "Using plain text in jsx is strongly not recommended, since you can hardly modify its attributes. Consider using Text element instead.",
"39": "Plain text is not supported outside Text element.",
"40": "Cannot return canvas that is not provided by this pool.",
"41": "Width of text content components must be positive. receive: $1",
"42": "Repeated Textbox id: '$1'.",
"43": "Cannot set icon of '$1', since it does not exists. Please ensure you have delivered correct icon id or number.",
"44": "Unexpected end when loading stream audio, reason: '$1'",
"45": "Audio route with id of '$1' has already existed. New route will override old route.",
"46": "Cannot pipe new StreamReader object when stream is loading.",
"47": "Audio stream decoder for audio type '$1' has already existed.",
"48": "Sample rate in stream audio must be constant.",
"49": "Repeated patch for '$1', key: '$2'.",
"50": "Unknown audio extension name: '$1'",
"51": "Cannot decode sound '$1', since audio file may not supported by 2.b.",
"52": "Cannot play sound '$1', since there is no added data named it.",
"53": "Cannot $1 audio route '$2', since there is not added route named it.",
"54": "Missing start tag for '$1', index: $2.",
"55": "Unchildable tag '$1' should follow with param.",
"56": "Method '$1' has been deprecated. Consider using '$2' instead.",
"57": "Repeated UI controller id '$1'.",
"58": "Fail to set ellipse round rect, since length of 'ellipse' property should only be 2, 4, 6 or 8. delivered: $1",
"59": "Unknown icon '$1' in parsing text content.",
"60": "Repeated Tip id: '$1'.",
"61": "Unexpected recursive call of $1.update?$2 in render function. Please ensure you have to do this, if you do, ignore this warn.",
"62": "Recursive fallback fonts in '$1'.",
"63": "Uncaught promise error in waiting box component. Error reason: $1",
"64": "Text node type and block type mismatch: '$1' vs '$2'",
"65": "Cannot bind a weather controller twice.",
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency."
}
}
+215
View File
@@ -0,0 +1,215 @@
import { debounce } from 'lodash-es';
import logInfo from './logger.json';
export const enum LogLevel {
/** 输出所有,包括日志 */
LOG,
/** 报错、严重警告和警告 */
WARNING,
/** 仅报错 */
ERROR
}
interface LoggerCatchInfo {
code?: number;
level: LogLevel;
message: string;
}
interface LoggerCatchReturns<T> {
ret: T;
info: LoggerCatchInfo[];
}
let logTip: HTMLSpanElement;
if (!main.replayChecking) {
const tip = document.createElement('span');
logTip = tip;
tip.style.position = 'fixed';
tip.style.right = '0';
tip.style.bottom = '0';
tip.style.height = '20px';
tip.style.width = 'auto';
tip.style.textAlign = 'right';
tip.style.padding = '0 5px';
tip.style.fontSize = '16px';
tip.style.fontFamily = 'Arial';
tip.style.display = 'none';
tip.style.margin = '2px';
document.body.appendChild(tip);
}
const hideTipText = debounce(() => {
if (main.replayChecking) return;
logTip.style.display = 'none';
}, 5000);
const nums = new Set('1234567890');
export interface ILoggerInfo {
error: Record<number, string>;
warn: Record<number, string>;
}
export class Logger {
enabled: boolean = true;
private catching: boolean = false;
private catchedInfo: LoggerCatchInfo[] = [];
private catchStack: LoggerCatchInfo[][] = [];
constructor(
public readonly level: LogLevel,
public readonly info: ILoggerInfo
) {}
private parseInfo(text: string, ...params: string[]) {
let pointer = -1;
let str = '';
let inParam = false;
let paramNum = '';
while (++pointer < text.length) {
const char = text[pointer];
if (char === '$' && text[pointer - 1] !== '\\') {
inParam = true;
continue;
}
if (inParam) {
if (nums.has(char)) {
paramNum += char;
}
if (!nums.has(text[pointer + 1])) {
inParam = false;
const num = Number(paramNum);
paramNum = '';
str += params[num - 1] ?? '[not delivered]';
}
continue;
}
str += char;
}
return str;
}
/**
* 输出报错信息
* @param code 错误代码,每个错误都应当使用唯一的错误代码
* @param params 参数
*/
error(code: number, ...params: string[]) {
const info = this.info.error[code];
if (!info) {
logger.error(16, 'error', code.toString());
return;
}
const text = this.parseInfo(info, ...params);
if (this.catching) {
this.catchedInfo.push({
level: LogLevel.ERROR,
message: text,
code
});
}
if (this.level <= LogLevel.ERROR && this.enabled) {
if (!main.replayChecking) {
logTip.style.color = 'lightcoral';
logTip.style.display = 'block';
logTip.textContent = `Error thrown, please check in console.`;
hideTipText();
}
const n = Math.floor(code / 50) + 1;
const n2 = code % 50;
const url = `${location.origin}/_docs/logger/error/error${n}.html#error-code-${n2}`;
console.error(`[ERROR Code ${code}] ${text} See ${url}`);
}
}
/**
* 输出警告信息
* @param code 警告代码
* @param text 警告信息
*/
warn(code: number, ...params: string[]) {
const info = this.info.warn[code];
if (!info) {
logger.error(16, 'warn', code.toString());
return;
}
const text = this.parseInfo(info, ...params);
if (this.catching) {
this.catchedInfo.push({
level: LogLevel.ERROR,
message: text,
code
});
}
if (this.level <= LogLevel.WARNING && this.enabled) {
if (!main.replayChecking) {
logTip.style.color = 'gold';
logTip.style.display = 'block';
logTip.textContent = `Warning thrown, please check in console.`;
hideTipText();
}
const n = Math.floor(code / 50) + 1;
const n2 = code % 50;
const url = `${location.origin}/_docs/logger/warn/warn${n}.html#warn-code-${n2}`;
console.warn(`[WARNING Code ${code}] ${text} See ${url}`);
}
}
/**
* 输出日志
* @param text 日志信息
*/
log(text: string) {
if (this.catching) {
this.catchedInfo.push({
level: LogLevel.ERROR,
message: text
});
}
if (this.level <= LogLevel.LOG && this.enabled) {
console.log(`[LOG] ${text}`);
}
}
catch<T>(fn: () => T): LoggerCatchReturns<T> {
const before = this.enabled;
this.catchedInfo = [];
this.catchStack.push(this.catchedInfo);
this.disable();
this.catching = true;
const ret = fn();
if (this.catchStack.length === 0) {
this.catching = false;
} else {
this.catching = true;
}
if (before) this.enable();
this.catchStack.pop();
const last = this.catchStack?.at(-1);
const info = this.catchedInfo;
this.catchedInfo = last ?? [];
return { ret, info };
}
disable() {
this.enabled = false;
}
enable() {
this.enabled = true;
}
}
export const logger = new Logger(LogLevel.LOG, logInfo);
+3
View File
@@ -0,0 +1,3 @@
export function sleep(time: number) {
return new Promise(res => setTimeout(res, time));
}