mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
server browser and joining logifwfdqd
This commit is contained in:
parent
251f4a6577
commit
c3962091cd
7 changed files with 734 additions and 51 deletions
194
server/MasterServer.hx
Normal file
194
server/MasterServer.hx
Normal file
|
|
@ -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<SignallingHandler> = [];
|
||||
|
||||
static var servers:Array<ServerInfo> = [];
|
||||
|
||||
static var joiningClients:Map<Int, SignallingHandler> = [];
|
||||
|
||||
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<SignallingHandler>("0.0.0.0", 8080, 2);
|
||||
ws.start();
|
||||
}
|
||||
}
|
||||
4
server/build_master.hxml
Normal file
4
server/build_master.hxml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
--library hxWebSockets
|
||||
--main MasterServer
|
||||
-cp .
|
||||
--hl bin/master.hl
|
||||
121
src/gui/CreateMatchGui.hx
Normal file
121
src/gui/CreateMatchGui.hx
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
141
src/gui/MPServerListGui.hx
Normal file
141
src/gui/MPServerListGui.hx
Normal file
|
|
@ -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<RemoteServerInfo> = [];
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
120
src/net/MasterServerClient.hx
Normal file
120
src/net/MasterServerClient.hx
Normal file
|
|
@ -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<RemoteServerInfo>->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<RemoteServerInfo>->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));
|
||||
}
|
||||
}
|
||||
}
|
||||
188
src/net/Net.hx
188
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<RTCPeerConnection, GameConnection> = [];
|
||||
public static var clientIdMap:Map<Int, GameConnection> = [];
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue