work on better lobby and gravity impl start

This commit is contained in:
RandomityGuy 2024-04-06 18:31:33 +05:30
parent d0f69e88d3
commit ecaa938f95
18 changed files with 407 additions and 241 deletions

View file

@ -1732,6 +1732,7 @@ class Marble extends GameObject {
marbleUpdate.oob = this.outOfBounds; marbleUpdate.oob = this.outOfBounds;
marbleUpdate.powerUpId = this.heldPowerup != null ? this.heldPowerup.netIndex : 0x1FF; marbleUpdate.powerUpId = this.heldPowerup != null ? this.heldPowerup.netIndex : 0x1FF;
marbleUpdate.netFlags = this.netFlags; marbleUpdate.netFlags = this.netFlags;
marbleUpdate.gravityDirection = this.currentUp;
this.netFlags = 0; this.netFlags = 0;
marbleUpdate.serialize(b); marbleUpdate.serialize(b);
return b.getBytes(); return b.getBytes();
@ -1756,6 +1757,8 @@ class Marble extends GameObject {
this.blastUseTick = p.blastTick; this.blastUseTick = p.blastTick;
this.helicopterUseTick = p.heliTick; this.helicopterUseTick = p.heliTick;
this.megaMarbleUseTick = p.megaTick; this.megaMarbleUseTick = p.megaTick;
this.currentUp = p.gravityDirection;
this.level.setUp(cast this, this.currentUp, this.level.timeState, true);
this.outOfBounds = p.oob; this.outOfBounds = p.oob;
this.camera.oob = p.oob; this.camera.oob = p.oob;
if (p.powerUpId == 0x1FF) { if (p.powerUpId == 0x1FF) {
@ -2306,7 +2309,7 @@ class Marble extends GameObject {
this.blastTicks = 0; this.blastTicks = 0;
this.helicopterUseTick = 0; this.helicopterUseTick = 0;
this.megaMarbleUseTick = 0; this.megaMarbleUseTick = 0;
this.netFlags = MarbleNetFlags.DoBlast | MarbleNetFlags.DoMega | MarbleNetFlags.DoHelicopter | MarbleNetFlags.PickupPowerup; this.netFlags = MarbleNetFlags.DoBlast | MarbleNetFlags.DoMega | MarbleNetFlags.DoHelicopter | MarbleNetFlags.PickupPowerup | MarbleNetFlags.GravityChange;
this.lastContactNormal = new Vector(0, 0, 1); this.lastContactNormal = new Vector(0, 0, 1);
this.contactEntities = []; this.contactEntities = [];
this._firstTick = true; this._firstTick = true;

View file

@ -342,12 +342,12 @@ class MarbleWorld extends Scheduler {
if (this.isMultiplayer) { if (this.isMultiplayer) {
// Add us // Add us
if (Net.isHost) { if (Net.isHost) {
this.playGui.addPlayer(0, 'Player 0', true); this.playGui.addPlayer(0, Settings.highscoreName.substr(0, 15), true);
} else { } else {
this.playGui.addPlayer(Net.clientId, 'Player ${Net.clientId}', true); this.playGui.addPlayer(Net.clientId, Settings.highscoreName.substr(0, 15), true);
} }
for (client in Net.clientIdMap) { for (client in Net.clientIdMap) {
this.playGui.addPlayer(client.id, 'Player ${client.id}', false); this.playGui.addPlayer(client.id, client.name.substr(0, 15), false);
} }
} }
@ -651,6 +651,7 @@ class MarbleWorld extends Scheduler {
if (isMultiplayer) { if (isMultiplayer) {
marble.megaMarbleUseTick = 0; marble.megaMarbleUseTick = 0;
marble.helicopterUseTick = 0; marble.helicopterUseTick = 0;
marble.collider.radius = marble._radius = 0.3;
} else { } else {
@:privateAccess marble.helicopterEnableTime = -1e8; @:privateAccess marble.helicopterEnableTime = -1e8;
@:privateAccess marble.megaMarbleEnableTime = -1e8; @:privateAccess marble.megaMarbleEnableTime = -1e8;
@ -1204,8 +1205,8 @@ class MarbleWorld extends Scheduler {
// Debug.drawSphere(@:privateAccess marbleToUpdate.newPos, marbleToUpdate._radius); // Debug.drawSphere(@:privateAccess marbleToUpdate.newPos, marbleToUpdate._radius);
var distFromUs = @:privateAccess marbleToUpdate.newPos.distance(this.marble.newPos); var distFromUs = @:privateAccess marbleToUpdate.newPos.distance(this.marble.newPos);
if (distFromUs < 5) // { // if (distFromUs < 5) // {
m.calculationTicks = ourQueuedMoves.length; m.calculationTicks = ourQueuedMoves.length;
// } else { // } else {
// m.calculationTicks = Std.int(Math.max(1, ourQueuedMoves.length - (distFromUs - 5) / 3)); // m.calculationTicks = Std.int(Math.max(1, ourQueuedMoves.length - (distFromUs - 5) / 3));
// } // }
@ -2058,7 +2059,12 @@ class MarbleWorld extends Scheduler {
} }
public function setUp(marble:Marble, vec:Vector, timeState:TimeState, instant:Bool = false) { public function setUp(marble:Marble, vec:Vector, timeState:TimeState, instant:Bool = false) {
if (vec == marble.currentUp)
return;
marble.currentUp = vec; marble.currentUp = vec;
if (isMultiplayer && Net.isHost) {
@:privateAccess marble.netFlags |= MarbleNetFlags.GravityChange;
}
if (marble == this.marble) { if (marble == this.marble) {
var currentQuat = this.getOrientationQuat(timeState.currentAttemptTime); var currentQuat = this.getOrientationQuat(timeState.currentAttemptTime);
var oldUp = new Vector(0, 0, 1); var oldUp = new Vector(0, 0, 1);

View file

@ -200,7 +200,7 @@ class Settings {
public static var achievementProgression:Int; public static var achievementProgression:Int;
public static var highscoreName = ""; public static var highscoreName = "Player";
public static var uiScale = 1.0; public static var uiScale = 1.0;

View file

@ -11,6 +11,7 @@ import gui.GuiControl.MouseState;
class Canvas extends GuiControl { class Canvas extends GuiControl {
var scene2d:Scene; var scene2d:Scene;
var marbleGame:MarbleGame; var marbleGame:MarbleGame;
var content:GuiControl;
public function new(scene, marbleGame:MarbleGame) { public function new(scene, marbleGame:MarbleGame) {
super(); super();
@ -30,6 +31,7 @@ class Canvas extends GuiControl {
public function setContent(content:GuiControl) { public function setContent(content:GuiControl) {
this.dispose(); this.dispose();
this.content = content;
this.addChild(content); this.addChild(content);
this.render(scene2d); this.render(scene2d);
} }

View file

@ -114,8 +114,8 @@ class CreateMatchGui extends GuiImage {
nextButton.gamepadAccelerator = ["A"]; nextButton.gamepadAccelerator = ["A"];
nextButton.accelerators = [hxd.Key.ENTER]; nextButton.accelerators = [hxd.Key.ENTER];
nextButton.pressedAction = (e) -> { nextButton.pressedAction = (e) -> {
Net.hostServer('${Settings.highscoreName}\'s Server', maxPlayers, privateSlots, privateGame);
MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(true)); MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(true));
Net.hostServer("My Server", maxPlayers, privateSlots, privateGame);
}; };
bottomBar.addChild(nextButton); bottomBar.addChild(nextButton);
} }

View file

@ -9,62 +9,53 @@ import h3d.Vector;
import src.ResourceLoader; import src.ResourceLoader;
import src.MarbleGame; import src.MarbleGame;
class EnterNameDlg extends GuiControl { class EnterNameDlg extends GuiImage {
public function new(place:Int, okFunc:String->Void) { public function new() {
super(); var res = ResourceLoader.getImage("data/ui/xbox/roundedBG.png").resource.toTile();
this.position = new Vector(); super(res);
this.extent = new Vector(640, 480);
this.horizSizing = Width; this.horizSizing = Width;
this.vertSizing = Height; this.vertSizing = Height;
this.position = new Vector();
this.extent = new Vector(640, 480);
function loadButtonImages(path:String) { var arial14fontdata = ResourceLoader.getFileEntry("data/font/Arial Bold.fnt");
var normal = ResourceLoader.getResource('${path}_n.png', ResourceLoader.getImage, this.imageResources).toTile();
var hover = ResourceLoader.getResource('${path}_h.png', ResourceLoader.getImage, this.imageResources).toTile();
var pressed = ResourceLoader.getResource('${path}_d.png', ResourceLoader.getImage, this.imageResources).toTile();
return [normal, hover, pressed];
}
var arial14fontdata = ResourceLoader.getFileEntry("data/font/arial.fnt");
var arial14b = new BitmapFont(arial14fontdata.entry); var arial14b = new BitmapFont(arial14fontdata.entry);
@:privateAccess arial14b.loader = ResourceLoader.loader; @:privateAccess arial14b.loader = ResourceLoader.loader;
var arial14 = arial14b.toSdfFont(cast 12 * Settings.uiScale, MultiChannel); var arial14 = arial14b.toSdfFont(cast 21 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel);
var domcasual32fontdata = ResourceLoader.getFileEntry("data/font/DomCasualD.fnt"); var yesNoFrame = new GuiImage(ResourceLoader.getResource("data/ui/xbox/popupGUI.png", ResourceLoader.getImage, this.imageResources).toTile());
var domcasual32b = new BitmapFont(domcasual32fontdata.entry); yesNoFrame.horizSizing = Center;
@:privateAccess domcasual32b.loader = ResourceLoader.loader; yesNoFrame.vertSizing = Center;
var domcasual32 = domcasual32b.toSdfFont(cast 26 * Settings.uiScale, MultiChannel); yesNoFrame.position = new Vector(70, 30);
var domcasual48 = domcasual32b.toSdfFont(cast 42 * Settings.uiScale, MultiChannel); yesNoFrame.extent = new Vector(512, 400);
this.addChild(yesNoFrame);
function mlFontLoader(text:String) { var text = "Enter your name";
switch (text) {
case "DomCasual32":
return domcasual32;
case "DomCasual48":
return domcasual48;
case "Arial14":
return arial14;
default:
return null;
}
}
var dlg = new GuiImage(ResourceLoader.getResource("data/ui/endgame/enternamebox.png", ResourceLoader.getImage, this.imageResources).toTile()); var yesNoText = new GuiMLText(arial14, null);
dlg.horizSizing = Center; yesNoText.position = new Vector(103, 85);
dlg.vertSizing = Center; yesNoText.extent = new Vector(313, 186);
dlg.position = new Vector(110, 112); yesNoText.text.text = text;
dlg.extent = new Vector(420, 256); yesNoText.text.textColor = 0xEBEBEB;
this.addChild(dlg); yesNoFrame.addChild(yesNoText);
var enterNameEdit = new GuiTextInput(domcasual32); var textFrame = new GuiControl();
enterNameEdit.text.textColor = 0; textFrame.position = new Vector(33, 107);
enterNameEdit.text.selectionColor.setColor(0xFFFFFFFF); textFrame.extent = new Vector(232, 40);
enterNameEdit.text.selectionTile = h2d.Tile.fromColor(0x808080, 0, hxd.Math.ceil(enterNameEdit.text.font.lineHeight)); textFrame.horizSizing = Center;
enterNameEdit.position = new Vector(28, 130); yesNoFrame.addChild(textFrame);
enterNameEdit.extent = new Vector(363, 38);
enterNameEdit.text.text = Settings.highscoreName; var textInput = new GuiTextInput(arial14);
haxe.Timer.delay(() -> { textInput.position = new Vector(6, 5);
enterNameEdit.text.focus(); textInput.extent = new Vector(216, 40);
}, 5); textInput.horizSizing = Width;
textInput.vertSizing = Height;
textInput.text.textColor = 0xEBEBEB;
textInput.text.selectionColor.setColor(0x8DFF8D);
textInput.text.selectionTile = h2d.Tile.fromColor(0x88BCEE, 0, hxd.Math.ceil(textInput.text.font.lineHeight));
textFrame.addChild(textInput);
textInput.text.text = Settings.highscoreName;
enterNameEdit.text.onFocus = (e) -> { enterNameEdit.text.onFocus = (e) -> {
dlg.vertSizing = Bottom; dlg.vertSizing = Bottom;
dlg.position = new Vector(110, 56); dlg.position = new Vector(110, 56);
@ -76,34 +67,28 @@ class EnterNameDlg extends GuiControl {
dlg.render(MarbleGame.canvas.scene2d); dlg.render(MarbleGame.canvas.scene2d);
} }
var okbutton = new GuiButton(loadButtonImages("data/ui/endgame/ok")); var okButton = new GuiXboxButton("Ok", 120);
okbutton.position = new Vector(151, 184); okButton.position = new Vector(211, 248);
okbutton.extent = new Vector(110, 55); okButton.extent = new Vector(120, 94);
okbutton.accelerator = hxd.Key.ENTER; okButton.vertSizing = Top;
okbutton.gamepadAccelerator = ["A"]; okButton.accelerators = [hxd.Key.ENTER];
okbutton.pressedAction = (sender) -> { okButton.gamepadAccelerator = ["A"];
MarbleGame.canvas.popDialog(this); okButton.pressedAction = (sender) -> {
Settings.highscoreName = enterNameEdit.text.text; Settings.highscoreName = textInput.text.text.substr(0, 15); // Max 15 pls
okFunc(enterNameEdit.text.text); Settings.save();
MarbleGame.canvas.setContent(new MultiplayerGui());
} }
dlg.addChild(okbutton); yesNoFrame.addChild(okButton);
var wnd = new GuiImage(ResourceLoader.getResource("data/ui/endgame/window.png", ResourceLoader.getImage, this.imageResources).toTile()); var cancelButton = new GuiXboxButton("Cancel", 120);
wnd.horizSizing = Width; cancelButton.position = new Vector(321, 248);
wnd.vertSizing = Height; cancelButton.extent = new Vector(120, 94);
wnd.position = new Vector(16, 119); cancelButton.vertSizing = Top;
wnd.extent = new Vector(388, 56); cancelButton.accelerators = [hxd.Key.ENTER];
dlg.addChild(wnd); cancelButton.gamepadAccelerator = ["A"];
cancelButton.pressedAction = (sender) -> {
var enterNameText = new GuiMLText(domcasual32, mlFontLoader); MarbleGame.canvas.setContent(new MultiplayerGui());
enterNameText.text.textColor = 0xFFFFFF; }
enterNameText.text.filter = new DropShadow(1.414, 0.785, 0x7777777F, 1, 0, 0.4, 1, true); yesNoFrame.addChild(cancelButton);
enterNameText.position = new Vector(37, 23);
enterNameText.extent = new Vector(345, 85);
// enterNameText.justify = Center;
enterNameText.text.text = '<font face="Arial14"><br/></font><p align="center"><font face="DomCasual48">Well Done!<br/></font><font face="DomCasual32">You have the${["", " second", " third", " fourth", " fifth"][place]} top time!</font></p>';
dlg.addChild(enterNameText);
dlg.addChild(enterNameEdit);
} }
} }

View file

@ -120,8 +120,10 @@ class MPServerListGui extends GuiImage {
nextButton.accelerators = [hxd.Key.ENTER]; nextButton.accelerators = [hxd.Key.ENTER];
nextButton.gamepadAccelerator = ["X"]; nextButton.gamepadAccelerator = ["X"];
nextButton.pressedAction = (e) -> { nextButton.pressedAction = (e) -> {
MarbleGame.canvas.setContent(new MultiplayerLoadingGui("Connecting"));
Net.joinServer(ourServerList[curSelection].name, () -> { Net.joinServer(ourServerList[curSelection].name, () -> {
MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false)); MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false));
Net.remoteServerInfo = ourServerList[curSelection];
}); });
}; };
bottomBar.addChild(nextButton); bottomBar.addChild(nextButton);

View file

@ -78,6 +78,10 @@ class MultiplayerGui extends GuiImage {
// }); // });
}); });
btnList.addButton(5, 'Change Display Name', (e) -> {
MarbleGame.canvas.setContent(new EnterNameDlg());
});
var bottomBar = new GuiControl(); var bottomBar = new GuiControl();
bottomBar.position = new Vector(0, 590); bottomBar.position = new Vector(0, 590);
bottomBar.extent = new Vector(640, 200); bottomBar.extent = new Vector(640, 200);

View file

@ -1,5 +1,6 @@
package gui; package gui;
import net.Net;
import net.NetCommands; import net.NetCommands;
import modes.GameMode.ScoreType; import modes.GameMode.ScoreType;
import src.Util; import src.Util;
@ -19,6 +20,8 @@ class MultiplayerLevelSelectGui extends GuiImage {
static var setLevelFn:Int->Void; static var setLevelFn:Int->Void;
static var playSelectedLevel:Void->Void; static var playSelectedLevel:Void->Void;
var playerList:GuiMLTextListCtrl;
var updatePlayerCountFn:(Int, Int, Int, Int) -> Void;
var innerCtrl:GuiControl; var innerCtrl:GuiControl;
public function new(isHost:Bool) { public function new(isHost:Bool) {
@ -29,8 +32,15 @@ class MultiplayerLevelSelectGui extends GuiImage {
var arial14b = new BitmapFont(arial14fontdata.entry); var arial14b = new BitmapFont(arial14fontdata.entry);
@:privateAccess arial14b.loader = ResourceLoader.loader; @:privateAccess arial14b.loader = ResourceLoader.loader;
var arial14 = arial14b.toSdfFont(cast 21 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel); var arial14 = arial14b.toSdfFont(cast 21 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel);
var arial12 = arial14b.toSdfFont(cast 16 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel);
function mlFontLoader(text:String) { function mlFontLoader(text:String) {
return arial14; switch (text) {
case "arial14":
return arial14;
case "arial12":
return arial12;
}
return null;
} }
MarbleGame.instance.toRecord = false; MarbleGame.instance.toRecord = false;
@ -127,11 +137,34 @@ class MultiplayerLevelSelectGui extends GuiImage {
rootTitle.position = new Vector(100, 30); rootTitle.position = new Vector(100, 30);
rootTitle.extent = new Vector(1120, 80); rootTitle.extent = new Vector(1120, 80);
rootTitle.text.textColor = 0xFFFFFF; rootTitle.text.textColor = 0xFFFFFF;
rootTitle.text.text = "SELECT LEVEL"; rootTitle.text.text = "LOBBY";
rootTitle.text.alpha = 0.5; rootTitle.text.alpha = 0.5;
innerCtrl.addChild(rootTitle); innerCtrl.addChild(rootTitle);
var bottomBar = new GuiControl();
var playerWnd = new GuiImage(ResourceLoader.getResource("data/ui/xbox/achievementWindow.png", ResourceLoader.getImage, this.imageResources).toTile());
playerWnd.horizSizing = Right;
playerWnd.vertSizing = Bottom;
playerWnd.position = new Vector(330, 58);
playerWnd.extent = new Vector(640, 480);
innerCtrl.addChild(playerWnd);
var playerListArr = [Settings.highscoreName];
if (Net.isClient) {
for (c => v in Net.clientIdMap) {
playerListArr.push(v.getName());
}
}
playerList = new GuiMLTextListCtrl(arial14, playerListArr, null);
playerList.selectedColor = 0xF29515;
playerList.selectedFillColor = 0xEBEBEB;
playerList.position = new Vector(25, 22);
playerList.extent = new Vector(550, 480);
playerList.scrollable = true;
playerList.onSelectedFunc = (sel) -> {}
playerWnd.addChild(playerList);
var bottomBar = new GuiControl();
bottomBar.position = new Vector(0, 590); bottomBar.position = new Vector(0, 590);
bottomBar.extent = new Vector(640, 200); bottomBar.extent = new Vector(640, 200);
bottomBar.horizSizing = Width; bottomBar.horizSizing = Width;
@ -144,7 +177,10 @@ class MultiplayerLevelSelectGui extends GuiImage {
backButton.horizSizing = Right; backButton.horizSizing = Right;
backButton.gamepadAccelerator = ["B"]; backButton.gamepadAccelerator = ["B"];
backButton.accelerators = [hxd.Key.ESCAPE, hxd.Key.BACKSPACE]; backButton.accelerators = [hxd.Key.ESCAPE, hxd.Key.BACKSPACE];
backButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new DifficultySelectGui()); backButton.pressedAction = (e) -> {
Net.disconnect();
MarbleGame.canvas.setContent(new CreateMatchGui());
}
bottomBar.addChild(backButton); bottomBar.addChild(backButton);
// var lbButton = new GuiXboxButton("Leaderboard", 220); // var lbButton = new GuiXboxButton("Leaderboard", 220);
@ -153,18 +189,17 @@ class MultiplayerLevelSelectGui extends GuiImage {
// lbButton.horizSizing = Right; // lbButton.horizSizing = Right;
// bottomBar.addChild(lbButton); // bottomBar.addChild(lbButton);
if (isHost) { var nextButton = new GuiXboxButton("Ready", 160);
var nextButton = new GuiXboxButton("Play", 160); nextButton.position = new Vector(960, 0);
nextButton.position = new Vector(960, 0); nextButton.vertSizing = Bottom;
nextButton.vertSizing = Bottom; nextButton.horizSizing = Right;
nextButton.horizSizing = Right; nextButton.gamepadAccelerator = ["A"];
nextButton.gamepadAccelerator = ["A"]; nextButton.accelerators = [hxd.Key.ENTER];
nextButton.accelerators = [hxd.Key.ENTER]; nextButton.pressedAction = (e) -> {
nextButton.pressedAction = (e) -> { NetCommands.toggleReadiness(Net.isClient ? Net.clientId : 0);
NetCommands.playLevel(); };
}; bottomBar.addChild(nextButton);
bottomBar.addChild(nextButton);
}
playSelectedLevel = () -> { playSelectedLevel = () -> {
MarbleGame.instance.playMission(curMission, true); MarbleGame.instance.playMission(curMission, true);
} }
@ -176,42 +211,19 @@ class MultiplayerLevelSelectGui extends GuiImage {
levelWnd.horizSizing = Right; levelWnd.horizSizing = Right;
innerCtrl.addChild(levelWnd); innerCtrl.addChild(levelWnd);
var statIcon = new GuiImage(ResourceLoader.getResource("data/ui/xbox/statIcon.png", ResourceLoader.getImage, this.imageResources).toTile());
statIcon.position = new Vector(29, 54);
statIcon.extent = new Vector(20, 20);
levelWnd.addChild(statIcon);
var eggIcon = new GuiImage(ResourceLoader.getResource("data/ui/xbox/eggIcon.png", ResourceLoader.getImage, this.imageResources).toTile());
eggIcon.position = new Vector(29, 79);
eggIcon.extent = new Vector(20, 20);
levelWnd.addChild(eggIcon);
var c0 = 0xEBEBEB; var c0 = 0xEBEBEB;
var c1 = 0x8DFF8D; var c1 = 0x8DFF8D;
var c2 = 0x88BCEE; var c2 = 0x88BCEE;
var c3 = 0xFF7575; var c3 = 0xFF7575;
var levelInfoLeft = new GuiMLText(arial14, mlFontLoader); var levelInfoLeft = new GuiMLText(arial14, mlFontLoader);
levelInfoLeft.position = new Vector(69, 54); levelInfoLeft.position = new Vector(33, 40);
levelInfoLeft.extent = new Vector(180, 100); levelInfoLeft.extent = new Vector(480, 100);
levelInfoLeft.text.text = '<p align="right"><font color="#EBEBEB">My Best Time:</font><br/><font color="#EBEBEB">Par Time:</font></p>'; levelInfoLeft.text.text = '';
levelInfoLeft.text.lineSpacing = 6; levelInfoLeft.text.lineSpacing = 0;
levelInfoLeft.text.filter = new h2d.filter.DropShadow(2, 0.785, 0x000000, 1, 0, 0.4, 1, true);
levelWnd.addChild(levelInfoLeft); levelWnd.addChild(levelInfoLeft);
var levelInfoMid = new GuiMLText(arial14, mlFontLoader);
levelInfoMid.position = new Vector(269, 54);
levelInfoMid.extent = new Vector(180, 100);
levelInfoMid.text.text = '<p align="left"><font color="#EBEBEB">None</font><br/><font color="#88BCEE">99:59:99</font></p>';
levelInfoMid.text.lineSpacing = 6;
levelWnd.addChild(levelInfoMid);
var levelInfoRight = new GuiMLText(arial14, mlFontLoader);
levelInfoRight.position = new Vector(379, 54);
levelInfoRight.extent = new Vector(180, 100);
levelInfoRight.text.text = '<p align="left"><font color="#EBEBEB">Level 1<br/>Difficulty 1</font></p>';
levelInfoRight.text.lineSpacing = 6;
levelWnd.addChild(levelInfoRight);
var levelNames = difficultyMissions.map(x -> x.title); var levelNames = difficultyMissions.map(x -> x.title);
var levelSelectOpts = new GuiXboxOptionsList(6, "Level", levelNames); var levelSelectOpts = new GuiXboxOptionsList(6, "Level", levelNames);
@ -230,10 +242,6 @@ class MultiplayerLevelSelectGui extends GuiImage {
var misFile = Path.withoutExtension(Path.withoutDirectory(curMission.path)); var misFile = Path.withoutExtension(Path.withoutDirectory(curMission.path));
var mis = difficultyMissions[idx]; var mis = difficultyMissions[idx];
var requestToken = currentToken; var requestToken = currentToken;
if (Settings.easterEggs.exists(mis.path))
eggIcon.bmp.visible = true;
else
eggIcon.bmp.visible = false;
MarbleGame.instance.setPreviewMission(misFile, () -> { MarbleGame.instance.setPreviewMission(misFile, () -> {
lock = false; lock = false;
if (requestToken != currentToken) if (requestToken != currentToken)
@ -243,34 +251,31 @@ class MultiplayerLevelSelectGui extends GuiImage {
loadText.text.visible = false; loadText.text.visible = false;
loadTextBg.text.visible = false; loadTextBg.text.visible = false;
}); });
var hostName = Settings.highscoreName;
var scoreType = mis.missionInfo.gamemode != null if (!Net.isHost) {
&& mis.missionInfo.gamemode.toLowerCase() == 'scrum' ? ScoreType.Score : ScoreType.Time; hostName = Net.clientIdMap[0].getName();
var myScore = Settings.getScores(mis.path);
var scoreDisp = "None";
if (myScore.length != 0)
scoreDisp = scoreType == Time ? Util.formatTime(myScore[0].time) : Util.formatScore(myScore[0].time);
var isPar = myScore.length != 0 && myScore[0].time < mis.qualifyTime;
var scoreColor = "#EBEBEB";
if (isPar)
scoreColor = "#8DFF8D";
if (scoreType == Score && myScore.length == 0)
scoreColor = "#EBEBEB";
if (scoreType == Time) {
levelInfoLeft.text.text = '<p align="right"><font color="#EBEBEB">My Best Time:</font><br/><font color="#EBEBEB">Par Time:</font></p>';
levelInfoMid.text.text = '<p align="left"><font color="${scoreColor}">${scoreDisp}</font><br/><font color="#88BCEE">${Util.formatTime(mis.qualifyTime)}</font></p>';
} }
if (scoreType == Score) {
levelInfoLeft.text.text = '<p align="right"><font color="#EBEBEB">My Best Score:</font></p>'; if (Net.isHost) {
levelInfoMid.text.text = '<p align="left"><font color="${scoreColor}">${scoreDisp}</font></p>'; updatePlayerCountFn = (pub:Int, priv:Int, publicTotal:Int, privateTotal:Int) -> {
levelInfoLeft.text.text = '<p><font face="arial14">Host: ${hostName}</font></p>'
+ '<p><font face="arial14">Level: ${mis.title}</font></p>'
+ '<p><font face="arial12">Private Slots: ${pub}/${publicTotal}, Public Slots: ${priv}/${privateTotal}</font></p>';
}
updatePlayerCountFn(0, 0, Net.serverInfo.maxPlayers - Net.serverInfo.privateSlots, Net.serverInfo.privateSlots);
}
if (Net.isClient) {
updatePlayerCountFn = (pub:Int, priv:Int, publicTotal:Int, privateTotal:Int) -> {
levelInfoLeft.text.text = '<p><font face="arial14">Host: ${hostName}</font></p>' + '<p><font face="arial14">Level: ${mis.title}</font></p>';
}
updatePlayerCountFn(0, 0, 0, 0);
} }
levelInfoRight.text.text = '<p align="left"><font color="#EBEBEB">Level ${mis.missionInfo.level}<br/>Difficulty ${mis.missionInfo.difficulty == null ? "" : mis.missionInfo.difficulty}</font></p>';
return true; return true;
} }
setLevelFn = setLevel; setLevelFn = setLevel;
levelSelectOpts.position = new Vector(380, 435); levelSelectOpts.position = new Vector(380, 430);
levelSelectOpts.extent = new Vector(815, 94); levelSelectOpts.extent = new Vector(815, 94);
levelSelectOpts.vertSizing = Bottom; levelSelectOpts.vertSizing = Bottom;
levelSelectOpts.horizSizing = Right; levelSelectOpts.horizSizing = Right;
@ -295,4 +300,16 @@ class MultiplayerLevelSelectGui extends GuiImage {
super.onResize(width, height); super.onResize(width, height);
} }
public function updateLobbyNames() {
var names = [Settings.highscoreName];
for (id => c in Net.clientIdMap) {
names.push(c.getName());
}
playerList.setTexts(names);
}
public function updatePlayerCount(pub:Int, priv:Int, publicTotal:Int, privateTotal:Int) {
updatePlayerCountFn(pub, priv, publicTotal, privateTotal);
}
} }

View file

@ -10,8 +10,11 @@ import src.Util;
class MultiplayerLoadingGui extends GuiImage { class MultiplayerLoadingGui extends GuiImage {
var loadText:GuiText; var loadText:GuiText;
var loadTextBg:GuiText; var loadTextBg:GuiText;
var loadAnim:GuiLoadAnim;
var bottomBar:GuiControl;
var innerCtrl:GuiControl;
public function new(missionName:String) { public function new(initialStatus:String) {
var res = ResourceLoader.getImage("data/ui/game/CloudBG.jpg").resource.toTile(); var res = ResourceLoader.getImage("data/ui/game/CloudBG.jpg").resource.toTile();
super(res); super(res);
this.position = new Vector(); this.position = new Vector();
@ -31,7 +34,7 @@ class MultiplayerLoadingGui extends GuiImage {
@:privateAccess arial14b.loader = ResourceLoader.loader; @:privateAccess arial14b.loader = ResourceLoader.loader;
var arial14 = arial14b.toSdfFont(cast 21 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel); var arial14 = arial14b.toSdfFont(cast 21 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel);
var loadAnim = new GuiLoadAnim(); loadAnim = new GuiLoadAnim();
loadAnim.position = new Vector(610, 253); loadAnim.position = new Vector(610, 253);
loadAnim.extent = new Vector(63, 63); loadAnim.extent = new Vector(63, 63);
loadAnim.horizSizing = Center; loadAnim.horizSizing = Center;
@ -44,7 +47,7 @@ class MultiplayerLoadingGui extends GuiImage {
loadTextBg.horizSizing = Center; loadTextBg.horizSizing = Center;
loadTextBg.vertSizing = Bottom; loadTextBg.vertSizing = Bottom;
loadTextBg.justify = Center; loadTextBg.justify = Center;
loadTextBg.text.text = "Loading"; loadTextBg.text.text = initialStatus;
loadTextBg.text.textColor = 0; loadTextBg.text.textColor = 0;
this.addChild(loadTextBg); this.addChild(loadTextBg);
@ -54,12 +57,69 @@ class MultiplayerLoadingGui extends GuiImage {
loadText.horizSizing = Center; loadText.horizSizing = Center;
loadText.vertSizing = Bottom; loadText.vertSizing = Bottom;
loadText.justify = Center; loadText.justify = Center;
loadText.text.text = "Loading"; loadText.text.text = initialStatus;
this.addChild(loadText); this.addChild(loadText);
#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);
bottomBar = new GuiControl();
bottomBar.position = new Vector(0, 590);
bottomBar.extent = new Vector(640, 200);
bottomBar.horizSizing = Width;
bottomBar.vertSizing = Bottom;
innerCtrl.addChild(bottomBar);
} }
public function setLoadingStatus(str:String) { public function setLoadingStatus(str:String) {
loadText.text.text = str; loadText.text.text = str;
loadTextBg.text.text = str; loadTextBg.text.text = str;
} }
public function setErrorStatus(str:String) {
loadText.text.text = str;
loadTextBg.text.text = str;
loadAnim.anim.visible = false;
var backButton = new GuiXboxButton("Ok", 160);
backButton.position = new Vector(960, 0);
backButton.vertSizing = Bottom;
backButton.horizSizing = Right;
backButton.gamepadAccelerator = ["A"];
backButton.accelerators = [hxd.Key.ENTER];
backButton.pressedAction = (e) -> {
MarbleGame.canvas.setContent(new MultiplayerGui());
};
bottomBar.addChild(backButton);
MarbleGame.canvas.render(MarbleGame.canvas.scene2d);
}
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);
}
} }

View file

@ -67,6 +67,15 @@ class InputBitStream {
public function readFloat() { public function readFloat() {
return FPHelper.i32ToFloat(readInt32()); return FPHelper.i32ToFloat(readInt32());
} }
public function readString() {
var length = readByte();
var str = "";
for (i in 0...length) {
str += String.fromCharCode(readByte());
}
return str;
}
} }
class OutputBitStream { class OutputBitStream {
@ -135,4 +144,11 @@ class OutputBitStream {
public function writeFloat(value:Float) { public function writeFloat(value:Float) {
writeInt(FPHelper.floatToI32(value), 32); writeInt(FPHelper.floatToI32(value), 32);
} }
public function writeString(value:String) {
writeByte(value.length);
for (i in 0...value.length) {
writeByte(StringTools.fastCodeAt(value, i));
}
}
} }

View file

@ -26,6 +26,7 @@ class ClientConnection extends GameConnection {
this.datachannel = datachannel; this.datachannel = datachannel;
this.state = GameplayState.LOBBY; this.state = GameplayState.LOBBY;
this.rtt = 0; this.rtt = 0;
this.name = "Unknown";
} }
override function sendBytes(b:Bytes) { override function sendBytes(b:Bytes) {
@ -38,6 +39,7 @@ class DummyConnection extends GameConnection {
public function new(id:Int) { public function new(id:Int) {
super(id); super(id);
this.state = GameplayState.GAME; this.state = GameplayState.GAME;
this.lobbyReady = true;
} }
} }
@ -46,16 +48,23 @@ abstract class GameConnection {
var id:Int; var id:Int;
var state:GameplayState; var state:GameplayState;
var moveManager:MoveManager; var moveManager:MoveManager;
var name:String;
var lobbyReady:Bool;
public function new(id:Int) { public function new(id:Int) {
this.id = id; this.id = id;
this.moveManager = new MoveManager(this); this.moveManager = new MoveManager(this);
this.lobbyReady = false;
} }
public function ready() { public function ready() {
state = GameplayState.GAME; state = GameplayState.GAME;
} }
public function toggleLobbyReady() {
lobbyReady = !lobbyReady;
}
public function queueMove(m:NetMove) { public function queueMove(m:NetMove) {
moveManager.queueMove(m); moveManager.queueMove(m);
} }
@ -81,4 +90,12 @@ abstract class GameConnection {
} }
public function sendBytes(b:haxe.io.Bytes) {} public function sendBytes(b:haxe.io.Bytes) {}
public inline function getName() {
return name;
}
public inline function setName(value:String) {
name = value;
}
} }

View file

@ -1,5 +1,6 @@
package net; package net;
import h3d.Vector;
import net.NetPacket.MarbleNetFlags; import net.NetPacket.MarbleNetFlags;
import net.NetPacket.MarbleUpdatePacket; import net.NetPacket.MarbleUpdatePacket;
import net.Net; import net.Net;
@ -11,6 +12,7 @@ class OtherMarbleUpdate {
var lastHeliTick:Int; var lastHeliTick:Int;
var lastMegaTick:Int; var lastMegaTick:Int;
var lastPowerUpId:Int; var lastPowerUpId:Int;
var lastGravityUp:Vector;
public function new() {} public function new() {}
} }
@ -48,6 +50,10 @@ class MarbleUpdateQueue {
update.powerUpId = otherUpdate.lastPowerUpId; update.powerUpId = otherUpdate.lastPowerUpId;
else else
otherUpdate.lastPowerUpId = update.powerUpId; otherUpdate.lastPowerUpId = update.powerUpId;
if (update.netFlags & MarbleNetFlags.GravityChange == 0)
update.gravityDirection = otherUpdate.lastGravityUp;
else
otherUpdate.lastGravityUp = update.gravityDirection;
ourList.push(update); ourList.push(update);
} else { } else {
var otherUpdate = new OtherMarbleUpdate(); var otherUpdate = new OtherMarbleUpdate();
@ -61,6 +67,8 @@ class MarbleUpdateQueue {
otherUpdate.lastMegaTick = update.megaTick; otherUpdate.lastMegaTick = update.megaTick;
if (update.netFlags & MarbleNetFlags.PickupPowerup != 0) if (update.netFlags & MarbleNetFlags.PickupPowerup != 0)
otherUpdate.lastPowerUpId = update.powerUpId; otherUpdate.lastPowerUpId = update.powerUpId;
if (update.netFlags & MarbleNetFlags.GravityChange != 0)
otherUpdate.lastGravityUp = update.gravityDirection;
otherMarbleUpdates[cc] = otherUpdate; otherMarbleUpdates[cc] = otherUpdate;
} }
} else { } else {
@ -75,6 +83,8 @@ class MarbleUpdateQueue {
update.megaTick = myMarbleUpdate.megaTick; update.megaTick = myMarbleUpdate.megaTick;
if (update.netFlags & MarbleNetFlags.PickupPowerup == 0) if (update.netFlags & MarbleNetFlags.PickupPowerup == 0)
update.powerUpId = myMarbleUpdate.powerUpId; update.powerUpId = myMarbleUpdate.powerUpId;
if (update.netFlags & MarbleNetFlags.GravityChange == 0)
update.gravityDirection = myMarbleUpdate.gravityDirection;
} }
myMarbleUpdate = update; myMarbleUpdate = update;
ourMoveApplied = false; ourMoveApplied = false;

View file

@ -7,6 +7,7 @@ import net.Net.ServerInfo;
import hx.ws.WebSocket; import hx.ws.WebSocket;
import src.Console; import src.Console;
import hx.ws.Types.MessageType; import hx.ws.Types.MessageType;
import gui.MultiplayerLoadingGui;
typedef RemoteServerInfo = { typedef RemoteServerInfo = {
name:String, name:String,
@ -93,7 +94,7 @@ class MasterServerClient {
if (conts.type == "connect") { if (conts.type == "connect") {
if (!Net.isHost) { if (!Net.isHost) {
ws.send(Json.stringify({ ws.send(Json.stringify({
type: "connectResponse", type: "connectFailed",
success: false, success: false,
reason: "The server has shut down" reason: "The server has shut down"
})); }));
@ -101,7 +102,7 @@ class MasterServerClient {
} }
if (Net.serverInfo.players >= Net.serverInfo.maxPlayers) { if (Net.serverInfo.players >= Net.serverInfo.maxPlayers) {
ws.send(Json.stringify({ ws.send(Json.stringify({
type: "connectResponse", type: "connectFailed",
success: false, success: false,
reason: "The server is full" reason: "The server is full"
})); }));
@ -122,7 +123,10 @@ class MasterServerClient {
@:privateAccess Net.client.setRemoteDescription(sdpObj.sdp, sdpObj.type); @:privateAccess Net.client.setRemoteDescription(sdpObj.sdp, sdpObj.type);
} }
if (conts.type == "connectFailed") { if (conts.type == "connectFailed") {
MarbleGame.canvas.pushDialog(new MessageBoxOkDlg(conts.reason)); var loadGui:MultiplayerLoadingGui = cast MarbleGame.canvas.content;
if (loadGui != null) {
loadGui.setErrorStatus(conts.reason);
}
} }
} }
} }

View file

@ -1,5 +1,9 @@
package net; package net;
import gui.MultiplayerLevelSelectGui;
import gui.Canvas;
import net.MasterServerClient.RemoteServerInfo;
import gui.MultiplayerLoadingGui;
import src.ResourceLoader; import src.ResourceLoader;
import src.AudioManager; import src.AudioManager;
import net.NetPacket.GemPickupPacket; import net.NetPacket.GemPickupPacket;
@ -18,6 +22,7 @@ import src.Console;
import net.NetCommands; import net.NetCommands;
import src.MarbleGame; import src.MarbleGame;
import hx.ws.Types.MessageType; import hx.ws.Types.MessageType;
import src.Settings;
enum abstract NetPacketType(Int) from Int to Int { enum abstract NetPacketType(Int) from Int to Int {
var NullPacket; var NullPacket;
@ -60,13 +65,11 @@ class Net {
static var client:RTCPeerConnection; static var client:RTCPeerConnection;
static var clientDatachannel:RTCDataChannel; static var clientDatachannel:RTCDataChannel;
static var masterWs:WebSocket;
public static var isMP:Bool; public static var isMP:Bool;
public static var isHost:Bool; public static var isHost:Bool;
public static var isClient:Bool; public static var isClient:Bool;
public static var startMP:Bool; public static var lobbyHostReady:Bool;
public static var clientId:Int; public static var clientId:Int;
public static var networkRNG:Float; public static var networkRNG:Float;
@ -74,6 +77,7 @@ class Net {
public static var clientIdMap:Map<Int, GameConnection> = []; public static var clientIdMap:Map<Int, GameConnection> = [];
public static var clientConnection:ClientConnection; public static var clientConnection:ClientConnection;
public static var serverInfo:ServerInfo; public static var serverInfo:ServerInfo;
public static var remoteServerInfo:RemoteServerInfo;
public static function hostServer(name:String, maxPlayers:Int, privateSlots:Int, privateServer:Bool) { 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()); serverInfo = new ServerInfo(name, 1, maxPlayers, privateSlots, privateServer, Std.int(999999 * Math.random()), "Lobby", getPlatform());
@ -84,30 +88,6 @@ class Net {
isMP = true; isMP = true;
MasterServerClient.instance.sendServerInfo(serverInfo); MasterServerClient.instance.sendServerInfo(serverInfo);
}); });
// 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");
// 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): {}
// }
// }
// isMP = true;
} }
public static function addClientFromSdp(sdpString:String, onFinishSdp:String->Void) { public static function addClientFromSdp(sdpString:String, onFinishSdp:String->Void) {
@ -167,6 +147,10 @@ class Net {
clientDatachannel = client.createDatachannel("mp"); clientDatachannel = client.createDatachannel("mp");
clientDatachannel.onOpen = (n) -> { clientDatachannel.onOpen = (n) -> {
var loadGui:MultiplayerLoadingGui = cast MarbleGame.canvas.content;
if (loadGui != null) {
loadGui.setLoadingStatus("Handshaking");
}
Console.log("Successfully connected!"); Console.log("Successfully connected!");
clients.set(client, new ClientConnection(0, client, clientDatachannel)); // host is always 0 clients.set(client, new ClientConnection(0, client, clientDatachannel)); // host is always 0
clientIdMap[0] = clients[client]; clientIdMap[0] = clients[client];
@ -182,6 +166,11 @@ class Net {
if (MarbleGame.instance.world != null) { if (MarbleGame.instance.world != null) {
MarbleGame.instance.quitMission(); MarbleGame.instance.quitMission();
} }
if (!(MarbleGame.canvas.content is MultiplayerLoadingGui)) {
var loadGui = new MultiplayerLoadingGui("Server closed");
MarbleGame.canvas.setContent(loadGui);
loadGui.setErrorStatus("Server closed");
}
} }
clientDatachannel.onError = (msg) -> { clientDatachannel.onError = (msg) -> {
Console.log('Errored out due to ${msg}'); Console.log('Errored out due to ${msg}');
@ -189,63 +178,15 @@ class Net {
if (MarbleGame.instance.world != null) { if (MarbleGame.instance.world != null) {
MarbleGame.instance.quitMission(); MarbleGame.instance.quitMission();
} }
var loadGui = new MultiplayerLoadingGui("Connection error");
MarbleGame.canvas.setContent(loadGui);
loadGui.setErrorStatus("Connection error");
} }
isMP = true; isMP = true;
isHost = false; isHost = false;
isClient = true; 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;
// }
} }
public static function disconnect() { public static function disconnect() {
@ -259,6 +200,9 @@ class Net {
Net.clientId = 0; Net.clientId = 0;
Net.clientIdMap.clear(); Net.clientIdMap.clear();
Net.clientConnection = null; Net.clientConnection = null;
Net.serverInfo = null;
Net.remoteServerInfo = null;
Net.lobbyHostReady = false;
} }
if (Net.isHost) { if (Net.isHost) {
NetCommands.serverClosed(); NetCommands.serverClosed();
@ -271,6 +215,9 @@ class Net {
Net.clients.clear(); Net.clients.clear();
Net.clientIdMap.clear(); Net.clientIdMap.clear();
MasterServerClient.disconnectFromMasterServer(); MasterServerClient.disconnectFromMasterServer();
Net.serverInfo = null;
Net.remoteServerInfo = null;
Net.lobbyHostReady = false;
} }
} }
@ -308,6 +255,10 @@ class Net {
serverInfo.players++; serverInfo.players++;
MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the new player MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the new player
if (MarbleGame.canvas.content is MultiplayerLevelSelectGui) {
cast(MarbleGame.canvas.content, MultiplayerLevelSelectGui).updateLobbyNames();
}
} }
static function onConnectedToServer() { static function onConnectedToServer() {
@ -327,6 +278,12 @@ class Net {
serverInfo.players--; serverInfo.players--;
MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the player leave MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the player leave
NetCommands.clientDisconnected(cc.id); NetCommands.clientDisconnected(cc.id);
AudioManager.playSound(ResourceLoader.getAudio("data/sound/infotutorial.wav").resource);
if (MarbleGame.canvas.content is MultiplayerLevelSelectGui) {
cast(MarbleGame.canvas.content, MultiplayerLevelSelectGui).updateLobbyNames();
}
} }
static function sendPlayerInfosBytes() { static function sendPlayerInfosBytes() {
@ -335,9 +292,22 @@ class Net {
var cnt = 0; var cnt = 0;
for (c in clientIdMap) for (c in clientIdMap)
cnt++; cnt++;
b.writeByte(cnt); b.writeByte(cnt + 1); // all + host
for (c => v in clientIdMap) for (c => v in clientIdMap) {
b.writeByte(c); b.writeByte(c);
var name = v.getName();
b.writeByte(name.length);
for (i in 0...name.length) {
b.writeByte(StringTools.fastCodeAt(name, i));
}
}
// Write host data
b.writeByte(0);
var name = Settings.highscoreName;
b.writeByte(name.length);
for (i in 0...name.length) {
b.writeByte(StringTools.fastCodeAt(name, i));
}
return b.getBytes(); return b.getBytes();
} }
@ -352,6 +322,7 @@ class Net {
case ClientIdAssign: case ClientIdAssign:
clientId = input.readByte(); // 8 bit client id, hopefully we don't exceed this clientId = input.readByte(); // 8 bit client id, hopefully we don't exceed this
Console.log('Client ID set to ${clientId}'); Console.log('Client ID set to ${clientId}');
NetCommands.setPlayerName(clientId, Settings.highscoreName); // Send our player name to the server
case Ping: case Ping:
var pingLeft = input.readByte(); var pingLeft = input.readByte();
@ -435,10 +406,21 @@ class Net {
addGhost(id); addGhost(id);
newP = true; newP = true;
} }
var nameLength = input.readByte();
var name = "";
for (j in 0...nameLength) {
name += String.fromCharCode(input.readByte());
}
if (clientIdMap.exists(id)) {
clientIdMap[id].setName(name);
}
} }
if (newP) { if (newP) {
AudioManager.playSound(ResourceLoader.getAudio("sounds/spawn_alternate.wav").resource); AudioManager.playSound(ResourceLoader.getAudio("sounds/spawn_alternate.wav").resource);
} }
if (MarbleGame.canvas.content is MultiplayerLevelSelectGui) {
cast(MarbleGame.canvas.content, MultiplayerLevelSelectGui).updateLobbyNames();
}
case _: case _:
Console.log("unknown command: " + packetType); Console.log("unknown command: " + packetType);

View file

@ -5,6 +5,7 @@ import net.ClientConnection.GameplayState;
import net.Net.NetPacketType; import net.Net.NetPacketType;
import gui.MultiplayerLevelSelectGui; import gui.MultiplayerLevelSelectGui;
import src.MarbleGame; import src.MarbleGame;
import gui.MultiplayerLoadingGui;
@:build(net.RPCMacro.build()) @:build(net.RPCMacro.build())
class NetCommands { class NetCommands {
@ -28,13 +29,34 @@ class NetCommands {
} }
} }
@:rpc(client) public static function toggleReadiness(clientId:Int) {
if (Net.isHost) {
if (clientId == 0)
Net.lobbyHostReady = !Net.lobbyHostReady;
else
Net.clientIdMap[clientId].toggleLobbyReady();
var allReady = true;
for (id => client in Net.clientIdMap) {
if (!client.lobbyReady) {
allReady = false;
break;
}
}
if (allReady && Net.lobbyHostReady) {
NetCommands.playLevel();
}
}
}
@:rpc(client) public static function clientIsReady(clientId:Int) { @:rpc(client) public static function clientIsReady(clientId:Int) {
if (Net.isHost) { if (Net.isHost) {
Net.clientIdMap[clientId].ready(); Net.clientIdMap[clientId].ready();
var allReady = true; var allReady = true;
for (id => client in Net.clientIdMap) { for (id => client in Net.clientIdMap) {
if (client.state != GameplayState.GAME) if (client.state != GameplayState.GAME) {
allReady = false; allReady = false;
break;
}
} }
if (allReady) { if (allReady) {
if (MarbleGame.instance.world != null) { if (MarbleGame.instance.world != null) {
@ -66,6 +88,9 @@ class NetCommands {
MarbleGame.instance.world.removePlayer(conn); MarbleGame.instance.world.removePlayer(conn);
} }
Net.clientIdMap.remove(clientId); Net.clientIdMap.remove(clientId);
if (MarbleGame.canvas.content is MultiplayerLevelSelectGui) {
cast(MarbleGame.canvas.content, MultiplayerLevelSelectGui).updateLobbyNames();
}
} }
@:rpc(server) public static function clientJoin(clientId:Int) {} @:rpc(server) public static function clientJoin(clientId:Int) {}
@ -81,6 +106,18 @@ class NetCommands {
if (MarbleGame.instance.world != null) { if (MarbleGame.instance.world != null) {
MarbleGame.instance.quitMission(); MarbleGame.instance.quitMission();
} }
var loadGui = new MultiplayerLoadingGui("Server closed");
MarbleGame.canvas.setContent(loadGui);
loadGui.setErrorStatus("Server closed");
}
}
@:rpc(client) public static function setPlayerName(clientId:Int, name:String) {
if (Net.isHost) {
Net.clientIdMap[clientId].setName(name);
if (MarbleGame.canvas.content is MultiplayerLevelSelectGui) {
cast(MarbleGame.canvas.content, MultiplayerLevelSelectGui).updateLobbyNames();
}
} }
} }
} }

View file

@ -37,6 +37,7 @@ enum abstract MarbleNetFlags(Int) from Int to Int {
var DoHelicopter = 1 << 1; var DoHelicopter = 1 << 1;
var DoMega = 1 << 2; var DoMega = 1 << 2;
var PickupPowerup = 1 << 3; var PickupPowerup = 1 << 3;
var GravityChange = 1 << 4;
} }
@:publicFields @:publicFields
@ -52,6 +53,7 @@ class MarbleUpdatePacket implements NetPacket {
var blastTick:Int; var blastTick:Int;
var megaTick:Int; var megaTick:Int;
var heliTick:Int; var heliTick:Int;
var gravityDirection:Vector;
var oob:Bool; var oob:Bool;
var powerUpId:Int; var powerUpId:Int;
var moveQueueSize:Int; var moveQueueSize:Int;
@ -99,6 +101,14 @@ class MarbleUpdatePacket implements NetPacket {
} else { } else {
b.writeFlag(false); b.writeFlag(false);
} }
if (netFlags & MarbleNetFlags.GravityChange > 0) {
b.writeFlag(true);
b.writeFloat(gravityDirection.x);
b.writeFloat(gravityDirection.y);
b.writeFloat(gravityDirection.z);
} else {
b.writeFlag(false);
}
} }
public inline function deserialize(b:InputBitStream) { public inline function deserialize(b:InputBitStream) {
@ -126,9 +136,12 @@ class MarbleUpdatePacket implements NetPacket {
oob = b.readFlag(); oob = b.readFlag();
if (b.readFlag()) { if (b.readFlag()) {
powerUpId = b.readInt(9); powerUpId = b.readInt(9);
trace('pickup: ${powerUpId}');
this.netFlags |= MarbleNetFlags.PickupPowerup; this.netFlags |= MarbleNetFlags.PickupPowerup;
} }
if (b.readFlag()) {
gravityDirection = new Vector(b.readFloat(), b.readFloat(), b.readFloat());
this.netFlags |= MarbleNetFlags.GravityChange;
}
} }
} }

View file

@ -42,6 +42,14 @@ class RPCMacro {
serializeFns.push(macro stream.writeFloat($i{argName})); serializeFns.push(macro stream.writeFloat($i{argName}));
} }
case TPath({
name: 'String'
}): {
deserializeFns.push(macro var $argName = stream.readString());
callExprs.push(macro $i{argName});
serializeFns.push(macro stream.writeString($i{argName}));
}
case _: {} case _: {}
} }
} }