From d0f69e88d3e8fb516c6dcf3246b11984722c8494 Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Fri, 5 Apr 2024 00:26:39 +0530 Subject: [PATCH] some work on join-leaves --- src/MarbleGame.hx | 3 ++ src/MarbleWorld.hx | 14 ++++--- src/PreviewWorld.hx | 7 ++++ src/gui/CreateMatchGui.hx | 3 +- src/gui/MultiplayerLoadingGui.hx | 65 ++++++++++++++++++++++++++++++ src/modes/HuntMode.hx | 64 ++++++++++++++++++++--------- src/net/MasterServerClient.hx | 7 ++++ src/net/Net.hx | 69 ++++++++++++++++++++++++++++++-- src/net/NetCommands.hx | 24 +++++++++++ 9 files changed, 227 insertions(+), 29 deletions(-) create mode 100644 src/gui/MultiplayerLoadingGui.hx diff --git a/src/MarbleGame.hx b/src/MarbleGame.hx index f1d2821b..a1210eae 100644 --- a/src/MarbleGame.hx +++ b/src/MarbleGame.hx @@ -283,6 +283,9 @@ class MarbleGame { public function quitMission() { Console.log("Quitting mission"); + if (Net.isMP) { + Net.disconnect(); + } var watching = world.isWatching; var missionType = world.mission.type; var isNotCustom = !world.mission.isClaMission && !world.mission.isCustom; diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index e7373bf5..57a94cb9 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -1264,12 +1264,16 @@ class MarbleWorld extends Scheduler { } } - public function removePlayer(cc:ClientConnection) { + public function removePlayer(cc:GameConnection) { var otherMarble = this.clientMarbles[cc]; - this.predictions.removeMarbleFromPrediction(otherMarble); - this.scene.removeChild(otherMarble); - this.collisionWorld.removeMarbleEntity(otherMarble.collider); - otherMarble.dispose(); + if (otherMarble != null) { + this.predictions.removeMarbleFromPrediction(otherMarble); + this.scene.removeChild(otherMarble); + this.collisionWorld.removeMarbleEntity(otherMarble.collider); + this.playGui.removePlayer(cc.id); + this.clientMarbles.remove(cc); + otherMarble.dispose(); + } } public function rollback(t:Float) { diff --git a/src/PreviewWorld.hx b/src/PreviewWorld.hx index 9adbbeae..0d1ae273 100644 --- a/src/PreviewWorld.hx +++ b/src/PreviewWorld.hx @@ -1,5 +1,6 @@ package src; +import net.Net; import collision.CollisionWorld; import mis.MissionElement.MissionElementSky; import shapes.Astrolabe; @@ -286,6 +287,12 @@ class PreviewWorld extends Scheduler { }, skyElem); }); + if (Net.isMP) { + // Load the MP sounds + worker.loadFile("sound/spawn_alternate.wav"); + worker.loadFile("sound/infotutorial.wav"); + } + worker.run(); } diff --git a/src/gui/CreateMatchGui.hx b/src/gui/CreateMatchGui.hx index 956676dd..5e445b7d 100644 --- a/src/gui/CreateMatchGui.hx +++ b/src/gui/CreateMatchGui.hx @@ -79,8 +79,9 @@ class CreateMatchGui extends GuiImage { maxPlayers = idx + 2; return true; }, 0.5, 118); + playerOpt.setCurrentOption(6); - var privateOpt = optionCollection.addOption(1, "Max Players", ["None", "1", "2", "3", "4", "5", "6", "7"], (idx) -> { + var privateOpt = optionCollection.addOption(1, "Private Slots", ["None", "1", "2", "3", "4", "5", "6", "7"], (idx) -> { privateSlots = idx; return true; }, 0.5, 118); diff --git a/src/gui/MultiplayerLoadingGui.hx b/src/gui/MultiplayerLoadingGui.hx new file mode 100644 index 00000000..9f06fd66 --- /dev/null +++ b/src/gui/MultiplayerLoadingGui.hx @@ -0,0 +1,65 @@ +package gui; + +import hxd.res.BitmapFont; +import h3d.Vector; +import src.ResourceLoader; +import src.MarbleGame; +import src.Settings; +import src.Util; + +class MultiplayerLoadingGui extends GuiImage { + var loadText:GuiText; + var loadTextBg:GuiText; + + public function new(missionName:String) { + var res = ResourceLoader.getImage("data/ui/game/CloudBG.jpg").resource.toTile(); + super(res); + this.position = new Vector(); + this.extent = new Vector(640, 480); + this.horizSizing = Width; + this.vertSizing = Height; + + var fadeEdge = new GuiImage(ResourceLoader.getResource("data/ui/xbox/BG_fadeOutSoftEdge.png", ResourceLoader.getImage, this.imageResources).toTile()); + fadeEdge.position = new Vector(0, 0); + fadeEdge.extent = new Vector(640, 480); + fadeEdge.vertSizing = Height; + fadeEdge.horizSizing = Width; + this.addChild(fadeEdge); + + var arial14fontdata = ResourceLoader.getFileEntry("data/font/Arial Bold.fnt"); + var arial14b = new BitmapFont(arial14fontdata.entry); + @:privateAccess arial14b.loader = ResourceLoader.loader; + var arial14 = arial14b.toSdfFont(cast 21 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel); + + var loadAnim = new GuiLoadAnim(); + loadAnim.position = new Vector(610, 253); + loadAnim.extent = new Vector(63, 63); + loadAnim.horizSizing = Center; + loadAnim.vertSizing = Bottom; + this.addChild(loadAnim); + + loadTextBg = new GuiText(arial14); + loadTextBg.position = new Vector(608, 335); + loadTextBg.extent = new Vector(63, 40); + loadTextBg.horizSizing = Center; + loadTextBg.vertSizing = Bottom; + loadTextBg.justify = Center; + loadTextBg.text.text = "Loading"; + loadTextBg.text.textColor = 0; + this.addChild(loadTextBg); + + loadText = new GuiText(arial14); + loadText.position = new Vector(610, 334); + loadText.extent = new Vector(63, 40); + loadText.horizSizing = Center; + loadText.vertSizing = Bottom; + loadText.justify = Center; + loadText.text.text = "Loading"; + this.addChild(loadText); + } + + public function setLoadingStatus(str:String) { + loadText.text.text = str; + loadTextBg.text.text = str; + } +} diff --git a/src/modes/HuntMode.hx b/src/modes/HuntMode.hx index b7263a61..6a501a7e 100644 --- a/src/modes/HuntMode.hx +++ b/src/modes/HuntMode.hx @@ -585,28 +585,52 @@ class HuntMode extends NullMode { override function onTimeExpire() { if (level.finishTime != null) return; - if (this.level.isMultiplayer) { - AudioManager.playSound(ResourceLoader.getResource('data/sound/finish.wav', ResourceLoader.getAudio, @:privateAccess level.soundResources)); - level.finishTime = level.timeState.clone(); - level.marble.setMode(Start); - level.marble.camera.finish = true; - level.finishYaw = level.marble.camera.CameraYaw; - level.finishPitch = level.marble.camera.CameraPitch; - level.displayAlert("Congratulations! You've finished!"); - level.cancel(@:privateAccess level.oobSchedule); - level.cancel(@:privateAccess level.marble.oobSchedule); - if (Net.isHost) { - NetCommands.timerRanOut(); - } - if (!level.isWatching) { - @:privateAccess level.schedule(level.timeState.currentAttemptTime, () -> cast level.showFinishScreen()); - } - // Stop the ongoing sounds - if (@:privateAccess level.timeTravelSound != null) { - @:privateAccess level.timeTravelSound.stop(); - @:privateAccess level.timeTravelSound = null; + + AudioManager.playSound(ResourceLoader.getResource('data/sound/finish.wav', ResourceLoader.getAudio, @:privateAccess level.soundResources)); + level.finishTime = level.timeState.clone(); + level.marble.setMode(Start); + level.marble.camera.finish = true; + level.finishYaw = level.marble.camera.CameraYaw; + level.finishPitch = level.marble.camera.CameraPitch; + level.displayAlert("Congratulations! You've finished!"); + level.cancel(@:privateAccess level.oobSchedule); + level.cancel(@:privateAccess level.marble.oobSchedule); + if (!level.isWatching) { + if (level.isMultiplayer) { + if (Net.isHost) { + for (marble in level.marbles) { + marble.setMode(Start); + level.cancel(@:privateAccess marble.oobSchedule); + } + + NetCommands.timerRanOut(); + } + if (!level.isWatching) { + @:privateAccess level.schedule(level.timeState.currentAttemptTime, () -> cast level.showFinishScreen()); + } + } else { + var myScore = { + name: "Player", + time: getFinishScore() + }; + Settings.saveScore(level.mission.path, myScore, getScoreType()); + var notifies = AchievementsGui.check(); + var delay = 5.0; + var achDelay = 0.0; + for (i in 0...9) { + if (notifies & (1 << i) > 0) + achDelay += 3; + } + if (notifies > 0) + achDelay += 0.5; + @:privateAccess level.schedule(level.timeState.currentAttemptTime + Math.max(delay, achDelay), () -> cast level.showFinishScreen()); } } + // Stop the ongoing sounds + if (@:privateAccess level.timeTravelSound != null) { + @:privateAccess level.timeTravelSound.stop(); + @:privateAccess level.timeTravelSound = null; + } } public function doTimerRunOut() { diff --git a/src/net/MasterServerClient.hx b/src/net/MasterServerClient.hx index f7d8c89c..29e6b370 100644 --- a/src/net/MasterServerClient.hx +++ b/src/net/MasterServerClient.hx @@ -47,6 +47,13 @@ class MasterServerClient { onConnect(); } + public static function disconnectFromMasterServer() { + if (instance != null) { + instance.ws.close(); + instance = null; + } + } + public function sendServerInfo(serverInfo:ServerInfo) { ws.send(Json.stringify({ type: "serverInfo", diff --git a/src/net/Net.hx b/src/net/Net.hx index 58ffd33e..7bbbc22d 100644 --- a/src/net/Net.hx +++ b/src/net/Net.hx @@ -1,5 +1,7 @@ package net; +import src.ResourceLoader; +import src.AudioManager; import net.NetPacket.GemPickupPacket; import net.NetPacket.GemSpawnPacket; import net.BitStream.InputBitStream; @@ -175,6 +177,19 @@ class Net { clientDatachannel.onMessage = (b) -> { onPacketReceived(client, clientDatachannel, new InputBitStream(b)); } + clientDatachannel.onClosed = () -> { + disconnect(); + if (MarbleGame.instance.world != null) { + MarbleGame.instance.quitMission(); + } + } + clientDatachannel.onError = (msg) -> { + Console.log('Errored out due to ${msg}'); + disconnect(); + if (MarbleGame.instance.world != null) { + MarbleGame.instance.quitMission(); + } + } isMP = true; isHost = false; @@ -233,6 +248,32 @@ class Net { // } } + public static function disconnect() { + if (Net.isClient) { + NetCommands.clientLeave(Net.clientId); + Net.isMP = false; + Net.isClient = false; + Net.isHost = false; + Net.client.close(); + Net.client = null; + Net.clientId = 0; + Net.clientIdMap.clear(); + Net.clientConnection = null; + } + if (Net.isHost) { + NetCommands.serverClosed(); + for (client => gc in clients) { + client.close(); + } + Net.isMP = false; + Net.isClient = false; + Net.isHost = false; + Net.clients.clear(); + Net.clientIdMap.clear(); + MasterServerClient.disconnectFromMasterServer(); + } + } + static function onClientConnect(c:RTCPeerConnection, dc:RTCDataChannel) { clientId += 1; var cc = new ClientConnection(clientId, c, dc); @@ -242,6 +283,12 @@ class Net { onPacketReceived(c, dc, new InputBitStream(msgBytes)); } dc.onClosed = () -> { + clients.remove(c); + onClientLeave(cc); + } + dc.onError = (msg) -> { + clients.remove(c); + Console.log('Client ${cc.id} errored out due to: ${msg}'); onClientLeave(cc); } var b = haxe.io.Bytes.alloc(2); @@ -257,6 +304,8 @@ class Net { dc.sendBytes(b); Console.log("Sending ping packet!"); + AudioManager.playSound(ResourceLoader.getAudio("data/sound/spawn_alternate.wav").resource); + serverInfo.players++; MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the new player } @@ -273,8 +322,11 @@ class Net { } static function onClientLeave(cc:ClientConnection) { + if (!Net.isMP) + return; serverInfo.players--; MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the player leave + NetCommands.clientDisconnected(cc.id); } static function sendPlayerInfosBytes() { @@ -290,6 +342,8 @@ class Net { } static function onPacketReceived(c:RTCPeerConnection, dc:RTCDataChannel, input:InputBitStream) { + if (!Net.isMP) + return; // only for MP var packetType = input.readByte(); switch (packetType) { case NetCommand: @@ -326,7 +380,9 @@ class Net { Console.log('Got RTT ${conn.rtt} for client ${conn.id}'); if (Net.isHost) { var b = sendPlayerInfosBytes(); - conn.sendBytes(b); + for (cc in clients) { + cc.sendBytes(b); + } } } @@ -371,13 +427,18 @@ class Net { case PlayerInfo: var count = input.readByte(); + var newP = false; for (i in 0...count) { var id = input.readByte(); if (id != 0 && id != Net.clientId && !clientIdMap.exists(id)) { Console.log('Adding ghost connection ${id}'); addGhost(id); + newP = true; } } + if (newP) { + AudioManager.playSound(ResourceLoader.getAudio("sounds/spawn_alternate.wav").resource); + } case _: Console.log("unknown command: " + packetType); @@ -392,8 +453,10 @@ class Net { } public static function sendPacketToHost(packetData:OutputBitStream) { - var bytes = packetData.getBytes(); - clientDatachannel.sendBytes(bytes); + if (clientDatachannel.state == Open) { + var bytes = packetData.getBytes(); + clientDatachannel.sendBytes(bytes); + } } public static function addDummyConnection() { diff --git a/src/net/NetCommands.hx b/src/net/NetCommands.hx index f2380f44..3ecd71aa 100644 --- a/src/net/NetCommands.hx +++ b/src/net/NetCommands.hx @@ -59,4 +59,28 @@ class NetCommands { huntMode.onTimeExpire(); } } + + @:rpc(server) public static function clientDisconnected(clientId:Int) { + var conn = Net.clientIdMap.get(clientId); + if (MarbleGame.instance.world != null) { + MarbleGame.instance.world.removePlayer(conn); + } + Net.clientIdMap.remove(clientId); + } + + @:rpc(server) public static function clientJoin(clientId:Int) {} + + @:rpc(client) public static function clientLeave(clientId:Int) { + if (Net.isHost) { + @:privateAccess Net.onClientLeave(cast Net.clientIdMap[clientId]); + } + } + + @:rpc(server) public static function serverClosed() { + if (Net.isClient) { + if (MarbleGame.instance.world != null) { + MarbleGame.instance.quitMission(); + } + } + } }