210 lines
8.6 KiB
C#
210 lines
8.6 KiB
C#
using System.Collections.Immutable;
|
|
using System.Threading.Tasks;
|
|
using Content.Server.Database;
|
|
using Content.Server.GameTicking;
|
|
using Content.Server.Preferences.Managers;
|
|
using Content.Shared.CCVar;
|
|
using Content.Shared.GameTicking;
|
|
using Content.Shared.Players.PlayTimeTracking;
|
|
using Robust.Server.Player;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Network;
|
|
|
|
|
|
namespace Content.Server.Connection
|
|
{
|
|
public interface IConnectionManager
|
|
{
|
|
void Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles various duties like guest username assignment, bans, connection logs, etc...
|
|
/// </summary>
|
|
public sealed class ConnectionManager : IConnectionManager
|
|
{
|
|
[Dependency] private readonly IServerDbManager _dbManager = default!;
|
|
[Dependency] private readonly IPlayerManager _plyMgr = default!;
|
|
[Dependency] private readonly IServerNetManager _netMgr = default!;
|
|
[Dependency] private readonly IServerDbManager _db = default!;
|
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
|
[Dependency] private readonly ILocalizationManager _loc = default!;
|
|
[Dependency] private readonly ServerDbEntryManager _serverDbEntry = default!;
|
|
|
|
public void Initialize()
|
|
{
|
|
_netMgr.Connecting += NetMgrOnConnecting;
|
|
_netMgr.AssignUserIdCallback = AssignUserIdCallback;
|
|
// Approval-based IP bans disabled because they don't play well with Happy Eyeballs.
|
|
// _netMgr.HandleApprovalCallback = HandleApproval;
|
|
}
|
|
|
|
/*
|
|
private async Task<NetApproval> HandleApproval(NetApprovalEventArgs eventArgs)
|
|
{
|
|
var ban = await _db.GetServerBanByIpAsync(eventArgs.Connection.RemoteEndPoint.Address);
|
|
if (ban != null)
|
|
{
|
|
var expires = Loc.GetString("ban-banned-permanent");
|
|
if (ban.ExpirationTime is { } expireTime)
|
|
{
|
|
var duration = expireTime - ban.BanTime;
|
|
var utc = expireTime.ToUniversalTime();
|
|
expires = Loc.GetString("ban-expires", ("duration", duration.TotalMinutes.ToString("N0")), ("time", utc.ToString("f")));
|
|
}
|
|
var reason = Loc.GetString("ban-banned-1") + "\n" + Loc.GetString("ban-banned-2", ("reason", this.Reason)) + "\n" + expires;;
|
|
return NetApproval.Deny(reason);
|
|
}
|
|
|
|
return NetApproval.Allow();
|
|
}
|
|
*/
|
|
|
|
private async Task NetMgrOnConnecting(NetConnectingArgs e)
|
|
{
|
|
var deny = await ShouldDeny(e);
|
|
|
|
var addr = e.IP.Address;
|
|
var userId = e.UserId;
|
|
|
|
var serverId = (await _serverDbEntry.ServerEntity).Id;
|
|
|
|
if (deny != null)
|
|
{
|
|
var (reason, msg, banHits) = deny.Value;
|
|
|
|
var id = await _db.AddConnectionLogAsync(userId, e.UserName, addr, e.UserData.HWId, reason, serverId);
|
|
if (banHits is { Count: > 0 })
|
|
await _db.AddServerBanHitsAsync(id, banHits);
|
|
|
|
e.Deny(msg);
|
|
}
|
|
else
|
|
{
|
|
await _db.AddConnectionLogAsync(userId, e.UserName, addr, e.UserData.HWId, null, serverId);
|
|
|
|
if (!ServerPreferencesManager.ShouldStorePrefs(e.AuthType))
|
|
return;
|
|
|
|
await _db.UpdatePlayerRecordAsync(userId, e.UserName, addr, e.UserData.HWId);
|
|
}
|
|
}
|
|
|
|
private async Task<(ConnectionDenyReason, string, List<ServerBanDef>? bansHit)?> ShouldDeny(
|
|
NetConnectingArgs e)
|
|
{
|
|
// Check if banned.
|
|
var addr = e.IP.Address;
|
|
var userId = e.UserId;
|
|
ImmutableArray<byte>? hwId = e.UserData.HWId;
|
|
if (hwId.Value.Length == 0 || !_cfg.GetCVar(CCVars.BanHardwareIds))
|
|
{
|
|
// HWId not available for user's platform, don't look it up.
|
|
// Or hardware ID checks disabled.
|
|
hwId = null;
|
|
}
|
|
|
|
var adminData = await _dbManager.GetAdminDataForAsync(e.UserId);
|
|
|
|
if (_cfg.GetCVar(CCVars.PanicBunkerEnabled))
|
|
{
|
|
var showReason = _cfg.GetCVar(CCVars.PanicBunkerShowReason);
|
|
var customReason = _cfg.GetCVar(CCVars.PanicBunkerCustomReason);
|
|
|
|
var minMinutesAge = _cfg.GetCVar(CCVars.PanicBunkerMinAccountAge);
|
|
var record = await _dbManager.GetPlayerRecordByUserId(userId);
|
|
var validAccountAge = record != null &&
|
|
record.FirstSeenTime.CompareTo(DateTimeOffset.Now - TimeSpan.FromMinutes(minMinutesAge)) <= 0;
|
|
|
|
// Use the custom reason if it exists & they don't have the minimum account age
|
|
if (customReason != string.Empty && !validAccountAge)
|
|
{
|
|
return (ConnectionDenyReason.Panic, customReason, null);
|
|
}
|
|
|
|
if (showReason && !validAccountAge)
|
|
{
|
|
return (ConnectionDenyReason.Panic,
|
|
Loc.GetString("panic-bunker-account-denied-reason",
|
|
("reason", Loc.GetString("panic-bunker-account-reason-account", ("minutes", minMinutesAge)))), null);
|
|
}
|
|
|
|
var minOverallHours = _cfg.GetCVar(CCVars.PanicBunkerMinOverallHours);
|
|
var overallTime = ( await _db.GetPlayTimes(e.UserId)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall);
|
|
var haveMinOverallTime = overallTime != null && overallTime.TimeSpent.TotalHours > minOverallHours;
|
|
|
|
// Use the custom reason if it exists & they don't have the minimum time
|
|
if (customReason != string.Empty && !haveMinOverallTime)
|
|
{
|
|
return (ConnectionDenyReason.Panic, customReason, null);
|
|
}
|
|
|
|
if (showReason && !haveMinOverallTime)
|
|
{
|
|
return (ConnectionDenyReason.Panic,
|
|
Loc.GetString("panic-bunker-account-denied-reason",
|
|
("reason", Loc.GetString("panic-bunker-account-reason-overall", ("hours", minOverallHours)))), null);
|
|
}
|
|
|
|
if (!validAccountAge || !haveMinOverallTime)
|
|
{
|
|
return (ConnectionDenyReason.Panic, Loc.GetString("panic-bunker-account-denied"), null);
|
|
}
|
|
}
|
|
|
|
var wasInGame = EntitySystem.TryGet<GameTicker>(out var ticker) &&
|
|
ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
|
|
status == PlayerGameStatus.JoinedGame;
|
|
if ((_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && adminData is null) && !wasInGame)
|
|
{
|
|
return (ConnectionDenyReason.Full, Loc.GetString("soft-player-cap-full"), null);
|
|
}
|
|
|
|
var bans = await _db.GetServerBansAsync(addr, userId, hwId, includeUnbanned: false);
|
|
if (bans.Count > 0)
|
|
{
|
|
var firstBan = bans[0];
|
|
var message = firstBan.FormatBanMessage(_cfg, _loc);
|
|
return (ConnectionDenyReason.Ban, message, bans);
|
|
}
|
|
|
|
if (_cfg.GetCVar(CCVars.WhitelistEnabled))
|
|
{
|
|
var min = _cfg.GetCVar(CCVars.WhitelistMinPlayers);
|
|
var max = _cfg.GetCVar(CCVars.WhitelistMaxPlayers);
|
|
var playerCountValid = _plyMgr.PlayerCount >= min && _plyMgr.PlayerCount < max;
|
|
|
|
if (playerCountValid && await _db.GetWhitelistStatusAsync(userId) == false
|
|
&& adminData is null)
|
|
{
|
|
var msg = Loc.GetString(_cfg.GetCVar(CCVars.WhitelistReason));
|
|
// was the whitelist playercount changed?
|
|
if (min > 0 || max < int.MaxValue)
|
|
msg += "\n" + Loc.GetString("whitelist-playercount-invalid", ("min", min), ("max", max));
|
|
return (ConnectionDenyReason.Whitelist, msg, null);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private async Task<NetUserId?> AssignUserIdCallback(string name)
|
|
{
|
|
if (!_cfg.GetCVar(CCVars.GamePersistGuests))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var userId = await _db.GetAssignedUserIdAsync(name);
|
|
if (userId != null)
|
|
{
|
|
return userId;
|
|
}
|
|
|
|
var assigned = new NetUserId(Guid.NewGuid());
|
|
await _db.AssignUserIdAsync(name, assigned);
|
|
return assigned;
|
|
}
|
|
}
|
|
}
|