diff --git a/src/Marble.hx b/src/Marble.hx index 21b107e7..dc796ca1 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -294,6 +294,7 @@ class Marble extends GameObject { var connection:net.Net.ClientConnection; var moveMotionDir:Vector; + var lastMove:Move; var isNetUpdate:Bool = false; public function new() { @@ -1140,16 +1141,17 @@ class Marble extends GameObject { // var v0 = surface.points[surface.indices[i]].transformed(tform); // var v = surface.points[surface.indices[i + 1]].transformed(tform); // var v2 = surface.points[surface.indices[i + 2]].transformed(tform); - var v0 = verts.v1; - var v = verts.v2; - var v2 = verts.v3; + var v0 = new Vector(verts.v1x, verts.v1y, verts.v1z); + var v = new Vector(verts.v2x, verts.v2y, verts.v2z); + var v2 = new Vector(verts.v3x, verts.v3y, verts.v3z); // var v0 = surface.points[surface.indices[i]].transformed(obj.transform); // var v = surface.points[surface.indices[i + 1]].transformed(obj.transform); // var v2 = surface.points[surface.indices[i + 2]].transformed(obj.transform); var triangleVerts = [v0, v, v2]; - var surfaceNormal = verts.n; // surface.normals[surface.indices[i]].transformed3x3(obj.transform).normalized(); + var surfaceNormal = new Vector(verts.nx, verts.ny, + verts.nz); // surface.normals[surface.indices[i]].transformed3x3(obj.transform).normalized(); if (obj is DtsObject) surfaceNormal.multiply(-1); var surfaceD = -surfaceNormal.dot(v0); @@ -1160,10 +1162,6 @@ class Marble extends GameObject { continue; } - // var v0T = v0.transformed(obj.transform); - // var vT = v.transformed(obj.transform); - // var v2T = v2.transformed(obj.transform); - // var vN = surfaceNormal.transformed3x3(obj.transform); testTriangles.push({ v: [v0, v, v2], n: surfaceNormal, @@ -1175,22 +1173,6 @@ class Marble extends GameObject { // Are we going to touch the plane during this time step? if (collisionTime >= 0.000001 && finalT >= collisionTime) { var collisionPoint = position.add(relVel.multiply(collisionTime)); - // var lastPoint = v2; - // var j = 0; - // while (j < 3) { - // var testPoint = surface.points[surface.indices[i + j]]; - // if (testPoint != lastPoint) { - // var a = surfaceNormal; - // var b = lastPoint.sub(testPoint); - // var planeNorm = b.cross(a); - // var planeD = -planeNorm.dot(testPoint); - // lastPoint = testPoint; - // // if we are on the far side of the edge - // if (planeNorm.dot(collisionPoint) + planeD >= 0.0) - // break; - // } - // j++; - // } // If we're inside the poly, just get the position if (Collision.PointInTriangle(collisionPoint, v0, v, v2)) { finalT = collisionTime; @@ -1252,14 +1234,6 @@ class Marble extends GameObject { // Check if the collision hasn't already happened if (edgeCollisionTime >= 0.000001) { - // if (edgeCollisionTime < 0.000001) { - // edgeCollisionTime = edgeCollisionTime2; - // } - // if (edgeCollisionTime < 0.00001) - // continue; - // if (edgeCollisionTime > finalT) - // continue; - var edgeLen = vertDiff.length(); var relativeCollisionPos = position.add(relVel.multiply(edgeCollisionTime)).sub(thisVert); @@ -1391,18 +1365,6 @@ class Marble extends GameObject { var finalPosition = position.add(deltaPosition); position = finalPosition; - // for (testTri in testTriangles) { - // var tsi = Collision.TriangleSphereIntersection(testTri.v[0], testTri.v[1], testTri.v[2], testTri.n, finalPosition, radius, testTri.edge, - // testTri.concavity); - // if (tsi.result) { - // var contact = new CollisionInfo(); - // contact.point = tsi.point; - // contact.normal = tsi.normal; - // contact.contactDistance = tsi.point.distance(position); - // finalContacts.push(contact); - // } - // } - return { position: position, t: finalT, @@ -1494,6 +1456,10 @@ class Marble extends GameObject { var piTime = timeRemaining; + if (this.isNetUpdate) { + lastMove = m; + } + _bounceYet = false; var contactTime = 0.0; @@ -1661,7 +1627,7 @@ class Marble extends GameObject { newPos = this.collider.transform.getPosition(); - if (this.prevPos != null && !this.isNetUpdate) { + if (this.prevPos != null) { var tempTimeState = timeState.clone(); tempTimeState.currentAttemptTime = passedTime; this.level.callCollisionHandlers(cast this, tempTimeState, oldPos, newPos); @@ -1719,7 +1685,7 @@ class Marble extends GameObject { if (this.controllable && this.mode != Finish && !MarbleGame.instance.paused && !this.level.isWatching && !this.level.isReplayingMovement) { if (Net.isClient) { var axis = getMarbleAxis()[1]; - move = Net.clientConnection.moveManager.recordMove(axis, timeState); + move = Net.clientConnection.moveManager.recordMove(this, axis, timeState); } else if (Net.isHost) { var axis = getMarbleAxis()[1]; var innerMove = recordMove(); @@ -1742,9 +1708,12 @@ class Marble extends GameObject { } } if (move == null) { - var axis = getMarbleAxis()[1]; - var innerMove = new Move(); - innerMove.d = new Vector(0, 0); + var axis = moveMotionDir != null ? moveMotionDir : new Vector(0, -1, 0); + var innerMove = lastMove; + if (innerMove == null) { + innerMove = new Move(); + innerMove.d = new Vector(0, 0); + } move = new NetMove(innerMove, axis, timeState, 65535); } playedSounds = []; diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 6e8108cc..3a6f1841 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -116,6 +116,7 @@ class MarbleWorld extends Scheduler { public var interiors:Array = []; public var pathedInteriors:Array = []; public var dtsObjects:Array = []; + public var powerUps:Array = []; public var forceObjects:Array = []; public var triggers:Array = []; public var gems:Array = []; @@ -938,6 +939,8 @@ class MarbleWorld extends Scheduler { var worker = new ResourceLoaderWorker(() -> { obj.idInLevel = this.dtsObjects.length; // Set the id of the thing this.dtsObjects.push(obj); + if (obj is PowerUp) + this.powerUps.push(cast obj); if (obj is ForceObject) { this.forceObjects.push(cast obj); } @@ -1066,31 +1069,41 @@ class MarbleWorld extends Scheduler { var ourQueuedMoves = @:privateAccess Net.clientConnection.moveManager.queuedMoves.copy(); - var advanceTimeState = timeState.clone(); + var qm = ourQueuedMoves[0]; + var advanceTimeState = qm != null ? qm.timeState.clone() : timeState.clone(); advanceTimeState.dt = 0.032; - ackLag = ourQueuedMoves.length; - - if (mvStored != null) { - // trace('ACK Lag: ${timeState.ticks - mvStored.timeState.ticks} ${ourQueuedMoves.length}'); + if (qm != null) { + var mvs = qm.powerupStates.copy(); + for (pw in marble.level.powerUps) { + pw.lastPickUpTime = mvs.shift(); + } + @:privateAccess marble.helicopterEnableTime = qm.helicopterState; + @:privateAccess marble.megaMarbleEnableTime = qm.megaState; + marble.blastAmount = qm.blastAmt; } + ackLag = ourQueuedMoves.length; + // Tick the remaining moves (ours) if (!lastMoves.ourMoveApplied) { @:privateAccess this.marble.isNetUpdate = true; var totalTicksToDo = ourQueuedMoves.length; var endTick = ourLastMoveTime + totalTicksToDo; var currentTick = ourLastMoveTime; + //- Std.int(ourLastMove.moveQueueSize - @:privateAccess Net.clientConnection.moveManager.ackRTT); // - Std.int((@:privateAccess Net.clientConnection.moveManager.ackRTT)) - offset; var marblesToTick = new Map(); for (client => arr in lastMoves.otherMarbleUpdates) { if (arr.length > 0) { var m = arr[0]; - if (m.serverTicks <= ourLastMoveTime) { + if (m.serverTicks == ourLastMoveTime) { var marbleToUpdate = clientMarbles[Net.clientIdMap[client]]; Debug.drawSphere(@:privateAccess marbleToUpdate.newPos, marbleToUpdate._radius); - m.calculationTicks = Std.int(ackLag); + m.calculationTicks = ourQueuedMoves.length; // ourQueuedMoves.length; + + // - Std.int((@:privateAccess Net.clientConnection.moveManager.ackRTT - ourLastMove.moveQueueSize) / 2); marblesToTick.set(client, m); arr.shift(); } @@ -1098,17 +1111,20 @@ class MarbleWorld extends Scheduler { } Debug.drawSphere(@:privateAccess this.marble.newPos, this.marble._radius); + // var syncTickStates = new Map(); for (move in ourQueuedMoves) { var m = move.move; // Debug.drawSphere(@:privateAccess this.marble.newPos, this.marble._radius); + this.marble.heldPowerup = move.powerup; @:privateAccess this.marble.moveMotionDir = move.motionDir; @:privateAccess this.marble.advancePhysics(advanceTimeState, m, this.collisionWorld, this.pathedInteriors); + // var collidings = @:privateAccess this.marble.contactEntities.filter(x -> x is SphereCollisionEntity); + advanceTimeState.currentAttemptTime += 0.032; for (client => m in marblesToTick) { if (m.calculationTicks > 0) { var marbleToUpdate = clientMarbles[Net.clientIdMap[client]]; - // Debug.drawSphere(@:privateAccess marbleToUpdate.newPos, marbleToUpdate._radius); var beforePos = @:privateAccess marbleToUpdate.newPos.clone(); @@ -1125,20 +1141,24 @@ class MarbleWorld extends Scheduler { currentTick++; } - for (client => m in marblesToTick) { - if (m.calculationTicks >= 0) { - var marbleToUpdate = clientMarbles[Net.clientIdMap[client]]; + // for (marble => state in syncTickStates) { + // marble.restoreState(state); + // } - while (m.calculationTicks > 0) { - var mv = m.move.move; - @:privateAccess marbleToUpdate.isNetUpdate = true; - @:privateAccess marbleToUpdate.moveMotionDir = m.move.motionDir; - @:privateAccess marbleToUpdate.advancePhysics(advanceTimeState, mv, this.collisionWorld, this.pathedInteriors); - @:privateAccess marbleToUpdate.isNetUpdate = false; - m.calculationTicks--; - } - } - } + // for (client => m in marblesToTick) { + // if (m.calculationTicks >= 0) { + // var marbleToUpdate = clientMarbles[Net.clientIdMap[client]]; + + // while (m.calculationTicks > 0) { + // var mv = m.move.move; + // @:privateAccess marbleToUpdate.isNetUpdate = true; + // @:privateAccess marbleToUpdate.moveMotionDir = m.move.motionDir; + // @:privateAccess marbleToUpdate.advancePhysics(advanceTimeState, mv, this.collisionWorld, this.pathedInteriors); + // @:privateAccess marbleToUpdate.isNetUpdate = false; + // m.calculationTicks--; + // } + // } + // } lastMoves.ourMoveApplied = true; @:privateAccess this.marble.isNetUpdate = false; @@ -2166,6 +2186,7 @@ class MarbleWorld extends Scheduler { dtsObject.dispose(); } dtsObjects = null; + powerUps = []; for (trigger in this.triggers) { trigger.dispose(); } diff --git a/src/net/MoveManager.hx b/src/net/MoveManager.hx index 228ebe43..69b548f5 100644 --- a/src/net/MoveManager.hx +++ b/src/net/MoveManager.hx @@ -1,5 +1,6 @@ package net; +import shapes.PowerUp; import net.NetPacket.MarbleMovePacket; import src.TimeState; import src.Console; @@ -13,6 +14,7 @@ import src.Settings; import hxd.Key; import src.MarbleGame; import src.Util; +import src.Marble; @:publicFields class NetMove { @@ -20,6 +22,12 @@ class NetMove { var move:Move; var id:Int; var timeState:TimeState; + // For rewind purposes + var powerup:PowerUp; + var powerupStates:Array; + var helicopterState:Float; + var megaState:Float; + var blastAmt:Float; public function new(move:Move, motionDir:Vector, timeState:TimeState, id:Int) { this.move = move; @@ -47,7 +55,7 @@ class MoveManager { this.connection = connection; } - public function recordMove(motionDir:Vector, timeState:TimeState) { + public function recordMove(marble:Marble, motionDir:Vector, timeState:TimeState) { if (queuedMoves.length >= maxMoves || stall) return queuedMoves[queuedMoves.length - 1]; var move = new Move(); @@ -88,6 +96,14 @@ class MoveManager { } var netMove = new NetMove(move, motionDir, timeState.clone(), nextMoveId++); + netMove.powerup = marble.heldPowerup; + netMove.powerupStates = []; + netMove.helicopterState = @:privateAccess marble.helicopterEnableTime; + netMove.megaState = @:privateAccess marble.megaMarbleEnableTime; + netMove.blastAmt = marble.blastAmount; + for (pw in marble.level.powerUps) { + netMove.powerupStates.push(pw.lastPickUpTime); + } queuedMoves.push(netMove); if (nextMoveId >= 65535) // 65535 is reserved for null move @@ -108,8 +124,8 @@ class MoveManager { public static inline function packMove(m:NetMove, b:haxe.io.BytesOutput) { b.writeUInt16(m.id); - b.writeFloat(m.move.d.x); - b.writeFloat(m.move.d.y); + b.writeByte(Std.int((m.move.d.x * 16) + 16)); + b.writeByte(Std.int((m.move.d.y * 16) + 16)); var flags = 0; if (m.move.jump) flags |= 1; @@ -128,8 +144,8 @@ class MoveManager { var moveId = b.readUInt16(); var move = new Move(); move.d = new Vector(); - move.d.x = b.readFloat(); - move.d.y = b.readFloat(); + move.d.x = (b.readByte() - 16) / 16.0; + move.d.y = (b.readByte() - 16) / 16.0; var flags = b.readByte(); move.jump = (flags & 1) != 0; move.powerup = (flags & 2) != 0; diff --git a/src/net/Net.hx b/src/net/Net.hx index 9cd74244..018b3411 100644 --- a/src/net/Net.hx +++ b/src/net/Net.hx @@ -122,55 +122,56 @@ class Net { public static function joinServer(connectedCb:() -> Void) { masterWs = new WebSocket("ws://localhost:8080"); + masterWs.onopen = () -> { + client = new RTCPeerConnection(["stun:stun.l.google.com:19302"], "0.0.0.0"); + var candidates = []; - client = new RTCPeerConnection(["stun:stun.l.google.com:19302"], "0.0.0.0"); - var candidates = []; - - client.onLocalCandidate = (c) -> { - if (c != "") - candidates.push('a=${c}'); - } - client.onGatheringStateChange = (s) -> { - if (s == RTC_GATHERING_COMPLETE) { - Console.log("Local Description Set!"); - var sdpObj = StringTools.trim(client.localDescription); - sdpObj = sdpObj + '\r\n' + candidates.join('\r\n'); - masterWs.send(Json.stringify({ - type: "connect", - sdpObj: { - sdp: sdpObj, - type: "offer" - } - })); + client.onLocalCandidate = (c) -> { + if (c != "") + candidates.push('a=${c}'); } - } - - masterWs.onmessage = (m) -> { - switch (m) { - case StrMessage(content): - Console.log("Remote Description Received!"); - var conts = Json.parse(content); - client.setRemoteDescription(conts.sdp, conts.type); - case _: {} + client.onGatheringStateChange = (s) -> { + if (s == RTC_GATHERING_COMPLETE) { + Console.log("Local Description Set!"); + var sdpObj = StringTools.trim(client.localDescription); + sdpObj = sdpObj + '\r\n' + candidates.join('\r\n'); + masterWs.send(Json.stringify({ + type: "connect", + sdpObj: { + sdp: sdpObj, + type: "offer" + } + })); + } } - } - clientDatachannel = client.createDatachannel("mp"); - clientDatachannel.onOpen = (n) -> { - Console.log("Successfully connected!"); - clients.set(client, new ClientConnection(0, client, clientDatachannel)); // host is always 0 - clientIdMap[0] = clients[client]; - clientConnection = clients[client]; - onConnectedToServer(); - haxe.Timer.delay(() -> connectedCb(), 1500); // 1.5 second delay to do the RTT calculation - } - clientDatachannel.onMessage = (b) -> { - onPacketReceived(client, clientDatachannel, new haxe.io.BytesInput(b)); - } + masterWs.onmessage = (m) -> { + switch (m) { + case StrMessage(content): + Console.log("Remote Description Received!"); + var conts = Json.parse(content); + client.setRemoteDescription(conts.sdp, conts.type); + case _: {} + } + } - isMP = true; - isHost = false; - isClient = true; + clientDatachannel = client.createDatachannel("mp"); + clientDatachannel.onOpen = (n) -> { + Console.log("Successfully connected!"); + clients.set(client, new ClientConnection(0, client, clientDatachannel)); // host is always 0 + clientIdMap[0] = clients[client]; + clientConnection = clients[client]; + onConnectedToServer(); + haxe.Timer.delay(() -> connectedCb(), 1500); // 1.5 second delay to do the RTT calculation + } + clientDatachannel.onMessage = (b) -> { + onPacketReceived(client, clientDatachannel, new haxe.io.BytesInput(b)); + } + + isMP = true; + isHost = false; + isClient = true; + } } static function onClientConnect(c:RTCPeerConnection, dc:RTCDataChannel) { diff --git a/src/shapes/MegaMarble.hx b/src/shapes/MegaMarble.hx index 01eeb8a6..79098831 100644 --- a/src/shapes/MegaMarble.hx +++ b/src/shapes/MegaMarble.hx @@ -43,7 +43,8 @@ class MegaMarble extends PowerUp { public function use(marble:Marble, timeState:TimeState) { marble.enableMegaMarble(timeState.currentAttemptTime); this.level.deselectPowerUp(marble); - AudioManager.playSound(ResourceLoader.getResource('data/sound/use_mega.wav', ResourceLoader.getAudio, this.soundResources)); + if (this.level.marble == marble && @:privateAccess !marble.isNetUpdate) + AudioManager.playSound(ResourceLoader.getResource('data/sound/use_mega.wav', ResourceLoader.getAudio, this.soundResources)); } override function getPreloadMaterials(dts:dts.DtsFile) { diff --git a/src/shapes/PowerUp.hx b/src/shapes/PowerUp.hx index 373b7325..9abc1555 100644 --- a/src/shapes/PowerUp.hx +++ b/src/shapes/PowerUp.hx @@ -39,7 +39,7 @@ abstract class PowerUp extends DtsObject { if (this.autoUse) this.use(marble, timeState); - if (level.marble == marble) { + if (level.marble == marble && @:privateAccess !marble.isNetUpdate) { if (customPickupMessage != null) this.level.displayAlert(customPickupMessage); else diff --git a/src/shapes/SuperJump.hx b/src/shapes/SuperJump.hx index fa7a1b13..50a78e03 100644 --- a/src/shapes/SuperJump.hx +++ b/src/shapes/SuperJump.hx @@ -71,10 +71,11 @@ class SuperJump extends PowerUp { var masslessFactor = marble.getMass() * 0.7 + 1 - 0.7; var boost = marble.currentUp.multiply(20 * masslessFactor / marble.getMass()); marble.velocity = marble.velocity.add(boost); - this.level.particleManager.createEmitter(superJumpParticleOptions, this.sjEmitterParticleData, null, () -> marble.getAbsPos().getPosition()); + if (@:privateAccess !marble.isNetUpdate) + this.level.particleManager.createEmitter(superJumpParticleOptions, this.sjEmitterParticleData, null, () -> marble.getAbsPos().getPosition()); // marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity // if (!this.level.rewinding) - if (level.marble == marble) + if (level.marble == marble && @:privateAccess !marble.isNetUpdate) AudioManager.playSound(ResourceLoader.getResource("data/sound/use_superjump.wav", ResourceLoader.getAudio, this.soundResources)); // this.level.particles.createEmitter(superJumpParticleOptions, null, () => Util.vecOimoToThree(marble.body.getPosition())); this.level.deselectPowerUp(marble); diff --git a/src/shapes/SuperSpeed.hx b/src/shapes/SuperSpeed.hx index 51f7337a..468df760 100644 --- a/src/shapes/SuperSpeed.hx +++ b/src/shapes/SuperSpeed.hx @@ -86,9 +86,10 @@ class SuperSpeed extends PowerUp { // marble.body.addLinearVelocity(Util.vecThreeToOimo(movementVector).scale(24.7)); // Whirligig's determined value // marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity // if (!this.level.rewinding) - if (level.marble == marble) + if (level.marble == marble && @:privateAccess !marble.isNetUpdate) AudioManager.playSound(ResourceLoader.getResource("data/sound/use_speed.wav", ResourceLoader.getAudio, this.soundResources)); - this.level.particleManager.createEmitter(superSpeedParticleOptions, this.ssEmitterParticleData, null, () -> marble.getAbsPos().getPosition()); + if (@:privateAccess !marble.isNetUpdate) + this.level.particleManager.createEmitter(superSpeedParticleOptions, this.ssEmitterParticleData, null, () -> marble.getAbsPos().getPosition()); this.level.deselectPowerUp(marble); }