Unstable 1.8.4.0
This commit is contained in:
@@ -120,7 +120,7 @@ namespace Barotrauma.Networking
|
||||
if (radioNoiseChannel == null || !radioNoiseChannel.IsPlaying)
|
||||
{
|
||||
radioNoiseChannel = SoundPlayer.PlaySound("radiostatic");
|
||||
radioNoiseChannel.Category = "voip";
|
||||
radioNoiseChannel.Category = SoundManager.SoundCategoryVoip;
|
||||
radioNoiseChannel.Looping = true;
|
||||
}
|
||||
radioNoiseChannel.Near = VoipSound.Near;
|
||||
|
||||
@@ -50,25 +50,29 @@ readonly record struct ConnectCommand(
|
||||
NameAndLidgrenEndpointOption: endpoint is LidgrenEndpoint lidgrenEndpoint
|
||||
? Option.Some(new NameAndLidgrenEndpoint(ServerName: serverName, lidgrenEndpoint))
|
||||
: Option.None,
|
||||
SteamLobbyIdOption: Option.None) { }
|
||||
SteamLobbyIdOption: Option.None)
|
||||
{ }
|
||||
|
||||
public ConnectCommand(string serverName, ImmutableArray<P2PEndpoint> endpoints)
|
||||
: this(
|
||||
NameAndP2PEndpointsOption: Option.Some(new NameAndP2PEndpoints(ServerName: serverName, Endpoints: endpoints)),
|
||||
NameAndLidgrenEndpointOption: Option.None,
|
||||
SteamLobbyIdOption: Option.None) { }
|
||||
SteamLobbyIdOption: Option.None)
|
||||
{ }
|
||||
|
||||
public ConnectCommand(string serverName, LidgrenEndpoint endpoint)
|
||||
: this(
|
||||
NameAndP2PEndpointsOption: Option.None,
|
||||
NameAndLidgrenEndpointOption: Option.Some(new NameAndLidgrenEndpoint(ServerName: serverName, Endpoint: endpoint)),
|
||||
SteamLobbyIdOption: Option.None) { }
|
||||
SteamLobbyIdOption: Option.None)
|
||||
{ }
|
||||
|
||||
public ConnectCommand(SteamLobbyId lobbyId)
|
||||
: this(
|
||||
NameAndP2PEndpointsOption: Option.None,
|
||||
NameAndLidgrenEndpointOption: Option.None,
|
||||
SteamLobbyIdOption: Option.Some(lobbyId)) { }
|
||||
SteamLobbyIdOption: Option.Some(lobbyId))
|
||||
{ }
|
||||
|
||||
public static Option<ConnectCommand> Parse(string str)
|
||||
=> Parse(ToolBox.SplitCommand(str));
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+28
-10
@@ -154,13 +154,25 @@ namespace Barotrauma.Networking
|
||||
//16 = entity ID, 8 = msg length
|
||||
if (msg.BitPosition + 16 + 8 > msg.LengthBits)
|
||||
{
|
||||
string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
|
||||
UInt16 potentialEntityId = Entity.NullEntityID;
|
||||
try
|
||||
{
|
||||
potentialEntityId = msg.ReadUInt16();
|
||||
}
|
||||
catch
|
||||
{
|
||||
//failed to read the ID, do nothing (we would've just used it for the error message)
|
||||
}
|
||||
Entity targetEntity = Entity.FindEntityByID(potentialEntityId);
|
||||
|
||||
string errorMsg = $"Error while reading a message from the server (entity: {targetEntity?.ToString() ?? "unknown"}).";
|
||||
errorMsg += $" Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
|
||||
errorMsg += "\nPrevious entities:";
|
||||
for (int j = tempEntityList.Count - 1; j >= 0; j--)
|
||||
{
|
||||
errorMsg += "\n" + (tempEntityList[j] == null ? "NULL" : tempEntityList[j].ToString());
|
||||
}
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg, contentPackage: targetEntity?.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -172,7 +184,7 @@ namespace Barotrauma.Networking
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
|
||||
Microsoft.Xna.Framework.Color.Orange);
|
||||
Color.Orange);
|
||||
}
|
||||
tempEntityList.Add(null);
|
||||
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
|
||||
@@ -187,7 +199,7 @@ namespace Barotrauma.Networking
|
||||
//skip the event if we've already received it or if the entity isn't found
|
||||
if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
|
||||
{
|
||||
if (thisEventID != (UInt16) (lastReceivedID + 1))
|
||||
if (thisEventID != (UInt16)(lastReceivedID + 1))
|
||||
{
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
@@ -195,7 +207,7 @@ namespace Barotrauma.Networking
|
||||
"Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
|
||||
NetIdUtils.IdMoreRecent(thisEventID, (UInt16)(lastReceivedID + 1))
|
||||
? GUIStyle.Red
|
||||
: Microsoft.Xna.Framework.Color.Yellow);
|
||||
: Color.Yellow);
|
||||
}
|
||||
}
|
||||
else if (entity == null)
|
||||
@@ -215,12 +227,18 @@ namespace Barotrauma.Networking
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
|
||||
Microsoft.Xna.Framework.Color.Green);
|
||||
Color.Green);
|
||||
}
|
||||
lastReceivedID++;
|
||||
ReadEvent(msg, entity, sendingTime);
|
||||
msg.ReadPadBits();
|
||||
|
||||
try
|
||||
{
|
||||
ReadEvent(msg, entity, sendingTime);
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new EntityEventException("Failed to read event." , entity as Entity, exception);
|
||||
}
|
||||
if (msg.BitPosition != msgPosition + msgLength * 8)
|
||||
{
|
||||
var prevEntity = tempEntityList.Count >= 2 ? tempEntityList[tempEntityList.Count - 2] : null;
|
||||
@@ -231,7 +249,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
|
||||
throw new Exception(errorMsg);
|
||||
throw new EntityEventException(errorMsg, entity as Entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -74,7 +74,16 @@ sealed class SteamConnectSocket : P2PSocket
|
||||
{
|
||||
if (!SteamManager.IsInitialized) { return Result.Failure(new Error(ErrorCode.SteamNotInitialized)); }
|
||||
|
||||
var connectionManager = Steamworks.SteamNetworkingSockets.ConnectRelay<ConnectionManager>(endpoint.SteamId.Value);
|
||||
ConnectionManager connectionManager;
|
||||
try
|
||||
{
|
||||
connectionManager = Steamworks.SteamNetworkingSockets.ConnectRelay<ConnectionManager>(endpoint.SteamId.Value);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to connect via SteamP2P. Are you logged in to Steam, is the same Steam account already connected to the server?", e);
|
||||
return Result.Failure(new Error(ErrorCode.FailedToCreateSteamP2PSocket));
|
||||
}
|
||||
if (connectionManager is null) { return Result.Failure(new Error(ErrorCode.FailedToCreateSteamP2PSocket)); }
|
||||
connectionManager.SetEndpointAndCallbacks(endpoint, callbacks);
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ sealed class SteamListenSocket : P2PSocket
|
||||
|
||||
public override void OnMessage(Steamworks.Data.Connection connection, Steamworks.Data.NetIdentity identity, IntPtr data, int size, long messageNum, long recvTime, int channel)
|
||||
{
|
||||
if (!identity.IsSteamId) { return; }
|
||||
if (!identity.IsSteamId || data == IntPtr.Zero) { return; }
|
||||
var endpoint = new SteamP2PEndpoint(new SteamId((Steamworks.SteamId)identity));
|
||||
|
||||
var dataArray = new byte[size];
|
||||
|
||||
@@ -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,
|
||||
@@ -39,6 +41,9 @@ namespace Barotrauma.Networking
|
||||
protected bool IsOwner => ownerKey.IsSome();
|
||||
protected readonly Option<int> ownerKey;
|
||||
|
||||
/// <summary>
|
||||
/// Has the ClientPeer been started? Set to true in <see cref="Start"/>, set to false when shutting the client down <see cref="Close(PeerDisconnectPacket)"/>.
|
||||
/// </summary>
|
||||
public bool IsActive => isActive;
|
||||
|
||||
protected bool isActive;
|
||||
@@ -102,8 +107,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
TaskPool.Add($"{GetType().Name}.{nameof(GetAccountId)}", GetAccountId(), t =>
|
||||
{
|
||||
if (GameMain.Client?.ClientPeer is null) { return; }
|
||||
|
||||
if (!IsActive)
|
||||
{
|
||||
//client has become inactive (cancelled/disconnected while waiting for initialization)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!t.TryGetResult(out Option<AccountId> accountId))
|
||||
{
|
||||
Close(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
|
||||
@@ -118,7 +127,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 +186,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();
|
||||
|
||||
+7
-4
@@ -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,59 @@ 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;
|
||||
bool clientHasChosenNewBotViaShuttle = 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;
|
||||
SetShuttleBodyType(teamSpecificState.TeamID, FarseerPhysics.BodyType.Dynamic);
|
||||
break;
|
||||
case State.Waiting:
|
||||
teamSpecificState.PendingRespawnCount = msg.ReadUInt16();
|
||||
teamSpecificState.RequiredRespawnCount = msg.ReadUInt16();
|
||||
respawnPromptPending = msg.ReadBoolean();
|
||||
clientHasChosenNewBotViaShuttle = 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));
|
||||
SetShuttleBodyType(teamSpecificState.TeamID, FarseerPhysics.BodyType.Static);
|
||||
break;
|
||||
case State.Returning:
|
||||
teamSpecificState.RespawnCountdownStarted = false;
|
||||
break;
|
||||
}
|
||||
teamSpecificState.CurrentState = newState;
|
||||
|
||||
if (respawnPromptPending && !clientHasChosenNewBotViaShuttle)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -385,14 +385,24 @@ namespace Barotrauma.Networking
|
||||
{ MinSize = new Point(0, 15) },
|
||||
package.Name)
|
||||
{
|
||||
CanBeFocused = false
|
||||
Enabled = false
|
||||
};
|
||||
packageText.Box.DisabledColor = packageText.Box.Color;
|
||||
packageText.TextBlock.DisabledTextColor = packageText.TextBlock.TextColor;
|
||||
if (!string.IsNullOrEmpty(package.Hash))
|
||||
{
|
||||
if (ContentPackageManager.AllPackages.Any(contentPackage => contentPackage.Hash.StringRepresentation == package.Hash))
|
||||
if (ContentPackageManager.AllPackages.FirstOrDefault(contentPackage => contentPackage.Hash.StringRepresentation == package.Hash) is { } matchingPackage)
|
||||
{
|
||||
packageText.TextColor = GUIStyle.Green;
|
||||
packageText.Selected = true;
|
||||
matchingPackage.TryFetchUgcDescription(onFinished: (string? description) =>
|
||||
{
|
||||
if (packageText.ToolTip.IsNullOrEmpty() &&
|
||||
!string.IsNullOrEmpty(description))
|
||||
{
|
||||
packageText.ToolTip = description + "...";
|
||||
}
|
||||
});
|
||||
}
|
||||
//workshop download link found
|
||||
else if (package.Id.TryUnwrap(out var ugcId) && ugcId is SteamWorkshopId)
|
||||
@@ -437,7 +447,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void UpdateInfo(Func<string, string?> valueGetter)
|
||||
{
|
||||
ServerMessage = valueGetter("message") ?? "";
|
||||
ServerMessage = ExtractServerMessage(valueGetter);
|
||||
if (Version.TryParse(valueGetter("version"), out var version))
|
||||
{
|
||||
GameVersion = version;
|
||||
@@ -477,6 +487,22 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private static string ExtractServerMessage(Func<string, string?> valueGetter)
|
||||
{
|
||||
string msg = valueGetter("message") ?? string.Empty;
|
||||
if (!msg.IsNullOrEmpty()) { return msg; }
|
||||
|
||||
int messageIndex = 0;
|
||||
string splitMessage;
|
||||
do
|
||||
{
|
||||
splitMessage = valueGetter($"message{messageIndex}") ?? string.Empty;
|
||||
msg += splitMessage;
|
||||
messageIndex++;
|
||||
} while (!splitMessage.IsNullOrEmpty());
|
||||
return msg;
|
||||
}
|
||||
|
||||
private static ServerListContentPackageInfo[] ExtractContentPackageInfo(string serverName, Func<string, string?> valueGetter)
|
||||
{
|
||||
//workaround to ServerRules queries truncating the values to 255 bytes
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -67,6 +67,7 @@ namespace Barotrauma
|
||||
return null;
|
||||
});
|
||||
serverInfo.Checked = true;
|
||||
serverInfo.HasPassword |= entry.Passworded;
|
||||
|
||||
onServerDataReceived(serverInfo, this);
|
||||
});
|
||||
|
||||
@@ -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),
|
||||
@@ -429,6 +465,12 @@ namespace Barotrauma.Networking
|
||||
var allowSpecBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsAllowSpectating"));
|
||||
AssignGUIComponent(nameof(AllowSpectating), allowSpecBox);
|
||||
|
||||
var allowAfkBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsAllowAFK"))
|
||||
{
|
||||
ToolTip = TextManager.Get("ServerSettingsAllowAFK.tooltip")
|
||||
};
|
||||
AssignGUIComponent(nameof(AllowAFK), allowAfkBox);
|
||||
|
||||
var losModeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("LosEffect"));
|
||||
var losModeSelection = new GUISelectionCarousel<LosMode>(new RectTransform(new Vector2(0.5f, 0.6f), losModeLabel.RectTransform, Anchor.CenterRight));
|
||||
@@ -485,6 +527,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
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
@@ -202,6 +202,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
DebugConsole.ThrowError("Capture device has been disconnected. You can select another available device in the settings.");
|
||||
Disconnected = true;
|
||||
TryRefreshDevice();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -320,7 +321,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 +332,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 +383,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,5 +402,65 @@ namespace Barotrauma.Networking
|
||||
captureThread = null;
|
||||
if (captureDevice != IntPtr.Zero) { Alc.CaptureCloseDevice(captureDevice); }
|
||||
}
|
||||
|
||||
public static void TryRefreshDevice()
|
||||
{
|
||||
DebugConsole.NewMessage("Refreshing audio capture device");
|
||||
|
||||
List<string> deviceList = Alc.GetStringList(IntPtr.Zero, Alc.CaptureDeviceSpecifier).ToList();
|
||||
int alcError = Alc.GetError(IntPtr.Zero);
|
||||
if (alcError != Alc.NoError)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to list available audio input devices: " + alcError.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (deviceList.Any())
|
||||
{
|
||||
string device;
|
||||
|
||||
if (deviceList.Find(n => n.Equals(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice, StringComparison.OrdinalIgnoreCase))
|
||||
is string availablePreviousDevice)
|
||||
{
|
||||
DebugConsole.NewMessage($" Previous device choice available: {availablePreviousDevice}");
|
||||
device = availablePreviousDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
device = Alc.GetString(IntPtr.Zero, Alc.CaptureDefaultDeviceSpecifier);
|
||||
DebugConsole.NewMessage($" Reverting to default device: {device}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(device))
|
||||
{
|
||||
device = deviceList[0];
|
||||
DebugConsole.NewMessage($" No default device found, resorting to first available device: {device}");
|
||||
}
|
||||
|
||||
// Save the new device choice and generate a new voice capture instance with it
|
||||
var currentConfig = GameSettings.CurrentConfig;
|
||||
currentConfig.Audio.VoiceCaptureDevice = device;
|
||||
GameSettings.SetCurrentConfig(currentConfig);
|
||||
if (Instance is VoipCapture currentCaptureInstance)
|
||||
{
|
||||
currentCaptureInstance.Dispose();
|
||||
}
|
||||
Create(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice);
|
||||
}
|
||||
|
||||
// Didn't end up with any capture device, so let's disable voice capture for now
|
||||
if (Instance == null)
|
||||
{
|
||||
DebugConsole.NewMessage($" No devices found, disabling");
|
||||
var currentConfig = GameSettings.CurrentConfig;
|
||||
currentConfig.Audio.VoiceSetting = VoiceMode.Disabled;
|
||||
GameSettings.SetCurrentConfig(currentConfig);
|
||||
}
|
||||
|
||||
if (GUI.SettingsMenuOpen)
|
||||
{
|
||||
SettingsMenu.Instance?.CreateAudioAndVCTab(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,12 +116,12 @@ namespace Barotrauma.Networking
|
||||
bool spectating = Character.Controlled == null;
|
||||
float rangeMultiplier = spectating ? 2.0f : 1.0f;
|
||||
WifiComponent senderRadio = null;
|
||||
|
||||
var messageType =
|
||||
!client.VoipQueue.ForceLocal &&
|
||||
ChatMessage.CanUseRadio(client.Character, out senderRadio) &&
|
||||
ChatMessage.CanUseRadio(Character.Controlled, out var recipientRadio) &&
|
||||
senderRadio.CanReceive(recipientRadio) ?
|
||||
ChatMessageType.Radio : ChatMessageType.Default;
|
||||
(spectating || (ChatMessage.CanUseRadio(Character.Controlled, out var recipientRadio) && senderRadio.CanReceive(recipientRadio)))
|
||||
? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
client.Character.ShowTextlessSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
|
||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
|
||||
@@ -149,7 +149,7 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen?.SetPlayerSpeaking(client);
|
||||
GameMain.GameSession?.CrewManager?.SetClientSpeaking(client);
|
||||
|
||||
if ((client.VoipSound.CurrentAmplitude * client.VoipSound.Gain * GameMain.SoundManager.GetCategoryGainMultiplier("voip")) > 0.1f) //TODO: might need to tweak
|
||||
if ((client.VoipSound.CurrentAmplitude * client.VoipSound.Gain * GameMain.SoundManager.GetCategoryGainMultiplier(SoundManager.SoundCategoryVoip)) > 0.1f) //TODO: might need to tweak
|
||||
{
|
||||
if (client.Character != null && !client.Character.Removed && !client.Character.IsDead)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user