38f1ddb...178a853: v0.8.9.1, removed content folder

This commit is contained in:
Joonas Rikkonen
2019-03-18 19:46:58 +02:00
parent 38f1ddb6fe
commit 6c0679c297
1054 changed files with 151673 additions and 144931 deletions
@@ -9,6 +9,7 @@ namespace Barotrauma.Networking
{
public string Name;
public string IP;
public ulong SteamID;
public string Reason;
public DateTime? ExpirationTime;
@@ -33,6 +34,14 @@ namespace Barotrauma.Networking
this.Reason = reason;
this.ExpirationTime = expirationTime;
}
public BannedPlayer(string name, ulong steamID, string reason, DateTime? expirationTime)
{
this.Name = name;
this.SteamID = steamID;
this.Reason = reason;
this.ExpirationTime = expirationTime;
}
}
partial class BanList
@@ -74,13 +83,12 @@ namespace Barotrauma.Networking
if (separatedLine.Length < 2) continue;
string name = separatedLine[0];
string ip = separatedLine[1];
string identifier = separatedLine[1];
DateTime? expirationTime = null;
if (separatedLine.Length > 2 && !string.IsNullOrEmpty(separatedLine[2]))
{
DateTime parsedTime;
if (DateTime.TryParse(separatedLine[2], out parsedTime))
if (DateTime.TryParse(separatedLine[2], out DateTime parsedTime))
{
expirationTime = parsedTime;
}
@@ -89,14 +97,49 @@ namespace Barotrauma.Networking
if (expirationTime.HasValue && DateTime.Now > expirationTime.Value) continue;
bannedPlayers.Add(new BannedPlayer(name, ip, reason, expirationTime));
if (identifier.Contains("."))
{
//identifier is an ip
bannedPlayers.Add(new BannedPlayer(name, identifier, reason, expirationTime));
}
else
{
//identifier should be a steam id
if (ulong.TryParse(identifier, out ulong steamID))
{
bannedPlayers.Add(new BannedPlayer(name, steamID, reason, expirationTime));
}
else
{
DebugConsole.ThrowError("Error in banlist: \"" + identifier + "\" is not a valid IP or a Steam ID");
}
}
}
}
}
public void BanPlayer(string name, string ip, string reason, TimeSpan? duration)
{
if (bannedPlayers.Any(bp => bp.IP == ip)) return;
BanPlayer(name, ip, 0, reason, duration);
}
public void BanPlayer(string name, ulong steamID, string reason, TimeSpan? duration)
{
BanPlayer(name, "", steamID, reason, duration);
}
private void BanPlayer(string name, string ip, ulong steamID, string reason, TimeSpan? duration)
{
var existingBan = bannedPlayers.Find(bp => bp.IP == ip && bp.SteamID == steamID);
if (existingBan != null)
{
if (!duration.HasValue) return;
DebugConsole.Log("Set \"" + name + "\"'s ban duration to " + duration.Value);
existingBan.ExpirationTime = DateTime.Now + duration.Value;
Save();
return;
}
System.Diagnostics.Debug.Assert(!name.Contains(','));
@@ -121,7 +164,7 @@ namespace Barotrauma.Networking
var player = bannedPlayers.Find(bp => bp.Name == name);
if (player == null)
{
DebugConsole.Log("Could not unban player \""+name+"\". Matching player not found.");
DebugConsole.Log("Could not unban player \"" + name + "\". Matching player not found.");
}
else
{
@@ -146,10 +189,10 @@ namespace Barotrauma.Networking
}
}
public bool IsBanned(string IP)
public bool IsBanned(string IP, ulong steamID)
{
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
return bannedPlayers.Any(bp => bp.CompareTo(IP));
return bannedPlayers.Any(bp => bp.CompareTo(IP) || (steamID != 0 && bp.SteamID == steamID));
}
private void RemoveBan(BannedPlayer banned)
@@ -200,10 +243,11 @@ namespace Barotrauma.Networking
List<string> lines = new List<string>();
foreach (BannedPlayer banned in bannedPlayers)
{
string line = banned.Name + "," + banned.IP;
string line = banned.Name;
line += "," + ((banned.SteamID > 0) ? banned.SteamID.ToString() : banned.IP);
line += "," + (banned.ExpirationTime.HasValue ? banned.ExpirationTime.Value.ToString() : "");
if (!string.IsNullOrWhiteSpace(banned.Reason)) line += "," + banned.Reason;
lines.Add(line);
}
@@ -1,13 +1,15 @@
using Lidgren.Network;
using Barotrauma.Items.Components;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Text;
namespace Barotrauma.Networking
{
enum ChatMessageType
{
Default, Error, Dead, Server, Radio, Private, Console, MessageBox, ServerLog
Default, Error, Dead, Server, Radio, Private, Console, MessageBox, Order, ServerLog
}
partial class ChatMessage
@@ -20,13 +22,15 @@ namespace Barotrauma.Networking
public static Color[] MessageColor =
{
new Color(125, 140, 153), //default
new Color(190, 198, 205), //default
new Color(204, 74, 78), //error
new Color(63, 72, 204), //dead
new Color(136, 177, 255), //dead
new Color(157, 225, 160), //server
new Color(238, 208, 0), //radio
new Color(64, 240, 89), //private
new Color(255, 255, 255) //console
new Color(255, 255, 255), //console
new Color(255, 255, 255), //messagebox
new Color(255, 128, 0) //order
};
public readonly string Text;
@@ -56,7 +60,7 @@ namespace Barotrauma.Networking
set;
}
private ChatMessage(string senderName, string text, ChatMessageType type, Character sender)
protected ChatMessage(string senderName, string text, ChatMessageType type, Character sender)
{
Text = text;
Type = type;
@@ -115,26 +119,104 @@ namespace Barotrauma.Networking
if (Submarine.CheckVisibility(listener.SimPosition, Sender.SimPosition) != null) dist = (dist + 100f) * obstructionmult;
if (dist > range) return "";
return ApplyDistanceEffect(text, dist / range);
}
float garbleAmount = dist / range;
public static string ApplyDistanceEffect(string text, float garbleAmount)
{
if (garbleAmount < 0.3f) return text;
if (garbleAmount > 1.0f) return "";
int startIndex = Math.Max(text.IndexOf(':') + 1, 1);
StringBuilder sb = new StringBuilder(text.Length);
for (int i = 0; i < text.Length; i++)
{
sb.Append((i>startIndex && Rand.Range(0.0f, 1.0f) < garbleAmount) ? '-' : text[i]);
sb.Append((i > startIndex && Rand.Range(0.0f, 1.0f) < garbleAmount) ? '-' : text[i]);
}
return sb.ToString();
}
public static string ApplyDistanceEffect(string message, ChatMessageType type, Character sender, Character receiver)
{
if (sender == null) return "";
switch (type)
{
case ChatMessageType.Default:
if (receiver != null && !receiver.IsDead)
{
return ApplyDistanceEffect(receiver, sender, message, SpeakRange * (1.0f - sender.SpeechImpediment / 100.0f), 3.0f);
}
break;
case ChatMessageType.Radio:
case ChatMessageType.Order:
if (receiver != null && !receiver.IsDead)
{
var receiverItem = receiver.Inventory?.Items.FirstOrDefault(i => i?.GetComponent<WifiComponent>() != null);
//character doesn't have a radio -> don't send
if (receiverItem == null || !receiver.HasEquippedItem(receiverItem)) return "";
var senderItem = sender.Inventory?.Items.FirstOrDefault(i => i?.GetComponent<WifiComponent>() != null);
if (senderItem == null || !sender.HasEquippedItem(senderItem)) return "";
var receiverRadio = receiverItem.GetComponent<WifiComponent>();
var senderRadio = senderItem.GetComponent<WifiComponent>();
if (!receiverRadio.CanReceive(senderRadio)) return "";
string msg = ApplyDistanceEffect(receiverItem, senderItem, message, senderRadio.Range);
if (sender.SpeechImpediment > 0.0f)
{
//speech impediment doesn't reduce the range when using a radio, but adds extra garbling
msg = ApplyDistanceEffect(msg, sender.SpeechImpediment / 100.0f);
}
return msg;
}
break;
}
return message;
}
public static void ServerRead(NetIncomingMessage msg, Client c)
{
c.KickAFKTimer = 0.0f;
UInt16 ID = msg.ReadUInt16();
string txt = msg.ReadString();
if (txt == null) txt = "";
ChatMessageType type = (ChatMessageType)msg.ReadByte();
string txt = "";
int orderIndex = -1;
Character orderTargetCharacter = null;
Entity orderTargetEntity = null;
int orderOptionIndex = -1;
OrderChatMessage orderMsg = null;
if (type == ChatMessageType.Order)
{
orderIndex = msg.ReadByte();
orderTargetCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
orderTargetEntity = Entity.FindEntityByID(msg.ReadUInt16()) as Entity;
orderOptionIndex = msg.ReadByte();
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
{
DebugConsole.ThrowError("Invalid order message from client \"" + c.Name + "\" - order index out of bounds.");
return;
}
Order order = Order.PrefabList[orderIndex];
string orderOption = orderOptionIndex < 0 || orderOptionIndex >= order.Options.Length ? "" : order.Options[orderOptionIndex];
orderMsg = new OrderChatMessage(order, orderOption, orderTargetEntity, orderTargetCharacter, c.Character);
txt = orderMsg.Text;
}
else
{
txt = msg.ReadString() ?? "";
}
if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) return;
@@ -148,15 +230,22 @@ namespace Barotrauma.Networking
c.LastSentChatMessages.Add(txt);
if (c.LastSentChatMessages.Count > 10)
{
c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count-10);
c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10);
}
float similarity = 0.0f;
for (int i = 0; i < c.LastSentChatMessages.Count; i++)
{
float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i);
int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]);
similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f);
if (string.IsNullOrEmpty(txt))
{
similarity += closeFactor;
}
else
{
int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]);
similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f);
}
}
if (similarity + c.ChatSpamSpeed > 5.0f)
@@ -172,7 +261,7 @@ namespace Barotrauma.Networking
{
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
c.ChatSpamTimer = 10.0f;
GameMain.Server.SendChatMessage(denyMsg, c);
GameMain.Server.SendDirectChatMessage(denyMsg, c);
}
return;
}
@@ -183,14 +272,42 @@ namespace Barotrauma.Networking
{
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
c.ChatSpamTimer = 10.0f;
GameMain.Server.SendChatMessage(denyMsg, c);
GameMain.Server.SendDirectChatMessage(denyMsg, c);
return;
}
if (type == ChatMessageType.Order)
{
if (c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) return;
//dead characters are allowed to send chat messages,
//we'll just switch the message type to dead chat in SendChatMessage
if (c.Character != null && (!c.Character.CanSpeak && !c.Character.IsDead)) return;
GameMain.Server.SendChatMessage(txt, null, c);
ChatMessageType messageType = CanUseRadio(orderMsg.Sender) ? ChatMessageType.Radio : ChatMessageType.Default;
if (orderMsg.Order.TargetAllCharacters)
{
#if CLIENT
//add the order to the crewmanager only if the host is not controlling a character
//OR the character is close enough to hear it
if (Character.Controlled == null ||
!string.IsNullOrEmpty(ApplyDistanceEffect(orderMsg.Text, messageType, orderMsg.Sender, Character.Controlled)))
{
GameMain.GameSession?.CrewManager?.AddOrder(
new Order(orderMsg.Order.Prefab, orderTargetEntity, (orderTargetEntity as Item)?.GetComponent<ItemComponent>()),
orderMsg.Order.Prefab.FadeOutTime);
}
#endif
}
else if (orderTargetCharacter != null)
{
orderTargetCharacter.SetOrder(
new Order(orderMsg.Order.Prefab, orderTargetEntity, (orderTargetEntity as Item)?.GetComponent<ItemComponent>()),
orderMsg.OrderOption, orderMsg.Sender);
}
GameMain.Server.SendOrderChatMessage(orderMsg);
}
else
{
GameMain.Server.SendChatMessage(txt, null, c);
}
}
public int EstimateLengthBytesClient()
@@ -221,7 +338,14 @@ namespace Barotrauma.Networking
return length;
}
public void ServerWrite(NetOutgoingMessage msg, Client c)
public static bool CanUseRadio(Character sender)
{
if (sender == null) return false;
var senderItem = sender.Inventory.Items.FirstOrDefault(i => i?.GetComponent<WifiComponent>() != null);
return senderItem != null && sender.HasEquippedItem(senderItem) && senderItem.GetComponent<WifiComponent>().CanTransmit();
}
public virtual void ServerWrite(NetOutgoingMessage msg, Client c)
{
msg.Write((byte)ServerNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);
@@ -9,6 +9,7 @@ namespace Barotrauma.Networking
{
public string Name;
public byte ID;
public ulong SteamID;
private float karma = 1.0f;
public float Karma
@@ -63,6 +64,8 @@ namespace Barotrauma.Networking
public float ChatSpamSpeed;
public float ChatSpamTimer;
public int ChatSpamCount;
public float KickAFKTimer;
public double MidRoundSyncTimeOut;
@@ -86,6 +89,8 @@ namespace Barotrauma.Networking
public float DeleteDisconnectedTimer;
public HashSet<string> GivenAchievements = new HashSet<string>();
public ClientPermissions Permissions = ClientPermissions.None;
public List<DebugConsole.Command> PermittedConsoleCommands
{
@@ -136,8 +141,8 @@ namespace Barotrauma.Networking
public static bool IsValidName(string name, GameServer server)
{
if (name.Contains("\n") || name.Contains("\r")) return false;
if (name.Any(c => c == ';' || c == ',' || c == '<' || c == '/')) return false;
char[] disallowedChars = new char[] { ';', ',', '<', '>', '/', '\\', '[', ']', '"', '?' };
if (name.Any(c => disallowedChars.Contains(c))) return false;
foreach (char character in name)
{
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml.Linq;
@@ -10,23 +9,14 @@ namespace Barotrauma.Networking
enum ClientPermissions
{
None = 0,
[Description("End round")]
EndRound = 1,
[Description("Kick")]
Kick = 2,
[Description("Ban")]
Ban = 4,
[Description("Revoke Ban")]
Unban = 8,
[Description("Select submarine")]
SelectSub = 16,
[Description("Select game mode")]
SelectMode = 32,
[Description("Manage campaign")]
ManageCampaign = 64,
[Description("Console commands")]
ConsoleCommands = 128,
[Description("Access server log")]
ServerLog = 256
}
@@ -118,7 +118,7 @@ namespace Barotrauma.Networking
public FileSender(NetworkMember networkMember)
{
peer = networkMember.netPeer;
peer = networkMember.NetPeer;
chunkLen = peer.Configuration.MaximumTransmissionUnit - 100;
activeTransfers = new List<FileTransferOut>();
@@ -205,6 +205,7 @@ namespace Barotrauma.Networking
message.Write((ushort)chunkLen);
message.Write((ulong)transfer.Data.Length);
message.Write(transfer.FileName);
GameMain.Server.CompressOutgoingMessage(message);
transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
transfer.Status = FileTransferStatus.Sending;
@@ -227,6 +228,7 @@ namespace Barotrauma.Networking
message.Write(sendBytes);
GameMain.Server.CompressOutgoingMessage(message);
transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
transfer.SentOffset += sendByteCount;
File diff suppressed because it is too large Load Diff
@@ -2,27 +2,29 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma.Networking
{
class UnauthenticatedClient
{
public NetConnection Connection;
public int Nonce;
public int failedAttempts;
public readonly NetConnection Connection;
public readonly ulong SteamID;
public Facepunch.Steamworks.ServerAuth.Status? SteamAuthStatus = null;
public readonly int Nonce;
public int FailedAttempts;
public float AuthTimer;
public UnauthenticatedClient(NetConnection connection, int nonce)
public UnauthenticatedClient(NetConnection connection, int nonce, ulong steamID = 0)
{
Connection = connection;
SteamID = steamID;
Nonce = nonce;
AuthTimer = 10.0f;
failedAttempts = 0;
FailedAttempts = 0;
}
}
@@ -30,28 +32,158 @@ namespace Barotrauma.Networking
{
List<UnauthenticatedClient> unauthenticatedClients = new List<UnauthenticatedClient>();
private void ClientAuthRequest(NetConnection conn)
private void ReadClientSteamAuthRequest(NetIncomingMessage inc, out ulong clientSteamID)
{
clientSteamID = 0;
if (!Steam.SteamManager.USE_STEAM)
{
//not using steam, handle auth normally
HandleClientAuthRequest(inc.SenderConnection, 0);
return;
}
clientSteamID = inc.ReadUInt64();
int authTicketLength = inc.ReadInt32();
inc.ReadBytes(authTicketLength, out byte[] authTicketData);
DebugConsole.Log("Received a Steam auth request");
DebugConsole.Log(" Steam ID: "+ clientSteamID);
DebugConsole.Log(" Auth ticket length: " + authTicketLength);
DebugConsole.Log(" Auth ticket data: " +
((authTicketData == null) ? "null" : ToolBox.LimitString(string.Concat(authTicketData.Select(b => b.ToString("X2"))), 16)));
if (banList.IsBanned("", clientSteamID))
{
return;
}
ulong steamID = clientSteamID;
if (unauthenticatedClients.Any(uc => uc.Connection == inc.SenderConnection))
{
var steamAuthedClient = unauthenticatedClients.Find(uc =>
uc.Connection == inc.SenderConnection &&
uc.SteamID == steamID &&
uc.SteamAuthStatus == Facepunch.Steamworks.ServerAuth.Status.OK);
if (steamAuthedClient != null)
{
DebugConsole.Log("Client already authenticated, sending AUTH_RESPONSE again...");
HandleClientAuthRequest(inc.SenderConnection, steamID);
}
DebugConsole.Log("Steam authentication already pending...");
return;
}
if (authTicketData == null)
{
DebugConsole.Log("Invalid request");
return;
}
unauthenticatedClients.RemoveAll(uc => uc.Connection == inc.SenderConnection);
var unauthClient = new UnauthenticatedClient(inc.SenderConnection, 0, clientSteamID)
{
AuthTimer = 20
};
unauthenticatedClients.Add(unauthClient);
if (!Steam.SteamManager.StartAuthSession(authTicketData, clientSteamID))
{
unauthenticatedClients.Remove(unauthClient);
if (GameMain.Config.RequireSteamAuthentication)
{
unauthClient.Connection.Disconnect(DisconnectReason.SteamAuthenticationFailed.ToString());
}
else
{
DebugConsole.Log("Steam authentication failed, skipping to basic auth...");
HandleClientAuthRequest(inc.SenderConnection);
return;
}
}
return;
}
public void OnAuthChange(ulong steamID, ulong ownerID, Facepunch.Steamworks.ServerAuth.Status status)
{
DebugConsole.Log("************ OnAuthChange");
DebugConsole.Log(" Steam ID: " + steamID);
DebugConsole.Log(" Owner ID: " + ownerID);
DebugConsole.Log(" Status: " + status);
UnauthenticatedClient unauthClient = unauthenticatedClients.Find(uc => uc.SteamID == ownerID);
if (unauthClient != null)
{
unauthClient.SteamAuthStatus = status;
switch (status)
{
case Facepunch.Steamworks.ServerAuth.Status.OK:
////steam authentication done, check password next
HandleClientAuthRequest(unauthClient.Connection, unauthClient.SteamID);
break;
default:
unauthenticatedClients.Remove(unauthClient);
if (GameMain.Config.RequireSteamAuthentication)
{
unauthClient.Connection.Disconnect(DisconnectReason.SteamAuthenticationFailed.ToString() + "; (" + status.ToString() + ")");
}
else
{
DebugConsole.Log("Steam authentication failed (" + status.ToString() + "), skipping to basic auth...");
HandleClientAuthRequest(unauthClient.Connection);
return;
}
break;
}
return;
}
else
{
DebugConsole.Log(" No unauthenticated clients found with the Steam ID " + steamID);
}
//kick connected client if status becomes invalid (e.g. VAC banned, not connected to steam)
if (status != Facepunch.Steamworks.ServerAuth.Status.OK && GameMain.Config.RequireSteamAuthentication)
{
var connectedClient = connectedClients.Find(c => c.SteamID == ownerID);
if (connectedClient != null)
{
KickClient(connectedClient, TextManager.Get("DisconnectMessage.SteamAuthNoLongerValid").Replace("[status]", status.ToString()));
}
}
}
private void HandleClientAuthRequest(NetConnection connection, ulong steamID = 0)
{
DebugConsole.Log("HandleClientAuthRequest (steamID " + steamID + ")");
if (GameMain.Config.RequireSteamAuthentication && steamID == 0)
{
connection.Disconnect(DisconnectReason.SteamAuthenticationRequired.ToString());
return;
}
//client wants to know if server requires password
if (ConnectedClients.Find(c => c.Connection == conn) != null)
if (ConnectedClients.Find(c => c.Connection == connection) != null)
{
//this client has already been authenticated
return;
}
UnauthenticatedClient unauthClient = unauthenticatedClients.Find(uc => uc.Connection == conn);
UnauthenticatedClient unauthClient = unauthenticatedClients.Find(uc => uc.Connection == connection);
if (unauthClient == null)
{
DebugConsole.Log("Unauthed client, generating a nonce...");
//new client, generate nonce and add to unauth queue
if (ConnectedClients.Count >= maxPlayers)
{
//server is full, can't allow new connection
conn.Disconnect("Server full");
connection.Disconnect(DisconnectReason.ServerFull.ToString());
return;
}
int nonce = CryptoRandom.Instance.Next();
unauthClient = new UnauthenticatedClient(conn, nonce);
unauthClient = new UnauthenticatedClient(connection, nonce, steamID);
unauthenticatedClients.Add(unauthClient);
}
unauthClient.AuthTimer = 10.0f;
@@ -67,15 +199,19 @@ namespace Barotrauma.Networking
nonceMsg.Write(true); //true = password
nonceMsg.Write((Int32)unauthClient.Nonce); //here's nonce, encrypt with this
}
server.SendMessage(nonceMsg, conn, NetDeliveryMethod.Unreliable);
CompressOutgoingMessage(nonceMsg);
DebugConsole.Log("Sending auth response...");
server.SendMessage(nonceMsg, connection, NetDeliveryMethod.Unreliable);
}
private void ClientInitRequest(NetIncomingMessage inc)
{
DebugConsole.Log("Received client init request");
if (ConnectedClients.Find(c => c.Connection == inc.SenderConnection) != null)
{
//this client was already authenticated
//another init request means they didn't get any update packets yet
DebugConsole.Log("Client already connected, ignoring...");
return;
}
@@ -83,7 +219,7 @@ namespace Barotrauma.Networking
if (unauthClient == null)
{
//client did not ask for nonce first, can't authorize
inc.SenderConnection.Disconnect("Client did not properly request authentication.");
inc.SenderConnection.Disconnect(DisconnectReason.AuthenticationRequired.ToString());
return;
}
@@ -96,12 +232,12 @@ namespace Barotrauma.Networking
string clPw = inc.ReadString();
if (clPw != saltedPw)
{
unauthClient.failedAttempts++;
if (unauthClient.failedAttempts > 3)
unauthClient.FailedAttempts++;
if (unauthClient.FailedAttempts > 3)
{
//disconnect and ban after too many failed attempts
banList.BanPlayer("Unnamed", unauthClient.Connection.RemoteEndPoint.Address.ToString(), "Too many failed login attempts.", null);
DisconnectUnauthClient(inc, unauthClient, "Too many failed login attempts. You have been automatically banned from the server.");
banList.BanPlayer("Unnamed", unauthClient.Connection.RemoteEndPoint.Address.ToString(), TextManager.Get("DisconnectMessage.TooManyFailedLogins"), duration: null);
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.TooManyFailedLogins, "");
Log(inc.SenderConnection.RemoteEndPoint.Address.ToString() + " has been banned from the server (too many wrong passwords)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(inc.SenderConnection.RemoteEndPoint.Address.ToString() + " has been banned from the server (too many wrong passwords)", Color.Red);
@@ -112,9 +248,10 @@ namespace Barotrauma.Networking
//not disconnecting the player here, because they'll still use the same connection and nonce if they try logging in again
NetOutgoingMessage reject = server.CreateMessage();
reject.Write((byte)ServerPacketHeader.AUTH_FAILURE);
reject.Write("Wrong password! You have "+Convert.ToString(4-unauthClient.failedAttempts)+" more attempts before you're banned from the server.");
reject.Write("Wrong password! You have " + Convert.ToString(4 - unauthClient.FailedAttempts) + " more attempts before you're banned from the server.");
Log(inc.SenderConnection.RemoteEndPoint.Address.ToString() + " failed to join the server (incorrect password)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(inc.SenderConnection.RemoteEndPoint.Address.ToString() + " failed to join the server (incorrect password)", Color.Red);
CompressOutgoingMessage(reject);
server.SendMessage(reject, unauthClient.Connection, NetDeliveryMethod.Unreliable);
unauthClient.AuthTimer = 10.0f;
return;
@@ -122,13 +259,32 @@ namespace Barotrauma.Networking
}
}
string clVersion = inc.ReadString();
string clPackageName = inc.ReadString();
string clPackageHash = inc.ReadString();
UInt16 contentPackageCount = inc.ReadUInt16();
List<string> contentPackageNames = new List<string>();
List<string> contentPackageHashes = new List<string>();
for (int i = 0; i < contentPackageCount; i++)
{
string packageName = inc.ReadString();
string packageHash = inc.ReadString();
contentPackageNames.Add(packageName);
contentPackageHashes.Add(packageHash);
if (contentPackageCount == 0)
{
DebugConsole.Log("Client is using content package " +
(packageName ?? "null") + " (" + (packageHash ?? "null" + ")"));
}
}
if (contentPackageCount == 0)
{
DebugConsole.Log("Client did not list any content packages.");
}
string clName = Client.SanitizeName(inc.ReadString());
if (string.IsNullOrWhiteSpace(clName))
{
DisconnectUnauthClient(inc, unauthClient, "You need a name.");
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.NoName, "");
Log(inc.SenderConnection.RemoteEndPoint.Address.ToString() + " couldn't join the server (no name given)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(inc.SenderConnection.RemoteEndPoint.Address.ToString() + " couldn't join the server (no name given)", Color.Red);
@@ -137,44 +293,100 @@ namespace Barotrauma.Networking
if (clVersion != GameMain.Version.ToString())
{
DisconnectUnauthClient(inc, unauthClient, "Version " + GameMain.Version + " required to connect to the server (Your version: " + clVersion + ")");
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion,
TextManager.Get("DisconnectMessage.InvalidVersion").Replace("[version]", GameMain.Version.ToString()).Replace("[clientversion]", clVersion));
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", Color.Red);
return;
}
if (clPackageName != GameMain.SelectedPackage.Name)
//check if the client is missing any of the content packages the server requires
List<ContentPackage> missingPackages = new List<ContentPackage>();
foreach (ContentPackage contentPackage in GameMain.SelectedPackages)
{
DisconnectUnauthClient(inc, unauthClient, "Your content package (" + clPackageName + ") doesn't match the server's version (" + GameMain.SelectedPackage.Name + ")");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong content package name)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong content package name)", Color.Red);
if (!contentPackage.HasMultiplayerIncompatibleContent) continue;
bool packageFound = false;
for (int i = 0; i < contentPackageCount; i++)
{
if (contentPackageNames[i] == contentPackage.Name && contentPackageHashes[i] == contentPackage.MD5hash.Hash)
{
packageFound = true;
break;
}
}
if (!packageFound) missingPackages.Add(contentPackage);
}
if (missingPackages.Count == 1)
{
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, TextManager.Get("DisconnectMessage.MissingContentPackage").Replace("[missingcontentpackage]", GetPackageStr(missingPackages[0])));
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
return;
}
if (clPackageHash != GameMain.SelectedPackage.MD5hash.Hash)
else if (missingPackages.Count > 1)
{
DisconnectUnauthClient(inc, unauthClient, "Your content package (MD5: " + clPackageHash + ") doesn't match the server's version (MD5: " + GameMain.SelectedPackage.MD5hash.Hash + ")");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong content package hash)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong content package hash)", Color.Red);
List<string> packageStrs = new List<string>();
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, TextManager.Get("DisconnectMessage.MissingContentPackages").Replace("[missingcontentpackages]", string.Join(", ", packageStrs)));
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return;
}
string GetPackageStr(ContentPackage contentPackage)
{
return "\"" + contentPackage.Name + "\" (hash " + contentPackage.MD5hash.ShortHash + ")";
}
//check if the client is using any contentpackages that are not compatible with the server
List<Pair<string, string>> incompatiblePackages = new List<Pair<string, string>>();
for (int i = 0; i < contentPackageNames.Count; i++)
{
if (!GameMain.Config.SelectedContentPackages.Any(cp => cp.Name == contentPackageNames[i] && cp.MD5hash.Hash == contentPackageHashes[i]))
{
incompatiblePackages.Add(new Pair<string, string>(contentPackageNames[i], contentPackageHashes[i]));
}
}
if (incompatiblePackages.Count == 1)
{
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage,
TextManager.Get("DisconnectMessage.IncompatibleContentPackage").Replace("[incompatiblecontentpackage]", GetPackageStr2(incompatiblePackages[0])));
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content package " + GetPackageStr2(incompatiblePackages[0]) + ")", ServerLog.MessageType.Error);
return;
}
else if (incompatiblePackages.Count > 1)
{
List<string> packageStrs = new List<string>();
incompatiblePackages.ForEach(cp => packageStrs.Add(GetPackageStr2(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage,
TextManager.Get("DisconnectMessage.IncompatibleContentPackages").Replace("[incompatiblecontentpackages]", string.Join(", ", packageStrs)));
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return;
}
string GetPackageStr2(Pair<string, string> nameAndHash)
{
return "\"" + nameAndHash.First + "\" (hash " + Md5Hash.GetShortHash(nameAndHash.Second) + ")";
}
if (!whitelist.IsWhiteListed(clName, inc.SenderConnection.RemoteEndPoint.Address.ToString()))
{
DisconnectUnauthClient(inc, unauthClient, "You're not in this server's whitelist.");
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.NotOnWhitelist, "");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (not in whitelist)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (not in whitelist)", Color.Red);
return;
}
if (!Client.IsValidName(clName, this))
{
DisconnectUnauthClient(inc, unauthClient, "Your name contains illegal symbols.");
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidName, "");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (invalid name)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (invalid name)", Color.Red);
return;
}
if (Homoglyphs.Compare(clName.ToLower(),Name.ToLower()))
{
DisconnectUnauthClient(inc, unauthClient, "That name is taken.");
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.NameTaken, "");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (name taken by the server)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (name taken by the server)", Color.Red);
return;
@@ -185,7 +397,7 @@ namespace Barotrauma.Networking
if (nameTaken.Connection.RemoteEndPoint.Address.ToString() == inc.SenderEndPoint.Address.ToString())
{
//both name and IP address match, replace this player's connection
nameTaken.Connection.Disconnect("Your session was taken by a new connection on the same IP address.");
nameTaken.Connection.Disconnect(DisconnectReason.SessionTaken.ToString());
nameTaken.Connection = unauthClient.Connection;
nameTaken.InitClientSync(); //reinitialize sync ids because this is a new connection
unauthenticatedClients.Remove(unauthClient);
@@ -195,7 +407,7 @@ namespace Barotrauma.Networking
else
{
//can't authorize this client
DisconnectUnauthClient(inc, unauthClient, "That name is taken.");
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.NameTaken, "");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (name already taken)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (name already taken)", Color.Red);
return;
@@ -206,6 +418,7 @@ namespace Barotrauma.Networking
Client newClient = new Client(clName, GetNewClientID());
newClient.InitClientSync();
newClient.Connection = unauthClient.Connection;
newClient.SteamID = unauthClient.SteamID;
unauthenticatedClients.Remove(unauthClient);
unauthClient = null;
ConnectedClients.Add(newClient);
@@ -215,7 +428,11 @@ namespace Barotrauma.Networking
#endif
GameMain.Server.SendChatMessage(clName + " has joined the server.", ChatMessageType.Server, null);
var savedPermissions = clientPermissions.Find(cp => cp.IP == newClient.Connection.RemoteEndPoint.Address.ToString());
var savedPermissions = clientPermissions.Find(cp =>
cp.SteamID > 0 ?
cp.SteamID == newClient.SteamID :
cp.IP == newClient.Connection.RemoteEndPoint.Address.ToString());
if (savedPermissions != null)
{
newClient.SetPermissions(savedPermissions.Permissions, savedPermissions.PermittedCommands);
@@ -226,9 +443,9 @@ namespace Barotrauma.Networking
}
}
private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, string reason)
private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, DisconnectReason reason, string message)
{
inc.SenderConnection.Disconnect(reason);
inc.SenderConnection.Disconnect(reason.ToString() + "; " + message);
if (unauthClient != null)
{
@@ -19,11 +19,17 @@ namespace Barotrauma.Networking
No = 0, Maybe = 1, Yes = 2
}
enum BotSpawnMode
{
Normal, Fill
}
partial class GameServer : NetworkMember, ISerializableEntity
{
private class SavedClientPermission
{
public readonly string IP;
public readonly ulong SteamID;
public readonly string Name;
public List<DebugConsole.Command> PermittedCommands;
@@ -34,6 +40,14 @@ namespace Barotrauma.Networking
this.Name = name;
this.IP = ip;
this.Permissions = permissions;
this.PermittedCommands = permittedCommands;
}
public SavedClientPermission(string name, ulong steamID, ClientPermissions permissions, List<DebugConsole.Command> permittedCommands)
{
this.Name = name;
this.SteamID = steamID;
this.Permissions = permissions;
this.PermittedCommands = permittedCommands;
}
@@ -58,6 +72,8 @@ namespace Barotrauma.Networking
private SelectionMode subSelectionMode, modeSelectionMode;
private float selectedLevelDifficulty;
private bool registeredToMaster;
private WhiteList whitelist;
@@ -102,8 +118,7 @@ namespace Barotrauma.Networking
get;
private set;
}
[Serialize(60.0f, true)]
public float AutoRestartInterval
{
@@ -111,6 +126,20 @@ namespace Barotrauma.Networking
set;
}
[Serialize(false, true)]
public bool StartWhenClientsReady
{
get;
private set;
}
[Serialize(0.8f, true)]
public float StartWhenClientsReadyRatio
{
get;
private set;
}
[Serialize(true, true)]
public bool AllowSpectating
{
@@ -176,6 +205,32 @@ namespace Barotrauma.Networking
get;
set;
}
[Serialize(0, true)]
public int BotCount
{
get;
set;
}
[Serialize(16, true)]
public int MaxBotCount
{
get;
set;
}
public BotSpawnMode BotSpawnMode
{
get;
set;
}
public float SelectedLevelDifficulty
{
get { return selectedLevelDifficulty; }
set { selectedLevelDifficulty = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
[Serialize(true, true)]
public bool AllowDisguises
@@ -233,6 +288,13 @@ namespace Barotrauma.Networking
private set;
}
[Serialize(120.0f, true)]
public float KickAFKTime
{
get;
private set;
}
[Serialize(true, true)]
public bool TraitorUseRatio
{
@@ -254,8 +316,8 @@ namespace Barotrauma.Networking
set;
}
[Serialize("Sandbox", true)]
public string GameMode
[Serialize("sandbox", true)]
public string GameModeIdentifier
{
get;
set;
@@ -267,8 +329,13 @@ namespace Barotrauma.Networking
get;
set;
}
public int MaxPlayers
{
get { return maxPlayers; }
}
public List<string> AllowedRandomMissionTypes
public List<MissionType> AllowedRandomMissionTypes
{
get;
set;
@@ -305,17 +372,22 @@ namespace Barotrauma.Networking
doc.Root.SetAttributeValue("name", name);
doc.Root.SetAttributeValue("public", isPublic);
doc.Root.SetAttributeValue("port", config.Port);
doc.Root.SetAttributeValue("port", NetPeerConfiguration.Port);
if (Steam.SteamManager.USE_STEAM) doc.Root.SetAttributeValue("queryport", QueryPort);
doc.Root.SetAttributeValue("maxplayers", maxPlayers);
doc.Root.SetAttributeValue("enableupnp", config.EnableUPnP);
doc.Root.SetAttributeValue("enableupnp", NetPeerConfiguration.EnableUPnP);
doc.Root.SetAttributeValue("autorestart", autoRestart);
doc.Root.SetAttributeValue("SubSelection", subSelectionMode.ToString());
doc.Root.SetAttributeValue("ModeSelection", modeSelectionMode.ToString());
doc.Root.SetAttributeValue("LevelDifficulty", ((int)selectedLevelDifficulty).ToString());
doc.Root.SetAttributeValue("TraitorsEnabled", TraitorsEnabled.ToString());
/*doc.Root.SetAttributeValue("BotCount", BotCount);
doc.Root.SetAttributeValue("MaxBotCount", MaxBotCount);*/
doc.Root.SetAttributeValue("BotSpawnMode", BotSpawnMode.ToString());
doc.Root.SetAttributeValue("AllowedRandomMissionTypes", string.Join(",", AllowedRandomMissionTypes));
doc.Root.SetAttributeValue("AllowedClientNameChars", string.Join(",", AllowedClientNameChars.Select(c => c.First + "-" + c.Second)));
@@ -374,10 +446,17 @@ namespace Barotrauma.Networking
Enum.TryParse(doc.Root.GetAttributeString("ModeSelection", "Manual"), out modeSelectionMode);
Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
selectedLevelDifficulty = doc.Root.GetAttributeFloat("LevelDifficulty", 20.0f);
GameMain.NetLobbyScreen.SetLevelDifficulty(selectedLevelDifficulty);
var traitorsEnabled = TraitorsEnabled;
Enum.TryParse(doc.Root.GetAttributeString("TraitorsEnabled", "No"), out traitorsEnabled);
TraitorsEnabled = traitorsEnabled;
GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled);
var botSpawnMode = BotSpawnMode.Fill;
Enum.TryParse(doc.Root.GetAttributeString("BotSpawnMode", "Fill"), out botSpawnMode);
BotSpawnMode = botSpawnMode;
//"65-90", "97-122", "48-59" = upper and lower case english alphabet and numbers
string[] allowedClientNameCharsStr = doc.Root.GetAttributeStringArray("AllowedClientNameChars", new string[] { "32-33", "65-90", "97-122", "48-59" });
@@ -406,12 +485,20 @@ namespace Barotrauma.Networking
}
}
if (min > -1 && max > -1) AllowedClientNameChars.Add(Pair<int, int>.Create(min, max));
if (min > -1 && max > -1) AllowedClientNameChars.Add(new Pair<int, int>(min, max));
}
AllowedRandomMissionTypes = doc.Root.GetAttributeStringArray(
"AllowedRandomMissionTypes",
MissionPrefab.MissionTypes.ToArray()).ToList();
AllowedRandomMissionTypes = new List<MissionType>();
string[] allowedMissionTypeNames = doc.Root.GetAttributeStringArray(
"AllowedRandomMissionTypes", Enum.GetValues(typeof(MissionType)).Cast<MissionType>().Select(m => m.ToString()).ToArray());
foreach (string missionTypeName in allowedMissionTypeNames)
{
if (Enum.TryParse(missionTypeName, out MissionType missionType))
{
if (missionType == Barotrauma.MissionType.None) continue;
AllowedRandomMissionTypes.Add(missionType);
}
}
if (GameMain.NetLobbyScreen != null
#if CLIENT
@@ -421,17 +508,20 @@ namespace Barotrauma.Networking
{
#if SERVER
GameMain.NetLobbyScreen.ServerName = doc.Root.GetAttributeString("name", "");
GameMain.NetLobbyScreen.SelectedModeName = GameMode;
GameMain.NetLobbyScreen.SelectedModeIdentifier = GameModeIdentifier;
GameMain.NetLobbyScreen.MissionTypeName = MissionType;
#endif
GameMain.NetLobbyScreen.ServerMessageText = doc.Root.GetAttributeString("ServerMessage", "");
}
GameMain.NetLobbyScreen.SetBotSpawnMode(BotSpawnMode);
GameMain.NetLobbyScreen.SetBotCount(BotCount);
#if CLIENT
showLogButton.Visible = SaveServerLogs;
#endif
List<string> monsterNames = GameMain.Config.SelectedContentPackage.GetFilesOfType(ContentType.Character);
List<string> monsterNames = GameMain.Instance.GetFilesOfType(ContentType.Character).ToList();
for (int i = 0; i < monsterNames.Count; i++)
{
monsterNames[i] = Path.GetFileName(Path.GetDirectoryName(monsterNames[i]));
@@ -465,15 +555,29 @@ namespace Barotrauma.Networking
{
string clientName = clientElement.GetAttributeString("name", "");
string clientIP = clientElement.GetAttributeString("ip", "");
if (string.IsNullOrWhiteSpace(clientName) || string.IsNullOrWhiteSpace(clientIP))
string steamIdStr = clientElement.GetAttributeString("steamid", "");
if (string.IsNullOrWhiteSpace(clientName))
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have a name and an IP address.");
continue;
}
if (string.IsNullOrWhiteSpace(clientIP) && string.IsNullOrWhiteSpace(steamIdStr))
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have an IP address or a Steam ID.");
continue;
}
string permissionsStr = clientElement.GetAttributeString("permissions", "");
ClientPermissions permissions;
if (!Enum.TryParse(permissionsStr, out permissions))
ClientPermissions permissions = ClientPermissions.None;
if (permissionsStr.ToLowerInvariant() == "all")
{
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
{
permissions |= permission;
}
}
else if (!Enum.TryParse(permissionsStr, out permissions))
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + permissionsStr + "\" is not a valid client permission.");
continue;
@@ -498,7 +602,22 @@ namespace Barotrauma.Networking
}
}
clientPermissions.Add(new SavedClientPermission(clientName, clientIP, permissions, permittedCommands));
if (!string.IsNullOrEmpty(steamIdStr))
{
if (ulong.TryParse(steamIdStr, out ulong steamID))
{
clientPermissions.Add(new SavedClientPermission(clientName, steamID, permissions, permittedCommands));
}
else
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + steamIdStr + "\" is not a valid Steam ID.");
continue;
}
}
else
{
clientPermissions.Add(new SavedClientPermission(clientName, clientIP, permissions, permittedCommands));
}
}
}
@@ -554,9 +673,17 @@ namespace Barotrauma.Networking
{
XElement clientElement = new XElement("Client",
new XAttribute("name", clientPermission.Name),
new XAttribute("ip", clientPermission.IP),
new XAttribute("permissions", clientPermission.Permissions.ToString()));
if (clientPermission.SteamID > 0)
{
clientElement.Add(new XAttribute("steamid", clientPermission.SteamID));
}
else
{
clientElement.Add(new XAttribute("ip", clientPermission.IP));
}
if (clientPermission.Permissions.HasFlag(ClientPermissions.ConsoleCommands))
{
foreach (DebugConsole.Command command in clientPermission.PermittedCommands)
@@ -2,8 +2,9 @@
{
static class NetConfig
{
public const int DefaultPort = 14242;
public const int DefaultPort = 27015;
public const int DefaultQueryPort = 27016;
public const int MaxPlayers = 16;
public static string MasterServerUrl = GameMain.Config.MasterServerUrl;
@@ -11,10 +11,11 @@ namespace Barotrauma.Networking
ComponentState,
InventoryState,
Status,
Repair,
Treatment,
ApplyStatusEffect,
ChangeProperty,
Control
Control,
UpdateSkills
}
public readonly Entity Entity;
@@ -37,7 +37,7 @@ namespace Barotrauma.Networking
//write an empty event to avoid messing up IDs
//(otherwise the clients might read the next event in the message and think its ID
//is consecutive to the previous one, even though we skipped over this broken event)
tempBuffer.Write((UInt16)0);
tempBuffer.Write(Entity.NullEntityID);
tempBuffer.WritePadBits();
eventCount++;
continue;
@@ -67,7 +67,7 @@ namespace Barotrauma.Networking
{
//technically the clients don't have any use for these, but removing events and shifting the IDs of all
//consecutive ones is so error-prone that I think this is a safer option
tempBuffer.Write((UInt16)0);
tempBuffer.Write(Entity.NullEntityID);
tempBuffer.WritePadBits();
}*/
else
@@ -88,10 +88,7 @@ namespace Barotrauma.Networking
msg.Write(tempBuffer);
}
}
protected virtual void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null)
{
throw new NotImplementedException();
}
protected abstract void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null);
}
}
@@ -75,7 +75,7 @@ namespace Barotrauma.Networking
return;
}
if (((Entity)entity).Removed)
if (((Entity)entity).Removed && !(entity is Level))
{
DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n"+Environment.StackTrace);
return;
@@ -338,7 +338,7 @@ namespace Barotrauma.Networking
UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
UInt16 entityID = msg.ReadUInt16();
if (entityID == 0)
if (entityID == Entity.NullEntityID)
{
msg.ReadPadBits();
if (thisEventID == (UInt16)(sender.LastSentEntityEventID + 1)) sender.LastSentEntityEventID++;
@@ -10,6 +10,7 @@ namespace Barotrauma.Networking
enum ClientPacketHeader
{
REQUEST_AUTH, //ask the server if a password is needed, if so we'll get nonce for encryption
REQUEST_STEAMAUTH, //the same as REQUEST_AUTH, but in addition we want to authenticate the player's Steam ID
REQUEST_INIT, //ask the server to give you initialization
UPDATE_LOBBY, //update state in lobby
UPDATE_INGAME, //update state ingame
@@ -45,6 +46,7 @@ namespace Barotrauma.Networking
UPDATE_INGAME, //update state ingame (character input and chat messages)
PERMISSIONS, //tell the client which special permissions they have (if any)
ACHIEVEMENT, //give the client a steam achievement
FILE_TRANSFER,
@@ -60,7 +62,7 @@ namespace Barotrauma.Networking
VOTE,
ENTITY_POSITION,
ENTITY_EVENT,
ENTITY_EVENT_INITIAL
ENTITY_EVENT_INITIAL,
}
enum VoteType
@@ -69,7 +71,29 @@ namespace Barotrauma.Networking
Sub,
Mode,
EndRound,
Kick
Kick,
StartRound
}
enum DisconnectReason
{
Unknown,
Banned,
Kicked,
ServerShutdown,
ServerFull,
AuthenticationRequired,
SteamAuthenticationRequired,
SteamAuthenticationFailed,
SessionTaken,
TooManyFailedLogins,
NoName,
InvalidName,
NameTaken,
InvalidVersion,
MissingContentPackage,
IncompatibleContentPackage,
NotOnWhitelist,
}
abstract partial class NetworkMember
@@ -78,7 +102,7 @@ namespace Barotrauma.Networking
public Dictionary<string, long> messageCount = new Dictionary<string, long>();
#endif
public NetPeer netPeer
public NetPeer NetPeer
{
get;
protected set;
@@ -136,6 +160,12 @@ namespace Barotrauma.Networking
protected set;
}
public NetPeerConfiguration NetPeerConfiguration
{
get;
protected set;
}
public NetworkMember()
{
InitProjSpecific();
@@ -155,17 +185,15 @@ namespace Barotrauma.Networking
return radioComponent.HasRequiredContainedItems(false);
}
public void AddChatMessage(string message, ChatMessageType type, string senderName="", Character senderCharacter = null)
public void AddChatMessage(string message, ChatMessageType type, string senderName = "", Character senderCharacter = null)
{
AddChatMessage(ChatMessage.Create(senderName, message, type, senderCharacter));
}
public void AddChatMessage(ChatMessage message)
{
GameServer.Log(message.TextWithSender, ServerLog.MessageType.Chat);
string displayedText = message.Text;
if (message.Sender != null && !message.Sender.IsDead)
{
message.Sender.ShowSpeechBubble(2.0f, ChatMessage.MessageColor[(int)message.Type]);
@@ -173,42 +201,7 @@ namespace Barotrauma.Networking
#if CLIENT
GameMain.NetLobbyScreen.NewChatMessage(message);
while (chatBox.CountChildren > 20)
{
chatBox.RemoveChild(chatBox.children[0]);
}
if (!string.IsNullOrWhiteSpace(message.SenderName))
{
displayedText = (message.Type == ChatMessageType.Private ? "[PM] " : "" ) + message.SenderName + ": " + displayedText;
}
GUITextBlock msg = new GUITextBlock(new Rectangle(0, 0, chatBox.Rect.Width - 40, 0), displayedText,
((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, message.Color,
Alignment.Left, Alignment.TopLeft, "", null, true, GUI.SmallFont);
msg.UserData = message.SenderName;
msg.Padding = new Vector4(20.0f, 0, 0, 0);
float prevSize = chatBox.BarSize;
msg.Padding = new Vector4(20, 0, 0, 0);
chatBox.AddChild(msg);
if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) chatBox.BarScroll = 1.0f;
GUISoundType soundType = GUISoundType.Message;
if (message.Type == ChatMessageType.Radio)
{
soundType = GUISoundType.RadioMessage;
}
else if (message.Type == ChatMessageType.Dead)
{
soundType = GUISoundType.DeadMessage;
}
GUI.PlayUISound(soundType);
chatBox.AddMessage(message);
#endif
}
@@ -221,44 +214,7 @@ namespace Barotrauma.Networking
public virtual void Update(float deltaTime)
{
#if CLIENT
GUITextBox msgBox = (Screen.Selected == GameMain.GameScreen ? chatMsgBox : GameMain.NetLobbyScreen.TextBox);
if (gameStarted && Screen.Selected == GameMain.GameScreen)
{
msgBox.Visible = Character.Controlled == null || Character.Controlled.CanSpeak;
if (!GUI.DisableHUD)
{
inGameHUD.Update(deltaTime);
GameMain.GameSession.CrewManager.Update(deltaTime);
}
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,
//because tab is used for autocompleting console commands
if ((PlayerInput.KeyHit(InputType.Chat) || PlayerInput.KeyHit(InputType.RadioChat)) &&
!DebugConsole.IsOpen && (Screen.Selected != GameMain.GameScreen || msgBox.Visible))
{
if (msgBox.Selected)
{
msgBox.Text = "";
msgBox.Deselect();
}
else
{
msgBox.Select();
if (Screen.Selected == GameMain.GameScreen && PlayerInput.KeyHit(InputType.RadioChat))
{
msgBox.Text = "r; ";
msgBox.OnTextChanged?.Invoke(msgBox, msgBox.Text);
}
}
}
UpdateHUD(deltaTime);
#endif
}
@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
namespace Barotrauma.Networking
{
partial class OrderChatMessage : ChatMessage
{
public readonly Order Order;
//who was this order given to
public readonly Character TargetCharacter;
//which entity is this order referring to (hull, reactor, railgun controller, etc)
public readonly Entity TargetEntity;
//additional instructions (power up, fire at will, etc)
public readonly string OrderOption;
public OrderChatMessage(Order order, string orderOption, Entity targetEntity, Character targetCharacter, Character sender)
: this(order, orderOption,
order.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.RoomName, orderOption),
targetEntity, targetCharacter, sender)
{
}
public OrderChatMessage(Order order, string orderOption, string text, Entity targetEntity, Character targetCharacter, Character sender)
: base(sender?.Name, text, ChatMessageType.Order, sender)
{
Order = order;
OrderOption = orderOption;
TargetCharacter = targetCharacter;
TargetEntity = targetEntity;
}
public override void ServerWrite(NetOutgoingMessage msg, Client c)
{
msg.Write((byte)ServerNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);
msg.Write((byte)ChatMessageType.Order);
msg.Write(SenderName);
msg.Write(Sender != null && c.InGame);
if (Sender != null && c.InGame)
{
msg.Write(Sender.ID);
}
msg.Write((byte)Order.PrefabList.IndexOf(Order.Prefab));
msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID);
msg.Write(TargetEntity == null ? (UInt16)0 : TargetEntity.ID);
msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption));
}
}
}
@@ -103,7 +103,10 @@ namespace Barotrauma.Networking
{
foreach (Connection connection in connectionPanel.Connections)
{
Array.ForEach(connection.Wires, w => { if (w != null) w.Locked = true; });
foreach (Wire wire in connection.Wires)
{
if (wire != null) wire.Locked = true;
}
}
}
}
@@ -128,6 +131,45 @@ namespace Barotrauma.Networking
(c.Character == null || c.Character.IsDead));
}
private List<CharacterInfo> GetBotsToRespawn()
{
GameServer server = networkMember as GameServer;
if (server.BotSpawnMode == BotSpawnMode.Normal)
{
return Character.CharacterList
.FindAll(c => c.TeamID == 1 && c.AIController != null && c.Info != null && c.IsDead)
.Select(c => c.Info)
.ToList();
}
int currPlayerCount = server.ConnectedClients.Count(c => c.InGame && (!c.SpectateOnly || !server.AllowSpectating));
if (server.CharacterInfo != null) currPlayerCount++;
var existingBots = Character.CharacterList
.FindAll(c => c.TeamID == 1 && c.AIController != null && c.Info != null);
int requiredBots = server.BotCount - currPlayerCount;
requiredBots -= existingBots.Count(b => !b.IsDead);
List<CharacterInfo> botsToRespawn = new List<CharacterInfo>();
for (int i = 0; i < requiredBots; i++)
{
CharacterInfo botToRespawn = existingBots.Find(b => b.IsDead)?.Info;
if (botToRespawn == null)
{
botToRespawn = new CharacterInfo(Character.HumanConfigFile);
}
else
{
existingBots.Remove(botToRespawn.Character);
}
botsToRespawn.Add(botToRespawn);
}
return botsToRespawn;
}
public void Update(float deltaTime)
{
if (respawnShuttle == null)
@@ -425,22 +467,21 @@ namespace Barotrauma.Networking
foreach (Character c in Character.CharacterList)
{
if (c.Submarine == respawnShuttle)
{
if (Character.Controlled == c) Character.Controlled = null;
c.Enabled = false;
if (c.Inventory != null)
{
foreach (Item item in c.Inventory.Items)
{
if (item == null) continue;
Spawner.AddToRemoveQueue(item);
}
}
if (c.Submarine != respawnShuttle) continue;
if (Character.Controlled == c) Character.Controlled = null;
c.Kill(CauseOfDeathType.Unknown, null, true);
c.Enabled = false;
c.Kill(CauseOfDeath.Damage, true);
}
Spawner.AddToRemoveQueue(c);
if (c.Inventory != null)
{
foreach (Item item in c.Inventory.Items)
{
if (item == null) continue;
Spawner.AddToRemoveQueue(item);
}
}
}
respawnShuttle.SetPosition(new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + respawnShuttle.Borders.Height));
@@ -466,8 +507,11 @@ namespace Barotrauma.Networking
c.TeamID = 1;
if (c.CharacterInfo == null) c.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, c.Name);
}
List<CharacterInfo> characterInfos = clients.Select(c => c.CharacterInfo).ToList();
var botsToSpawn = GetBotsToRespawn();
characterInfos.AddRange(botsToSpawn);
if (server.Character != null && server.Character.IsDead)
{
characterInfos.Add(server.CharacterInfo);
@@ -485,10 +529,10 @@ namespace Barotrauma.Networking
//(in order to give them appropriate ID card tags)
var mainSubSpawnPoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub);
ItemPrefab divingSuitPrefab = MapEntityPrefab.Find("Diving Suit") as ItemPrefab;
ItemPrefab oxyPrefab = MapEntityPrefab.Find("Oxygen Tank") as ItemPrefab;
ItemPrefab scooterPrefab = MapEntityPrefab.Find("Underwater Scooter") as ItemPrefab;
ItemPrefab batteryPrefab = MapEntityPrefab.Find("Battery Cell") as ItemPrefab;
ItemPrefab divingSuitPrefab = MapEntityPrefab.Find(null, "divingsuit") as ItemPrefab;
ItemPrefab oxyPrefab = MapEntityPrefab.Find(null, "oxygentank") as ItemPrefab;
ItemPrefab scooterPrefab = MapEntityPrefab.Find(null, "underwaterscooter") as ItemPrefab;
ItemPrefab batteryPrefab = MapEntityPrefab.Find(null, "batterycell") as ItemPrefab;
var cargoSp = WayPoint.WayPointList.Find(wp => wp.Submarine == respawnSub && wp.SpawnType == SpawnType.Cargo);
@@ -496,14 +540,16 @@ namespace Barotrauma.Networking
{
bool myCharacter = false;
#if CLIENT
myCharacter = i >= clients.Count;
myCharacter = i >= clients.Count + botsToSpawn.Count;
#endif
bool bot = i >= clients.Count && !myCharacter;
var character = Character.Create(characterInfos[i], shuttleSpawnPoints[i].WorldPosition, !myCharacter, false);
var character = Character.Create(characterInfos[i], shuttleSpawnPoints[i].WorldPosition, characterInfos[i].Name, !myCharacter && !bot, bot);
character.TeamID = 1;
#if CLIENT
GameMain.GameSession.CrewManager.AddCharacter(character);
if (myCharacter)
{
server.Character = character;
@@ -512,19 +558,23 @@ namespace Barotrauma.Networking
GameMain.LightManager.LosEnabled = true;
GameServer.Log(string.Format("Respawning {0} (host) as {1}", character.Name, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
}
else
#endif
if (!myCharacter)
{
#endif
clients[i].Character = character;
character.OwnerClientIP = clients[i].Connection.RemoteEndPoint.Address.ToString();
character.OwnerClientName = clients[i].Name;
GameServer.Log(string.Format("Respawning {0} ({1}) as {2}", clients[i].Name, clients[i].Connection?.RemoteEndPoint?.Address, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
#if CLIENT
if (bot)
{
GameServer.Log(string.Format("Respawning bot {0} as {1}", character.Info.Name, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
}
else
{
clients[i].Character = character;
character.OwnerClientIP = clients[i].Connection.RemoteEndPoint.Address.ToString();
character.OwnerClientName = clients[i].Name;
GameServer.Log(string.Format("Respawning {0} ({1}) as {2}", clients[i].Name, clients[i].Connection?.RemoteEndPoint?.Address, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
}
}
#endif
if (respawnShuttle != null)
if (divingSuitPrefab != null && oxyPrefab != null && respawnShuttle != null)
{
Vector2 pos = cargoSp == null ? character.Position : cargoSp.Position;
if (divingSuitPrefab != null && oxyPrefab != null)
@@ -567,9 +617,6 @@ namespace Barotrauma.Networking
if (!string.IsNullOrWhiteSpace(shuttleSpawnPoints[i].IdCardDesc))
item.Description = shuttleSpawnPoints[i].IdCardDesc;
}
#if CLIENT
GameMain.GameSession.CrewManager.AddCharacter(character);
#endif
}
}
@@ -0,0 +1,60 @@
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Networking
{
partial class ServerInfo
{
public string IP;
public string Port;
public string ServerName;
public string ServerMessage;
public bool GameStarted;
public int PlayerCount;
public int MaxPlayers;
public bool HasPassword;
public bool PingChecked;
public int Ping = -1;
//null value means that the value isn't known (the server may be using
//an old version of the game that didn't report these values or the FetchRules query to Steam may not have finished yet)
public bool? UsingWhiteList;
public SelectionMode? ModeSelectionMode;
public SelectionMode? SubSelectionMode;
public bool? AllowSpectating;
public bool? AllowRespawn;
public YesNoMaybe? TraitorsEnabled;
public string GameMode;
public bool? RespondedToSteamQuery = null;
public string GameVersion;
public List<string> ContentPackageNames
{
get;
private set;
} = new List<string>();
public List<string> ContentPackageHashes
{
get;
private set;
} = new List<string>();
public List<string> ContentPackageWorkshopUrls
{
get;
private set;
} = new List<string>();
public bool ContentPackagesMatch(IEnumerable<ContentPackage> myContentPackages)
{
return ContentPackagesMatch(myContentPackages.Select(cp => cp.MD5hash.Hash));
}
public bool ContentPackagesMatch(IEnumerable<string> myContentPackageHashes)
{
HashSet<string> contentPackageHashes = new HashSet<string>(ContentPackageHashes);
return contentPackageHashes.SetEquals(myContentPackageHashes);
}
}
}
@@ -116,9 +116,9 @@ namespace Barotrauma.Networking
}
#if CLIENT
while (listBox != null && listBox.children.Count > LinesPerFile)
while (listBox != null && listBox.Content.CountChildren > LinesPerFile)
{
listBox.RemoveChild(listBox.children[0]);
listBox.RemoveChild(listBox.Content.Children.First());
}
#endif
}
File diff suppressed because it is too large Load Diff
@@ -26,7 +26,7 @@ namespace Barotrauma
var existingVotable = voteList.Find(v => v.First == vote || v.First.Equals(vote));
if (existingVotable == null)
{
voteList.Add(Pair<object, int>.Create(vote, 1));
voteList.Add(new Pair<object, int>(vote, 1));
}
else
{
@@ -101,8 +101,8 @@ namespace Barotrauma
break;
case VoteType.Mode:
string modeName = inc.ReadString();
GameModePreset mode = GameModePreset.list.Find(gm => gm.Name == modeName);
string modeIdentifier = inc.ReadString();
GameModePreset mode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier);
if (!mode.Votable) break;
sender.SetVote(voteType, mode);
@@ -130,6 +130,18 @@ namespace Barotrauma
GameMain.Server.SendChatMessage(sender.Name + " has voted to kick " + kicked.Name, ChatMessageType.Server, null);
}
break;
case VoteType.StartRound:
bool ready = inc.ReadBoolean();
if (ready != sender.GetVote<bool>(VoteType.StartRound))
{
sender.SetVote(VoteType.StartRound, ready);
GameServer.Log(sender.Name + (ready ? " is ready to start the game." : " is not ready to start the game."), ServerLog.MessageType.ServerMessage);
#if CLIENT
UpdateVoteTexts(GameMain.Server.ConnectedClients, voteType);
#endif
}
break;
}
@@ -161,7 +173,7 @@ namespace Barotrauma
foreach (Pair<object, int> vote in voteList)
{
msg.Write((byte)vote.Second);
msg.Write(((GameModePreset)vote.First).Name);
msg.Write(((GameModePreset)vote.First).Identifier);
}
}
msg.Write(AllowEndVoting);
@@ -173,6 +185,13 @@ namespace Barotrauma
msg.Write(AllowVoteKick);
var readyClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote<bool>(VoteType.StartRound));
msg.Write((byte)readyClients.Count);
foreach (Client c in readyClients)
{
msg.Write(c.ID);
}
msg.WritePadBits();
}