Unstable v0.1300.0.0 (February 19th 2021)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -48,7 +48,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,7 +111,6 @@ 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.");
|
||||
@@ -73,13 +119,10 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
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,7 +150,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else if (targetCharacter != null)
|
||||
{
|
||||
targetCharacter.SetOrder(order, orderOption, senderCharacter);
|
||||
targetCharacter.SetOrder(order, orderOption, orderPriority, senderCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +158,7 @@ namespace Barotrauma.Networking
|
||||
if (NetIdUtils.IdMoreRecent(ID, LastID))
|
||||
{
|
||||
GameMain.Client.AddChatMessage(
|
||||
new OrderChatMessage(orderPrefab, orderOption, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
|
||||
new OrderChatMessage(orderPrefab, orderOption, orderPriority, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
|
||||
LastID = ID;
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
public string Name;
|
||||
public string PreferredJob;
|
||||
public CharacterTeamType PreferredTeam;
|
||||
public UInt16 NameID;
|
||||
public UInt64 SteamID;
|
||||
public byte ID;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
private byte myID;
|
||||
|
||||
private List<Client> otherClients;
|
||||
private readonly List<Client> otherClients;
|
||||
|
||||
public readonly List<SubmarineInfo> ServerSubmarines = new List<SubmarineInfo>();
|
||||
|
||||
@@ -94,13 +94,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
private UInt16 lastSentChatMsgID = 0; //last message this client has successfully sent
|
||||
private UInt16 lastQueueChatMsgID = 0; //last message added to the queue
|
||||
private List<ChatMessage> chatMsgQueue = new List<ChatMessage>();
|
||||
private readonly List<ChatMessage> chatMsgQueue = new List<ChatMessage>();
|
||||
|
||||
public UInt16 LastSentEntityEventID;
|
||||
|
||||
private ClientEntityEventManager entityEventManager;
|
||||
private readonly ClientEntityEventManager entityEventManager;
|
||||
|
||||
private FileReceiver fileReceiver;
|
||||
private readonly FileReceiver fileReceiver;
|
||||
|
||||
#if DEBUG
|
||||
public void PrintReceiverTransters()
|
||||
@@ -138,6 +138,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Client> previouslyConnectedClients = new List<Client>();
|
||||
public IEnumerable<Client> PreviouslyConnectedClients
|
||||
{
|
||||
get { return previouslyConnectedClients; }
|
||||
}
|
||||
|
||||
public FileReceiver FileReceiver
|
||||
{
|
||||
get { return fileReceiver; }
|
||||
@@ -153,9 +159,9 @@ namespace Barotrauma.Networking
|
||||
get { return entityEventManager; }
|
||||
}
|
||||
|
||||
private object serverEndpoint;
|
||||
private int ownerKey;
|
||||
private bool steamP2POwner;
|
||||
private readonly object serverEndpoint;
|
||||
private readonly int ownerKey;
|
||||
private readonly bool steamP2POwner;
|
||||
|
||||
public bool IsServerOwner
|
||||
{
|
||||
@@ -272,6 +278,7 @@ namespace Barotrauma.Networking
|
||||
private void ConnectToServer(object endpoint, string hostName)
|
||||
{
|
||||
LastClientListUpdateID = 0;
|
||||
|
||||
foreach (var c in ConnectedClients)
|
||||
{
|
||||
GameMain.NetLobbyScreen.RemovePlayer(c);
|
||||
@@ -361,6 +368,14 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
}
|
||||
else
|
||||
{
|
||||
entityEventManager.ClearSelf();
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
c.ResetNetState();
|
||||
}
|
||||
}
|
||||
|
||||
chatBox.InputBox.Enabled = true;
|
||||
if (GameMain.NetLobbyScreen?.ChatInput != null)
|
||||
@@ -842,12 +857,25 @@ namespace Barotrauma.Networking
|
||||
string endMessage = string.Empty;
|
||||
|
||||
endMessage = inc.ReadString();
|
||||
bool missionSuccessful = inc.ReadBoolean();
|
||||
Character.TeamType winningTeam = (Character.TeamType)inc.ReadByte();
|
||||
if (missionSuccessful && GameMain.GameSession?.Mission != null)
|
||||
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 (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();
|
||||
@@ -913,7 +941,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);
|
||||
@@ -944,17 +976,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());
|
||||
}
|
||||
@@ -989,7 +1030,10 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
GameMain.GameSession.Mission?.ClientReadInitial(inc);
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
mission.ClientReadInitial(inc);
|
||||
}
|
||||
|
||||
roundInitStatus = RoundInitStatus.Started;
|
||||
}
|
||||
@@ -1417,7 +1461,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;
|
||||
@@ -1471,7 +1520,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
|
||||
@@ -1625,7 +1676,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (Submarine.MainSubs[i] == null) { break; }
|
||||
|
||||
var teamID = i == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2;
|
||||
var teamID = i == 0 ? CharacterTeamType.Team1 : CharacterTeamType.Team2;
|
||||
Submarine.MainSubs[i].TeamID = teamID;
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
@@ -1697,7 +1748,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;
|
||||
}
|
||||
@@ -1819,6 +1870,7 @@ namespace Barotrauma.Networking
|
||||
UInt16 nameId = inc.ReadUInt16();
|
||||
string name = inc.ReadString();
|
||||
string preferredJob = inc.ReadString();
|
||||
byte preferredTeam = inc.ReadByte();
|
||||
UInt16 characterID = inc.ReadUInt16();
|
||||
float karma = inc.ReadSingle();
|
||||
bool muted = inc.ReadBoolean();
|
||||
@@ -1835,6 +1887,7 @@ namespace Barotrauma.Networking
|
||||
SteamID = steamId,
|
||||
Name = name,
|
||||
PreferredJob = preferredJob,
|
||||
PreferredTeam = (CharacterTeamType)preferredTeam,
|
||||
CharacterID = characterID,
|
||||
Karma = karma,
|
||||
Muted = muted,
|
||||
@@ -1869,6 +1922,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
existingClient.NameID = tc.NameID;
|
||||
existingClient.PreferredJob = tc.PreferredJob;
|
||||
existingClient.PreferredTeam = tc.PreferredTeam;
|
||||
existingClient.Character = null;
|
||||
existingClient.Karma = tc.Karma;
|
||||
existingClient.Muted = tc.Muted;
|
||||
@@ -1908,6 +1962,17 @@ namespace Barotrauma.Networking
|
||||
refreshCampaignUI = true;
|
||||
}
|
||||
}
|
||||
foreach (Client client in ConnectedClients)
|
||||
{
|
||||
if (!previouslyConnectedClients.Any(c => c.ID == client.ID))
|
||||
{
|
||||
while (previouslyConnectedClients.Count > 100)
|
||||
{
|
||||
previouslyConnectedClients.RemoveAt(0);
|
||||
}
|
||||
previouslyConnectedClients.Add(client);
|
||||
}
|
||||
}
|
||||
if (updateClientListId) { LastClientListUpdateID = listId; }
|
||||
|
||||
if (clientPeer is SteamP2POwnerPeer)
|
||||
@@ -2133,6 +2198,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
break;
|
||||
case ServerNetObject.ENTITY_POSITION:
|
||||
bool isItem = inc.ReadBoolean();
|
||||
UInt16 id = inc.ReadUInt16();
|
||||
uint msgLength = inc.ReadVariableUInt32();
|
||||
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
|
||||
@@ -2147,8 +2213,15 @@ namespace Barotrauma.Networking
|
||||
entities.Add(entity);
|
||||
if (entity != null && (entity is Item || entity is Character || entity is Submarine))
|
||||
{
|
||||
entity.ClientRead(objHeader.Value, inc, sendingTime);
|
||||
}
|
||||
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
|
||||
{
|
||||
entity.ClientRead(objHeader.Value, inc, sendingTime);
|
||||
}
|
||||
}
|
||||
|
||||
//force to the correct position in case the entity doesn't exist
|
||||
//or the message wasn't read correctly for whatever reason
|
||||
@@ -2221,9 +2294,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsSDK.Net.EGAErrorSeverity.Critical, string.Join("\n", errorLines));
|
||||
|
||||
DebugConsole.ThrowError("Writing object data to \"crashreport_object.log\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues");
|
||||
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("crashreport_object.log", System.IO.FileMode.Create))
|
||||
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))
|
||||
@@ -2236,7 +2309,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Exception("Read error: please send us \"crashreport_object.bin\"!");
|
||||
throw new Exception("Read error: please send us \"networkerror_data.log\"!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2260,6 +2333,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
outmsg.Write("");
|
||||
}
|
||||
outmsg.Write((byte)GameMain.Config.TeamPreference);
|
||||
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
|
||||
{
|
||||
@@ -2287,7 +2361,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);
|
||||
@@ -2338,7 +2412,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);
|
||||
@@ -2895,8 +2969,9 @@ namespace Barotrauma.Networking
|
||||
return false;
|
||||
}
|
||||
if (button != null) { button.Enabled = false; }
|
||||
if (campaign != null) LateCampaignJoin = true;
|
||||
if (campaign != null) { LateCampaignJoin = true; }
|
||||
|
||||
if (clientPeer == null) { return false; }
|
||||
|
||||
IWriteMessage readyToStartMsg = new WriteOnlyMessage();
|
||||
readyToStartMsg.Write((byte)ClientPacketHeader.RESPONSE_STARTGAME);
|
||||
@@ -3231,12 +3306,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
public virtual bool SelectCrewCharacter(Character character, GUIComponent frame)
|
||||
{
|
||||
if (character == null) return false;
|
||||
if (character == null) { return false; }
|
||||
|
||||
if (character != myCharacter)
|
||||
{
|
||||
var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.Character == character);
|
||||
if (client == null) return false;
|
||||
var client = previouslyConnectedClients.Find(c => c.Character == character);
|
||||
if (client == null) { return false; }
|
||||
|
||||
CreateSelectionRelatedButtons(client, frame);
|
||||
}
|
||||
@@ -3246,7 +3321,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public virtual bool SelectCrewClient(Client client, GUIComponent frame)
|
||||
{
|
||||
if (client == null || client.ID == ID) return false;
|
||||
if (client == null || client.ID == ID) { return false; }
|
||||
CreateSelectionRelatedButtons(client, frame);
|
||||
return true;
|
||||
}
|
||||
@@ -3328,9 +3403,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,
|
||||
@@ -3341,10 +3419,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"))
|
||||
{
|
||||
@@ -3455,7 +3532,10 @@ 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)
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace Barotrauma
|
||||
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(DamageEnemyKarmaIncrease));
|
||||
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(ItemRepairKarmaIncrease));
|
||||
CreateLabeledSlider(parent, 0.0f, 10.0f, 0.05f, nameof(ExtinguishFireKarmaIncrease));
|
||||
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(BallastFloraKarmaIncrease));
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.12f), parent.RectTransform), TextManager.Get("Karma.NegativeActions"),
|
||||
textAlignment: Alignment.Center, font: GUI.SubHeadingFont)
|
||||
@@ -61,7 +62,6 @@ namespace Barotrauma
|
||||
CreateLabeledNumberInput(parent, 0, 20, nameof(AllowedWireDisconnectionsPerMinute));
|
||||
CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, nameof(WireDisconnectionKarmaDecrease));
|
||||
CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, nameof(SpamFilterKarmaDecrease));
|
||||
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(BallastFloraKarmaIncrease));
|
||||
|
||||
//hide these for now if a localized text is not available
|
||||
if (TextManager.ContainsTag("Karma." + nameof(DangerousItemStealKarmaDecrease)))
|
||||
|
||||
+14
-4
@@ -193,7 +193,7 @@ namespace Barotrauma.Networking
|
||||
continue;
|
||||
}
|
||||
|
||||
byte msgLength = msg.ReadByte();
|
||||
int msgLength = (int)msg.ReadVariableUInt32();
|
||||
|
||||
IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
|
||||
entities.Add(entity);
|
||||
@@ -226,7 +226,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
long msgPosition = msg.BitPosition;
|
||||
int msgPosition = msg.BitPosition;
|
||||
if (GameSettings.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
|
||||
@@ -242,9 +242,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString()
|
||||
+ "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits.";
|
||||
#if DEBUG
|
||||
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#endif
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.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
|
||||
@@ -300,5 +300,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
MidRoundSyncingDone = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears events generated by the current client, used
|
||||
/// when resynchronizing with the server after a timeout.
|
||||
/// </summary>
|
||||
public void ClearSelf()
|
||||
{
|
||||
ID = 0;
|
||||
events.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,19 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
Close(disableReconnect: true);
|
||||
|
||||
string missingModNames = "\n- " + string.Join("\n\n- ", missingPackages.Select(p => GetPackageStr(p))) + "\n\n";
|
||||
string missingModNames = "\n";
|
||||
int displayedModCount = 0;
|
||||
foreach (ServerContentPackage missingPackage in missingPackages)
|
||||
{
|
||||
missingModNames += "\n- " + GetPackageStr(missingPackage);
|
||||
displayedModCount++;
|
||||
if (GUI.Font.MeasureString(missingModNames).Y > GameMain.GraphicsHeight * 0.5f)
|
||||
{
|
||||
missingModNames += "\n\n" + TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (missingPackages.Count - displayedModCount).ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
missingModNames += "\n\n";
|
||||
|
||||
var msgBox = new GUIMessageBox(
|
||||
TextManager.Get("WorkshopItemDownloadTitle"),
|
||||
@@ -189,6 +201,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (!contentPackageOrderReceived)
|
||||
{
|
||||
GameMain.Config.BackUpModOrder();
|
||||
GameMain.Config.SwapPackages(corePackage.CorePackage, regularPackages.Select(p => p.RegularPackage).ToList());
|
||||
contentPackageOrderReceived = true;
|
||||
}
|
||||
|
||||
@@ -378,8 +378,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (maxPlayersElement > NetConfig.MaxPlayers)
|
||||
{
|
||||
DebugConsole.IsOpen = true;
|
||||
DebugConsole.NewMessage($"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", Color.Red);
|
||||
/*DebugConsole.IsOpen = true;
|
||||
DebugConsole.NewMessage($"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", Color.Red);*/
|
||||
maxPlayersElement = NetConfig.MaxPlayers;
|
||||
}
|
||||
|
||||
@@ -540,5 +540,18 @@ namespace Barotrauma.Networking
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ServerInfo other ? Equals(other) : base.Equals(obj);
|
||||
}
|
||||
|
||||
public bool Equals(ServerInfo other)
|
||||
{
|
||||
return
|
||||
other.OwnerID == OwnerID &&
|
||||
(other.LobbyID == LobbyID || other.LobbyID == 0 || LobbyID == 0) &&
|
||||
((OwnerID == 0) ? (other.IP == IP && other.Port == Port) : true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,10 +195,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
foreach (var data in line.RichData)
|
||||
{
|
||||
UInt64 id = 0;
|
||||
if (!UInt64.TryParse(data.Metadata, out id)) { return; }
|
||||
Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id);
|
||||
client ??= GameMain.Client.ConnectedClients.Find(c => c.ID == id);
|
||||
if (!UInt64.TryParse(data.Metadata, out ulong 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 && client.Karma < 40.0f)
|
||||
{
|
||||
textContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform),
|
||||
@@ -243,10 +244,11 @@ namespace Barotrauma.Networking
|
||||
Data = data,
|
||||
OnClick = (component, area) =>
|
||||
{
|
||||
UInt64 id = 0;
|
||||
if (!UInt64.TryParse(area.Data.Metadata, out id)) { return; }
|
||||
Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id);
|
||||
client ??= GameMain.Client.ConnectedClients.Find(c => c.ID == id);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -500,8 +500,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) =>
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using Barotrauma.Networking;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
@@ -268,14 +269,13 @@ namespace Barotrauma.Steam
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: find a better strategy to fetch all lobbies, this is gonna take forever if we actually have 10000 lobbies
|
||||
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery().FilterDistanceWorldwide().WithMaxResults(10000);
|
||||
|
||||
Steamworks.Dispatch.OnDebugCallback = (callbackType, contents, isServer) =>
|
||||
{
|
||||
DebugConsole.NewMessage($"{callbackType}: " + contents, Color.Yellow);
|
||||
};
|
||||
TaskPool.Add("LobbyQueryRequest", lobbyQuery.RequestAsync(),
|
||||
|
||||
TaskPool.Add("LobbyQueryRequest", LobbyQueryRequest(),
|
||||
(t) =>
|
||||
{
|
||||
Steamworks.Dispatch.OnDebugCallback = null;
|
||||
@@ -285,7 +285,7 @@ namespace Barotrauma.Steam
|
||||
taskDone();
|
||||
return;
|
||||
}
|
||||
var lobbies = ((Task<Steamworks.Data.Lobby[]>)t).Result;
|
||||
var lobbies = ((Task<List<Steamworks.Data.Lobby>>)t).Result;
|
||||
if (lobbies != null)
|
||||
{
|
||||
foreach (var lobby in lobbies)
|
||||
@@ -372,6 +372,32 @@ namespace Barotrauma.Steam
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task<List<Steamworks.Data.Lobby>> LobbyQueryRequest()
|
||||
{
|
||||
List<Steamworks.Data.Lobby> allLobbies = new List<Steamworks.Data.Lobby>();
|
||||
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery()
|
||||
.FilterDistanceWorldwide()
|
||||
.WithMaxResults(50);
|
||||
//steamworks seems to unable to retrieve more than 50
|
||||
//lobbies per request; to work around this, we'll make
|
||||
//up to 10 requests, asking to ignore all previous results
|
||||
//in each subsequent request
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
Steamworks.Data.Lobby[] lobbies = await lobbyQuery.RequestAsync();
|
||||
if (lobbies == null) { break; }
|
||||
foreach (var l in lobbies)
|
||||
{
|
||||
lobbyQuery = lobbyQuery
|
||||
.WithoutKeyValue("lobbyowner", l.GetData("lobbyowner"));
|
||||
}
|
||||
allLobbies.AddRange(lobbies);
|
||||
}
|
||||
|
||||
//make sure all returned lobbies are distinct, don't want any duplicates here
|
||||
return allLobbies.Select(l => l.Id).Distinct().Select(i => allLobbies.Find(l => l.Id == i)).ToList();
|
||||
}
|
||||
|
||||
public static void AssignLobbyDataToServerInfo(Steamworks.Data.Lobby lobby, ServerInfo serverInfo)
|
||||
{
|
||||
serverInfo.OwnerVerified = true;
|
||||
@@ -1172,7 +1198,7 @@ namespace Barotrauma.Steam
|
||||
|
||||
foreach (ContentFile contentFile in contentPackage.Files)
|
||||
{
|
||||
contentFile.Path = contentFile.Path.CleanUpPath();
|
||||
contentFile.Path = contentFile.Path.CleanUpPathCrossPlatform(correctFilenameCase: true, item?.Directory);
|
||||
string sourceFile = Path.Combine(item?.Directory, contentFile.Path);
|
||||
if (!File.Exists(sourceFile))
|
||||
{
|
||||
|
||||
@@ -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 : false;
|
||||
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)
|
||||
{
|
||||
@@ -101,8 +102,8 @@ namespace Barotrauma.Networking
|
||||
var messageType = !client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
|
||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio;
|
||||
if (client.VoipSound.UseRadioFilter)
|
||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameMain.Config.DisableVoiceChatFilters;
|
||||
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)
|
||||
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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user