From f990ed02a76effba4e990c8100442c36b56c80be Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:20:22 +0530 Subject: [PATCH] rough trapdoor networking, bare minimum --- src/Marble.hx | 14 ++++++ src/MarbleWorld.hx | 46 +++++++++++++++++-- src/net/MarbleUpdateQueue.hx | 9 ++++ src/net/NetPacket.hx | 25 +++++++++++ src/net/TrapdoorPredictionStore.hx | 36 +++++++++++++++ src/shapes/Trapdoor.hx | 71 +++++++++++++++++++++++++----- 6 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 src/net/TrapdoorPredictionStore.hx diff --git a/src/Marble.hx b/src/Marble.hx index ef40e66a..95425e95 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -330,6 +330,7 @@ class Marble extends GameObject { var serverTicks:Int; var recvServerTick:Int; var serverUsePowerup:Bool; + var trapdoorContacts:Map = []; public function new() { super(); @@ -1870,6 +1871,11 @@ class Marble extends GameObject { this.netFlags = 0; } + public inline function queueTrapdoorUpdate(tId:Int, lastContactTick:Int) { + trapdoorContacts.set(tId, lastContactTick); + this.netFlags |= MarbleNetFlags.UpdateTrapdoor; + } + public function packUpdate(move:NetMove, timeState:TimeState) { var b = new OutputBitStream(); b.writeByte(NetPacketType.MarbleUpdate); @@ -1891,8 +1897,11 @@ class Marble extends GameObject { marbleUpdate.powerUpId = this.heldPowerup != null ? this.heldPowerup.netIndex : 0x1FF; marbleUpdate.netFlags = this.netFlags; marbleUpdate.gravityDirection = this.currentUp; + marbleUpdate.trapdoorUpdates = this.trapdoorContacts; marbleUpdate.serialize(b); + this.trapdoorContacts = []; + return b.getBytes(); } @@ -1938,6 +1947,11 @@ class Marble extends GameObject { // Pad null move on client this.connection.moveManager.duplicateLastMove(); } + if (p.netFlags & MarbleNetFlags.UpdateTrapdoor > 0) { + for (tId => tTime in p.trapdoorUpdates) { + @:privateAccess level.trapdoorPredictions.acknowledgeTrapdoorUpdate(tId, tTime); + } + } // if (Net.isClient && !this.controllable && (this.serverTicks - this.blastUseTick) < 12) { // var ticksSince = (this.serverTicks - this.blastUseTick); // if (ticksSince >= 0) { diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 173d71b0..0e35f622 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -1,5 +1,6 @@ package src; +import net.TrapdoorPredictionStore; import shapes.Explodable; import net.ExplodablePredictionStore; import gui.MPPreGameDlg; @@ -150,6 +151,8 @@ class MarbleWorld extends Scheduler { public var forceObjects:Array = []; public var explodables:Array = []; public var explodablesToTick:Array = []; + public var trapdoors:Array = []; + public var trapdoorsToTick:Array = []; public var triggers:Array = []; public var gems:Array = []; public var namedObjects:Map = []; @@ -236,6 +239,7 @@ class MarbleWorld extends Scheduler { var powerupPredictions:PowerupPredictionStore; var gemPredictions:GemPredictionStore; var explodablePredictions:ExplodablePredictionStore; + var trapdoorPredictions:TrapdoorPredictionStore; public var lastMoves:MarbleUpdateQueue; @@ -281,6 +285,7 @@ class MarbleWorld extends Scheduler { powerupPredictions = new PowerupPredictionStore(); gemPredictions = new GemPredictionStore(); explodablePredictions = new ExplodablePredictionStore(cast this); + trapdoorPredictions = new TrapdoorPredictionStore(cast this); } } @@ -620,9 +625,6 @@ class MarbleWorld extends Scheduler { powerupPredictions.reset(); gemPredictions.reset(); explodablePredictions.reset(); - for (exp in explodables) { - exp.lastContactTick = -100000; - } } } @@ -1189,6 +1191,13 @@ class MarbleWorld extends Scheduler { if (Net.isClient) explodablePredictions.alloc(); } + if (obj is Trapdoor) { + var t:Trapdoor = cast obj; + t.netId = this.trapdoors.length; + this.trapdoors.push(t); + if (Net.isClient) + trapdoorPredictions.alloc(); + } obj.isTSStatic = isTsStatic; obj.init(cast this, () -> { obj.update(this.timeState); @@ -1300,6 +1309,14 @@ class MarbleWorld extends Scheduler { restart(marble, true); } + for (exp in explodables) { + exp.lastContactTick = -100000; + } + trapdoorPredictions.reset(); + for (t in trapdoors) { + t.lastContactTicks = -100000; + } + showPreGame(); serverStartTicks = 0; @@ -1317,6 +1334,14 @@ class MarbleWorld extends Scheduler { startTime = this.timeState.timeSinceLoad + 4; + for (exp in explodables) { + exp.lastContactTick = -100000; + } + trapdoorPredictions.reset(); + for (t in trapdoors) { + t.lastContactTicks = -100000; + } + if (Net.isHost) { haxe.Timer.delay(() -> { this.gameMode.onRestart(); @@ -1516,6 +1541,12 @@ class MarbleWorld extends Scheduler { exp.revertContactTicks(explodablePredictions.getState(exp.netId)); } explodablesToTick = []; + for (tT in trapdoorsToTick) { + var t = trapdoors[tT]; + t.lastContactTicks = trapdoorPredictions.getState(t.netId); + t.update(advanceTimeState); + } + var huntMode:HuntMode = cast this.gameMode; if (@:privateAccess huntMode.activeGemSpawnGroup != null) { for (activeGem in @:privateAccess huntMode.activeGemSpawnGroup) { @@ -1599,6 +1630,7 @@ class MarbleWorld extends Scheduler { m.calculationTicks--; } } + advanceTimeState.currentAttemptTime += 0.032; advanceTimeState.ticks++; currentTick++; @@ -1608,9 +1640,16 @@ class MarbleWorld extends Scheduler { pi.computeNextPathStep(0.032); pi.advance(0.032); } + + for (tT in trapdoorsToTick) { + var t = trapdoors[tT]; + t.update(advanceTimeState); + } // } } + trapdoorsToTick = []; + lastMoves.ourMoveApplied = true; @:privateAccess this.marble.isNetUpdate = false; return advanceTimeState.ticks; @@ -2832,6 +2871,7 @@ class MarbleWorld extends Scheduler { dtsObjects = null; powerUps = null; explodables = null; + trapdoors = null; for (trigger in this.triggers) { trigger.dispose(); } diff --git a/src/net/MarbleUpdateQueue.hx b/src/net/MarbleUpdateQueue.hx index a57e1c5f..94e4eb76 100644 --- a/src/net/MarbleUpdateQueue.hx +++ b/src/net/MarbleUpdateQueue.hx @@ -15,6 +15,7 @@ class OtherMarbleUpdate { var lastShockAbsorberTick:Int; var lastPowerUpId:Int; var lastGravityUp:Vector; + var lastTrapdoorUpdates:Map = []; public function new() {} } @@ -64,6 +65,10 @@ class MarbleUpdateQueue { update.gravityDirection = otherUpdate.lastGravityUp; else otherUpdate.lastGravityUp = update.gravityDirection; + if (update.netFlags & MarbleNetFlags.UpdateTrapdoor == 0) + update.trapdoorUpdates = otherUpdate.lastTrapdoorUpdates; + else + otherUpdate.lastTrapdoorUpdates = update.trapdoorUpdates; ourList.push(update); } else { var otherUpdate = new OtherMarbleUpdate(); @@ -83,6 +88,8 @@ class MarbleUpdateQueue { otherUpdate.lastPowerUpId = update.powerUpId; if (update.netFlags & MarbleNetFlags.GravityChange != 0) otherUpdate.lastGravityUp = update.gravityDirection; + if (update.netFlags & MarbleNetFlags.UpdateTrapdoor != 0) + otherUpdate.lastTrapdoorUpdates = update.trapdoorUpdates; otherMarbleUpdates[cc] = otherUpdate; } } else { @@ -103,6 +110,8 @@ class MarbleUpdateQueue { update.powerUpId = myMarbleUpdate.powerUpId; if (update.netFlags & MarbleNetFlags.GravityChange == 0) update.gravityDirection = myMarbleUpdate.gravityDirection; + if (update.netFlags & MarbleNetFlags.UpdateTrapdoor == 0) + update.trapdoorUpdates = myMarbleUpdate.trapdoorUpdates; } myMarbleUpdate = update; ourMoveApplied = false; diff --git a/src/net/NetPacket.hx b/src/net/NetPacket.hx index bae8441d..a5ddcbc5 100644 --- a/src/net/NetPacket.hx +++ b/src/net/NetPacket.hx @@ -49,6 +49,7 @@ enum abstract MarbleNetFlags(Int) from Int to Int { var PickupPowerup = 1 << 5; var GravityChange = 1 << 6; var UsePowerup = 1 << 7; + var UpdateTrapdoor = 1 << 8; } @:publicFields @@ -71,6 +72,7 @@ class MarbleUpdatePacket implements NetPacket { var powerUpId:Int; var moveQueueSize:Int; var netFlags:Int; + var trapdoorUpdates:Map = []; public function new() {} @@ -141,6 +143,20 @@ class MarbleUpdatePacket implements NetPacket { } else { b.writeFlag(false); } + if (netFlags & MarbleNetFlags.UpdateTrapdoor > 0) { + b.writeFlag(true); + var cnt = 0; + for (k => v in trapdoorUpdates) { + cnt++; + } + b.writeInt(cnt, 4); + for (k => v in trapdoorUpdates) { + b.writeInt(k, 8); + b.writeUInt16(v); + } + } else { + b.writeFlag(false); + } } public inline function deserialize(b:InputBitStream) { @@ -184,6 +200,15 @@ class MarbleUpdatePacket implements NetPacket { gravityDirection = new Vector(b.readFloat(), b.readFloat(), b.readFloat()); this.netFlags |= MarbleNetFlags.GravityChange; } + if (b.readFlag()) { + var cnt = b.readInt(4); + for (i in 0...cnt) { + var k = b.readInt(8); + var v = b.readUInt16(); + trapdoorUpdates[k] = v; + } + this.netFlags |= MarbleNetFlags.UpdateTrapdoor; + } } } diff --git a/src/net/TrapdoorPredictionStore.hx b/src/net/TrapdoorPredictionStore.hx new file mode 100644 index 00000000..496ab914 --- /dev/null +++ b/src/net/TrapdoorPredictionStore.hx @@ -0,0 +1,36 @@ +package net; + +import src.MarbleWorld; +import net.NetPacket.ExplodableUpdatePacket; +import src.TimeState; +import net.NetPacket.PowerupPickupPacket; + +class TrapdoorPredictionStore { + var world:MarbleWorld; + var predictions:Array; + + public inline function new(world:MarbleWorld) { + predictions = []; + this.world = world; + } + + public inline function alloc() { + predictions.push(-100000); + } + + public inline function getState(netIndex:Int) { + return predictions[netIndex]; + } + + public inline function acknowledgeTrapdoorUpdate(id:Int, ticks:Int) { + predictions[id] = ticks; + if (!world.trapdoorsToTick.contains(id)) + world.trapdoorsToTick.push(id); + } + + public inline function reset() { + for (i in 0...predictions.length) { + predictions[i] = -100000; + } + } +} diff --git a/src/shapes/Trapdoor.hx b/src/shapes/Trapdoor.hx index 0ada0d77..abd93f02 100644 --- a/src/shapes/Trapdoor.hx +++ b/src/shapes/Trapdoor.hx @@ -1,5 +1,6 @@ package shapes; +import net.Net; import hxd.snd.effect.Spatialization; import src.TimeState; import collision.CollisionInfo; @@ -17,6 +18,10 @@ class Trapdoor extends DtsObject { var lastDirection:Int; var lastCompletion:Float = 0; + var lastContactTicks:Int = -100000; + + var netId:Int; + public function new() { super(); this.dtsPath = "data/shapes/hazards/trapdoor.dts"; @@ -57,24 +62,70 @@ class Trapdoor extends DtsObject { } function getCurrentCompletion(timeState:TimeState) { - var elapsed = timeState.timeSinceLoad - this.lastContactTime; - var completion = Util.clamp(elapsed / 1.6666676998138428, 0, 1); - if (elapsed > 5) - completion = Util.clamp(1 - (elapsed - 5) / 1.6666676998138428, 0, 1); - return completion; + if (level.isMultiplayer) { + if (Net.isHost) { + var elapsed = (timeState.ticks - this.lastContactTicks) * 0.032 + (timeState.subframe * 0.032); + var completion = Util.clamp(elapsed / 1.6666676998138428, 0, 1); + if (elapsed > 5) + completion = Util.clamp(1 - (elapsed - 5) / 1.6666676998138428, 0, 1); + return completion; + } else { + var elapsed = (@:privateAccess level.marble.serverTicks - this.lastContactTicks) * 0.032 + (timeState.subframe * 0.032); + var completion = Util.clamp(elapsed / 1.6666676998138428, 0, 1); + if (elapsed > 5) + completion = Util.clamp(1 - (elapsed - 5) / 1.6666676998138428, 0, 1); + return completion; + } + } else { + var elapsed = timeState.timeSinceLoad - this.lastContactTime; + var completion = Util.clamp(elapsed / 1.6666676998138428, 0, 1); + if (elapsed > 5) + completion = Util.clamp(1 - (elapsed - 5) / 1.6666676998138428, 0, 1); + return completion; + } } override function onMarbleContact(marble:src.Marble, time:TimeState, ?contact:CollisionInfo) { super.onMarbleContact(marble, time, contact); - if (time.timeSinceLoad - this.lastContactTime <= 0) - return; // The trapdoor is queued to open, so don't do anything. + if (level.isMultiplayer) { + if (Net.isHost) { + if (time.ticks - this.lastContactTicks <= 0) + return; // The trapdoor is queued to open, so don't do anything. + } else { + if (@:privateAccess marble.serverTicks - this.lastContactTicks <= 0) + return; // The trapdoor is queued to open, so don't do anything. + } + } else { + if (time.timeSinceLoad - this.lastContactTime <= 0) + return; // The trapdoor is queued to open, so don't do anything. + } var currentCompletion = this.getCurrentCompletion(time); // Set the last contact time accordingly so that the trapdoor starts closing (again) - this.lastContactTime = time.timeSinceLoad - currentCompletion * 1.6666676998138428; - if (currentCompletion == 0) - this.lastContactTime += this.timeout; + if (level.isMultiplayer) { + if (Net.isHost) { + this.lastContactTicks = Std.int(time.ticks - currentCompletion * 1.6666676998138428 / 0.032); + } else { + this.lastContactTicks = Std.int(@:privateAccess marble.serverTicks - currentCompletion * 1.6666676998138428 / 0.032); + } + if (currentCompletion == 0) { + this.lastContactTicks += Std.int(this.timeout / 0.032); + } + } else { + this.lastContactTime = time.timeSinceLoad - currentCompletion * 1.6666676998138428; + if (currentCompletion == 0) + this.lastContactTime += this.timeout; + } + + if (Net.isHost) { + marble.queueTrapdoorUpdate(netId, this.lastContactTicks); + } + + if (Net.isClient) { + if (!level.trapdoorsToTick.contains(netId)) + level.trapdoorsToTick.push(netId); + } // this.level.replay.recordMarbleContact(this); } }