v1.6.17.0 (Unto the Breach update)

This commit is contained in:
Regalis11
2024-10-22 17:29:04 +03:00
parent e74b3cdb17
commit 6e6c17e100
417 changed files with 17166 additions and 5870 deletions
@@ -260,7 +260,7 @@ namespace Barotrauma.Networking
{
try
{
Directory.CreateDirectory(downloadFolder);
Directory.CreateDirectory(downloadFolder, catchUnauthorizedAccessExceptions: false);
}
catch (Exception e)
{
@@ -572,7 +572,7 @@ namespace Barotrauma.Networking
{
try
{
File.Delete(transfer.FilePath);
File.Delete(transfer.FilePath, catchUnauthorizedAccessExceptions: false);
}
catch (Exception e)
{
@@ -7,9 +7,11 @@ using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.PerkBehaviors;
namespace Barotrauma.Networking
{
@@ -41,8 +43,9 @@ namespace Barotrauma.Networking
nameId++;
}
public void ForceNameAndJobUpdate()
public void ForceNameJobTeamUpdate()
{
// Triggers SendLobbyUpdate() which causes the server to call GameServer.ClientReadLobby()
nameId++;
}
@@ -137,13 +140,14 @@ namespace Barotrauma.Networking
}
}
public Client MyClient => ConnectedClients.FirstOrDefault(c => c.SessionId == SessionId);
public Option<int> Ping
{
get
{
Client selfClient = ConnectedClients.FirstOrDefault(c => c.SessionId == SessionId);
if (selfClient is null || selfClient.Ping == 0) { return Option<int>.None(); }
return Option<int>.Some(selfClient.Ping);
if (MyClient is null || MyClient.Ping == 0) { return Option<int>.None(); }
return Option<int>.Some(MyClient.Ping);
}
}
@@ -485,14 +489,13 @@ namespace Barotrauma.Networking
{
if (VoipCapture.Instance.LastEnqueueAudio > DateTime.Now - new TimeSpan(0, 0, 0, 0, milliseconds: 100))
{
var myClient = ConnectedClients.Find(c => c.SessionId == SessionId);
if (Screen.Selected == GameMain.NetLobbyScreen)
{
GameMain.NetLobbyScreen.SetPlayerSpeaking(myClient);
GameMain.NetLobbyScreen.SetPlayerSpeaking(MyClient);
}
else
{
GameMain.GameSession?.CrewManager?.SetClientSpeaking(myClient);
GameMain.GameSession?.CrewManager?.SetClientSpeaking(MyClient);
}
}
}
@@ -689,6 +692,16 @@ namespace Barotrauma.Networking
string subName = inc.ReadString();
string subHash = inc.ReadString();
bool hasEnemySub = inc.ReadBoolean();
string enemySubName = subName;
string enemySubHash = subHash;
if (hasEnemySub)
{
enemySubName = inc.ReadString();
enemySubHash = inc.ReadString();
}
bool usingShuttle = inc.ReadBoolean();
string shuttleName = inc.ReadString();
string shuttleHash = inc.ReadString();
@@ -709,8 +722,13 @@ namespace Barotrauma.Networking
bool readyToStart;
if (campaign == null && campaignID == 0)
{
readyToStart = GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList) &&
GameMain.NetLobbyScreen.TrySelectSub(shuttleName, shuttleHash, GameMain.NetLobbyScreen.ShuttleList.ListBox);
readyToStart = GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, SelectedSubType.Sub, GameMain.NetLobbyScreen.SubList) &&
GameMain.NetLobbyScreen.TrySelectSub(shuttleName, shuttleHash, SelectedSubType.Shuttle, GameMain.NetLobbyScreen.ShuttleList.ListBox);
if (hasEnemySub && !GameMain.NetLobbyScreen.TrySelectSub(enemySubName, enemySubHash, SelectedSubType.EnemySub, GameMain.NetLobbyScreen.SubList))
{
readyToStart = false;
}
}
else
{
@@ -733,6 +751,19 @@ namespace Barotrauma.Networking
CoroutineManager.StartCoroutine(NetLobbyScreen.WaitForStartRound(startButton: null), "WaitForStartRound");
}
break;
case ServerPacketHeader.WARN_STARTGAME:
DebugConsole.Log("Received WARN_STARTGAME packet.");
RoundStartWarningData warningData = INetSerializableStruct.Read<RoundStartWarningData>(inc);
var team1IncompatiblePerks = ToolBox.UintIdentifierArrayToPrefabCollection(DisembarkPerkPrefab.Prefabs, warningData.Team1IncompatiblePerks);
var team2IncompatiblePerks = ToolBox.UintIdentifierArrayToPrefabCollection(DisembarkPerkPrefab.Prefabs, warningData.Team2IncompatiblePerks);
GameMain.NetLobbyScreen?.ShowStartRoundWarning(SerializableDateTime.UtcNow + TimeSpan.FromSeconds(warningData.RoundStartsAnywaysTimeInSeconds), warningData.Team1Sub, team1IncompatiblePerks, warningData.Team2Sub, team2IncompatiblePerks);
break;
case ServerPacketHeader.CANCEL_STARTGAME:
DebugConsole.Log("Received CANCEL_STARTGAME packet.");
GameMain.NetLobbyScreen?.CloseStartRoundWarning();
break;
case ServerPacketHeader.STARTGAME:
DebugConsole.Log("Received STARTGAME packet.");
if (Screen.Selected == GameMain.GameScreen && GameMain.GameSession?.GameMode is CampaignMode)
@@ -869,6 +900,9 @@ namespace Barotrauma.Networking
case ServerPacketHeader.EVENTACTION:
GameMain.GameSession?.EventManager.ClientRead(inc);
break;
case ServerPacketHeader.SEND_BACKUP_INDICES:
GameMain.NetLobbyScreen?.CampaignSetupUI?.OnBackupIndicesReceived(inc);
break;
}
}
@@ -952,7 +986,7 @@ namespace Barotrauma.Networking
", server value " + stage + ": " + levelEqualityCheckValues[stage].ToString("X") +
", level value count: " + levelEqualityCheckValues.Count +
", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")" +
", sub: " + (Submarine.MainSub == null ? "null" : (Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")")) +
", mirrored: " + Level.Loaded.Mirrored + "). Round init status: " + roundInitStatus + "." + campaignErrorInfo;
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg);
@@ -969,9 +1003,24 @@ namespace Barotrauma.Networking
CrewManager.ClientReadActiveOrders(inc);
}
if (inc.ReadBoolean())
{
ApplyDisembarkPerk();
}
roundInitStatus = RoundInitStatus.Started;
}
private void ApplyDisembarkPerk()
{
var characters = GameSession.GetSessionCrewCharacters(CharacterType.Both);
ImmutableArray<Character> team1Characters = characters.Where(static c => c.TeamID is CharacterTeamType.Team1).ToImmutableArray(),
team2Characters = characters.Where(static c => c.TeamID is CharacterTeamType.Team2).ToImmutableArray();
GameSession.GetPerks().ApplyAll(team1Characters, team2Characters);
}
/// <summary>
/// Fires when the ClientPeer gets disconnected from the server. Does not necessarily mean the client is shutting down, we may still be able to reconnect.
/// </summary>
@@ -1396,6 +1445,7 @@ namespace Barotrauma.Networking
ServerSettings.LockAllDefaultWires = inc.ReadBoolean();
ServerSettings.AllowLinkingWifiToChat = inc.ReadBoolean();
ServerSettings.MaximumMoneyTransferRequest = inc.ReadInt32();
ServerSettings.RespawnMode = (RespawnMode)inc.ReadByte();
bool usingShuttle = GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
ServerSettings.ShowEnemyHealthBars = (EnemyHealthBarMode)inc.ReadByte();
@@ -1419,19 +1469,38 @@ namespace Barotrauma.Networking
string subHash = inc.ReadString();
string shuttleName = inc.ReadString();
string shuttleHash = inc.ReadString();
bool hasEnemySub = inc.ReadBoolean();
string enemySubName = subName;
string enemySubHash = subHash;
if (hasEnemySub)
{
enemySubName = inc.ReadString();
enemySubHash = inc.ReadString();
}
List<UInt32> missionHashes = new List<UInt32>();
int missionCount = inc.ReadByte();
for (int i = 0; i < missionCount; i++)
{
missionHashes.Add(inc.ReadUInt32());
}
if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList))
if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, SelectedSubType.Sub, GameMain.NetLobbyScreen.SubList))
{
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Success;
}
if (!GameMain.NetLobbyScreen.TrySelectSub(shuttleName, shuttleHash, GameMain.NetLobbyScreen.ShuttleList.ListBox))
if (hasEnemySub)
{
if (!GameMain.NetLobbyScreen.TrySelectSub(enemySubName, enemySubHash, SelectedSubType.EnemySub, GameMain.NetLobbyScreen.SubList))
{
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Success;
}
}
if (!GameMain.NetLobbyScreen.TrySelectSub(shuttleName, shuttleHash, SelectedSubType.Shuttle, GameMain.NetLobbyScreen.ShuttleList.ListBox))
{
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Success;
@@ -1480,8 +1549,10 @@ namespace Barotrauma.Networking
var selectedMissions = missionHashes.Select(i => MissionPrefab.Prefabs.Find(p => p.UintIdentifier == i));
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, gameMode, missionPrefabs: selectedMissions);
GameMain.GameSession.StartRound(levelSeed, levelDifficulty);
var selectedEnemySub = hasEnemySub && GameMain.NetLobbyScreen.SelectedEnemySub is { } enemySub ? Option.Some(enemySub) : Option.None;
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, selectedEnemySub, gameMode, missionPrefabs: selectedMissions);
GameMain.GameSession.StartRound(levelSeed, levelDifficulty, levelGenerationParams: null, forceBiome: ServerSettings.Biome);
}
else
{
@@ -1667,7 +1738,8 @@ namespace Barotrauma.Networking
}
}
if (GameMain.GameSession.Submarine.Info.IsFileCorrupted)
if (GameMain.GameSession.Submarine != null &&
GameMain.GameSession.Submarine.Info.IsFileCorrupted)
{
DebugConsole.ThrowError($"Failed to start a round. Could not load the submarine \"{GameMain.GameSession.Submarine.Info.Name}\".");
yield return CoroutineStatus.Failure;
@@ -1759,12 +1831,15 @@ namespace Barotrauma.Networking
refSub = Submarine.MainSubs[1];
}
// Enable characters near the main sub for the endCinematic
foreach (Character c in Character.CharacterList)
if (refSub != null)
{
if (Vector2.DistanceSquared(refSub.WorldPosition, c.WorldPosition) < MathUtils.Pow2(c.Params.DisableDistance))
// Enable characters near the main sub for the endCinematic
foreach (Character c in Character.CharacterList)
{
c.Enabled = true;
if (Vector2.DistanceSquared(refSub.WorldPosition, c.WorldPosition) < MathUtils.Pow2(c.Params.DisableDistance))
{
c.Enabled = true;
}
}
}
@@ -1851,6 +1926,8 @@ namespace Barotrauma.Networking
{
bool refreshCampaignUI = false;
UInt16 listId = inc.ReadUInt16();
GameMain.NetLobbyScreen.Team1Count = inc.ReadByte();
GameMain.NetLobbyScreen.Team2Count = inc.ReadByte();
List<TempClient> tempClients = new List<TempClient>();
int clientCount = inc.ReadByte();
for (int i = 0; i < clientCount; i++)
@@ -1883,19 +1960,30 @@ namespace Barotrauma.Networking
existingClient.NameId = tc.NameId;
existingClient.PreferredJob = tc.PreferredJob;
existingClient.PreferredTeam = tc.PreferredTeam;
existingClient.TeamID = tc.TeamID;
existingClient.Character = null;
existingClient.Karma = tc.Karma;
existingClient.Muted = tc.Muted;
existingClient.InGame = tc.InGame;
existingClient.IsOwner = tc.IsOwner;
existingClient.IsDownloading = tc.IsDownloading;
GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient);
GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient); // refresh lobby player list in the local UI
if (Screen.Selected != GameMain.NetLobbyScreen && tc.CharacterId > 0)
{
existingClient.CharacterID = tc.CharacterId;
}
if (existingClient.SessionId == SessionId)
{
MultiplayerPreferences.Instance.TeamPreference = existingClient.PreferredTeam;
// If a team is already selected, make sure the UI reflects it
if (MultiplayerPreferences.Instance.TeamPreference != CharacterTeamType.None)
{
GameMain.NetLobbyScreen.TeamPreferenceListBox?.Select(MultiplayerPreferences.Instance.TeamPreference);
}
else
{
GameMain.NetLobbyScreen.RefreshPvpTeamSelectionButtons();
}
existingClient.SetPermissions(permissions, permittedConsoleCommands);
if (!NetIdUtils.IdMoreRecent(nameId, tc.NameId))
{
@@ -1950,6 +2038,7 @@ namespace Barotrauma.Networking
Steam.SteamManager.UpdateLobby(ServerSettings);
}
GameMain.NetLobbyScreen?.UpdateDisembarkPointListFromServerSettings();
}
if (refreshCampaignUI)
@@ -1960,6 +2049,7 @@ namespace Barotrauma.Networking
campaign.CampaignUI?.HRManagerUI?.RefreshUI();
}
}
}
private bool initialUpdateReceived;
@@ -1997,6 +2087,15 @@ namespace Barotrauma.Networking
string selectSubName = inc.ReadString();
string selectSubHash = inc.ReadString();
bool usingEnemySub = inc.ReadBoolean();
string selectEnemySubName = selectSubName;
string selectEnemySubHash = selectSubHash;
if (usingEnemySub)
{
selectEnemySubName = inc.ReadString();
selectEnemySubHash = inc.ReadString();
}
bool usingShuttle = inc.ReadBoolean();
string selectShuttleName = inc.ReadString();
string selectShuttleHash = inc.ReadString();
@@ -2011,7 +2110,13 @@ namespace Barotrauma.Networking
float traitorProbability = inc.ReadSingle();
int traitorDangerLevel = inc.ReadRangedInteger(TraitorEventPrefab.MinDangerLevel, TraitorEventPrefab.MaxDangerLevel);
MissionType missionType = (MissionType)inc.ReadRangedInteger(0, (int)MissionType.All);
List<Identifier> missionTypes = new List<Identifier>();
uint missionTypeCount = inc.ReadVariableUInt32();
for (int i = 0; i < missionTypeCount; i++)
{
missionTypes.Add(inc.ReadIdentifier());
}
int modeIndex = inc.ReadByte();
string levelSeed = inc.ReadString();
@@ -2048,12 +2153,19 @@ namespace Barotrauma.Networking
ServerSettings.ServerLog.ServerName = ServerSettings.ServerName;
GameMain.NetLobbyScreen.UsingShuttle = usingShuttle;
if (!allowSubVoting || GameMain.NetLobbyScreen.SelectedSub == null) { GameMain.NetLobbyScreen.TrySelectSub(selectSubName, selectSubHash, GameMain.NetLobbyScreen.SubList); }
GameMain.NetLobbyScreen.TrySelectSub(selectShuttleName, selectShuttleHash, GameMain.NetLobbyScreen.ShuttleList.ListBox);
if (!allowSubVoting || GameMain.NetLobbyScreen.SelectedSub == null)
{
GameMain.NetLobbyScreen.TrySelectSub(selectSubName, selectSubHash, SelectedSubType.Sub, GameMain.NetLobbyScreen.SubList);
if (usingEnemySub)
{
GameMain.NetLobbyScreen.TrySelectSub(selectEnemySubName, selectEnemySubHash, SelectedSubType.EnemySub, GameMain.NetLobbyScreen.SubList);
}
}
GameMain.NetLobbyScreen.TrySelectSub(selectShuttleName, selectShuttleHash, SelectedSubType.Shuttle, GameMain.NetLobbyScreen.ShuttleList.ListBox);
GameMain.NetLobbyScreen.SetTraitorProbability(traitorProbability);
GameMain.NetLobbyScreen.SetTraitorDangerLevel(traitorDangerLevel);
GameMain.NetLobbyScreen.SetMissionType(missionType);
GameMain.NetLobbyScreen.SetMissionTypes(missionTypes);
GameMain.NetLobbyScreen.LevelSeed = levelSeed;
GameMain.NetLobbyScreen.SelectMode(modeIndex);
@@ -2489,14 +2601,20 @@ namespace Barotrauma.Networking
((SubmarineInfo)c.UserData).MD5Hash.StringRepresentation == newSub.MD5Hash.StringRepresentation);
if (subElement == null) { continue; }
Color newSubTextColor = new Color(subElement.GetChild<GUITextBlock>().TextColor, 1.0f);
subElement.GetChild<GUITextBlock>().TextColor = newSubTextColor;
if (subElement.GetChildByUserData("classtext") is GUITextBlock classTextBlock)
//set the dimmed out submarine info back to normal and update texts
if (subElement.FindChild("nametext", recursive: true) is GUITextBlock nameTextBlock)
{
nameTextBlock.TextColor = new Color(nameTextBlock.TextColor, 1.0f);
}
if (subElement.FindChild("classtext", recursive: true) is GUITextBlock classTextBlock)
{
Color newSubClassTextColor = new Color(classTextBlock.TextColor, 0.8f);
classTextBlock.Text = TextManager.Get($"submarineclass.{newSub.SubmarineClass}");
classTextBlock.TextColor = newSubClassTextColor;
classTextBlock.TextColor = new Color(classTextBlock.TextColor, 0.8f);
}
if (subElement.FindChild("pricetext", recursive: true) is GUITextBlock priceTextBlock)
{
priceTextBlock.Text = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", newSub.Price));
priceTextBlock.TextColor = new Color(priceTextBlock.TextColor, 0.8f);
}
subElement.UserData = newSub;
@@ -2507,14 +2625,22 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.FailedSelectedSub.Value.Name == newSub.Name &&
GameMain.NetLobbyScreen.FailedSelectedSub.Value.Hash == newSub.MD5Hash.StringRepresentation)
{
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.StringRepresentation, GameMain.NetLobbyScreen.SubList);
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.StringRepresentation, SelectedSubType.Sub, GameMain.NetLobbyScreen.SubList);
}
if (GameMain.NetLobbyScreen.FailedSelectedShuttle.HasValue &&
GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Name == newSub.Name &&
GameMain.NetLobbyScreen.FailedSelectedShuttle.Value.Hash == newSub.MD5Hash.StringRepresentation)
{
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.StringRepresentation, GameMain.NetLobbyScreen.ShuttleList.ListBox);
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.StringRepresentation, SelectedSubType.Shuttle, GameMain.NetLobbyScreen.ShuttleList.ListBox);
}
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.PvP &&
GameMain.NetLobbyScreen.FailedSelectedEnemySub.HasValue &&
GameMain.NetLobbyScreen.FailedSelectedEnemySub.Value.Name == newSub.Name &&
GameMain.NetLobbyScreen.FailedSelectedEnemySub.Value.Hash == newSub.MD5Hash.StringRepresentation)
{
GameMain.NetLobbyScreen.TrySelectSub(newSub.Name, newSub.MD5Hash.StringRepresentation, SelectedSubType.EnemySub, GameMain.NetLobbyScreen.SubList);
}
NetLobbyScreen.FailedSubInfo failedCampaignSub = GameMain.NetLobbyScreen.FailedCampaignSubs.Find(s => s.Name == newSub.Name && s.Hash == newSub.MD5Hash.StringRepresentation);
@@ -2547,20 +2673,20 @@ namespace Barotrauma.Networking
if (GameMain.GameSession?.GameMode is not MultiPlayerCampaign campaign || campaign.CampaignID != campaignID)
{
string savePath = transfer.FilePath;
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty);
GameMain.GameSession = new GameSession(null, Option.None, CampaignDataPath.CreateRegular(savePath), GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
GameMain.GameSession.SavePath = transfer.FilePath;
GameMain.GameSession.DataPath = CampaignDataPath.CreateRegular(transfer.FilePath);
if (GameMain.GameSession.SubmarineInfo == null || campaign.Map == null)
{
string subPath = Path.Combine(SaveUtil.TempPath, gameSessionDocRoot.GetAttributeString("submarine", "")) + ".sub";
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(subPath, "");
}
campaign.LoadState(GameMain.GameSession.SavePath);
campaign.LoadState(GameMain.GameSession.DataPath.LoadPath);
GameMain.GameSession?.SubmarineInfo?.Reload();
GameMain.GameSession?.SubmarineInfo?.CheckSubsLeftBehind();
@@ -2577,7 +2703,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.Select();
}
DebugConsole.Log("Campaign save received (" + GameMain.GameSession.SavePath + "), save ID " + campaign.LastSaveID);
DebugConsole.Log("Campaign save received (" + GameMain.GameSession.DataPath + "), save ID " + campaign.LastSaveID);
//decrement campaign update IDs so the server will send us the latest data
//(as there may have been campaign updates after the save file was created)
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
@@ -2858,14 +2984,14 @@ namespace Barotrauma.Networking
/// <summary>
/// Tell the server to select a submarine (permission required)
/// </summary>
public void RequestSelectSub(SubmarineInfo sub, bool isShuttle)
public void RequestSelectSub(SubmarineInfo sub, SelectedSubType type)
{
if (!HasPermission(ClientPermissions.SelectSub) || sub == null) { return; }
IWriteMessage msg = new WriteOnlyMessage();
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
msg.WriteUInt16((UInt16)ClientPermissions.SelectSub);
msg.WriteBoolean(isShuttle); msg.WritePadBits();
msg.WriteUInt16((ushort)ClientPermissions.SelectSub);
msg.WriteByte((byte)type);
msg.WriteString(sub.MD5Hash.StringRepresentation);
ClientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -2909,7 +3035,7 @@ namespace Barotrauma.Networking
ClientPeer.Send(msg, DeliveryMethod.Reliable);
}
public void SetupLoadCampaign(string saveName)
public void SetupLoadCampaign(string filePath, Option<uint> backupIndex)
{
if (ClientPeer == null) { return; }
@@ -2920,7 +3046,19 @@ namespace Barotrauma.Networking
msg.WriteByte((byte)ClientPacketHeader.CAMPAIGN_SETUP_INFO);
msg.WriteBoolean(false); msg.WritePadBits();
msg.WriteString(saveName);
msg.WriteString(filePath);
if (backupIndex.TryUnwrap(out uint index))
{
msg.WriteBoolean(true);
msg.WritePadBits();
msg.WriteUInt32(index);
}
else
{
msg.WriteBoolean(false);
msg.WritePadBits();
}
ClientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -3062,7 +3200,8 @@ namespace Barotrauma.Networking
public bool EnterChatMessage(GUITextBox textBox, string message)
{
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
var messageType = NetLobbyScreen.TeamChatSelected ? ChatMessageType.Team : ChatMessageType.Default;
textBox.TextColor = ChatMessage.MessageColor[(int)messageType];
if (string.IsNullOrWhiteSpace(message))
{
@@ -3070,7 +3209,7 @@ namespace Barotrauma.Networking
return false;
}
chatBox.ChatManager.Store(message);
SendChatMessage(message);
SendChatMessage(message, type: messageType);
if (textBox.DeselectAfterMessage)
{
@@ -3559,9 +3698,9 @@ namespace Barotrauma.Networking
{
errorLines.Add("Submarine: " + GameMain.GameSession.Submarine.Info.Name);
}
if (GameMain.NetworkMember?.RespawnManager?.RespawnShuttle != null)
if (GameMain.NetworkMember?.RespawnManager is { } respawnManager)
{
errorLines.Add("Respawn shuttle: " + GameMain.NetworkMember.RespawnManager.RespawnShuttle.Info.Name);
errorLines.Add("Respawn shuttles: " + string.Join(", ", respawnManager.RespawnShuttles.Select(s => s.Info.Name)));
}
if (Level.Loaded != null)
{
@@ -20,6 +20,8 @@ namespace Barotrauma.Networking
public bool AllowModDownloads { get; private set; } = true;
public string AutomaticallyAttemptedPassword = string.Empty;
public readonly record struct Callbacks(
Callbacks.MessageCallback OnMessageReceived,
Callbacks.DisconnectCallback OnDisconnect,
@@ -102,8 +104,9 @@ namespace Barotrauma.Networking
TaskPool.Add($"{GetType().Name}.{nameof(GetAccountId)}", GetAccountId(), t =>
{
if (GameMain.Client?.ClientPeer is null) { return; }
// FIXME what to do with this?
//if (GameMain.Client?.ClientPeer is null) { return; }
if (!t.TryGetResult(out Option<AccountId> accountId))
{
Close(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
@@ -118,7 +121,7 @@ namespace Barotrauma.Networking
var body = new ClientAuthTicketAndVersionPacket
{
Name = GameMain.Client.Name,
Name = GameMain.Client?.Name ?? "Unknown",
OwnerKey = ownerKey,
AccountId = accountId,
AuthTicket = authTicket,
@@ -177,10 +180,16 @@ namespace Barotrauma.Networking
var passwordPacket = INetSerializableStruct.Read<ServerPeerPasswordPacket>(inc.Message);
if (WaitingForPassword) { return; }
passwordPacket.Salt.TryUnwrap(out passwordSalt);
passwordPacket.RetriesLeft.TryUnwrap(out var retries);
if (!string.IsNullOrWhiteSpace(AutomaticallyAttemptedPassword))
{
SendPassword(AutomaticallyAttemptedPassword);
return;
}
LocalizedString pwMsg = TextManager.Get("PasswordRequired");
passwordMsgBox?.Close();
@@ -224,10 +224,13 @@ namespace Barotrauma.Networking
ToolBox.ThrowIfNull(netPeerConfiguration);
#if DEBUG
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Client.SimulatedDuplicatesChance;
netPeerConfiguration.SimulatedMinimumLatency = GameMain.Client.SimulatedMinimumLatency;
netPeerConfiguration.SimulatedRandomLatency = GameMain.Client.SimulatedRandomLatency;
netPeerConfiguration.SimulatedLoss = GameMain.Client.SimulatedLoss;
if (GameMain.Client != null)
{
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Client.SimulatedDuplicatesChance;
netPeerConfiguration.SimulatedMinimumLatency = GameMain.Client.SimulatedMinimumLatency;
netPeerConfiguration.SimulatedRandomLatency = GameMain.Client.SimulatedRandomLatency;
netPeerConfiguration.SimulatedLoss = GameMain.Client.SimulatedLoss;
}
#endif
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
@@ -22,6 +22,12 @@ namespace Barotrauma.Networking
get; private set;
}
public DateTime ReturnTime { get; private set; }
public DateTime RespawnTime { get; private set; }
public State CurrentState { get; private set; }
public bool ReturnCountdownStarted { get; private set; }
public bool RespawnCountdownStarted { get; private set; }
public static void ShowDeathPromptIfNeeded(float delay = 1.0f)
{
if (UseDeathPrompt)
@@ -30,13 +36,18 @@ namespace Barotrauma.Networking
}
}
partial void UpdateTransportingProjSpecific(float deltaTime)
partial void UpdateTransportingProjSpecific(TeamSpecificState teamSpecificState, float deltaTime)
{
if (GameMain.Client?.Character == null || GameMain.Client.Character.Submarine != RespawnShuttle) { return; }
if (!ReturnCountdownStarted) { return; }
if (GameMain.Client?.Character == null ||
GameMain.Client.Character.Submarine is not { IsRespawnShuttle: true } ||
GameMain.Client.Character.TeamID != teamSpecificState.TeamID)
{
return;
}
if (!teamSpecificState.ReturnCountdownStarted) { return; }
//show a warning when there's 20 seconds until the shuttle leaves
if ((ReturnTime - DateTime.Now).TotalSeconds < 20.0f &&
if ((teamSpecificState.ReturnTime - DateTime.Now).TotalSeconds < 20.0f &&
(DateTime.Now - lastShuttleLeavingWarningTime).TotalSeconds > 30.0f)
{
lastShuttleLeavingWarningTime = DateTime.Now;
@@ -46,43 +57,56 @@ namespace Barotrauma.Networking
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
bool respawnPromptPending = false;
var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);
switch (newState)
var myTeamId = (CharacterTeamType)msg.ReadByte();
foreach (var teamSpecificState in teamSpecificStates.Values)
{
case State.Transporting:
ReturnCountdownStarted = msg.ReadBoolean();
maxTransportTime = msg.ReadSingle();
float transportTimeLeft = msg.ReadSingle();
var teamId = (CharacterTeamType)msg.ReadByte();
ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(transportTimeLeft * 1000.0f));
RespawnCountdownStarted = false;
if (CurrentState != newState)
{
CoroutineManager.StopCoroutines("forcepos");
}
break;
case State.Waiting:
PendingRespawnCount = msg.ReadUInt16();
RequiredRespawnCount = msg.ReadUInt16();
respawnPromptPending = msg.ReadBoolean();
RespawnCountdownStarted = msg.ReadBoolean();
ResetShuttle();
float newRespawnTime = msg.ReadSingle();
RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(newRespawnTime * 1000.0f));
break;
case State.Returning:
RespawnCountdownStarted = false;
break;
bool respawnPromptPending = false;
var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);
switch (newState)
{
case State.Transporting:
teamSpecificState.ReturnCountdownStarted = msg.ReadBoolean();
maxTransportTime = msg.ReadSingle();
float transportTimeLeft = msg.ReadSingle();
teamSpecificState.ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(transportTimeLeft * 1000.0f));
teamSpecificState.RespawnCountdownStarted = false;
break;
case State.Waiting:
teamSpecificState.PendingRespawnCount = msg.ReadUInt16();
teamSpecificState.RequiredRespawnCount = msg.ReadUInt16();
respawnPromptPending = msg.ReadBoolean();
teamSpecificState.RespawnCountdownStarted = msg.ReadBoolean();
ResetShuttle(teamSpecificState);
float newRespawnTime = msg.ReadSingle();
teamSpecificState.RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(newRespawnTime * 1000.0f));
break;
case State.Returning:
teamSpecificState.RespawnCountdownStarted = false;
break;
}
teamSpecificState.CurrentState = newState;
if (respawnPromptPending)
{
GameMain.Client.HasSpawned = true;
DeathPrompt.Create(delay: 1.0f);
}
if (teamId == myTeamId)
{
PendingRespawnCount = teamSpecificState.PendingRespawnCount;
RequiredRespawnCount = teamSpecificState.RequiredRespawnCount;
ReturnTime = teamSpecificState.ReturnTime;
RespawnTime = teamSpecificState.RespawnTime;
CurrentState = teamSpecificState.CurrentState;
ReturnCountdownStarted = teamSpecificState.ReturnCountdownStarted;
RespawnCountdownStarted = teamSpecificState.RespawnCountdownStarted;
}
}
CurrentState = newState;
if (respawnPromptPending)
{
GameMain.Client.HasSpawned = true;
DeathPrompt.Create(delay: 1.0f);
}
msg.ReadPadBits();
}
}
@@ -144,9 +144,9 @@ namespace Barotrauma.Networking
var pingLocation = NetPingLocation.TryParseFromString(pingLocationStr);
if (pingLocation.HasValue && Steamworks.SteamNetworkingUtils.LocalPingLocation.HasValue)
if (pingLocation.HasValue)
{
int ping = Steamworks.SteamNetworkingUtils.LocalPingLocation.Value.EstimatePingTo(pingLocation.Value);
int ping = Steamworks.SteamNetworkingUtils.EstimatePingTo(pingLocation.Value);
if (ping < 0) { return Result.Failure(SteamLobbyPingError.PingEstimationFailed); }
return Result.Success(ping);
}
@@ -1,6 +1,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Linq;
@@ -178,6 +179,11 @@ namespace Barotrauma.Networking
extraCargoPanel.Visible = true;
}
}
if (ReadPerks(incMsg))
{
GameMain.NetLobbyScreen?.UpdateDisembarkPointListFromServerSettings();
}
}
if (requiredFlags.HasFlag(NetFlags.HiddenSubs))
@@ -194,10 +200,41 @@ namespace Barotrauma.Networking
}
}
public static bool HasPermissionToChangePerks()
{
if (GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) { return true; }
bool isPvP = GameMain.NetLobbyScreen?.SelectedMode == GameModePreset.PvP;
bool hasSelectedTeam = MultiplayerPreferences.Instance.TeamPreference is CharacterTeamType.Team1 or CharacterTeamType.Team2;
var otherClients = GameMain.Client?.ConnectedClients.Where(static c => c.SessionId != GameMain.Client.SessionId).ToImmutableArray() ?? ImmutableArray<Client>.Empty;
if (isPvP)
{
if (!hasSelectedTeam) { return false; }
return !otherClients
.Where(static c => c.PreferredTeam == MultiplayerPreferences.Instance.TeamPreference)
.Any(static c => c.HasPermission(Networking.ClientPermissions.ManageSettings));
}
else
{
return !otherClients.Any(static c => c.HasPermission(Networking.ClientPermissions.ManageSettings));
}
}
public void ClientAdminWritePerks()
{
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.WriteByte((byte)ClientPacketHeader.SERVER_SETTINGS_PERKS);
WritePerks(outMsg);
GameMain.Client?.ClientPeer?.Send(outMsg, DeliveryMethod.Reliable);
}
public void ClientAdminWrite(
NetFlags dataToSend,
int? missionTypeOr = null,
int? missionTypeAnd = null,
Identifier addedMissionType = default,
Identifier removedMissionType = default,
int traitorDangerLevel = 0)
{
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) { return; }
@@ -220,7 +257,7 @@ namespace Barotrauma.Networking
outMsg.WriteUInt32(count);
foreach (KeyValuePair<UInt32, NetPropertyData> prop in changedProperties)
{
DebugConsole.NewMessage(prop.Value.Name.Value, Color.Lime);
DebugConsole.NewMessage($"Changed {prop.Value.Name.Value} to {prop.Value.GUIComponentValue}", Color.Lime);
outMsg.WriteUInt32(prop.Key);
prop.Value.Write(outMsg, prop.Value.GUIComponentValue);
}
@@ -237,8 +274,8 @@ namespace Barotrauma.Networking
if (dataToSend.HasFlag(NetFlags.Misc))
{
outMsg.WriteRangedInteger(missionTypeOr ?? (int)Barotrauma.MissionType.None, 0, (int)Barotrauma.MissionType.All);
outMsg.WriteRangedInteger(missionTypeAnd ?? (int)Barotrauma.MissionType.All, 0, (int)Barotrauma.MissionType.All);
outMsg.WriteIdentifier(addedMissionType);
outMsg.WriteIdentifier(removedMissionType);
outMsg.WriteByte((byte)(traitorDangerLevel + 1));
outMsg.WritePadBits();
}
@@ -418,8 +418,44 @@ namespace Barotrauma.Networking
var randomizeLevelBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsRandomizeSeed"));
AssignGUIComponent(nameof(RandomizeSeed), randomizeLevelBox);
//***********************************************
// ******* PVP ********************************
NetLobbyScreen.CreateSubHeader("gamemode.pvp", listBox.Content);
var teamSelectModeLabel = new GUITextBlock(
new RectTransform(new Vector2(1.0f, 0.05f),
listBox.Content.RectTransform),
TextManager.Get("TeamSelectionMode"));
teamSelectModeLabel.ToolTip = TextManager.Get("TeamSelectionMode.tooltip");
var teamSelectionMode = new GUISelectionCarousel<PvpTeamSelectionMode>(
new RectTransform(new Vector2(0.5f, 0.6f),
teamSelectModeLabel.RectTransform,
Anchor.CenterRight));
foreach (PvpTeamSelectionMode teamSelectionModeOption in Enum.GetValues(typeof(PvpTeamSelectionMode)))
{
var optionName = teamSelectionModeOption.ToString();
teamSelectionMode.AddElement(teamSelectionModeOption,
TextManager.Get($"TeamSelectionMode.{optionName}"),
TextManager.Get($"TeamSelectionMode.{optionName}.tooltip"));
}
AssignGUIComponent(nameof(PvpTeamSelectionMode), teamSelectionMode);
var autoBalanceThresholdLabel = new GUITextBlock(
new RectTransform(new Vector2(1.0f, 0.05f),
listBox.Content.RectTransform),
TextManager.Get("AutoBalanceThreshold"));
var autoBalanceThresholdTooltip = TextManager.Get("AutoBalanceThreshold.tooltip");
autoBalanceThresholdLabel.ToolTip = autoBalanceThresholdTooltip;
var autoBalanceThreshold = new GUISelectionCarousel<int>(
new RectTransform(new Vector2(0.5f, 0.6f),
autoBalanceThresholdLabel.RectTransform,
Anchor.CenterRight));
autoBalanceThreshold.AddElement(0, TextManager.Get($"AutoBalanceThreshold.Off"), autoBalanceThresholdTooltip);
autoBalanceThreshold.AddElement(1, "1", autoBalanceThresholdTooltip);
autoBalanceThreshold.AddElement(2, "2", autoBalanceThresholdTooltip);
autoBalanceThreshold.AddElement(3, "3", autoBalanceThresholdTooltip);
AssignGUIComponent(nameof(PvpAutoBalanceThreshold), autoBalanceThreshold);
// ******* GAMEPLAY ***************************
NetLobbyScreen.CreateSubHeader("serversettingsroundstab", listBox.Content);
var voiceChatEnabled = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
@@ -485,6 +521,9 @@ namespace Barotrauma.Networking
AssignGUIComponent(nameof(NewCampaignDefaultSalary), defaultSalarySlider);
defaultSalarySlider.OnMoved(defaultSalarySlider, defaultSalarySlider.BarScroll);
var pvpDisembarkPoints = NetLobbyScreen.CreateLabeledNumberInput(listBox.Content, "serversettingsdisembarkpoints", 0, 100, "serversettingsdisembarkpointstooltip");
AssignGUIComponent(nameof(DisembarkPointAllowance), pvpDisembarkPoints);
//--------------------------------------------------------------------------------
// game settings
//--------------------------------------------------------------------------------
@@ -320,7 +320,7 @@ namespace Barotrauma.Networking
private Sound overrideSound;
private int overridePos;
private short[] overrideBuf = new short[VoipConfig.BUFFER_SIZE];
private readonly short[] overrideBuf = new short[VoipConfig.BUFFER_SIZE];
private void FillBuffer()
{
@@ -331,13 +331,13 @@ namespace Barotrauma.Networking
{
int sampleCount = overrideSound.FillStreamBuffer(overridePos, overrideBuf);
overridePos += sampleCount * 2;
Array.Copy(overrideBuf, 0, uncompressedBuffer, totalSampleCount, sampleCount);
Array.Copy(overrideBuf, 0, uncompressedBuffer, totalSampleCount, Math.Min(sampleCount, uncompressedBuffer.Length - totalSampleCount));
totalSampleCount += sampleCount;
if (sampleCount == 0)
{
overridePos = 0;
}
}
}
int sleepMs = VoipConfig.BUFFER_SIZE * 800 / VoipConfig.FREQUENCY;
Thread.Sleep(sleepMs - 1);
@@ -382,7 +382,14 @@ namespace Barotrauma.Networking
}
else
{
overrideSound = GameMain.SoundManager.LoadSound(fileName, true);
try
{
overrideSound = GameMain.SoundManager.LoadSound(fileName, true);
}
catch (Exception e)
{
DebugConsole.ThrowError($"Failed to load the sound {fileName}.", e);
}
}
}
@@ -59,22 +59,77 @@ namespace Barotrauma
switch (voteType)
{
case VoteType.Sub:
case VoteType.Mode:
GUIListBox listBox = (voteType == VoteType.Sub) ?
GameMain.NetLobbyScreen.SubList : GameMain.NetLobbyScreen.ModeList;
var subList = GameMain.NetLobbyScreen.SubList;
foreach (GUIComponent comp in listBox.Content.Children)
foreach (GUIComponent comp in subList.Content.Children)
{
if (comp.FindChild("votes") is GUITextBlock voteText) { comp.RemoveChild(voteText); }
TryRemoveVoteText(comp);
var container = comp.GetChild<GUILayoutGroup>();
var imageFrame = container.GetChild<GUIFrame>();
var coalIcon = imageFrame.GetChildByUserData(NetLobbyScreen.CoalitionIconUserData);
var sepIcon = imageFrame.GetChildByUserData(NetLobbyScreen.SeparatistsIconUserData);
coalIcon.Enabled = false;
sepIcon.Enabled = false;
TryRemoveVoteText(coalIcon);
TryRemoveVoteText(sepIcon);
static void TryRemoveVoteText(GUIComponent component)
{
if (component.FindChild("votes") is GUITextBlock foundText)
{
component.RemoveChild(foundText);
}
}
}
if (clients == null) { return; }
IReadOnlyDictionary<object, int> voteList = GetVoteCounts<object>(voteType, clients);
foreach (KeyValuePair<object, int> votable in voteList)
bool isPvP = GameMain.NetLobbyScreen?.SelectedMode == GameModePreset.PvP;
if (isPvP)
{
SetVoteText(listBox, votable.Key, votable.Value);
}
var coalitionVoteList = GetVoteCounts<SubmarineInfo>(voteType, clients.Where(static c => c.PreferredTeam is CharacterTeamType.Team1));
var separatistVoteList = GetVoteCounts<SubmarineInfo>(voteType, clients.Where(static c => c.PreferredTeam is CharacterTeamType.Team2));
foreach (var (subInfo, amount) in coalitionVoteList)
{
SetSubVoteText(subList, subInfo, amount, CharacterTeamType.Team1);
}
foreach (var (subInfo, amount) in separatistVoteList)
{
SetSubVoteText(subList, subInfo, amount, CharacterTeamType.Team2);
}
}
else
{
var subVoteList = GetVoteCounts<SubmarineInfo>(voteType, clients);
foreach (var (subInfo, amount) in subVoteList)
{
SetSubVoteText(subList, subInfo, amount, CharacterTeamType.None);
}
}
break;
case VoteType.Mode:
var modeList = GameMain.NetLobbyScreen.ModeList;
foreach (GUIComponent comp in modeList.Content.Children)
{
if (comp.FindChild("votes") is GUITextBlock voteText)
{
comp.RemoveChild(voteText);
}
}
if (clients == null) { return; }
var modeVoteList = GetVoteCounts<GameModePreset>(voteType, clients);
foreach (var (preset, amount) in modeVoteList)
{
SetVoteText(modeList, preset, amount);
}
break;
case VoteType.StartRound:
if (clients == null) { return; }
@@ -90,12 +145,70 @@ namespace Barotrauma
}
}
private void SetSubVoteText(GUIListBox subListBox, SubmarineInfo userData, int votes, CharacterTeamType type)
{
GUIComponent subElement = subListBox.Content.GetChildByUserData(userData);
if (subElement is null)
{
DebugConsole.ThrowError("Failed to find the submarine element in the listbox");
return;
}
var (coalIcon, sepIcon) = GetPvPIcons(subElement);
switch (type)
{
case CharacterTeamType.None:
{
SetVoteText(subListBox, userData, votes);
break;
}
case CharacterTeamType.Team1:
{
coalIcon.Enabled = votes > 0;
CreateSubmarineVoteText(coalIcon, votes);
break;
}
case CharacterTeamType.Team2:
{
sepIcon.Enabled = votes > 0;
CreateSubmarineVoteText(sepIcon, votes);
break;
}
default:
return;
}
static void CreateSubmarineVoteText(GUIComponent parent, int votes)
{
if (parent is null) { return; }
var voteText = new GUITextBlock(new RectTransform(Vector2.One, parent.RectTransform, Anchor.TopLeft), $"{votes}", textAlignment: Alignment.Center)
{
Padding = Vector4.Zero,
UserData = "votes",
Shadow = true
};
voteText.RectTransform.RelativeOffset = new Vector2(0.33f, 0.33f);
}
static (GUIComponent CoalitionIcon, GUIComponent SeparatistsIcon) GetPvPIcons(GUIComponent child)
{
var container = child.GetChild<GUILayoutGroup>();
var imageFrame = container.GetChild<GUIFrame>();
var coalIcon = imageFrame.GetChildByUserData(NetLobbyScreen.CoalitionIconUserData);
var sepIcon = imageFrame.GetChildByUserData(NetLobbyScreen.SeparatistsIconUserData);
return (CoalitionIcon: coalIcon, SeparatistsIcon: sepIcon);
}
}
private void SetVoteText(GUIListBox listBox, object userData, int votes)
{
if (userData == null) { return; }
foreach (GUIComponent comp in listBox.Content.Children)
{
if (comp.UserData != userData) { continue; }
if (comp.FindChild("votes") is not GUITextBlock voteText)
{
voteText = new GUITextBlock(new RectTransform(new Point(GUI.IntScale(30), comp.Rect.Height), comp.RectTransform, Anchor.CenterRight),
@@ -197,28 +310,48 @@ namespace Barotrauma
msg.WritePadBits();
return true;
}
public void ClientRead(IReadMessage inc)
{
GameMain.Client.ServerSettings.AllowSubVoting = inc.ReadBoolean();
if (GameMain.Client.ServerSettings.AllowSubVoting)
{
UpdateVoteTexts(null, VoteType.Sub);
bool isMultiSub = inc.ReadBoolean();
int votableCount = inc.ReadByte();
List<SubmarineInfo> serversubs = new List<SubmarineInfo>();
if (GameMain.NetLobbyScreen?.SubList?.Content != null)
{
foreach (GUIComponent item in GameMain.NetLobbyScreen.SubList.Content.Children)
{
if (item.UserData is SubmarineInfo info)
{
serversubs.Add(info);
}
}
}
for (int i = 0; i < votableCount; i++)
{
int votes = inc.ReadByte();
string subName = inc.ReadString();
List<SubmarineInfo> serversubs = new List<SubmarineInfo>();
if (GameMain.NetLobbyScreen?.SubList?.Content != null)
{
foreach (GUIComponent item in GameMain.NetLobbyScreen.SubList.Content.Children)
{
if (item.UserData != null && item.UserData is SubmarineInfo) { serversubs.Add(item.UserData as SubmarineInfo); }
}
}
SubmarineInfo sub = serversubs.FirstOrDefault(s => s.Name == subName);
SetVoteText(GameMain.NetLobbyScreen.SubList, sub, votes);
SetSubVoteText(GameMain.NetLobbyScreen.SubList, sub, votes, isMultiSub ? CharacterTeamType.Team1 : CharacterTeamType.None);
}
if (isMultiSub)
{
int separatistsCount = inc.ReadByte();
for (int i = 0; i < separatistsCount; i++)
{
int votes = inc.ReadByte();
string subName = inc.ReadString();
SubmarineInfo sub = serversubs.FirstOrDefault(s => s.Name == subName);
SetSubVoteText(GameMain.NetLobbyScreen.SubList, sub, votes, CharacterTeamType.Team2);
}
}
}
GameMain.Client.ServerSettings.AllowModeVoting = inc.ReadBoolean();