Release 1.10.5.0 - Autumn Update 2025
This commit is contained in:
@@ -256,6 +256,15 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
string downloadFolder = downloadFolders[(FileTransferType)fileType];
|
||||
#if CLIENT && DEBUG
|
||||
if (GameClient.MultiClientTestMode)
|
||||
{
|
||||
//append the name of the client to the download folder to avoid multiple clients
|
||||
//from trying to download a file into the same path at the same time
|
||||
downloadFolder += "_" + GameMain.Client.Name;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!Directory.Exists(downloadFolder))
|
||||
{
|
||||
try
|
||||
|
||||
@@ -11,7 +11,6 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.PerkBehaviors;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -26,6 +25,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
#if DEBUG
|
||||
public float DebugServerVoipAmplitude;
|
||||
|
||||
public static bool MultiClientTestMode;
|
||||
#endif
|
||||
|
||||
public override Voting Voting { get; }
|
||||
@@ -873,8 +874,9 @@ namespace Barotrauma.Networking
|
||||
ReadAchievement(inc);
|
||||
break;
|
||||
case ServerPacketHeader.UNLOCKRECIPE:
|
||||
CharacterTeamType team = (CharacterTeamType)inc.ReadByte();
|
||||
Identifier identifier = inc.ReadIdentifier();
|
||||
GameMain.GameSession.UnlockRecipe(identifier, showNotifications: true);
|
||||
GameMain.GameSession?.UnlockRecipe(team, identifier, showNotifications: true);
|
||||
break;
|
||||
case ServerPacketHeader.ACHIEVEMENT_STAT:
|
||||
ReadAchievementStat(inc);
|
||||
|
||||
+14
-13
@@ -10,30 +10,31 @@ sealed class DualStackP2PSocket : P2PSocket
|
||||
private DualStackP2PSocket(
|
||||
Callbacks callbacks,
|
||||
Option<EosP2PSocket> eosSocket,
|
||||
Option<SteamListenSocket> steamSocket) :
|
||||
base(callbacks)
|
||||
Option<SteamListenSocket> steamSocket,
|
||||
OwnerOrClient type) :
|
||||
base(callbacks, type)
|
||||
{
|
||||
this.eosSocket = eosSocket;
|
||||
this.steamSocket = steamSocket;
|
||||
}
|
||||
|
||||
public static Result<P2PSocket, Error> Create(Callbacks callbacks)
|
||||
public static Result<P2PSocket, Error> Create(Callbacks callbacks, OwnerOrClient type)
|
||||
{
|
||||
var eosP2PSocketResult = EosP2PSocket.Create(callbacks);
|
||||
var steamP2PSocketResult = SteamListenSocket.Create(callbacks);
|
||||
var eosP2PSocketResult = EosP2PSocket.Create(callbacks, type);
|
||||
var steamP2PSocketResult = SteamListenSocket.Create(callbacks, type);
|
||||
if (eosP2PSocketResult.TryUnwrapFailure(out var eosError)
|
||||
&& steamP2PSocketResult.TryUnwrapFailure(out var steamError))
|
||||
{
|
||||
return Result.Failure(new Error(eosError, steamError));
|
||||
}
|
||||
return Result.Success((P2PSocket)new DualStackP2PSocket(
|
||||
callbacks,
|
||||
eosP2PSocketResult.TryUnwrapSuccess(out var eosP2PSocket)
|
||||
? Option.Some((EosP2PSocket)eosP2PSocket)
|
||||
: Option.None,
|
||||
steamP2PSocketResult.TryUnwrapSuccess(out var steamP2PSocket)
|
||||
? Option.Some((SteamListenSocket)steamP2PSocket)
|
||||
: Option.None));
|
||||
return Result.Success<P2PSocket>(new DualStackP2PSocket(
|
||||
callbacks,
|
||||
eosP2PSocketResult.TryUnwrapSuccess(out var eosP2PSocket)
|
||||
? Option.Some((EosP2PSocket)eosP2PSocket)
|
||||
: Option.None,
|
||||
steamP2PSocketResult.TryUnwrapSuccess(out var steamP2PSocket)
|
||||
? Option.Some((SteamListenSocket)steamP2PSocket)
|
||||
: Option.None, type));
|
||||
}
|
||||
|
||||
public override void ProcessIncomingMessages()
|
||||
|
||||
+13
-6
@@ -8,13 +8,14 @@ sealed class EosP2PSocket : P2PSocket
|
||||
|
||||
private EosP2PSocket(
|
||||
Callbacks callbacks,
|
||||
EosInterface.P2PSocket eosSocket)
|
||||
: base(callbacks)
|
||||
EosInterface.P2PSocket eosSocket,
|
||||
OwnerOrClient type)
|
||||
: base(callbacks, type)
|
||||
{
|
||||
this.eosSocket = eosSocket;
|
||||
}
|
||||
|
||||
public static Result<P2PSocket, Error> Create(Callbacks callbacks)
|
||||
public static Result<P2PSocket, Error> Create(Callbacks callbacks, OwnerOrClient type)
|
||||
{
|
||||
if (!EosInterface.Core.IsInitialized) { return Result.Failure(new Error(ErrorCode.EosNotInitialized)); }
|
||||
|
||||
@@ -26,19 +27,25 @@ sealed class EosP2PSocket : P2PSocket
|
||||
var socketCreateResult = EosInterface.P2PSocket.Create(puids[0], eosSocketId);
|
||||
|
||||
if (!socketCreateResult.TryUnwrapSuccess(out var eosSocket)) { return Result.Failure(new Error(ErrorCode.FailedToCreateEosP2PSocket, socketCreateResult.ToString())); }
|
||||
var retVal = new EosP2PSocket(callbacks, eosSocket);
|
||||
var retVal = new EosP2PSocket(callbacks, eosSocket, type);
|
||||
|
||||
eosSocket.HandleIncomingConnection.Register("Event".ToIdentifier(), retVal.OnIncomingConnection);
|
||||
eosSocket.HandleClosedConnection.Register("Event".ToIdentifier(), retVal.OnConnectionClosed);
|
||||
|
||||
return Result.Success((P2PSocket)retVal);
|
||||
return Result.Success<P2PSocket>(retVal);
|
||||
}
|
||||
|
||||
public override void ProcessIncomingMessages()
|
||||
{
|
||||
foreach (var msg in eosSocket.GetMessageBatch())
|
||||
{
|
||||
callbacks.OnData(new EosP2PEndpoint(msg.Sender), new ReadWriteMessage(msg.Buffer, 0, msg.ByteLength * 8, false));
|
||||
EosP2PEndpoint endpoint = new EosP2PEndpoint(msg.Sender);
|
||||
callbacks.OnData(endpoint, new ReadWriteMessage(msg.Buffer, 0, msg.ByteLength * 8, false));
|
||||
|
||||
if (Type is OwnerOrClient.Owner)
|
||||
{
|
||||
dosProtection.OnPacket(endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-1
@@ -8,6 +8,15 @@ namespace Barotrauma.Networking;
|
||||
|
||||
abstract class P2PSocket : IDisposable
|
||||
{
|
||||
public readonly P2POwnerDoSProtection dosProtection;
|
||||
public readonly OwnerOrClient Type;
|
||||
|
||||
public enum OwnerOrClient
|
||||
{
|
||||
Client,
|
||||
Owner
|
||||
}
|
||||
|
||||
public enum ErrorCode
|
||||
{
|
||||
EosNotInitialized,
|
||||
@@ -38,12 +47,16 @@ abstract class P2PSocket : IDisposable
|
||||
public readonly record struct Callbacks(
|
||||
Predicate<P2PEndpoint> OnIncomingConnection,
|
||||
Action<P2PEndpoint, PeerDisconnectPacket> OnConnectionClosed,
|
||||
P2POwnerDoSProtection.ExcessivePacketDelegate OnExcessivePackets,
|
||||
Action<P2PEndpoint, IReadMessage> OnData);
|
||||
protected readonly Callbacks callbacks;
|
||||
|
||||
protected P2PSocket(Callbacks callbacks)
|
||||
protected P2PSocket(Callbacks callbacks, OwnerOrClient type)
|
||||
{
|
||||
this.callbacks = callbacks;
|
||||
Type = type;
|
||||
|
||||
dosProtection = new P2POwnerDoSProtection(callbacks.OnExcessivePackets);
|
||||
}
|
||||
|
||||
public abstract void ProcessIncomingMessages();
|
||||
|
||||
+3
-3
@@ -64,13 +64,13 @@ sealed class SteamConnectSocket : P2PSocket
|
||||
private readonly SteamP2PEndpoint expectedEndpoint;
|
||||
private readonly ConnectionManager connectionManager;
|
||||
|
||||
private SteamConnectSocket(SteamP2PEndpoint expectedEndpoint, Callbacks callbacks, ConnectionManager connectionManager) : base(callbacks)
|
||||
private SteamConnectSocket(SteamP2PEndpoint expectedEndpoint, Callbacks callbacks, ConnectionManager connectionManager, OwnerOrClient type) : base(callbacks, type)
|
||||
{
|
||||
this.expectedEndpoint = expectedEndpoint;
|
||||
this.connectionManager = connectionManager;
|
||||
}
|
||||
|
||||
public static Result<P2PSocket, Error> Create(SteamP2PEndpoint endpoint, Callbacks callbacks)
|
||||
public static Result<P2PSocket, Error> Create(SteamP2PEndpoint endpoint, Callbacks callbacks, OwnerOrClient type)
|
||||
{
|
||||
if (!SteamManager.IsInitialized) { return Result.Failure(new Error(ErrorCode.SteamNotInitialized)); }
|
||||
|
||||
@@ -87,7 +87,7 @@ sealed class SteamConnectSocket : P2PSocket
|
||||
if (connectionManager is null) { return Result.Failure(new Error(ErrorCode.FailedToCreateSteamP2PSocket)); }
|
||||
connectionManager.SetEndpointAndCallbacks(endpoint, callbacks);
|
||||
|
||||
return Result.Success((P2PSocket)new SteamConnectSocket(endpoint, callbacks, connectionManager));
|
||||
return Result.Success<P2PSocket>(new SteamConnectSocket(endpoint, callbacks, connectionManager, type));
|
||||
}
|
||||
|
||||
public override void ProcessIncomingMessages()
|
||||
|
||||
+40
-10
@@ -10,12 +10,14 @@ sealed class SteamListenSocket : P2PSocket
|
||||
private sealed class SocketManager : Steamworks.SocketManager, Steamworks.ISocketManager
|
||||
{
|
||||
private Callbacks callbacks;
|
||||
private P2PSocket socket;
|
||||
private readonly Dictionary<SteamP2PEndpoint, Steamworks.Data.Connection> endpointToConnection = new();
|
||||
|
||||
public void SetCallbacks(Callbacks callbacks)
|
||||
{
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
=> this.callbacks = callbacks;
|
||||
|
||||
public void SetSocket(P2PSocket socket)
|
||||
=> this.socket = socket;
|
||||
|
||||
public override void OnConnecting(Steamworks.Data.Connection connection, Steamworks.Data.ConnectionInfo info)
|
||||
{
|
||||
@@ -65,7 +67,7 @@ sealed class SteamListenSocket : P2PSocket
|
||||
callbacks.OnConnectionClosed(remoteEndpoint, peerDisconnectPacket);
|
||||
base.OnDisconnected(connection, info);
|
||||
}
|
||||
|
||||
|
||||
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 || data == IntPtr.Zero) { return; }
|
||||
@@ -75,6 +77,11 @@ sealed class SteamListenSocket : P2PSocket
|
||||
Marshal.Copy(source: data, destination: dataArray, startIndex: 0, length: size);
|
||||
|
||||
callbacks.OnData(endpoint, new ReadWriteMessage(dataArray, bitPos: 0, lBits: size * 8, copyBuf: false));
|
||||
|
||||
if (socket?.Type is OwnerOrClient.Owner)
|
||||
{
|
||||
socket.dosProtection.OnPacket(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool SendMessage(SteamP2PEndpoint endpoint, IWriteMessage outMsg, DeliveryMethod deliveryMethod)
|
||||
@@ -107,26 +114,49 @@ sealed class SteamListenSocket : P2PSocket
|
||||
|
||||
private SteamListenSocket(
|
||||
Callbacks callbacks,
|
||||
SocketManager socketManager)
|
||||
: base(callbacks)
|
||||
SocketManager socketManager,
|
||||
OwnerOrClient type)
|
||||
: base(callbacks, type)
|
||||
{
|
||||
this.socketManager = socketManager;
|
||||
}
|
||||
|
||||
public static Result<P2PSocket, Error> Create(Callbacks callbacks)
|
||||
public static Result<P2PSocket, Error> Create(Callbacks callbacks, OwnerOrClient type)
|
||||
{
|
||||
if (!SteamManager.IsInitialized) { return Result.Failure(new Error(ErrorCode.SteamNotInitialized)); }
|
||||
|
||||
var socketManager = Steamworks.SteamNetworkingSockets.CreateRelaySocket<SocketManager>();
|
||||
if (socketManager is null) { return Result.Failure(new Error(ErrorCode.FailedToCreateSteamP2PSocket)); }
|
||||
socketManager.SetCallbacks(callbacks);
|
||||
|
||||
return Result.Success((P2PSocket)new SteamListenSocket(callbacks, socketManager));
|
||||
socketManager.SetCallbacks(callbacks);
|
||||
P2PSocket socket = new SteamListenSocket(callbacks, socketManager, type);
|
||||
socketManager.SetSocket(socket);
|
||||
|
||||
return Result.Success(socket);
|
||||
}
|
||||
|
||||
public override void ProcessIncomingMessages()
|
||||
{
|
||||
socketManager.Receive();
|
||||
const int bufferSize = 32;
|
||||
const int maxIterations = 100;
|
||||
|
||||
// could technically cause a stack overflow since the call is recursive,
|
||||
// use a while loop instead
|
||||
int iteration;
|
||||
for (iteration = 0; iteration < maxIterations; iteration++)
|
||||
{
|
||||
int received = socketManager.Receive(bufferSize: bufferSize, receiveToEnd: false);
|
||||
|
||||
if (received < bufferSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iteration >= maxIterations)
|
||||
{
|
||||
DebugConsole.ThrowError("Steam P2P socket received too many messages in a single frame.");
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SendMessage(P2PEndpoint endpoint, IWriteMessage outMsg, DeliveryMethod deliveryMethod)
|
||||
|
||||
+9
-4
@@ -59,11 +59,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
ServerConnection = ServerEndpoint.MakeConnectionFromEndpoint();
|
||||
|
||||
var socketCallbacks = new P2PSocket.Callbacks(OnIncomingConnection, OnConnectionClosed, OnP2PData);
|
||||
var socketCallbacks = new P2PSocket.Callbacks(OnIncomingConnection, OnConnectionClosed, OnExcessivePackets, OnP2PData);
|
||||
var socketCreateResult = ServerEndpoint switch
|
||||
{
|
||||
EosP2PEndpoint => EosP2PSocket.Create(socketCallbacks),
|
||||
SteamP2PEndpoint steamP2PEndpoint => SteamConnectSocket.Create(steamP2PEndpoint, socketCallbacks),
|
||||
EosP2PEndpoint => EosP2PSocket.Create(socketCallbacks, P2PSocket.OwnerOrClient.Client),
|
||||
SteamP2PEndpoint steamP2PEndpoint => SteamConnectSocket.Create(steamP2PEndpoint, socketCallbacks, P2PSocket.OwnerOrClient.Client),
|
||||
_ => throw new Exception($"Invalid server endpoint: {ServerEndpoint.GetType()} {ServerEndpoint}")
|
||||
};
|
||||
socket = socketCreateResult.TryUnwrapSuccess(out var s)
|
||||
@@ -97,6 +97,11 @@ namespace Barotrauma.Networking
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
private void OnExcessivePackets(P2PEndpoint endpoint, bool shouldBan)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private bool OnIncomingConnection(P2PEndpoint remoteEndpoint)
|
||||
{
|
||||
if (remoteEndpoint == ServerEndpoint)
|
||||
@@ -163,7 +168,7 @@ namespace Barotrauma.Networking
|
||||
int completeMessageLengthBits = completeMessage.Length * 8;
|
||||
incomingDataMessages.Add(new ReadWriteMessage(completeMessage.ToArray(), 0, completeMessageLengthBits, copyBuf: false));
|
||||
}
|
||||
else if (packetHeader.IsHeartbeatMessage())
|
||||
else if (packetHeader.IsHeartbeatMessage() || packetHeader.IsDoSProtectionMessage())
|
||||
{
|
||||
return; //TODO: implement heartbeats
|
||||
}
|
||||
|
||||
+25
-2
@@ -88,8 +88,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
remotePeers.Clear();
|
||||
|
||||
var socketCallbacks = new P2PSocket.Callbacks(OnIncomingConnection, OnConnectionClosed, OnP2PData);
|
||||
var socketCreateResult = DualStackP2PSocket.Create(socketCallbacks);
|
||||
var socketCallbacks = new P2PSocket.Callbacks(OnIncomingConnection, OnConnectionClosed, OnExcessivePackets, OnP2PData);
|
||||
var socketCreateResult = DualStackP2PSocket.Create(socketCallbacks, type: P2PSocket.OwnerOrClient.Owner);
|
||||
socket = socketCreateResult.TryUnwrapSuccess(out var s)
|
||||
? s
|
||||
: throw new Exception($"Failed to create dual-stack socket: {socketCreateResult}");
|
||||
@@ -187,6 +187,29 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExcessivePackets(P2PEndpoint endpoint, bool shouldBan)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.WriteNetSerializableStruct(new P2POwnerToServerHeader
|
||||
{
|
||||
EndpointStr = selfPrimaryEndpoint.StringRepresentation,
|
||||
AccountInfo = selfAccountInfo
|
||||
});
|
||||
msg.WriteNetSerializableStruct(new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsDoSProtectionMessage
|
||||
});
|
||||
msg.WriteNetSerializableStruct(new DoSProtectionPacket(endpoint.StringRepresentation, shouldBan));
|
||||
string dcMsg = TextManager.Get(shouldBan ? "DoSProtectionBanned" : "DoSProtectionKicked")
|
||||
.Fallback(TextManager.Get("DoSProtectionKicked")).Value;
|
||||
|
||||
msg.WriteNetSerializableStruct(shouldBan
|
||||
? PeerDisconnectPacket.Banned(dcMsg)
|
||||
: PeerDisconnectPacket.Kicked(dcMsg));
|
||||
ForwardToServerProcess(msg);
|
||||
}
|
||||
|
||||
private void StartAuthTask(IReadMessage inc, RemotePeer remotePeer)
|
||||
{
|
||||
remotePeer.AuthStatus = RemotePeer.AuthenticationStatus.AuthenticationPending;
|
||||
|
||||
@@ -612,7 +612,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
public bool Equals(ServerInfo other)
|
||||
=> other.Endpoints.Any(e => Endpoints.Contains(e));
|
||||
=> other != null && other.Endpoints.Any(Endpoints.Contains);
|
||||
|
||||
public override int GetHashCode() => Endpoints.First().GetHashCode();
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
public partial class ServerLog
|
||||
{
|
||||
const int MaxLines = 500;
|
||||
|
||||
public GUIButton LogFrame;
|
||||
private GUIListBox listBox;
|
||||
private GUIButton reverseButton;
|
||||
@@ -17,6 +19,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
private bool reverseOrder = false;
|
||||
|
||||
private readonly bool[] msgTypeHidden = new bool[Enum.GetValues(typeof(MessageType)).Length];
|
||||
|
||||
private bool OnReverseClicked(GUIButton btn, object obj)
|
||||
{
|
||||
SetMessageReversal(!reverseOrder);
|
||||
@@ -105,7 +109,10 @@ namespace Barotrauma.Networking
|
||||
reverseButton.Children.ForEach(c => c.SpriteEffects = reverseOrder ? SpriteEffects.FlipVertically : SpriteEffects.None);
|
||||
reverseButton.OnClicked = OnReverseClicked;
|
||||
|
||||
listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), listBoxLayout.RectTransform));
|
||||
listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), listBoxLayout.RectTransform))
|
||||
{
|
||||
AutoHideScrollBar = false
|
||||
};
|
||||
|
||||
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.05f), rightColumn.RectTransform), TextManager.Get("Close"))
|
||||
{
|
||||
@@ -127,7 +134,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
listBox.UpdateScrollBarSize();
|
||||
|
||||
if (listBox.BarScroll == 0.0f || listBox.BarScroll == 1.0f) { listBox.BarScroll = 1.0f; }
|
||||
//scrolled all the way down by default
|
||||
listBox.BarScroll = 1.0f;
|
||||
|
||||
msgFilter = "";
|
||||
}
|
||||
@@ -189,11 +197,19 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
float prevSize = listBox.BarSize;
|
||||
|
||||
GUIComponent firstVisibleLine = listBox.Content.Children.FirstOrDefault(c => c.Rect.Y > listBox.Content.Rect.Y);
|
||||
int firstVisibileYPos = firstVisibleLine?.Rect.Y ?? 0;
|
||||
|
||||
while (listBox.Content.CountChildren > MaxLines)
|
||||
{
|
||||
listBox.Content.RemoveChild(reverseOrder ? listBox.Content.Children.Last() : listBox.Content.Children.First());
|
||||
}
|
||||
|
||||
GUIFrame textContainer = null;
|
||||
|
||||
Anchor anchor = Anchor.TopLeft;
|
||||
Pivot pivot = Pivot.TopLeft;
|
||||
RichString richString = line.Text as RichString;
|
||||
RichString richString = line.Text;
|
||||
if (richString != null && richString.RichTextData.HasValue)
|
||||
{
|
||||
foreach (var data in richString.RichTextData.Value)
|
||||
@@ -217,7 +233,7 @@ namespace Barotrauma.Networking
|
||||
line.Text, wrap: true, font: GUIStyle.SmallFont)
|
||||
{
|
||||
TextColor = messageColor[line.Type],
|
||||
Visible = !msgTypeHidden[(int)line.Type],
|
||||
Visible = !ShouldFilterMessage(line),
|
||||
CanBeFocused = false,
|
||||
UserData = line
|
||||
};
|
||||
@@ -247,31 +263,47 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
if ((prevSize == 1.0f && listBox.BarScroll == 0.0f) || (prevSize < 1.0f && listBox.BarScroll == 1.0f)) listBox.BarScroll = 1.0f;
|
||||
//if the list was scrolled to the bottom, or to the top while the list wasn't full yet,
|
||||
//keep it scrolled to the bottom
|
||||
if ((MathUtils.NearlyEqual(prevSize, 1.0f) && MathUtils.NearlyEqual(listBox.BarScroll, 0.0f)) ||
|
||||
(prevSize < 1.0f && MathUtils.NearlyEqual(listBox.BarScroll, 1.0f)))
|
||||
{
|
||||
listBox.BarScroll = 1.0f;
|
||||
}
|
||||
//otherwise modify the scroll so the topmost element stays where it was (list doesn't jump as new lines are added when scrolled up)
|
||||
else if (firstVisibleLine != null)
|
||||
{
|
||||
listBox.UpdateScrollBarSize();
|
||||
listBox.RecalculateChildren();
|
||||
int diff = firstVisibleLine.Rect.Y - firstVisibileYPos;
|
||||
if (diff != 0)
|
||||
{
|
||||
listBox.BarScroll += diff / listBox.TotalSize * (prevSize / listBox.BarSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool FilterMessages()
|
||||
{
|
||||
string filter = msgFilter == null ? "" : msgFilter.ToLower();
|
||||
|
||||
foreach (GUIComponent child in listBox.Content.Children)
|
||||
{
|
||||
if (!(child is GUITextBlock textBlock)) { continue; }
|
||||
if (child is not GUITextBlock) { continue; }
|
||||
child.Visible = true;
|
||||
if (msgTypeHidden[(int)((LogMessage)child.UserData).Type])
|
||||
{
|
||||
child.Visible = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
textBlock.Visible = string.IsNullOrEmpty(filter) || textBlock.Text.ToLower().Contains(filter);
|
||||
child.Visible = !ShouldFilterMessage((LogMessage)child.UserData);
|
||||
}
|
||||
listBox.UpdateScrollBarSize();
|
||||
listBox.BarScroll = 0.0f;
|
||||
listBox.BarScroll = 1.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ShouldFilterMessage(LogMessage message)
|
||||
{
|
||||
if (msgTypeHidden[(int)message.Type]) { return true; }
|
||||
string text = message.Text.SanitizedValue;
|
||||
return !string.IsNullOrEmpty(msgFilter) && !text.Contains(msgFilter, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private void SetMessageReversal(bool reverse)
|
||||
{
|
||||
if (reverseOrder == reverse) { return; }
|
||||
|
||||
Reference in New Issue
Block a user