pixelstream-cn
Version:
基于虚幻引擎的像素流平台
363 lines (336 loc) • 13.7 kB
JavaScript
import "./adapter.js";
import run from "./run/index.js"; //引擎运行程序
import actionBase from "./action/base.js";
import action from "./action/index.js"; //引擎动作
import message from "./message.js"; //引擎消息
import player from "./player.js";
import progress from "./progress.js";
import _structure from "./structure/index.js";
import bufferUtil from "./util/buffer.js";
import messageType from "./action/messageType.js";
import gamepad from "./gamepad.js";
/**
* @class pixelStream
* @function constructor 构造函数
* @param config<Array>
* @key keyboard<Array> 允许开放的键盘监听
* @example ["a","s","d","w","A","S","D","W","F1","F2","F3","F4","F5"," "]
* @key idleClose<Object> 鼠标/键盘无操作自动关闭配置 {timeout:Number,callback:Function}
* @param element<DOM> 渲染的DOM容器对象
* @param tools<Boolen> 工具是否出现
* @param baseHost<String> 云端基础地址
*
* @function render 渲染画面
* @param code<String> 渲染编号
* @param screen<String> 画面名称
* @return Promise then {} catch msg<String> resolve以后表示渲染画面成功接入
*
* @function send 发送消息
* @param data<pixelStream.action> 给UE程序发送数据对象 该对象按照pixelStream.action对象提供的标准封装
* @return Promise then {} catch msg<String> resolve为针对当前消息UE程序的回复
*
*
* 继承方式封装动作 pixelStream.action
* class MyAction extends pixelStream.action{
* $action="my_test";
* $data={message:"hello world"}
* }
*
* @example
* import pixelStream from "pixelStream";
* let ps = new pixelStream({element:this.$refs.engine});
* ps.message.responseHandel=(response)=>{ //收到ue4发送过来的数据包 该方法也会接受发送数据包的响应包
* console.log(response);
* }
* ps.message.waitHandel=(num)=>{ //排队事件触发
* console.log("前面还有" + num + "人排队");
* }
* ps.message.errorHandel=(message)=>{ //错误触发
* console.log("错误消息:" + message);
* }
* ps.message.speedHandel=(speed)=>{ //流畅度 数字越小越流畅 0-100
* console.log("当前流畅值:" + speed);
* }
*
* ps.render(code,"first3D").then(_=>{
* console.log("渲染画面成功接入");
* ps.send(new MyAction()).then(data=>{
* //收到针对该消息的响应
* }); //采用继承方式封装动作对象 并且发送
* ps.send(new pixelStream.action("my_test",{message:"hello world"})); //世界实例化pixelStream.action对象并发送
* }).catch(e=>{console.error(e)});
*/
class pixelStream{
constructor(config){
config = config || {};
this.config={
mode:"ratio", //图像尺寸模式 stretch拉伸 ratio等比(默认)
keyboard:[], //允许的键盘
element:null, //渲染的element对象
tools:false, //是否展现工具栏
baseHost:"wss://sws.pixelstream.cn/player",
encodeKey:"CljiLwnyih7POqtNja8KeQV6sq77U9ge",
mouseSimulate:null,
actionTimeout:null,
idleClose:null,
};
this.run=null;
this.rtc=null;
this.player=null;
this.message=new message();
if(config["mouseSimulate"]){ this.config.mouseSimulate=config["mouseSimulate"]; }
if(config["mode"]){ this.config.mode=config["mode"]; }
if(config["encodeKey"]){ this.config.encodeKey = config["encodeKey"]; }
if(typeof(config["keyboard"])!="undefined"){ this.config.keyboard=config["keyboard"]; }
if(config["element"]){ this.config.element=config["element"]; }
if(config["tools"]){ this.config.tools=config["tools"]; }
if(config["baseHost"]){ this.config.baseHost=config["baseHost"]; }
if(config["actionTimeout"]){ this.config.actionTimeout=config["actionTimeout"]; }
setIdleCloseConfig(this,config);
}
setConfig(config){
config = config || {};
if(typeof(config["keyboard"])!="undefined"){
this.config.keyboard=config["keyboard"];
if(this.player){
this.player.setKeyboard(config["keyboard"]);
}
}
if(typeof(config["actionTimeout"])!="undefined"){
this.config.actionTimeout=config["actionTimeout"];
if(isConnected(this)){
this.keepLive();
}else{
clearTimeoutClose(this);
}
}
if(typeof(config["idleClose"])!="undefined" || typeof(config["idleTimeout"])!="undefined"){
setIdleCloseConfig(this,config);
if(isConnected(this)){
keepInputLive(this);
}else{
clearIdleTimeoutClose(this);
}
}
}
render(code,screen){
let _this = this;
let swsUrl = this.config.baseHost+"/"+code+"/"+screen;
let element = this.config.element;
return new Promise((resolve,reject)=>{
if(!element){
reject("渲染对象不存在");
return ;
}
//初始化外部element对象
let outElement = elementHelper.init();
element.append(outElement);
let gamepadObj = new gamepad();
let pro = new progress(outElement);
this.run = new run();
this.run.message(this.message).start(swsUrl,this.config.encodeKey,pro).then(rtc=>{
_this.keepLive();
keepInputLive(_this);
_this.rtc = rtc;
gamepadObj.emitAxisMove = (controllerIndex, axisIndex, analogValue)=>{
_this.send(new action.gamepad.axisMove(controllerIndex, axisIndex, analogValue));
}
gamepadObj.emitButtonPressed = (controllerIndex, buttonIndex, isRepeat)=>{
_this.send(new action.gamepad.ButtonPressed(controllerIndex, buttonIndex, isRepeat));
}
gamepadObj.emitButtonReleased = (controllerIndex, buttonIndex)=>{
_this.send(new action.gamepad.ButtonReleased(controllerIndex, buttonIndex));
}
gamepadObj.init();
_this.rtc.message(_this.message);
_this.player = new player(_this.config.tools,_this.config.keyboard,_this.config.mode,_this.config.mouseSimulate);
_this.player.init(outElement);
_this.player.mouseDownHandel=(button,coord)=>{
sendInput(_this,new action.mouse.down(button,coord));
};
_this.player.mouseMoveHandel=(coord,delta)=>{
sendInput(_this,new action.mouse.move(coord,delta));
};
_this.player.mouseEnterHandel=()=>{
sendInput(_this,new action.mouse.enter());
};
_this.player.mouseLeaveHandel=()=>{
sendInput(_this,new action.mouse.leave());
};
_this.player.mouseUpHandel=(button,coord)=>{
sendInput(_this,new action.mouse.up(button,coord));
};
_this.player.mouseWheelHandel=(coord,delta)=>{
sendInput(_this,new action.mouse.wheel(coord,delta));
};
_this.player.mousePressHandel=(buttons,coord)=>{
if(buttons){
sendInput(_this,new action.mouse.down(0,coord));
sendInput(_this,new action.mouse.down(1,coord));
sendInput(_this,new action.mouse.down(2,coord));
sendInput(_this,new action.mouse.down(3,coord));
sendInput(_this,new action.mouse.down(4,coord));
}
};
_this.player.mouseReleaseHandel=(buttons,coord)=>{
if(buttons){
sendInput(_this,new action.mouse.up(0,coord));
sendInput(_this,new action.mouse.up(1,coord));
sendInput(_this,new action.mouse.up(2,coord));
sendInput(_this,new action.mouse.up(3,coord));
sendInput(_this,new action.mouse.up(4,coord));
}
};
_this.player.touchStartHandel=(touches)=>{
_this.send(new action.touch.start(touches));
}
_this.player.touchEndHandel=(touches)=>{
_this.send(new action.touch.end(touches));
}
_this.player.touchMoveHandel=(touches)=>{
_this.send(new action.touch.move(touches));
}
_this.player.keyboardDownHandel=(e)=>{
sendInput(_this,new action.keyboard.down(e));
};
_this.player.keyboardUpHandel=(e)=>{
sendInput(_this,new action.keyboard.up(e));
};
_this.player.keyboardPressHandel=(e)=>{
sendInput(_this,new action.keyboard.press(e));
};
rtc.setAudioHandel((stream)=>{
_this.player.audio(stream);
});
rtc.setStreamHandel((stream)=>{
pro.over();
_this.player.play(stream);
resolve();
});
}).catch(msg=>{
reject(msg);
});
});
}
close(){
clearTimeoutClose(this);
clearIdleTimeoutClose(this);
if(this.run){
this.run.close();
this.run=null;
}else if(this.rtc && this.rtc.close){
this.rtc.close();
}
this.rtc=null;
}
send(data){
if(!isConnected(this)){
return new Promise((resolve,reject)=>{
reject("还未建立连接,无法发送指令");
});
}
this.keepLive();
return this.rtc.send(data);
}
keepLive(){
clearTimeoutClose(this);
if(this.config.actionTimeout){
this.timeoutClose=setTimeout(this.sendTimeout.bind(this),this.config.actionTimeout*1000);
}
}
sendTimeout(){
this.close();
}
sendUIInteraction(str){
if(!isConnected(this)){
return new Promise((resolve,reject)=>{
reject("还未建立连接,无法发送指令");
});
}
this.keepLive();
let ArrayBuffer = bufferUtil.toBuffer(str,messageType.UIInteraction);
return this.rtc.sendBuffer(ArrayBuffer.buffer);
}
}
const idleCloseTimerStore = new WeakMap();
function sendInput(instance,data){
if(isConnected(instance)){
keepInputLive(instance);
}
return instance.send(data);
}
function isConnected(instance){
return instance.rtc && instance.rtc.pc && instance.rtc.pc.sctp && instance.rtc.pc.sctp.state=="connected";
}
function keepInputLive(instance){
clearIdleTimeoutClose(instance);
let idleClose = instance.config.idleClose;
if(idleClose && idleClose.timeout){
idleCloseTimerStore.set(instance,setTimeout(()=>{
runIdleClose(instance);
},idleClose.timeout*1000));
}
}
function runIdleClose(instance){
clearIdleTimeoutClose(instance);
let idleClose = instance.config.idleClose;
if(idleClose && typeof(idleClose.callback)=="function"){
try {
idleClose.callback.call(instance,instance);
} catch (error) {
console.error(error);
}
}
instance.close();
}
function setIdleCloseConfig(instance,config){
if(typeof(config["idleClose"])!="undefined"){
instance.config.idleClose=normalizeIdleCloseConfig(config["idleClose"]);
return;
}
if(typeof(config["idleTimeout"])!="undefined"){
instance.config.idleClose=normalizeIdleCloseConfig(config["idleTimeout"]);
}
}
function normalizeIdleCloseConfig(config){
if(typeof(config)=="number"){
return {
timeout:config,
callback:null,
};
}
if(config && typeof(config)=="object"){
return {
timeout:config.timeout || null,
callback:typeof(config.callback)=="function" ? config.callback : null,
};
}
return null;
}
function clearTimeoutClose(instance){
if(instance.timeoutClose){
clearTimeout(instance.timeoutClose);
instance.timeoutClose=null;
}
}
function clearIdleTimeoutClose(instance){
let timeoutClose = idleCloseTimerStore.get(instance);
if(timeoutClose){
clearTimeout(timeoutClose);
idleCloseTimerStore.delete(instance);
}
}
pixelStream.action=actionBase;
pixelStream.structure=_structure;
class elementHelper{
static init(){
let div = document.createElement("div");
div.style.width="100%";
div.style.height="100%";
div.style.position="relative";
div.style.outline="none";
div.style.tabindex="0";
return div;
}
}
export default pixelStream;