some more mid game join

This commit is contained in:
RandomityGuy 2024-04-27 00:00:44 +05:30
parent c57870c53e
commit 27b1ff183d
7 changed files with 157 additions and 22 deletions

View file

@ -1728,6 +1728,10 @@ class Marble extends GameObject {
// MP Only Functions
public function clearNetFlags() {
this.netFlags = 0;
}
public function packUpdate(move:NetMove, timeState:TimeState) {
var b = new OutputBitStream();
b.writeByte(NetPacketType.MarbleUpdate);
@ -1747,7 +1751,6 @@ class Marble extends GameObject {
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();
}

View file

@ -1,5 +1,8 @@
package src;
import net.NetPacket.GemSpawnPacket;
import net.BitStream.OutputBitStream;
import net.MasterServerClient;
import gui.MarblePickerGui;
import gui.MultiplayerLevelSelectGui;
import collision.CollisionPool;
@ -522,9 +525,19 @@ class MarbleWorld extends Scheduler {
cc++;
if (Net.isHost && cc == 0) {
allClientsReady();
Net.serverInfo.state = "PLAYING";
MasterServerClient.instance.sendServerInfo(Net.serverInfo); // notify the server of the playing state
}
}
public function addJoiningClient(cc:GameConnection, onAdded:() -> Void) {
this.initMarble(cc, () -> {
var addedMarble = clientMarbles.get(cc);
this.restart(addedMarble); // spawn it
onAdded();
});
}
public function restartMultiplayerState() {
if (this.isMultiplayer) {
serverStartTicks = 0;
@ -654,7 +667,7 @@ class MarbleWorld extends Scheduler {
AudioManager.playSound(ResourceLoader.getResource('data/sound/spawn_alternate.wav', ResourceLoader.getAudio, this.soundResources));
if (marble == this.marble)
if (!this.isMultiplayer)
this.gameMode.onRestart();
if (Net.isClient) {
this.gameMode.onClientRestart();
@ -711,6 +724,7 @@ class MarbleWorld extends Scheduler {
public function allClientsReady() {
NetCommands.setStartTicks(this.timeState.ticks);
this.gameMode.onRestart();
}
public function updateGameState() {
@ -1105,6 +1119,29 @@ class MarbleWorld extends Scheduler {
}
}
public function getWorldStateForClientJoin() {
var packets = [];
// First, gem spawn packet
var bs = new OutputBitStream();
bs.writeByte(GemSpawn);
var packet = new GemSpawnPacket();
var hunt = cast(this.gameMode, HuntMode);
var activeGemIds = [];
for (gemId in @:privateAccess hunt.activeGemSpawnGroup) {
if (@:privateAccess hunt.gemSpawnPoints[gemId].gem != null && @:privateAccess !hunt.gemSpawnPoints[gemId].gem.pickedUp) {
activeGemIds.push(gemId);
}
}
packet.gemIds = activeGemIds;
packet.serialize(bs);
packets.push(bs.getBytes());
// Powerup states
return packets;
}
public function applyReceivedMoves() {
var needsPrediction = 0;
if (!lastMoves.ourMoveApplied) {
@ -1212,8 +1249,10 @@ class MarbleWorld extends Scheduler {
pw.lastPickUpTime = powerupPredictions.getState(pw.netIndex);
}
var huntMode:HuntMode = cast this.gameMode;
for (activeGem in @:privateAccess huntMode.activeGemSpawnGroup) {
huntMode.setGemHiddenStatus(activeGem, gemPredictions.getState(activeGem));
if (@:privateAccess huntMode.activeGemSpawnGroup != null) {
for (activeGem in @:privateAccess huntMode.activeGemSpawnGroup) {
huntMode.setGemHiddenStatus(activeGem, gemPredictions.getState(activeGem));
}
}
// }
// }
@ -1519,7 +1558,7 @@ class MarbleWorld extends Scheduler {
for (client => marble in clientMarbles) {
otherMoves.push(marble.updateServer(fixedDt, collisionWorld, pathedInteriors));
}
if (myMove != null) {
if (myMove != null && Net.isClient) {
this.predictions.storeState(marble, myMove.timeState.ticks);
for (client => marble in clientMarbles) {
this.predictions.storeState(marble, myMove.timeState.ticks);
@ -1536,6 +1575,9 @@ class MarbleWorld extends Scheduler {
// pktClone.sort((a, b) -> {
// return (a.c == client.id) ? 1 : (b.c == client.id) ? -1 : 0;
// });
if (client.state != GAME)
continue; // Only send if in game
marble.clearNetFlags();
for (packet in packets) {
client.sendBytes(packet);
}
@ -1679,6 +1721,21 @@ class MarbleWorld extends Scheduler {
this.timeState.gameplayClock += ((this.timeState.currentAttemptTime + dt) - 3.5) * timeMultiplier;
}
} else if (this.multiplayerStarted) {
if (Net.isClient) {
var ticksSinceTimerStart = @:privateAccess this.marble.serverTicks - (this.serverStartTicks + 109);
var ourStartTime = this.gameMode.getStartTime();
var gameplayHigh = ourStartTime - ticksSinceTimerStart * 0.032;
var gameplayLow = ourStartTime - (ticksSinceTimerStart + 1) * 0.032;
// Clamp timer to be between these two
if (gameplayLow > this.timeState.gameplayClock) {
this.timeState.gameplayClock = gameplayLow - this.timeState.gameplayClock + gameplayHigh;
}
if (gameplayHigh < this.timeState.gameplayClock) {
this.timeState.gameplayClock = gameplayLow + this.timeState.gameplayClock - gameplayHigh;
}
}
this.timeState.gameplayClock += dt * timeMultiplier;
}
if (this.timeState.gameplayClock < 0)

View file

@ -133,7 +133,6 @@ class MPServerListGui extends GuiImage {
}, 15000);
Net.joinServer(ourServerList[curSelection].name, () -> {
failed = false;
MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false));
Net.remoteServerInfo = ourServerList[curSelection];
});
};

View file

@ -362,7 +362,7 @@ class HuntMode extends NullMode {
var os = new OutputBitStream();
os.writeByte(GemPickup);
packet.serialize(os);
Net.sendPacketToAll(os);
Net.sendPacketToIngame(os);
@:privateAccess level.playGui.incrementPlayerScore(packet.clientId, packet.scoreIncr);
}
@ -434,7 +434,7 @@ class HuntMode extends NullMode {
var packet = new GemSpawnPacket();
packet.gemIds = spawnGroup;
packet.serialize(bs);
Net.sendPacketToAll(bs);
Net.sendPacketToIngame(bs);
}
}
}

View file

@ -79,7 +79,7 @@ class Net {
public static var remoteServerInfo:RemoteServerInfo;
public static function hostServer(name:String, maxPlayers:Int, privateSlots:Int, privateServer:Bool, onHosted:() -> Void) {
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());
MasterServerClient.connectToMasterServer(() -> {
isHost = true;
isClient = false;
@ -292,6 +292,20 @@ class Net {
}
}
static function onClientHandshakeComplete(conn:ClientConnection) {
// Send our current mission to connecting client
NetCommands.setLobbyLevelIndexClient(conn, MultiplayerLevelSelectGui.currentSelectionStatic);
if (serverInfo.state == "PLAYING") { // We initiated the game, directly add in the marble
NetCommands.playLevelMidJoinClient(conn, MultiplayerLevelSelectGui.currentSelectionStatic);
MarbleGame.instance.world.addJoiningClient(conn, () -> {});
}
if (serverInfo.state == "LOBBY") {
// Connect client to lobby
NetCommands.enterLobbyClient(conn);
}
}
public static function sendPlayerInfosBytes() {
var b = new haxe.io.BytesOutput();
b.writeByte(PlayerInfo);
@ -367,8 +381,7 @@ class Net {
for (cc in clients) {
cc.sendBytes(b);
}
// Send our current mission to connecting client
NetCommands.setLobbyLevelIndexClient(conn, MultiplayerLevelSelectGui.currentSelectionStatic);
onClientHandshakeComplete(conn);
}
}
@ -459,6 +472,14 @@ class Net {
}
}
public static function sendPacketToIngame(packetData:OutputBitStream) {
var bytes = packetData.getBytes();
for (c => v in clients) {
if (v.state == GAME)
v.sendBytes(bytes);
}
}
public static function sendPacketToHost(packetData:OutputBitStream) {
if (clientDatachannel.state == Open) {
var bytes = packetData.getBytes();

View file

@ -8,6 +8,8 @@ import net.Net.NetPacketType;
import gui.MultiplayerLevelSelectGui;
import src.MarbleGame;
import gui.MultiplayerLoadingGui;
import src.MissionList;
import src.Console;
@:build(net.RPCMacro.build())
class NetCommands {
@ -21,6 +23,24 @@ class NetCommands {
@:rpc(server) public static function playLevel(levelIndex:Int) {
MultiplayerLevelSelectGui.playSelectedLevel(levelIndex);
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"];
var curMission = difficultyMissions[index];
MarbleGame.instance.playMission(curMission, true);
}
}
@:rpc(server) public static function enterLobby() {
if (Net.isClient) {
MarbleGame.canvas.setContent(new MultiplayerLevelSelectGui(false));
}
}
@:rpc(server) public static function setNetworkRNG(rng:Float) {
@ -64,18 +84,36 @@ class NetCommands {
@: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) {
allReady = false;
break;
if (Net.serverInfo.state == "WAITING" && MarbleGame.instance.world != null) {
Net.clientIdMap[clientId].ready();
var allReady = true;
for (id => client in Net.clientIdMap) {
if (client.state != GameplayState.GAME) {
allReady = false;
break;
}
}
}
if (allReady) {
if (MarbleGame.instance.world != null) {
MarbleGame.instance.world.allClientsReady();
if (allReady) {
if (MarbleGame.instance.world != null) {
MarbleGame.instance.world.allClientsReady();
}
}
if (Net.isHost) {
Net.serverInfo.state = "PLAYING";
MasterServerClient.instance.sendServerInfo(Net.serverInfo); // notify the server of the playing state
}
} else {
// Mid game join
Console.log("Mid game join for client " + clientId);
// Send em our present world state
var packets = MarbleGame.instance.world.getWorldStateForClientJoin();
var c = Net.clientIdMap[clientId];
for (packet in packets) {
c.sendBytes(packet);
}
Net.clientIdMap[clientId].ready();
// Send the start ticks
NetCommands.setStartTicksMidJoinClient(c, MarbleGame.instance.world.serverStartTicks, MarbleGame.instance.world.timeState.ticks);
}
}
}
@ -87,11 +125,23 @@ class NetCommands {
}
}
@:rpc(server) public static function setStartTicksMidJoin(startTicks:Int, currentTicks:Int) {
if (MarbleGame.instance.world != null) {
MarbleGame.instance.world.serverStartTicks = startTicks + 1; // Extra tick so we don't get 0
MarbleGame.instance.world.startTime = MarbleGame.instance.world.timeState.timeSinceLoad + 0.032; // 1 extra tick
MarbleGame.instance.world.timeState.ticks = currentTicks;
}
}
@:rpc(server) public static function timerRanOut() {
if (Net.isClient && MarbleGame.instance.world != null) {
var huntMode:HuntMode = cast MarbleGame.instance.world.gameMode;
huntMode.onTimeExpire();
}
if (Net.isHost) {
Net.serverInfo.state = "WAITING";
MasterServerClient.instance.sendServerInfo(Net.serverInfo); // notify the server of the playing state
}
}
@:rpc(server) public static function clientDisconnected(clientId:Int) {
@ -155,6 +205,11 @@ class NetCommands {
v.lobbyReady = false;
}
Net.lobbyHostReady = false;
if (Net.isHost) {
Net.serverInfo.state = "LOBBY";
MasterServerClient.instance.sendServerInfo(Net.serverInfo); // notify the server of the playing state
}
}
}

View file

@ -47,7 +47,7 @@ abstract class PowerUp extends DtsObject {
pickupPacket.serverTicks = timeState.ticks;
pickupPacket.powerupItemId = this.netIndex;
pickupPacket.serialize(b);
Net.sendPacketToAll(b);
Net.sendPacketToIngame(b);
}
this.lastPickUpTime = timeState.currentAttemptTime;