make hunt network again, and fix marble radius for MP

This commit is contained in:
RandomityGuy 2024-06-16 20:25:17 +05:30
parent d20d3a1e90
commit 141a3f892b
7 changed files with 303 additions and 91 deletions

View file

@ -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");
}

View file

@ -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<MarbleTestMoveFoundContact>, foundMarbles:Array<SphereCollisionEntity>) {
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);

View file

@ -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<InputRecorderFrame>;
// 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();

View file

@ -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() {

View file

@ -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);
}
}

View file

@ -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

View file

@ -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<Vector>;
var pos:Vector;
var velocity:Vector;
public function new() {}
}
class InputRecorder {
var frames:Array<InputRecorderFrame>;
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<Vector>) {
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);
}
}