这段时间需要把原有的一个老项目改造成WebGL版本,遇到了一些困难,这里将记录改造的一些情况。
用Node.js来管理游戏内容
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.use(express.static('tttt'));
var server = app.listen(8080, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
下面是webSocket的通信相关。
由于安全性问题,WebGL不支持TCPSocket,但是支持WebSockets;下面是几个socket的写法。
TCP Socket,用node.js运行:
var net = require('net');
var HOST = 'ipaddr';
var PORT = 123;
var b64encode = data => Buffer.from(data).toString('base64');
var b64decode = data => Buffer.from(data, 'base64');
var login1 = "MwAAAAEAAAAHAAAAMTA5NzU5AAIAAAA5ABEAAAA5ZmYzZjA1MzcyOWQ0ZmE0AAEAAAAA";
var login2 = "sgAAACMAAABJAAAAAQYAMTA5NzU5NpZHUC/B+SHjMCVg5U/R0BQzxFqpNRyv0JlnhMMzBXK+x7nT6RiAuuxrijfvanzYHySvXnCPToRHuenl1t/k8iEAAAA0NTY5MjcxRkM2REMyOTJBNUMwNzQxOTcwREMwQjk3OAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
var client = new net.Socket();
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
// Write a message to the socket as soon as the client is connected, the server will receive it as message from the client
client.write(b64decode(login1));
client.write(b64decode(login2));
});
// Add a 'data' event handler for the client socket
// data is what the server sent to this socket
client.on('data', function(data) {
console.log('DATA: ' + b64encode(data));
// Close the client socket completely
client.destroy();
});
// Add a 'close' event handler for the client socket
client.on('close', function() {
console.log('Connection closed');
});
WebSocket, 运行在浏览器里面:
hello websocket!! Please see console to check the result!
<script>
(function(r){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=r()}else if(typeof define==="function"&&define.amd){define([],r)}else{var e;if(typeof window!=="undefined"){e=window}else if(typeof global!=="undefined"){e=global}else if(typeof self!=="undefined"){e=self}else{e=this}e.base64js=r()}})(function(){var r,e,n;return function(){function r(e,n,t){function o(i,a){if(!n[i]){if(!e[i]){var u=typeof require=="function"&&require;if(!a&&u)return u(i,!0);if(f)return f(i,!0);var d=new Error("Cannot find module '"+i+"'");throw d.code="MODULE_NOT_FOUND",d}var c=n[i]={exports:{}};e[i][0].call(c.exports,function(r){var n=e[i][1][r];return o(n?n:r)},c,c.exports,r,e,n,t)}return n[i].exports}var f=typeof require=="function"&&require;for(var i=0;i<t.length;i++)o(t[i]);return o}return r}()({"/":[function(r,e,n){"use strict";n.byteLength=c;n.toByteArray=v;n.fromByteArray=s;var t=[];var o=[];var f=typeof Uint8Array!=="undefined"?Uint8Array:Array;var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for(var a=0,u=i.length;a<u;++a){t[a]=i[a];o[i.charCodeAt(a)]=a}o["-".charCodeAt(0)]=62;o["_".charCodeAt(0)]=63;function d(r){var e=r.length;if(e%4>0){throw new Error("Invalid string. Length must be a multiple of 4")}return r[e-2]==="="?2:r[e-1]==="="?1:0}function c(r){return r.length*3/4-d(r)}function v(r){var e,n,t,i,a;var u=r.length;i=d(r);a=new f(u*3/4-i);n=i>0?u-4:u;var c=0;for(e=0;e<n;e+=4){t=o[r.charCodeAt(e)]<<18|o[r.charCodeAt(e+1)]<<12|o[r.charCodeAt(e+2)]<<6|o[r.charCodeAt(e+3)];a[c++]=t>>16&255;a[c++]=t>>8&255;a[c++]=t&255}if(i===2){t=o[r.charCodeAt(e)]<<2|o[r.charCodeAt(e+1)]>>4;a[c++]=t&255}else if(i===1){t=o[r.charCodeAt(e)]<<10|o[r.charCodeAt(e+1)]<<4|o[r.charCodeAt(e+2)]>>2;a[c++]=t>>8&255;a[c++]=t&255}return a}function l(r){return t[r>>18&63]+t[r>>12&63]+t[r>>6&63]+t[r&63]}function h(r,e,n){var t;var o=[];for(var f=e;f<n;f+=3){t=(r[f]<<16&16711680)+(r[f+1]<<8&65280)+(r[f+2]&255);o.push(l(t))}return o.join("")}function s(r){var e;var n=r.length;var o=n%3;var f="";var i=[];var a=16383;for(var u=0,d=n-o;u<d;u+=a){i.push(h(r,u,u+a>d?d:u+a))}if(o===1){e=r[n-1];f+=t[e>>2];f+=t[e<<4&63];f+="=="}else if(o===2){e=(r[n-2]<<8)+r[n-1];f+=t[e>>10];f+=t[e>>4&63];f+=t[e<<2&63];f+="="}i.push(f);return i.join("")}},{}]},{},[])("/")});
// ===================================================================
var b64encode = data => base64js.fromByteArray(data);
var b64decode = data => base64js.toByteArray(data);
var login1 = "MwAAAAEAAAAHAAAAMTA5NzU5AAIAAAA5ABEAAAA5ZmYzZjA1MzcyOWQ0ZmE0AAEAAAAA";
var login2 = "sgAAACMAAABJAAAAAQYAMTA5NzU5NpZHUC/B+SHjMCVg5U/R0BQzxFqpNRyv0JlnhMMzBXK+x7nT6RiAuuxrijfvanzYHySvXnCPToRHuenl1t/k8iEAAAA0NTY5MjcxRkM2REMyOTJBNUMwNzQxOTcwREMwQjk3OAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
var addr = 'ws://127.0.0.1:811';
var ws = new WebSocket(addr);
ws.onopen = function(){
console.log('Connected to: ' + addr);
console.log('Try to login!!!');
ws.send(b64decode(login1));
ws.send(b64decode(login2));
}
ws.addEventListener("message", function(event) {
console.log('Chrome Received: ' + b64encode(event.data));
alert("Login OK!!");
});
</script>
webSocket Server,用Node.js运行:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 811 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(msg) {
console.log('received: %s', msg);
setInterval(() => {
ws.send(msg);
}, 1500);
});
});
C# 端的TCP socket例子:
string serverIP = "192.168.1.1";
int port = 12345;
Socket clientSocket = null;
private IEnumerator oldSendLogin() {
Debug.Log("try to connect!!");
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSocket.Blocking = true;
var address = new IPEndPoint(IPAddress.Parse(serverIP), port);
IAsyncResult result = clientSocket.BeginConnect(address, null, null);
bool success = result.AsyncWaitHandle.WaitOne(5000);
if (success) {
clientSocket.EndConnect(result);
}
clientSocket.Blocking = false;
yield return StartCoroutine(trySocketSend());
yield return StartCoroutine(trySocketReceive());
}
string login_param1 = "MwAAAAEAAAAHAAAAMTA5NzU5AAIAAAA5ABEAAAA5ZmYzZjA1MzcyOWQ0ZmE0AAEAAAAA";
string login_param2 = "sgAAACMAAABJAAAAAQYAMTA5NzU5NpZHUC/B+SHjMCVg5U/R0BQzxFqpNRyv0JlnhMMzBXK+x7nT6RiAuuxrijfvanzYHySvXnCPToRHuenl1t/k8iEAAAA0NTY5MjcxRkM2REMyOTJBNUMwNzQxOTcwREMwQjk3OAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
private bool isSended = false;
IEnumerator trySocketSend() {
Debug.Log(">>>>>>> try socketSend!!!!");
while (true && !isSended) {
yield return null;
var ret = clientSocket.Poll(0, SelectMode.SelectWrite);
if (ret) {
var buff = Convert.FromBase64String(login_param1);
int n = clientSocket.Send(buff, 0, buff.Length, SocketFlags.None);
Debug.Log("Send Data Byte: " + n);
break;
}
}
while (true && !isSended) {
yield return null;
var ret = clientSocket.Poll(0, SelectMode.SelectWrite);
if (ret) {
var buff = Convert.FromBase64String(login_param2);
int n = clientSocket.Send(buff, 0, buff.Length, SocketFlags.None);
Debug.Log("Send Data Byte: " + n);
break;
}
}
isSended = true;
}
IEnumerator trySocketReceive() {
Debug.Log(">>>>>>> try trySocketReceive!!!!");
while (true) {
yield return null;
var ret = clientSocket.Poll(0, SelectMode.SelectRead);
if (ret) {
var recvStream = new MemoryStream(64 * 1024);
var n = clientSocket.Receive(recvStream.GetBuffer(), (int)recvStream.Position, 64 * 1024, SocketFlags.None);
Debug.Log("Receive Data Byte: " + n);
}
}
}
C#端WebSocket的登陆逻辑,使用插件:Simple Web Sockets for Unity WebGL
public IEnumerator webSocketLogin()
{
var login1 = Convert.FromBase64String(login_param1);
var login2 = Convert.FromBase64String(login_param2);
WebSocket w = new WebSocket(new Uri("ws://192.168.1.1:1234"));
yield return StartCoroutine(w.Connect());
w.Send(login1);
w.Send(login2);
int i = 0;
while (true) {
var reply = w.Recv();
if (reply != null) {
Debug.LogFormat("Received: {0} ==> {1}", reply.Length, Convert.ToBase64String(reply));
// w.SendString("Hi there" + i++);
}
if (w.error != null) {
Debug.LogError("Error: " + w.error);
break;
}
yield return 0;
}
w.Close();
}
一个带CORS和https功能的server
var path = require('path');
var express = require('express');
var fs = require('fs');
var http = require('http');
var https = require('https');
var privateKey = fs.readFileSync('key.pem', 'utf8');
var certificate = fs.readFileSync('cert.pem', 'utf8');
var app = express();
app.get('/', function (req, res) {
res.send('<a href="/index.html">Enter Game</a>');
});
app.use(express.static(
path.join('.', "WebBuild"),
{
setHeaders: (res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Credentials", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT");
res.setHeader("Access-Control-Allow-Headers", "Content-Type,Accept, X-Access-Token, X-Application-Name, X-Request-Sent-Time");
}
}
))
var credentials = {key: privateKey, cert: certificate};
// your express configuration here
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);
httpServer.listen(8080);
httpsServer.listen(8443);
console.log(`Server Started, http on 8080, https on 8443`);
一个webSocket转socket的服务:
const net = require('net');
const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');
// ============ proxy服务器地址 {{{{ ===============
var HOST = 'xxxxxx';
var PORT = 123;
var WS_PORT = 811; // webSocket 的监听端口
var SSL_CERT = "./cert.pem";
var SSL_KEY = "./key.pem";
// ============ }}}} proxy服务器地址 ===============
var port = parseInt(process.argv[2]);
if(port>0) {
WS_PORT = port;
} else {
console.log(`port is not set or not a number, use default ${WS_PORT}`);
}
var b64encode = data => Buffer.from(data).toString('base64');
var b64decode = data => Buffer.from(data, 'base64');
// 日志相关
var curDay = -1;
Log = msg => {
var date = new Date();
var hour = date.getHours();
var min = date.getMinutes();
var sec = date.getSeconds();
var mi = date.getMilliseconds();
var rt = `[${hour >= 10 ? hour : "0" + hour}:${min >= 10 ? min : "0" + min}:${sec >= 10 ? sec : "0" + sec} ${mi >= 100 ? mi : ((mi >= 10 ? "0" : "00") + mi)}]`;
var day = date.getDate();
if(curDay != day) { // 隔天,要显示日期信息
curDay = day;
var year = date.getFullYear();
var month = date.getMonth() + 1;
rt = `************** ${year}-${month >= 10 ? month : "0" + month}-${day >= 10 ? day : "0" + day} *****************\r\n` + rt;
}
console.log(`${rt} ${msg}`);
}
// ============ 下面是TcpSocket,辅助连接Proxy服务器和客户端 ===========
// referTo https://nodejs.org/api/net.html
var TcpSocket = function() {
var _userId = 0;
var _wsocket = null;
var _socket = null;
var _pendingSend = null;
var _attachCnt = 0;
var _alive = false;
// 进行连接
function init() {
_alive = false;
_socket = new net.Socket();
_socket.on("connect", () => {
_alive = true;
if(_pendingSend!=null) { // 处理链接期间到来的数据
while(_pendingSend.length > 0) {
_socket.write(_pendingSend.shift()); // TODO 判断状态,并进行异常处理
}
}
});
_socket.on('data', data => {
if(_wsocket!=null && _wsocket.readyState === WebSocket.OPEN) {
_wsocket.send(data);
}
});
_socket.on('close', had_err => {
_alive = false;
});
_socket.on('error', err => {
console.log("Error :: " + err);
});
};
init();
var _close = () => {
_attachCnt--;
if(_attachCnt<=0) {
if(_socket!=null) {
_socket.end(); // 通知服务器关闭socket
_socket = null;
}
if(_wsocket!=null) {
_wsocket.close();
_wsocket = null;
}
if(tcpDict[_userId]!=null) {
delete tcpDict[_userId];
}
}
};
var _trySend = data => {
if(_socket == null) {
return;
}
if(_alive) {
_socket.write(data); //TODO 进行异常处理
} else {
if(!_socket.connecting) { // 尝试恢复连接
_socket.connect(PORT, HOST);
}
if(_pendingSend == null) {
_pendingSend = [];
}
if(_pendingSend.length < 10) { // 10条之内的消息,做缓存
_pendingSend.push(data);
} else {
console.log("cache is full!!");
}
}
};
return {
close : _close,
send : _trySend,
get uid() {
return _userId;
},
attach: (ws, uid) => {
_attachCnt++;
if(_wsocket!=null) {
_wsocket.terminate();
}
_wsocket = ws;
_userId = uid;
if(_socket == null) {
init();
}
if(!_alive && !_socket.connecting) {
_socket.connect(PORT, HOST);
}
}
};
}
var tcpDict = {};
// ============ 下面是WebSocket,负责和客户端连接 ================
// https://github.com/websockets/ws
var idReg = /\/(\d+)/;
const server = new https.createServer({
cert: fs.readFileSync(SSL_CERT),
key: fs.readFileSync(SSL_KEY)
});
var wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => {
var uid = parseInt(req.url.replace(idReg, "$1"));
Log(`${wss.clients.size} @ <${uid}> ${req.connection.remoteAddress}`); // Connected!
if(uid <= 0) { // uid is not correct!
console.log("userid is not correct!!!");
ws.terminate();
return;
}
var tcp = tcpDict[uid];
if(tcp==null) {
tcp = new TcpSocket();
}
tcpDict[uid] = tcp;
tcp.attach(ws, uid);
ws.on('message', msg => {
tcp.send(msg);
});
ws.on("close", () => {
tcp.close();
Log(`${wss.clients.size} ~ <${tcp.uid}> ${req.connection.remoteAddress}`); // Disconnect
});
});
server.listen(WS_PORT);
Log(`WebSocket Server listen on : ${WS_PORT}`);
===================下面是浏览器中相关编程 ==========================
从网路下载文本的方式
var url = "https://192.168.1.194:8443/StreamingAssets/AssetsBundles/LocalBundleMap.txt";
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
xhr.responseType = 'json';
xhr.onload = function (evt) {
if (xhr.status == 200) {
var json = xhr.response;
if (json) {
console.log(json);
}
} else {
console.error("error:", xhr.status);
}
};
xhr.send();
var url = "https://192.168.1.194:8443/StreamingAssets/AssetsBundles/LocalBundleMap.txt";
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
xhr.responseType = 'blob';
xhr.onload = function (evt) {
if (xhr.status == 200) {
var val = xhr.response;
if (val) {
var reader = new FileReader();
reader.addEventListener("loadend", function() {
console.log(reader.result);
});
reader.readAsText(val); // 读取成text
}
} else {
console.error("Error: ", xhr.status);
}
};
xhr.send();
IndexDB相关操作
参考:https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
// md5 function
!function (n) { "use strict"; function t(n, t) { var r = (65535 & n) + (65535 & t); return (n >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r } function r(n, t) { return n << t | n >>> 32 - t } function e(n, e, o, u, c, f) { return t(r(t(t(e, n), t(u, f)), c), o) } function o(n, t, r, o, u, c, f) { return e(t & r | ~t & o, n, t, u, c, f) } function u(n, t, r, o, u, c, f) { return e(t & o | r & ~o, n, t, u, c, f) } function c(n, t, r, o, u, c, f) { return e(t ^ r ^ o, n, t, u, c, f) } function f(n, t, r, o, u, c, f) { return e(r ^ (t | ~o), n, t, u, c, f) } function i(n, r) { n[r >> 5] |= 128 << r % 32, n[14 + (r + 64 >>> 9 << 4)] = r; var e, i, a, d, h, l = 1732584193, g = -271733879, v = -1732584194, m = 271733878; for (e = 0; e < n.length; e += 16)i = l, a = g, d = v, h = m, g = f(g = f(g = f(g = f(g = c(g = c(g = c(g = c(g = u(g = u(g = u(g = u(g = o(g = o(g = o(g = o(g, v = o(v, m = o(m, l = o(l, g, v, m, n[e], 7, -680876936), g, v, n[e + 1], 12, -389564586), l, g, n[e + 2], 17, 606105819), m, l, n[e + 3], 22, -1044525330), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 4], 7, -176418897), g, v, n[e + 5], 12, 1200080426), l, g, n[e + 6], 17, -1473231341), m, l, n[e + 7], 22, -45705983), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 8], 7, 1770035416), g, v, n[e + 9], 12, -1958414417), l, g, n[e + 10], 17, -42063), m, l, n[e + 11], 22, -1990404162), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 12], 7, 1804603682), g, v, n[e + 13], 12, -40341101), l, g, n[e + 14], 17, -1502002290), m, l, n[e + 15], 22, 1236535329), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 1], 5, -165796510), g, v, n[e + 6], 9, -1069501632), l, g, n[e + 11], 14, 643717713), m, l, n[e], 20, -373897302), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 5], 5, -701558691), g, v, n[e + 10], 9, 38016083), l, g, n[e + 15], 14, -660478335), m, l, n[e + 4], 20, -405537848), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 9], 5, 568446438), g, v, n[e + 14], 9, -1019803690), l, g, n[e + 3], 14, -187363961), m, l, n[e + 8], 20, 1163531501), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 13], 5, -1444681467), g, v, n[e + 2], 9, -51403784), l, g, n[e + 7], 14, 1735328473), m, l, n[e + 12], 20, -1926607734), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 5], 4, -378558), g, v, n[e + 8], 11, -2022574463), l, g, n[e + 11], 16, 1839030562), m, l, n[e + 14], 23, -35309556), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 1], 4, -1530992060), g, v, n[e + 4], 11, 1272893353), l, g, n[e + 7], 16, -155497632), m, l, n[e + 10], 23, -1094730640), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 13], 4, 681279174), g, v, n[e], 11, -358537222), l, g, n[e + 3], 16, -722521979), m, l, n[e + 6], 23, 76029189), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 9], 4, -640364487), g, v, n[e + 12], 11, -421815835), l, g, n[e + 15], 16, 530742520), m, l, n[e + 2], 23, -995338651), v = f(v, m = f(m, l = f(l, g, v, m, n[e], 6, -198630844), g, v, n[e + 7], 10, 1126891415), l, g, n[e + 14], 15, -1416354905), m, l, n[e + 5], 21, -57434055), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 12], 6, 1700485571), g, v, n[e + 3], 10, -1894986606), l, g, n[e + 10], 15, -1051523), m, l, n[e + 1], 21, -2054922799), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 8], 6, 1873313359), g, v, n[e + 15], 10, -30611744), l, g, n[e + 6], 15, -1560198380), m, l, n[e + 13], 21, 1309151649), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 4], 6, -145523070), g, v, n[e + 11], 10, -1120210379), l, g, n[e + 2], 15, 718787259), m, l, n[e + 9], 21, -343485551), l = t(l, i), g = t(g, a), v = t(v, d), m = t(m, h); return [l, g, v, m] } function a(n) { var t, r = "", e = 32 * n.length; for (t = 0; t < e; t += 8)r += String.fromCharCode(n[t >> 5] >>> t % 32 & 255); return r } function d(n) { var t, r = []; for (r[(n.length >> 2) - 1] = void 0, t = 0; t < r.length; t += 1)r[t] = 0; var e = 8 * n.length; for (t = 0; t < e; t += 8)r[t >> 5] |= (255 & n.charCodeAt(t / 8)) << t % 32; return r } function h(n) { return a(i(d(n), 8 * n.length)) } function l(n, t) { var r, e, o = d(n), u = [], c = []; for (u[15] = c[15] = void 0, o.length > 16 && (o = i(o, 8 * n.length)), r = 0; r < 16; r += 1)u[r] = 909522486 ^ o[r], c[r] = 1549556828 ^ o[r]; return e = i(u.concat(d(t)), 512 + 8 * t.length), a(i(c.concat(e), 640)) } function g(n) { var t, r, e = ""; for (r = 0; r < n.length; r += 1)t = n.charCodeAt(r), e += "0123456789abcdef".charAt(t >>> 4 & 15) + "0123456789abcdef".charAt(15 & t); return e } function v(n) { return unescape(encodeURIComponent(n)) } function m(n) { return h(v(n)) } function p(n) { return g(m(n)) } function s(n, t) { return l(v(n), v(t)) } function C(n, t) { return g(s(n, t)) } function A(n, t, r) { return t ? r ? s(t, n) : C(t, n) : r ? m(n) : p(n) } "function" == typeof define && define.amd ? define(function () { return A }) : "object" == typeof module && module.exports ? module.exports = A : n.md5 = A }(this);
// In the following line, you should include the prefixes of implementations you want to test.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
// DON'T use "var indexedDB = ..." if you're not in a function.
// Moreover, you may need references to some window.IDB* objects:
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || { READ_WRITE: "readwrite" }; // This line should only be needed if it is needed to support the object's constants for older browsers
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)
if (!window.indexedDB) {
window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
} else {
console.log("the browser is OK!! --- ");
}
const DB_NAME = '/idbfs';
const DB_STORE = "FILE_DATA";
var db;
var req = window.indexedDB.open(DB_NAME);
var baseDir = `/idbfs/${md5(window.location.href.replace(/(https?.*)\/[^\/]*/, "$1"))}`;
console.log(baseDir);
function bin2Str(array) {
var result = "";
for (var i = 0; i < array.length; ++i) {
result += (String.fromCharCode(array[i]));
}
return result;
}
req.onsuccess = function () {
// Better use "this" than "req" to the result to
// avoid problems with garbage collection
db = this.result;
var fn = `${baseDir}/AssetsBundles/LocalBundleMap.txt`;
console.log("==> " + fn);
db.transaction(DB_STORE).objectStore(DB_STORE).get(fn).onsuccess = evt => {
var obj = evt.target.result;
var d = JSON.parse(bin2Str(obj.contents));
// console.log(JSON.stringify(d));
};
};
req.onerror = evt => {
console.error("Error openDB:", evt.target.errorCode);
};
req.onupgradeneeded = evt => {
console.log("openDb.onupgradeneeded");
// var store = evt.currentTarget.result.createObjectStore(
// DB_STORE, {keyPath: 'id', autoIncrement: true });
// store.createIndex('title', 'title', { unique: false });
};
JavaScript中的协程的用法:
参考:https://x.st/javascript-coroutines/
function coroutine(f) {
var o = f(); // instantiate the coroutine
o.next(); // execute until the first yield
return function(x) {
o.next(x);
}
}
var doJob = coroutine(function*(){
console.log("init");
var x = yield;
console.log("First I got : " + x);
var y = yield;
console.log("Then I got: " + y);
});
doJob('a dog');
doJob('a cat');
JavaScript的枚举:
var JobStatus = Object.freeze({
UnInited:1,
DBReady:2,
BundleMapReady:3,
LocalResReady: 4,
});
Promise的用法 :
function get(url) {
// Return a new promise.
return new Promise(function(resolve, reject) {
// Do the usual XHR stuff
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
// This is called even on 404 etc
// so check the status
if (req.status == 200) {
// Resolve the promise with the response text
resolve(req.response);
}
else {
// Otherwise reject with the status text
// which will hopefully be a meaningful error
reject(Error(req.statusText));
}
};
// Handle network errors
req.onerror = function() {
reject(Error("Network Error"));
};
// Make the request
req.send();
});
}
//Now let's use it:
get('story.json').then(function(response) {
console.log("Success!", response);
}, function(error) {
console.error("Failed!", error);
})
Promise的动态串联:
getJson('story.json').then(function (story) {
addHtmlToPage(story.heading);
return story.chapterUrls.reduce(function (chain, chapterUrl) {
// Once the last chapter's promise is done…
return chain.then(function () {
// …fetch the next chapter
return getJson(chapterUrl);
}).then(function (chapter) {
// and add it to the page
addHtmlToPage(chapter.html);
});
}, Promise.resolve());
}).then(function () {
// And we're all done!
addTextToPage("All done");
}).catch(function (err) {
// Catch any error that happened along the way
addTextToPage("Argh, broken: " + err.message);
}).then(function () {
// Always hide the spinner
document.querySelector('.spinner').style.display = 'none';
});
async函数的基本写法
参考:点击打开链接
// async的写法:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
}
catch (err) {
console.log('fetch failed', err);
}
}
// 相对于的promise的写法
function logFetch(url) {
return fetch(url)
.then(response => response.text())
.then(text => {
console.log(text);
}).catch(err => {
console.error('fetch failed', err);
});
}
批量下载例子:
参考:https://developers.google.com/web/fundamentals/primers/async-functions
In following examples, the URLs are fetched and read in parallel, but the "smart" reduce
bit is replaced with a standard, boring, readable for-loop.
// 方法1:
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map(url => {
return fetch(url).then(response => response.text());
});
// log them in order
textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise)
.then(text => console.log(text));
}, Promise.resolve());
}
// 方法2:
async function logInOrder(urls) {
// fetch all the URLs in parallel
const textPromises = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
// log them in sequence
for (const textPromise of textPromises) {
console.log(await textPromise);
}
}
================
C# 和 JavaScript的交互中的回调函数
参考: emscripten代码, emscripten文档 unity论坛 一个日文的文章 官方解决方案(SendMessage)
C#端:
// C#端对javascript的申明
[DllImport("__Internal")]
public static extern void callFuncStalendp(string strEx, System.Action<System.IntPtr> func);
// 回调函数
[MonoPInvokeCallback(typeof(System.Action))]
public static void callback(System.IntPtr ptr) {
var val = Marshal.PtrToStringAuto(ptr);
Debug.LogError("The function is called!! " + val);
}
// 调用
FacebookHelper.callFuncStalendp("C#端", callback);
jslib端:
var LibraryFackbookHelper = {
callFuncStalendp : function(msgEx, callback) {
var msg = Pointer_stringify(msgEx);
var hello = "hello world! 你好!";
if (callback) {
var stack = Runtime.stackSave();
Runtime.dynCall('vi', callback, [allocate(intArrayFromString(msg + hello), 'i8', ALLOC_STACK)]);
Runtime.stackRestore(stack);
}
}
};
mergeInto(LibraryManager.library, LibraryFackbookHelper);
关于Jslib的注意点,
var LibraryWonHelper = {
$WonCommon: {
str2Ptr : function(str) {
return str && allocate(intArrayFromString(str), 'i8', ALLOC_STACK) || null;
},
invoke: function (callback, methodId, isOK, returnVal) {
var stack = Runtime.stackSave();
Runtime.dynCall('viii', callback, [methodId, isOK, WonCommon.str2Ptr(returnVal)]);
Runtime.stackRestore(stack);
}
},
wbgl_won_login: function (wonId, callback) { // TYPE 0
WonWrapper.login(Pointer_stringify(wonId)).then(function (id, token) {
WonCommon.invoke(callback, 0, 1, JSON.stringify([id, token]));
}).catch(function (err) {
WonCommon.invoke(callback, 0, 0);
console.error(err);
});
},
wbgl_won_token: function () {
return WonCommon.str2Ptr(WonWrapper.getToken());
}
};
autoAddDeps(LibraryWonHelper, '$WonCommon');
mergeInto(LibraryManager.library, LibraryWonHelper);
函数的声明,
1. 都要使用function(...){} 的形式,不能用 (param1, parram2) => {} 这种形式。
2. 导出的函数(在c#中 用[DllImport("__Internal")] 申明的),会在js代码中生成对应的函数(但是函数名前面加了"_")。比如wbgl_won_tokens生成的函数名是 _wbgl_won_tokens。这个代码是生成在mudule中的,在javascript不能够被直接调用。如果想直接调用,在合适的时机,把这些函数绑定到全局变量中。没有被导出的函数,是不会生成js代码的。
关于合适的时机,可以写一个函数,在unity启动的时候调用,并进行代码的注册。
3. jslib中的公用代码,参考上面例子中的 $WonCommon(注意要用autoAddDeps注册一下)
==============
Cookie的辅助类
let CookieHelper = {
setCookie : (cname, cvalue, exdays) => {
let d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
document.cookie = `${cname}=${cvalue};expires=${d.toUTCString()};path=/`;
},
getCookie : cname => {
return document.cookie.replace(new RegExp(`(?:(?:^|.*;\\s*)${cname}\\s*=\\s*([^;]*).*$)|^.*$`, 'i'), "$1");
},
delCookie : cname => {
document.cookie = `${cname}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}
};
=============
异步加载类
let loadScript = (src, id) => {
return new Promise(function (resolve, reject) {
if (document.getElementById(id)) {
reject("already loaded");
} else {
let s = document.createElement('script');
s.id = id;
s.src = src;
s.type = 'text/javascript';
s.onload = resolve;
s.onerror = reject;
document.head.appendChild(s);
}
});
}
===========
单例模式
var UnityLoader = UnityLoader || { .... }