From c3962091cdb82726d5d9fc46f731b6fc459e6528 Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Sat, 2 Mar 2024 01:33:50 +0530 Subject: [PATCH] server browser and joining logifwfdqd --- server/MasterServer.hx | 194 ++++++++++++++++++++++++++++++++++ server/build_master.hxml | 4 + src/gui/CreateMatchGui.hx | 121 +++++++++++++++++++++ src/gui/MPServerListGui.hx | 141 ++++++++++++++++++++++++ src/gui/MultiplayerGui.hx | 17 +-- src/net/MasterServerClient.hx | 120 +++++++++++++++++++++ src/net/Net.hx | 188 ++++++++++++++++++++++++-------- 7 files changed, 734 insertions(+), 51 deletions(-) create mode 100644 server/MasterServer.hx create mode 100644 server/build_master.hxml create mode 100644 src/gui/CreateMatchGui.hx create mode 100644 src/gui/MPServerListGui.hx create mode 100644 src/net/MasterServerClient.hx diff --git a/server/MasterServer.hx b/server/MasterServer.hx new file mode 100644 index 00000000..bdd6f974 --- /dev/null +++ b/server/MasterServer.hx @@ -0,0 +1,194 @@ +import hx.ws.WebSocket; +import haxe.Json; +import hx.ws.SocketImpl; +import hx.ws.WebSocketHandler; +import hx.ws.WebSocketServer; +import hx.ws.State; +import hx.ws.Log; +import hx.ws.HttpHeader; +import hx.ws.HttpResponse; +import hx.ws.HttpRequest; + +using Lambda; + +@:publicFields +class ServerInfo { + var socket:SignallingHandler; + var name:String; + var players:Int; + var maxPlayers:Int; + var privateSlots:Int; + var privateServer:Bool; + var inviteCode:Int; + var state:String; + var platform:String; + + public function new(socket:SignallingHandler, name:String, players:Int, maxPlayers:Int, privateSlots:Int, privateServer:Bool, inviteCode:Int, + state:String, platform:String) { + this.socket = socket; + this.name = name; + this.players = players; + this.maxPlayers = maxPlayers; + this.privateSlots = privateSlots; + this.privateServer = privateServer; + this.inviteCode = inviteCode; + this.state = state; + this.platform = platform; + } +} + +class SignallingHandler extends WebSocketHandler { + static var clients:Array = []; + + static var servers:Array = []; + + static var joiningClients:Map = []; + + static var clientId = 0; + + public override function handshake(httpRequest:HttpRequest) { + var httpResponse = new HttpResponse(); + + httpResponse.headers.set(HttpHeader.SEC_WEBSOSCKET_VERSION, "13"); + httpResponse.headers.set("Access-Control-Allow-Origin", "*"); // Enable CORS pls, why do i have to override this entire function for a single line + if (httpRequest.method != "GET" || httpRequest.httpVersion != "HTTP/1.1") { + httpResponse.code = 400; + httpResponse.text = "Bad"; + httpResponse.headers.set(HttpHeader.CONNECTION, "close"); + httpResponse.headers.set(HttpHeader.X_WEBSOCKET_REJECT_REASON, 'Bad request'); + } else if (httpRequest.headers.get(HttpHeader.SEC_WEBSOSCKET_VERSION) != "13") { + httpResponse.code = 426; + httpResponse.text = "Upgrade"; + httpResponse.headers.set(HttpHeader.CONNECTION, "close"); + httpResponse.headers.set(HttpHeader.X_WEBSOCKET_REJECT_REASON, + 'Unsupported websocket client version: ${httpRequest.headers.get(HttpHeader.SEC_WEBSOSCKET_VERSION)}, Only version 13 is supported.'); + } else if (httpRequest.headers.get(HttpHeader.UPGRADE) != "websocket") { + httpResponse.code = 426; + httpResponse.text = "Upgrade"; + httpResponse.headers.set(HttpHeader.CONNECTION, "close"); + httpResponse.headers.set(HttpHeader.X_WEBSOCKET_REJECT_REASON, 'Unsupported upgrade header: ${httpRequest.headers.get(HttpHeader.UPGRADE)}.'); + } else if (httpRequest.headers.get(HttpHeader.CONNECTION).indexOf("Upgrade") == -1) { + httpResponse.code = 426; + httpResponse.text = "Upgrade"; + httpResponse.headers.set(HttpHeader.CONNECTION, "close"); + httpResponse.headers.set(HttpHeader.X_WEBSOCKET_REJECT_REASON, 'Unsupported connection header: ${httpRequest.headers.get(HttpHeader.CONNECTION)}.'); + } else { + Log.debug('Handshaking', id); + var key = httpRequest.headers.get(HttpHeader.SEC_WEBSOCKET_KEY); + var result = makeWSKeyResponse(key); + Log.debug('Handshaking key - ${result}', id); + + httpResponse.code = 101; + httpResponse.text = "Switching Protocols"; + httpResponse.headers.set(HttpHeader.UPGRADE, "websocket"); + httpResponse.headers.set(HttpHeader.CONNECTION, "Upgrade"); + httpResponse.headers.set(HttpHeader.SEC_WEBSOSCKET_ACCEPT, result); + } + + sendHttpResponse(httpResponse); + + if (httpResponse.code == 101) { + _onopenCalled = false; + state = State.Head; + Log.debug('Connected', id); + } else { + close(); + } + } + + public function new(s:SocketImpl) { + super(s); + onopen = () -> { + clients.push(this); + } + onclose = () -> { + servers = servers.filter(x -> x.socket != this); // remove server + for (key => val in joiningClients) { + if (val == this) { + joiningClients.remove(key); + break; + } + } + clients.remove(this); + } + onmessage = (m) -> { + switch (m) { + case StrMessage(content): + var conts = Json.parse(content); + trace('Received ${conts.type}'); + if (conts.type == "serverInfo") { + var serverInfo = new ServerInfo(this, conts.name, conts.players, conts.maxPlayers, conts.privateSlots, conts.privateServer, + conts.inviteCode, conts.state, conts.platform); + if (servers.find(x -> x.name == serverInfo.name) == null) { + servers.push(serverInfo); + } else { + servers = servers.filter(x -> x.socket != this); + servers.push(serverInfo); // update server + } + } + if (conts.type == "connect") { + var serverInfo = servers.find(x -> x.name == conts.serverName); + if (serverInfo != null) { + if (serverInfo.players >= serverInfo.maxPlayers) { + this.send(Json.stringify({ + type: "connectFailed", + reason: "The server is full" + })); + } else { + var cid = clientId++; + joiningClients.set(cid, this); + serverInfo.socket.send(Json.stringify({ + type: "connect", + sdp: conts.sdp, + clientId: cid + })); + } + } else { + this.send(Json.stringify({ + type: "connectFailed", + reason: "Server not found" + })); + } + } + if (conts.type == "connectResponse") { + var client = joiningClients.get(conts.clientId); + if (client != null) { + var success = conts.success; + if (!success) { + client.send(Json.stringify({ + type: "connectFailed", + reason: conts.reason + })); + } else { + client.send(Json.stringify({ + type: "connectResponse", + sdp: conts.sdp + })); + } + } + } + if (conts.type == "serverList") { + this.send(Json.stringify({ + type: "serverList", + servers: servers.filter(x -> !x.privateServer && x.state == "Lobby").map(x -> { + return { + name: x.name, + players: x.players, + maxPlayers: x.maxPlayers, + platform: x.platform + } + }) + })); + } + case _: {} + } + } + } +} + +class MasterServer { + static function main() { + var ws = new WebSocketServer("0.0.0.0", 8080, 2); + ws.start(); + } +} diff --git a/server/build_master.hxml b/server/build_master.hxml new file mode 100644 index 00000000..511943f5 --- /dev/null +++ b/server/build_master.hxml @@ -0,0 +1,4 @@ +--library hxWebSockets +--main MasterServer +-cp . +--hl bin/master.hl \ No newline at end of file diff --git a/src/gui/CreateMatchGui.hx b/src/gui/CreateMatchGui.hx new file mode 100644 index 00000000..956676dd --- /dev/null +++ b/src/gui/CreateMatchGui.hx @@ -0,0 +1,121 @@ +package gui; + +import net.Net; +import src.MarbleGame; +import hxd.res.BitmapFont; +import h3d.Vector; +import src.ResourceLoader; +import src.Settings; +import src.Util; + +class CreateMatchGui extends GuiImage { + var innerCtrl:GuiControl; + + public function new() { + var res = ResourceLoader.getImage("data/ui/xbox/BG_fadeOutSoftEdge.png").resource.toTile(); + super(res); + var domcasual32fontdata = ResourceLoader.getFileEntry("data/font/DomCasualD.fnt"); + var domcasual32b = new BitmapFont(domcasual32fontdata.entry); + @:privateAccess domcasual32b.loader = ResourceLoader.loader; + var domcasual32 = domcasual32b.toSdfFont(cast 42 * Settings.uiScale, MultiChannel); + + this.horizSizing = Width; + this.vertSizing = Height; + this.position = new Vector(); + this.extent = new Vector(640, 480); + + #if hl + var scene2d = hxd.Window.getInstance(); + #end + #if js + var scene2d = MarbleGame.instance.scene2d; + #end + + var offsetX = (scene2d.width - 1280) / 2; + var offsetY = (scene2d.height - 720) / 2; + + var subX = 640 - (scene2d.width - offsetX) * 640 / scene2d.width; + var subY = 480 - (scene2d.height - offsetY) * 480 / scene2d.height; + + innerCtrl = new GuiControl(); + innerCtrl.position = new Vector(offsetX, offsetY); + innerCtrl.extent = new Vector(640 - subX, 480 - subY); + innerCtrl.horizSizing = Width; + innerCtrl.vertSizing = Height; + this.addChild(innerCtrl); + + var coliseumfontdata = ResourceLoader.getFileEntry("data/font/ColiseumRR.fnt"); + var coliseumb = new BitmapFont(coliseumfontdata.entry); + @:privateAccess coliseumb.loader = ResourceLoader.loader; + var coliseum = coliseumb.toSdfFont(cast 44 * Settings.uiScale, MultiChannel); + + var rootTitle = new GuiText(coliseum); + rootTitle.position = new Vector(100, 30); + rootTitle.extent = new Vector(1120, 80); + rootTitle.text.textColor = 0xFFFFFF; + rootTitle.text.text = "CREATE MATCH"; + rootTitle.text.alpha = 0.5; + innerCtrl.addChild(rootTitle); + + function numberRange(start:Int, stop:Int, step:Int) { + var range = []; + while (start <= stop) { + range.push('${start}'); + start += step; + } + return range; + } + + var optionCollection = new GuiXboxOptionsListCollection(); + optionCollection.position = new Vector(380, 373); + optionCollection.extent = new Vector(815, 500); + innerCtrl.addChild(optionCollection); + + var maxPlayers = 2; + var privateSlots = 0; + var privateGame = false; + + var playerOpt = optionCollection.addOption(1, "Max Players", ["2", "3", "4", "5", "6", "7", "8"], (idx) -> { + maxPlayers = idx + 2; + return true; + }, 0.5, 118); + + var privateOpt = optionCollection.addOption(1, "Max Players", ["None", "1", "2", "3", "4", "5", "6", "7"], (idx) -> { + privateSlots = idx; + return true; + }, 0.5, 118); + + var privateGameOpt = optionCollection.addOption(1, "Private Game", ["No", "Yes"], (idx) -> { + privateGame = idx == 1; + return true; + }, 0.5, 118); + + var bottomBar = new GuiControl(); + bottomBar.position = new Vector(0, 590); + bottomBar.extent = new Vector(640, 200); + bottomBar.horizSizing = Width; + bottomBar.vertSizing = Bottom; + innerCtrl.addChild(bottomBar); + + var backButton = new GuiXboxButton("Back", 160); + backButton.position = new Vector(400, 0); + backButton.vertSizing = Bottom; + backButton.horizSizing = Right; + backButton.gamepadAccelerator = ["B"]; + backButton.accelerators = [hxd.Key.ESCAPE, hxd.Key.BACKSPACE]; + backButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new MultiplayerGui()); + bottomBar.addChild(backButton); + + var nextButton = new GuiXboxButton("Go", 160); + nextButton.position = new Vector(960, 0); + nextButton.vertSizing = Bottom; + nextButton.horizSizing = Right; + nextButton.gamepadAccelerator = ["A"]; + nextButton.accelerators = [hxd.Key.ENTER]; + nextButton.pressedAction = (e) -> { + MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(true)); + Net.hostServer("My Server", maxPlayers, privateSlots, privateGame); + }; + bottomBar.addChild(nextButton); + } +} diff --git a/src/gui/MPServerListGui.hx b/src/gui/MPServerListGui.hx new file mode 100644 index 00000000..e7dabf54 --- /dev/null +++ b/src/gui/MPServerListGui.hx @@ -0,0 +1,141 @@ +package gui; + +import src.Console; +import net.Net; +import net.Net.ServerInfo; +import net.MasterServerClient; +import hxd.res.BitmapFont; +import h3d.Vector; +import src.ResourceLoader; +import src.MarbleGame; +import src.Settings; +import src.Mission; +import src.MissionList; + +class MPServerListGui extends GuiImage { + var innerCtrl:GuiControl; + var serverWnd:GuiImage; + + public function new() { + var res = ResourceLoader.getImage("data/ui/xbox/BG_fadeOutSoftEdge.png").resource.toTile(); + super(res); + + 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); + + this.horizSizing = Width; + this.vertSizing = Height; + this.position = new Vector(); + this.extent = new Vector(640, 480); + + #if hl + var scene2d = hxd.Window.getInstance(); + #end + #if js + var scene2d = MarbleGame.instance.scene2d; + #end + + var offsetX = (scene2d.width - 1280) / 2; + var offsetY = (scene2d.height - 720) / 2; + + var subX = 640 - (scene2d.width - offsetX) * 640 / scene2d.width; + var subY = 480 - (scene2d.height - offsetY) * 480 / scene2d.height; + + innerCtrl = new GuiControl(); + innerCtrl.position = new Vector(offsetX, offsetY); + innerCtrl.extent = new Vector(640 - subX, 480 - subY); + innerCtrl.horizSizing = Width; + innerCtrl.vertSizing = Height; + this.addChild(innerCtrl); + + serverWnd = new GuiImage(ResourceLoader.getResource("data/ui/xbox/helpWindow.png", ResourceLoader.getImage, this.imageResources).toTile()); + serverWnd.horizSizing = Right; + serverWnd.vertSizing = Bottom; + serverWnd.position = new Vector(260, 107); + serverWnd.extent = new Vector(736, 460); + innerCtrl.addChild(serverWnd); + + function imgLoader(path:String) { + switch (path) { + case "locked": + return ResourceLoader.getResource("data/ui/xbox/DemoOutOfTimeIcon.png", ResourceLoader.getImage, this.imageResources).toTile(); + case "unlocked": + return ResourceLoader.getResource("data/ui/xbox/Ready.png", ResourceLoader.getImage, this.imageResources).toTile(); + } + return null; + } + + var curSelection = -1; + + var serverDisplays = []; + + var serverList = new GuiMLTextListCtrl(arial14, serverDisplays, imgLoader); + + serverList.selectedColor = 0xF29515; + serverList.selectedFillColor = 0xEBEBEB; + serverList.position = new Vector(25, 22); + serverList.extent = new Vector(550, 480); + serverList.scrollable = true; + serverList.onSelectedFunc = (sel) -> { + curSelection = sel; + } + serverWnd.addChild(serverList); + + var ourServerList:Array = []; + + function updateServerListDisplay() { + serverDisplays = ourServerList.map(x -> x.name); + serverList.setTexts(serverDisplays); + } + + MasterServerClient.connectToMasterServer(() -> { + MasterServerClient.instance.getServerList((servers) -> { + ourServerList = servers; + updateServerListDisplay(); + }); + }); + + var bottomBar = new GuiControl(); + bottomBar.position = new Vector(0, 590); + bottomBar.extent = new Vector(640, 200); + bottomBar.horizSizing = Width; + bottomBar.vertSizing = Bottom; + innerCtrl.addChild(bottomBar); + + var backButton = new GuiXboxButton("Back", 160); + backButton.position = new Vector(400, 0); + backButton.vertSizing = Bottom; + backButton.horizSizing = Right; + backButton.gamepadAccelerator = ["B"]; + backButton.accelerators = [hxd.Key.ESCAPE, hxd.Key.BACKSPACE]; + backButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new MainMenuGui()); + bottomBar.addChild(backButton); + + var nextButton = new GuiXboxButton("Join", 160); + nextButton.position = new Vector(960, 0); + nextButton.vertSizing = Bottom; + nextButton.horizSizing = Right; + nextButton.accelerators = [hxd.Key.ENTER]; + nextButton.gamepadAccelerator = ["X"]; + nextButton.pressedAction = (e) -> { + Net.joinServer(ourServerList[curSelection].name, () -> { + MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false)); + }); + }; + bottomBar.addChild(nextButton); + } + + override function onResize(width:Int, height:Int) { + var offsetX = (width - 1280) / 2; + var offsetY = (height - 720) / 2; + + var subX = 640 - (width - offsetX) * 640 / width; + var subY = 480 - (height - offsetY) * 480 / height; + innerCtrl.position = new Vector(offsetX, offsetY); + innerCtrl.extent = new Vector(640 - subX, 480 - subY); + + super.onResize(width, height); + } +} diff --git a/src/gui/MultiplayerGui.hx b/src/gui/MultiplayerGui.hx index 27b5db3a..f63547a4 100644 --- a/src/gui/MultiplayerGui.hx +++ b/src/gui/MultiplayerGui.hx @@ -64,15 +64,18 @@ class MultiplayerGui extends GuiImage { btnList.extent = new Vector(502, 500); innerCtrl.addChild(btnList); - btnList.addButton(3, 'Create Game', (e) -> { - MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(true)); - Net.hostServer(); + btnList.addButton(3, 'Search Matches', (e) -> { + MarbleGame.canvas.setContent(new MPServerListGui()); }); - btnList.addButton(3, 'Join Game', (e) -> { - Net.joinServer(() -> { - MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false)); - }); + btnList.addButton(3, 'Create Match', (e) -> { + MarbleGame.canvas.setContent(new CreateMatchGui()); + }); + + btnList.addButton(3, 'Join Match', (e) -> { + // Net.joinServer(() -> { + // MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false)); + // }); }); var bottomBar = new GuiControl(); diff --git a/src/net/MasterServerClient.hx b/src/net/MasterServerClient.hx new file mode 100644 index 00000000..061440da --- /dev/null +++ b/src/net/MasterServerClient.hx @@ -0,0 +1,120 @@ +package net; + +import gui.MessageBoxOkDlg; +import src.MarbleGame; +import haxe.Json; +import net.Net.ServerInfo; +import hx.ws.WebSocket; +import src.Console; + +typedef RemoteServerInfo = { + name:String, + players:Int, + maxPlayers:Int, + platform:String, +} + +class MasterServerClient { + static var serverIp = "ws://localhost:8080"; + public static var instance:MasterServerClient; + + var ws:WebSocket; + var serverListCb:Array->Void; + + var open = false; + + public function new(onOpenFunc:() -> Void) { + ws = new WebSocket(serverIp); + ws.onopen = () -> { + open = true; + onOpenFunc(); + } + ws.onmessage = (m) -> { + switch (m) { + case StrMessage(content): + handleMessage(content); + case _: + return; + } + } + } + + public static function connectToMasterServer(onConnect:() -> Void) { + if (instance == null) + instance = new MasterServerClient(onConnect); + else + onConnect(); + } + + public function sendServerInfo(serverInfo:ServerInfo) { + ws.send(Json.stringify({ + type: "serverInfo", + name: serverInfo.name, + players: serverInfo.players, + maxPlayers: serverInfo.maxPlayers, + privateSlots: serverInfo.privateSlots, + privateServer: serverInfo.privateServer, + inviteCode: serverInfo.inviteCode, + state: serverInfo.state + })); + } + + public function sendConnectToServer(serverName:String, sdp:String) { + ws.send(Json.stringify({ + type: "connect", + serverName: serverName, + sdp: sdp + })); + } + + public function getServerList(serverListCb:Array->Void) { + this.serverListCb = serverListCb; + ws.send(Json.stringify({ + type: "serverList" + })); + } + + function handleMessage(message:String) { + var conts = Json.parse(message); + Console.log('Received ${conts.type}'); + if (conts.type == "serverList") { + if (serverListCb != null) { + serverListCb(conts.servers); + } + } + if (conts.type == "connect") { + if (!Net.isHost) { + ws.send(Json.stringify({ + type: "connectResponse", + success: false, + reason: "The server has shut down" + })); + return; + } + if (Net.serverInfo.players >= Net.serverInfo.maxPlayers) { + ws.send(Json.stringify({ + type: "connectResponse", + success: false, + reason: "The server is full" + })); + return; + } + Net.addClientFromSdp(conts.sdp, (sdpReply) -> { + ws.send(Json.stringify({ + success: true, + type: "connectResponse", + sdp: sdpReply, + clientId: conts.clientId + })); + }); + } + if (conts.type == "connectResponse") { + Console.log("Remote Description Received!"); + var sdpObj = Json.parse(conts.sdp); + @:privateAccess Net.client.setRemoteDescription(sdpObj.sdp, sdpObj.type); + } + if (conts.type == "connectFailed") { + MarbleGame.canvas.pushDialog(new MessageBoxOkDlg(conts.reason)); + } + } +} diff --git a/src/net/Net.hx b/src/net/Net.hx index b9803d60..bdf32112 100644 --- a/src/net/Net.hx +++ b/src/net/Net.hx @@ -31,6 +31,29 @@ enum abstract NetPacketType(Int) from Int to Int { var PlayerInfo; } +@:publicFields +class ServerInfo { + var name:String; + var players:Int; + var maxPlayers:Int; + var privateSlots:Int; + var privateServer:Bool; + var inviteCode:Int; + var state:String; + var platform:String; + + public function new(name:String, players:Int, maxPlayers:Int, privateSlots:Int, privateServer:Bool, inviteCode:Int, state:String, platform:String) { + this.name = name; + this.players = players; + this.maxPlayers = maxPlayers; + this.privateSlots = privateSlots; + this.privateServer = privateServer; + this.inviteCode = inviteCode; + this.state = state; + this.platform = platform; + } +} + class Net { static var client:RTCPeerConnection; static var clientDatachannel:RTCDataChannel; @@ -48,34 +71,51 @@ class Net { public static var clients:Map = []; public static var clientIdMap:Map = []; public static var clientConnection:ClientConnection; + public static var serverInfo:ServerInfo; + + public static function hostServer(name:String, maxPlayers:Int, privateSlots:Int, privateServer:Bool) { + serverInfo = new ServerInfo(name, 1, maxPlayers, privateSlots, privateServer, Std.int(999999 * Math.random()), "Lobby", getPlatform()); + MasterServerClient.connectToMasterServer(() -> { + isHost = true; + isClient = false; + clientId = 0; + isMP = true; + MasterServerClient.instance.sendServerInfo(serverInfo); + }); - public static function hostServer() { // host = new RTCPeerConnection(["stun.l.google.com:19302"], "0.0.0.0"); // host.bind("127.0.0.1", 28000, (c) -> { // onClientConnect(c); // isMP = true; // }); - isHost = true; - isClient = false; - clientId = 0; - masterWs = new WebSocket("ws://localhost:8080"); + // isHost = true; + // isClient = false; + // clientId = 0; + // masterWs = new WebSocket("ws://localhost:8080"); - masterWs.onmessage = (m) -> { - switch (m) { - case StrMessage(content): - var conts = Json.parse(content); - var peer = new RTCPeerConnection(["stun:stun.l.google.com:19302"], "0.0.0.0"); - peer.setRemoteDescription(conts.sdp, conts.type); - addClient(peer); + // masterWs.onmessage = (m) -> { + // switch (m) { + // case StrMessage(content): + // var conts = Json.parse(content); + // var peer = new RTCPeerConnection(["stun:stun.l.google.com:19302"], "0.0.0.0"); + // peer.setRemoteDescription(conts.sdp, conts.type); + // addClient(peer); - case BytesMessage(content): {} - } - } + // case BytesMessage(content): {} + // } + // } - isMP = true; + // isMP = true; } - static function addClient(peer:RTCPeerConnection) { + public static function addClientFromSdp(sdpString:String, onFinishSdp:String->Void) { + var peer = new RTCPeerConnection(["stun:stun.l.google.com:19302"], "0.0.0.0"); + var sdpObj = Json.parse(sdpString); + peer.setRemoteDescription(sdpObj.sdp, sdpObj.type); + addClient(peer, onFinishSdp); + } + + static function addClient(peer:RTCPeerConnection, onFinishSdp:String->Void) { var candidates = []; peer.onLocalCandidate = (c) -> { if (c != "") @@ -85,12 +125,9 @@ class Net { if (s == RTC_GATHERING_COMPLETE) { var sdpObj = StringTools.trim(peer.localDescription); sdpObj = sdpObj + '\r\n' + candidates.join('\r\n') + '\r\n'; - masterWs.send(Json.stringify({ - type: "connect", - sdpObj: { - sdp: sdpObj, - type: "answer" - } + onFinishSdp(Json.stringify({ + sdp: sdpObj, + type: "answer" })); } } @@ -104,9 +141,8 @@ class Net { clientIdMap[id] = ghost; } - public static function joinServer(connectedCb:() -> Void) { - masterWs = new WebSocket("ws://localhost:8080"); - masterWs.onopen = () -> { + public static function joinServer(serverName:String, connectedCb:() -> Void) { + MasterServerClient.connectToMasterServer(() -> { client = new RTCPeerConnection(["stun:stun.l.google.com:19302"], "0.0.0.0"); var candidates = []; @@ -114,31 +150,19 @@ class Net { if (c != "") candidates.push('a=${c}'); } + client.onGatheringStateChange = (s) -> { if (s == RTC_GATHERING_COMPLETE) { Console.log("Local Description Set!"); var sdpObj = StringTools.trim(client.localDescription); sdpObj = sdpObj + '\r\n' + candidates.join('\r\n') + '\r\n'; - masterWs.send(Json.stringify({ - type: "connect", - sdpObj: { - sdp: sdpObj, - type: "offer" - } + MasterServerClient.instance.sendConnectToServer(serverName, Json.stringify({ + sdp: sdpObj, + type: "offer" })); } } - masterWs.onmessage = (m) -> { - switch (m) { - case StrMessage(content): - Console.log("Remote Description Received!"); - var conts = Json.parse(content); - client.setRemoteDescription(conts.sdp, conts.type); - case _: {} - } - } - clientDatachannel = client.createDatachannel("mp"); clientDatachannel.onOpen = (n) -> { Console.log("Successfully connected!"); @@ -155,16 +179,71 @@ class Net { isMP = true; isHost = false; isClient = true; - } + }); + // masterWs = new WebSocket("ws://localhost:8080"); + // masterWs.onopen = () -> { + // client = new RTCPeerConnection(["stun:stun.l.google.com:19302"], "0.0.0.0"); + // var candidates = []; + + // client.onLocalCandidate = (c) -> { + // if (c != "") + // candidates.push('a=${c}'); + // } + // client.onGatheringStateChange = (s) -> { + // if (s == RTC_GATHERING_COMPLETE) { + // Console.log("Local Description Set!"); + // var sdpObj = StringTools.trim(client.localDescription); + // sdpObj = sdpObj + '\r\n' + candidates.join('\r\n') + '\r\n'; + // masterWs.send(Json.stringify({ + // type: "connect", + // sdpObj: { + // sdp: sdpObj, + // type: "offer" + // } + // })); + // } + // } + + // masterWs.onmessage = (m) -> { + // switch (m) { + // case StrMessage(content): + // Console.log("Remote Description Received!"); + // var conts = Json.parse(content); + // client.setRemoteDescription(conts.sdp, conts.type); + // case _: {} + // } + // } + + // clientDatachannel = client.createDatachannel("mp"); + // clientDatachannel.onOpen = (n) -> { + // Console.log("Successfully connected!"); + // clients.set(client, new ClientConnection(0, client, clientDatachannel)); // host is always 0 + // clientIdMap[0] = clients[client]; + // clientConnection = cast clients[client]; + // onConnectedToServer(); + // haxe.Timer.delay(() -> connectedCb(), 1500); // 1.5 second delay to do the RTT calculation + // } + // clientDatachannel.onMessage = (b) -> { + // onPacketReceived(client, clientDatachannel, new InputBitStream(b)); + // } + + // isMP = true; + // isHost = false; + // isClient = true; + // } } static function onClientConnect(c:RTCPeerConnection, dc:RTCDataChannel) { clientId += 1; - clients.set(c, new ClientConnection(clientId, c, dc)); + var cc = new ClientConnection(clientId, c, dc); + clients.set(c, cc); clientIdMap[clientId] = clients[c]; dc.onMessage = (msgBytes) -> { onPacketReceived(c, dc, new InputBitStream(msgBytes)); } + dc.onClosed = () -> { + onClientLeave(cc); + } var b = haxe.io.Bytes.alloc(2); b.set(0, ClientIdAssign); b.set(1, clientId); @@ -177,6 +256,9 @@ class Net { cast(clients[c], ClientConnection).pingSendTime = Console.time(); dc.sendBytes(b); Console.log("Sending ping packet!"); + + serverInfo.players++; + MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the new player } static function onConnectedToServer() { @@ -190,6 +272,11 @@ class Net { Console.log("Sending ping packet!"); } + static function onClientLeave(cc:ClientConnection) { + serverInfo.players--; + MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the player leave + } + static function sendPlayerInfosBytes() { var b = new haxe.io.BytesOutput(); b.writeByte(PlayerInfo); @@ -314,4 +401,17 @@ class Net { addGhost(Net.clientId++); } } + + public static function getPlatform() { + #if js + return "Browser"; + #end + #if hl + #if MACOS_BUNDLE + return "MacOS"; + #else + return "Windows"; + #end + #end + } }