import Actions from "./enum/Actions.js" import {eventsManager} from "./EventsManager.js" import util from "./util.js" import Person from "./enum/Person.js" import ClickType from "./enum/ClickType.js" import Logger from "./Logger.js" import Response from "./Response.js" import { VDecoder } from "./h264Decoder/VDecoder.js"; import { SrsRTC } from "./srsRtc.js"; import { Connection } from "./connection.js"; const ossVideoDir = "https://laser-data.oss-cn-shenzhen.aliyuncs.com/new-video/720p/" const walkVideoFrame = 29 window.currentFrame = null const logger = new Logger('actions-handler') const QueueActions = [Actions.Transfer, Actions.ChangeSkin, Actions.GetOnVehicle, Actions.GetOffVehicle]; export default class ActionsHandler { constructor(e) { this.currentActiveAction = null this.room = e //xst window.workerReady = false this.vDecoder = new VDecoder({ maxChip: 3, }); window.vDecoder = this.vDecoder this.init() } init(){ this.vDecoder.on('ready', () => { console.log("ready"); window.workerReady = true }) this.initSocket() } initSocket() { // const sdk = new SrsRTC(); const testConfig = { sceneCode: "testApp", userId: "testUser", roomId: "8888", }; const connection = new Connection(testConfig); // console.log("sdk", sdk); // console.log("sdk", connection); // const url = "webrtc://demo-kms.4dage.com/live/test1"; // sdk.start(url); window.connection = connection window.connection.socket.on("getRoute", (pathArr) => { console.log("getRoute", pathArr); if(!pathArr) return let {anglePlus, dircNum} = window.room.sceneManager.correctCameraDirec() pathArr.forEach(point => { let ue4Pos = util.xversePosition2Ue4({x: -point.location.x, y: point.location.y, z: point.location.z}) point.location = new BABYLON.Vector3(ue4Pos.x, ue4Pos.y, ue4Pos.z) }) const walkSpeed = 1 // m/s const frameRate = 29 // fps let walkPoints = [] let sumWalkFrame = 0 let videoArr = [] for(let i = 0; i < pathArr.length - 1; i++) { // 目前视频返回帧数到159条会停止 if(sumWalkFrame + walkFrame > 159) break let point0 = new BABYLON.Vector3(pathArr[i].location.x, pathArr[i].location.y, pathArr[i].location.z) let point1 = new BABYLON.Vector3(pathArr[i+1].location.x, pathArr[i+1].location.y, pathArr[i+1].location.z) let distanceVec = point1.clone().subtract(point0) let distance = distanceVec.length() / 100 // m let distancePerFrame = walkSpeed / frameRate // m/帧 // walkVideoFrame let walkFrame = Math.round(distance / distancePerFrame) // 帧 let dir = distanceVec.normalize() videoArr.push( { path: ossVideoDir + pathArr[i].id + "/" + pathArr[i].id + "_" + pathArr[i+1].id + "_" + dircNum, frame: walkFrame }) sumWalkFrame += walkFrame for(let currentFrame = 1; currentFrame <= walkFrame; currentFrame++) { walkPoints.push(point0.add(dir.scale(distancePerFrame * 100 * currentFrame))) // 单位转换成cm } } console.log("[Walk]::path", walkPoints) window.walkSettings = { "frameNum": sumWalkFrame, "walkPoints": walkPoints, } window.walking = true this.mutiFetchData({ type: 'move', videoDataArr: videoArr }) }); } async avatarComponentsSync(e){ const t = { action_type: Actions.SetPlayerState, set_player_state_action: { player_state: { avatar_components: JSON.stringify(e) } } }; this.sendData({ data: t }) } async sendData(e) { /* //console.log('发送数据:'+JSON.stringify(e)) await this.beforeSend(e); const t = util.uuid(); this.room.networkController.sendRtcData(le(oe({}, e.data), { trace_id: t, user_id: this.room.options.userId })); if (e.track === !1) { return Promise.resolve(null); } const {sampleRate: r=1, timeout: n=2e3, tag: o, data: a, special: s} = e; return eventsManager.track({ timeout: n, traceId: t, event: a.action_type, tag: o, extra: a }, { special: s, sampleRate: r, noReport: this.room.viewMode === "serverless" || this.room.options.viewMode === "serverless" }).finally(()=>{ QueueActions.includes(e.data.action_type) && (this.currentActiveAction = void 0) } ) */ } //async sendData(e) { //console.log('发送数据:'+JSON.stringify(e)) //旋转 //window.room.sceneManager.scene.activeCamera.rotation.y //心跳:要去掉 //邻居点:要去掉 //漫游 //人物所在位置和角度,相机的位置和角度,相机的目标点,点击的坐标 //} async beforeSend(e) { var o; const t = (o = this.room._userAvatar) == null ? void 0 : o.isMoving , r = e.data.action_type; if (QueueActions.includes(r)) { if (this.currentActiveAction) return logger.error(`${Actions[this.currentActiveAction]} still pending, reject ${Actions[r]}`), Promise.reject(new FrequencyLimitError(`${Actions[r]} action request frequency limit`)); this.currentActiveAction = r } if (t && QueueActions.includes(e.data.action_type)) try { await this.stopMoving() } catch (a) { this.currentActiveAction = void 0, logger.error("before action stopMoving failed", a) } } async moveTo(e) { // 镜头矫正 let {anglePlus, dircNum} = window.room.sceneManager.correctCameraDirec() let anglePlusFrame = Math.floor(anglePlus / (Math.PI/30)) anglePlus = anglePlusFrame * (Math.PI/30) if(Math.abs(anglePlus) > 0.0001) { window.moveCallBack = { func: this.moveTo.bind(this), args: [e] } window.rotateCallBack = { func: this.rotate.bind(this), args: [{type: 'rotate', angle: Math.PI/30 * Math.sign(anglePlus)}], frame: anglePlusFrame, currentFrame: 0, } this.rotate({type: 'rotate', angle: Math.PI/30 * Math.sign(anglePlus)}) return } const {point: t, extra: r="", motionType: n} = e , o = { action_type: Actions.Clicking, clicking_action: { clicking_point: t, clicking_type: ClickType.IgnoreView, extra: encodeURIComponent(r), attitude: n }, clicking_state: this.room._currentClickingState }; let player = window.room.sceneManager.avatarComponent._mainUser let closestPoint = window.room.sceneManager.getClosestPointData(t) let closestPointSelf = window.room.sceneManager.getClosestPointData(player.position) console.log("[Walk]::from", closestPointSelf.position0, ":to", closestPoint.position0) window.connection.socket.emit("getRoute", { sceneCode: "testApp", userId: "testUser", roomId: "8888", s_location: { x: closestPointSelf.position0.x, y: closestPointSelf.position0.y, z: closestPointSelf.position0.z, }, e_location: { x: closestPoint.position0.x, y: closestPoint.position0.y, z: closestPoint.position0.z, }, }); // if(this.walkHandle) clearInterval(this.walkHandle) // // window.room.moveTo(t) // let currentFrame = 0 // this.walkHandle = setInterval(() => { // currentFrame++ // let targetPos = { // "x": player.position.x + distanceVec.x / frameNum, // "y": player.position.y + distanceVec.y / frameNum, // "z": player.position.z + distanceVec.z / frameNum // } // if(currentFrame < frameNum) { // window.room.moveTo({ // position: targetPos, // isEnd: false // }) // } else { // window.room.moveTo({ // position: targetPos, // isEnd: true // }) // clearInterval(this.walkHandle) // } // }, 1000 / fps) // let date = new Date() // console.error(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()) // console.error(closestPointSelf.id) // return this.sendData({ // data: o // }) } transfer(e) { const {renderType: t, player: r, camera: n, areaName: o, attitude: a, pathName: s, person: l, noMedia: u, timeout: c, tag: h, special: f} = e , d = { data: { action_type: Actions.Transfer, transfer_action: { render_type: t, player: r, camera: n, areaName: o, attitude: a, pathName: s, person: { type: l }, noMedia: u, tiles: [0, 1, 2, 4] } }, special: f, timeout: c || 4e3, tag: h }; return this.sendData(d).then(_=>(typeof l != "undefined" && this.room.updateCurrentNetworkOptions({ person: l, rotationRenderType: t }), _)) } changeRotationRenderType(e) { const {renderType: t, player: r, camera: n, areaName: o, attitude: a, pathName: s} = e; return this.transfer({ renderType: t, player: r, camera: n, areaName: o, attitude: a, pathName: s, tag: "changeToRotationVideo" }) } requestPanorama(e, noMedia, timeout) { const {camera: camera, player: player, areaName: areaName, attitude: attitude, pathName: pathName, tag: tag} = e; return this.transfer({ renderType: RenderType.ClientRotationPano, player: player, camera: camera, person: Person.First, areaName: areaName, attitude: attitude, pathName: pathName, noMedia: noMedia, timeout: timeout, tag: tag || "requestPanorama", special: !noMedia }) } setMotionType(e) { return this.transfer({ attitude: e, tag: "setMotionType" }) } setNickName(e) { const t = { action_type: Actions.ChangeNickname, change_nickname_action: { nickname: e } }; return this.sendData({ data: t }) } getReserveSeat({routeId: e, name: t}) { const r = { action_type: Actions.ReserveSeat, reserve_seat_action: { route_id: e, name: t } }; return this.sendData({ data: r }) } getReserveStatus({routeId: e, name: t, need_detail: r}) { const n = { action_type: Actions.GetReserveStatus, get_reserve_status_action: { route_id: e, name: t, need_detail: r } }; return this.sendData({ data: n, timeout: 2e3 }).then(o=>o.reserveDetail) } stopMoving() { const e = { action_type: Actions.StopMoving, stop_move_action: {} }; return this.sendData({ data: e }) } getOnVehicle({routeId: e, name: t, camera: r}) { const n = { action_type: Actions.GetOnVehicle, get_on_vehicle_action: { route_id: e, name: t, camera: r } }; return this.sendData({ data: n }) } getOffVehicle({renderType: e, player: t, camera: r}) { const n = { action_type: Actions.GetOffVehicle, get_off_vehicle_action: { render_type: e, player: t, camera: r } }; return this.sendData({ data: n }) } confirmEvent(e) { const t = { action_type: Actions.ConfirmEvent, confirm_event_action: { id: e } }; return this.sendData({ data: t, track: !1 }) } echo(e) { const t = { action_type: Actions.Echo, echo_msg: { echoMsg: e } }; return this.sendData({ data: t, track: !1 }) } async changeSkin(e) { const t = e.special === void 0 ? e.renderType === RenderType.ClientRotationPano : e.special , {skinId: r, mode: n, landingType: o=LandingType.Stay, landingPoint: a, landingCamera: s, renderType: l, areaName: u, attitude: c, pathName: h, person: f, noMedia: d, timeout: _, roomTypeId: g=""} = e , m = this.room.skinList.filter(y=>y.id === r)[0]; if (!m) { const y = `skin ${r} is invalid`; return logger.error(y), Promise.reject(new ParamError(y)) } const v = { action_type: Actions.ChangeSkin, change_skin_action: { skinID: r, mode: n === ChangeMode.Preview ? ChangeMode.Preview : ChangeMode.Confirm, skin_data_version: r + m.versionId, landing_type: o, landing_point: a, landing_camera: s, render_wrapper: { render_type: l }, areaName: u, attitude: c, noMedia: d, person: f, pathName: h, roomTypeId: g } }; return this.sendData({ data: v, timeout: _ || 6e3, special: t }).then(async y=>{ if (l === RenderType.ClientRotationPano && y) { const b = await this.room.modelManager.findRoute(r, h) , {camera: T} = util.getRandomItem(b.birthPointList) || {}; await this.room.panorama.handleReceivePanorama(y, T) } return this.handleChangeSkin(e) } ).catch(y=>d ? this.handleChangeSkin(e) : Promise.reject(y)) } handleChangeSkin(e) { const {skinId: t, mode: r, renderType: n, areaName: o, attitude: a, pathName: s} = e; return this.room.sceneManager.staticmeshComponent.getCgMesh().show(), this.room.sceneManager.cameraComponent.switchToCgCamera(), this.room.engineProxy._updateSkinAssets(t).then(()=>{ this.room.sceneManager.staticmeshComponent.getCgMesh().hide(), this.room.sceneManager.cameraComponent.switchToMainCamera(), this.room.pathManager.currentArea = o, logger.info("changeSkin _updateSkinAssets susccss"), this.room.updateCurrentNetworkOptions({ pathName: s, attitude: a, areaName: o }), this.room.skinChangedHook(), this.room.emit("skinChanged", { skin: { id: t }, mode: r }), n === RenderType.ClientRotationPano && this.room.sceneManager.cameraComponent.allowMainCameraController() } ) } /***********************************************************************xst****************************************************************/ /* rotate({pitch: e, yaw: t}) { var n; if (this.room.disableRotate || this.room.isPano || ((n = this.room._userAvatar) == null ? void 0 : n._isChangingComponentsMode)) return; const r = { action_type: Actions.Rotation, rotation_action: { vertical_move: e, horizontal_move: -t } }; //console.log('发送数据rotate:'+JSON.stringify(r.rotation_action)) this.sendData({ data: r, sampleRate: .02 }) } */ //zeg rotate({type: type, angle: angle}) { //camera的position,angle //起始和结束的帧 let endRotation = util.xverseRotation2Ue4(window.room.sceneManager.cameraComponent.mainCamera.rotation).yaw endRotation = (endRotation % 360 + 360) % 360 // todo 矫正 let sfns = Math.round((endRotation + 180)/6) // 仅视频贴图偏移矫正180度 endRotation += angle/Math.PI*180 endRotation = (endRotation % 360 + 360) % 360 let efns = Math.round((endRotation + 180)/6) if(window.currentFrame == null){ window.currentFrame = efns } else if(window.currentFrame != efns){ window.currentFrame = efns } else if(window.currentFrame == efns){ return } // 起始帧不需要传入 if(efns>sfns) sfns = (sfns + 1 + 60) % 60 if(efns2*Math.PI){ endRotation -= 2*Math.PI } let sfns = Math.round(endRotation/Math.PI * 180 /6) //每6°一帧 for(let i=0;i<60;++i){ if(Math.abs(Math.abs(angle) - i/60 * 2 * Math.PI)<0.01){ //找到了 endRotation += angle break; } } let efns = Math.round(endRotation/Math.PI * 180 /6) console.log('取帧:'+sfns+','+efns); this.fetchData({ type:type, sFrame:sfns, eFrame:efns }) this.room.sceneManager.materialComponent.initreceveFrames() // this.room.sceneManager.cameraComponent.mainCamera.rotation.y = endRotation // window.room.sceneManager.cameraComponent._cameraPose.rotation._y = endRotation // window.room.sceneManager.cameraComponent.mainCamera.rotation.y=endRotation let response = JSON.parse(JSON.stringify(Response)) response.signal.newUserStates[0].playerState.player.position.x = this.room.avatarManager.avatars.get(nickname).position.x response.signal.newUserStates[0].playerState.player.position.y = this.room.avatarManager.avatars.get(nickname).position.y response.signal.newUserStates[0].playerState.player.position.z = this.room.avatarManager.avatars.get(nickname).position.z response.signal.newUserStates[0].playerState.player.angle.pitch = this.room.avatarManager.avatars.get(nickname).rotation.pitch response.signal.newUserStates[0].playerState.player.angle.yaw = this.room.avatarManager.avatars.get(nickname).rotation.yaw response.signal.newUserStates[0].playerState.player.angle.roll = this.room.avatarManager.avatars.get(nickname).rotation.roll response.signal.newUserStates[0].playerState.camera.position = response.signal.newUserStates[0].playerState.camera.angle = response.signal.newUserStates[0].playerState.cameraCenter.x = this.room.avatarManager.avatars.get(nickname).position.x response.signal.newUserStates[0].playerState.cameraCenter.y = this.room.avatarManager.avatars.get(nickname).position.y response.signal.newUserStates[0].playerState.cameraCenter.z = this.room.avatarManager.avatars.get(nickname).position.z } */ fetchData({type, path, sFrame: sfns, eFrame: efns}){ // alert(path) if(window.workerReady){ this.vDecoder.fetch({ // path: "https://laser-data.oss-cn-shenzhen.aliyuncs.com/test-video/res/720p", path: path, // range: [sfns, efns], range: sfns == efns ? [efns] : [sfns, efns], }); } else{ console.error('还没准备好') } } mutiFetchData({type, videoDataArr}){ console.log("[Walk]::videoData", videoDataArr) if(window.workerReady){ // https://laser-data.oss-cn-shenzhen.aliyuncs.com/test-video/optest/0/0_1_0/ let sendData = [] // let videoPathArr = [ // "https://laser-data.oss-cn-shenzhen.aliyuncs.com/test-video/1011", // "https://laser-data.oss-cn-shenzhen.aliyuncs.com/test-video/1", // ] videoDataArr.forEach(data => { for(let i = 0; i < data.frame; i++) { sendData.push({ path: data.path, frame: i, }) } }) this.vDecoder.mutiFetch(sendData); } else{ console.error('还没准备好') } } /*******************************************************************************************************************************************/ turnTo(e) { const {point: t, timeout: r=2e3, offset: n=8} = e || {} , o = { action_type: Actions.TurnTo, turn_to_action: { turn_to_point: t, offset: n } }; return this.sendData({ data: o, timeout: r }) } rotateTo(e) { const {point: t, offset: r=0, speed: n=3} = e || {} , o = { action_type: Actions.RotateTo, rotate_to_action: { rotate_to_point: t, offset: r, speed: n } }; console.log('发送数据rotateTo:'+JSON.stringify(o.rotate_to_action)) return this.sendData({ data: o }) } broadcast(e) { const {data: t, msgType: r=MessageHandleType.MHT_FollowListMulticast, targetUserIds: n} = e; if (r === MessageHandleType.MHT_CustomTargetSync && !Array.isArray(n)) return Promise.reject(new ParamError(`param targetUserIds is required when msgType is ${MessageHandleType[r]}`)); const o = { action_type: Actions.Broadcast, broadcast_action: { data: JSON.stringify(t), user_id: this.room.options.userId, msgType: r } }; return Array.isArray(n) && r === MessageHandleType.MHT_CustomTargetSync && (o.broadcast_action.target_user_ids = n), this.room.actionsHandler.sendData({ data: o, tag: t.broadcastType }) } getNeighborPoints(e) { const {point: t, containSelf: r=!1, searchRange: n=500} = e , o = { action_type: Actions.GetNeighborPoints, get_neighbor_points_action: { point: t, level: 1, containSelf: r, searchRange: n } }; return this.sendData({ data: o }).then(a=>a.nps) } playCG(e) { const t = { action_type: Actions.PlayCG, play_cg_action: { cg_name: e } }; return this.sendData({ data: t }) } audienceToVisitor(e) { const {avatarId: t, avatarComponents: r, player: n, camera: o} = e , a = { action_type: Actions.AudienceChangeToVisitor, audienceChangeToVisitorAction: { avatarID: t, avatarComponents: r, player: n, camera: o } }; return logger.debug("send data: audience to visitor"), this.sendData({ data: a }) } visitorToAudience(e) { const {renderType: t, player: r, camera: n, areaName: o, attitude: a, pathName: s, person: l, noMedia: u} = e , c = { action_type: Actions.VisitorChangeToAudience, visitorChangeToAudienceAction: { transferAction: { render_type: t, player: r, camera: n, areaName: o, attitude: a, pathName: s, person: { type: l }, noMedia: u, tiles: [0, 1, 2, 4] } } }; return logger.debug("send data: visitor to audience"), this.sendData({ data: c }) } removeVisitor(e) { const {removeType: t, userIDList: r, extraInfo: n=""} = e , o = { action_type: Actions.RemoveVisitor, removeVisitorAction: { removeVisitorEvent: t, userIDList: r, extraInfo: encodeURIComponent(n) } }; return logger.debug("send data: remove visitor"), this.sendData({ data: o }) } getUserWithAvatar(e, t) { const r = { action_type: Actions.GetUserWithAvatar, getUserWithAvatarAction: { userType: e, roomID: t } }; return logger.debug("send data: get user with avatar"), this.sendData({ data: r }).then(n=>n.userWithAvatarList) } joystick(e) { const {degree: t, level: r=1} = e , n = util.uuid(); let o = -t + 90 + 360; o >= 360 && (o -= 360); const a = { action_type: Actions.Joystick, dir_action: { move_angle: o, speed_level: r }, trace_id: n, user_id: this.room.options.userId, packet_id: n }; return this.sendData({ data: a, track: !1 }) } }