Initial commit: Client Doc docs Server Tools

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ud18010
2026-07-10 10:24:29 +08:00
co-authored by Cursor
commit 7e35d8da31
3374 changed files with 680813 additions and 0 deletions
@@ -0,0 +1,51 @@
import { WEBAudio, audios } from './store';
import { resumeWebAudio, mkCacheDir } from './utils';
mkCacheDir();
export default {
WXGetAudioCount() {
return {
innerAudio: Object.keys(audios).length,
webAudio: WEBAudio.bufferSourceNodeLength,
buffer: WEBAudio.audioBufferLength,
};
},
};
const HandleInterruption = {
init() {
let INTERRUPT_LIST = {};
wx.onHide(() => {
Object.keys(audios).forEach((key) => {
if (!audios[key].paused !== false) {
INTERRUPT_LIST[key] = true;
}
});
});
wx.onShow(() => {
Object.keys(audios).forEach((key) => {
if (audios[key].paused !== false && INTERRUPT_LIST[key]) {
audios[key].play();
}
});
INTERRUPT_LIST = {};
});
wx.onAudioInterruptionBegin(() => {
Object.keys(audios).forEach((key) => {
if (!audios[key].paused !== false) {
INTERRUPT_LIST[key] = true;
}
});
});
wx.onAudioInterruptionEnd(() => {
Object.keys(audios).forEach((key) => {
if (audios[key].paused !== false && INTERRUPT_LIST[key]) {
audios[key].play();
}
});
INTERRUPT_LIST = {};
resumeWebAudio();
});
},
};
HandleInterruption.init();
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1346b877eca95bc4e981004851766fa3
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
export const INNER_AUDIO_UNDEFINED_MSG = 'InnerAudioContext does not exist!';
export const IGNORE_ERROR_MSG = 'audio is playing, don\'t play again';
export const TEMP_DIR_PATH = `${wx.env.USER_DATA_PATH}/__GAME_FILE_CACHE/audios`;
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 585369bd09f595d4b954f11aad189025
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
import innerAudio from './inner-audio';
import unityAudio from './unity-audio';
import common from './common';
export default {
...innerAudio,
...unityAudio,
...common,
};
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2706efb73cecc9a408b84934894867a3
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,365 @@
import moduleHelper from '../module-helper';
import { isSupportPlayBackRate } from '../../check-version';
import { audios, localAudioMap, downloadingAudioMap } from './store';
import { createInnerAudio, destroyInnerAudio, printErrMsg } from './utils';
import { IGNORE_ERROR_MSG, INNER_AUDIO_UNDEFINED_MSG } from './const';
const funs = {
getFullUrl(v) {
if (!/^https?:\/\//.test(v) && !/^wxfile:\/\//.test(v)) {
const cdnPath = GameGlobal.manager.assetPath;
v = `${cdnPath.replace(/\/$/, '')}/${v.replace(/^\//, '').replace(/^Assets\//, '')}`;
}
return v;
},
downloadAudios(paths) {
const list = paths.split(',');
return Promise.all(list.map((v) => {
const src = funs.getFullUrl(v);
return new Promise((resolve, reject) => {
if (!downloadingAudioMap[src]) {
downloadingAudioMap[src] = [
{
resolve,
reject,
},
];
if (funs.checkLocalFile(src)) {
funs.handleDownloadEnd(src, true);
}
else if (!GameGlobal.unityNamespace.isCacheableFile(src)) {
wx.downloadFile({
url: src,
success(res) {
if (res.statusCode === 200 && res.tempFilePath) {
localAudioMap[src] = res.tempFilePath;
funs.handleDownloadEnd(src, true);
}
else {
funs.handleDownloadEnd(src, false);
}
},
fail(e) {
funs.handleDownloadEnd(src, false);
printErrMsg(e);
},
});
}
else {
const xmlhttp = new GameGlobal.unityNamespace.UnityLoader.UnityCache.XMLHttpRequest();
xmlhttp.open('GET', src, true);
xmlhttp.responseType = 'arraybuffer';
xmlhttp.onsave = () => {
localAudioMap[src] = GameGlobal.manager.getCachePath(src);
funs.handleDownloadEnd(src, true);
};
xmlhttp.onsavefail = () => {
funs.handleDownloadEnd(src, false);
};
xmlhttp.onerror = () => {
funs.handleDownloadEnd(src, false);
};
xmlhttp.send();
}
}
else {
downloadingAudioMap[src].push({
resolve,
reject,
});
}
});
}));
},
handleDownloadEnd(src, succeeded) {
if (!downloadingAudioMap[src]) {
return;
}
while (downloadingAudioMap[src] && downloadingAudioMap[src].length > 0) {
const item = downloadingAudioMap[src].shift();
if (!succeeded) {
item?.reject();
}
else {
item?.resolve('');
}
}
delete downloadingAudioMap[src];
},
// 是否存在本地文件
checkLocalFile(src) {
if (localAudioMap[src]) {
return true;
}
const path = GameGlobal.manager.getCachePath(src);
if (path) {
localAudioMap[src] = path;
return true;
}
return false;
},
// 设置路径
setAudioSrc(audio, getSrc) {
return new Promise((resolve, reject) => {
const src = funs.getFullUrl(getSrc);
// 设置原始路径,后面用此路径作为key值
audio.isLoading = src;
if (funs.checkLocalFile(src)) {
audio.src = localAudioMap[src];
delete audio.isLoading;
funs.handleDownloadEnd(src, true);
resolve(localAudioMap[src]);
}
else if (audio.needDownload) {
funs
.downloadAudios(src)
.then(() => {
if (audio) {
audio.src = localAudioMap[src];
delete audio.isLoading;
resolve(localAudioMap[src]);
}
else {
console.warn('资源已被删除:', src);
reject({
errCode: -1,
errMsg: '资源已被删除',
});
}
})
.catch(() => {
console.warn('资源下载失败:', src);
if (audio) {
audio.src = src;
delete audio.isLoading;
}
reject({
errCode: -1,
errMsg: '资源下载失败',
});
});
}
else {
// 不推荐这样处理,建议优先下载再使用,除非是需要立即播放的长音频文件或一次性播放音频
// console.warn('建议优先下载再使用:', src);
audio.src = src;
delete audio.isLoading;
resolve(src);
}
});
},
};
function checkHasAudio(id) {
if (audios[id]) {
return true;
}
console.error(INNER_AUDIO_UNDEFINED_MSG, id);
return false;
}
export default {
// 创建audio对象
WXCreateInnerAudioContext(src, loop, startTime, autoplay, volume, playbackRate, needDownload) {
const { audio: getAudio, id } = createInnerAudio();
getAudio.needDownload = needDownload;
if (src) {
// 设置原始src
funs.setAudioSrc(getAudio, src).catch((e) => {
moduleHelper.send('OnAudioCallback', JSON.stringify({
callbackId: id,
errMsg: 'onError',
result: JSON.stringify(e),
}));
});
}
if (loop) {
getAudio.loop = true;
}
if (autoplay) {
getAudio.autoplay = true;
}
if (typeof startTime === 'undefined') {
startTime = 0;
}
if (startTime > 0) {
getAudio.startTime = +startTime.toFixed(2);
}
if (typeof volume === 'undefined') {
volume = 1;
}
if (volume !== 1) {
getAudio.volume = +volume.toFixed(2);
}
if (!isSupportPlayBackRate) {
playbackRate = 1;
}
if (typeof playbackRate !== 'undefined' && playbackRate !== 1) {
getAudio.playbackRate = +playbackRate.toFixed(2);
}
return id;
},
WXInnerAudioContextSetBool(id, k, v) {
if (!checkHasAudio(id)) {
return;
}
audios[id][k] = Boolean(+v);
},
WXInnerAudioContextSetString(id, k, v) {
if (!checkHasAudio(id)) {
return;
}
if (k === 'src') {
funs.setAudioSrc(audios[id], v);
}
else if (k === 'needDownload') {
audios[id].needDownload = !!v;
}
else {
audios[id][k] = v;
}
},
WXInnerAudioContextSetFloat(id, k, v) {
if (!checkHasAudio(id)) {
return;
}
audios[id][k] = +v.toFixed(2);
},
WXInnerAudioContextGetFloat(id, k) {
if (!checkHasAudio(id)) {
return 0;
}
return audios[id][k];
},
WXInnerAudioContextGetBool(id, k) {
if (!checkHasAudio(id)) {
return false;
}
return audios[id][k];
},
WXInnerAudioContextPlay(id) {
if (!checkHasAudio(id)) {
return;
}
const url = audios[id].isLoading;
if (url) {
if (downloadingAudioMap[url]) {
downloadingAudioMap[url].push({
resolve: () => {
if (typeof audios[id] !== 'undefined') {
audios[id].play();
}
},
reject: () => { },
});
}
else {
audios[id].src = url;
audios[id].play();
}
}
else {
audios[id].play();
}
},
WXInnerAudioContextPause(id) {
if (!checkHasAudio(id)) {
return;
}
audios[id].pause();
},
WXInnerAudioContextStop(id) {
if (!checkHasAudio(id)) {
return;
}
audios[id].stop();
},
WXInnerAudioContextDestroy(id) {
if (!checkHasAudio(id)) {
return;
}
destroyInnerAudio(id, false);
},
WXInnerAudioContextSeek(id, position) {
if (!checkHasAudio(id)) {
return;
}
audios[id].seek(+position.toFixed(3));
},
WXInnerAudioContextAddListener(id, key) {
if (!checkHasAudio(id)) {
return;
}
if (key === 'onCanplay') {
audios[id][key](() => {
const { duration, buffered, referrerPolicy, volume } = audios[id];
setTimeout(() => {
moduleHelper.send('OnAudioCallback', JSON.stringify({
callbackId: id,
errMsg: key,
}));
}, 0);
});
}
else if (key === 'onError') {
audios[id][key]((e) => {
if (key === 'onError') {
console.error(e);
if (e.errMsg && e.errMsg.indexOf(IGNORE_ERROR_MSG) > -1) {
return;
}
}
moduleHelper.send('OnAudioCallback', JSON.stringify({
callbackId: id,
errMsg: key,
result: JSON.stringify(e),
}));
});
}
else {
audios[id][key](() => {
moduleHelper.send('OnAudioCallback', JSON.stringify({
callbackId: id,
errMsg: key,
}));
});
}
},
WXInnerAudioContextRemoveListener(id, key) {
if (!checkHasAudio(id)) {
return;
}
audios[id][key]();
},
WXPreDownloadAudios(paths, id) {
funs
.downloadAudios(paths)
.then(() => {
moduleHelper.send('WXPreDownloadAudiosCallback', JSON.stringify({
callbackId: id.toString(),
errMsg: '0',
}));
})
.catch(() => {
moduleHelper.send('WXPreDownloadAudiosCallback', JSON.stringify({
callbackId: id.toString(),
errMsg: '1',
}));
});
},
};
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 783d3d4e7ae93344aadefb76a6c5248a
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
export const WEBAudio = {
audioInstanceIdCounter: 0,
audioInstances: {},
audioContext: null,
audioWebEnabled: 0,
audioCache: [],
lOrientation: {
x: 0,
y: 0,
z: 0,
xUp: 0,
yUp: 0,
zUp: 0,
},
lPosition: { x: 0, y: 0, z: 0 },
audio3DSupport: 0,
audioWebSupport: 0,
bufferSourceNodeLength: 0,
audioBufferLength: 0,
};
export const audios = {};
export const localAudioMap = {};
export const downloadingAudioMap = {};
export const soundVolumeHandler = {};
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 89a6a02588fcb274aaa8ddca052949a0
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a0e0368c963c215469b90cd1924ff6bf
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,74 @@
import { uid } from '../utils';
import { isSupportCacheAudio } from '../../check-version';
import { WEBAudio, audios } from './store';
import { TEMP_DIR_PATH } from './const';
export const resumeWebAudio = () => {
if (WEBAudio.audioContext
&& (WEBAudio.audioContext.state === 'suspended' || WEBAudio.audioContext.state === 'interrupted')) {
WEBAudio.audioContext.resume();
}
};
export const createInnerAudio = () => {
const id = uid();
const audio = (isSupportCacheAudio && WEBAudio.audioCache.length ? WEBAudio.audioCache.shift() : wx.createInnerAudioContext());
if (audio) {
audios[id] = audio;
}
return {
id,
audio,
};
};
export const destroyInnerAudio = (id, useCache) => {
if (!id) {
return;
}
if (!useCache || !isSupportCacheAudio || WEBAudio.audioCache.length > 32) {
audios[id].destroy();
}
else {
['Play', 'Pause', 'Stop', 'Canplay', 'Error', 'Ended', 'Waiting', 'Seeking', 'Seeked', 'TimeUpdate'].forEach((eventName) => {
audios[id][`off${eventName}`]();
});
const state = {
startTime: 0,
obeyMuteSwitch: true,
volume: 1,
autoplay: false,
loop: false,
referrerPolicy: '',
};
Object.keys(state).forEach((key) => {
try {
audios[id][key] = state[key];
}
catch (e) { }
});
audios[id].stop();
const cacheAudio = audios[id];
setTimeout(() => {
WEBAudio.audioCache.push(cacheAudio);
}, 1000);
}
delete audios[id];
};
export const printErrMsg = (msg) => {
GameGlobal.manager.printErr(msg);
};
export function mkCacheDir() {
const fs = wx.getFileSystemManager();
fs.rmdir({
dirPath: TEMP_DIR_PATH,
recursive: true,
complete: () => {
fs.mkdir({
dirPath: TEMP_DIR_PATH,
});
},
});
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e2a44d191d5dc284b82ce97ddb30f888
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: