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.powerUpId = this.heldPowerup != null ? this.heldPowerup.netIndex : 0x1FF;
marbleUpdate.netFlags = this.netFlags;
marbleUpdate.gravityDirection = this.currentUp;
this.netFlags = 0;
marbleUpdate.serialize(b);
return b.getBytes();
@ -1756,6 +1757,8 @@ class Marble extends GameObject {
this.blastUseTick = p.blastTick;
this.helicopterUseTick = p.heliTick;
this.megaMarbleUseTick = p.megaTick;
this.currentUp = p.gravityDirection;
this.level.setUp(cast this, this.currentUp, this.level.timeState, true);
this.outOfBounds = p.oob;
this.camera.oob = p.oob;
if (p.powerUpId == 0x1FF) {
@ -2306,7 +2309,7 @@ class Marble extends GameObject {
this.blastTicks = 0;
this.helicopterUseTick = 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.contactEntities = [];
this._firstTick = true;

View file

@ -342,12 +342,12 @@ class MarbleWorld extends Scheduler {
if (this.isMultiplayer) {
// Add us
if (Net.isHost) {
this.playGui.addPlayer(0, 'Player 0', true);
this.playGui.addPlayer(0, Settings.highscoreName.substr(0, 15), true);
} 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) {
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) {
marble.megaMarbleUseTick = 0;
marble.helicopterUseTick = 0;
marble.collider.radius = marble._radius = 0.3;
} else {
@:privateAccess marble.helicopterEnableTime = -1e8;
@:privateAccess marble.megaMarbleEnableTime = -1e8;
@ -1204,8 +1205,8 @@ class MarbleWorld extends Scheduler {
// Debug.drawSphere(@:privateAccess marbleToUpdate.newPos, marbleToUpdate._radius);
var distFromUs = @:privateAccess marbleToUpdate.newPos.distance(this.marble.newPos);
if (distFromUs < 5) // {
m.calculationTicks = ourQueuedMoves.length;
// if (distFromUs < 5) // {
m.calculationTicks = ourQueuedMoves.length;
// } else {
// 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) {
if (vec == marble.currentUp)
return;
marble.currentUp = vec;
if (isMultiplayer && Net.isHost) {
@:privateAccess marble.netFlags |= MarbleNetFlags.GravityChange;
}
if (marble == this.marble) {
var currentQuat = this.getOrientationQuat(timeState.currentAttemptTime);
var oldUp = new Vector(0, 0, 1);

View file

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

View file

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

View file

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

View file

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

View file

@ -120,8 +120,10 @@ class MPServerListGui extends GuiImage {
nextButton.accelerators = [hxd.Key.ENTER];
nextButton.gamepadAccelerator = ["X"];
nextButton.pressedAction = (e) -> {
MarbleGame.canvas.setContent(new MultiplayerLoadingGui("Connecting"));
Net.joinServer(ourServerList[curSelection].name, () -> {
MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false));
Net.remoteServerInfo = ourServerList[curSelection];
});
};
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();
bottomBar.position = new Vector(0, 590);
bottomBar.extent = new Vector(640, 200);

View file

@ -1,5 +1,6 @@
package gui;
import net.Net;
import net.NetCommands;
import modes.GameMode.ScoreType;
import src.Util;
@ -19,6 +20,8 @@ class MultiplayerLevelSelectGui extends GuiImage {
static var setLevelFn:Int->Void;
static var playSelectedLevel:Void->Void;
var playerList:GuiMLTextListCtrl;
var updatePlayerCountFn:(Int, Int, Int, Int) -> Void;
var innerCtrl:GuiControl;
public function new(isHost:Bool) {
@ -29,8 +32,15 @@ class MultiplayerLevelSelectGui extends GuiImage {
var arial14b = new BitmapFont(arial14fontdata.entry);
@:privateAccess arial14b.loader = ResourceLoader.loader;
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) {
return arial14;
switch (text) {
case "arial14":
return arial14;
case "arial12":
return arial12;
}
return null;
}
MarbleGame.instance.toRecord = false;
@ -127,11 +137,34 @@ class MultiplayerLevelSelectGui extends GuiImage {
rootTitle.position = new Vector(100, 30);
rootTitle.extent = new Vector(1120, 80);
rootTitle.text.textColor = 0xFFFFFF;
rootTitle.text.text = "SELECT LEVEL";
rootTitle.text.text = "LOBBY";
rootTitle.text.alpha = 0.5;
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.extent = new Vector(640, 200);
bottomBar.horizSizing = Width;
@ -144,7 +177,10 @@ class MultiplayerLevelSelectGui extends GuiImage {
backButton.horizSizing = Right;
backButton.gamepadAccelerator = ["B"];
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);
// var lbButton = new GuiXboxButton("Leaderboard", 220);
@ -153,18 +189,17 @@ class MultiplayerLevelSelectGui extends GuiImage {
// lbButton.horizSizing = Right;
// bottomBar.addChild(lbButton);
if (isHost) {
var nextButton = new GuiXboxButton("Play", 160);
nextButton.position = new Vector(960, 0);
nextButton.vertSizing = Bottom;
nextButton.horizSizing = Right;
nextButton.gamepadAccelerator = ["A"];
nextButton.accelerators = [hxd.Key.ENTER];
nextButton.pressedAction = (e) -> {
NetCommands.playLevel();
};
bottomBar.addChild(nextButton);
}
var nextButton = new GuiXboxButton("Ready", 160);
nextButton.position = new Vector(960, 0);
nextButton.vertSizing = Bottom;
nextButton.horizSizing = Right;
nextButton.gamepadAccelerator = ["A"];
nextButton.accelerators = [hxd.Key.ENTER];
nextButton.pressedAction = (e) -> {
NetCommands.toggleReadiness(Net.isClient ? Net.clientId : 0);
};
bottomBar.addChild(nextButton);
playSelectedLevel = () -> {
MarbleGame.instance.playMission(curMission, true);
}
@ -176,42 +211,19 @@ class MultiplayerLevelSelectGui extends GuiImage {
levelWnd.horizSizing = Right;
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 c1 = 0x8DFF8D;
var c2 = 0x88BCEE;
var c3 = 0xFF7575;
var levelInfoLeft = new GuiMLText(arial14, mlFontLoader);
levelInfoLeft.position = new Vector(69, 54);
levelInfoLeft.extent = new Vector(180, 100);
levelInfoLeft.text.text = '<p align="right"><font color="#EBEBEB">My Best Time:</font><br/><font color="#EBEBEB">Par Time:</font></p>';
levelInfoLeft.text.lineSpacing = 6;
levelInfoLeft.position = new Vector(33, 40);
levelInfoLeft.extent = new Vector(480, 100);
levelInfoLeft.text.text = '';
levelInfoLeft.text.lineSpacing = 0;
levelInfoLeft.text.filter = new h2d.filter.DropShadow(2, 0.785, 0x000000, 1, 0, 0.4, 1, true);
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 levelSelectOpts = new GuiXboxOptionsList(6, "Level", levelNames);
@ -230,10 +242,6 @@ class MultiplayerLevelSelectGui extends GuiImage {
var misFile = Path.withoutExtension(Path.withoutDirectory(curMission.path));
var mis = difficultyMissions[idx];
var requestToken = currentToken;
if (Settings.easterEggs.exists(mis.path))
eggIcon.bmp.visible = true;
else
eggIcon.bmp.visible = false;
MarbleGame.instance.setPreviewMission(misFile, () -> {
lock = false;
if (requestToken != currentToken)
@ -243,34 +251,31 @@ class MultiplayerLevelSelectGui extends GuiImage {
loadText.text.visible = false;
loadTextBg.text.visible = false;
});
var scoreType = mis.missionInfo.gamemode != null
&& mis.missionInfo.gamemode.toLowerCase() == 'scrum' ? ScoreType.Score : ScoreType.Time;
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>';
var hostName = Settings.highscoreName;
if (!Net.isHost) {
hostName = Net.clientIdMap[0].getName();
}
if (scoreType == Score) {
levelInfoLeft.text.text = '<p align="right"><font color="#EBEBEB">My Best Score:</font></p>';
levelInfoMid.text.text = '<p align="left"><font color="${scoreColor}">${scoreDisp}</font></p>';
if (Net.isHost) {
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;
}
setLevelFn = setLevel;
levelSelectOpts.position = new Vector(380, 435);
levelSelectOpts.position = new Vector(380, 430);
levelSelectOpts.extent = new Vector(815, 94);
levelSelectOpts.vertSizing = Bottom;
levelSelectOpts.horizSizing = Right;
@ -295,4 +300,16 @@ class MultiplayerLevelSelectGui extends GuiImage {
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 {
var loadText: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();
super(res);
this.position = new Vector();
@ -31,7 +34,7 @@ class MultiplayerLoadingGui extends GuiImage {
@:privateAccess arial14b.loader = ResourceLoader.loader;
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.extent = new Vector(63, 63);
loadAnim.horizSizing = Center;
@ -44,7 +47,7 @@ class MultiplayerLoadingGui extends GuiImage {
loadTextBg.horizSizing = Center;
loadTextBg.vertSizing = Bottom;
loadTextBg.justify = Center;
loadTextBg.text.text = "Loading";
loadTextBg.text.text = initialStatus;
loadTextBg.text.textColor = 0;
this.addChild(loadTextBg);
@ -54,12 +57,69 @@ class MultiplayerLoadingGui extends GuiImage {
loadText.horizSizing = Center;
loadText.vertSizing = Bottom;
loadText.justify = Center;
loadText.text.text = "Loading";
loadText.text.text = initialStatus;
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) {
loadText.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() {
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 {
@ -135,4 +144,11 @@ class OutputBitStream {
public function writeFloat(value:Float) {
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.state = GameplayState.LOBBY;
this.rtt = 0;
this.name = "Unknown";
}
override function sendBytes(b:Bytes) {
@ -38,6 +39,7 @@ class DummyConnection extends GameConnection {
public function new(id:Int) {
super(id);
this.state = GameplayState.GAME;
this.lobbyReady = true;
}
}
@ -46,16 +48,23 @@ abstract class GameConnection {
var id:Int;
var state:GameplayState;
var moveManager:MoveManager;
var name:String;
var lobbyReady:Bool;
public function new(id:Int) {
this.id = id;
this.moveManager = new MoveManager(this);
this.lobbyReady = false;
}
public function ready() {
state = GameplayState.GAME;
}
public function toggleLobbyReady() {
lobbyReady = !lobbyReady;
}
public function queueMove(m:NetMove) {
moveManager.queueMove(m);
}
@ -81,4 +90,12 @@ abstract class GameConnection {
}
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;
import h3d.Vector;
import net.NetPacket.MarbleNetFlags;
import net.NetPacket.MarbleUpdatePacket;
import net.Net;
@ -11,6 +12,7 @@ class OtherMarbleUpdate {
var lastHeliTick:Int;
var lastMegaTick:Int;
var lastPowerUpId:Int;
var lastGravityUp:Vector;
public function new() {}
}
@ -48,6 +50,10 @@ class MarbleUpdateQueue {
update.powerUpId = otherUpdate.lastPowerUpId;
else
otherUpdate.lastPowerUpId = update.powerUpId;
if (update.netFlags & MarbleNetFlags.GravityChange == 0)
update.gravityDirection = otherUpdate.lastGravityUp;
else
otherUpdate.lastGravityUp = update.gravityDirection;
ourList.push(update);
} else {
var otherUpdate = new OtherMarbleUpdate();
@ -61,6 +67,8 @@ class MarbleUpdateQueue {
otherUpdate.lastMegaTick = update.megaTick;
if (update.netFlags & MarbleNetFlags.PickupPowerup != 0)
otherUpdate.lastPowerUpId = update.powerUpId;
if (update.netFlags & MarbleNetFlags.GravityChange != 0)
otherUpdate.lastGravityUp = update.gravityDirection;
otherMarbleUpdates[cc] = otherUpdate;
}
} else {
@ -75,6 +83,8 @@ class MarbleUpdateQueue {
update.megaTick = myMarbleUpdate.megaTick;
if (update.netFlags & MarbleNetFlags.PickupPowerup == 0)
update.powerUpId = myMarbleUpdate.powerUpId;
if (update.netFlags & MarbleNetFlags.GravityChange == 0)
update.gravityDirection = myMarbleUpdate.gravityDirection;
}
myMarbleUpdate = update;
ourMoveApplied = false;

View file

@ -7,6 +7,7 @@ import net.Net.ServerInfo;
import hx.ws.WebSocket;
import src.Console;
import hx.ws.Types.MessageType;
import gui.MultiplayerLoadingGui;
typedef RemoteServerInfo = {
name:String,
@ -93,7 +94,7 @@ class MasterServerClient {
if (conts.type == "connect") {
if (!Net.isHost) {
ws.send(Json.stringify({
type: "connectResponse",
type: "connectFailed",
success: false,
reason: "The server has shut down"
}));
@ -101,7 +102,7 @@ class MasterServerClient {
}
if (Net.serverInfo.players >= Net.serverInfo.maxPlayers) {
ws.send(Json.stringify({
type: "connectResponse",
type: "connectFailed",
success: false,
reason: "The server is full"
}));
@ -122,7 +123,10 @@ class MasterServerClient {
@:privateAccess Net.client.setRemoteDescription(sdpObj.sdp, sdpObj.type);
}
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;
import gui.MultiplayerLevelSelectGui;
import gui.Canvas;
import net.MasterServerClient.RemoteServerInfo;
import gui.MultiplayerLoadingGui;
import src.ResourceLoader;
import src.AudioManager;
import net.NetPacket.GemPickupPacket;
@ -18,6 +22,7 @@ import src.Console;
import net.NetCommands;
import src.MarbleGame;
import hx.ws.Types.MessageType;
import src.Settings;
enum abstract NetPacketType(Int) from Int to Int {
var NullPacket;
@ -60,13 +65,11 @@ class Net {
static var client:RTCPeerConnection;
static var clientDatachannel:RTCDataChannel;
static var masterWs:WebSocket;
public static var isMP:Bool;
public static var isHost:Bool;
public static var isClient:Bool;
public static var startMP:Bool;
public static var lobbyHostReady:Bool;
public static var clientId:Int;
public static var networkRNG:Float;
@ -74,6 +77,7 @@ class Net {
public static var clientIdMap:Map<Int, GameConnection> = [];
public static var clientConnection:ClientConnection;
public static var serverInfo:ServerInfo;
public static var remoteServerInfo:RemoteServerInfo;
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());
@ -84,30 +88,6 @@ class Net {
isMP = true;
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) {
@ -167,6 +147,10 @@ class Net {
clientDatachannel = client.createDatachannel("mp");
clientDatachannel.onOpen = (n) -> {
var loadGui:MultiplayerLoadingGui = cast MarbleGame.canvas.content;
if (loadGui != null) {
loadGui.setLoadingStatus("Handshaking");
}
Console.log("Successfully connected!");
clients.set(client, new ClientConnection(0, client, clientDatachannel)); // host is always 0
clientIdMap[0] = clients[client];
@ -182,6 +166,11 @@ class Net {
if (MarbleGame.instance.world != null) {
MarbleGame.instance.quitMission();
}
if (!(MarbleGame.canvas.content is MultiplayerLoadingGui)) {
var loadGui = new MultiplayerLoadingGui("Server closed");
MarbleGame.canvas.setContent(loadGui);
loadGui.setErrorStatus("Server closed");
}
}
clientDatachannel.onError = (msg) -> {
Console.log('Errored out due to ${msg}');
@ -189,63 +178,15 @@ class Net {
if (MarbleGame.instance.world != null) {
MarbleGame.instance.quitMission();
}
var loadGui = new MultiplayerLoadingGui("Connection error");
MarbleGame.canvas.setContent(loadGui);
loadGui.setErrorStatus("Connection error");
}
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;
// }
}
public static function disconnect() {
@ -259,6 +200,9 @@ class Net {
Net.clientId = 0;
Net.clientIdMap.clear();
Net.clientConnection = null;
Net.serverInfo = null;
Net.remoteServerInfo = null;
Net.lobbyHostReady = false;
}
if (Net.isHost) {
NetCommands.serverClosed();
@ -271,6 +215,9 @@ class Net {
Net.clients.clear();
Net.clientIdMap.clear();
MasterServerClient.disconnectFromMasterServer();
Net.serverInfo = null;
Net.remoteServerInfo = null;
Net.lobbyHostReady = false;
}
}
@ -308,6 +255,10 @@ class Net {
serverInfo.players++;
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() {
@ -327,6 +278,12 @@ class Net {
serverInfo.players--;
MasterServerClient.instance.sendServerInfo(serverInfo); // notify the server of the player leave
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() {
@ -335,9 +292,22 @@ class Net {
var cnt = 0;
for (c in clientIdMap)
cnt++;
b.writeByte(cnt);
for (c => v in clientIdMap)
b.writeByte(cnt + 1); // all + host
for (c => v in clientIdMap) {
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();
}
@ -352,6 +322,7 @@ class Net {
case ClientIdAssign:
clientId = input.readByte(); // 8 bit client id, hopefully we don't exceed this
Console.log('Client ID set to ${clientId}');
NetCommands.setPlayerName(clientId, Settings.highscoreName); // Send our player name to the server
case Ping:
var pingLeft = input.readByte();
@ -435,10 +406,21 @@ class Net {
addGhost(id);
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) {
AudioManager.playSound(ResourceLoader.getAudio("sounds/spawn_alternate.wav").resource);
}
if (MarbleGame.canvas.content is MultiplayerLevelSelectGui) {
cast(MarbleGame.canvas.content, MultiplayerLevelSelectGui).updateLobbyNames();
}
case _:
Console.log("unknown command: " + packetType);

View file

@ -5,6 +5,7 @@ import net.ClientConnection.GameplayState;
import net.Net.NetPacketType;
import gui.MultiplayerLevelSelectGui;
import src.MarbleGame;
import gui.MultiplayerLoadingGui;
@:build(net.RPCMacro.build())
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) {
if (Net.isHost) {
Net.clientIdMap[clientId].ready();
var allReady = true;
for (id => client in Net.clientIdMap) {
if (client.state != GameplayState.GAME)
if (client.state != GameplayState.GAME) {
allReady = false;
break;
}
}
if (allReady) {
if (MarbleGame.instance.world != null) {
@ -66,6 +88,9 @@ class NetCommands {
MarbleGame.instance.world.removePlayer(conn);
}
Net.clientIdMap.remove(clientId);
if (MarbleGame.canvas.content is MultiplayerLevelSelectGui) {
cast(MarbleGame.canvas.content, MultiplayerLevelSelectGui).updateLobbyNames();
}
}
@:rpc(server) public static function clientJoin(clientId:Int) {}
@ -81,6 +106,18 @@ class NetCommands {
if (MarbleGame.instance.world != null) {
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 DoMega = 1 << 2;
var PickupPowerup = 1 << 3;
var GravityChange = 1 << 4;
}
@:publicFields
@ -52,6 +53,7 @@ class MarbleUpdatePacket implements NetPacket {
var blastTick:Int;
var megaTick:Int;
var heliTick:Int;
var gravityDirection:Vector;
var oob:Bool;
var powerUpId:Int;
var moveQueueSize:Int;
@ -99,6 +101,14 @@ class MarbleUpdatePacket implements NetPacket {
} else {
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) {
@ -126,9 +136,12 @@ class MarbleUpdatePacket implements NetPacket {
oob = b.readFlag();
if (b.readFlag()) {
powerUpId = b.readInt(9);
trace('pickup: ${powerUpId}');
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}));
}
case TPath({
name: 'String'
}): {
deserializeFns.push(macro var $argName = stream.readString());
callExprs.push(macro $i{argName});
serializeFns.push(macro stream.writeString($i{argName}));
}
case _: {}
}
}