Merge remote-tracking branch 'upstream/dev' into develop
This commit is contained in:
@@ -6,9 +6,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
partial class ChatMessage
|
||||
{
|
||||
public virtual void ClientWrite(IWriteMessage msg)
|
||||
public virtual void ClientWrite(in SegmentTableWriter<ClientNetSegment> segmentTableWriter, IWriteMessage msg)
|
||||
{
|
||||
msg.WriteByte((byte)ClientNetObject.CHAT_MESSAGE);
|
||||
segmentTableWriter.StartNewSegment(ClientNetSegment.ChatMessage);
|
||||
msg.WriteUInt16(NetStateID);
|
||||
msg.WriteRangedInteger((int)Type, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
|
||||
msg.WriteRangedInteger((int)ChatMode, 0, Enum.GetValues(typeof(ChatMode)).Length - 1);
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
static partial class ChildServerRelay
|
||||
{
|
||||
public static Process Process;
|
||||
public static bool IsProcessAlive => Process is { HasExited: false };
|
||||
|
||||
private static bool localHandlesDisposed;
|
||||
private static AnonymousPipeServerStream writePipe;
|
||||
private static AnonymousPipeServerStream readPipe;
|
||||
@@ -44,18 +47,27 @@ namespace Barotrauma.Networking
|
||||
localHandlesDisposed = true;
|
||||
}
|
||||
|
||||
public static void ClosePipes()
|
||||
public static void AttemptGracefulShutDown(int maxAttempts = 20)
|
||||
{
|
||||
writePipe?.Dispose(); writePipe = null;
|
||||
readPipe?.Dispose(); readPipe = null;
|
||||
shutDown = true;
|
||||
status = StatusEnum.RequestedShutDown;
|
||||
writeManualResetEvent?.Set();
|
||||
int checks = 0;
|
||||
while (Process is { HasExited: false })
|
||||
{
|
||||
if (checks >= maxAttempts)
|
||||
{
|
||||
DebugConsole.AddWarning("Server could not be shut down gracefully");
|
||||
break;
|
||||
}
|
||||
Thread.Sleep(100);
|
||||
checks++;
|
||||
}
|
||||
ForceShutDown();
|
||||
}
|
||||
|
||||
public static void ShutDown()
|
||||
|
||||
public static void ForceShutDown()
|
||||
{
|
||||
Process?.Kill(); Process = null;
|
||||
writePipe = null; readPipe = null;
|
||||
|
||||
PrivateShutDown();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,16 @@ namespace Barotrauma.Networking
|
||||
set;
|
||||
}
|
||||
|
||||
private SoundChannel radioNoiseChannel;
|
||||
private float radioNoise;
|
||||
|
||||
public float RadioNoise
|
||||
{
|
||||
get { return radioNoise; }
|
||||
set { radioNoise = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
|
||||
private bool mutedLocally;
|
||||
public bool MutedLocally
|
||||
{
|
||||
@@ -42,35 +52,64 @@ namespace Barotrauma.Networking
|
||||
!HasPermission(ClientPermissions.Kick) &&
|
||||
!HasPermission(ClientPermissions.Unban);
|
||||
|
||||
public void UpdateSoundPosition()
|
||||
public void UpdateVoipSound()
|
||||
{
|
||||
if (VoipSound == null) { return; }
|
||||
|
||||
if (!VoipSound.IsPlaying)
|
||||
if (VoipSound == null || !VoipSound.IsPlaying)
|
||||
{
|
||||
DebugConsole.Log("Destroying voipsound");
|
||||
VoipSound.Dispose();
|
||||
radioNoiseChannel?.Dispose();
|
||||
radioNoiseChannel = null;
|
||||
if (VoipSound != null)
|
||||
{
|
||||
DebugConsole.Log("Destroying voipsound");
|
||||
VoipSound.Dispose();
|
||||
}
|
||||
VoipSound = null;
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Screen.Selected is ModDownloadScreen)
|
||||
{
|
||||
VoipSound.Gain = 0.0f;
|
||||
}
|
||||
|
||||
float gain = 1.0f;
|
||||
float noiseGain = 0.0f;
|
||||
Vector3? position = null;
|
||||
if (character != null)
|
||||
{
|
||||
if (GameSettings.CurrentConfig.Audio.UseDirectionalVoiceChat)
|
||||
{
|
||||
VoipSound.SetPosition(new Vector3(character.WorldPosition.X, character.WorldPosition.Y, 0.0f));
|
||||
position = new Vector3(character.WorldPosition.X, character.WorldPosition.Y, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
VoipSound.SetPosition(null);
|
||||
float dist = Vector3.Distance(new Vector3(character.WorldPosition, 0.0f), GameMain.SoundManager.ListenerPosition);
|
||||
VoipSound.Gain = 1.0f - MathUtils.InverseLerp(VoipSound.Near, VoipSound.Far, dist);
|
||||
gain = 1.0f - MathUtils.InverseLerp(VoipSound.Near, VoipSound.Far, dist);
|
||||
}
|
||||
if (RadioNoise > 0.0f)
|
||||
{
|
||||
noiseGain = gain * RadioNoise;
|
||||
gain *= 1.0f - RadioNoise;
|
||||
}
|
||||
}
|
||||
else
|
||||
VoipSound.SetPosition(position);
|
||||
VoipSound.Gain = gain;
|
||||
if (noiseGain > 0.0f)
|
||||
{
|
||||
VoipSound.SetPosition(null);
|
||||
VoipSound.Gain = 1.0f;
|
||||
if (radioNoiseChannel == null || !radioNoiseChannel.IsPlaying)
|
||||
{
|
||||
radioNoiseChannel = SoundPlayer.PlaySound("radiostatic");
|
||||
radioNoiseChannel.Category = "voip";
|
||||
radioNoiseChannel.Looping = true;
|
||||
}
|
||||
radioNoiseChannel.Near = VoipSound.Near;
|
||||
radioNoiseChannel.Far = VoipSound.Far;
|
||||
radioNoiseChannel.Position = position;
|
||||
radioNoiseChannel.Gain = noiseGain;
|
||||
}
|
||||
else if (radioNoiseChannel != null)
|
||||
{
|
||||
radioNoiseChannel.Gain = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +197,11 @@ namespace Barotrauma.Networking
|
||||
VoipSound.Dispose();
|
||||
VoipSound = null;
|
||||
}
|
||||
if (radioNoiseChannel != null)
|
||||
{
|
||||
radioNoiseChannel.Dispose();
|
||||
radioNoiseChannel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,6 +311,12 @@ namespace Barotrauma.Networking
|
||||
CoroutineManager.StartCoroutine(WaitForStartingInfo(), "WaitForStartingInfo");
|
||||
}
|
||||
|
||||
public void SetLobbyPublic(bool isPublic)
|
||||
{
|
||||
GameMain.NetLobbyScreen.SetPublic(isPublic);
|
||||
SteamManager.SetLobbyPublic(isPublic);
|
||||
}
|
||||
|
||||
private ClientPeer CreateNetPeer()
|
||||
{
|
||||
Networking.ClientPeer.Callbacks callbacks = new ClientPeer.Callbacks(
|
||||
@@ -326,10 +332,27 @@ namespace Barotrauma.Networking
|
||||
};
|
||||
}
|
||||
|
||||
public void CreateServerCrashMessage()
|
||||
{
|
||||
// Close any message boxes that say "The server has crashed."
|
||||
var basicServerCrashMsg = TextManager.Get($"{nameof(DisconnectReason)}.{nameof(DisconnectReason.ServerCrashed)}");
|
||||
GUIMessageBox.MessageBoxes
|
||||
.OfType<GUIMessageBox>()
|
||||
.Where(mb => mb.Text?.Text == basicServerCrashMsg)
|
||||
.ToArray()
|
||||
.ForEach(mb => mb.Close());
|
||||
|
||||
// Open a new message box with the crash report path
|
||||
if (GUIMessageBox.MessageBoxes.All(
|
||||
mb => (mb as GUIMessageBox)?.Text?.Text != ChildServerRelay.CrashMessage))
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReturnToPreviousMenu(GUIButton button, object obj)
|
||||
{
|
||||
Quit();
|
||||
|
||||
Submarine.Unload();
|
||||
GameMain.Client = null;
|
||||
GameMain.GameSession = null;
|
||||
@@ -443,7 +466,7 @@ namespace Barotrauma.Networking
|
||||
foreach (Client c in ConnectedClients)
|
||||
{
|
||||
if (c.Character != null && c.Character.Removed) { c.Character = null; }
|
||||
c.UpdateSoundPosition();
|
||||
c.UpdateVoipSound();
|
||||
}
|
||||
|
||||
if (VoipCapture.Instance != null)
|
||||
@@ -479,15 +502,11 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Error while reading a message from server. {" + e + "}. ";
|
||||
string errorMsg = "Error while reading a message from server. ";
|
||||
if (GameMain.Client == null) { errorMsg += "Client disposed."; }
|
||||
errorMsg += "\n" + e.StackTrace.CleanupStackTrace();
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace();
|
||||
}
|
||||
AppendExceptionInfo(ref errorMsg, e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
DebugConsole.ThrowError("Error while reading a message from server.", e);
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", ("[message]", e.Message), ("[targetsite]", e.TargetSite.ToString())))
|
||||
{
|
||||
DisplayInLoadingScreens = true
|
||||
@@ -529,14 +548,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (GameMain.WindowActive)
|
||||
{
|
||||
if (ChildServerRelay.Process?.HasExited ?? true)
|
||||
if (!ChildServerRelay.IsProcessAlive)
|
||||
{
|
||||
Quit();
|
||||
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text?.Text == ChildServerRelay.CrashMessage))
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
|
||||
}
|
||||
CreateServerCrashMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -632,14 +647,8 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Error while reading an ingame update message from server. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace();
|
||||
}
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Error while reading an ingame update message from server.", e);
|
||||
#endif
|
||||
string errorMsg = "Error while reading an ingame update message from server.";
|
||||
AppendExceptionInfo(ref errorMsg, e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadDataMessage:ReadIngameUpdate", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw;
|
||||
}
|
||||
@@ -868,22 +877,31 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
byte missionCount = inc.ReadByte();
|
||||
if (missionCount != GameMain.GameSession.Missions.Count())
|
||||
{
|
||||
string errorMsg = $"Mission equality check failed. Mission count doesn't match the server (server: {missionCount}, client: {GameMain.GameSession.Missions.Count()})";
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
List<Identifier> serverMissionIdentifiers = new List<Identifier>();
|
||||
for (int i = 0; i < missionCount; i++)
|
||||
{
|
||||
serverMissionIdentifiers.Add(inc.ReadIdentifier());
|
||||
}
|
||||
if (missionCount != GameMain.GameSession.GameMode.Missions.Count())
|
||||
{
|
||||
string errorMsg =
|
||||
$"Mission equality check failed. Mission count doesn't match the server. " +
|
||||
$"Server: {string.Join(", ", serverMissionIdentifiers)}, " +
|
||||
$"client: {string.Join(", ", GameMain.GameSession.GameMode.Missions.Select(m => m.Prefab.Identifier))}, " +
|
||||
$"game session: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsCountMismatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
|
||||
if (missionCount > 0)
|
||||
{
|
||||
if (!GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier).OrderBy(id => id).SequenceEqual(serverMissionIdentifiers.OrderBy(id => id)))
|
||||
if (!GameMain.GameSession.GameMode.Missions.Select(m => m.Prefab.Identifier).OrderBy(id => id).SequenceEqual(serverMissionIdentifiers.OrderBy(id => id)))
|
||||
{
|
||||
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {string.Join(", ", serverMissionIdentifiers)}, client: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})";
|
||||
string errorMsg =
|
||||
$"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server " +
|
||||
$"Server: {string.Join(", ", serverMissionIdentifiers)}, " +
|
||||
$"client: {string.Join(", ", GameMain.GameSession.GameMode.Missions.Select(m => m.Prefab.Identifier))}, " +
|
||||
$"game session: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
@@ -939,13 +957,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
GUI.ClearCursorWait();
|
||||
|
||||
ChildServerRelay.ShutDown();
|
||||
|
||||
if (SteamManager.IsInitialized)
|
||||
{
|
||||
Steamworks.SteamFriends.ClearRichPresence();
|
||||
}
|
||||
|
||||
if (disconnectPacket.ShouldCreateAnalyticsEvent)
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
@@ -971,11 +982,43 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
ReturnToPreviousMenu(null, null);
|
||||
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
|
||||
if (ClientPeer is SteamP2PClientPeer or SteamP2POwnerPeer)
|
||||
{
|
||||
DisplayInLoadingScreens = true
|
||||
};
|
||||
SteamManager.LeaveLobby();
|
||||
}
|
||||
|
||||
GameMain.ModDownloadScreen.Reset();
|
||||
ContentPackageManager.EnabledPackages.Restore();
|
||||
|
||||
CampaignMode.StartRoundCancellationToken?.Cancel();
|
||||
|
||||
if (SteamManager.IsInitialized)
|
||||
{
|
||||
Steamworks.SteamFriends.ClearRichPresence();
|
||||
}
|
||||
foreach (var fileTransfer in FileReceiver.ActiveTransfers.ToArray())
|
||||
{
|
||||
FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
|
||||
}
|
||||
|
||||
ChildServerRelay.AttemptGracefulShutDown();
|
||||
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
|
||||
|
||||
characterInfo?.Remove();
|
||||
|
||||
VoipClient?.Dispose();
|
||||
VoipClient = null;
|
||||
GameMain.Client = null;
|
||||
GameMain.GameSession = null;
|
||||
|
||||
ReturnToPreviousMenu(null, null);
|
||||
if (disconnectPacket.DisconnectReason != DisconnectReason.Disconnected)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
|
||||
{
|
||||
DisplayInLoadingScreens = true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1321,6 +1364,7 @@ namespace Barotrauma.Networking
|
||||
ServerSettings.MaximumMoneyTransferRequest = inc.ReadInt32();
|
||||
bool usingShuttle = GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
|
||||
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
|
||||
ServerSettings.ShowEnemyHealthBars = (EnemyHealthBarMode)inc.ReadByte();
|
||||
bool includesFinalize = inc.ReadBoolean(); inc.ReadPadBits();
|
||||
GameMain.LightManager.LightingEnabled = true;
|
||||
|
||||
@@ -1878,12 +1922,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
private void ReadLobbyUpdate(IReadMessage inc)
|
||||
{
|
||||
ServerNetObject objHeader;
|
||||
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
|
||||
SegmentTableReader<ServerNetSegment>.Read(inc, (segment, inc) =>
|
||||
{
|
||||
switch (objHeader)
|
||||
switch (segment)
|
||||
{
|
||||
case ServerNetObject.SYNC_IDS:
|
||||
case ServerNetSegment.SyncIds:
|
||||
bool lobbyUpdated = inc.ReadBoolean();
|
||||
inc.ReadPadBits();
|
||||
|
||||
@@ -2015,17 +2058,19 @@ namespace Barotrauma.Networking
|
||||
|
||||
lastSentChatMsgID = inc.ReadUInt16();
|
||||
break;
|
||||
case ServerNetObject.CLIENT_LIST:
|
||||
case ServerNetSegment.ClientList:
|
||||
ReadClientList(inc);
|
||||
break;
|
||||
case ServerNetObject.CHAT_MESSAGE:
|
||||
case ServerNetSegment.ChatMessage:
|
||||
ChatMessage.ClientRead(inc);
|
||||
break;
|
||||
case ServerNetObject.VOTE:
|
||||
case ServerNetSegment.Vote:
|
||||
Voting.ClientRead(inc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.No;
|
||||
});
|
||||
}
|
||||
|
||||
readonly List<IServerSerializable> debugEntityList = new List<IServerSerializable>();
|
||||
@@ -2035,117 +2080,106 @@ namespace Barotrauma.Networking
|
||||
|
||||
float sendingTime = inc.ReadSingle() - 0.0f;//TODO: reimplement inc.SenderConnection.RemoteTimeOffset;
|
||||
|
||||
ServerNetObject? prevObjHeader = null;
|
||||
long prevBitPos = 0;
|
||||
long prevBytePos = 0;
|
||||
|
||||
long prevBitLength = 0;
|
||||
long prevByteLength = 0;
|
||||
|
||||
ServerNetObject? objHeader = null;
|
||||
try
|
||||
SegmentTableReader<ServerNetSegment>.Read(inc,
|
||||
segmentDataReader: (segment, inc) =>
|
||||
{
|
||||
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
|
||||
switch (segment)
|
||||
{
|
||||
switch (objHeader)
|
||||
{
|
||||
case ServerNetObject.SYNC_IDS:
|
||||
lastSentChatMsgID = inc.ReadUInt16();
|
||||
LastSentEntityEventID = inc.ReadUInt16();
|
||||
case ServerNetSegment.SyncIds:
|
||||
lastSentChatMsgID = inc.ReadUInt16();
|
||||
LastSentEntityEventID = inc.ReadUInt16();
|
||||
|
||||
bool campaignUpdated = inc.ReadBoolean();
|
||||
inc.ReadPadBits();
|
||||
if (campaignUpdated)
|
||||
{
|
||||
MultiPlayerCampaign.ClientRead(inc);
|
||||
}
|
||||
else if (GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign)
|
||||
{
|
||||
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
|
||||
}
|
||||
break;
|
||||
case ServerNetObject.ENTITY_POSITION:
|
||||
inc.ReadPadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
|
||||
|
||||
bool isItem = inc.ReadBoolean(); inc.ReadPadBits();
|
||||
UInt32 incomingUintIdentifier = inc.ReadUInt32();
|
||||
UInt16 id = inc.ReadUInt16();
|
||||
uint msgLength = inc.ReadVariableUInt32();
|
||||
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
|
||||
bool campaignUpdated = inc.ReadBoolean();
|
||||
inc.ReadPadBits();
|
||||
if (campaignUpdated)
|
||||
{
|
||||
MultiPlayerCampaign.ClientRead(inc);
|
||||
}
|
||||
else if (GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign)
|
||||
{
|
||||
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
|
||||
}
|
||||
break;
|
||||
case ServerNetSegment.EntityPosition:
|
||||
inc.ReadPadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
|
||||
|
||||
bool isItem = inc.ReadBoolean(); inc.ReadPadBits();
|
||||
UInt32 incomingUintIdentifier = inc.ReadUInt32();
|
||||
UInt16 id = inc.ReadUInt16();
|
||||
uint msgLength = inc.ReadVariableUInt32();
|
||||
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
|
||||
|
||||
var entity = Entity.FindEntityByID(id) as IServerPositionSync;
|
||||
if (msgEndPos > inc.LengthBits)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
|
||||
return;
|
||||
}
|
||||
var entity = Entity.FindEntityByID(id) as IServerPositionSync;
|
||||
if (msgEndPos > inc.LengthBits)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
|
||||
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.Yes;
|
||||
}
|
||||
|
||||
debugEntityList.Add(entity);
|
||||
if (entity != null)
|
||||
debugEntityList.Add(entity);
|
||||
if (entity != null)
|
||||
{
|
||||
if (entity is Item != isItem)
|
||||
{
|
||||
if (entity is Item != isItem)
|
||||
{
|
||||
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(isItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
|
||||
}
|
||||
else if (entity is MapEntity { Prefab: { UintIdentifier: { } uintIdentifier } } me &&
|
||||
uintIdentifier != incomingUintIdentifier)
|
||||
{
|
||||
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message."
|
||||
+$"Entity identifier does not match (server entity is {MapEntityPrefab.List.FirstOrDefault(p => p.UintIdentifier == incomingUintIdentifier)?.Identifier.Value ?? "[not found]"}, "
|
||||
+$"client entity is {me.Prefab.Identifier}). Ignoring the message...");
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.ClientReadPosition(inc, sendingTime);
|
||||
}
|
||||
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(isItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
|
||||
}
|
||||
|
||||
//force to the correct position in case the entity doesn't exist
|
||||
//or the message wasn't read correctly for whatever reason
|
||||
inc.BitPosition = msgEndPos;
|
||||
inc.ReadPadBits();
|
||||
break;
|
||||
case ServerNetObject.CLIENT_LIST:
|
||||
ReadClientList(inc);
|
||||
break;
|
||||
case ServerNetObject.ENTITY_EVENT:
|
||||
case ServerNetObject.ENTITY_EVENT_INITIAL:
|
||||
if (!EntityEventManager.Read(objHeader.Value, inc, sendingTime, debugEntityList))
|
||||
else if (entity is MapEntity { Prefab: { UintIdentifier: { } uintIdentifier } } me &&
|
||||
uintIdentifier != incomingUintIdentifier)
|
||||
{
|
||||
return;
|
||||
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message."
|
||||
+$"Entity identifier does not match (server entity is {MapEntityPrefab.List.FirstOrDefault(p => p.UintIdentifier == incomingUintIdentifier)?.Identifier.Value ?? "[not found]"}, "
|
||||
+$"client entity is {me.Prefab.Identifier}). Ignoring the message...");
|
||||
}
|
||||
break;
|
||||
case ServerNetObject.CHAT_MESSAGE:
|
||||
ChatMessage.ClientRead(inc);
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unknown object header \"{objHeader}\"!)");
|
||||
}
|
||||
prevBitLength = inc.BitPosition - prevBitPos;
|
||||
prevByteLength = inc.BytePosition - prevBytePos;
|
||||
else
|
||||
{
|
||||
entity.ClientReadPosition(inc, sendingTime);
|
||||
}
|
||||
}
|
||||
|
||||
prevObjHeader = objHeader;
|
||||
prevBitPos = inc.BitPosition;
|
||||
prevBytePos = inc.BytePosition;
|
||||
//force to the correct position in case the entity doesn't exist
|
||||
//or the message wasn't read correctly for whatever reason
|
||||
inc.BitPosition = msgEndPos;
|
||||
inc.ReadPadBits();
|
||||
break;
|
||||
case ServerNetSegment.ClientList:
|
||||
ReadClientList(inc);
|
||||
break;
|
||||
case ServerNetSegment.EntityEvent:
|
||||
case ServerNetSegment.EntityEventInitial:
|
||||
if (!EntityEventManager.Read(segment, inc, sendingTime, debugEntityList))
|
||||
{
|
||||
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.Yes;
|
||||
}
|
||||
break;
|
||||
case ServerNetSegment.ChatMessage:
|
||||
ChatMessage.ClientRead(inc);
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unknown segment \"{segment}\"!)");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.No;
|
||||
},
|
||||
exceptionHandler: (segment, prevSegments, ex) =>
|
||||
{
|
||||
List<string> errorLines = new List<string>
|
||||
{
|
||||
ex.Message,
|
||||
"Message length: " + inc.LengthBits + " (" + inc.LengthBytes + " bytes)",
|
||||
"Read position: " + inc.BitPosition,
|
||||
"Header: " + (objHeader != null ? objHeader.Value.ToString() : "Error occurred on the very first header!"),
|
||||
prevObjHeader != null ? "Previous header: " + prevObjHeader : "Error occurred on the very first header!",
|
||||
"Previous object was " + (prevBitLength) + " bits long (" + (prevByteLength) + " bytes)",
|
||||
" "
|
||||
$"Segment with error: {segment}"
|
||||
};
|
||||
if (prevSegments.Any())
|
||||
{
|
||||
errorLines.Add("Prev segments: " + string.Join(", ", prevSegments));
|
||||
errorLines.Add(" ");
|
||||
}
|
||||
errorLines.Add(ex.StackTrace.CleanupStackTrace());
|
||||
errorLines.Add(" ");
|
||||
if (prevObjHeader == ServerNetObject.ENTITY_EVENT || prevObjHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
|
||||
objHeader == ServerNetObject.ENTITY_EVENT || objHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
|
||||
objHeader == ServerNetObject.ENTITY_POSITION || prevObjHeader == ServerNetObject.ENTITY_POSITION)
|
||||
if (prevSegments.Concat(segment.ToEnumerable()).Any(s => s.Identifier
|
||||
is ServerNetSegment.EntityPosition
|
||||
or ServerNetSegment.EntityEvent
|
||||
or ServerNetSegment.EntityEventInitial))
|
||||
{
|
||||
foreach (IServerSerializable ent in debugEntityList)
|
||||
{
|
||||
@@ -2159,34 +2193,18 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string line in errorLines)
|
||||
{
|
||||
DebugConsole.ThrowError(line);
|
||||
}
|
||||
errorLines.Add("Last console messages:");
|
||||
for (int i = DebugConsole.Messages.Count - 1; i > Math.Max(0, DebugConsole.Messages.Count - 20); i--)
|
||||
{
|
||||
errorLines.Add("[" + DebugConsole.Messages[i].Time + "] " + DebugConsole.Messages[i].Text);
|
||||
}
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsManager.ErrorSeverity.Critical, string.Join("\n", errorLines));
|
||||
|
||||
DebugConsole.ThrowError("Writing object data to \"networkerror_data.log\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues");
|
||||
|
||||
using (FileStream fl = File.Open("networkerror_data.log", System.IO.FileMode.Create))
|
||||
{
|
||||
using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(fl))
|
||||
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fl))
|
||||
{
|
||||
bw.Write(inc.Buffer, (int)(prevBytePos - prevByteLength), (int)(prevByteLength));
|
||||
sw.WriteLine("");
|
||||
foreach (string line in errorLines)
|
||||
{
|
||||
sw.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Exception("Read error: please send us \"networkerror_data.log\"!");
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
$"Exception thrown while reading segment {segment.Identifier} at position {segment.Pointer}." +
|
||||
(prevSegments.Any() ? $" Previous segments: {string.Join(", ", prevSegments)}" : ""),
|
||||
ex);
|
||||
});
|
||||
}
|
||||
|
||||
private void SendLobbyUpdate()
|
||||
@@ -2194,50 +2212,51 @@ namespace Barotrauma.Networking
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.WriteByte((byte)ClientPacketHeader.UPDATE_LOBBY);
|
||||
|
||||
outmsg.WriteByte((byte)ClientNetObject.SYNC_IDS);
|
||||
outmsg.WriteUInt16(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
outmsg.WriteUInt16(ChatMessage.LastID);
|
||||
outmsg.WriteUInt16(LastClientListUpdateID);
|
||||
outmsg.WriteUInt16(nameId);
|
||||
outmsg.WriteString(Name);
|
||||
var jobPreferences = GameMain.NetLobbyScreen.JobPreferences;
|
||||
if (jobPreferences.Count > 0)
|
||||
using (var segmentTable = SegmentTableWriter<ClientNetSegment>.StartWriting(outmsg))
|
||||
{
|
||||
outmsg.WriteIdentifier(jobPreferences[0].Prefab.Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
outmsg.WriteIdentifier(Identifier.Empty);
|
||||
}
|
||||
outmsg.WriteByte((byte)MultiplayerPreferences.Instance.TeamPreference);
|
||||
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
|
||||
{
|
||||
outmsg.WriteUInt16((UInt16)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outmsg.WriteUInt16(campaign.LastSaveID);
|
||||
outmsg.WriteByte(campaign.CampaignID);
|
||||
foreach (MultiPlayerCampaign.NetFlags netFlag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
|
||||
segmentTable.StartNewSegment(ClientNetSegment.SyncIds);
|
||||
outmsg.WriteUInt16(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
outmsg.WriteUInt16(ChatMessage.LastID);
|
||||
outmsg.WriteUInt16(LastClientListUpdateID);
|
||||
outmsg.WriteUInt16(nameId);
|
||||
outmsg.WriteString(Name);
|
||||
var jobPreferences = GameMain.NetLobbyScreen.JobPreferences;
|
||||
if (jobPreferences.Count > 0)
|
||||
{
|
||||
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(netFlag));
|
||||
outmsg.WriteIdentifier(jobPreferences[0].Prefab.Identifier);
|
||||
}
|
||||
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
}
|
||||
|
||||
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
|
||||
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
|
||||
{
|
||||
if (outmsg.LengthBytes + chatMsgQueue[i].EstimateLengthBytesClient() > MsgConstants.MTU - 5)
|
||||
else
|
||||
{
|
||||
//no more room in this packet
|
||||
break;
|
||||
outmsg.WriteIdentifier(Identifier.Empty);
|
||||
}
|
||||
chatMsgQueue[i].ClientWrite(outmsg);
|
||||
}
|
||||
outmsg.WriteByte((byte)ClientNetObject.END_OF_MESSAGE);
|
||||
outmsg.WriteByte((byte)MultiplayerPreferences.Instance.TeamPreference);
|
||||
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
|
||||
{
|
||||
outmsg.WriteUInt16((UInt16)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outmsg.WriteUInt16(campaign.LastSaveID);
|
||||
outmsg.WriteByte(campaign.CampaignID);
|
||||
foreach (MultiPlayerCampaign.NetFlags netFlag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
|
||||
{
|
||||
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(netFlag));
|
||||
}
|
||||
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
}
|
||||
|
||||
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
|
||||
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
|
||||
{
|
||||
if (outmsg.LengthBytes + chatMsgQueue[i].EstimateLengthBytesClient() > MsgConstants.MTU - 5)
|
||||
{
|
||||
//no more room in this packet
|
||||
break;
|
||||
}
|
||||
chatMsgQueue[i].ClientWrite(segmentTable, outmsg);
|
||||
}
|
||||
}
|
||||
if (outmsg.LengthBytes > MsgConstants.MTU)
|
||||
{
|
||||
DebugConsole.ThrowError($"Maximum packet size exceeded ({outmsg.LengthBytes} > {MsgConstants.MTU})");
|
||||
@@ -2253,44 +2272,47 @@ namespace Barotrauma.Networking
|
||||
outmsg.WriteBoolean(EntityEventManager.MidRoundSyncingDone);
|
||||
outmsg.WritePadBits();
|
||||
|
||||
outmsg.WriteByte((byte)ClientNetObject.SYNC_IDS);
|
||||
//outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
outmsg.WriteUInt16(ChatMessage.LastID);
|
||||
outmsg.WriteUInt16(EntityEventManager.LastReceivedID);
|
||||
outmsg.WriteUInt16(LastClientListUpdateID);
|
||||
using (var segmentTable = SegmentTableWriter<ClientNetSegment>.StartWriting(outmsg))
|
||||
{
|
||||
segmentTable.StartNewSegment(ClientNetSegment.SyncIds);
|
||||
//outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
outmsg.WriteUInt16(ChatMessage.LastID);
|
||||
outmsg.WriteUInt16(EntityEventManager.LastReceivedID);
|
||||
outmsg.WriteUInt16(LastClientListUpdateID);
|
||||
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
|
||||
{
|
||||
outmsg.WriteUInt16((UInt16)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outmsg.WriteUInt16(campaign.LastSaveID);
|
||||
outmsg.WriteByte(campaign.CampaignID);
|
||||
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
|
||||
{
|
||||
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(flag));
|
||||
outmsg.WriteUInt16((UInt16)0);
|
||||
}
|
||||
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
}
|
||||
|
||||
Character.Controlled?.ClientWriteInput(outmsg);
|
||||
GameMain.GameScreen.Cam?.ClientWrite(outmsg);
|
||||
|
||||
EntityEventManager.Write(outmsg, ClientPeer?.ServerConnection);
|
||||
|
||||
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
|
||||
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
|
||||
{
|
||||
if (outmsg.LengthBytes + chatMsgQueue[i].EstimateLengthBytesClient() > MsgConstants.MTU - 5)
|
||||
else
|
||||
{
|
||||
//not enough room in this packet
|
||||
break;
|
||||
}
|
||||
chatMsgQueue[i].ClientWrite(outmsg);
|
||||
}
|
||||
outmsg.WriteUInt16(campaign.LastSaveID);
|
||||
outmsg.WriteByte(campaign.CampaignID);
|
||||
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
|
||||
{
|
||||
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(flag));
|
||||
}
|
||||
|
||||
outmsg.WriteByte((byte)ClientNetObject.END_OF_MESSAGE);
|
||||
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
}
|
||||
|
||||
Character.Controlled?.ClientWriteInput(segmentTable, outmsg);
|
||||
GameMain.GameScreen.Cam?.ClientWrite(segmentTable, outmsg);
|
||||
|
||||
EntityEventManager.Write(segmentTable, outmsg, ClientPeer?.ServerConnection);
|
||||
|
||||
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
|
||||
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
|
||||
{
|
||||
if (outmsg.LengthBytes + chatMsgQueue[i].EstimateLengthBytesClient() > MsgConstants.MTU - 5)
|
||||
{
|
||||
//not enough room in this packet
|
||||
break;
|
||||
}
|
||||
|
||||
chatMsgQueue[i].ClientWrite(segmentTable, outmsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (outmsg.LengthBytes > MsgConstants.MTU)
|
||||
{
|
||||
@@ -2523,7 +2545,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public override void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (!(entity is IClientSerializable clientSerializable))
|
||||
if (entity is not IClientSerializable clientSerializable)
|
||||
{
|
||||
throw new InvalidCastException($"Entity is not {nameof(IClientSerializable)}");
|
||||
}
|
||||
@@ -2557,46 +2579,10 @@ namespace Barotrauma.Networking
|
||||
public void Quit()
|
||||
{
|
||||
GameMain.LuaCs.Stop();
|
||||
if (ClientPeer is SteamP2PClientPeer || ClientPeer is SteamP2POwnerPeer)
|
||||
{
|
||||
SteamManager.LeaveLobby();
|
||||
}
|
||||
|
||||
GameMain.ModDownloadScreen.Reset();
|
||||
ContentPackageManager.EnabledPackages.Restore();
|
||||
|
||||
CampaignMode.StartRoundCancellationToken?.Cancel();
|
||||
|
||||
|
||||
ClientPeer?.Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
||||
ClientPeer = null;
|
||||
|
||||
foreach (var fileTransfer in FileReceiver.ActiveTransfers.ToArray())
|
||||
{
|
||||
FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
|
||||
}
|
||||
|
||||
if (ChildServerRelay.Process != null)
|
||||
{
|
||||
int checks = 0;
|
||||
while (ChildServerRelay.Process is { HasExited: false })
|
||||
{
|
||||
if (checks > 10)
|
||||
{
|
||||
ChildServerRelay.ShutDown();
|
||||
}
|
||||
Thread.Sleep(100);
|
||||
checks++;
|
||||
}
|
||||
}
|
||||
ChildServerRelay.ShutDown();
|
||||
|
||||
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
|
||||
|
||||
characterInfo?.Remove();
|
||||
|
||||
VoipClient?.Dispose();
|
||||
VoipClient = null;
|
||||
GameMain.Client = null;
|
||||
GameMain.GameSession = null;
|
||||
}
|
||||
|
||||
public void SendCharacterInfo(string newName = null)
|
||||
@@ -2604,7 +2590,6 @@ namespace Barotrauma.Networking
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.WriteByte((byte)ClientPacketHeader.UPDATE_CHARACTERINFO);
|
||||
WriteCharacterInfo(msg, newName);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2644,9 +2629,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.WriteByte((byte)ClientPacketHeader.UPDATE_LOBBY);
|
||||
msg.WriteByte((byte)ClientNetObject.VOTE);
|
||||
Voting.ClientWrite(msg, voteType, data);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
using (var segmentTable = SegmentTableWriter<ClientNetSegment>.StartWriting(msg))
|
||||
{
|
||||
segmentTable.StartNewSegment(ClientNetSegment.Vote);
|
||||
Voting.ClientWrite(msg, voteType, data);
|
||||
}
|
||||
|
||||
ClientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2762,7 +2749,6 @@ namespace Barotrauma.Networking
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.ManageCampaign);
|
||||
campaign.ClientWrite(msg);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
ClientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2811,7 +2797,6 @@ namespace Barotrauma.Networking
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.SelectSub);
|
||||
msg.WriteBoolean(isShuttle); msg.WritePadBits();
|
||||
msg.WriteString(sub.MD5Hash.StringRepresentation);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
ClientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2831,7 +2816,6 @@ namespace Barotrauma.Networking
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.SelectMode);
|
||||
msg.WriteUInt16((UInt16)modeIndex);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
|
||||
ClientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -3537,6 +3521,23 @@ namespace Barotrauma.Networking
|
||||
eventErrorWritten = true;
|
||||
}
|
||||
|
||||
private static void AppendExceptionInfo(ref string errorMsg, Exception e)
|
||||
{
|
||||
if (!errorMsg.EndsWith("\n")) { errorMsg += "\n"; }
|
||||
errorMsg += e.Message + "\n";
|
||||
var innermostException = e.GetInnermost();
|
||||
if (innermostException != e)
|
||||
{
|
||||
// If available, only append the stacktrace of the innermost exception,
|
||||
// because that's the most important one to fix
|
||||
errorMsg += "Inner exception: " + innermostException.Message + "\n" + innermostException.StackTrace.CleanupStackTrace();
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMsg += e.StackTrace.CleanupStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public void ForceTimeOut()
|
||||
{
|
||||
|
||||
+22
-45
@@ -66,7 +66,7 @@ namespace Barotrauma.Networking
|
||||
events.Add(newEvent);
|
||||
}
|
||||
|
||||
public void Write(IWriteMessage msg, NetworkConnection serverConnection)
|
||||
public void Write(in SegmentTableWriter<ClientNetSegment> segmentTable, IWriteMessage msg, NetworkConnection serverConnection)
|
||||
{
|
||||
if (events.Count == 0 || serverConnection == null) return;
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Barotrauma.Networking
|
||||
eventLastSent[entityEvent.ID] = (float)Lidgren.Network.NetTime.Now;
|
||||
}
|
||||
|
||||
msg.WriteByte((byte)ClientNetObject.ENTITY_STATE);
|
||||
segmentTable.StartNewSegment(ClientNetSegment.EntityState);
|
||||
Write(msg, eventsToSync, out _);
|
||||
}
|
||||
|
||||
@@ -112,11 +112,11 @@ namespace Barotrauma.Networking
|
||||
/// <summary>
|
||||
/// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
|
||||
/// </summary>
|
||||
public bool Read(ServerNetObject type, IReadMessage msg, float sendingTime, List<IServerSerializable> entities)
|
||||
public bool Read(ServerNetSegment type, IReadMessage msg, float sendingTime, List<IServerSerializable> entities)
|
||||
{
|
||||
UInt16 unreceivedEntityEventCount = 0;
|
||||
|
||||
if (type == ServerNetObject.ENTITY_EVENT_INITIAL)
|
||||
if (type == ServerNetSegment.EntityEventInitial)
|
||||
{
|
||||
unreceivedEntityEventCount = msg.ReadUInt16();
|
||||
firstNewID = msg.ReadUInt16();
|
||||
@@ -218,43 +218,20 @@ namespace Barotrauma.Networking
|
||||
Microsoft.Xna.Framework.Color.Green);
|
||||
}
|
||||
lastReceivedID++;
|
||||
try
|
||||
ReadEvent(msg, entity, sendingTime);
|
||||
msg.ReadPadBits();
|
||||
|
||||
if (msg.BitPosition != msgPosition + msgLength * 8)
|
||||
{
|
||||
ReadEvent(msg, entity, sendingTime);
|
||||
msg.ReadPadBits();
|
||||
var prevEntity = entities.Count >= 2 ? entities[entities.Count - 2] : null;
|
||||
ushort prevId = prevEntity is Entity p ? p.ID : (ushort)0;
|
||||
string errorMsg = $"Message byte position incorrect after reading an event for the entity \"{entity}\" (ID {(entity is Entity e ? e.ID : 0)}). "
|
||||
+$"The previous entity was \"{prevEntity}\" (ID {prevId}) "
|
||||
+$"Read {msg.BitPosition - msgPosition} bits, expected message length was {msgLength * 8} bits.";
|
||||
|
||||
if (msg.BitPosition != msgPosition + msgLength * 8)
|
||||
{
|
||||
var prevEntity = entities.Count >= 2 ? entities[entities.Count - 2] : null;
|
||||
ushort prevId = prevEntity is Entity p ? p.ID : (ushort)0;
|
||||
string errorMsg = $"Message byte position incorrect after reading an event for the entity \"{entity}\" (ID {(entity is Entity e ? e.ID : 0)}). "
|
||||
+$"The previous entity was \"{prevEntity}\" (ID {prevId}) "
|
||||
+$"Read {msg.BitPosition - msgPosition} bits, expected message length was {msgLength * 8} bits.";
|
||||
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
|
||||
//TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick
|
||||
//msg.BitPosition = (int)(msgPosition + msgLength * 8);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = $"Failed to read event {thisEventID} for entity \"{entity}\"" +
|
||||
$"{(entity is Entity { ID: var entityId } ? $", id {entityId}" : "")} ";
|
||||
DebugConsole.ThrowError(errorMsg, e);
|
||||
|
||||
errorMsg += $"({e.Message})! (MidRoundSyncing: {thisClient.MidRoundSyncing})\n{e.StackTrace.CleanupStackTrace()}";
|
||||
errorMsg += "\nPrevious entities:";
|
||||
for (int j = entities.Count - 2; j >= 0; j--)
|
||||
{
|
||||
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
|
||||
}
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
|
||||
GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
msg.BitPosition = (int)(msgPosition + msgLength * 8);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,16 +254,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ID = 0;
|
||||
|
||||
lastReceivedID = 0;
|
||||
|
||||
firstNewID = null;
|
||||
|
||||
events.Clear();
|
||||
eventLastSent.Clear();
|
||||
|
||||
MidRoundSyncingDone = false;
|
||||
|
||||
ClearSelf();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -297,6 +270,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ID = 0;
|
||||
events.Clear();
|
||||
if (thisClient != null)
|
||||
{
|
||||
thisClient.LastSentEntityEventID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
partial class OrderChatMessage : ChatMessage
|
||||
{
|
||||
public override void ClientWrite(IWriteMessage msg)
|
||||
public override void ClientWrite(in SegmentTableWriter<ClientNetSegment> segmentTableWriter, IWriteMessage msg)
|
||||
{
|
||||
msg.WriteByte((byte)ClientNetObject.CHAT_MESSAGE);
|
||||
segmentTableWriter.StartNewSegment(ClientNetSegment.ChatMessage);
|
||||
msg.WriteUInt16(NetStateID);
|
||||
msg.WriteRangedInteger((int)ChatMessageType.Order, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
|
||||
msg.WriteRangedInteger((int)ChatMode.None, 0, Enum.GetValues(typeof(ChatMode)).Length - 1);
|
||||
|
||||
+4
-8
@@ -91,15 +91,11 @@ namespace Barotrauma.Networking
|
||||
ToolBox.ThrowIfNull(netClient);
|
||||
ToolBox.ThrowIfNull(incomingLidgrenMessages);
|
||||
|
||||
if (isOwner && !(ChildServerRelay.Process is { HasExited: false }))
|
||||
if (isOwner && !ChildServerRelay.IsProcessAlive)
|
||||
{
|
||||
var gameClient = GameMain.Client;
|
||||
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) =>
|
||||
{
|
||||
GameMain.MainMenuScreen.Select();
|
||||
return false;
|
||||
};
|
||||
gameClient?.CreateServerCrashMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,7 +107,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
foreach (NetIncomingMessage inc in incomingLidgrenMessages)
|
||||
{
|
||||
if (!inc.SenderConnection.RemoteEndPoint.Equals(lidgrenEndpoint.NetEndpoint))
|
||||
if (!inc.SenderConnection.RemoteEndPoint.EquivalentTo(lidgrenEndpoint.NetEndpoint))
|
||||
{
|
||||
DebugConsole.AddWarning($"Mismatched endpoint: expected {lidgrenEndpoint.NetEndpoint}, got {inc.SenderConnection.RemoteEndPoint}");
|
||||
continue;
|
||||
|
||||
+3
-9
@@ -187,15 +187,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
if (ChildServerRelay.HasShutDown || !(ChildServerRelay.Process is { HasExited: false }))
|
||||
if (ChildServerRelay.HasShutDown || !ChildServerRelay.IsProcessAlive)
|
||||
{
|
||||
var gameClient = GameMain.Client;
|
||||
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) =>
|
||||
{
|
||||
GameMain.MainMenuScreen.Select();
|
||||
return false;
|
||||
};
|
||||
gameClient?.CreateServerCrashMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -401,8 +397,6 @@ namespace Barotrauma.Networking
|
||||
ClosePeerSession(remotePeers[i]);
|
||||
}
|
||||
|
||||
ChildServerRelay.ClosePipes();
|
||||
|
||||
callbacks.OnDisconnect.Invoke(peerDisconnectPacket);
|
||||
|
||||
SteamManager.LeaveLobby();
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
static class PingUtils
|
||||
{
|
||||
private static readonly Dictionary<IPAddress, int> activePings = new Dictionary<IPAddress, int>();
|
||||
private static readonly Dictionary<IPEndPoint, int> activePings = new Dictionary<IPEndPoint, int>();
|
||||
|
||||
private static bool steamPingInfoReady;
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
switch (serverInfo.Endpoint)
|
||||
{
|
||||
case LidgrenEndpoint { NetEndpoint: { Address: var address } }:
|
||||
case LidgrenEndpoint { NetEndpoint: var endPoint }:
|
||||
|
||||
GetIPAddressPing(serverInfo, address, onPingDiscovered);
|
||||
GetIPAddressPing(serverInfo, endPoint, onPingDiscovered);
|
||||
break;
|
||||
case SteamP2PEndpoint steamP2PEndpoint:
|
||||
TaskPool.Add($"EstimateSteamLobbyPing ({steamP2PEndpoint.StringRepresentation})",
|
||||
@@ -131,9 +131,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetIPAddressPing(ServerInfo serverInfo, IPAddress address, Action<ServerInfo> onPingDiscovered)
|
||||
private static void GetIPAddressPing(ServerInfo serverInfo, IPEndPoint endPoint, Action<ServerInfo> onPingDiscovered)
|
||||
{
|
||||
if (IPAddress.IsLoopback(address))
|
||||
if (IPAddress.IsLoopback(endPoint.Address))
|
||||
{
|
||||
serverInfo.Ping = Option<int>.Some(0);
|
||||
onPingDiscovered(serverInfo);
|
||||
@@ -142,24 +142,24 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
lock (activePings)
|
||||
{
|
||||
if (activePings.ContainsKey(address)) { return; }
|
||||
activePings.Add(address, activePings.Any() ? activePings.Values.Max() + 1 : 0);
|
||||
if (activePings.ContainsKey(endPoint)) { return; }
|
||||
activePings.Add(endPoint, activePings.Any() ? activePings.Values.Max() + 1 : 0);
|
||||
}
|
||||
serverInfo.Ping = Option<int>.None();
|
||||
TaskPool.Add($"PingServerAsync ({address})", PingServerAsync(address, 1000),
|
||||
TaskPool.Add($"PingServerAsync ({endPoint})", PingServerAsync(endPoint, 1000),
|
||||
rtt =>
|
||||
{
|
||||
if (!rtt.TryGetResult(out serverInfo.Ping)) { serverInfo.Ping = Option<int>.None(); }
|
||||
onPingDiscovered(serverInfo);
|
||||
lock (activePings)
|
||||
{
|
||||
activePings.Remove(address);
|
||||
activePings.Remove(endPoint);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<Option<int>> PingServerAsync(IPAddress ipAddress, int timeOut)
|
||||
private static async Task<Option<int>> PingServerAsync(IPEndPoint endPoint, int timeOut)
|
||||
{
|
||||
await Task.Yield();
|
||||
bool shouldGo = false;
|
||||
@@ -167,21 +167,21 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
lock (activePings)
|
||||
{
|
||||
shouldGo = activePings.Count(kvp => kvp.Value < activePings[ipAddress]) < 25;
|
||||
shouldGo = activePings.Count(kvp => kvp.Value < activePings[endPoint]) < 25;
|
||||
}
|
||||
await Task.Delay(25);
|
||||
}
|
||||
|
||||
if (ipAddress == null) { return Option<int>.None(); }
|
||||
if (endPoint?.Address == null) { return Option<int>.None(); }
|
||||
|
||||
//don't attempt to ping if the address is IPv6 and it's not supported
|
||||
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6) { return Option<int>.None(); }
|
||||
if (endPoint.Address.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6) { return Option<int>.None(); }
|
||||
|
||||
Ping ping = new Ping();
|
||||
byte[] buffer = new byte[32];
|
||||
try
|
||||
{
|
||||
PingReply pingReply = await ping.SendPingAsync(ipAddress, timeOut, buffer, new PingOptions(128, true));
|
||||
PingReply pingReply = await ping.SendPingAsync(endPoint.Address, timeOut, buffer, new PingOptions(128, true));
|
||||
|
||||
return pingReply.Status switch
|
||||
{
|
||||
@@ -191,9 +191,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ipAddress, GameAnalyticsManager.ErrorSeverity.Warning, "Failed to ping a server - " + (ex?.InnerException?.Message ?? ex.Message));
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + endPoint.Address, GameAnalyticsManager.ErrorSeverity.Warning, "Failed to ping a server - " + (ex?.InnerException?.Message ?? ex.Message));
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Failed to ping a server (" + ipAddress + ") - " + (ex?.InnerException?.Message ?? ex.Message), Color.Red);
|
||||
DebugConsole.NewMessage("Failed to ping a server (" + endPoint.Address + ") - " + (ex?.InnerException?.Message ?? ex.Message), Color.Red);
|
||||
#endif
|
||||
|
||||
return Option<int>.None();
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace Barotrauma.Networking
|
||||
MaxPlayers = incMsg.ReadByte();
|
||||
HasPassword = incMsg.ReadBoolean();
|
||||
IsPublic = incMsg.ReadBoolean();
|
||||
GameMain.NetLobbyScreen.SetPublic(IsPublic);
|
||||
GameMain.Client?.SetLobbyPublic(IsPublic);
|
||||
AllowFileTransfers = incMsg.ReadBoolean();
|
||||
incMsg.ReadPadBits();
|
||||
TickRate = incMsg.ReadRangedInteger(1, 60);
|
||||
@@ -367,6 +367,17 @@ namespace Barotrauma.Networking
|
||||
|
||||
//***********************************************
|
||||
|
||||
//changing server visibility on the fly is not supported in dedicated servers
|
||||
if (GameMain.Client?.ClientPeer is not LidgrenClientPeer)
|
||||
{
|
||||
var isPublic = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
|
||||
TextManager.Get("publicserver"))
|
||||
{
|
||||
ToolTip = TextManager.Get("publicservertooltip")
|
||||
};
|
||||
GetPropertyData(nameof(IsPublic)).AssignGUIComponent(isPublic);
|
||||
}
|
||||
|
||||
// Sub Selection
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverTab.RectTransform), TextManager.Get("ServerSettingsSubSelection"), font: GUIStyle.SubHeadingFont);
|
||||
var selectionFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.02f), serverTab.RectTransform), isHorizontal: true)
|
||||
@@ -475,9 +486,10 @@ namespace Barotrauma.Networking
|
||||
// game settings
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
var roundsTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.Center)) { };
|
||||
var roundsTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.Center));
|
||||
var roundsContent = new GUIListBox(new RectTransform(Vector2.One, roundsTab.RectTransform, Anchor.Center), style: "GUIListBoxNoBorder").Content;
|
||||
|
||||
GUILayoutGroup playStyleLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), roundsTab.RectTransform));
|
||||
GUILayoutGroup playStyleLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), roundsContent.RectTransform));
|
||||
// Play Style Selection
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), playStyleLayout.RectTransform), TextManager.Get("ServerSettingsPlayStyle"), font: GUIStyle.SubHeadingFont);
|
||||
var playstyleList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), playStyleLayout.RectTransform))
|
||||
@@ -502,7 +514,7 @@ namespace Barotrauma.Networking
|
||||
GUITextBlock.AutoScaleAndNormalize(playStyleTickBoxes.Select(t => t.TextBlock));
|
||||
playstyleList.RectTransform.MinSize = new Point(0, (int)(playstyleList.Content.Children.First().Rect.Height * 2.0f + playstyleList.Padding.Y + playstyleList.Padding.W));
|
||||
|
||||
GUILayoutGroup sliderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.35f), roundsTab.RectTransform))
|
||||
GUILayoutGroup sliderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.35f), roundsContent.RectTransform))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
@@ -608,7 +620,7 @@ namespace Barotrauma.Networking
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
GUILayoutGroup losModeLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.14f), roundsTab.RectTransform));
|
||||
GUILayoutGroup losModeLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.14f), roundsContent.RectTransform));
|
||||
|
||||
var losModeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), losModeLayout.RectTransform),
|
||||
TextManager.Get("LosEffect"));
|
||||
@@ -629,7 +641,30 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
GetPropertyData(nameof(LosMode)).AssignGUIComponent(losModeRadioButtonGroup);
|
||||
|
||||
GUILayoutGroup numberLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), roundsTab.RectTransform))
|
||||
GUILayoutGroup healthBarModeLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.14f), roundsContent.RectTransform));
|
||||
|
||||
var healthBarModeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), healthBarModeLayout.RectTransform),
|
||||
TextManager.Get("ShowEnemyHealthBars"));
|
||||
|
||||
var healthBarModeRadioButtonLayout
|
||||
= new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), healthBarModeLayout.RectTransform),
|
||||
isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var healthBarModeRadioButtonGroup = new GUIRadioButtonGroup();
|
||||
EnemyHealthBarMode[] healthBarModeModes = Enum.GetValues<EnemyHealthBarMode>();
|
||||
for (int i = 0; i < healthBarModeModes.Length; i++)
|
||||
{
|
||||
var losTick = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), healthBarModeRadioButtonLayout.RectTransform),
|
||||
TextManager.Get($"ShowEnemyHealthBars.{healthBarModeModes[i]}"),
|
||||
font: GUIStyle.SmallFont, style: "GUIRadioButton");
|
||||
healthBarModeRadioButtonGroup.AddRadioButton(i, losTick);
|
||||
}
|
||||
GetPropertyData(nameof(ShowEnemyHealthBars)).AssignGUIComponent(healthBarModeRadioButtonGroup);
|
||||
|
||||
GUILayoutGroup numberLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), roundsContent.RectTransform))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
@@ -651,7 +686,7 @@ namespace Barotrauma.Networking
|
||||
var disableBotConversationsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), numberLayout.RectTransform), TextManager.Get("ServerSettingsDisableBotConversations"));
|
||||
GetPropertyData(nameof(DisableBotConversations)).AssignGUIComponent(disableBotConversationsBox);
|
||||
|
||||
GUILayoutGroup buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), roundsTab.RectTransform), isHorizontal: true)
|
||||
GUILayoutGroup buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), roundsContent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
|
||||
@@ -17,9 +17,7 @@ namespace Barotrauma.Networking
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<string> CaptureDeviceNames =>
|
||||
Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
|
||||
|
||||
|
||||
private readonly IntPtr captureDevice;
|
||||
|
||||
@@ -169,6 +167,11 @@ namespace Barotrauma.Networking
|
||||
Create(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice, storedBufferID);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<string> GetCaptureDeviceNames()
|
||||
{
|
||||
return Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
|
||||
}
|
||||
|
||||
IntPtr nativeBuffer;
|
||||
readonly short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
readonly short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
@@ -260,6 +263,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Screen.Selected is ModDownloadScreen)
|
||||
{
|
||||
allowEnqueue = false;
|
||||
captureTimer = 0;
|
||||
}
|
||||
|
||||
if (allowEnqueue || captureTimer > 0)
|
||||
{
|
||||
LastEnqueueAudio = DateTime.Now;
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
using Barotrauma.Sounds;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Sounds;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class VoipClient : IDisposable
|
||||
{
|
||||
private GameClient gameClient;
|
||||
private ClientPeer netClient;
|
||||
/// <summary>
|
||||
/// The "near" range of the voice chat (a percentage of either SpeakRange or radio range), further than this the volume starts to diminish
|
||||
/// </summary>
|
||||
const float RangeNear = 0.4f;
|
||||
|
||||
private readonly GameClient gameClient;
|
||||
private readonly ClientPeer netClient;
|
||||
private DateTime lastSendTime;
|
||||
private List<VoipQueue> queues;
|
||||
private readonly List<VoipQueue> queues;
|
||||
|
||||
private UInt16 storedBufferID = 0;
|
||||
|
||||
@@ -32,13 +35,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void RegisterQueue(VoipQueue queue)
|
||||
{
|
||||
if (queue == VoipCapture.Instance) return;
|
||||
if (!queues.Contains(queue)) queues.Add(queue);
|
||||
if (queue == VoipCapture.Instance) { return; }
|
||||
if (!queues.Contains(queue)) { queues.Add(queue); }
|
||||
}
|
||||
|
||||
public void UnregisterQueue(VoipQueue queue)
|
||||
{
|
||||
if (queues.Contains(queue)) queues.Remove(queue);
|
||||
if (queues.Contains(queue)) { queues.Remove(queue); }
|
||||
}
|
||||
|
||||
public void SendToServer()
|
||||
@@ -85,6 +88,7 @@ namespace Barotrauma.Networking
|
||||
public void Read(IReadMessage msg)
|
||||
{
|
||||
byte queueId = msg.ReadByte();
|
||||
float distanceFactor = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
VoipQueue queue = queues.Find(q => q.QueueID == queueId);
|
||||
|
||||
if (queue == null)
|
||||
@@ -105,9 +109,12 @@ namespace Barotrauma.Networking
|
||||
client.VoipSound = new VoipSound(client.Name, GameMain.SoundManager, client.VoipQueue);
|
||||
}
|
||||
GameMain.SoundManager.ForceStreamUpdate();
|
||||
|
||||
client.RadioNoise = 0.0f;
|
||||
if (client.Character != null && !client.Character.IsDead && !client.Character.Removed && client.Character.SpeechImpediment <= 100.0f)
|
||||
{
|
||||
float speechImpedimentMultiplier = 1.0f - client.Character.SpeechImpediment / 100.0f;
|
||||
bool spectating = Character.Controlled == null;
|
||||
float rangeMultiplier = spectating ? 2.0f : 1.0f;
|
||||
WifiComponent radio = null;
|
||||
var messageType = !client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
@@ -115,11 +122,17 @@ namespace Barotrauma.Networking
|
||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
|
||||
if (messageType == ChatMessageType.Radio)
|
||||
{
|
||||
client.VoipSound.SetRange(radio.Range * 0.8f, radio.Range);
|
||||
client.VoipSound.SetRange(radio.Range * RangeNear * speechImpedimentMultiplier * rangeMultiplier, radio.Range * speechImpedimentMultiplier * rangeMultiplier);
|
||||
if (distanceFactor > RangeNear && !spectating)
|
||||
{
|
||||
//noise starts increasing exponentially after 40% range
|
||||
client.RadioNoise = MathF.Pow(MathUtils.InverseLerp(RangeNear, 1.0f, distanceFactor), 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange);
|
||||
|
||||
client.VoipSound.SetRange(ChatMessage.SpeakRange * RangeNear * speechImpedimentMultiplier * rangeMultiplier, ChatMessage.SpeakRange * speechImpedimentMultiplier * rangeMultiplier);
|
||||
}
|
||||
client.VoipSound.UseMuffleFilter =
|
||||
messageType != ChatMessageType.Radio && Character.Controlled != null && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters &&
|
||||
|
||||
Reference in New Issue
Block a user