diff --git a/src/ParticleSystem.hx b/src/ParticleSystem.hx index cf0d2bc1..58a9004f 100644 --- a/src/ParticleSystem.hx +++ b/src/ParticleSystem.hx @@ -67,9 +67,9 @@ class Particle { public function update(time:Float, dt:Float) { var t = dt; var a = this.acc; - 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)); + 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))); this.currentAge += dt; @@ -257,7 +257,7 @@ class ParticleEmitter { this.currentWaitPeriod = this.o.ejectionPeriod; var pos = this.getPosAtTime(time).clone(); if (this.o.spawnOffset != null) - pos = pos.add(this.o.spawnOffset()); // Call the spawnOffset function if it's there + pos.load(pos.add(this.o.spawnOffset())); // Call the spawnOffset function if it's there // This isn't necessarily uniform but it's fine for the purpose. var randomPointOnSphere = new Vector(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1).normalized(); randomPointOnSphere.x *= this.spawnSphereSquish.x; diff --git a/src/Util.hx b/src/Util.hx index 4def1d34..3d495e98 100644 --- a/src/Util.hx +++ b/src/Util.hx @@ -297,7 +297,7 @@ class Util { return false; } - public static function formatScore(score:Float) { + public static inline function formatScore(score:Float) { var scoreInt = Std.int(Math.round(score)); return '${scoreInt}'; } @@ -349,7 +349,7 @@ class Util { return str; } - public static function getKeyForButton(button:Int) { + public static inline function getKeyForButton(button:Int) { var keyName = Key.getKeyName(button); if (keyName == "MouseLeft") keyName = "the Left Mouse Button"; @@ -362,7 +362,7 @@ class Util { return keyName; } - public static function getKeyForButton2(button:Int) { + public static inline function getKeyForButton2(button:Int) { var keyName = Key.getKeyName(button); if (keyName == "MouseLeft") keyName = "Left Mouse"; diff --git a/src/gui/EndGameGui.hx b/src/gui/EndGameGui.hx index db94c59c..6d952ccd 100644 --- a/src/gui/EndGameGui.hx +++ b/src/gui/EndGameGui.hx @@ -1,5 +1,6 @@ package gui; +import net.Net; import modes.GameMode.ScoreType; import mis.MisParser; import hxd.BitmapData; @@ -154,14 +155,16 @@ class EndGameGui extends GuiImage { bottomBar.vertSizing = Bottom; innerCtrl.addChild(bottomBar); - var retryButton = new GuiXboxButton("Retry", 160); - retryButton.position = new Vector(400, 0); - retryButton.vertSizing = Bottom; - retryButton.horizSizing = Right; - retryButton.gamepadAccelerator = ["B"]; - retryButton.accelerators = [hxd.Key.ESCAPE, hxd.Key.BACKSPACE]; - retryButton.pressedAction = (e) -> restartFunc(retryButton); - bottomBar.addChild(retryButton); + if (!Net.isMP || Net.isHost) { + var retryButton = new GuiXboxButton("Retry", 160); + retryButton.position = new Vector(400, 0); + retryButton.vertSizing = Bottom; + retryButton.horizSizing = Right; + retryButton.gamepadAccelerator = ["B"]; + retryButton.accelerators = [hxd.Key.ESCAPE, hxd.Key.BACKSPACE]; + retryButton.pressedAction = (e) -> restartFunc(retryButton); + bottomBar.addChild(retryButton); + } // var lbButton = new GuiXboxButton("Leaderboard", 220); // lbButton.position = new Vector(750, 0); diff --git a/src/gui/MultiplayerLevelSelectGui.hx b/src/gui/MultiplayerLevelSelectGui.hx index e9e52eb3..0bd93187 100644 --- a/src/gui/MultiplayerLevelSelectGui.hx +++ b/src/gui/MultiplayerLevelSelectGui.hx @@ -163,6 +163,9 @@ class MultiplayerLevelSelectGui extends GuiImage { state: Net.clientConnection.lobbyReady, platform: Net.getPlatform() }); + } + + if (Net.clientIdMap != null) { for (c => v in Net.clientIdMap) { playerListArr.push({ name: v.name, @@ -313,7 +316,6 @@ class MultiplayerLevelSelectGui extends GuiImage { } return true; } - setLevelFn = setLevel; levelSelectOpts.position = new Vector(380, 430); levelSelectOpts.extent = new Vector(815, 94); @@ -324,6 +326,12 @@ class MultiplayerLevelSelectGui extends GuiImage { NetCommands.setLobbyLevelIndex(i); return true; }; + + setLevelFn = (idx) -> { + setLevel(idx); + levelSelectOpts.setCurrentOption(idx); + }; + levelSelectOpts.setCurrentOption(currentSelectionStatic); setLevel(currentSelectionStatic); innerCtrl.addChild(levelSelectOpts); @@ -367,12 +375,14 @@ class MultiplayerLevelSelectGui extends GuiImage { platform: Net.getPlatform() }); } - for (c => v in Net.clientIdMap) { - playerListArr.push({ - name: v.name, - state: v.lobbyReady, - platform: v.platform - }); + if (Net.clientIdMap != null) { + for (c => v in Net.clientIdMap) { + playerListArr.push({ + name: v.name, + state: v.lobbyReady, + platform: v.platform + }); + } } playerList.setTexts(playerListArr.map(player -> { @@ -383,4 +393,10 @@ class MultiplayerLevelSelectGui extends GuiImage { public function updatePlayerCount(pub:Int, priv:Int, publicTotal:Int, privateTotal:Int) { updatePlayerCountFn(pub, priv, publicTotal, privateTotal); } + + override function dispose() { + super.dispose(); + playSelectedLevel = null; + setLevelFn = null; + } } diff --git a/src/net/MasterServerClient.hx b/src/net/MasterServerClient.hx index 38d0625d..93047736 100644 --- a/src/net/MasterServerClient.hx +++ b/src/net/MasterServerClient.hx @@ -33,6 +33,12 @@ class MasterServerClient { ws.onmessageString = (m) -> { handleMessage(m); } + ws.onerror = (m) -> { + MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("Failed to connect to master server: " + m)); + open = false; + ws = null; + instance = null; + } } public static function process() { diff --git a/src/net/Net.hx b/src/net/Net.hx index d219cdf5..029db428 100644 --- a/src/net/Net.hx +++ b/src/net/Net.hx @@ -162,14 +162,17 @@ class Net { onPacketReceived(client, clientDatachannel, new InputBitStream(b)); } clientDatachannel.onClosed = () -> { + var weLeftOurselves = !Net.isClient; // If we left ourselves, this would be set to false due to order of ops, disconnect being called first, and then the datachannel closing disconnect(); if (MarbleGame.instance.world != null) { MarbleGame.instance.quitMission(); } - if (!(MarbleGame.canvas.content is MultiplayerLoadingGui)) { - var loadGui = new MultiplayerLoadingGui("Server closed"); - MarbleGame.canvas.setContent(loadGui); - loadGui.setErrorStatus("Server closed"); + if (!weLeftOurselves) { + if (!(MarbleGame.canvas.content is MultiplayerLoadingGui)) { + var loadGui = new MultiplayerLoadingGui("Server closed"); + MarbleGame.canvas.setContent(loadGui); + loadGui.setErrorStatus("Server closed"); + } } } clientDatachannel.onError = (msg) -> { @@ -195,7 +198,8 @@ class Net { Net.isMP = false; Net.isClient = false; Net.isHost = false; - Net.client.close(); + if (Net.client != null) + Net.client.close(); Net.client = null; Net.clientId = 0; Net.clientIdMap.clear(); @@ -361,6 +365,8 @@ class Net { for (cc in clients) { cc.sendBytes(b); } + // Send our current mission to connecting client + NetCommands.setLobbyLevelIndexClient(conn, MultiplayerLevelSelectGui.currentSelectionStatic); } } @@ -456,6 +462,11 @@ class Net { } } + public static function sendPacketToClient(client:GameConnection, packetData:OutputBitStream) { + var bytes = packetData.getBytes(); + client.sendBytes(bytes); + } + public static function addDummyConnection() { if (Net.isHost) { addGhost(Net.clientId++); diff --git a/src/net/NetCommands.hx b/src/net/NetCommands.hx index d17d0a30..afedc87c 100644 --- a/src/net/NetCommands.hx +++ b/src/net/NetCommands.hx @@ -12,7 +12,11 @@ import gui.MultiplayerLoadingGui; @:build(net.RPCMacro.build()) class NetCommands { @:rpc(server) public static function setLobbyLevelIndex(i:Int) { - MultiplayerLevelSelectGui.setLevelFn(i); + if (MultiplayerLevelSelectGui.setLevelFn == null) { + MultiplayerLevelSelectGui.currentSelectionStatic = i; + } else { + MultiplayerLevelSelectGui.setLevelFn(i); + } } @:rpc(server) public static function playLevel(levelIndex:Int) { diff --git a/src/net/RPCMacro.hx b/src/net/RPCMacro.hx index b9643f48..55a57670 100644 --- a/src/net/RPCMacro.hx +++ b/src/net/RPCMacro.hx @@ -15,6 +15,8 @@ class RPCMacro { deserialize:Array }> = new Map(); + var fieldsToAdd = []; + for (field in fields) { if (field.meta.length > 0 && field.meta[0].name == ':rpc') { switch (field.kind) { @@ -74,6 +76,32 @@ class RPCMacro { Net.sendPacketToAll(stream); } }; + var origExpr = f.expr; + var lastExprSingle = macro { + if (Net.isHost) { + var stream = new net.BitStream.OutputBitStream(); + stream.writeByte(NetPacketType.NetCommand); + stream.writeByte($v{rpcFnId}); + $b{serializeFns}; + Net.sendPacketToClient(client, stream); + } + }; + + var singleClientfn:Field = { + name: field.name + "Client", + pos: Context.currentPos(), + access: [APublic, AStatic], + kind: FFun({ + args: [ + { + name: "client", + type: haxe.macro.TypeTools.toComplexType(Context.getType('net.ClientConnection.GameConnection')) + } + ].concat(f.args), + expr: macro $b{[origExpr, lastExprSingle]} + }) + }; + fieldsToAdd.push(singleClientfn); f.expr = macro $b{[f.expr, lastExpr]}; @@ -138,6 +166,9 @@ class RPCMacro { }; fields.push(deserializeField); + for (fn in fieldsToAdd) { + fields.push(fn); + } return fields; }