5202af9...3ea33fb

This commit is contained in:
Joonas Rikkonen
2019-03-18 21:46:09 +02:00
parent 044fd3344b
commit 97f31d0c94
61 changed files with 2585 additions and 558 deletions
@@ -116,22 +116,12 @@ namespace Barotrauma.Networking
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
//do nothing
}
else if (orderTargetCharacter != null)
{
orderTargetCharacter.SetOrder(
new Order(orderMsg.Order.Prefab, orderTargetEntity, (orderTargetEntity as Item)?.GetComponent<ItemComponent>()),
new Order(orderMsg.Order.Prefab, orderTargetEntity, (orderTargetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == orderMsg.Order.ItemComponentType)),
orderMsg.OrderOption, orderMsg.Sender);
}
@@ -483,7 +483,7 @@ namespace Barotrauma.Networking
(OwnerConnection == null || c.Connection != OwnerConnection));
foreach (Client c in kickAFK)
{
KickClient(c, TextManager.Get("DisconnectMessage.AFK"));
KickClient(c, "DisconnectMessage.AFK");
}
NetIncomingMessage inc = null;
@@ -956,11 +956,11 @@ namespace Barotrauma.Networking
Log("Client \"" + sender.Name + "\" banned \"" + bannedClient.Name + "\".", ServerLog.MessageType.ServerMessage);
if (durationSeconds > 0)
{
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? "Banned by " + sender.Name : banReason, range, TimeSpan.FromSeconds(durationSeconds));
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? $"ServerMessage.BannedBy_[initiator]={sender.Name}" : banReason, range, TimeSpan.FromSeconds(durationSeconds));
}
else
{
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? "Banned by " + sender.Name : banReason, range);
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? $"ServerMessage.BannedBy_[initiator]={sender.Name}" : banReason, range);
}
}
break;
@@ -1981,14 +1981,14 @@ namespace Barotrauma.Networking
client.HasSpawned = false;
client.InGame = false;
if (string.IsNullOrWhiteSpace(msg))
if (string.IsNullOrWhiteSpace(msg)) msg = $"ServerMessage.ClientLeftServer_[client]={client.Name}";
if (string.IsNullOrWhiteSpace(targetmsg)) targetmsg = "ServerMessage.YouLeftServer";
if (!string.IsNullOrWhiteSpace(reason))
{
msg = $"ServerMessage.ClientLeftServer_[client]={client.Name}";
msg += $"; ;ServerMessage.Reason;: ;{reason}";
targetmsg += $";\n;ServerMessage.Reason;: ;{reason}";
}
if (string.IsNullOrWhiteSpace(targetmsg)) targetmsg = "ServerMessage.YouLeftServer";
if (!string.IsNullOrWhiteSpace(reason)) msg += $";ServerMessage.Reason;{reason}";
Log(msg, ServerLog.MessageType.ServerMessage);
if (client.SteamID > 0) { SteamManager.StopAuthSession(client.SteamID); }
@@ -2236,7 +2236,7 @@ namespace Barotrauma.Networking
if (type.Value != ChatMessageType.MessageBox)
{
string myReceivedMessage = message;
string myReceivedMessage = TextManager.GetServerMessage(message);
if (!string.IsNullOrWhiteSpace(myReceivedMessage) &&
(targetClient == null || senderClient == null))
@@ -0,0 +1,511 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma.Networking
{
class UnauthenticatedClient
{
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, ulong steamID = 0)
{
Connection = connection;
SteamID = steamID;
Nonce = nonce;
AuthTimer = 10.0f;
FailedAttempts = 0;
}
}
partial class GameServer : NetworkMember
{
private Int32 ownerKey = 0;
List<UnauthenticatedClient> unauthenticatedClients = new List<UnauthenticatedClient>();
private void ReadClientSteamAuthRequest(NetIncomingMessage inc, NetConnection senderConnection, out ulong clientSteamID)
{
clientSteamID = 0;
if (!Steam.SteamManager.USE_STEAM)
{
DebugConsole.Log("Received a Steam auth request from " + senderConnection.RemoteEndPoint + ". Steam authentication not required, handling auth normally.");
//not using steam, handle auth normally
HandleClientAuthRequest(senderConnection, 0);
return;
}
if (senderConnection == OwnerConnection)
{
//the client is the owner of the server, no need for authentication
//(it would fail with a "duplicate request" error anyway)
HandleClientAuthRequest(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 (senderConnection != OwnerConnection &&
serverSettings.BanList.IsBanned(senderConnection.RemoteEndPoint.Address, 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 == senderConnection);
int nonce = CryptoRandom.Instance.Next();
var unauthClient = new UnauthenticatedClient(senderConnection, nonce, 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());
Log("Disconnected unauthenticated client (Steam ID: " + steamID + "). Steam authentication failed.", ServerLog.MessageType.ServerMessage);
}
else
{
DebugConsole.Log("Steam authentication failed, skipping to basic auth...");
HandleClientAuthRequest(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
Log("Successfully authenticated client via Steam (Steam ID: " + steamID + ").", ServerLog.MessageType.ServerMessage);
HandleClientAuthRequest(unauthClient.Connection, unauthClient.SteamID);
break;
default:
unauthenticatedClients.Remove(unauthClient);
if (GameMain.Config.RequireSteamAuthentication)
{
Log("Disconnected unauthenticated client (Steam ID: " + steamID + "). Steam authentication failed, (" + status + ").", ServerLog.MessageType.ServerMessage);
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)
{
Log("Disconnecting client " + connectedClient.Name + " (Steam ID: " + steamID + "). Steam authentication no longer valid (" + status + ").", ServerLog.MessageType.ServerMessage);
KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid_[status]={status.ToString()}");
}
}
}
private bool IsServerOwner(NetIncomingMessage inc, NetConnection senderConnection)
{
string address = senderConnection.RemoteEndPoint.Address.MapToIPv4().ToString();
int incKey = inc.ReadInt32();
if (ownerKey == 0)
{
return false; //ownership key has been destroyed or has never existed
}
if (address.ToString() != "127.0.0.1")
{
return false; //not localhost
}
if (incKey != ownerKey)
{
return false; //incorrect owner key, how did this even happen
}
return true;
}
private void HandleOwnership(NetIncomingMessage inc, NetConnection senderConnection)
{
DebugConsole.Log("HandleOwnership (" + senderConnection.RemoteEndPoint.Address + ")");
if (IsServerOwner(inc, senderConnection))
{
ownerKey = 0; //destroy owner key so nobody else can take ownership of the server
OwnerConnection = senderConnection;
DebugConsole.NewMessage("Successfully set up server owner", Color.Lime);
}
}
private void HandleClientAuthRequest(NetConnection connection, ulong steamID = 0)
{
DebugConsole.Log("HandleClientAuthRequest (steamID " + steamID + ")");
if (GameMain.Config.RequireSteamAuthentication && connection != OwnerConnection && steamID == 0)
{
DebugConsole.Log("Disconnecting " + connection.RemoteEndPoint + ", Steam authentication required.");
connection.Disconnect(DisconnectReason.SteamAuthenticationRequired.ToString());
return;
}
//client wants to know if server requires password
if (ConnectedClients.Find(c => c.Connection == connection) != null)
{
//this client has already been authenticated
return;
}
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 >= serverSettings.MaxPlayers)
{
//server is full, can't allow new connection
connection.Disconnect(DisconnectReason.ServerFull.ToString());
if (steamID > 0) { Steam.SteamManager.StopAuthSession(steamID); }
return;
}
int nonce = CryptoRandom.Instance.Next();
unauthClient = new UnauthenticatedClient(connection, nonce, steamID);
unauthenticatedClients.Add(unauthClient);
}
unauthClient.AuthTimer = 10.0f;
//if the client is already in the queue, getting another unauth request means that our response was lost; resend
NetOutgoingMessage nonceMsg = server.CreateMessage();
nonceMsg.Write((byte)ServerPacketHeader.AUTH_RESPONSE);
if (serverSettings.HasPassword && connection != OwnerConnection)
{
nonceMsg.Write(true); //true = password
nonceMsg.Write((Int32)unauthClient.Nonce); //here's nonce, encrypt with this
}
else
{
nonceMsg.Write(false); //false = no password
}
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;
}
UnauthenticatedClient unauthClient = unauthenticatedClients.Find(uc => uc.Connection == inc.SenderConnection);
if (unauthClient == null)
{
//client did not ask for nonce first, can't authorize
inc.SenderConnection.Disconnect(DisconnectReason.AuthenticationRequired.ToString());
if (unauthClient.SteamID > 0) { Steam.SteamManager.StopAuthSession(unauthClient.SteamID); }
return;
}
if (serverSettings.HasPassword && inc.SenderConnection != OwnerConnection)
{
//decrypt message and compare password
string clPw = inc.ReadString();
if (!serverSettings.IsPasswordCorrect(clPw, unauthClient.Nonce))
{
unauthClient.FailedAttempts++;
if (unauthClient.FailedAttempts > 3)
{
//disconnect and ban after too many failed attempts
serverSettings.BanList.BanPlayer("Unnamed", unauthClient.Connection.RemoteEndPoint.Address, "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);
return;
}
else
{
//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.");
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;
}
}
}
string clVersion = 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, 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);
return;
}
if (clVersion != GameMain.Version.ToString())
{
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion_[version]={GameMain.Version.ToString()}_[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;
}
//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)
{
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, $"DisconnectMessage.MissingContentPackage_[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;
}
else if (missingPackages.Count > 1)
{
List<string> packageStrs = new List<string>();
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages_[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,
$"DisconnectMessage.IncompatibleContentPackage_[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,
$"DisconnectMessage.IncompatibleContentPackages_[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 (!serverSettings.Whitelist.IsWhiteListed(clName, inc.SenderConnection.RemoteEndPoint.Address))
{
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, 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 (inc.SenderConnection != OwnerConnection && Homoglyphs.Compare(clName.ToLower(),Name.ToLower()))
{
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;
}
Client nameTaken = ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), clName.ToLower()));
if (nameTaken != null)
{
if (nameTaken.Connection.RemoteEndPoint.Address.ToString() == inc.SenderEndPoint.Address.ToString())
{
//both name and IP address match, replace this player's connection
nameTaken.Connection.Disconnect(DisconnectReason.SessionTaken.ToString());
nameTaken.Connection = unauthClient.Connection;
nameTaken.InitClientSync(); //reinitialize sync ids because this is a new connection
unauthenticatedClients.Remove(unauthClient);
unauthClient = null;
return;
}
else
{
//can't authorize this client
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;
}
}
//new client
Client newClient = new Client(clName, GetNewClientID());
newClient.InitClientSync();
newClient.Connection = unauthClient.Connection;
newClient.SteamID = unauthClient.SteamID;
unauthenticatedClients.Remove(unauthClient);
unauthClient = null;
ConnectedClients.Add(newClient);
LastClientListUpdateID++;
if (newClient.Connection == OwnerConnection)
{
newClient.GivePermission(ClientPermissions.All);
newClient.PermittedConsoleCommands.AddRange(DebugConsole.Commands);
GameMain.Server.UpdateClientPermissions(newClient);
GameMain.Server.SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient);
}
GameMain.Server.SendChatMessage($"ServerMessage.JoinedServer_[client]={clName}", ChatMessageType.Server, null);
var savedPermissions = serverSettings.ClientPermissions.Find(cp =>
cp.SteamID > 0 ?
cp.SteamID == newClient.SteamID :
newClient.IPMatches(cp.IP));
if (savedPermissions != null)
{
newClient.SetPermissions(savedPermissions.Permissions, savedPermissions.PermittedCommands);
}
else
{
newClient.SetPermissions(ClientPermissions.None, new List<DebugConsole.Command>());
}
}
private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, DisconnectReason reason, string message)
{
inc.SenderConnection.Disconnect(reason.ToString() + "; " + message);
if (unauthClient.SteamID > 0) { Steam.SteamManager.StopAuthSession(unauthClient.SteamID); }
if (unauthClient != null)
{
unauthenticatedClients.Remove(unauthClient);
}
}
}
}
@@ -0,0 +1,482 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace Barotrauma.Networking
{
partial class ServerSettings
{
public const string SettingsFile = "serversettings.xml";
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml";
partial void InitProjSpecific()
{
LoadSettings();
LoadClientPermissions();
}
private void WriteNetProperties(NetBuffer outMsg)
{
outMsg.Write((UInt16)netProperties.Keys.Count);
foreach (UInt32 key in netProperties.Keys)
{
outMsg.Write(key);
netProperties[key].Write(outMsg);
}
}
public void ServerAdminWrite(NetBuffer outMsg, Client c)
{
//outMsg.Write(isPublic);
//outMsg.Write(EnableUPnP);
//outMsg.WritePadBits();
//outMsg.Write((UInt16)QueryPort);
WriteNetProperties(outMsg);
WriteMonsterEnabled(outMsg);
BanList.ServerAdminWrite(outMsg, c);
Whitelist.ServerAdminWrite(outMsg, c);
}
public void ServerWrite(NetBuffer outMsg,Client c)
{
outMsg.Write(ServerName);
outMsg.Write(ServerMessageText);
outMsg.WriteRangedInteger(1, 60, TickRate);
WriteExtraCargo(outMsg);
Voting.ServerWrite(outMsg);
if (c.HasPermission(Networking.ClientPermissions.ManageSettings))
{
outMsg.Write(true);
outMsg.WritePadBits();
ServerAdminWrite(outMsg, c);
}
else
{
outMsg.Write(false);
outMsg.WritePadBits();
}
}
public void ServerRead(NetIncomingMessage incMsg,Client c)
{
if (!c.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
NetFlags flags = (NetFlags)incMsg.ReadByte();
bool changed = false;
if (flags.HasFlag(NetFlags.Name))
{
string serverName = incMsg.ReadString();
if (ServerName != serverName) changed = true;
ServerName = serverName;
}
if (flags.HasFlag(NetFlags.Message))
{
string serverMessageText = incMsg.ReadString();
if (ServerMessageText != serverMessageText) changed = true;
ServerMessageText = serverMessageText;
}
if (flags.HasFlag(NetFlags.Properties))
{
changed |= ReadExtraCargo(incMsg);
UInt32 count = incMsg.ReadUInt32();
for (int i = 0; i < count; i++)
{
UInt32 key = incMsg.ReadUInt32();
if (netProperties.ContainsKey(key))
{
netProperties[key].Read(incMsg);
GameServer.Log(c.Name + " changed " + netProperties[key].Name + " to " + netProperties[key].Value.ToString(), ServerLog.MessageType.ServerMessage);
changed = true;
}
else
{
UInt32 size = incMsg.ReadVariableUInt32();
incMsg.Position += 8 * size;
}
}
bool changedMonsterSettings = incMsg.ReadBoolean(); incMsg.ReadPadBits();
changed |= changedMonsterSettings;
if (changedMonsterSettings) ReadMonsterEnabled(incMsg);
changed |= BanList.ServerAdminRead(incMsg, c);
changed |= Whitelist.ServerAdminRead(incMsg, c);
}
if (flags.HasFlag(NetFlags.Misc))
{
int missionType = GameMain.NetLobbyScreen.MissionTypeIndex + incMsg.ReadByte() - 1;
while (missionType < 0) missionType += Enum.GetValues(typeof(MissionType)).Length;
while (missionType >= Enum.GetValues(typeof(MissionType)).Length) missionType -= Enum.GetValues(typeof(MissionType)).Length;
GameMain.NetLobbyScreen.MissionTypeIndex = missionType;
int traitorSetting = (int)TraitorsEnabled + incMsg.ReadByte() - 1;
if (traitorSetting < 0) traitorSetting = 2;
if (traitorSetting > 2) traitorSetting = 0;
TraitorsEnabled = (YesNoMaybe)traitorSetting;
int botCount = BotCount + incMsg.ReadByte() - 1;
if (botCount < 0) botCount = MaxBotCount;
if (botCount > MaxBotCount) botCount = 0;
BotCount = botCount;
int botSpawnMode = (int)BotSpawnMode + incMsg.ReadByte() - 1;
if (botSpawnMode < 0) botSpawnMode = 1;
if (botSpawnMode > 1) botSpawnMode = 0;
BotSpawnMode = (BotSpawnMode)botSpawnMode;
float levelDifficulty = incMsg.ReadFloat();
if (levelDifficulty >= 0.0f) SelectedLevelDifficulty = levelDifficulty;
bool changedAutoRestart = incMsg.ReadBoolean();
bool autoRestart = incMsg.ReadBoolean();
if (changedAutoRestart)
{
AutoRestart = autoRestart;
}
changed |= true;
}
if (flags.HasFlag(NetFlags.LevelSeed))
{
GameMain.NetLobbyScreen.LevelSeed = incMsg.ReadString();
changed |= true;
}
if (changed) GameMain.NetLobbyScreen.LastUpdateID++;
}
public void SaveSettings()
{
XDocument doc = new XDocument(new XElement("serversettings"));
SerializableProperty.SerializeProperties(this, doc.Root, true);
doc.Root.SetAttributeValue("name", ServerName);
doc.Root.SetAttributeValue("public", isPublic);
doc.Root.SetAttributeValue("port", GameMain.Server.NetPeerConfiguration.Port);
if (Steam.SteamManager.USE_STEAM) doc.Root.SetAttributeValue("queryport", QueryPort);
doc.Root.SetAttributeValue("maxplayers", maxPlayers);
doc.Root.SetAttributeValue("enableupnp", GameMain.Server.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)));
doc.Root.SetAttributeValue("ServerMessage", ServerMessageText);
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = true
};
using (var writer = XmlWriter.Create(SettingsFile, settings))
{
doc.Save(writer);
}
}
private void LoadSettings()
{
XDocument doc = null;
if (File.Exists(SettingsFile))
{
doc = XMLExtensions.TryLoadXml(SettingsFile);
}
if (doc == null || doc.Root == null)
{
doc = new XDocument(new XElement("serversettings"));
}
SerializableProperties = SerializableProperty.DeserializeProperties(this, doc.Root);
AutoRestart = doc.Root.GetAttributeBool("autorestart", false);
Voting.AllowSubVoting = SubSelectionMode == SelectionMode.Vote;
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[] { "65-90", "97-122", "48-59" });
foreach (string allowedClientNameCharRange in allowedClientNameCharsStr)
{
string[] splitRange = allowedClientNameCharRange.Split('-');
if (splitRange.Length == 0 || splitRange.Length > 2)
{
DebugConsole.ThrowError("Error in server settings - " + allowedClientNameCharRange + " is not a valid range for characters allowed in client names.");
continue;
}
int min = -1;
if (!int.TryParse(splitRange[0], out min))
{
DebugConsole.ThrowError("Error in server settings - " + allowedClientNameCharRange + " is not a valid range for characters allowed in client names.");
continue;
}
int max = min;
if (splitRange.Length == 2)
{
if (!int.TryParse(splitRange[1], out max))
{
DebugConsole.ThrowError("Error in server settings - " + allowedClientNameCharRange + " is not a valid range for characters allowed in client names.");
continue;
}
}
if (min > -1 && max > -1) AllowedClientNameChars.Add(new Pair<int, int>(min, max));
}
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);
}
}
ServerName = doc.Root.GetAttributeString("name", "");
ServerMessageText = doc.Root.GetAttributeString("ServerMessage", "");
GameMain.NetLobbyScreen.SelectedModeIdentifier = GameModeIdentifier;
GameMain.NetLobbyScreen.MissionTypeName = MissionType;
GameMain.NetLobbyScreen.SetBotSpawnMode(BotSpawnMode);
GameMain.NetLobbyScreen.SetBotCount(BotCount);
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]));
}
MonsterEnabled = new Dictionary<string, bool>();
foreach (string s in monsterNames)
{
if (!MonsterEnabled.ContainsKey(s)) MonsterEnabled.Add(s, true);
}
AutoBanTime = doc.Root.GetAttributeFloat("autobantime", 60);
MaxAutoBanTime = doc.Root.GetAttributeFloat("maxautobantime", 360);
}
public void LoadClientPermissions()
{
ClientPermissions.Clear();
if (!File.Exists(ClientPermissionsFile))
{
if (File.Exists("Data/clientpermissions.txt"))
{
LoadClientPermissionsOld("Data/clientpermissions.txt");
}
return;
}
XDocument doc = XMLExtensions.TryLoadXml(ClientPermissionsFile);
foreach (XElement clientElement in doc.Root.Elements())
{
string clientName = clientElement.GetAttributeString("name", "");
string clientIP = clientElement.GetAttributeString("ip", "");
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 = Networking.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;
}
List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
if (permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands))
{
foreach (XElement commandElement in clientElement.Elements())
{
if (commandElement.Name.ToString().ToLowerInvariant() != "command") continue;
string commandName = commandElement.GetAttributeString("name", "");
DebugConsole.Command command = DebugConsole.FindCommand(commandName);
if (command == null)
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + commandName + "\" is not a valid console command.");
continue;
}
permittedCommands.Add(command);
}
}
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));
}
}
}
/// <summary>
/// Method for loading old .txt client permission files to provide backwards compatibility
/// </summary>
private void LoadClientPermissionsOld(string file)
{
if (!File.Exists(file)) return;
string[] lines;
try
{
lines = File.ReadAllLines(file);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to open client permission file " + ClientPermissionsFile, e);
return;
}
ClientPermissions.Clear();
foreach (string line in lines)
{
string[] separatedLine = line.Split('|');
if (separatedLine.Length < 3) continue;
string name = string.Join("|", separatedLine.Take(separatedLine.Length - 2));
string ip = separatedLine[separatedLine.Length - 2];
ClientPermissions permissions = Networking.ClientPermissions.None;
if (Enum.TryParse(separatedLine.Last(), out permissions))
{
ClientPermissions.Add(new SavedClientPermission(name, ip, permissions, new List<DebugConsole.Command>()));
}
}
}
public void SaveClientPermissions()
{
//delete old client permission file
if (File.Exists("Data/clientpermissions.txt"))
{
File.Delete("Data/clientpermissions.txt");
}
GameServer.Log("Saving client permissions", ServerLog.MessageType.ServerMessage);
XDocument doc = new XDocument(new XElement("ClientPermissions"));
foreach (SavedClientPermission clientPermission in ClientPermissions)
{
XElement clientElement = new XElement("Client",
new XAttribute("name", clientPermission.Name),
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(Barotrauma.Networking.ClientPermissions.ConsoleCommands))
{
foreach (DebugConsole.Command command in clientPermission.PermittedCommands)
{
clientElement.Add(new XElement("command", new XAttribute("name", command.names[0])));
}
}
doc.Root.Add(clientElement);
}
try
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineOnAttributes = true;
using (var writer = XmlWriter.Create(ClientPermissionsFile, settings))
{
doc.Save(writer);
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving client permissions to " + ClientPermissionsFile + " failed", e);
}
}
}
}