rough trapdoor networking, bare minimum

This commit is contained in:
RandomityGuy 2024-06-26 19:20:22 +05:30
parent cc9cc2ab3a
commit f990ed02a7
6 changed files with 188 additions and 13 deletions

View file

@ -330,6 +330,7 @@ class Marble extends GameObject {
var serverTicks:Int;
var recvServerTick:Int;
var serverUsePowerup:Bool;
var trapdoorContacts:Map<Int, Int> = [];
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) {

View file

@ -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<ForceObject> = [];
public var explodables:Array<Explodable> = [];
public var explodablesToTick:Array<Int> = [];
public var trapdoors:Array<Trapdoor> = [];
public var trapdoorsToTick:Array<Int> = [];
public var triggers:Array<Trigger> = [];
public var gems:Array<Gem> = [];
public var namedObjects:Map<String, {obj:DtsObject, elem:MissionElementBase}> = [];
@ -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();
}

View file

@ -15,6 +15,7 @@ class OtherMarbleUpdate {
var lastShockAbsorberTick:Int;
var lastPowerUpId:Int;
var lastGravityUp:Vector;
var lastTrapdoorUpdates:Map<Int, Int> = [];
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;

View file

@ -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<Int, Int> = [];
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;
}
}
}

View file

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

View file

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