make nukes network successfully

This commit is contained in:
RandomityGuy 2024-06-26 01:43:22 +05:30
parent a33fb533df
commit a8d316d4fd
9 changed files with 275 additions and 146 deletions

View file

@ -1,5 +1,7 @@
package src;
import shapes.Explodable;
import net.ExplodablePredictionStore;
import gui.MPPreGameDlg;
import src.Radar;
import rewind.InputRecorder;
@ -145,6 +147,7 @@ class MarbleWorld extends Scheduler {
public var dtsObjects:Array<DtsObject> = [];
public var powerUps:Array<PowerUp> = [];
public var forceObjects:Array<ForceObject> = [];
public var explodables:Array<Explodable> = [];
public var triggers:Array<Trigger> = [];
public var gems:Array<Gem> = [];
public var namedObjects:Map<String, {obj:DtsObject, elem:MissionElementBase}> = [];
@ -230,6 +233,7 @@ class MarbleWorld extends Scheduler {
var predictions:MarblePredictionStore;
var powerupPredictions:PowerupPredictionStore;
var gemPredictions:GemPredictionStore;
var explodablePredictions:ExplodablePredictionStore;
public var lastMoves:MarbleUpdateQueue;
@ -274,6 +278,7 @@ class MarbleWorld extends Scheduler {
predictions = new MarblePredictionStore();
powerupPredictions = new PowerupPredictionStore();
gemPredictions = new GemPredictionStore();
explodablePredictions = new ExplodablePredictionStore();
}
}
@ -613,6 +618,7 @@ class MarbleWorld extends Scheduler {
predictions = new MarblePredictionStore();
powerupPredictions = new PowerupPredictionStore();
gemPredictions = new GemPredictionStore();
explodablePredictions = new ExplodablePredictionStore();
}
}
@ -1171,6 +1177,13 @@ class MarbleWorld extends Scheduler {
if (obj is ForceObject) {
this.forceObjects.push(cast obj);
}
if (obj is Explodable) {
var exp:Explodable = cast obj;
exp.netId = this.explodables.length;
this.explodables.push(exp);
if (Net.isClient)
explodablePredictions.alloc();
}
obj.isTSStatic = isTsStatic;
obj.init(cast this, () -> {
obj.update(this.timeState);
@ -1485,7 +1498,7 @@ class MarbleWorld extends Scheduler {
// if (marbleNeedsPrediction & (1 << Net.clientId) > 0) { // Only for our clients pls
// if (qm != null) {
// var mvs = qm.powerupStates.copy();
for (pw in marble.level.powerUps) {
for (pw in powerUps) {
// var val = mvs.shift();
// if (pw.lastPickUpTime != val)
// Console.log('Revert powerup pickup: ${pw.lastPickUpTime} -> ${val}');
@ -1493,6 +1506,9 @@ class MarbleWorld extends Scheduler {
if (pw.pickupClient != -1 && marbleNeedsPrediction & (1 << pw.pickupClient) > 0)
pw.lastPickUpTime = powerupPredictions.getState(pw.netIndex);
}
for (exp in explodables) {
exp.revertContactTicks(explodablePredictions.getState(exp.netId));
}
var huntMode:HuntMode = cast this.gameMode;
if (@:privateAccess huntMode.activeGemSpawnGroup != null) {
for (activeGem in @:privateAccess huntMode.activeGemSpawnGroup) {
@ -2807,7 +2823,8 @@ class MarbleWorld extends Scheduler {
dtsObject.dispose();
}
dtsObjects = null;
powerUps = [];
powerUps = null;
explodables = null;
for (trigger in this.triggers) {
trigger.dispose();
}

View file

@ -65,9 +65,9 @@ class Particle {
public function update(time:Float, dt:Float) {
var t = dt;
var a = this.acc;
a.load(a.sub(this.vel.multiply(this.o.dragCoefficient)));
this.vel.load(this.vel.add(a.multiply(dt)));
this.position.load(this.position.add(this.vel.multiply(dt)));
a = a.sub(this.vel.multiply(this.o.dragCoefficient));
this.vel = this.vel.add(a.multiply(dt));
this.position = this.position.add(this.vel.multiply(dt));
this.currentAge += dt;

View file

@ -429,7 +429,7 @@ class Util {
#end
}
public static inline inline function isIOS() {
public static inline function isIOS() {
#if js
var reg = ~/iPad|iPhone|iPod/;
return reg.match(js.Browser.navigator.userAgent);

View file

@ -0,0 +1,25 @@
package net;
import net.NetPacket.ExplodableUpdatePacket;
import src.TimeState;
import net.NetPacket.PowerupPickupPacket;
class ExplodablePredictionStore {
var predictions:Array<Int>;
public inline function new() {
predictions = [];
}
public inline function alloc() {
predictions.push(-100000);
}
public inline function getState(netIndex:Int) {
return predictions[netIndex];
}
public inline function acknowledgeExplodableUpdate(packet:ExplodableUpdatePacket) {
predictions[packet.explodableId] = packet.serverTicks;
}
}

View file

@ -1,5 +1,6 @@
package net;
import net.NetPacket.ExplodableUpdatePacket;
import gui.MPMessageGui;
import gui.MessageBoxOkDlg;
import gui.JoinServerGui;
@ -37,6 +38,7 @@ enum abstract NetPacketType(Int) from Int to Int {
var PowerupPickup;
var GemSpawn;
var GemPickup;
var ExplodableUpdate;
var PlayerInfo;
var ScoreBoardInfo;
}
@ -784,6 +786,13 @@ class Net {
@:privateAccess MarbleGame.instance.world.playGui.updatePlayerScores(scoreboardPacket);
}
case ExplodableUpdate:
var explodableUpdatePacket = new ExplodableUpdatePacket();
explodableUpdatePacket.deserialize(input);
if (MarbleGame.instance.world != null && !MarbleGame.instance.world._disposed) {
@:privateAccess MarbleGame.instance.world.explodablePredictions.acknowledgeExplodableUpdate(explodableUpdatePacket);
}
case _:
Console.log("unknown command: " + packetType);
}

View file

@ -208,6 +208,24 @@ class PowerupPickupPacket implements NetPacket {
}
}
@:publicFields
class ExplodableUpdatePacket implements NetPacket {
var serverTicks:Int;
var explodableId:Int;
public function new() {}
public inline function deserialize(b:InputBitStream) {
serverTicks = b.readUInt16();
explodableId = b.readInt(11);
}
public inline function serialize(b:OutputBitStream) {
b.writeUInt16(serverTicks);
b.writeInt(explodableId, 11);
}
}
@:publicFields
class GemSpawnPacket implements NetPacket {
var gemIds:Array<Int>;

154
src/shapes/Explodable.hx Normal file
View file

@ -0,0 +1,154 @@
package shapes;
import src.ParticleSystem.ParticleEmitter;
import src.Marble;
import h3d.Vector;
import src.ParticleSystem.ParticleEmitterOptions;
import net.BitStream.OutputBitStream;
import net.NetPacket.ExplodableUpdatePacket;
import collision.CollisionInfo;
import src.ParticleSystem.ParticleData;
import src.DtsObject;
import src.TimeState;
import src.Util;
import net.Net;
import src.MarbleWorld;
import src.ResourceLoader;
import src.AudioManager;
abstract class Explodable extends DtsObject {
var particle:ParticleEmitterOptions;
var smokeParticle:ParticleEmitterOptions;
var sparksParticle:ParticleEmitterOptions;
var particleData:ParticleData;
var smokeParticleData:ParticleData;
var sparkParticleData:ParticleData;
var disappearTime = -1e8;
var lastContactTick:Int = -100000;
var renewTime = 5000;
var explodeSoundFile:String = "data/sound/explode1.wav";
var emitter1:ParticleEmitter;
var emitter2:ParticleEmitter;
var emitter3:ParticleEmitter;
public var netId:Int;
override function update(timeState:TimeState) {
super.update(timeState);
if (Net.isMP) {
if (Net.isHost) {
if (timeState.ticks >= this.lastContactTick + (renewTime >> 5) || timeState.ticks < this.lastContactTick) {
this.setHide(false);
} else {
this.setHide(true);
}
var opacity = Util.clamp((timeState.ticks - (this.lastContactTick + (renewTime >> 5))), 0, 1);
this.setOpacity(opacity);
} else {
if (@:privateAccess level.marble.serverTicks >= this.lastContactTick + (renewTime >> 5) || @:privateAccess level.marble.serverTicks < this.lastContactTick) {
this.setHide(false);
} else {
this.setHide(true);
}
var opacity = Util.clamp((@:privateAccess level.marble.serverTicks - (this.lastContactTick + (renewTime >> 5))), 0, 1);
this.setOpacity(opacity);
}
} else {
if (timeState.timeSinceLoad >= this.disappearTime + (renewTime / 1000) || timeState.timeSinceLoad < this.disappearTime) {
this.setHide(false);
} else {
this.setHide(true);
}
var opacity = Util.clamp((timeState.timeSinceLoad - (this.disappearTime + (renewTime / 1000))), 0, 1);
this.setOpacity(opacity);
}
}
public override function init(level:MarbleWorld, onFinish:Void->Void) {
super.init(level, () -> {
ResourceLoader.load(explodeSoundFile).entry.load(onFinish);
});
}
override function onMarbleContact(marble:src.Marble, timeState:TimeState, ?contact:CollisionInfo) {
if (this.isCollideable && !this.level.rewinding) {
// marble.velocity = marble.velocity.add(vec);
this.disappearTime = timeState.timeSinceLoad;
if (Net.isClient) {
this.lastContactTick = @:privateAccess marble.serverTicks;
} else {
this.lastContactTick = timeState.ticks;
}
this.setCollisionEnabled(false);
if (!this.level.rewinding && @:privateAccess !marble.isNetUpdate)
AudioManager.playSound(ResourceLoader.getResource(explodeSoundFile, ResourceLoader.getAudio, this.soundResources));
if (@:privateAccess !marble.isNetUpdate) {
emitter1 = this.level.particleManager.createEmitter(particle, particleData, this.getAbsPos().getPosition());
emitter2 = this.level.particleManager.createEmitter(smokeParticle, smokeParticleData, this.getAbsPos().getPosition());
emitter3 = this.level.particleManager.createEmitter(sparksParticle, sparkParticleData, this.getAbsPos().getPosition());
}
// var minePos = this.getAbsPos().getPosition();
// var off = marble.getAbsPos().getPosition().sub(minePos);
// var strength = computeExplosionStrength(off.length());
// var impulse = off.normalized().multiply(strength);
applyImpulse(marble);
if (Net.isHost) {
var packet = new ExplodableUpdatePacket();
packet.explodableId = netId;
packet.serverTicks = timeState.ticks;
var os = new OutputBitStream();
os.writeByte(ExplodableUpdate);
packet.serialize(os);
Net.sendPacketToIngame(os);
}
// light = new h3d.scene.fwd.PointLight(MarbleGame.instance.scene);
// light.setPosition(minePos.x, minePos.y, minePos.z);
// light.enableSpecular = false;
// for (collider in this.colliders) {
// var hull:CollisionHull = cast collider;
// hull.force = strength;
// }
}
// Normally, we would add a light here, but that's too expensive for THREE, apparently.
// this.level.replay.recordMarbleContact(this);
}
public function revertContactTicks(ticks:Int) {
this.lastContactTick = ticks;
if (level.timeState.ticks >= this.lastContactTick + (renewTime >> 5) || level.timeState.ticks < this.lastContactTick) {
if (emitter1 != null) {
this.level.particleManager.removeEmitter(emitter1);
emitter1 = null;
}
if (emitter2 != null) {
this.level.particleManager.removeEmitter(emitter2);
emitter2 = null;
}
if (emitter3 != null) {
this.level.particleManager.removeEmitter(emitter3);
emitter3 = null;
}
}
}
abstract function applyImpulse(marble:Marble):Void;
}

View file

@ -1,5 +1,8 @@
package shapes;
import net.BitStream.OutputBitStream;
import net.NetPacket.ExplodableUpdatePacket;
import net.Net;
import src.AudioManager;
import src.TimeState;
import collision.CollisionHull;
@ -90,13 +93,7 @@ final landMineSparksParticle:ParticleEmitterOptions = {
}
};
class LandMine extends DtsObject {
var disappearTime = -1e8;
var landMineParticleData:ParticleData;
var landMineSmokeParticleData:ParticleData;
var landMineSparkParticleData:ParticleData;
class LandMine extends Explodable {
var light:h3d.scene.fwd.PointLight;
public function new() {
@ -105,57 +102,21 @@ class LandMine extends DtsObject {
this.identifier = "LandMine";
this.isCollideable = true;
landMineParticleData = new ParticleData();
landMineParticleData.identifier = "landMineParticle";
landMineParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);
particleData = new ParticleData();
particleData.identifier = "landMineParticle";
particleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);
landMineSmokeParticleData = new ParticleData();
landMineSmokeParticleData.identifier = "landMineSmokeParticle";
landMineSmokeParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);
smokeParticleData = new ParticleData();
smokeParticleData.identifier = "landMineSmokeParticle";
smokeParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);
landMineSparkParticleData = new ParticleData();
landMineSparkParticleData.identifier = "landMineSparkParticle";
landMineSparkParticleData.texture = ResourceLoader.getResource("data/particles/spark.png", ResourceLoader.getTexture, this.textureResources);
}
sparkParticleData = new ParticleData();
sparkParticleData.identifier = "landMineSparkParticle";
sparkParticleData.texture = ResourceLoader.getResource("data/particles/spark.png", ResourceLoader.getTexture, this.textureResources);
public override function init(level:MarbleWorld, onFinish:Void->Void) {
super.init(level, () -> {
ResourceLoader.load("sound/explode1.wav").entry.load(onFinish);
});
}
override function onMarbleContact(marble:src.Marble, timeState:TimeState, ?contact:CollisionInfo) {
if (this.isCollideable && !this.level.rewinding) {
// marble.velocity = marble.velocity.add(vec);
this.disappearTime = timeState.timeSinceLoad;
this.setCollisionEnabled(false);
if (!this.level.rewinding)
AudioManager.playSound(ResourceLoader.getResource("data/sound/explode1.wav", ResourceLoader.getAudio, this.soundResources));
this.level.particleManager.createEmitter(landMineParticle, landMineParticleData, this.getAbsPos().getPosition());
this.level.particleManager.createEmitter(landMineSmokeParticle, landMineSmokeParticleData, this.getAbsPos().getPosition());
this.level.particleManager.createEmitter(landMineSparksParticle, landMineSparkParticleData, this.getAbsPos().getPosition());
var minePos = this.getAbsPos().getPosition();
var off = marble.getAbsPos().getPosition().sub(minePos);
var strength = computeExplosionStrength(off.length());
var impulse = off.normalized().multiply(strength);
marble.applyImpulse(impulse);
// light = new h3d.scene.fwd.PointLight(MarbleGame.instance.scene);
// light.setPosition(minePos.x, minePos.y, minePos.z);
// light.enableSpecular = false;
// for (collider in this.colliders) {
// var hull:CollisionHull = cast collider;
// hull.force = strength;
// }
}
// Normally, we would add a light here, but that's too expensive for THREE, apparently.
// this.level.replay.recordMarbleContact(this);
this.smokeParticle = landMineSmokeParticle;
this.sparksParticle = landMineSparksParticle;
this.particle = landMineParticle;
}
function computeExplosionStrength(r:Float) {
@ -172,28 +133,13 @@ class LandMine extends DtsObject {
return v;
}
override function update(timeState:TimeState) {
super.update(timeState);
if (timeState.timeSinceLoad >= this.disappearTime + 5 || timeState.timeSinceLoad < this.disappearTime) {
this.setHide(false);
} else {
this.setHide(true);
}
public function applyImpulse(marble:src.Marble) {
var minePos = this.getAbsPos().getPosition();
var off = marble.getAbsPos().getPosition().sub(minePos);
// if (light != null) {
// var t = Util.clamp((timeState.timeSinceLoad - this.disappearTime) / 1.2, 0, 1);
var strength = computeExplosionStrength(off.length());
// light.color = Util.lerpThreeVectors(new Vector(0.5, 0.5, 0), new Vector(0, 0, 0), t);
// var radius = Util.lerp(6, 3, t);
// light.params = new Vector(0, 1 / radius, 0);
// if (t >= 1) {
// light.remove();
// light = null;
// }
// }
var opacity = Util.clamp((timeState.timeSinceLoad - (this.disappearTime + 5)), 0, 1);
this.setOpacity(opacity);
var impulse = off.normalized().multiply(strength);
marble.applyImpulse(impulse);
}
}

View file

@ -1,5 +1,7 @@
package shapes;
import net.BitStream.OutputBitStream;
import net.NetPacket.ExplodableUpdatePacket;
import src.AudioManager;
import src.TimeState;
import collision.CollisionHull;
@ -11,6 +13,7 @@ import src.ParticleSystem.ParticleData;
import h3d.Vector;
import src.ResourceLoader;
import src.MarbleWorld;
import net.Net;
final nukeParticle:ParticleEmitterOptions = {
ejectionPeriod: 0.2,
@ -89,71 +92,31 @@ final nukeSparksParticle:ParticleEmitterOptions = {
}
};
class Nuke extends DtsObject {
var disappearTime = -1e8;
var nukeParticleData:ParticleData;
var nukeSmokeParticleData:ParticleData;
var nukeSparkParticleData:ParticleData;
class Nuke extends Explodable {
public function new() {
super();
dtsPath = "data/shapes/hazards/nuke/nuke.dts";
this.identifier = "Nuke";
this.isCollideable = true;
nukeParticleData = new ParticleData();
nukeParticleData.identifier = "nukeParticle";
nukeParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);
particleData = new ParticleData();
particleData.identifier = "nukeParticle";
particleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);
nukeSmokeParticleData = new ParticleData();
nukeSmokeParticleData.identifier = "nukeSmokeParticle";
nukeSmokeParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);
smokeParticleData = new ParticleData();
smokeParticleData.identifier = "nukeSmokeParticle";
smokeParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);
nukeSparkParticleData = new ParticleData();
nukeSparkParticleData.identifier = "nukeSparkParticle";
nukeSparkParticleData.texture = ResourceLoader.getResource("data/particles/spark.png", ResourceLoader.getTexture, this.textureResources);
}
sparkParticleData = new ParticleData();
sparkParticleData.identifier = "nukeSparkParticle";
sparkParticleData.texture = ResourceLoader.getResource("data/particles/spark.png", ResourceLoader.getTexture, this.textureResources);
public override function init(level:MarbleWorld, onFinish:Void->Void) {
super.init(level, () -> {
ResourceLoader.load("sound/nukeexplode.wav").entry.load(onFinish);
});
}
particle = nukeParticle;
smokeParticle = nukeSmokeParticle;
sparksParticle = nukeSparksParticle;
override function onMarbleContact(marble:src.Marble, timeState:TimeState, ?contact:CollisionInfo) {
if (this.isCollideable && !this.level.rewinding) {
// marble.velocity = marble.velocity.add(vec);
this.disappearTime = timeState.timeSinceLoad;
this.setCollisionEnabled(false);
// if (!this.level.rewinding)
if (@:privateAccess !marble.isNetUpdate) {
AudioManager.playSound(ResourceLoader.getResource("data/sound/nukeexplode.wav", ResourceLoader.getAudio, this.soundResources));
this.level.particleManager.createEmitter(nukeParticle, nukeParticleData, this.getAbsPos().getPosition());
this.level.particleManager.createEmitter(nukeSmokeParticle, nukeSmokeParticleData, this.getAbsPos().getPosition());
this.level.particleManager.createEmitter(nukeSparksParticle, nukeSparkParticleData, this.getAbsPos().getPosition());
}
var minePos = this.getAbsPos().getPosition();
var dtsCenter = this.dts.bounds.center();
// dtsCenter.x = -dtsCenter.x;
// minePos.x += dtsCenter.x;
// minePos.y += dtsCenter.y;
// minePos.z += dtsCenter.z;
var off = marble.getAbsPos().getPosition().sub(minePos);
var force = computeExplosionForce(off);
marble.applyImpulse(force, true);
// for (collider in this.colliders) {
// var hull:CollisionHull = cast collider;
// hull.force = strength;
// }
}
// Normally, we would add a light here, but that's too expensive for THREE, apparently.
// this.level.replay.recordMarbleContact(this);
renewTime = 15000;
explodeSoundFile = "data/sound/nukeexplode.wav";
}
function computeExplosionForce(distVec:Vector) {
@ -169,15 +132,12 @@ class Nuke extends DtsObject {
return distVec;
}
override function update(timeState:TimeState) {
super.update(timeState);
if (timeState.timeSinceLoad >= this.disappearTime + 15 || timeState.timeSinceLoad < this.disappearTime) {
this.setHide(false);
} else {
this.setHide(true);
}
public function applyImpulse(marble:src.Marble) {
var minePos = this.getAbsPos().getPosition();
var dtsCenter = this.dts.bounds.center();
var off = marble.getAbsPos().getPosition().sub(minePos);
var opacity = Util.clamp((timeState.timeSinceLoad - (this.disappearTime + 15)), 0, 1);
this.setOpacity(opacity);
var force = computeExplosionForce(off);
marble.applyImpulse(force, true);
}
}