v0.13.0.11

This commit is contained in:
Joonas Rikkonen
2021-04-22 17:33:08 +03:00
parent 0697d7fc64
commit 8bb31f2893
391 changed files with 17271 additions and 5949 deletions
@@ -3,12 +3,13 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma.Networking
{
partial class BannedPlayer
{
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string endPoint, ulong steamID)
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string endPoint, ulong steamID, string reason, DateTime? expiration)
{
this.Name = name;
this.EndPoint = endPoint;
@@ -16,6 +17,8 @@ namespace Barotrauma.Networking
ParseEndPointAsSteamId();
this.IsRangeBan = isRangeBan;
this.UniqueIdentifier = uniqueIdentifier;
this.Reason = reason;
this.ExpirationTime = expiration;
}
}
@@ -152,12 +155,24 @@ namespace Barotrauma.Networking
bannedPlayers.Clear();
UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32();
for (int i = 0; i < (int)bannedPlayerCount; i++)
{
string name = incMsg.ReadString();
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
bool isRangeBan = incMsg.ReadBoolean(); incMsg.ReadPadBits();
bool isRangeBan = incMsg.ReadBoolean();
bool includesExpiration = incMsg.ReadBoolean();
incMsg.ReadPadBits();
DateTime? expiration = null;
if (includesExpiration)
{
double hoursFromNow = incMsg.ReadDouble();
expiration = DateTime.Now + TimeSpan.FromHours(hoursFromNow);
}
string reason = incMsg.ReadString();
string endPoint = "";
UInt64 steamID = 0;
if (isOwner)
@@ -170,7 +185,7 @@ namespace Barotrauma.Networking
endPoint = "Endpoint concealed by host";
steamID = 0;
}
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, endPoint, steamID));
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, endPoint, steamID, reason, expiration));
}
if (banFrame != null)
@@ -15,7 +15,7 @@ namespace Barotrauma.Networking
public static void ClientRead(IReadMessage msg)
{
UInt16 ID = msg.ReadUInt16();
UInt16 id = msg.ReadUInt16();
ChatMessageType type = (ChatMessageType)msg.ReadByte();
PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None;
string txt = "";
@@ -29,6 +29,14 @@ namespace Barotrauma.Networking
string senderName = msg.ReadString();
Character senderCharacter = null;
Client senderClient = null;
bool hasSenderClient = msg.ReadBoolean();
if (hasSenderClient)
{
UInt64 clientId = msg.ReadUInt64();
senderClient = GameMain.Client.ConnectedClients.Find(c => c.SteamID == clientId || c.ID == clientId);
if (senderClient != null) { senderName = senderClient.Name; }
}
bool hasSenderCharacter = msg.ReadBoolean();
if (hasSenderCharacter)
{
@@ -38,6 +46,7 @@ namespace Barotrauma.Networking
senderName = senderCharacter.Name;
}
}
msg.ReadPadBits();
switch (type)
{
@@ -48,7 +57,54 @@ namespace Barotrauma.Networking
UInt16 targetCharacterID = msg.ReadUInt16();
Character targetCharacter = Entity.FindEntityByID(targetCharacterID) as Character;
Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16());
int optionIndex = msg.ReadByte();
Order orderPrefab = null;
int? optionIndex = null;
string orderOption = null;
// The option of a Dismiss order is written differently so we know what order we target
// now that the game supports multiple current orders simultaneously
if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count)
{
orderPrefab = Order.PrefabList[orderIndex];
if (orderPrefab.Identifier != "dismissed")
{
optionIndex = msg.ReadByte();
}
// Does the dismiss order have a specified target?
else if (msg.ReadBoolean())
{
int identifierCount = msg.ReadByte();
if (identifierCount > 0)
{
int dismissedOrderIndex = msg.ReadByte();
Order dismissedOrderPrefab = null;
if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count)
{
dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex];
orderOption = dismissedOrderPrefab.Identifier;
}
if (identifierCount > 1)
{
int dismissedOrderOptionIndex = msg.ReadByte();
if (dismissedOrderPrefab != null)
{
var options = dismissedOrderPrefab.Options;
if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length)
{
orderOption += $".{options[dismissedOrderOptionIndex]}";
}
}
}
}
}
}
else
{
optionIndex = msg.ReadByte();
}
int orderPriority = msg.ReadByte();
OrderTarget orderTargetPosition = null;
Order.OrderTargetType orderTargetType = (Order.OrderTargetType)msg.ReadByte();
int wallSectionIndex = 0;
@@ -64,22 +120,18 @@ namespace Barotrauma.Networking
wallSectionIndex = msg.ReadByte();
}
Order orderPrefab;
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
{
DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
if (NetIdUtils.IdMoreRecent(ID, LastID)) { LastID = ID; }
if (NetIdUtils.IdMoreRecent(id, LastID)) { LastID = id; }
return;
}
else
{
orderPrefab = Order.PrefabList[orderIndex];
}
string orderOption = "";
if (optionIndex >= 0 && optionIndex < orderPrefab.Options.Length)
{
orderOption = orderPrefab.Options[optionIndex];
orderPrefab ??= Order.PrefabList[orderIndex];
}
orderOption ??= optionIndex.HasValue && optionIndex >= 0 && optionIndex < orderPrefab.Options.Length ? orderPrefab.Options[optionIndex.Value] : "";
txt = orderPrefab.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
@@ -107,16 +159,16 @@ namespace Barotrauma.Networking
}
else if (targetCharacter != null)
{
targetCharacter.SetOrder(order, orderOption, senderCharacter);
targetCharacter.SetOrder(order, orderOption, orderPriority, senderCharacter);
}
}
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
if (NetIdUtils.IdMoreRecent(id, LastID))
{
GameMain.Client.AddChatMessage(
new OrderChatMessage(orderPrefab, orderOption, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
LastID = ID;
new OrderChatMessage(orderPrefab, orderOption, orderPriority, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
LastID = id;
}
return;
case ChatMessageType.ServerMessageBox:
@@ -128,7 +180,7 @@ namespace Barotrauma.Networking
break;
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
if (NetIdUtils.IdMoreRecent(id, LastID))
{
switch (type)
{
@@ -154,10 +206,10 @@ namespace Barotrauma.Networking
GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
break;
default:
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter, changeType);
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType);
break;
}
LastID = ID;
LastID = id;
}
}
}
@@ -114,7 +114,7 @@ namespace Barotrauma.Networking
return;
}
Permissions = permissions;
PermittedConsoleCommands = new List<DebugConsole.Command>(permittedConsoleCommands);
PermittedConsoleCommands.Clear(); PermittedConsoleCommands.AddRange(permittedConsoleCommands);
}
public void GivePermission(ClientPermissions permission)
@@ -56,6 +56,11 @@ namespace Barotrauma.Networking
public readonly NetStats NetStats;
protected GUITickBox cameraFollowsSub;
public GUITickBox FollowSubTickBox => cameraFollowsSub;
public bool IsFollowSubTickBoxVisible =>
gameStarted && Screen.Selected == GameMain.GameScreen &&
cameraFollowsSub != null && cameraFollowsSub.Visible;
public CameraTransition EndCinematic;
@@ -159,6 +164,12 @@ namespace Barotrauma.Networking
get { return entityEventManager; }
}
public bool? WaitForNextRoundRespawn
{
get;
set;
}
private readonly object serverEndpoint;
private readonly int ownerKey;
private readonly bool steamP2POwner;
@@ -185,10 +196,10 @@ namespace Barotrauma.Networking
CanBeFocused = false
};
cameraFollowsSub = new GUITickBox(new RectTransform(new Vector2(0.05f, 0.05f), inGameHUD.RectTransform, anchor: Anchor.TopCenter)
cameraFollowsSub = new GUITickBox(new RectTransform(new Vector2(0.05f, 0.05f), inGameHUD.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.CenterLeft)
{
AbsoluteOffset = new Point(0, 5),
MaxSize = new Point(25, 25)
AbsoluteOffset = new Point(0, HUDLayoutSettings.ButtonAreaTop.Y + HUDLayoutSettings.ButtonAreaTop.Height / 2),
MaxSize = new Point(GUI.IntScale(25))
}, TextManager.Get("CamFollowSubmarine"))
{
Selected = Camera.FollowSub,
@@ -857,12 +868,25 @@ namespace Barotrauma.Networking
string endMessage = string.Empty;
endMessage = inc.ReadString();
bool missionSuccessful = inc.ReadBoolean();
byte missionCount = inc.ReadByte();
for (int i = 0; i < missionCount; i++)
{
bool missionSuccessful = inc.ReadBoolean();
var mission = GameMain.GameSession?.GetMission(i);
if (mission != null)
{
mission.Completed = missionSuccessful;
}
}
CharacterTeamType winningTeam = (CharacterTeamType)inc.ReadByte();
if (missionSuccessful && GameMain.GameSession?.Mission != null)
if (winningTeam != CharacterTeamType.None)
{
GameMain.GameSession.WinningTeam = winningTeam;
GameMain.GameSession.Mission.Completed = true;
var combatMission = GameMain.GameSession.Missions.FirstOrDefault(m => m is CombatMission);
if (combatMission != null)
{
combatMission.Completed = true;
}
}
byte traitorCount = inc.ReadByte();
@@ -928,7 +952,11 @@ namespace Barotrauma.Networking
ReadTraitorMessage(inc);
break;
case ServerPacketHeader.MISSION:
GameMain.GameSession?.Mission?.ClientRead(inc);
{
int missionIndex = inc.ReadByte();
Mission mission = GameMain.GameSession?.GetMission(missionIndex);
mission?.ClientRead(inc);
}
break;
case ServerPacketHeader.EVENTACTION:
GameMain.GameSession?.EventManager.ClientRead(inc);
@@ -959,17 +987,26 @@ namespace Barotrauma.Networking
throw new Exception(errorMsg);
}
string missionIdentifier = inc.ReadString() ?? "";
if (missionIdentifier != (GameMain.GameSession.Mission?.Prefab.Identifier ?? ""))
byte missionCount = inc.ReadByte();
if (missionCount != GameMain.GameSession.Missions.Count())
{
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {missionIdentifier ?? "null"}, client: {GameMain.GameSession.Mission?.Prefab.Identifier ?? ""})";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
string errorMsg = $"Mission equality check failed. Mission count doesn't match the server (server: {missionCount}, client: {GameMain.GameSession.Missions.Count()})";
throw new Exception(errorMsg);
}
foreach (Mission mission in GameMain.GameSession.Missions)
{
string missionIdentifier = inc.ReadString() ?? "";
if (missionIdentifier != mission.Prefab.Identifier)
{
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {missionIdentifier ?? "null"}, client: {mission.Prefab.Identifier})";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg);
}
}
byte equalityCheckValueCount = inc.ReadByte();
List<int> levelEqualityCheckValues = new List<int>();
for (int i = 0; i<equalityCheckValueCount; i++)
for (int i = 0; i < equalityCheckValueCount; i++)
{
levelEqualityCheckValues.Add(inc.ReadInt32());
}
@@ -1004,7 +1041,10 @@ namespace Barotrauma.Networking
}
}
GameMain.GameSession.Mission?.ClientReadInitial(inc);
foreach (Mission mission in GameMain.GameSession.Missions)
{
mission.ClientReadInitial(inc);
}
roundInitStatus = RoundInitStatus.Started;
}
@@ -1395,6 +1435,8 @@ namespace Barotrauma.Networking
EndVoteTickBox.Selected = false;
WaitForNextRoundRespawn = null;
roundInitStatus = RoundInitStatus.Starting;
int seed = inc.ReadInt32();
@@ -1411,6 +1453,7 @@ namespace Barotrauma.Networking
bool respawnAllowed = inc.ReadBoolean();
serverSettings.AllowDisguises = inc.ReadBoolean();
serverSettings.AllowRewiring = inc.ReadBoolean();
serverSettings.LockAllDefaultWires = inc.ReadBoolean();
serverSettings.AllowRagdollButton = inc.ReadBoolean();
GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
@@ -1432,7 +1475,12 @@ namespace Barotrauma.Networking
string subHash = inc.ReadString();
string shuttleName = inc.ReadString();
string shuttleHash = inc.ReadString();
int missionIndex = inc.ReadInt16();
List<int> missionIndices = new List<int>();
int missionCount = inc.ReadByte();
for (int i = 0; i < missionCount; i++)
{
missionIndices.Add(inc.ReadInt16());
}
if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList))
{
roundInitStatus = RoundInitStatus.Interrupted;
@@ -1486,7 +1534,9 @@ namespace Barotrauma.Networking
yield return CoroutineStatus.Failure;
}
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, gameMode, missionPrefab: missionIndex < 0 ? null : MissionPrefab.List[missionIndex]);
var selectedMissions = missionIndices.Select(i => MissionPrefab.List[i]);
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, gameMode, missionPrefabs: selectedMissions);
GameMain.GameSession.StartRound(levelSeed, levelDifficulty);
}
else
@@ -1653,13 +1703,15 @@ namespace Barotrauma.Networking
}
foreach (Submarine sub in Submarine.MainSubs[i].DockedTo)
{
if (sub.Info.Type == SubmarineType.Outpost) { continue; }
sub.TeamID = teamID;
}
}
if (respawnAllowed)
{
respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle && gameMode != GameModePreset.MultiPlayerCampaign ? GameMain.NetLobbyScreen.SelectedShuttle : null);
if (respawnAllowed)
{
bool isOutpost = GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && Level.Loaded?.Type == LevelData.LevelType.Outpost;
respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle && !isOutpost ? GameMain.NetLobbyScreen.SelectedShuttle : null);
}
gameStarted = true;
@@ -1702,6 +1754,7 @@ namespace Barotrauma.Networking
gameStarted = false;
Character.Controlled = null;
WaitForNextRoundRespawn = null;
SpawnAsTraitor = false;
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
@@ -1712,7 +1765,7 @@ namespace Barotrauma.Networking
// Enable characters near the main sub for the endCinematic
foreach (Character c in Character.CharacterList)
{
if (Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition) < NetConfig.EnableCharacterDistSqr)
if (Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition) < MathUtils.Pow2(c.Params.DisableDistance))
{
c.Enabled = true;
}
@@ -1757,8 +1810,10 @@ namespace Barotrauma.Networking
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
if (matchingSub == null)
{
matchingSub = new SubmarineInfo(Path.Combine(SubmarineInfo.SavePath, subName) + ".sub", subHash, tryLoad: false);
matchingSub.SubmarineClass = (SubmarineClass)subClass;
matchingSub = new SubmarineInfo(Path.Combine(SubmarineInfo.SavePath, subName) + ".sub", subHash, tryLoad: false)
{
SubmarineClass = (SubmarineClass)subClass
};
}
matchingSub.RequiredContentPackagesInstalled = requiredContentPackagesInstalled;
ServerSubmarines.Add(matchingSub);
@@ -1928,14 +1983,19 @@ namespace Barotrauma.Networking
}
foreach (Client client in ConnectedClients)
{
if (!previouslyConnectedClients.Any(c => c.ID == client.ID))
int index = previouslyConnectedClients.FindIndex(c => c.ID == client.ID);
if (index < 0)
{
while (previouslyConnectedClients.Count > 100)
if (previouslyConnectedClients.Count > 100)
{
previouslyConnectedClients.RemoveAt(0);
previouslyConnectedClients.RemoveRange(0, previouslyConnectedClients.Count - 100);
}
previouslyConnectedClients.Add(client);
}
else
{
previouslyConnectedClients.RemoveAt(index);
}
previouslyConnectedClients.Add(client);
}
if (updateClientListId) { LastClientListUpdateID = listId; }
@@ -2028,6 +2088,8 @@ namespace Barotrauma.Networking
bool autoRestartEnabled = inc.ReadBoolean();
float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f;
bool radiationEnabled = inc.ReadBoolean();
//ignore the message if we already a more up-to-date one
//or if we're still waiting for the initial update
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) &&
@@ -2038,8 +2100,14 @@ namespace Barotrauma.Networking
serverSettings.ClientRead(settingsBuf);
if (!IsServerOwner)
{
ServerInfo info = GameMain.ServerListScreen.UpdateServerInfoWithServerSettings(serverEndpoint, serverSettings);
ServerInfo info = serverSettings.GetServerListInfo();
GameMain.ServerListScreen.AddToRecentServers(info);
GameMain.NetLobbyScreen.Favorite.Visible = true;
GameMain.NetLobbyScreen.Favorite.Selected = GameMain.ServerListScreen.IsFavorite(info);
}
else
{
GameMain.NetLobbyScreen.Favorite.Visible = false;
}
GameMain.NetLobbyScreen.LastUpdateID = updateID;
@@ -2083,8 +2151,9 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating);
GameMain.NetLobbyScreen.LevelSeed = levelSeed;
GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty);
GameMain.NetLobbyScreen.SetBotCount(botCount);
GameMain.NetLobbyScreen.SetRadiationEnabled(radiationEnabled);
GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode);
GameMain.NetLobbyScreen.SetBotCount(botCount);
GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer);
serverSettings.VoiceChatEnabled = voiceChatEnabled;
@@ -2325,7 +2394,7 @@ namespace Barotrauma.Networking
if (outmsg.LengthBytes > MsgConstants.MTU)
{
DebugConsole.ThrowError("Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU);
DebugConsole.ThrowError($"Maximum packet size exceeded ({outmsg.LengthBytes} > {MsgConstants.MTU})");
}
clientPeer.Send(outmsg, DeliveryMethod.Unreliable);
@@ -2376,7 +2445,7 @@ namespace Barotrauma.Networking
if (outmsg.LengthBytes > MsgConstants.MTU)
{
DebugConsole.ThrowError("Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU);
DebugConsole.ThrowError($"Maximum packet size exceeded ({outmsg.LengthBytes} > {MsgConstants.MTU})");
}
clientPeer.Send(outmsg, DeliveryMethod.Unreliable);
@@ -2406,6 +2475,15 @@ namespace Barotrauma.Networking
chatMsgQueue.Add(chatMessage);
}
public void SendRespawnPromptResponse(bool waitForNextRoundRespawn)
{
WaitForNextRoundRespawn = waitForNextRoundRespawn;
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.READY_TO_SPAWN);
msg.Write((bool)waitForNextRoundRespawn);
clientPeer?.Send(msg, DeliveryMethod.Reliable);
}
public void RequestFile(FileTransferType fileType, string file, string fileHash)
{
IWriteMessage msg = new WriteOnlyMessage();
@@ -2528,7 +2606,7 @@ namespace Barotrauma.Networking
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.CampaignID != campaignID)
{
string savePath = transfer.FilePath;
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
@@ -2876,7 +2954,7 @@ namespace Barotrauma.Networking
clientPeer.Send(msg, DeliveryMethod.Reliable);
}
public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed)
public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed, CampaignSettings settings)
{
GameMain.NetLobbyScreen.CampaignSetupFrame.Visible = false;
GameMain.NetLobbyScreen.CampaignFrame.Visible = false;
@@ -2891,6 +2969,7 @@ namespace Barotrauma.Networking
msg.Write(mapSeed);
msg.Write(sub.Name);
msg.Write(sub.MD5Hash.Hash);
settings.Serialize(msg);
clientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -3096,11 +3175,11 @@ namespace Barotrauma.Networking
cameraFollowsSub.Visible = Character.Controlled == null;
}
if (Character.Controlled == null || Character.Controlled.IsDead)
/*if (Character.Controlled == null || Character.Controlled.IsDead)
{
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
}
}*/
}
//tab doesn't autoselect the chatbox when debug console is open,
@@ -3208,9 +3287,13 @@ namespace Barotrauma.Networking
if (respawnManager != null)
{
string respawnText = "";
float textScale = 1.0f;
string respawnText = string.Empty;
Color textColor = Color.White;
bool canChooseRespawn =
GameMain.GameSession.GameMode is CampaignMode &&
Character.Controlled == null &&
Level.Loaded?.Type != LevelData.LevelType.Outpost &&
(characterInfo == null || HasSpawned);
if (respawnManager.CurrentState == RespawnManager.State.Waiting &&
respawnManager.RespawnCountdownStarted)
{
@@ -3228,18 +3311,18 @@ namespace Barotrauma.Networking
{
//oscillate between 0-1
float phase = (float)(Math.Sin(timeLeft * MathHelper.Pi) + 1.0f) * 0.5f;
textScale = 1.0f + phase * 0.5f;
//textScale = 1.0f + phase * 0.5f;
textColor = Color.Lerp(GUI.Style.Red, Color.White, 1.0f - phase);
}
canChooseRespawn = false;
}
if (!string.IsNullOrEmpty(respawnText))
{
GUI.SmallFont.DrawString(spriteBatch, respawnText, new Vector2(120.0f, 10), textColor, 0.0f, Vector2.Zero, textScale, Microsoft.Xna.Framework.Graphics.SpriteEffects.None, 0.0f);
}
GameMain.GameSession?.SetRespawnInfo(
visible: !string.IsNullOrEmpty(respawnText) || canChooseRespawn, text: respawnText, textColor: textColor,
buttonsVisible: canChooseRespawn, waitForNextRoundRespawn: (WaitForNextRoundRespawn ?? true));
}
if (!ShowNetStats) return;
if (!ShowNetStats) { return; }
NetStats.Draw(spriteBatch, new Rectangle(300, 10, 300, 150));
@@ -3367,9 +3450,12 @@ namespace Barotrauma.Networking
{
var banReasonPrompt = new GUIMessageBox(
TextManager.Get(ban ? "BanReasonPrompt" : "KickReasonPrompt"),
"", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") }, new Vector2(0.25f, 0.22f), new Point(400, 220));
"", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") }, new Vector2(0.25f, 0.25f), new Point(400, 260));
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.6f), banReasonPrompt.InnerFrame.RectTransform, Anchor.Center));
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.6f), banReasonPrompt.InnerFrame.RectTransform, Anchor.Center))
{
AbsoluteSpacing = GUI.IntScale(5)
};
var banReasonBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.3f), content.RectTransform))
{
Wrap = true,
@@ -3380,10 +3466,9 @@ namespace Barotrauma.Networking
GUITickBox permaBanTickBox = null;
if (ban)
{
{
var labelContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), content.RectTransform), isHorizontal: false);
new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), labelContainer.RectTransform), TextManager.Get("BanDuration")) { Padding = Vector4.Zero };
new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), labelContainer.RectTransform), TextManager.Get("BanDuration"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
var buttonContent = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), labelContainer.RectTransform), isHorizontal: true);
permaBanTickBox = new GUITickBox(new RectTransform(new Vector2(0.4f, 0.15f), buttonContent.RectTransform), TextManager.Get("BanPermanent"))
{
@@ -3494,12 +3579,19 @@ namespace Barotrauma.Networking
errorLines.Add("Campaign ID: " + campaign.CampaignID);
errorLines.Add("Campaign save ID: " + campaign.LastSaveID + "(pending: " + campaign.PendingSaveID + ")");
}
errorLines.Add("Mission: " + (GameMain.GameSession?.Mission?.Prefab.Identifier ?? "none"));
foreach (Mission mission in GameMain.GameSession.Missions)
{
errorLines.Add("Mission: " + mission.Prefab.Identifier);
}
}
if (GameMain.GameSession?.Submarine != null)
{
errorLines.Add("Submarine: " + GameMain.GameSession.Submarine.Info.Name);
}
if (GameMain.NetworkMember?.RespawnManager?.RespawnShuttle != null)
{
errorLines.Add("Respawn shuttle: " + GameMain.NetworkMember.RespawnManager.RespawnShuttle.Info.Name);
}
if (Level.Loaded != null)
{
errorLines.Add("Level: " + Level.Loaded.Seed + ", " + string.Join(", ", Level.Loaded.EqualityCheckValues.Select(cv => cv.ToString("X"))));
@@ -1,6 +1,4 @@
using System;
namespace Barotrauma.Networking
namespace Barotrauma.Networking
{
partial class OrderChatMessage : ChatMessage
{
@@ -9,26 +7,7 @@ namespace Barotrauma.Networking
msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);
msg.Write((byte)ChatMessageType.Order);
msg.Write((byte)Order.PrefabList.IndexOf(Order.Prefab));
msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID);
msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0);
msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption));
msg.Write((byte)Order.TargetType);
if (Order.TargetType == Order.OrderTargetType.Position && TargetEntity is OrderTarget orderTarget)
{
msg.Write(true);
msg.Write(orderTarget.Position.X);
msg.Write(orderTarget.Position.Y);
msg.Write(orderTarget.Hull == null ? (UInt16)0 : orderTarget.Hull.ID);
}
else
{
msg.Write(false);
if (Order.TargetType == Order.OrderTargetType.WallSection)
{
msg.Write((byte)(WallSectionIndex ?? Order.WallSectionIndex ?? 0));
}
}
WriteOrder(msg);
}
}
}
@@ -147,13 +147,36 @@ namespace Barotrauma.Networking
if (missingPackages.Count > 0)
{
var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0);
var mismatchedButDownloaded = missingPackages.Where(p =>
{
var localMatching = ContentPackage.RegularPackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId);
localMatching ??= ContentPackage.CorePackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId);
if (nonDownloadable.Any())
return localMatching != null;
});
if (mismatchedButDownloaded.Any())
{
string disconnectMsg;
if (mismatchedButDownloaded.Count() == 1)
{
disconnectMsg = $"DisconnectMessage.MismatchedWorkshopMod~[incompatiblecontentpackage]={GetPackageStr(mismatchedButDownloaded.First())}";
}
else
{
List<string> packageStrs = new List<string>();
mismatchedButDownloaded.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
disconnectMsg = $"DisconnectMessage.MismatchedWorkshopMods~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}";
}
Close(disconnectMsg, disableReconnect: true);
OnDisconnectMessageReceived?.Invoke(DisconnectReason.MissingContentPackage + "/" + disconnectMsg);
}
else if (nonDownloadable.Any())
{
string disconnectMsg;
if (nonDownloadable.Count() == 1)
{
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}";
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(nonDownloadable.First())}";
}
else
{
@@ -56,6 +56,7 @@ namespace Barotrauma.Networking
Steamworks.SteamNetworking.AllowP2PPacketRelay(true);
ServerConnection = new SteamP2PConnection("Server", hostSteamId);
ServerConnection.SetOwnerSteamIDIfUnknown(hostSteamId);
incomingInitializationMessages = new List<IReadMessage>();
incomingDataMessages = new List<IReadMessage>();
@@ -17,6 +17,7 @@ namespace Barotrauma.Networking
class RemotePeer
{
public UInt64 SteamID;
public UInt64 OwnerSteamID;
public double? DisconnectTime;
public bool Authenticating;
public bool Authenticated;
@@ -31,6 +32,7 @@ namespace Barotrauma.Networking
public RemotePeer(UInt64 steamId)
{
SteamID = steamId;
OwnerSteamID = 0;
DisconnectTime = null;
Authenticating = false;
Authenticated = false;
@@ -90,10 +92,19 @@ namespace Barotrauma.Networking
if (status == Steamworks.AuthResponse.OK)
{
remotePeer.OwnerSteamID = ownerID;
remotePeer.Authenticated = true;
remotePeer.Authenticating = false;
foreach (var msg in remotePeer.UnauthedMessages)
{
//rewrite the owner id before
//forwarding the messages to
//the server, since it's only
//known now
int prevBitPosition = msg.Message.BitPosition;
msg.Message.BitPosition = sizeof(ulong) * 8;
msg.Message.Write(ownerID);
msg.Message.BitPosition = prevBitPosition;
byte[] msgToSend = (byte[])msg.Message.Buffer.Clone();
Array.Resize(ref msgToSend, msg.Message.LengthBytes);
ChildServerRelay.Write(msgToSend);
@@ -131,6 +142,7 @@ namespace Barotrauma.Networking
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(steamId);
outMsg.Write(remotePeer.OwnerSteamID);
outMsg.Write(data, 1, dataLength - 1);
DeliveryMethod deliveryMethod = (DeliveryMethod)data[0];
@@ -142,34 +154,27 @@ namespace Barotrauma.Networking
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (!remotePeer.Authenticated)
if (!remotePeer.Authenticated & !remotePeer.Authenticating && isConnectionInitializationStep)
{
if (!remotePeer.Authenticating)
remotePeer.DisconnectTime = null;
IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null);
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
{
if (isConnectionInitializationStep)
remotePeer.Authenticating = true;
authMsg.ReadString(); //skip name
authMsg.ReadInt32(); //skip owner key
authMsg.ReadUInt64(); //skip steamid
UInt16 ticketLength = authMsg.ReadUInt16();
byte[] ticket = authMsg.ReadBytes(ticketLength);
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{
remotePeer.DisconnectTime = null;
IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null);
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
//Console.WriteLine("received init step from "+steamId.ToString()+" ("+initializationStep.ToString()+")");
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
{
remotePeer.Authenticating = true;
authMsg.ReadString(); //skip name
authMsg.ReadInt32(); //skip owner key
authMsg.ReadUInt64(); //skip steamid
UInt16 ticketLength = authMsg.ReadUInt16();
byte[] ticket = authMsg.ReadBytes(ticketLength);
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
return;
}
}
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
return;
}
}
}
@@ -336,6 +341,7 @@ namespace Barotrauma.Networking
{
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(selfSteamID);
outMsg.Write(selfSteamID);
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep));
outMsg.Write(Name);
@@ -428,6 +434,7 @@ namespace Barotrauma.Networking
byte[] msgData = new byte[msg.LengthBytes];
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
msgToSend.Write(selfSteamID);
msgToSend.Write(selfSteamID);
msgToSend.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
msgToSend.Write((UInt16)length);
msgToSend.Write(msgData, 0, length);
@@ -159,6 +159,20 @@ namespace Barotrauma.Networking
color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader");
playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint();
playStyleName.RectTransform.IsFixedSize = true;
var serverTypeContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.2f), playStyleBanner.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft),
"MainMenuNotifBackground", Color.Black)
{
CanBeFocused = false,
};
var serverType = new GUITextBlock(new RectTransform(Vector2.One, serverTypeContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.CenterLeft);
}
else
{
var serverType = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), previewContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.CenterLeft);
}
var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), previewContainer.RectTransform))
@@ -553,5 +567,10 @@ namespace Barotrauma.Networking
(other.LobbyID == LobbyID || other.LobbyID == 0 || LobbyID == 0) &&
((OwnerID == 0) ? (other.IP == IP && other.Port == Port) : true);
}
public bool MatchesByEndpoint(ServerInfo other)
{
return OwnerID == other.OwnerID && (OwnerID != 0 ? true : (IP == other.IP && Port == other.Port));
}
}
}
@@ -242,16 +242,7 @@ namespace Barotrauma.Networking
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
{
Data = data,
OnClick = (component, area) =>
{
if (!UInt64.TryParse(area.Data.Metadata, out UInt64 id)) { return; }
Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id)
?? GameMain.Client.ConnectedClients.Find(c => c.ID == id)
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.SteamID == id)
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.ID == id);
if (client == null) { return; }
GameMain.NetLobbyScreen.SelectPlayer(client);
}
OnClick = GameMain.NetLobbyScreen.SelectPlayer
});
}
}
@@ -126,11 +126,15 @@ namespace Barotrauma.Networking
public void ClientRead(IReadMessage incMsg)
{
cachedServerListInfo = null;
ServerName = incMsg.ReadString();
ServerMessageText = incMsg.ReadString();
MaxPlayers = incMsg.ReadByte();
HasPassword = incMsg.ReadBoolean();
IsPublic = incMsg.ReadBoolean();
GameMain.NetLobbyScreen.SetPublic(IsPublic);
AllowFileTransfers = incMsg.ReadBoolean();
incMsg.ReadPadBits();
TickRate = incMsg.ReadRangedInteger(1, 60);
GameMain.NetworkMember.TickRate = TickRate;
@@ -147,7 +151,7 @@ namespace Barotrauma.Networking
}
}
public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? useRespawnShuttle = null)
public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? radiationEnabled = null, bool? useRespawnShuttle = null)
{
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
@@ -212,6 +216,7 @@ namespace Barotrauma.Networking
outMsg.Write(autoRestart != null);
outMsg.Write(autoRestart ?? false);
outMsg.Write(radiationEnabled ?? RadiationEnabled);
outMsg.WritePadBits();
}
@@ -274,7 +279,7 @@ namespace Barotrauma.Networking
if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); }
return true;
};
new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null)
{
OnClicked = ToggleSettingsFrame
@@ -500,8 +505,8 @@ namespace Barotrauma.Networking
CreateLabeledSlider(roundsTab, "ServerSettingsRespawnInterval", out slider, out sliderLabel);
string intervalLabel = sliderLabel.Text;
slider.Step = 0.05f;
slider.Range = new Vector2(10.0f, 600.0f);
slider.StepValue = 10.0f;
GetPropertyData("RespawnInterval").AssignGUIComponent(slider);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
@@ -646,7 +651,14 @@ namespace Barotrauma.Networking
foreach (ItemPrefab ip in ItemPrefab.Prefabs)
{
if (!ip.CanBeBought && !ip.Tags.Contains("smallitem")) continue;
if (ip.AllowAsExtraCargo.HasValue)
{
if (!ip.AllowAsExtraCargo.Value) { continue; }
}
else
{
if (!ip.CanBeBought) { continue; }
}
var itemFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), cargoFrame.Content.RectTransform) { MinSize = new Point(0, 30) }, isHorizontal: true)
{
@@ -725,6 +737,10 @@ namespace Barotrauma.Networking
TextManager.Get("ServerSettingsDestructibleOutposts"));
GetPropertyData("DestructibleOutposts").AssignGUIComponent(destructibleOutposts);
var lockAllDefaultWires = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsLockAllDefaultWires"));
GetPropertyData("LockAllDefaultWires").AssignGUIComponent(lockAllDefaultWires);
var allowRewiring = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsAllowRewiring"));
GetPropertyData("AllowRewiring").AssignGUIComponent(allowRewiring);
@@ -915,6 +931,7 @@ namespace Barotrauma.Networking
public bool ToggleSettingsFrame(GUIButton button, object obj)
{
if (GameMain.NetworkMember == null) { return false; }
if (settingsFrame == null)
{
CreateSettingsFrame();
@@ -936,5 +953,12 @@ namespace Barotrauma.Networking
return false;
}
private ServerInfo cachedServerListInfo = null;
public ServerInfo GetServerListInfo()
{
cachedServerListInfo ??= GameMain.ServerListScreen.UpdateServerInfoWithServerSettings(GameMain.Client.ClientPeer.ServerConnection, this);
return cachedServerListInfo;
}
}
}
@@ -1,6 +1,8 @@
using Microsoft.Xna.Framework;
using Barotrauma.Sounds;
using Microsoft.Xna.Framework;
using OpenAL;
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
@@ -56,7 +58,7 @@ namespace Barotrauma.Networking
public bool Disconnected { get; private set; }
public static void Create(string deviceName, UInt16? storedBufferID=null)
public static void Create(string deviceName, UInt16? storedBufferID = null)
{
if (Instance != null)
{
@@ -84,7 +86,7 @@ namespace Barotrauma.Networking
if (captureDevice == IntPtr.Zero)
{
DebugConsole.NewMessage("Alc.CaptureOpenDevice attempt 1 failed: error code " + Alc.GetError(IntPtr.Zero).ToString(),Color.Orange);
DebugConsole.NewMessage("Alc.CaptureOpenDevice attempt 1 failed: error code " + Alc.GetError(IntPtr.Zero).ToString(), Color.Orange);
//attempt using a smaller buffer size
captureDevice = Alc.CaptureOpenDevice(deviceName, VoipConfig.FREQUENCY, Al.FormatMono16, VoipConfig.BUFFER_SIZE * 2);
}
@@ -162,6 +164,7 @@ namespace Barotrauma.Networking
}
}
IntPtr nativeBuffer;
short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
bool prevCaptured = true;
@@ -171,143 +174,198 @@ namespace Barotrauma.Networking
{
Array.Copy(uncompressedBuffer, 0, prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE);
Array.Clear(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE);
while (capturing && !Disconnected)
nativeBuffer = Marshal.AllocHGlobal(VoipConfig.BUFFER_SIZE * 2);
try
{
int alcError;
if (CanDetectDisconnect)
while (capturing)
{
Alc.GetInteger(captureDevice, Alc.EnumConnected, out int isConnected);
int alcError;
if (CanDetectDisconnect)
{
Alc.GetInteger(captureDevice, Alc.EnumConnected, out int isConnected);
alcError = Alc.GetError(captureDevice);
if (alcError != Alc.NoError)
{
throw new Exception("Failed to determine if capture device is connected: " + alcError.ToString());
}
if (isConnected == 0)
{
DebugConsole.ThrowError("Capture device has been disconnected. You can select another available device in the settings.");
Disconnected = true;
break;
}
}
FillBuffer();
alcError = Alc.GetError(captureDevice);
if (alcError != Alc.NoError)
{
throw new Exception("Failed to determine if capture device is connected: " + alcError.ToString());
throw new Exception("Failed to capture samples: " + alcError.ToString());
}
if (isConnected == 0)
double maxAmplitude = 0.0f;
for (int i = 0; i < VoipConfig.BUFFER_SIZE; i++)
{
DebugConsole.ThrowError("Capture device has been disconnected. You can select another available device in the settings.");
Disconnected = true;
break;
uncompressedBuffer[i] = (short)MathHelper.Clamp((uncompressedBuffer[i] * Gain), -short.MaxValue, short.MaxValue);
double sampleVal = uncompressedBuffer[i] / (double)short.MaxValue;
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal));
}
}
double dB = Math.Min(20 * Math.Log10(maxAmplitude), 0.0);
Alc.GetInteger(captureDevice, Alc.EnumCaptureSamples, out int sampleCount);
LastdB = dB;
LastAmplitude = maxAmplitude;
alcError = Alc.GetError(captureDevice);
if (alcError != Alc.NoError)
{
throw new Exception("Failed to determine sample count: " + alcError.ToString());
}
if (sampleCount < VoipConfig.BUFFER_SIZE)
{
int sleepMs = (VoipConfig.BUFFER_SIZE - sampleCount) * 800 / VoipConfig.FREQUENCY;
if (sleepMs < 5) sleepMs = 5;
Thread.Sleep(sleepMs);
continue;
}
GCHandle handle = GCHandle.Alloc(uncompressedBuffer, GCHandleType.Pinned);
try
{
Alc.CaptureSamples(captureDevice, handle.AddrOfPinnedObject(), VoipConfig.BUFFER_SIZE);
}
finally
{
handle.Free();
}
alcError = Alc.GetError(captureDevice);
if (alcError != Alc.NoError)
{
throw new Exception("Failed to capture samples: " + alcError.ToString());
}
double maxAmplitude = 0.0f;
for (int i = 0; i < VoipConfig.BUFFER_SIZE; i++)
{
uncompressedBuffer[i] = (short)MathHelper.Clamp((uncompressedBuffer[i] * Gain), -short.MaxValue, short.MaxValue);
double sampleVal = uncompressedBuffer[i] / (double)short.MaxValue;
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal));
}
double dB = Math.Min(20 * Math.Log10(maxAmplitude), 0.0);
LastdB = dB;
LastAmplitude = maxAmplitude;
bool allowEnqueue = false;
if (GameMain.WindowActive)
{
ForceLocal = captureTimer > 0 ? ForceLocal : GameMain.Config.UseLocalVoiceByDefault;
bool pttDown = false;
if ((PlayerInput.KeyDown(InputType.Voice) || PlayerInput.KeyDown(InputType.LocalVoice)) &&
GUI.KeyboardDispatcher.Subscriber == null)
bool allowEnqueue = overrideSound != null;
if (GameMain.WindowActive)
{
pttDown = true;
if (PlayerInput.KeyDown(InputType.LocalVoice))
ForceLocal = captureTimer > 0 ? ForceLocal : GameMain.Config.UseLocalVoiceByDefault;
bool pttDown = false;
if ((PlayerInput.KeyDown(InputType.Voice) || PlayerInput.KeyDown(InputType.LocalVoice)) &&
GUI.KeyboardDispatcher.Subscriber == null)
{
ForceLocal = true;
pttDown = true;
if (PlayerInput.KeyDown(InputType.LocalVoice))
{
ForceLocal = true;
}
else
{
ForceLocal = false;
}
}
else
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Activity)
{
ForceLocal = false;
if (dB > GameMain.Config.NoiseGateThreshold)
{
allowEnqueue = true;
}
}
else if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.PushToTalk)
{
if (pttDown)
{
allowEnqueue = true;
}
}
}
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Activity)
if (allowEnqueue || captureTimer > 0)
{
if (dB > GameMain.Config.NoiseGateThreshold)
LastEnqueueAudio = DateTime.Now;
if (GameMain.Client?.Character != null)
{
allowEnqueue = true;
var messageType = !ForceLocal && ChatMessage.CanUseRadio(GameMain.Client.Character, out _) ? ChatMessageType.Radio : ChatMessageType.Default;
GameMain.Client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
}
}
else if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.PushToTalk)
{
if (pttDown)
//encode audio and enqueue it
lock (buffers)
{
allowEnqueue = true;
if (!prevCaptured) //enqueue the previous buffer if not sent to avoid cutoff
{
int compressedCountPrev = VoipConfig.Encoder.Encode(prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
EnqueueBuffer(compressedCountPrev);
}
int compressedCount = VoipConfig.Encoder.Encode(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
EnqueueBuffer(compressedCount);
}
captureTimer -= (VoipConfig.BUFFER_SIZE * 1000) / VoipConfig.FREQUENCY;
if (allowEnqueue)
{
captureTimer = GameMain.Config.VoiceChatCutoffPrevention;
}
prevCaptured = true;
}
else
{
captureTimer = 0;
prevCaptured = false;
//enqueue silence
lock (buffers)
{
EnqueueBuffer(0);
}
}
}
}
catch (Exception e)
{
DebugConsole.ThrowError($"VoipCapture threw an exception. Disabling capture...", e);
capturing = false;
}
finally
{
Marshal.FreeHGlobal(nativeBuffer);
}
}
if (allowEnqueue || captureTimer > 0)
private Sound overrideSound;
private int overridePos;
private short[] overrideBuf = new short[VoipConfig.BUFFER_SIZE];
private void FillBuffer()
{
if (overrideSound != null)
{
int totalSampleCount = 0;
while (totalSampleCount < VoipConfig.BUFFER_SIZE)
{
LastEnqueueAudio = DateTime.Now;
if (GameMain.Client?.Character != null)
int sampleCount = overrideSound.FillStreamBuffer(overridePos, overrideBuf);
overridePos += sampleCount * 2;
Array.Copy(overrideBuf, 0, uncompressedBuffer, totalSampleCount, sampleCount);
totalSampleCount += sampleCount;
if (sampleCount == 0)
{
var messageType = !ForceLocal && ChatMessage.CanUseRadio(GameMain.Client.Character, out _) ? ChatMessageType.Radio : ChatMessageType.Default;
GameMain.Client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
overridePos = 0;
}
//encode audio and enqueue it
lock (buffers)
}
int sleepMs = VoipConfig.BUFFER_SIZE * 800 / VoipConfig.FREQUENCY;
Thread.Sleep(sleepMs - 1);
}
else
{
int sampleCount = 0;
while (sampleCount < VoipConfig.BUFFER_SIZE)
{
Alc.GetInteger(captureDevice, Alc.EnumCaptureSamples, out sampleCount);
int alcError = Alc.GetError(captureDevice);
if (alcError != Alc.NoError)
{
if (!prevCaptured) //enqueue the previous buffer if not sent to avoid cutoff
throw new Exception("Failed to determine sample count: " + alcError.ToString());
}
if (sampleCount < VoipConfig.BUFFER_SIZE)
{
int sleepMs = (VoipConfig.BUFFER_SIZE - sampleCount) * 800 / VoipConfig.FREQUENCY;
if (sleepMs >= 1)
{
int compressedCountPrev = VoipConfig.Encoder.Encode(prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
EnqueueBuffer(compressedCountPrev);
Thread.Sleep(sleepMs);
}
int compressedCount = VoipConfig.Encoder.Encode(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
EnqueueBuffer(compressedCount);
}
captureTimer -= (VoipConfig.BUFFER_SIZE * 1000) / VoipConfig.FREQUENCY;
if (allowEnqueue)
{
captureTimer = GameMain.Config.VoiceChatCutoffPrevention;
}
prevCaptured = true;
}
else
{
captureTimer = 0;
prevCaptured = false;
//enqueue silence
lock (buffers)
{
EnqueueBuffer(0);
}
if (!capturing) { return; }
}
Thread.Sleep(10);
Alc.CaptureSamples(captureDevice, nativeBuffer, VoipConfig.BUFFER_SIZE);
Marshal.Copy(nativeBuffer, uncompressedBuffer, 0, uncompressedBuffer.Length);
}
}
public void SetOverrideSound(string fileName)
{
overrideSound?.Dispose();
if (string.IsNullOrEmpty(fileName))
{
overrideSound = null;
}
else
{
overrideSound = GameMain.SoundManager.LoadSound(fileName, true);
}
}
@@ -94,6 +94,7 @@ namespace Barotrauma.Networking
DebugConsole.Log("Recreating voipsound " + queueId);
client.VoipSound = new VoipSound(client.Name, GameMain.SoundManager, client.VoipQueue);
}
GameMain.SoundManager.ForceStreamUpdate();
if (client.Character != null && !client.Character.IsDead && !client.Character.Removed && client.Character.SpeechImpediment <= 100.0f)
{
@@ -102,7 +103,7 @@ namespace Barotrauma.Networking
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameMain.Config.DisableVoiceChatFilters;
if (client.VoipSound.UseRadioFilter)
if (messageType == ChatMessageType.Radio)
{
client.VoipSound.SetRange(radio.Range * 0.8f, radio.Range);
}
@@ -110,7 +111,7 @@ namespace Barotrauma.Networking
{
client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange);
}
if (!client.VoipSound.UseRadioFilter && Character.Controlled != null && !GameMain.Config.DisableVoiceChatFilters)
if (messageType != ChatMessageType.Radio && Character.Controlled != null && !GameMain.Config.DisableVoiceChatFilters)
{
client.VoipSound.UseMuffleFilter = SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull);
}
@@ -12,8 +12,8 @@ namespace Barotrauma.Networking
{
public static bool Ready = false;
public const int FREQUENCY = 48000; //not amazing, but not bad audio quality
public const int BUFFER_SIZE = 2880; //60ms window, the max Opus seems to support
public const int FREQUENCY = 48000;
public const int BUFFER_SIZE = 960; //20ms window
public static OpusEncoder Encoder
{
@@ -112,14 +112,17 @@ namespace Barotrauma
switch (voteType)
{
case VoteType.Sub:
SubmarineInfo sub = data as SubmarineInfo;
if (sub == null) { return; }
case VoteType.Sub:
if (!(data is SubmarineInfo sub)) { return; }
msg.Write(sub.EqualityCheckVal);
if (sub.EqualityCheckVal == 0)
{
//sub doesn't exist client-side, use hash to let the server know which one we voted for
msg.Write(sub.MD5Hash.Hash);
}
break;
case VoteType.Mode:
GameModePreset gameMode = data as GameModePreset;
if (gameMode == null) { return; }
if (!(data is GameModePreset gameMode)) { return; }
msg.Write(gameMode.Identifier);
break;
case VoteType.EndRound:
@@ -127,8 +130,7 @@ namespace Barotrauma
msg.Write((bool)data);
break;
case VoteType.Kick:
Client votedClient = data as Client;
if (votedClient == null) return;
if (!(data is Client votedClient)) { return; }
msg.Write(votedClient.ID);
break;