diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 881ba1590..d5d4b87db 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -417,6 +417,20 @@ namespace Barotrauma //Hull.DebugDraw = !Hull.DebugDraw; //Ragdoll.DebugDraw = !Ragdoll.DebugDraw; GameMain.DebugDraw = !GameMain.DebugDraw; + break; + case "sendrandomdata": + int messageCount = 1; + + if (commands.Length>1) int.TryParse(commands[1], out messageCount); + + for (int i = 0; i < messageCount; i++ ) + { + if (GameMain.Server!=null) + { + GameMain.Server.SendRandomData(); + } + } + break; case "netstats": if (GameMain.Server == null) return; diff --git a/Subsurface/Source/GUI/GUIMessageBox.cs b/Subsurface/Source/GUI/GUIMessageBox.cs index 5d25a0f2c..3546b5766 100644 --- a/Subsurface/Source/GUI/GUIMessageBox.cs +++ b/Subsurface/Source/GUI/GUIMessageBox.cs @@ -7,7 +7,7 @@ namespace Barotrauma { public static Queue MessageBoxes = new Queue(); - const int DefaultWidth=400, DefaultHeight=200; + const int DefaultWidth=400, DefaultHeight=250; //public delegate bool OnClickedHandler(GUIButton button, object obj); //public OnClickedHandler OnClicked; diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 8c2879ee4..c1cc4d92d 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -631,12 +631,16 @@ namespace Barotrauma if (extension == ".sub") { - Stream stream = SaveUtil.DecompressFiletoStream(file); - if (stream == null) + Stream stream = null; + try { - DebugConsole.ThrowError("Loading submarine ''" + file + "'' failed!"); - return null; + stream = SaveUtil.DecompressFiletoStream(file); } + catch (Exception e) + { + DebugConsole.ThrowError("Loading submarine ''" + file + "'' failed!", e); + return null; + } try { diff --git a/Subsurface/Source/Networking/FileStreamReceiver.cs b/Subsurface/Source/Networking/FileStreamReceiver.cs index 8030dfff3..89f8c32fd 100644 --- a/Subsurface/Source/Networking/FileStreamReceiver.cs +++ b/Subsurface/Source/Networking/FileStreamReceiver.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Text.RegularExpressions; +using System.Xml.Linq; namespace Barotrauma.Networking { @@ -18,7 +19,7 @@ namespace Barotrauma.Networking private FileStream writeStream; private int timeStarted; - private string filePath; + private string downloadFolder; private FileTransferType fileType; @@ -28,11 +29,6 @@ namespace Barotrauma.Networking private set; } - public string FilePath - { - get { return filePath; } - } - public ulong FileSize { get { return length; } @@ -69,14 +65,13 @@ namespace Barotrauma.Networking public float Progress { get { return (float)received / (float)length; } - } public FileStreamReceiver(NetClient client, string filePath, FileTransferType fileType, OnFinished onFinished) { this.client = client; - this.filePath = filePath; + this.downloadFolder = filePath; this.fileType = fileType; this.onFinished = onFinished; @@ -92,8 +87,10 @@ namespace Barotrauma.Networking } catch (Exception e) { - DebugConsole.ThrowError("Error while receiving file ''"+FileName+"''", e); - Status = FileTransferStatus.Error; + ErrorMessage = "Error while receiving file ''"+FileName+"'' {"+e.Message+"}"; + DeleteFile(); + + if (onFinished != null) onFinished(this); } } @@ -102,21 +99,18 @@ namespace Barotrauma.Networking if (fileSize > MaxFileSize) { ErrorMessage = "File too large (" + MathUtils.GetBytesReadable((long)fileSize) + ")"; - Status = FileTransferStatus.Error; return false; } if (type != (byte)fileType) { - ErrorMessage = "Unexpected file type ''"+type+"'' (expected "+fileType+")"; - Status = FileTransferStatus.Error; + ErrorMessage = "Unexpected file type ''" + type + "'' (expected " + fileType + ")"; return false; } if (!Regex.Match(fileName, @"^[\w\- ]+[\w\-. ]*$").Success) { - ErrorMessage = "Illegal characters in file name ''"+fileName+"''"; - Status = FileTransferStatus.Error; + ErrorMessage = "Illegal characters in file name ''" + fileName + "''"; return false; } @@ -125,9 +119,7 @@ namespace Barotrauma.Networking case (byte)FileTransferType.Submarine: if (Path.GetExtension(fileName) != ".sub") { - ErrorMessage = "Wrong file extension ''" + Path.GetExtension(fileName)+"''! (Expected .sub)"; - - Status = FileTransferStatus.Error; + ErrorMessage = "Wrong file extension ''" + Path.GetExtension(fileName) + "''! (Expected .sub)"; return false; } break; @@ -138,12 +130,17 @@ namespace Barotrauma.Networking public void DeleteFile() { - string file = Path.Combine(filePath, FileName); + if (FileName == null) return; - writeStream.Flush(); - writeStream.Close(); - writeStream.Dispose(); - writeStream = null; + string file = Path.Combine(downloadFolder, FileName); + + if (writeStream!=null) + { + writeStream.Flush(); + writeStream.Close(); + writeStream.Dispose(); + writeStream = null; + } Status = FileTransferStatus.Canceled; @@ -170,9 +167,9 @@ namespace Barotrauma.Networking if (length == 0) { - if (!Directory.Exists(filePath)) + if (!string.IsNullOrWhiteSpace(downloadFolder) && !Directory.Exists(downloadFolder)) { - Directory.CreateDirectory(filePath); + Directory.CreateDirectory(downloadFolder); } byte fileTypeByte = inc.ReadByte(); @@ -183,11 +180,12 @@ namespace Barotrauma.Networking if (!ValidateInitialData(fileTypeByte, FileName, length)) { Status = FileTransferStatus.Error; + DeleteFile(); if (onFinished != null) onFinished(this); return; } - writeStream = new FileStream(Path.Combine(filePath, FileName), FileMode.Create, FileAccess.Write, FileShare.None); + writeStream = new FileStream(Path.Combine(downloadFolder, FileName), FileMode.Create, FileAccess.Write, FileShare.None); timeStarted = Environment.TickCount; Status = FileTransferStatus.NotStarted; @@ -218,10 +216,60 @@ namespace Barotrauma.Networking if (received >= length) { - Status = FileTransferStatus.Finished; + writeStream.Flush(); + writeStream.Close(); + writeStream.Dispose(); + writeStream = null; + + Status = IsReceivedFileValid() ? FileTransferStatus.Finished : FileTransferStatus.Error; if (onFinished!=null) onFinished(this); + + if (Status == FileTransferStatus.Error) DeleteFile(); + Dispose(); } } + + private bool IsReceivedFileValid() + { + switch (fileType) + { + case FileTransferType.Submarine: + string file = Path.Combine(downloadFolder, FileName); + Stream stream = null; + + try + { + stream = SaveUtil.DecompressFiletoStream(file); + } + catch (Exception e) + { + ErrorMessage = "Loading submarine ''" + file + "'' failed! {"+ e.Message + "}"; + return false; + } + + if (stream == null) + { + ErrorMessage = "Decompressing submarine file''" + file + "'' failed!"; + return false; + } + + try + { + stream.Position = 0; + var doc = XDocument.Load(stream); //ToolBox.TryLoadXml(file); + stream.Close(); + stream.Dispose(); + } + catch + { + ErrorMessage = "Failed to parse submarine file ''"+file+"''!"; + return false; + } + break; + } + + return true; + } public void Dispose() { diff --git a/Subsurface/Source/Networking/FileStreamSender.cs b/Subsurface/Source/Networking/FileStreamSender.cs index 86c2caf4b..8832ba541 100644 --- a/Subsurface/Source/Networking/FileStreamSender.cs +++ b/Subsurface/Source/Networking/FileStreamSender.cs @@ -11,7 +11,7 @@ namespace Barotrauma.Networking enum FileTransferType { - Unknown, Submarine + Unknown, Submarine, Cancel } class FileStreamSender : IDisposable @@ -39,6 +39,20 @@ namespace Barotrauma.Networking private set; } + public float Progress + { + get { return inputStream == null ? 0.0f : (float)sentOffset / (float)inputStream.Length; } + } + + public int Sent + { + get { return sentOffset; } + } + + public long FileSize + { + get { return inputStream == null ? 0 : inputStream.Length; } + } public static FileStreamSender Create(NetConnection conn, string fileName, FileTransferType fileType) { @@ -68,7 +82,10 @@ namespace Barotrauma.Networking public void Update(float deltaTime) { - if (inputStream == null) return; + if (inputStream == null || + Status == FileTransferStatus.Canceled || + Status == FileTransferStatus.Error || + Status == FileTransferStatus.Finished) return; waitTimer -= deltaTime; if (waitTimer > 0.0f) return; @@ -112,7 +129,12 @@ namespace Barotrauma.Networking //Dispose(); Status = FileTransferStatus.Finished; - } + } + } + + public void CancelTransfer() + { + Status = FileTransferStatus.Canceled; } diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 7b938b5d6..486858c4d 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -713,6 +713,11 @@ namespace Barotrauma.Networking fileStreamReceiver.DeleteFile(); fileStreamReceiver.Dispose(); fileStreamReceiver = null; + + NetOutgoingMessage msg = client.CreateMessage(); + msg.Write((byte)PacketTypes.RequestFile); + msg.Write((byte)FileTransferType.Cancel); + client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); } } @@ -736,7 +741,7 @@ namespace Barotrauma.Networking { if (receiver.Status == FileTransferStatus.Error) { - new GUIMessageBox("Error while receiving file from server", receiver.ErrorMessage); + new GUIMessageBox("Error while receiving file from server", receiver.ErrorMessage, 400, 350); receiver.DeleteFile(); } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index b970c6c9d..b408f8d6f 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -312,13 +312,36 @@ namespace Barotrauma.Networking foreach (Client c in ConnectedClients) { - if (c.FileStreamSender!=null && Rand.Range(0.0f, 1.0f)<0.01f) + if (c.FileStreamSender != null) { + var clientNameBox = GameMain.NetLobbyScreen.PlayerList.FindChild(c.name); + var clientInfo = clientNameBox.FindChild(c.FileStreamSender); + + if (clientInfo==null) + { + clientInfo = new GUIFrame(new Rectangle(0,0,180,0), Color.Transparent, Alignment.TopRight, null, clientNameBox); + clientInfo.UserData = c.FileStreamSender; + new GUIProgressBar(new Rectangle(0, 4, 0, clientInfo.Rect.Height-8), Color.Green, GUI.Style, 0.0f, Alignment.Left, clientInfo).IsHorizontal = true; + new GUITextBlock(new Rectangle(0,2,0,0), "", GUI.Style, Alignment.TopLeft, Alignment.Left | Alignment.CenterY, clientInfo, true, GUI.SmallFont); + } + else + { + var progressBar = clientInfo.GetChild(); + progressBar.BarSize = c.FileStreamSender.Progress; + + var progressText = clientInfo.GetChild(); + progressText.Text = c.FileStreamSender.FileName + " " + + MathUtils.GetBytesReadable(c.FileStreamSender.Sent) + " / " + MathUtils.GetBytesReadable(c.FileStreamSender.FileSize); + } + c.FileStreamSender.Update(deltaTime); if (c.FileStreamSender.Status == FileTransferStatus.Finished || - c.FileStreamSender.Status == FileTransferStatus.Error) + c.FileStreamSender.Status == FileTransferStatus.Error || + c.FileStreamSender.Status == FileTransferStatus.Canceled) { + clientNameBox.RemoveChild(clientInfo); + c.FileStreamSender.Dispose(); c.FileStreamSender = null; } @@ -522,11 +545,13 @@ namespace Barotrauma.Networking var outmsg = server.CreateMessage(); outmsg.Write((byte)PacketTypes.RequestFile); outmsg.Write(false); + outmsg.Write("File downloads disabled by the server"); + server.SendMessage(outmsg, dataSender.Connection, NetDeliveryMethod.ReliableUnordered); break; } byte fileType = inc.ReadByte(); - string fileName = inc.ReadString(); + string fileName = fileType == (byte)FileTransferType.Cancel ? "" : inc.ReadString(); switch (fileType) { @@ -544,6 +569,12 @@ namespace Barotrauma.Networking if (fileStreamSender != null) dataSender.FileStreamSender = fileStreamSender; } break; + case (byte)FileTransferType.Cancel: + if (dataSender.FileStreamSender != null) + { + dataSender.FileStreamSender.CancelTransfer(); + } + break; default: DebugConsole.ThrowError("Unknown file type was requested ("+fileType+")"); break; @@ -1079,6 +1110,12 @@ namespace Barotrauma.Networking server.SendMessage(outmsg, server.Connections, NetDeliveryMethod.ReliableUnordered, 0); } + if (client.FileStreamSender != null) + { + client.FileStreamSender.Dispose(); + client.FileStreamSender = null; + } + AddChatMessage(msg, ChatMessageType.Server); UpdateCrewFrame(); @@ -1516,38 +1553,38 @@ namespace Barotrauma.Networking /// sends some random data to the clients /// use for debugging purposes /// - //public void SendRandomData() - //{ - // NetOutgoingMessage msg = server.CreateMessage(); - // switch (Rand.Int(5)) - // { - // case 0: - // msg.WriteEnum(PacketTypes.NetworkEvent); - // msg.Write(Rand.Int(Enum.GetNames(typeof(NetworkEventType)).Length)); - // msg.Write(Rand.Int(MapEntity.mapEntityList.Count)); - // break; - // case 1: - // msg.WriteEnum(PacketTypes.NetworkEvent); - // msg.WriteEnum(NetworkEventType.ComponentUpdate); - // msg.Write((int)Item.ItemList[Rand.Int(Item.ItemList.Count)].ID); - // msg.Write(Rand.Int(8)); - // break; - // case 2: - // msg.Write((byte)Enum.GetNames(typeof(PacketTypes)).Length); - // break; - // case 3: - // msg.Write((byte)PacketTypes.UpdateNetLobby); - // break; - // } + public void SendRandomData() + { + NetOutgoingMessage msg = server.CreateMessage(); + switch (Rand.Int(5)) + { + case 0: + msg.Write((byte)PacketTypes.NetworkEvent); + msg.Write((byte)Rand.Int(Enum.GetNames(typeof(NetworkEventType)).Length)); + msg.Write((ushort)Rand.Int(MapEntity.mapEntityList.Count)); + break; + case 1: + msg.Write((byte)PacketTypes.NetworkEvent); + msg.Write((byte)NetworkEventType.ComponentUpdate); + msg.Write((int)Item.ItemList[Rand.Int(Item.ItemList.Count)].ID); + msg.Write(Rand.Int(8)); + break; + case 2: + msg.Write((byte)Enum.GetNames(typeof(PacketTypes)).Length); + break; + case 3: + msg.Write((byte)PacketTypes.UpdateNetLobby); + break; + } - // int bitCount = Rand.Int(100); - // for (int i = 0; i < bitCount; i++) - // { - // msg.Write(Rand.Int(2) == 0); - // } - // SendMessage(msg, (Rand.Int(2) == 0) ? NetDeliveryMethod.ReliableOrdered : NetDeliveryMethod.Unreliable, null); + int bitCount = Rand.Int(100); + for (int i = 0; i < bitCount; i++) + { + msg.Write(Rand.Int(2) == 0); + } + SendMessage(msg, (Rand.Int(2) == 0) ? NetDeliveryMethod.ReliableOrdered : NetDeliveryMethod.Unreliable, null); - //} + } public override void Disconnect() { @@ -1559,6 +1596,11 @@ namespace Barotrauma.Networking log.Save(); } + foreach (Client client in ConnectedClients) + { + if (client.FileStreamSender != null) client.FileStreamSender.Dispose(); + } + server.Shutdown("The server has shut down"); } } diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 215131455..bcad8ce5e 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -55,6 +55,11 @@ namespace Barotrauma get { return modeList; } } + public GUIListBox PlayerList + { + get { return playerList; } + } + public GUIFrame InfoFrame { get { return infoFrame; } @@ -505,14 +510,11 @@ namespace Barotrauma { valueChanged = true; - //Submarine sub = (Submarine)obj; + + var hash = (obj as Submarine).MD5Hash; - //submarine already loaded - //if (Submarine.Loaded != null && sub.FilePath == Submarine.Loaded.FilePath) return true; - - //sub.Load(); - - return true; + //hash will be null if opening the sub file failed -> don't select the sub + return hash.Hash != null; } public void UpdateSubList() @@ -657,10 +659,7 @@ namespace Barotrauma public void ClearPlayers() { - for (int i = 1; i