Syncing campaign state & save files with clients (WIP)

This commit is contained in:
Joonas Rikkonen
2017-09-11 21:32:48 +03:00
parent 51cfef214c
commit 63bca3c7ea
13 changed files with 383 additions and 107 deletions
@@ -508,6 +508,11 @@ namespace Barotrauma
public static void AddToGUIUpdateList() public static void AddToGUIUpdateList()
{ {
if (GUIMessageBox.VisibleBox != null)
{
GUIMessageBox.VisibleBox.AddToGUIUpdateList();
}
if (pauseMenuOpen) if (pauseMenuOpen)
{ {
pauseMenu.AddToGUIUpdateList(); pauseMenu.AddToGUIUpdateList();
@@ -517,11 +522,6 @@ namespace Barotrauma
{ {
GameMain.Config.SettingsFrame.AddToGUIUpdateList(); GameMain.Config.SettingsFrame.AddToGUIUpdateList();
} }
if (GUIMessageBox.VisibleBox != null)
{
GUIMessageBox.VisibleBox.AddToGUIUpdateList();
}
} }
public static void Update(float deltaTime) public static void Update(float deltaTime)
@@ -11,8 +11,6 @@ namespace Barotrauma
private static Texture2D iceCraters; private static Texture2D iceCraters;
private static Texture2D iceCrack; private static Texture2D iceCrack;
public Action<Location, LocationConnection> OnLocationSelected;
public void Update(float deltaTime, Rectangle rect, float scale = 1.0f) public void Update(float deltaTime, Rectangle rect, float scale = 1.0f)
{ {
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y); Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
@@ -142,14 +142,18 @@ namespace Barotrauma.Networking
private List<FileTransferIn> activeTransfers; private List<FileTransferIn> activeTransfers;
private string downloadFolder; private Dictionary<FileTransferType, string> downloadFolders = new Dictionary<FileTransferType, string>()
{
{ FileTransferType.Submarine, "Submarines/Downloaded" },
{ FileTransferType.CampaignSave, "Data/Saves/Multiplayer" }
};
public List<FileTransferIn> ActiveTransfers public List<FileTransferIn> ActiveTransfers
{ {
get { return activeTransfers; } get { return activeTransfers; }
} }
public FileReceiver(string downloadFolder) public FileReceiver()
{ {
if (GameMain.Server != null) if (GameMain.Server != null)
{ {
@@ -157,8 +161,6 @@ namespace Barotrauma.Networking
} }
activeTransfers = new List<FileTransferIn>(); activeTransfers = new List<FileTransferIn>();
this.downloadFolder = downloadFolder;
} }
public void ReadMessage(NetIncomingMessage inc) public void ReadMessage(NetIncomingMessage inc)
@@ -201,11 +203,13 @@ namespace Barotrauma.Networking
if (GameSettings.VerboseLogging) if (GameSettings.VerboseLogging)
{ {
DebugConsole.Log("Received file transfer initiation message: "); DebugConsole.Log("Received file transfer initiation message: ");
DebugConsole.Log(" File: "+fileName); DebugConsole.Log(" File: " + fileName);
DebugConsole.Log(" Size: " + fileSize); DebugConsole.Log(" Size: " + fileSize);
DebugConsole.Log(" Sequence channel: " + inc.SequenceChannel); DebugConsole.Log(" Sequence channel: " + inc.SequenceChannel);
} }
string downloadFolder = downloadFolders[(FileTransferType)fileType];
if (!Directory.Exists(downloadFolder)) if (!Directory.Exists(downloadFolder))
{ {
try try
@@ -214,7 +218,7 @@ namespace Barotrauma.Networking
} }
catch (Exception e) catch (Exception e)
{ {
DebugConsole.ThrowError("Could not start a file transfer: failed to create the folder \""+downloadFolder+"\".", e); DebugConsole.ThrowError("Could not start a file transfer: failed to create the folder \"" + downloadFolder + "\".", e);
return; return;
} }
} }
@@ -322,6 +326,13 @@ namespace Barotrauma.Networking
return false; return false;
} }
break; break;
case (byte)FileTransferType.CampaignSave:
if (Path.GetExtension(fileName) != ".save")
{
errorMessage = "Wrong file extension ''" + Path.GetExtension(fileName) + "''! (Expected .save)";
return false;
}
break;
} }
return true; return true;
@@ -376,6 +387,9 @@ namespace Barotrauma.Networking
stream.Close(); stream.Close();
stream.Dispose(); stream.Dispose();
break; break;
case FileTransferType.CampaignSave:
//TODO: verify that the received file is a valid save file
break;
} }
return true; return true;
@@ -89,7 +89,7 @@ namespace Barotrauma.Networking
entityEventManager = new ClientEntityEventManager(this); entityEventManager = new ClientEntityEventManager(this);
fileReceiver = new FileReceiver("Submarines/Downloaded"); fileReceiver = new FileReceiver();
fileReceiver.OnFinished += OnFileReceived; fileReceiver.OnFinished += OnFileReceived;
characterInfo = new CharacterInfo(Character.HumanConfigFile, name,Gender.None,null); characterInfo = new CharacterInfo(Character.HumanConfigFile, name,Gender.None,null);
@@ -703,8 +703,11 @@ namespace Barotrauma.Networking
Rand.SetSyncedSeed(seed); Rand.SetSyncedSeed(seed);
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, Mission.MissionTypes[missionTypeIndex]); if (gameMode.Name != "Campaign")
GameMain.GameSession.StartRound(levelSeed,loadSecondSub); {
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, Mission.MissionTypes[missionTypeIndex]);
}
GameMain.GameSession.StartRound(levelSeed, loadSecondSub);
if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.SelectedShuttle); if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.SelectedShuttle);
@@ -896,7 +899,16 @@ namespace Barotrauma.Networking
Voting.AllowModeVoting = allowModeVoting; Voting.AllowModeVoting = allowModeVoting;
} }
} }
bool campaignUpdated = inc.ReadBoolean();
inc.ReadPadBits();
if (campaignUpdated)
{
MultiplayerCampaign.ClientRead(inc);
}
lastSentChatMsgID = inc.ReadUInt16(); lastSentChatMsgID = inc.ReadUInt16();
break; break;
case ServerNetObject.CHAT_MESSAGE: case ServerNetObject.CHAT_MESSAGE:
ChatMessage.ClientRead(inc); ChatMessage.ClientRead(inc);
@@ -961,6 +973,9 @@ namespace Barotrauma.Networking
outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID); outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
outmsg.Write(ChatMessage.LastID); outmsg.Write(ChatMessage.LastID);
var campaign = GameMain.GameSession?.GameMode as MultiplayerCampaign;
outmsg.Write(campaign == null ? 0 : campaign.LastUpdateID);
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID)); chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++) for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
{ {
@@ -1038,8 +1053,8 @@ namespace Barotrauma.Networking
msg.Write((byte)ClientPacketHeader.FILE_REQUEST); msg.Write((byte)ClientPacketHeader.FILE_REQUEST);
msg.Write((byte)FileTransferMessageType.Initiate); msg.Write((byte)FileTransferMessageType.Initiate);
msg.Write((byte)fileType); msg.Write((byte)fileType);
msg.Write(file); if (file != null) msg.Write(file);
msg.Write(fileHash); if (fileHash != null) msg.Write(fileHash);
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
} }
@@ -1083,6 +1098,22 @@ namespace Barotrauma.Networking
textBlock.UserData = newSub; textBlock.UserData = newSub;
textBlock.ToolTip = newSub.Description; textBlock.ToolTip = newSub.Description;
} }
break;
case FileTransferType.CampaignSave:
var campaign = GameMain.GameSession?.GameMode as MultiplayerCampaign;
if (campaign == null) return;
GameMain.GameSession.SavePath = transfer.FilePath;
campaign.LastSaveID = campaign.PendingSaveID;
if (GameMain.GameSession.Submarine == null)
{
var gameSessionDoc = SaveUtil.LoadGameSessionDoc(GameMain.GameSession.SavePath);
string subPath = Path.Combine(SaveUtil.TempPath, ToolBox.GetAttributeString(gameSessionDoc.Root, "submarine", "")) + ".sub";
GameMain.GameSession.Submarine = new Submarine(subPath, "");
}
break; break;
} }
} }
@@ -1,10 +1,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
@@ -18,6 +15,8 @@ namespace Barotrauma
private GUITextBox saveNameBox, seedBox; private GUITextBox saveNameBox, seedBox;
private GUIButton loadGameButton;
public Action<Submarine, string, string> StartNewGame; public Action<Submarine, string, string> StartNewGame;
public Action<string> LoadGame; public Action<string> LoadGame;
@@ -138,8 +137,14 @@ namespace Barotrauma
textBlock.UserData = saveFile; textBlock.UserData = saveFile;
} }
var button = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", Alignment.Right | Alignment.Bottom, "", loadGameContainer); loadGameButton = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", Alignment.Right | Alignment.Bottom, "", loadGameContainer);
button.OnClicked = (btn, obj) => { LoadGame?.Invoke(saveList.SelectedData as string); return true; }; loadGameButton.OnClicked = (btn, obj) =>
{
if (string.IsNullOrWhiteSpace(saveList.SelectedData as string)) return false;
LoadGame?.Invoke(saveList.SelectedData as string);
return true;
};
loadGameButton.Enabled = false;
} }
private bool SelectSaveFile(GUIComponent component, object obj) private bool SelectSaveFile(GUIComponent component, object obj)
@@ -154,6 +159,8 @@ namespace Barotrauma
return false; return false;
} }
loadGameButton.Enabled = true;
RemoveSaveFrame(); RemoveSaveFrame();
string subName = ToolBox.GetAttributeString(doc.Root, "submarine", ""); string subName = ToolBox.GetAttributeString(doc.Root, "submarine", "");
@@ -1001,7 +1001,7 @@ namespace Barotrauma
if (campaignContainer.Visible && campaignUI != null) if (campaignContainer.Visible && campaignUI != null)
{ {
campaignContainer.Update((float)deltaTime); //campaignContainer.Update((float)deltaTime);
campaignUI.Update((float)deltaTime); campaignUI.Update((float)deltaTime);
} }
@@ -1096,9 +1096,18 @@ namespace Barotrauma
public void SelectMode(int modeIndex) public void SelectMode(int modeIndex)
{ {
modeList.Select(modeIndex, true); if (modeIndex < 0 || modeIndex >= modeList.children.Count) return;
ToggleCampaignMode(SelectedMode.Name == "Campaign"); if (GameMain.Server != null)
{
if (((GameModePreset)modeList.children[modeIndex].UserData).Name == "Campaign")
{
MultiplayerCampaign.StartCampaignSetup();
return;
}
}
modeList.Select(modeIndex, true);
missionTypeBlock.Visible = SelectedMode != null && SelectedMode.Name == "Mission"; missionTypeBlock.Visible = SelectedMode != null && SelectedMode.Name == "Mission";
} }
@@ -1111,9 +1120,15 @@ namespace Barotrauma
missionTypeBlock.Visible = modePreset.Name == "Mission"; missionTypeBlock.Visible = modePreset.Name == "Mission";
if (modePreset.Name == "Campaign") if (GameMain.Server != null)
{ {
MultiplayerCampaign.StartCampaignSetup(); //campaign selected and the campaign view has not been set up yet
// -> don't select the mode yet and start campaign setup
if (modePreset.Name == "Campaign" && !campaignContainer.Visible)
{
MultiplayerCampaign.StartCampaignSetup();
return false;
}
} }
lastUpdateID++; lastUpdateID++;
@@ -1122,23 +1137,31 @@ namespace Barotrauma
public void ToggleCampaignView(bool enabled) public void ToggleCampaignView(bool enabled)
{ {
defaultModeContainer.Visible = !enabled;
StartButton.Visible = !enabled;
campaignContainer.Visible = enabled; campaignContainer.Visible = enabled;
defaultModeContainer.Visible = !enabled;
if (StartButton != null)
{
StartButton.Visible = !enabled;
}
} }
public void ToggleCampaignMode(bool enabled) public void ToggleCampaignMode(bool enabled)
{ {
StartButton.Visible = !enabled;
campaignViewButton.Visible = enabled;
ToggleCampaignView(enabled); ToggleCampaignView(enabled);
if (enabled && campaignUI == null) if (enabled)
{ {
campaignUI = new CampaignUI(GameMain.GameSession.GameMode as CampaignMode, campaignContainer); if (campaignUI == null)
campaignUI.StartRound = () => { GameMain.Server.StartGame(); }; {
campaignUI = new CampaignUI(GameMain.GameSession.GameMode as CampaignMode, campaignContainer);
campaignUI.StartRound = () => { GameMain.Server.StartGame(); };
}
modeList.Select(2, true);
}
if (GameMain.Server != null)
{
lastUpdateID++;
} }
} }
@@ -1,14 +1,34 @@
using Microsoft.Xna.Framework; using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using Lidgren.Network;
using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
class MultiplayerCampaign : CampaignMode class MultiplayerCampaign : CampaignMode
{ {
private UInt16 lastUpdateID;
public UInt16 LastUpdateID
{
get { if (GameMain.Server != null && lastUpdateID < 1) lastUpdateID++; return lastUpdateID; }
set { lastUpdateID = value; }
}
private UInt16 lastSaveID;
public UInt16 LastSaveID
{
get { if (GameMain.Server != null && lastSaveID < 1) lastSaveID++; return lastSaveID; }
set { lastSaveID = value; }
}
public UInt16 PendingSaveID
{
get;
set;
}
public MultiplayerCampaign(GameModePreset preset, object param) : public MultiplayerCampaign(GameModePreset preset, object param) :
base(preset, param) base(preset, param)
@@ -49,88 +69,123 @@ namespace Barotrauma
GameMain.GameSession = new GameSession(sub, saveName, GameModePreset.list.Find(g => g.Name == "Campaign")); GameMain.GameSession = new GameSession(sub, saveName, GameModePreset.list.Find(g => g.Name == "Campaign"));
var campaign = ((MultiplayerCampaign)GameMain.GameSession.GameMode); var campaign = ((MultiplayerCampaign)GameMain.GameSession.GameMode);
campaign.GenerateMap(mapSeed); campaign.GenerateMap(mapSeed);
campaign.map.OnLocationSelected += (loc, connection) => { campaign.LastUpdateID++; };
setupBox.Close(); setupBox.Close();
GameMain.NetLobbyScreen.ToggleCampaignMode(true); GameMain.NetLobbyScreen.ToggleCampaignMode(true);
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
campaign.LastSaveID++;
}; };
campaignSetupUI.LoadGame = (string fileName) => campaignSetupUI.LoadGame = (string fileName) =>
{ {
SaveUtil.LoadGame(fileName); SaveUtil.LoadGame(fileName);
var campaign = ((MultiplayerCampaign)GameMain.GameSession.GameMode);
campaign.LastSaveID++;
setupBox.Close(); setupBox.Close();
GameMain.NetLobbyScreen.ToggleCampaignMode(true); GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}; };
var cancelButton = new GUIButton(new Rectangle(0,0,120,30), "Cancel", Alignment.BottomLeft, "", setupBox.InnerFrame);
cancelButton.OnClicked += (btn, obj) =>
{
setupBox.Close();
int otherModeIndex = 0;
for (otherModeIndex = 0; otherModeIndex < GameMain.NetLobbyScreen.ModeList.children.Count; otherModeIndex++)
{
if (GameMain.NetLobbyScreen.ModeList.children[otherModeIndex].UserData is MultiplayerCampaign) continue;
break;
}
GameMain.NetLobbyScreen.SelectMode(otherModeIndex);
return true;
};
} }
#endif #endif
public override void Start()
{
base.Start();
lastUpdateID++;
}
public override void End(string endMessage = "") public override void End(string endMessage = "")
{ {
isRunning = false; isRunning = false;
bool success = if (GameMain.Server != null)
GameMain.Server.ConnectedClients.Any(c => c.inGame && c.Character != null && !c.Character.IsDead) ||
(GameMain.Server.Character != null && !GameMain.Server.Character.IsDead);
/*if (success)
{ {
if (subsToLeaveBehind == null || leavingSub == null) lastUpdateID++;
bool success =
GameMain.Server.ConnectedClients.Any(c => c.inGame && c.Character != null && !c.Character.IsDead) ||
(GameMain.Server.Character != null && !GameMain.Server.Character.IsDead);
/*if (success)
{ {
DebugConsole.ThrowError("Leaving submarine not selected -> selecting the closest one"); if (subsToLeaveBehind == null || leavingSub == null)
leavingSub = GetLeavingSub();
subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
}
}*/
GameMain.GameSession.EndRound("");
if (success)
{
bool atEndPosition = Submarine.MainSub.AtEndPosition;
/*if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
{
Submarine.MainSub = leavingSub;
GameMain.GameSession.Submarine = leavingSub;
foreach (Submarine sub in subsToLeaveBehind)
{ {
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine); DebugConsole.ThrowError("Leaving submarine not selected -> selecting the closest one");
LinkedSubmarine.CreateDummy(leavingSub, sub);
leavingSub = GetLeavingSub();
subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
} }
}*/ }*/
if (atEndPosition) GameMain.GameSession.EndRound("");
if (success)
{ {
Map.MoveToNextLocation(); bool atEndPosition = Submarine.MainSub.AtEndPosition;
/*if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
{
Submarine.MainSub = leavingSub;
GameMain.GameSession.Submarine = leavingSub;
foreach (Submarine sub in subsToLeaveBehind)
{
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
LinkedSubmarine.CreateDummy(leavingSub, sub);
}
}*/
if (atEndPosition)
{
Map.MoveToNextLocation();
}
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
}
if (!success)
if (!success)
{
/* var summaryScreen = GUIMessageBox.VisibleBox;
if (summaryScreen != null)
{ {
summaryScreen = summaryScreen.children[0]; /* var summaryScreen = GUIMessageBox.VisibleBox;
summaryScreen.RemoveChild(summaryScreen.children.Find(c => c is GUIButton));
var okButton = new GUIButton(new Rectangle(-120, 0, 100, 30), "Load game", Alignment.BottomRight, "", summaryScreen); if (summaryScreen != null)
okButton.OnClicked += GameMain.GameSession.LoadPrevious; {
okButton.OnClicked += (GUIButton button, object obj) => { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox); return true; }; summaryScreen = summaryScreen.children[0];
summaryScreen.RemoveChild(summaryScreen.children.Find(c => c is GUIButton));
var quitButton = new GUIButton(new Rectangle(0, 0, 100, 30), "Quit", Alignment.BottomRight, "", summaryScreen); var okButton = new GUIButton(new Rectangle(-120, 0, 100, 30), "Load game", Alignment.BottomRight, "", summaryScreen);
quitButton.OnClicked += GameMain.LobbyScreen.QuitToMainMenu; okButton.OnClicked += GameMain.GameSession.LoadPrevious;
quitButton.OnClicked += (GUIButton button, object obj) => { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox); return true; }; okButton.OnClicked += (GUIButton button, object obj) => { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox); return true; };
}*/
var quitButton = new GUIButton(new Rectangle(0, 0, 100, 30), "Quit", Alignment.BottomRight, "", summaryScreen);
quitButton.OnClicked += GameMain.LobbyScreen.QuitToMainMenu;
quitButton.OnClicked += (GUIButton button, object obj) => { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox); return true; };
}*/
}
} }
for (int i = Character.CharacterList.Count - 1; i >= 0; i--) for (int i = Character.CharacterList.Count - 1; i >= 0; i--)
@@ -165,6 +220,8 @@ namespace Barotrauma
campaign.map.SetLocation(ToolBox.GetAttributeInt(element, "currentlocation", 0)); campaign.map.SetLocation(ToolBox.GetAttributeInt(element, "currentlocation", 0));
} }
campaign.map.OnLocationSelected += (loc, connection) => { campaign.LastUpdateID++; };
//campaign.savedOnStart = true; //campaign.savedOnStart = true;
return campaign; return campaign;
} }
@@ -175,6 +232,73 @@ namespace Barotrauma
modeElement.Add(new XAttribute("money", Money)); modeElement.Add(new XAttribute("money", Money));
Map.Save(modeElement); Map.Save(modeElement);
element.Add(modeElement); element.Add(modeElement);
lastSaveID++;
} }
public void ServerWrite(NetBuffer msg, Client c)
{
System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
msg.Write(lastUpdateID);
msg.Write(lastSaveID);
msg.Write(map.Seed);
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
}
#if CLIENT
public static void ClientRead(NetBuffer msg)
{
//static because we may need to instantiate the campaign if it hasn't been done yet
UInt16 updateID = msg.ReadUInt16();
UInt16 saveID = msg.ReadUInt16();
string mapSeed = msg.ReadString();
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
MultiplayerCampaign campaign = GameMain.GameSession?.GameMode as MultiplayerCampaign;
if (campaign == null)
{
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
GameMain.GameSession = new GameSession(
null, //TODO: set the sub (after receiving the save file?)
savePath, GameModePreset.list.Find(g => g.Name == "Campaign"));
campaign = ((MultiplayerCampaign)GameMain.GameSession.GameMode);
campaign.GenerateMap(mapSeed);
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
if (NetIdUtils.IdMoreRecent(campaign.lastUpdateID, updateID)) return;
//server has a newer save file
if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID))
{
//stop any active campaign save transfers, they're outdated now
List<FileReceiver.FileTransferIn> saveTransfers =
GameMain.Client.FileReceiver.ActiveTransfers.FindAll(t => t.FileType == FileTransferType.CampaignSave);
foreach (var transfer in saveTransfers)
{
GameMain.Client.FileReceiver.StopTransfer(transfer);
}
GameMain.Client.RequestFile(FileTransferType.CampaignSave, null, null);
campaign.PendingSaveID = saveID;
}
//we've got the latest save file
else if (!NetIdUtils.IdMoreRecent(saveID, campaign.lastSaveID))
{
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.lastUpdateID = updateID;
}
}
#endif
} }
} }
@@ -79,6 +79,7 @@ namespace Barotrauma
public string SavePath public string SavePath
{ {
get { return savePath; } get { return savePath; }
set { savePath = value; }
} }
public GameSession(Submarine submarine, string savePath, GameModePreset gameModePreset = null, string missionType = "") public GameSession(Submarine submarine, string savePath, GameModePreset gameModePreset = null, string missionType = "")
@@ -168,7 +169,7 @@ namespace Barotrauma
this.level = level; this.level = level;
if (submarine==null) if (submarine == null)
{ {
DebugConsole.ThrowError("Couldn't start game session, submarine not selected"); DebugConsole.ThrowError("Couldn't start game session, submarine not selected");
return; return;
@@ -28,6 +28,8 @@ namespace Barotrauma
private LocationConnection selectedConnection; private LocationConnection selectedConnection;
public Action<Location, LocationConnection> OnLocationSelected;
public Location CurrentLocation public Location CurrentLocation
{ {
get { return currentLocation; } get { return currentLocation; }
@@ -43,6 +45,11 @@ namespace Barotrauma
get { return selectedLocation; } get { return selectedLocation; }
} }
public int SelectedLocationIndex
{
get { return locations.IndexOf(selectedLocation); }
}
public LocationConnection SelectedConnection public LocationConnection SelectedConnection
{ {
get { return selectedConnection; } get { return selectedConnection; }
@@ -53,6 +60,11 @@ namespace Barotrauma
get { return seed; } get { return seed; }
} }
public List<Location> Locations
{
get { return locations; }
}
public static Map Load(XElement element) public static Map Load(XElement element)
{ {
string mapSeed = ToolBox.GetAttributeString(element, "seed", "a"); string mapSeed = ToolBox.GetAttributeString(element, "seed", "a");
@@ -342,6 +354,12 @@ namespace Barotrauma
public void SetLocation(int index) public void SetLocation(int index)
{ {
if (index == -1)
{
currentLocation = null;
return;
}
if (index < 0 || index >= locations.Count) if (index < 0 || index >= locations.Count)
{ {
DebugConsole.ThrowError("Location index out of bounds"); DebugConsole.ThrowError("Location index out of bounds");
@@ -352,6 +370,28 @@ namespace Barotrauma
currentLocation.Discovered = true; currentLocation.Discovered = true;
} }
public void SelectLocation(int index)
{
if (index == -1)
{
selectedLocation = null;
selectedConnection = null;
OnLocationSelected?.Invoke(null, null);
return;
}
if (index < 0 || index >= locations.Count)
{
DebugConsole.ThrowError("Location index out of bounds");
return;
}
selectedLocation = locations[index];
selectedConnection = connections.Find(c => c.Locations.Contains(currentLocation) && c.Locations.Contains(selectedLocation));
OnLocationSelected?.Invoke(selectedLocation, selectedConnection);
}
public void Save(XElement element) public void Save(XElement element)
{ {
XElement mapElement = new XElement("map"); XElement mapElement = new XElement("map");
@@ -37,6 +37,8 @@ namespace Barotrauma.Networking
public UInt16 lastSentEntityEventID = 0; public UInt16 lastSentEntityEventID = 0;
public UInt16 lastRecvEntityEventID = 0; public UInt16 lastRecvEntityEventID = 0;
public UInt16 lastRecvCampaignUpdate = 0;
public List<ChatMessage> chatMsgQueue = new List<ChatMessage>(); public List<ChatMessage> chatMsgQueue = new List<ChatMessage>();
public UInt16 lastChatMsgQueueID; public UInt16 lastChatMsgQueueID;
@@ -18,7 +18,7 @@ namespace Barotrauma.Networking
enum FileTransferType enum FileTransferType
{ {
Submarine Submarine, CampaignSave
} }
class FileSender class FileSender
@@ -277,6 +277,12 @@ namespace Barotrauma.Networking
StartTransfer(inc.SenderConnection, FileTransferType.Submarine, requestedSubmarine.FilePath); StartTransfer(inc.SenderConnection, FileTransferType.Submarine, requestedSubmarine.FilePath);
} }
break; break;
case (byte)FileTransferType.CampaignSave:
if (GameMain.GameSession != null)
{
StartTransfer(inc.SenderConnection, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
}
break;
} }
} }
@@ -611,6 +611,8 @@ namespace Barotrauma.Networking
//TODO: might want to use a clever class for this //TODO: might want to use a clever class for this
c.lastRecvGeneralUpdate = NetIdUtils.Clamp(inc.ReadUInt16(), c.lastRecvGeneralUpdate, GameMain.NetLobbyScreen.LastUpdateID); c.lastRecvGeneralUpdate = NetIdUtils.Clamp(inc.ReadUInt16(), c.lastRecvGeneralUpdate, GameMain.NetLobbyScreen.LastUpdateID);
c.lastRecvChatMsgID = NetIdUtils.Clamp(inc.ReadUInt16(), c.lastRecvChatMsgID, c.lastChatMsgQueueID); c.lastRecvChatMsgID = NetIdUtils.Clamp(inc.ReadUInt16(), c.lastRecvChatMsgID, c.lastChatMsgQueueID);
c.lastRecvCampaignUpdate = inc.ReadUInt16();
break; break;
case ClientNetObject.CHAT_MESSAGE: case ClientNetObject.CHAT_MESSAGE:
ChatMessage.ServerRead(inc, c); ChatMessage.ServerRead(inc, c);
@@ -965,6 +967,27 @@ namespace Barotrauma.Networking
outmsg.WritePadBits(); outmsg.WritePadBits();
} }
var campaign = GameMain.GameSession?.GameMode as MultiplayerCampaign;
if (campaign != null)
{
if (NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.lastRecvCampaignUpdate))
{
outmsg.Write(true);
outmsg.WritePadBits();
campaign.ServerWrite(outmsg, c);
}
else
{
outmsg.Write(false);
outmsg.WritePadBits();
}
}
else
{
outmsg.Write(false);
outmsg.WritePadBits();
}
outmsg.Write(c.lastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server outmsg.Write(c.lastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server
WriteChatMessages(outmsg, c); WriteChatMessages(outmsg, c);
@@ -1129,8 +1152,6 @@ namespace Barotrauma.Networking
int teamCount = 1; int teamCount = 1;
byte hostTeam = 1; byte hostTeam = 1;
string levelSeed = GameMain.NetLobbyScreen.LevelSeed;
MultiplayerCampaign campaign = GameMain.GameSession?.GameMode as MultiplayerCampaign; MultiplayerCampaign campaign = GameMain.GameSession?.GameMode as MultiplayerCampaign;
//don't instantiate a new gamesession if we're playing a campaign //don't instantiate a new gamesession if we're playing a campaign
@@ -1310,7 +1331,7 @@ namespace Barotrauma.Networking
msg.Write(seed); msg.Write(seed);
msg.Write(GameMain.NetLobbyScreen.LevelSeed); msg.Write(GameMain.GameSession.Level.Seed);
msg.Write((byte)GameMain.NetLobbyScreen.MissionTypeIndex); msg.Write((byte)GameMain.NetLobbyScreen.MissionTypeIndex);
@@ -40,10 +40,19 @@ namespace Barotrauma
try try
{ {
if (Submarine.MainSub != null && Submarine.Loaded.Contains(Submarine.MainSub)) string subPath = Path.Combine(tempPath, Submarine.MainSub.Name + ".sub");
if (Submarine.MainSub != null)
{ {
Submarine.MainSub.FilePath = Path.Combine(tempPath, Submarine.MainSub.Name + ".sub"); if (Submarine.Loaded.Contains(Submarine.MainSub))
Submarine.MainSub.SaveAs(Submarine.MainSub.FilePath); {
Submarine.MainSub.FilePath = subPath;
Submarine.MainSub.SaveAs(Submarine.MainSub.FilePath);
}
else
{
File.Copy(Submarine.MainSub.FilePath, subPath);
Submarine.MainSub.FilePath = subPath;
}
} }
} }
catch (Exception e) catch (Exception e)
@@ -79,8 +88,8 @@ namespace Barotrauma
XDocument doc = ToolBox.TryLoadXml(Path.Combine(TempPath, "gamesession.xml")); XDocument doc = ToolBox.TryLoadXml(Path.Combine(TempPath, "gamesession.xml"));
string subPath = Path.Combine(TempPath, ToolBox.GetAttributeString(doc.Root, "submarine", "")) + ".sub"; string subPath = Path.Combine(TempPath, ToolBox.GetAttributeString(doc.Root, "submarine", "")) + ".sub";
Submarine selectedMap = new Submarine(subPath, ""); Submarine selectedSub = new Submarine(subPath, "");
GameMain.GameSession = new GameSession(selectedMap, filePath, doc); GameMain.GameSession = new GameSession(selectedSub, filePath, doc);
} }
public static XDocument LoadGameSessionDoc(string filePath) public static XDocument LoadGameSessionDoc(string filePath)