diff --git a/scripts/network.js b/scripts/network.js index cff255a..2458436 100644 --- a/scripts/network.js +++ b/scripts/network.js @@ -1,355 +1,358 @@ /* * 3DCycles - A lightcycle game. * Copyright (C) 2019 Glen Harpring * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ function connectTo(host,port) { engine.playGame = false; engine.inputState = "game"; try { var connection = new WebSocket('ws'+(settings.CONNECT_SSL?'s':'')+'://'+host+':'+port); connection.onmessage = connectionHandler; connection.onerror = function(e) { console.log(e); var msg; disconnectFromGame();menu("leave");showMenu(); menu("menu:connectfail"); if(engine.network) { engine.console.print(msg="An error occurred with the connection."); } else { engine.console.print(msg="Couldn't connect to server. Please ensure that you have the right host and port."); } } connection.onclose = function(e) { console.log(e); var msg; if(engine.network) { disconnectFromGame();menu("leave");showMenu(); menu("menu:connectterm"); engine.console.print(msg="Our connection with the server has been terminated."); msg += " "; if(e.reason == "") engine.console.print(msg+="No reason given.\n"); else engine.console.print(msg+="Reason: "+e.reason+"\n"); } } return connection; } catch(e) { engine.console.print(e+"\n"); disconnectFromGame();menu("leave");showMenu(); menu("menu:"); document.getElementById('menu').innerHTML = "

An error occurred

"+e+"
"; } } function doNetSlide(cycle,timestep=1) { if(isNaN(cycle.position.x) || isNaN(cycle.position.y)) { cycle.position.x = cycle.newPos.x; cycle.position.y = cycle.newPos.y; delete cycle.newPos; } else { timestep *= settings.CYCLE_SMOOTH_TIME; if(timestep > 1) timestep = 1; cycle.lastpos.x = (cycle.position.x += (cycle.newPos.x-cycle.position.x)*timestep); cycle.lastpos.y = (cycle.position.y += (cycle.newPos.y-cycle.position.y)*timestep); cycle.resetCurrWallSegment(); if(cycle.position.x == cycle.newPos.x && cycle.position.y == cycle.newPos.y) delete cycle.newPos; } } function connectionHandler(e) { //console.log(e); var msg = JSON.parse(e.data); switch(msg.type) { case "ping": engine.connection.send((JSON.stringify({type:"pong"}))); break; case "timeSync": if(msg.data) { engine.gtime = msg.data; engine.connection.send(JSON.stringify({type:"timeSync",data:engine.gtime})); } engine.connection.timeSync = false; break; case "version": engine.connection.send(JSON.stringify({type:"version",data:0.7})); break; case "endRound": if(inround()) endRound(); break; case "newRound": if(!engine.playGame) playGame(); else if(!inround()) newRound(); break; case "setting": console.log(e); netcfg(msg.setting,""+msg.data); break; case "con": engine.console.print(msg.data); break; case "cen": centerMessage(msg.data.msg,msg.data.time); break; case "syncdata": console.log(msg.gtime); engine.connection.send(JSON.stringify({type:"player",data:settings.player})); if(typeof(msg.netid) != "undefined") { engine.activePlayer = msg.netid||0; //we got id engine.network = true; if(!engine.scene) init(); //endRound(); } //engine.totalPauseTime += (msg.gtime||0)-engine.gtime; if(msg.gtime !== undefined) engine.gtime = msg.gtime; break; case "leave": if(engine.players[msg.data]) { if(msg.data == engine.activePlayer) { console.warn("Player being deleted is ours."); } if(msg.data == engine.viewTarget) { changeViewTarget(1); } if(engine.players[msg.data].alive) engine.players[msg.data].kill(); engine.players.splice(msg.data,1); updateScoreBoard(); } else { console.warn("Left player doesn't seem to exist."); } break; case "playerdata": console.log(msg.data); for(var i=msg.data.length;i--;) if(typeof(msg.data[i]) != "undefined") { var data = msg.data[i]; if(!engine.players[data.netid]) { var cycleinfo; data.ai = false; // AIs should be exclusively handled by the server. Maybe a different solution in the future? engine.players[data.netid] = new Player(cycleinfo={ x:data.x||0, y:data.y||0, z:data.z||0, dir:data.dir||0, ai:data.ai||false, name:data.name||"1", cycleColor:data.cycleColor, tailColor:data.tailColor, engineType:data.engineType||settings.player.engineType, team: engine.teams[data.team]||new Team({name:(data.alive?data.name:"")}), }); console.log(data); - engine.players[data.netid].spawn(cycleinfo,false); + if(data.alive) engine.players[data.netid].spawn(cycleinfo,false); + else if(data.netid == engine.activePlayer) changeViewTarget(); audioMixing(engine.players[data.netid]); } else { engine.players[data.netid].name = data.name; engine.players[data.netid].cycleColor = data.cycleColor; engine.players[data.netid].tailColor = data.tailColor; + if(data.alive) engine.players[data.netid].spawn({x:data.x||0, y:data.y||0, z:data.z||0, dir:data.dir||0},false); + else if(data.netid == engine.activePlayer) changeViewTarget(); } } break; case "griddata": if(!engine.playGame) playGame(); //console.log(msg.data); //engine.gtime = (performance.now()/settings.TIME_FACTOR)-engine.timeStart-engine.totalPauseTime-4000; if(msg.gtime > engine.gtime) { //engine.gtime = msg.gtime; //console.log("S: ",msg.gtime-engine.gtime); if(!engine.connection.timeSync) { engine.connection.timeSync = true; engine.connection.send(JSON.stringify({type:"timeSync",data:engine.gtime})); } } var delta = engine.gtime-(msg.gtime); var timestep = delta/1000; //var delta = timestep*1000; for(var i=msg.data.length;i--;) if(typeof(msg.data[i]) != "undefined") { var data = msg.data[i]; var cycle = engine.players[data.netid]; if(cycle.alive != data.alive) { if(cycle.alive == true) cycle.killAt(data.position[0],data.position[1],data.position[2]); else cycle.spawn({x:data.position[0],y:data.position[1],z:data.position[2]},(!!data.spawntime)); delete cycle.newPos; } cycle.speed = data.speed; cycle.rubber = data.rubber; if(cycle.alive) { var olddir = cycle.rotation.z, newdir = deg2rad(data.direction||0); if(!data.wall && normalizeRad(olddir) == normalizeRad(newdir) && isFinite(data.position[0]) && isFinite(data.position[1])) //slide to position { if(!cycle.newPos) cycle.newPos = new THREE.Vector2(cycle.position.x,cycle.position.y); cycle.newPos.x = data.position[0]; cycle.newPos.y = data.position[1]; if(!cycle.handleNetTurn) { doNetSlide(cycle,Infinity); cycle.handleNetTurn = true; } } else if(cycle.handleNetTurn) //jump straight to the position, we're doing a turn { //also, don't handle updates until our last turn has been sent by the server cycle.lastpos.x = cycle.position.x = data.position[0]||0; cycle.lastpos.y = cycle.position.y = data.position[1]||0; delete cycle.newPos; cycle.gameTime = Math.max(0,msg.gtime); if(settings.DEBUG_NETWORK_TURN_WAIT) cycle.lastTurnTime = 0; //HACK to avoid cycle sticking to wall on a turn { var fakeTS = 0.01/cycle.speed; var move2d = Math.cos(cycle.model.rotation.y)*(cycle.speed*fakeTS), dir = cdir(cycle.rotation.z); cycle.lastpos.x = (cycle.position.x += move2d*dir[0]); cycle.lastpos.y = (cycle.position.y += move2d*dir[1]); cycle.gameTime += fakeTS; } } if(data.wall) { cycle.walls.map = data.wall; cycle.resetWall(false); } cycle.lastpos.z = cycle.position.z = data.position[2]||0; if(cycle.haswall) { if(rad2deg(olddir) == rad2deg(newdir)) { cycle.walls.map[cycle.walls.map.length-1] = [cycle.position.x,cycle.position.y]; } else { cycle.rotation.z = newdir; var wallmod = cycle.walls.children[cycle.walls.children.length-1]; cycle.newWallSegment(); } } else { cycle.rotation.z = newdir; } } //if(isFinite(timestep)) cycle.update(timestep); } if(!engine.lastPingTime) engine.players[engine.activePlayer].ping = parseInt(delta); updateScoreBoard(); //console.log(timestep); break; case "scoredata": for(var i=msg.data.length;i--;) if(typeof(msg.data[i]) != "undefined") { var data = msg.data[i]; var cycle = engine.players[data.netid]; cycle.chatting = data.chatting||false; cycle.ping = data.ping||0; cycle.score = data.score||0; engine.lastPingTime = performance.now(); } updateScoreBoard(); break; case "team": engine.teams.splice(0); for(var i=msg.data.length-1;i>=0;--i) if(msg.data[i]) { engine.teams[msg.data[i].id] = new Team(msg.data[i]); } break; case "zone": for(var i=msg.data.length-1;i>=0;--i) if(msg.data[i]) { var zone = msg.data[i]; if(zone.destroyed) { if(engine.zones[zone.id]) { engine.zones[zone.id].destroy(); } } else if(engine.zones.children[zone.id]) { var myZone = engine.zones.children[zone.id]; if(zone.type!==undefined) {myZone.cfg.type = zone.type;} if(zone.x!==undefined) {myZone.position.x = zone.x; myZone.position.y = zone.y; myZone.position.z = zone.z;} if(zone.xdir!==undefined) {myZone.cfg.xdir = zone.xdir; myZone.cfg.ydir = zone.ydir;} if(zone.bounce!== undefined)myZone.cfg.bounce = zone.bounce; if(zone.type == "flagHeld") myZone.cfg.heldBy = engine.players[zone.heldBy]; myZone.cfg.netObject = true; } else { try { var s = new Zone(zone).spawn(); //we don't need to process the zone ourselves (as that will all be handled by the server), however we should still know the type for our chatbot. s.netObject = true; } catch(e) { engine.console.print("0xff7f7fAn error occurred when syncing zones. You may be missing some important elements of the game.\n"); console.error(e); } } } break; } } function connectToGame() { if(!engine.connection) { engine.connection = connectTo(settings.CONNECT_HOST,settings.CONNECT_PORT) document.getElementById("progtitle").innerHTML = tStringify("@progtitleshort@ • Connecting to "+settings.CONNECT_HOST+":"+settings.CONNECT_PORT); menu("menu:connect"); } } function disconnectFromGame() { if(engine.connection && engine.connection.close) engine.connection.close(); engine.connection = engine.network = false; engine.viewTarget = engine.activePlayer = 0; for(var i=netChanged.length-1;i>=0;--i) { chsetting(netChanged[i][0],netChanged[i][1],true); } netChanged = []; }