From 141a3f892b1dc031f7cf63b247625595ccf54c03 Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Sun, 16 Jun 2024 20:25:17 +0530 Subject: [PATCH] make hunt network again, and fix marble radius for MP --- src/Console.hx | 3 + src/Marble.hx | 158 +++++++++++++++++++------------ src/MarbleWorld.hx | 75 ++++++++++++++- src/modes/HuntMode.hx | 81 ++++++++++------ src/net/BitStream.hx | 12 +++ src/net/MarblePredictionStore.hx | 3 + src/rewind/InputRecorder.hx | 62 ++++++++++++ 7 files changed, 303 insertions(+), 91 deletions(-) create mode 100644 src/rewind/InputRecorder.hx diff --git a/src/Console.hx b/src/Console.hx index aed1e5b2..aced50f2 100644 --- a/src/Console.hx +++ b/src/Console.hx @@ -163,6 +163,9 @@ class Console { } else { error("Expected one argument, got " + (cmdSplit.length - 1)); } + } else if (cmdType == 'rollback') { + var t = Std.parseFloat(cmdSplit[1]); + MarbleGame.instance.world.rollback(t); } else { error("Unknown command"); } diff --git a/src/Marble.hx b/src/Marble.hx index 126c6d86..500a3a91 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -482,6 +482,11 @@ class Marble extends GameObject { } else this._radius = avgRadius; + if (Net.isMP) { + this._radius = 0.2; // For the sake of physics + marbleDts.scale(0.2 / avgRadius); + } + this._prevRadius = this._radius; if (isUltra) { @@ -556,8 +561,8 @@ class Marble extends GameObject { public function getMarbleAxis() { var motiondir = new Vector(0, -1, 0); - // if (level.isReplayingMovement) - // return level.currentInputMoves[1].marbleAxes; + if (level.isReplayingMovement) + return level.currentInputMoves[1].marbleAxes; if (this.controllable && !this.isNetUpdate) { motiondir.transform(Matrix.R(0, 0, camera.CameraYaw)); motiondir.transform(level.newOrientationQuat.toMatrix()); @@ -663,6 +668,8 @@ class Marble extends GameObject { var R = currentGravityDir.multiply(-this._radius); var rollVelocity = this.omega.cross(R); var axes = this.getMarbleAxis(); + // if (!level.isReplayingMovement) + // level.inputRecorder.recordAxis(axes); var sideDir = axes[0]; var motionDir = axes[1]; var upDir = axes[2]; @@ -811,37 +818,37 @@ class Marble extends GameObject { } } } while (!done && itersIn < 1e4); // Maximum limit pls - // if (this.velocity.lengthSq() < 625) { - var gotOne = false; - var dir = new Vector(0, 0, 0); - for (j in 0...contacts.length) { - var dir2 = dir.add(contacts[j].normal); - if (dir2.lengthSq() < 0.01) { - dir2.load(dir2.add(contacts[j].normal)); - } - dir = dir2; - dir.normalize(); - gotOne = true; - } - if (gotOne) { - dir.normalize(); - var soFar = 0.0; - for (k in 0...contacts.length) { - var dist = this._radius - contacts[k].contactDistance; - var timeToSeparate = 0.1; - var vel = this.velocity.sub(contacts[k].velocity); - var outVel = vel.add(dir.multiply(soFar)).dot(contacts[k].normal); - if (dist > timeToSeparate * outVel) { - soFar += (dist - outVel * timeToSeparate) / timeToSeparate / contacts[k].normal.dot(dir); + if (this.velocity.lengthSq() < 625) { + var gotOne = false; + var dir = new Vector(0, 0, 0); + for (j in 0...contacts.length) { + var dir2 = dir.add(contacts[j].normal); + if (dir2.lengthSq() < 0.01) { + dir2.load(dir2.add(contacts[j].normal)); } + dir = dir2; + dir.normalize(); + gotOne = true; + } + if (gotOne) { + dir.normalize(); + var soFar = 0.0; + for (k in 0...contacts.length) { + var dist = this._radius - contacts[k].contactDistance; + var timeToSeparate = 0.1; + var vel = this.velocity.sub(contacts[k].velocity); + var outVel = vel.add(dir.multiply(soFar)).dot(contacts[k].normal); + if (dist > timeToSeparate * outVel) { + soFar += (dist - outVel * timeToSeparate) / timeToSeparate / contacts[k].normal.dot(dir); + } + } + if (soFar < -25) + soFar = -25; + if (soFar > 25) + soFar = 25; + this.velocity.load(this.velocity.add(dir.multiply(soFar))); } - if (soFar < -25) - soFar = -25; - if (soFar > 25) - soFar = 25; - this.velocity.load(this.velocity.add(dir.multiply(soFar))); } - // } return stoppedPaths; } @@ -1228,6 +1235,7 @@ class Marble extends GameObject { finalT = collisionTime; currentFinalPos = position.add(relVel.multiply(finalT)); found = true; + lastContactPos = currentFinalPos.clone(); // iterationFound = true; i += 3; // Debug.drawSphere(currentFinalPos, radius); @@ -1427,6 +1435,8 @@ class Marble extends GameObject { } function nudgeToContacts(position:Vector, radius:Float, foundContacts:Array, foundMarbles:Array) { + if (Net.isMP) + return position; var it = 0; var concernedContacts = foundContacts; // PathedInteriors have their own nudge logic var prevResolved = 0; @@ -1717,36 +1727,36 @@ class Marble extends GameObject { this.updateRollSound(timeState, contactTime / timeState.dt, this._slipAmount); - // if (this.megaMarbleUseTick > 0) { - // if (Net.isHost) { - // if ((timeState.ticks - this.megaMarbleUseTick) <= 312 && this.megaMarbleUseTick > 0) { - // this._radius = 0.675; - // this.collider.radius = 0.675; - // } else if ((timeState.ticks - this.megaMarbleUseTick) > 312) { - // this.collider.radius = this._radius = 0.3; - // if (!this.isNetUpdate && this.controllable) - // AudioManager.playSound(ResourceLoader.getResource("data/sound/MegaShrink.wav", ResourceLoader.getAudio, this.soundResources), null, - // false); - // this.megaMarbleUseTick = 0; - // this.netFlags |= MarbleNetFlags.DoMega; - // } - // } - // if (Net.isClient) { - // if (this.serverTicks - this.megaMarbleUseTick <= 312 && this.megaMarbleUseTick > 0) { - // this._radius = 0.675; - // this.collider.radius = 0.675; - // } else { - // this.collider.radius = this._radius = 0.3; - // if (!this.isNetUpdate && this.controllable) - // AudioManager.playSound(ResourceLoader.getResource("data/sound/MegaShrink.wav", ResourceLoader.getAudio, this.soundResources), null, - // false); - // this.megaMarbleUseTick = 0; - // } - // } - // } - // if (Net.isClient && this.megaMarbleUseTick == 0) { - // this.collider.radius = this._radius = 0.3; - // } + if (this.megaMarbleUseTick > 0) { + if (Net.isHost) { + if ((timeState.ticks - this.megaMarbleUseTick) <= 312 && this.megaMarbleUseTick > 0) { + this._radius = 0.675; + this.collider.radius = 0.675; + } else if ((timeState.ticks - this.megaMarbleUseTick) > 312) { + this.collider.radius = this._radius = 0.2; + if (!this.isNetUpdate && this.controllable) + AudioManager.playSound(ResourceLoader.getResource("data/sound/MegaShrink.wav", ResourceLoader.getAudio, this.soundResources), null, + false); + this.megaMarbleUseTick = 0; + this.netFlags |= MarbleNetFlags.DoMega; + } + } + if (Net.isClient) { + if (this.serverTicks - this.megaMarbleUseTick <= 312 && this.megaMarbleUseTick > 0) { + this._radius = 0.675; + this.collider.radius = 0.675; + } else { + this.collider.radius = this._radius = 0.2; + if (!this.isNetUpdate && this.controllable) + AudioManager.playSound(ResourceLoader.getResource("data/sound/MegaShrink.wav", ResourceLoader.getAudio, this.soundResources), null, + false); + this.megaMarbleUseTick = 0; + } + } + } + if (Net.isClient && this.megaMarbleUseTick == 0) { + this.collider.radius = this._radius = 0.2; + } if (Net.isMP) { if (m.jump && this.outOfBounds) { @@ -2035,6 +2045,8 @@ class Marble extends GameObject { if (this.controllable && !this.level.isWatching) { move = recordMove(); } + if (level.isReplayingMovement) + move = level.currentInputMoves[1].move; if (this.level.isWatching) { if (this.level.replay.currentPlaybackFrame.marbleStateFlags.has(Jumped)) @@ -2056,6 +2068,32 @@ class Marble extends GameObject { playedSounds = []; advancePhysics(timeState, move, collisionWorld, pathedInteriors); + // physicsAccumulator += timeState.dt; + + // while (physicsAccumulator > 0.032) { + // var adt = timeState.clone(); + // adt.dt = 0.032; + // advancePhysics(adt, move, collisionWorld, pathedInteriors); + // physicsAccumulator -= 0.032; + // } + // if (oldPos != null && newPos != null) { + // var deltaT = physicsAccumulator / 0.032; + // var renderPos = Util.lerpThreeVectors(this.oldPos, this.newPos, deltaT); + // this.setPosition(renderPos.x, renderPos.y, renderPos.z); + + // var rot = this.getRotationQuat(); + // var quat = new Quat(); + // quat.initRotation(omega.x * timeState.dt, omega.y * timeState.dt, omega.z * timeState.dt); + // quat.multiply(quat, rot); + // this.setRotationQuat(quat); + + // var adt = timeState.clone(); + // adt.dt = physicsAccumulator; + // for (pi in pathedInteriors) { + // pi.update(adt); + // } + // } + if (!this.level.isWatching) { if (this.level.isRecording) { this.level.replay.recordMarbleState(this.getAbsPos().getPosition(), this.velocity, this.getRotationQuat(), this.omega); diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 7628fbdb..61de6b27 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -1,5 +1,6 @@ package src; +import rewind.InputRecorder; import net.NetPacket.ScoreboardPacket; import net.NetPacket.PowerupPickupPacket; import net.Move; @@ -209,6 +210,10 @@ class MarbleWorld extends Scheduler { public var rewindManager:RewindManager; public var rewinding:Bool = false; + public var inputRecorder:InputRecorder; + public var isReplayingMovement:Bool = false; + public var currentInputMoves:Array; + // Multiplayer public var isMultiplayer:Bool; @@ -257,6 +262,7 @@ class MarbleWorld extends Scheduler { this.replay = new Replay(mission.path, mission.isClaMission ? mission.id : 0); this.isRecording = record; this.rewindManager = new RewindManager(cast this); + this.inputRecorder = new InputRecorder(cast this); this.isMultiplayer = multiplayer; if (this.isMultiplayer) { isRecording = false; @@ -761,7 +767,7 @@ class MarbleWorld extends Scheduler { if (isMultiplayer) { marble.megaMarbleUseTick = 0; marble.helicopterUseTick = 0; - marble.collider.radius = marble._radius = 0.3; + // marble.collider.radius = marble._radius = 0.3; @:privateAccess marble.netFlags |= MarbleNetFlags.DoHelicopter | MarbleNetFlags.DoMega | MarbleNetFlags.GravityChange; } else { @:privateAccess marble.helicopterEnableTime = -1e8; @@ -1496,11 +1502,44 @@ class MarbleWorld extends Scheduler { } } + public function rollback(t:Float) { + var newT = timeState.currentAttemptTime - t; + var rewindFrame = rewindManager.getNextRewindFrame(timeState.currentAttemptTime - t); + rewindManager.applyFrame(rewindFrame); + this.isReplayingMovement = true; + this.currentInputMoves = this.inputRecorder.getMovesFrom(timeState.currentAttemptTime); + } + + public function advanceWorld(dt:Float) { + ProfilerUI.measure("updateTimer"); + this.updateTimer(dt); + this.tickSchedule(timeState.currentAttemptTime); + + this.updateGameState(); + ProfilerUI.measure("updateDTS"); + for (obj in dtsObjects) { + obj.update(timeState); + } + for (obj in triggers) { + obj.update(timeState); + } + + ProfilerUI.measure("updateMarbles"); + marble.update(timeState, collisionWorld, this.pathedInteriors); + for (client => marble in clientMarbles) { + marble.update(timeState, collisionWorld, this.pathedInteriors); + } + } + public function update(dt:Float) { if (!_ready) { return; } + if (Key.isPressed(Key.T)) { + rollback(0.4); + } + var realDt = dt; if ((Key.isDown(Settings.controlsSettings.rewind) @@ -1558,6 +1597,31 @@ class MarbleWorld extends Scheduler { if (dt < 0) return; + if (this.isReplayingMovement) { + trace('Rollback start'); + while (this.currentInputMoves.length > 1) { + while (this.currentInputMoves[1].time <= timeState.currentAttemptTime) { + this.currentInputMoves = this.currentInputMoves.slice(1); + if (this.currentInputMoves.length == 1) + break; + } + if (this.currentInputMoves.length > 1) { + dt = this.currentInputMoves[1].time - this.currentInputMoves[0].time; + } + + if (this.isReplayingMovement) { + if (this.timeState.currentAttemptTime != this.currentInputMoves[0].time) + trace("fucked"); + } + + if (this.currentInputMoves.length > 1) { + advanceWorld(dt); + trace('Position: ${@:privateAccess marble.newPos.sub(currentInputMoves[1].pos).length()}. Vel: ${marble.velocity.sub(currentInputMoves[1].velocity).length()}'); + } + } + this.isReplayingMovement = false; + } + ProfilerUI.measure("updateTimer"); this.updateTimer(dt); @@ -1615,6 +1679,11 @@ class MarbleWorld extends Scheduler { for (obj in triggers) { obj.update(timeState); } + + // if (!isReplayingMovement) { + // inputRecorder.recordInput(timeState.currentAttemptTime); + // } + ProfilerUI.measure("updateMarbles"); if (this.isMultiplayer) { tickAccumulator += timeState.dt; @@ -1726,6 +1795,10 @@ class MarbleWorld extends Scheduler { if (!this.rewinding && Settings.optionsSettings.rewindEnabled && !this.isMultiplayer) this.rewindManager.recordFrame(); + // if (!this.isReplayingMovement) { + // inputRecorder.recordMarble(); + // } + _instancesNeedsUpdate = true; this.updateTexts(); diff --git a/src/modes/HuntMode.hx b/src/modes/HuntMode.hx index e76bae06..0ba534f2 100644 --- a/src/modes/HuntMode.hx +++ b/src/modes/HuntMode.hx @@ -1,5 +1,8 @@ package modes; +import net.BitStream.OutputBitStream; +import net.NetPacket.GemPickupPacket; +import net.NetPacket.GemSpawnPacket; import octree.IOctreeObject; import octree.IOctreeObject.RayIntersectionData; import h3d.col.Bounds; @@ -17,6 +20,7 @@ import src.Mission; import src.Marble; import src.AudioManager; import src.ResourceLoader; +import net.Net; @:structInit @:publicFields @@ -142,10 +146,7 @@ class HuntMode extends NullMode { return null; } - function setupGems() { - hideExisting(); - this.activeGems = []; - this.activeGemSpawnGroup = []; + function prepareGems() { if (this.gemSpawnPoints == null) { this.gemOctree = new Octree(); this.gemSpawnPoints = []; @@ -158,6 +159,13 @@ class HuntMode extends NullMode { gem.setHide(true); } } + } + + function setupGems() { + hideExisting(); + this.activeGems = []; + this.activeGemSpawnGroup = []; + prepareGems(); spawnHuntGems(); } @@ -227,6 +235,15 @@ class HuntMode extends NullMode { } activeGemSpawnGroup = spawnSet; + if (level.isMultiplayer && Net.isHost) { + var bs = new OutputBitStream(); + bs.writeByte(GemSpawn); + var packet = new GemSpawnPacket(); + packet.gemIds = spawnSet; + packet.serialize(bs); + Net.sendPacketToIngame(bs); + } + lastSpawn = furthest; } @@ -309,21 +326,25 @@ class HuntMode extends NullMode { points = 0; } + override function onClientRestart() { + prepareGems(); + } + override function onGemPickup(marble:Marble, gem:Gem) { - // if ((@:privateAccess !marble.isNetUpdate && Net.isHost) || !Net.isMP) { - if (marble == level.marble) - AudioManager.playSound(ResourceLoader.getResource('data/sound/gotgem.wav', ResourceLoader.getAudio, @:privateAccess this.level.soundResources)); - else - AudioManager.playSound(ResourceLoader.getResource('data/sound/opponent_gem_collect.wav', ResourceLoader.getAudio, - @:privateAccess this.level.soundResources)); - // } + if ((@:privateAccess !marble.isNetUpdate && Net.isHost) || !Net.isMP) { + if (marble == level.marble) + AudioManager.playSound(ResourceLoader.getResource('data/sound/gotgem.wav', ResourceLoader.getAudio, @:privateAccess this.level.soundResources)); + else + AudioManager.playSound(ResourceLoader.getResource('data/sound/opponentdiamond.wav', ResourceLoader.getAudio, + @:privateAccess this.level.soundResources)); + } activeGems.remove(gem); var beam = gemToBeamMap.get(gem); beam.setHide(true); - // if (!this.level.isMultiplayer || Net.isHost) { - spawnHuntGems(); - // } + if (!this.level.isMultiplayer || Net.isHost) { + spawnHuntGems(); + } var incr = 0; switch (gem.gemColor) { @@ -352,24 +373,24 @@ class HuntMode extends NullMode { } } - // if (this.level.isMultiplayer && Net.isHost) { - // var packet = new GemPickupPacket(); - // packet.clientId = @:privateAccess marble.connection == null ? 0 : @:privateAccess marble.connection.id; - // packet.gemId = gem.netIndex; - // packet.serverTicks = level.timeState.ticks; - // packet.scoreIncr = incr; - // var os = new OutputBitStream(); - // os.writeByte(GemPickup); - // packet.serialize(os); - // Net.sendPacketToIngame(os); + if (this.level.isMultiplayer && Net.isHost) { + var packet = new GemPickupPacket(); + packet.clientId = @:privateAccess marble.connection == null ? 0 : @:privateAccess marble.connection.id; + packet.gemId = gem.netIndex; + packet.serverTicks = level.timeState.ticks; + packet.scoreIncr = incr; + var os = new OutputBitStream(); + os.writeByte(GemPickup); + packet.serialize(os); + Net.sendPacketToIngame(os); - // Settings.playStatistics.totalMPScore += incr; + // Settings.playStatistics.totalMPScore += incr; - // @:privateAccess level.playGui.incrementPlayerScore(packet.clientId, packet.scoreIncr); - // } - // if (this.level.isMultiplayer && Net.isClient) { - // gem.pickUpClient = @:privateAccess marble.connection == null ? Net.clientId : @:privateAccess marble.connection.id; - // } + // @:privateAccess level.playGui.incrementPlayerScore(packet.clientId, packet.scoreIncr); + } + if (this.level.isMultiplayer && Net.isClient) { + gem.pickUpClient = @:privateAccess marble.connection == null ? Net.clientId : @:privateAccess marble.connection.id; + } } override public function timeMultiplier() { diff --git a/src/net/BitStream.hx b/src/net/BitStream.hx index 7210db53..e07bb73e 100644 --- a/src/net/BitStream.hx +++ b/src/net/BitStream.hx @@ -68,6 +68,12 @@ class InputBitStream { return FPHelper.i32ToFloat(readInt32()); } + public function readDouble() { + var lo = readInt32(); + var hi = readInt32(); + return FPHelper.i64ToDouble(lo, hi); + } + public function readString() { var length = readUInt16(); var str = ""; @@ -151,4 +157,10 @@ class OutputBitStream { writeByte(StringTools.fastCodeAt(value, i)); } } + + public function writeDouble(value:Float) { + var i64 = FPHelper.doubleToI64(value); + writeInt32(i64.low); + writeInt32(i64.high); + } } diff --git a/src/net/MarblePredictionStore.hx b/src/net/MarblePredictionStore.hx index 7f7da41a..742ff878 100644 --- a/src/net/MarblePredictionStore.hx +++ b/src/net/MarblePredictionStore.hx @@ -29,6 +29,9 @@ class MarblePrediction { var subs = position.sub(p.position).lengthSq(); // + velocity.sub(p.velocity).lengthSq() + omega.sub(p.omega).lengthSq(); if (p.netFlags != 0) subs += 1; + if (subs > 0.01) { + trace('Desync: ${position.x} ${position.y} ${position.z} != ${p.position.x} ${p.position.y} ${p.position.z}'); + } // if (p.powerUpId != powerupItemId) // if (tick % 10 == 0) // subs += 1; // temp diff --git a/src/rewind/InputRecorder.hx b/src/rewind/InputRecorder.hx new file mode 100644 index 00000000..7cf21750 --- /dev/null +++ b/src/rewind/InputRecorder.hx @@ -0,0 +1,62 @@ +package rewind; + +import src.MarbleWorld; +import h3d.Vector; +import net.Move; + +@:publicFields +class InputRecorderFrame { + var time:Float; + var move:Move; + var marbleAxes:Array; + var pos:Vector; + var velocity:Vector; + + public function new() {} +} + +class InputRecorder { + var frames:Array; + var level:MarbleWorld; + + public function new(level:MarbleWorld) { + frames = []; + this.level = level; + } + + public function recordInput(t:Float) { + var frame = new InputRecorderFrame(); + frame.time = t; + frame.move = level.marble.recordMove(); + frames.push(frame); + } + + public function recordMarble() { + frames[frames.length - 1].pos = @:privateAccess level.marble.newPos?.clone(); + frames[frames.length - 1].velocity = level.marble.velocity.clone(); + } + + public function recordAxis(axis:Array) { + frames[frames.length - 1].marbleAxes = axis.copy(); + } + + public function getMovesFrom(t:Float) { + if (frames.length == 0) + return []; + var start = 0; + var end = frames.length - 1; + var mid = Std.int(frames.length / 2); + while (end - start > 1) { + mid = Std.int((start / 2) + (end / 2)); + if (frames[mid].time < t) { + start = mid + 1; + } else if (frames[mid].time > t) { + end = mid - 1; + } else { + start = end = mid; + } + } + + return frames.slice(start - 1); + } +}