diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Door.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Door.cs index d536e9fe5..8a1858095 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Door.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Door.cs @@ -93,7 +93,7 @@ namespace Barotrauma.Items.Components public void Draw(SpriteBatch spriteBatch, bool editing) { - Color color = Color.White; + Color color = item.SpriteColor; if (brokenSprite == null) { //broken doors turn black if no broken sprite has been configured @@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components weldSpritePos.Y = -weldSpritePos.Y; weldedSprite.Draw(spriteBatch, - weldSpritePos, Color.White * (stuck / 100.0f), scale: item.Scale); + weldSpritePos, item.SpriteColor * (stuck / 100.0f), scale: item.Scale); } if (openState == 1.0f) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs index 4ed31820f..6e42a2949 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs @@ -237,13 +237,13 @@ namespace Barotrauma.Items.Components railSprite?.Draw(spriteBatch, drawPos, - Color.White, + item.SpriteColor, rotation + MathHelper.PiOver2, item.Scale, SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth)); barrelSprite?.Draw(spriteBatch, - drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale, - Color.White, + drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale, + item.SpriteColor, rotation + MathHelper.PiOver2, item.Scale, SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth)); diff --git a/Barotrauma/BarotraumaClient/Source/Networking/FileTransfer/FileReceiver.cs b/Barotrauma/BarotraumaClient/Source/Networking/FileTransfer/FileReceiver.cs index ad3a3fb8b..be2dd8d7b 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/FileTransfer/FileReceiver.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/FileTransfer/FileReceiver.cs @@ -241,20 +241,30 @@ namespace Barotrauma.Networking FileSize = fileSize }; - try + int maxRetries = 4; + for (int i = 0; i <= maxRetries; i++) { - newTransfer.OpenStream(); + try + { + newTransfer.OpenStream(); + } + catch (IOException e) + { + if (i < maxRetries) + { + DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}, retrying in 250 ms...", Color.Red); + Thread.Sleep(250); + } + else + { + DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}", Color.Red); + GameMain.Client.CancelFileTransfer(inc.SequenceChannel); + newTransfer.Status = FileTransferStatus.Error; + OnTransferFailed(newTransfer); + return; + } + } } - catch (IOException e) - { - GameMain.Client.CancelFileTransfer(inc.SequenceChannel); - DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}", Color.Red); - - newTransfer.Status = FileTransferStatus.Error; - OnTransferFailed(newTransfer); - return; - } - activeTransfers.Add(newTransfer); } break; diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs index dd064d8fe..fb9abb67a 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs @@ -141,8 +141,23 @@ namespace Barotrauma.Networking { OnClicked = (btn, userdata) => { - if (!permissions.HasFlag(ClientPermissions.ManageRound)) return false; - RequestRoundEnd(); + if (!permissions.HasFlag(ClientPermissions.ManageRound)) { return false; } + if (!Submarine.MainSub.AtStartPosition && !Submarine.MainSub.AtEndPosition) + { + var msgBox = new GUIMessageBox("", TextManager.Get("EndRoundSubNotAtLevelEnd"), + new string[] { TextManager.Get("Yes"), TextManager.Get("No") }); + msgBox.Buttons[0].OnClicked = (_, __) => + { + GameMain.Client.RequestRoundEnd(); + return true; + }; + msgBox.Buttons[0].OnClicked += msgBox.Close; + msgBox.Buttons[1].OnClicked += msgBox.Close; + } + else + { + RequestRoundEnd(); + } return true; }, Visible = false @@ -1267,6 +1282,75 @@ namespace Barotrauma.Networking } } + private void ReadLobbyUpdate(NetIncomingMessage inc) + { + UInt16 listId = inc.ReadUInt16(); + List tempClients = new List(); + int clientCount = inc.ReadByte(); + for (int i = 0; i < clientCount; i++) + { + byte id = inc.ReadByte(); + string name = inc.ReadString(); + UInt16 characterID = inc.ReadUInt16(); + bool muted = inc.ReadBoolean(); + inc.ReadPadBits(); + + tempClients.Add(new TempClient + { + ID = id, + Name = name, + CharacterID = characterID, + Muted = muted + }); + } + + if (NetIdUtils.IdMoreRecent(listId, LastClientListUpdateID)) + { + bool updateClientListId = true; + List currentClients = new List(); + foreach (TempClient tc in tempClients) + { + //see if the client already exists + var existingClient = ConnectedClients.Find(c => c.ID == tc.ID && c.Name == tc.Name); + if (existingClient == null) //if not, create it + { + existingClient = new Client(tc.Name, tc.ID) + { + Muted = tc.Muted + }; + ConnectedClients.Add(existingClient); + GameMain.NetLobbyScreen.AddPlayer(existingClient); + } + existingClient.Character = null; + existingClient.Muted = tc.Muted; + if (tc.CharacterID > 0) + { + existingClient.Character = Entity.FindEntityByID(tc.CharacterID) as Character; + if (existingClient.Character == null) + { + updateClientListId = false; + } + } + if (existingClient.ID == myID) + { + existingClient.SetPermissions(permissions, permittedConsoleCommands); + } + currentClients.Add(existingClient); + } + //remove clients that aren't present anymore + for (int i = ConnectedClients.Count - 1; i >= 0; i--) + { + if (!currentClients.Contains(ConnectedClients[i])) + { + GameMain.NetLobbyScreen.RemovePlayer(ConnectedClients[i]); + ConnectedClients[i].Dispose(); + ConnectedClients.RemoveAt(i); + } + } + if (updateClientListId) LastClientListUpdateID = listId; + } + } + private void ReadLobbyUpdate(NetIncomingMessage inc) { ServerNetObject objHeader; @@ -1429,7 +1513,11 @@ namespace Barotrauma.Networking break; case ServerNetObject.ENTITY_EVENT: case ServerNetObject.ENTITY_EVENT_INITIAL: - if (!entityEventManager.Read(objHeader, inc, sendingTime, entities)) { break; } + if (!entityEventManager.Read(objHeader, inc, sendingTime, entities)) + { + eventReadFailed = true; + break; + } break; case ServerNetObject.CHAT_MESSAGE: ChatMessage.ClientRead(inc); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs index 827b8af57..3c322d12d 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs @@ -457,11 +457,14 @@ namespace Barotrauma { IgnoreLayoutGroups = true, OnClicked = (GUIButton btn, object obj) => { StartRound?.Invoke(); return true; }, - Enabled = true, - Visible = GameMain.Client == null || - GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) || - GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign) + Enabled = true }; + if (GameMain.Client != null) + { + startButton.Visible = !GameMain.Client.GameStarted && + (GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) || + GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)); + } } OnLocationSelected?.Invoke(location, connection); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index e249c088f..02f6ba21c 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -726,6 +726,12 @@ namespace Barotrauma spectateButton.Visible = GameMain.Client.GameStarted; ReadyToStartBox.Visible = !GameMain.Client.GameStarted; ReadyToStartBox.Selected = false; + if (campaignUI?.StartButton != null) + { + campaignUI.StartButton.Visible = !GameMain.Client.GameStarted && + (GameMain.Client.HasPermission(ClientPermissions.ManageRound) || + GameMain.Client.HasPermission(ClientPermissions.ManageCampaign)); + } GameMain.Client.SetReadyToStart(ReadyToStartBox); } else @@ -847,9 +853,9 @@ namespace Barotrauma if (campaignUI?.StartButton != null) { - campaignUI.StartButton.Visible = - GameMain.Client.HasPermission(ClientPermissions.ManageRound) || - GameMain.Client.HasPermission(ClientPermissions.ManageCampaign); + campaignUI.StartButton.Visible = !GameMain.Client.GameStarted && + (GameMain.Client.HasPermission(ClientPermissions.ManageRound) || + GameMain.Client.HasPermission(ClientPermissions.ManageCampaign)); } } diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs index dd514f544..202c87097 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs @@ -813,7 +813,16 @@ namespace Barotrauma.Networking Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error); GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:LevelsDontMatch" + error, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr); - KickClient(c, errorStr); + + if (c.Connection == OwnerConnection) + { + SendDirectChatMessage(errorStr, c, ChatMessageType.MessageBox); + EndGame(); + } + else + { + KickClient(c, errorStr); + } } public override void CreateEntityEvent(INetSerializable entity, object[] extraData = null) @@ -1030,7 +1039,7 @@ namespace Barotrauma.Networking if (command == ClientPermissions.ManageRound && inc.PeekBoolean() && GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign) { - if (!mpCampaign.AllowedToEndRound(sender.Character)) + if (!mpCampaign.AllowedToEndRound(sender.Character) && !sender.HasPermission(command)) { return; } diff --git a/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs index 1aa0e23cf..d82b66631 100644 --- a/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs +++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs @@ -103,12 +103,6 @@ namespace Barotrauma if (missionType == MissionType.Random) { allowedMissions.AddRange(MissionPrefab.List); -#if SERVER - if (GameMain.Server != null) - { - allowedMissions.RemoveAll(mission => !GameMain.Server.ServerSettings.AllowedRandomMissionTypes.Contains(mission.type)); - } -#endif } else if (missionType == MissionType.None) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 32cb6f3be..11c413b9b 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -211,14 +211,14 @@ namespace Barotrauma set { spriteColor = value; } } - [Serialize("1.0,1.0,1.0,1.0", false), Editable] + [Serialize("1.0,1.0,1.0,1.0", true), Editable] public Color InventoryIconColor { get; protected set; } - [Serialize("1.0,1.0,1.0,1.0", false), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")] + [Serialize("1.0,1.0,1.0,1.0", true), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")] public Color ContainerColor { get; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Map/Location.cs b/Barotrauma/BarotraumaShared/Source/Map/Map/Location.cs index f507b0796..5c2a35a8c 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Map/Location.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Map/Location.cs @@ -33,7 +33,7 @@ namespace Barotrauma get { CheckMissionCompleted(); - + for (int i = availableMissions.Count; i < Connections.Count * 2; i++) { int seed = (ToolBox.StringToInt(BaseName) + MissionsCompleted * 10 + i) % int.MaxValue; @@ -47,7 +47,7 @@ namespace Barotrauma if (availableMissions.Any(m => m.Prefab == mission.Prefab)) { continue; } if (GameSettings.VerboseLogging && mission != null) { - DebugConsole.NewMessage("Generated a new mission for a location connection (seed: " + seed.ToString("X") + ", type: " + mission.Name + ")", Color.White); + DebugConsole.NewMessage("Generated a new mission for a location (location: " + Name + ", seed: " + seed.ToString("X") + ", missions completed: " + MissionsCompleted + ", type: " + mission.Name + ")", Color.White); } availableMissions.Add(mission); } @@ -100,7 +100,16 @@ namespace Barotrauma public void ChangeType(LocationType newType) { - if (newType == Type) return; + if (newType == Type) { return; } + + //clear missions from this and adjacent locations (they may be invalid now) + availableMissions.Clear(); + foreach (LocationConnection connection in Connections) + { + connection.OtherLocation(this)?.availableMissions.Clear(); + } + + DebugConsole.Log("Location " + baseName + " changed it's type from " + Type + " to " + newType); Type = newType; Name = Type.NameFormats[nameFormatIndex % Type.NameFormats.Count].Replace("[name]", baseName); @@ -112,6 +121,7 @@ namespace Barotrauma { if (mission.Completed) { + DebugConsole.Log("Mission \"" + mission.Name + "\" completed in \"" + Name + "\"."); MissionsCompleted++; } } @@ -119,7 +129,7 @@ namespace Barotrauma availableMissions.RemoveAll(m => m.Completed); } - private string RandomName(LocationType type) + public void Remove() { baseName = type.GetRandomName(); nameFormatIndex = Rand.Int(type.NameFormats.Count, Rand.RandSync.Server); diff --git a/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs b/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs index 822be89c9..601318392 100644 --- a/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs +++ b/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs @@ -32,6 +32,7 @@ namespace Barotrauma public static void SaveGame(string filePath) { + DebugConsole.Log("Saving the game to: " + filePath); Directory.CreateDirectory(TempPath); try { @@ -111,6 +112,7 @@ namespace Barotrauma public static XDocument LoadGameSessionDoc(string filePath) { + DebugConsole.Log("Loading game session doc: " + filePath); try { DecompressToDirectory(filePath, TempPath, null); @@ -401,18 +403,18 @@ namespace Barotrauma } } - public static void ClearFolder(string FolderName, string[] ignoredFiles = null) + public static void ClearFolder(string FolderName, string[] ignoredFileNames = null) { DirectoryInfo dir = new DirectoryInfo(FolderName); foreach (FileInfo fi in dir.GetFiles()) { - if (ignoredFiles != null) + if (ignoredFileNames != null) { bool ignore = false; - foreach (string ignoredFile in ignoredFiles) + foreach (string ignoredFile in ignoredFileNames) { - if (Path.GetFullPath(fi.FullName).Equals(Path.GetFullPath(ignoredFile))) + if (Path.GetFileName(fi.FullName).Equals(Path.GetFileName(ignoredFile))) { ignore = true; break;