mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
add support for customs in MP
This commit is contained in:
parent
83f72c4dfe
commit
b3ba40ed94
12 changed files with 224 additions and 34 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 450 B |
|
|
@ -26,6 +26,8 @@ class Http {
|
|||
threadPool = new sys.thread.FixedThreadPool(2);
|
||||
threadPool.run(() -> threadLoop());
|
||||
threadPool.run(() -> threadLoop());
|
||||
threadPool.run(() -> threadLoop());
|
||||
threadPool.run(() -> threadLoop());
|
||||
#end
|
||||
}
|
||||
|
||||
|
|
|
|||
68
src/MPCustoms.hx
Normal file
68
src/MPCustoms.hx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import src.MissionList;
|
||||
import gui.MessageBoxOkDlg;
|
||||
import haxe.zip.Reader;
|
||||
import haxe.io.BytesInput;
|
||||
import haxe.Json;
|
||||
import src.Http;
|
||||
import src.Console;
|
||||
import src.MarbleGame;
|
||||
import src.ResourceLoader;
|
||||
|
||||
typedef MPCustomEntry = {
|
||||
artist:String,
|
||||
description:String,
|
||||
path:String,
|
||||
title:String
|
||||
};
|
||||
|
||||
class MPCustoms {
|
||||
public static var missionList:Array<MPCustomEntry> = [];
|
||||
|
||||
static var _requestSent = false;
|
||||
|
||||
public static function loadMissionList() {
|
||||
if (missionList.length == 0 && !_requestSent) {
|
||||
_requestSent = true;
|
||||
Http.get("https://marbleblastultra.randomityguy.me/data/ultraCustom.json", (b) -> {
|
||||
var misList = Json.parse(b.toString());
|
||||
missionList = misList;
|
||||
Console.log('Loaded ${misList.length} custom missions.');
|
||||
_requestSent = false;
|
||||
}, (e) -> {
|
||||
Console.log('Error getting custom list from marbleland.');
|
||||
_requestSent = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static function download(mission:MPCustomEntry, onFinish:() -> Void, onFail:() -> Void) {
|
||||
var lastSlashIdx = mission.path.lastIndexOf('/');
|
||||
var dlPath = "https://marbleblastultra.randomityguy.me/" + mission.path.substr(0, lastSlashIdx) + ".zip";
|
||||
Http.get(dlPath, (zipData) -> {
|
||||
var reader = new Reader(new BytesInput(zipData));
|
||||
var entries:Array<haxe.zip.Entry> = null;
|
||||
try {
|
||||
entries = [for (x in reader.read()) x];
|
||||
} catch (e) {}
|
||||
ResourceLoader.loadZip(entries, 'missions/mpcustom/');
|
||||
if (entries != null) {
|
||||
onFinish();
|
||||
} else {
|
||||
MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("Failed to download mission"));
|
||||
onFail();
|
||||
}
|
||||
}, (e) -> {
|
||||
MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("Failed to download mission"));
|
||||
onFail();
|
||||
});
|
||||
}
|
||||
|
||||
public static function play(mission:MPCustomEntry, onFinish:() -> Void, onFail:() -> Void) {
|
||||
download(mission, () -> {
|
||||
var f = ResourceLoader.getFileEntry(mission.path);
|
||||
var mis = MissionList.parseMisHeader(f.entry.getBytes().toString(), mission.path);
|
||||
MarbleGame.instance.playMission(mis, true);
|
||||
onFinish();
|
||||
}, onFail);
|
||||
}
|
||||
}
|
||||
|
|
@ -94,6 +94,7 @@ class Main extends hxd.App {
|
|||
haxe.MainLoop.add(() -> Http.loop());
|
||||
Settings.init();
|
||||
Gamepad.init();
|
||||
MPCustoms.loadMissionList();
|
||||
ResourceLoader.init(s2d, () -> {
|
||||
AudioManager.init();
|
||||
AudioManager.playShell();
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ class MissionList {
|
|||
ultraMissions.set("intermediate", parseDifficulty("ultra", "missions", "intermediate", 1));
|
||||
ultraMissions.set("advanced", parseDifficulty("ultra", "missions", "advanced", 2));
|
||||
ultraMissions.set("multiplayer", parseDifficulty("ultra", "missions", "multiplayer", 3));
|
||||
// var mpCustoms = parseDifficulty("ultra", "missions", "mpcustom", 3);
|
||||
// ultraMissions["multiplayer"] = ultraMissions["multiplayer"].concat(mpCustoms);
|
||||
|
||||
@:privateAccess ultraMissions["beginner"][ultraMissions["beginner"].length - 1].next = ultraMissions["intermediate"][0];
|
||||
@:privateAccess ultraMissions["intermediate"][ultraMissions["intermediate"].length - 1].next = ultraMissions["advanced"][0];
|
||||
|
|
@ -91,4 +93,16 @@ class MissionList {
|
|||
|
||||
_build = true;
|
||||
}
|
||||
|
||||
public static function parseMisHeader(conts:String, path:String) {
|
||||
var misParser = new MisParser(conts);
|
||||
var mInfo = misParser.parseMissionInfo();
|
||||
var mission = Mission.fromMissionInfo(path, mInfo);
|
||||
mission.game = "ultra";
|
||||
// do egg thing
|
||||
if (StringTools.contains(conts.toLowerCase(), 'datablock = "easteregg"')) { // Ew
|
||||
mission.hasEgg = true;
|
||||
}
|
||||
return mission;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package src;
|
||||
|
||||
import haxe.zip.Uncompress;
|
||||
import hxd.res.Any;
|
||||
import hxd.fs.BytesFileSystem.BytesFileEntry;
|
||||
#if (js || android)
|
||||
|
|
@ -590,20 +591,21 @@ class ResourceLoader {
|
|||
return names;
|
||||
}
|
||||
|
||||
public static function loadZip(entries:Array<haxe.zip.Entry>, game:String) {
|
||||
public static function loadZip(entries:Array<haxe.zip.Entry>, prefix:String) {
|
||||
zipFilesystem.clear(); // We are only allowed to load one zip
|
||||
for (entry in entries) {
|
||||
var fname = entry.fileName.toLowerCase();
|
||||
var fname = prefix + entry.fileName.toLowerCase();
|
||||
#if sys
|
||||
fname = "data/" + fname;
|
||||
#end
|
||||
if (game == 'gold')
|
||||
fname = StringTools.replace(fname, 'interiors/', 'interiors_mbg/');
|
||||
fname = StringTools.replace(fname, "lbinteriors", "interiors"); // Normalize
|
||||
if (exists(fname))
|
||||
continue;
|
||||
Console.log("Loaded zip entry: " + fname);
|
||||
var zfe = new BytesFileEntry(fname, entry.data);
|
||||
|
||||
var zdata = entry.data;
|
||||
|
||||
var zfe = new BytesFileEntry(fname, zdata);
|
||||
zipFilesystem.set(fname, zfe);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ class MainMenuGui extends GuiImage {
|
|||
cast(this.parent, Canvas).setContent(new DifficultySelectGui());
|
||||
});
|
||||
btnList.addButton(0, "Multiplayer Game", (sender) -> {
|
||||
if (MPCustoms.missionList.length == 0) {
|
||||
cast(this.parent, Canvas).pushDialog(new MessageBoxOkDlg("Custom levels not loaded yet, please wait."));
|
||||
MPCustoms.loadMissionList();
|
||||
} else
|
||||
cast(this.parent, Canvas).setContent(new MultiplayerGui());
|
||||
});
|
||||
// btnList.addButton(2, "Leaderboards", (e) -> {}, 20);
|
||||
|
|
|
|||
|
|
@ -20,12 +20,19 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
|
||||
static var setLevelFn:Int->Void;
|
||||
static var playSelectedLevel:Int->Void;
|
||||
static var setLevelStr:String->Void;
|
||||
|
||||
var playerList:GuiMLTextListCtrl;
|
||||
var customList:GuiTextListCtrl;
|
||||
var updatePlayerCountFn:(Int, Int, Int, Int) -> Void;
|
||||
var innerCtrl:GuiControl;
|
||||
var inviteVisibility:Bool = true;
|
||||
|
||||
static var custSelected:Bool = false;
|
||||
static var custPath:String;
|
||||
|
||||
var showingCustoms = false;
|
||||
|
||||
public function new(isHost:Bool) {
|
||||
var res = ResourceLoader.getImage("data/ui/game/CloudBG.jpg").resource.toTile();
|
||||
super(res);
|
||||
|
|
@ -150,6 +157,8 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
playerWnd.extent = new Vector(640, 480);
|
||||
innerCtrl.addChild(playerWnd);
|
||||
|
||||
custSelected = false;
|
||||
|
||||
var playerListArr = [];
|
||||
if (Net.isHost) {
|
||||
playerListArr.push({
|
||||
|
|
@ -212,6 +221,31 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
playerList.onSelectedFunc = (sel) -> {}
|
||||
playerWnd.addChild(playerList);
|
||||
|
||||
var customListScroll = new GuiConsoleScrollCtrl(ResourceLoader.getResource("data/ui/common/osxscroll.png", ResourceLoader.getImage, this.imageResources)
|
||||
.toTile());
|
||||
customListScroll.position = new Vector(25, 22);
|
||||
customListScroll.extent = new Vector(590, 330);
|
||||
|
||||
customList = new GuiTextListCtrl(arial14, MPCustoms.missionList.map(mission -> {
|
||||
return mission.title;
|
||||
}));
|
||||
var custSelectedIdx = 0;
|
||||
customList.selectedColor = 0xF29515;
|
||||
customList.selectedFillColor = 0xEBEBEB;
|
||||
customList.position = new Vector(0, 0);
|
||||
customList.extent = new Vector(550, 2880);
|
||||
customList.scrollable = true;
|
||||
customList.onSelectedFunc = (idx) -> {
|
||||
NetCommands.setLobbyCustLevelName(MPCustoms.missionList[idx].title);
|
||||
custSelected = true;
|
||||
custSelectedIdx = idx;
|
||||
custPath = MPCustoms.missionList[idx].path;
|
||||
updateLobbyNames();
|
||||
}
|
||||
customListScroll.addChild(customList);
|
||||
customListScroll.setScrollMax(customList.calculateFullHeight());
|
||||
// playerWnd.addChild(customList);
|
||||
|
||||
var bottomBar = new GuiControl();
|
||||
bottomBar.position = new Vector(0, 590);
|
||||
bottomBar.extent = new Vector(640, 200);
|
||||
|
|
@ -236,6 +270,25 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
bottomBar.addChild(backButton);
|
||||
|
||||
if (Net.isHost) {
|
||||
var customsButton = new GuiXboxButton("Customs", 200);
|
||||
customsButton.position = new Vector(560, 0);
|
||||
customsButton.vertSizing = Bottom;
|
||||
customsButton.horizSizing = Right;
|
||||
customsButton.gamepadAccelerator = ["X"];
|
||||
customsButton.pressedAction = (e) -> {
|
||||
showingCustoms = !showingCustoms;
|
||||
if (showingCustoms) {
|
||||
playerWnd.addChild(customListScroll);
|
||||
playerWnd.removeChild(playerList);
|
||||
} else {
|
||||
playerWnd.removeChild(customListScroll);
|
||||
playerWnd.addChild(playerList);
|
||||
updateLobbyNames();
|
||||
}
|
||||
MarbleGame.canvas.render(MarbleGame.canvas.scene2d);
|
||||
}
|
||||
bottomBar.addChild(customsButton);
|
||||
|
||||
var inviteButton = new GuiXboxButton("Invite Visibility", 220);
|
||||
inviteButton.position = new Vector(750, 0);
|
||||
inviteButton.vertSizing = Bottom;
|
||||
|
|
@ -259,9 +312,13 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
bottomBar.addChild(nextButton);
|
||||
|
||||
playSelectedLevel = (index:Int) -> {
|
||||
if (custSelected) {
|
||||
NetCommands.playCustomLevel(MPCustoms.missionList[custSelectedIdx].path);
|
||||
} else {
|
||||
curMission = difficultyMissions[index];
|
||||
MarbleGame.instance.playMission(curMission, true);
|
||||
}
|
||||
}
|
||||
|
||||
var levelWnd = new GuiImage(ResourceLoader.getResource("data/ui/xbox/levelPreviewWindow.png", ResourceLoader.getImage, this.imageResources).toTile());
|
||||
levelWnd.position = new Vector(555, 469);
|
||||
|
|
@ -289,6 +346,7 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
function setLevel(idx:Int) {
|
||||
// if (lock)
|
||||
// return false;
|
||||
custSelected = false;
|
||||
levelSelectOpts.currentOption = idx;
|
||||
this.bmp.visible = true;
|
||||
loadAnim.anim.visible = true;
|
||||
|
|
@ -319,12 +377,12 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
updatePlayerCountFn = (pub:Int, priv:Int, publicTotal:Int, privateTotal:Int) -> {
|
||||
if (inviteVisibility)
|
||||
levelInfoLeft.text.text = '<p><font face="arial14">Host: ${hostName}</font></p>'
|
||||
+ '<p><font face="arial14">Level: ${mis.title}</font></p>'
|
||||
+ '<p><font face="arial14">Level: ${levelSelectOpts.optionText.text.text}</font></p>'
|
||||
+
|
||||
'<p><font face="arial12">Public Slots: ${pub}/${publicTotal}, Private Slots: ${priv}/${privateTotal}, Invite Code: ${Net.serverInfo.inviteCode}</font></p>';
|
||||
else
|
||||
levelInfoLeft.text.text = '<p><font face="arial14">Host: ${hostName}</font></p>'
|
||||
+ '<p><font face="arial14">Level: ${mis.title}</font></p>'
|
||||
+ '<p><font face="arial14">Level: ${levelSelectOpts.optionText.text.text}</font></p>'
|
||||
+ '<p><font face="arial12">Public Slots: ${pub}/${publicTotal}, Private Slots: ${priv}/${privateTotal}</font></p>';
|
||||
}
|
||||
var pubCount = 1; // 1 for host
|
||||
|
|
@ -341,7 +399,8 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
}
|
||||
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>';
|
||||
levelInfoLeft.text.text = '<p><font face="arial14">Host: ${hostName}</font></p>'
|
||||
+ '<p><font face="arial14">Level: ${levelSelectOpts.optionText.text.text}</font></p>';
|
||||
}
|
||||
updatePlayerCountFn(0, 0, 0, 0);
|
||||
}
|
||||
|
|
@ -363,6 +422,11 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
levelSelectOpts.setCurrentOption(idx);
|
||||
};
|
||||
|
||||
setLevelStr = (str) -> {
|
||||
levelSelectOpts.optionText.text.text = str;
|
||||
updateLobbyNames();
|
||||
}
|
||||
|
||||
levelSelectOpts.setCurrentOption(currentSelectionStatic);
|
||||
setLevel(currentSelectionStatic);
|
||||
innerCtrl.addChild(levelSelectOpts);
|
||||
|
|
@ -416,6 +480,7 @@ class MultiplayerLevelSelectGui extends GuiImage {
|
|||
}
|
||||
}
|
||||
|
||||
if (!showingCustoms)
|
||||
playerList.setTexts(playerListArr.map(player -> {
|
||||
return '<img src="${player.state ? "ready" : "notready"}"></img><img src="${platformToString(player.platform)}"></img>${player.name}';
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class MultiplayerLoadingGui extends GuiImage {
|
|||
var innerCtrl:GuiControl;
|
||||
var backButton:GuiXboxButton;
|
||||
|
||||
public function new(initialStatus:String) {
|
||||
public function new(initialStatus:String, showCancel = true) {
|
||||
var res = ResourceLoader.getImage("data/ui/game/CloudBG.jpg").resource.toTile();
|
||||
super(res);
|
||||
this.position = new Vector();
|
||||
|
|
@ -89,6 +89,7 @@ class MultiplayerLoadingGui extends GuiImage {
|
|||
bottomBar.vertSizing = Bottom;
|
||||
innerCtrl.addChild(bottomBar);
|
||||
|
||||
if (showCancel) {
|
||||
backButton = new GuiXboxButton("Cancel", 160);
|
||||
backButton.position = new Vector(960, 0);
|
||||
backButton.vertSizing = Bottom;
|
||||
|
|
@ -101,6 +102,7 @@ class MultiplayerLoadingGui extends GuiImage {
|
|||
};
|
||||
bottomBar.addChild(backButton);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLoadingStatus(str:String) {
|
||||
loadText.text.text = str;
|
||||
|
|
|
|||
|
|
@ -564,6 +564,9 @@ class Net {
|
|||
NetCommands.setLobbyLevelIndexClient(conn, MultiplayerLevelSelectGui.currentSelectionStatic);
|
||||
|
||||
if (serverInfo.state == "PLAYING") { // We initiated the game, directly add in the marble
|
||||
if (MultiplayerLevelSelectGui.custSelected) {
|
||||
NetCommands.playCustomLevelMidJoinClient(conn, MultiplayerLevelSelectGui.custPath);
|
||||
} else
|
||||
NetCommands.playLevelMidJoinClient(conn, MultiplayerLevelSelectGui.currentSelectionStatic);
|
||||
MarbleGame.instance.world.addJoiningClient(conn, () -> {});
|
||||
var playerInfoBytes = sendPlayerInfosBytes();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package net;
|
||||
|
||||
import gui.MultiplayerGui;
|
||||
import net.ClientConnection.NetPlatform;
|
||||
import gui.EndGameGui;
|
||||
import modes.HuntMode;
|
||||
|
|
@ -21,6 +22,12 @@ class NetCommands {
|
|||
}
|
||||
}
|
||||
|
||||
@:rpc(server) public static function setLobbyCustLevelName(str:String) {
|
||||
if (MultiplayerLevelSelectGui.setLevelFn != null) {
|
||||
MultiplayerLevelSelectGui.setLevelStr(str);
|
||||
}
|
||||
}
|
||||
|
||||
@:rpc(server) public static function playLevel(levelIndex:Int) {
|
||||
MultiplayerLevelSelectGui.playSelectedLevel(levelIndex);
|
||||
if (Net.isHost) {
|
||||
|
|
@ -29,6 +36,19 @@ class NetCommands {
|
|||
}
|
||||
}
|
||||
|
||||
@:rpc(server) public static function playCustomLevel(levelPath:String) {
|
||||
var levelEntry = MPCustoms.missionList.filter(x -> x.path == levelPath)[0];
|
||||
MarbleGame.canvas.setContent(new MultiplayerLoadingGui("Downloading", false));
|
||||
MPCustoms.play(levelEntry, () -> {}, () -> {
|
||||
MarbleGame.canvas.setContent(new MultiplayerGui());
|
||||
Net.disconnect(); // disconnect from the server
|
||||
});
|
||||
if (Net.isHost) {
|
||||
Net.serverInfo.state = "WAITING";
|
||||
MasterServerClient.instance.sendServerInfo(Net.serverInfo); // notify the server of the wait state
|
||||
}
|
||||
}
|
||||
|
||||
@:rpc(server) public static function playLevelMidJoin(index:Int) {
|
||||
if (Net.isClient) {
|
||||
var difficultyMissions = MissionList.missionList['ultra']["multiplayer"];
|
||||
|
|
@ -37,6 +57,12 @@ class NetCommands {
|
|||
}
|
||||
}
|
||||
|
||||
@:rpc(server) public static function playCustomLevelMidJoin(path:String) {
|
||||
if (Net.isClient) {
|
||||
playCustomLevel(path);
|
||||
}
|
||||
}
|
||||
|
||||
@:rpc(server) public static function enterLobby() {
|
||||
if (Net.isClient) {
|
||||
MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false));
|
||||
|
|
@ -77,6 +103,9 @@ class NetCommands {
|
|||
}
|
||||
|
||||
if (allReady && Net.lobbyHostReady) {
|
||||
if (MultiplayerLevelSelectGui.custSelected) {
|
||||
NetCommands.playCustomLevel(MultiplayerLevelSelectGui.custPath);
|
||||
} else
|
||||
NetCommands.playLevel(MultiplayerLevelSelectGui.currentSelectionStatic);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,13 +173,13 @@ class PowerupPickupPacket implements NetPacket {
|
|||
public inline function deserialize(b:InputBitStream) {
|
||||
clientId = b.readByte();
|
||||
serverTicks = b.readUInt16();
|
||||
powerupItemId = b.readInt(9);
|
||||
powerupItemId = b.readInt(10);
|
||||
}
|
||||
|
||||
public inline function serialize(b:OutputBitStream) {
|
||||
b.writeByte(clientId);
|
||||
b.writeUInt16(serverTicks);
|
||||
b.writeInt(powerupItemId, 9);
|
||||
b.writeInt(powerupItemId, 10);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -194,14 +194,14 @@ class GemSpawnPacket implements NetPacket {
|
|||
public function serialize(b:OutputBitStream) {
|
||||
b.writeInt(gemIds.length, 5);
|
||||
for (gemId in gemIds) {
|
||||
b.writeInt(gemId, 10);
|
||||
b.writeInt(gemId, 11);
|
||||
}
|
||||
}
|
||||
|
||||
public function deserialize(b:InputBitStream) {
|
||||
var count = b.readInt(5);
|
||||
for (i in 0...count) {
|
||||
gemIds.push(b.readInt(10));
|
||||
gemIds.push(b.readInt(11));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,14 +218,14 @@ class GemPickupPacket implements NetPacket {
|
|||
public inline function deserialize(b:InputBitStream) {
|
||||
clientId = b.readByte();
|
||||
serverTicks = b.readUInt16();
|
||||
gemId = b.readInt(10);
|
||||
gemId = b.readInt(11);
|
||||
scoreIncr = b.readInt(4);
|
||||
}
|
||||
|
||||
public inline function serialize(b:OutputBitStream) {
|
||||
b.writeByte(clientId);
|
||||
b.writeUInt16(serverTicks);
|
||||
b.writeInt(gemId, 10);
|
||||
b.writeInt(gemId, 11);
|
||||
b.writeInt(scoreIncr, 4);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue