v0.10.5.1
This commit is contained in:
@@ -30,6 +30,14 @@ namespace Barotrauma
|
||||
|
||||
healthUpdateTimer = 0.0f;
|
||||
|
||||
if (CauseOfDeath.Killer != null && CauseOfDeath.Killer.IsTraitor && CauseOfDeath.Killer != this)
|
||||
{
|
||||
var owner = GameMain.Server.ConnectedClients.Find(c => c.Character == this);
|
||||
if (owner != null)
|
||||
{
|
||||
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("KilledByTraitorNotification"), owner, ChatMessageType.ServerMessageBoxInGame);
|
||||
}
|
||||
}
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (client.InGame)
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Barotrauma
|
||||
|
||||
private double LastInputTime;
|
||||
|
||||
public bool HealthUpdatePending;
|
||||
|
||||
public float GetPositionUpdateInterval(Client recipient)
|
||||
{
|
||||
if (!Enabled) { return 1000.0f; }
|
||||
@@ -418,6 +420,7 @@ namespace Barotrauma
|
||||
if (writeStatus)
|
||||
{
|
||||
WriteStatus(tempBuffer);
|
||||
HealthUpdatePending = false;
|
||||
}
|
||||
|
||||
tempBuffer.WritePadBits();
|
||||
|
||||
@@ -1443,7 +1443,7 @@ namespace Barotrauma
|
||||
if (GameMain.Server == null) return null;
|
||||
return new string[][]
|
||||
{
|
||||
GameMain.Server.ServerSettings.BanList.BannedIPs.Where(ip => !string.IsNullOrEmpty(ip)).ToArray()
|
||||
GameMain.Server.ServerSettings.BanList.BannedEndPoints.ToArray()
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -1563,15 +1563,45 @@ namespace Barotrauma
|
||||
}
|
||||
);
|
||||
|
||||
AssignOnClientRequestExecute("togglecampaignteleport",
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
{
|
||||
if (!(GameMain.GameSession?.Campaign is MultiPlayerCampaign mpCampaign))
|
||||
{
|
||||
GameMain.Server.SendConsoleMessage("No campaign active.", client);
|
||||
return;
|
||||
}
|
||||
mpCampaign.LastUpdateID++;
|
||||
GameMain.GameSession.Map.AllowDebugTeleport = !GameMain.GameSession.Map.AllowDebugTeleport;
|
||||
NewMessage(client.Name + (GameMain.GameSession.Map.AllowDebugTeleport ? " enabled" : " disabled") + " teleportation on the campaign map.", Color.White);
|
||||
GameMain.Server.SendConsoleMessage((GameMain.GameSession.Map.AllowDebugTeleport ? "Enabled" : "Disabled") + " teleportation on the campaign map.", client);
|
||||
}
|
||||
);
|
||||
|
||||
AssignOnClientRequestExecute(
|
||||
"godmode",
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
{
|
||||
Character targetCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args, false);
|
||||
|
||||
if (targetCharacter == null) { return; }
|
||||
|
||||
targetCharacter.GodMode = !targetCharacter.GodMode;
|
||||
|
||||
NewMessage(targetCharacter.Name + (targetCharacter.GodMode ? "'s godmode turned on by \"" : "'s godmode turned off by \"") + client.Name + "\"", Color.White);
|
||||
GameMain.Server.SendConsoleMessage(targetCharacter.Name + (targetCharacter.GodMode ? "'s godmode on" : "'s godmode off"), client);
|
||||
}
|
||||
);
|
||||
|
||||
AssignOnClientRequestExecute(
|
||||
"godmode_mainsub",
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
{
|
||||
if (Submarine.MainSub == null) return;
|
||||
|
||||
Submarine.MainSub.GodMode = !Submarine.MainSub.GodMode;
|
||||
NewMessage((Submarine.MainSub.GodMode ? "Godmode turned on by \"" : "Godmode off by \"") + client.Name + "\"", Color.White);
|
||||
GameMain.Server.SendConsoleMessage(Submarine.MainSub.GodMode ? "Godmode on" : "Godmode off", client);
|
||||
NewMessage((Submarine.MainSub.GodMode ? "Mainsub godmode turned on by \"" : "Mainsub godmode turned off by \"") + client.Name + "\"", Color.White);
|
||||
GameMain.Server.SendConsoleMessage(Submarine.MainSub.GodMode ? "Mainsub godmode on" : "Mainsub godmode off", client);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1581,7 +1611,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (args.Length < 2) return;
|
||||
|
||||
AfflictionPrefab afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(a => a.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
AfflictionPrefab afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(a =>
|
||||
a.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase) ||
|
||||
a.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
GameMain.Server.SendConsoleMessage("Affliction \"" + args[0] + "\" not found.", client);
|
||||
|
||||
@@ -43,16 +43,13 @@ namespace Barotrauma
|
||||
//but they're checked for all over the place
|
||||
//TODO: maybe clean up instead of having these constants
|
||||
public static readonly Screen SubEditorScreen = UnimplementedScreen.Instance;
|
||||
|
||||
|
||||
public static DecalManager DecalManager;
|
||||
|
||||
public static bool ShouldRun = true;
|
||||
|
||||
private static Stopwatch stopwatch;
|
||||
|
||||
public static IEnumerable<ContentPackage> SelectedPackages
|
||||
{
|
||||
get { return Config?.SelectedContentPackages; }
|
||||
}
|
||||
|
||||
private static ContentPackage vanillaContent;
|
||||
public static ContentPackage VanillaContent
|
||||
{
|
||||
@@ -61,7 +58,7 @@ namespace Barotrauma
|
||||
if (vanillaContent == null)
|
||||
{
|
||||
// TODO: Dynamic method for defining and finding the vanilla content package.
|
||||
vanillaContent = ContentPackage.List.SingleOrDefault(cp => Path.GetFileName(cp.Path).Equals("vanilla 0.9.xml", StringComparison.OrdinalIgnoreCase));
|
||||
vanillaContent = ContentPackage.CorePackages.SingleOrDefault(cp => Path.GetFileName(cp.Path).Equals("vanilla 0.9.xml", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
return vanillaContent;
|
||||
}
|
||||
@@ -110,10 +107,10 @@ namespace Barotrauma
|
||||
EventSet.LoadPrefabs();
|
||||
Order.Init();
|
||||
EventManagerSettings.Init();
|
||||
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
|
||||
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
|
||||
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
|
||||
StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure));
|
||||
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
|
||||
UpgradePrefab.LoadAll(GetFilesOfType(ContentType.UpgradeModules));
|
||||
JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs));
|
||||
CorpsePrefab.LoadAll(GetFilesOfType(ContentType.Corpses));
|
||||
@@ -122,6 +119,7 @@ namespace Barotrauma
|
||||
LevelObjectPrefab.LoadAll();
|
||||
|
||||
GameModePreset.Init();
|
||||
DecalManager = new DecalManager();
|
||||
LocationType.Init();
|
||||
|
||||
SubmarineInfo.RefreshSavedSubs();
|
||||
@@ -136,8 +134,8 @@ namespace Barotrauma
|
||||
|
||||
private void CheckContentPackage()
|
||||
{
|
||||
|
||||
foreach (ContentPackage contentPackage in Config.SelectedContentPackages)
|
||||
//TODO: reimplement using only core package?
|
||||
/*foreach (ContentPackage contentPackage in Config.AllEnabledPackages)
|
||||
{
|
||||
var exePaths = contentPackage.GetFilesOfType(ContentType.ServerExecutable);
|
||||
if (exePaths.Count() > 0 && AppDomain.CurrentDomain.FriendlyName != exePaths.First())
|
||||
@@ -155,7 +153,7 @@ namespace Barotrauma
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -167,11 +165,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (searchAllContentPackages)
|
||||
{
|
||||
return ContentPackage.GetFilesOfType(ContentPackage.List, type);
|
||||
return ContentPackage.GetFilesOfType(ContentPackage.AllPackages, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ContentPackage.GetFilesOfType(SelectedPackages, type);
|
||||
return ContentPackage.GetFilesOfType(Config.AllEnabledPackages, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
foreach (PurchasedItem item in itemsToSell)
|
||||
{
|
||||
var itemValue = GetBuyValueAtCurrentLocation(item);
|
||||
campaign.Map.CurrentLocation.StoreCurrentBalance -= itemValue;
|
||||
Location.StoreCurrentBalance -= itemValue;
|
||||
campaign.Money += itemValue;
|
||||
PurchasedItems.Remove(item);
|
||||
}
|
||||
@@ -20,8 +20,8 @@ namespace Barotrauma
|
||||
foreach (SoldItem item in itemsToBuy)
|
||||
{
|
||||
var itemValue = GetSellValueAtCurrentLocation(item.ItemPrefab);
|
||||
if (location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
|
||||
location.StoreCurrentBalance += itemValue;
|
||||
if (Location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
|
||||
Location.StoreCurrentBalance += itemValue;
|
||||
campaign.Money -= itemValue;
|
||||
SoldItems.Remove(item);
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace Barotrauma
|
||||
var itemValue = GetSellValueAtCurrentLocation(item.ItemPrefab);
|
||||
|
||||
// check if the store can afford the item and if the item hasn't been removed already
|
||||
if (location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
|
||||
if (Location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
|
||||
|
||||
if (!item.Removed && canAddToRemoveQueue && Entity.FindEntityByID(item.ID) is Item entity)
|
||||
{
|
||||
@@ -43,7 +43,7 @@ namespace Barotrauma
|
||||
Entity.Spawner.AddToRemoveQueue(entity);
|
||||
}
|
||||
SoldItems.Add(item);
|
||||
location.StoreCurrentBalance -= itemValue;
|
||||
Location.StoreCurrentBalance -= itemValue;
|
||||
campaign.Money += itemValue;
|
||||
}
|
||||
OnSoldItemsChanged?.Invoke();
|
||||
|
||||
@@ -32,7 +32,10 @@ namespace Barotrauma
|
||||
XElement saveElement = new XElement("bots", new XAttribute("hasbots", HasBots));
|
||||
foreach (CharacterInfo info in characterInfos)
|
||||
{
|
||||
if (info?.Character == null || info.Character.IsDead) { continue; }
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
if (!info.IsNewHire && (info.Character == null || info.Character.IsDead)) { continue; }
|
||||
}
|
||||
|
||||
XElement characterElement = info.Save(saveElement);
|
||||
if (info.InventoryData != null) { characterElement.Add(info.InventoryData); }
|
||||
|
||||
+33
-28
@@ -161,7 +161,6 @@ namespace Barotrauma
|
||||
break;
|
||||
case TransitionType.ProgressToNextLocation:
|
||||
Map.MoveToNextLocation();
|
||||
Map.ProgressWorld();
|
||||
break;
|
||||
case TransitionType.End:
|
||||
EndCampaign();
|
||||
@@ -169,6 +168,8 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
|
||||
Map.ProgressWorld(transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
|
||||
|
||||
bool success = GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
|
||||
|
||||
GameMain.GameSession.EndRound("", traitorResults, transitionType);
|
||||
@@ -196,7 +197,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (data.HasSpawned && !characterData.Any(cd => cd.IsDuplicate(data)))
|
||||
{
|
||||
var character = Character.CharacterList.Find(c => c.Info == data.CharacterInfo);
|
||||
var character = Character.CharacterList.Find(c => c.Info == data.CharacterInfo && !c.IsHusk);
|
||||
if (character != null && (!character.IsDead || character.CauseOfDeath?.Type == CauseOfDeathType.Disconnected))
|
||||
{
|
||||
data.Refresh(character);
|
||||
@@ -243,10 +244,28 @@ namespace Barotrauma
|
||||
}
|
||||
NextLevel = newLevel;
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
|
||||
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
|
||||
PendingSubmarineSwitch = null;
|
||||
|
||||
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
|
||||
{
|
||||
if (GameMain.GameSession.OwnedSubmarines[i].Name == previousSub.Name)
|
||||
{
|
||||
GameMain.GameSession.OwnedSubmarines[i] = previousSub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingSubmarineSwitch = null;
|
||||
GameMain.Server.EndGame(TransitionType.None);
|
||||
LoadCampaign(GameMain.GameSession.SavePath);
|
||||
LastSaveID++;
|
||||
@@ -262,23 +281,6 @@ namespace Barotrauma
|
||||
|
||||
NextLevel = newLevel;
|
||||
MirrorLevel = mirror;
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
|
||||
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
|
||||
PendingSubmarineSwitch = null;
|
||||
|
||||
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
|
||||
{
|
||||
if (GameMain.GameSession.OwnedSubmarines[i].Name == previousSub.Name)
|
||||
{
|
||||
GameMain.GameSession.OwnedSubmarines[i] = previousSub;
|
||||
}
|
||||
}
|
||||
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
LastSaveID++;
|
||||
}
|
||||
|
||||
//give clients time to play the end cinematic before starting the next round
|
||||
if (transitionType == TransitionType.End)
|
||||
@@ -305,6 +307,7 @@ namespace Barotrauma
|
||||
UpgradeManager.OnUpgradesChanged += () => { LastUpdateID++; };
|
||||
Map.OnLocationSelected += (loc, connection) => { LastUpdateID++; };
|
||||
Map.OnMissionSelected += (loc, mission) => { LastUpdateID++; };
|
||||
Reputation.OnAnyReputationValueChanged += () => { LastUpdateID++; };
|
||||
}
|
||||
//increment save ID so clients know they're lacking the most up-to-date save file
|
||||
LastSaveID++;
|
||||
@@ -363,13 +366,16 @@ namespace Barotrauma
|
||||
{
|
||||
LoadNewLevel();
|
||||
}
|
||||
else if (transitionType == TransitionType.ProgressToNextLocation && Level.Loaded.EndOutpost != null && Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
|
||||
else if (GameMain.Server.ConnectedClients.Count == 0 || GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead))
|
||||
{
|
||||
LoadNewLevel();
|
||||
}
|
||||
else if (transitionType == TransitionType.ReturnToPreviousLocation && Level.Loaded.StartOutpost != null && Level.Loaded.StartOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
LoadNewLevel();
|
||||
if (transitionType == TransitionType.ProgressToNextLocation && Level.Loaded.EndOutpost != null && Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
LoadNewLevel();
|
||||
}
|
||||
else if (transitionType == TransitionType.ReturnToPreviousLocation && Level.Loaded.StartOutpost != null && Level.Loaded.StartOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
LoadNewLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Level.Loaded.Type == LevelData.LevelType.Outpost)
|
||||
@@ -399,6 +405,7 @@ namespace Barotrauma
|
||||
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
|
||||
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
|
||||
msg.Write(map.SelectedMissionIndex == -1 ? byte.MaxValue : (byte)map.SelectedMissionIndex);
|
||||
msg.Write(map.AllowDebugTeleport);
|
||||
msg.Write(reputation != null);
|
||||
if (reputation != null) { msg.Write(reputation.Value); }
|
||||
|
||||
@@ -592,12 +599,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (currentLocIndex < Map.Locations.Count)
|
||||
if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
|
||||
{
|
||||
Map.SetLocation(currentLocIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||
if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); }
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable
|
||||
{
|
||||
|
||||
private UInt16 originalDockingTargetID;
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(docked);
|
||||
|
||||
if (docked)
|
||||
{
|
||||
msg.Write(originalDockingTargetID);
|
||||
msg.Write(hulls != null && hulls[0] != null && hulls[1] != null && gap != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ namespace Barotrauma.Items.Components
|
||||
msg.Write(isOpen);
|
||||
msg.Write(isBroken);
|
||||
msg.Write(extraData.Length == 3 ? (bool)extraData[2] : false); //forced open
|
||||
msg.Write(isStuck);
|
||||
msg.WriteRangedSingle(stuck, 0.0f, 100.0f, 8);
|
||||
msg.Write(lastUser == null ? (UInt16)0 : lastUser.ID);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
internal partial class Growable
|
||||
{
|
||||
partial void LoadVines(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "flowersprite":
|
||||
flowerVariants++;
|
||||
break;
|
||||
case "leafsprite":
|
||||
leafVariants++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.WriteRangedSingle(Health, 0f, (float) MaxHealth, 8);
|
||||
if (extraData != null && extraData.Length >= 3 && extraData[2] is int offset)
|
||||
{
|
||||
int amountToSend = Math.Min(Vines.Count - offset, VineChunkSize);
|
||||
msg.WriteRangedInteger(offset, -1, MaximumVines);
|
||||
msg.WriteRangedInteger(amountToSend, 0, VineChunkSize);
|
||||
for (int i = offset; i < offset + amountToSend; i++)
|
||||
{
|
||||
VineTile vine = Vines[i];
|
||||
var (x, y) = vine.Position;
|
||||
msg.WriteRangedInteger((byte) vine.Type, 0b0000, 0b1111);
|
||||
msg.WriteRangedInteger(vine.FlowerConfig.Serialize(), 0, 0xFFF);
|
||||
msg.WriteRangedInteger(vine.LeafConfig.Serialize(), 0, 0xFFF);
|
||||
msg.Write((byte) (x / VineTile.Size));
|
||||
msg.Write((byte) (y / VineTile.Size));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.WriteRangedInteger(-1, -1, MaximumVines);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ namespace Barotrauma.Items.Components
|
||||
AttachToWall();
|
||||
|
||||
item.CreateServerEvent(this);
|
||||
c.Character.Inventory?.CreateNetworkEvent();
|
||||
|
||||
GameServer.Log(GameServer.CharacterLogName(c.Character) + " attached " + item.Name + " to a wall", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
return true; //element processed
|
||||
}
|
||||
|
||||
public virtual void ServerAppendExtraData(ref object[] extraData) { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ItemLabel : ItemComponent, IDrawableComponent
|
||||
{
|
||||
private CoroutineHandle sendStateCoroutine;
|
||||
private string lastSentText;
|
||||
private float sendStateTimer;
|
||||
|
||||
[Serialize("", true, description: "The text to display on the label.", alwaysUseInstanceValues: true), Editable(100)]
|
||||
public string Text
|
||||
{
|
||||
@@ -35,5 +41,38 @@ namespace Barotrauma.Items.Components
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
partial void OnStateChanged()
|
||||
{
|
||||
sendStateTimer = 0.1f;
|
||||
if (sendStateCoroutine == null)
|
||||
{
|
||||
sendStateCoroutine = CoroutineManager.StartCoroutine(SendStateAfterDelay());
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<object> SendStateAfterDelay()
|
||||
{
|
||||
while (sendStateTimer > 0.0f)
|
||||
{
|
||||
sendStateTimer -= CoroutineManager.DeltaTime;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
if (item.Removed || GameMain.NetworkMember == null)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
sendStateCoroutine = null;
|
||||
if (lastSentText != Text) { item.CreateServerEvent(this); }
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(Text);
|
||||
lastSentText = Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//force can only be adjusted at 10% intervals -> no need for more accuracy than this
|
||||
msg.WriteRangedInteger((int)(targetForce / 10.0f), -10, 10);
|
||||
msg.Write(User == null ? Entity.NullEntityID : User.ID);
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
|
||||
@@ -23,6 +24,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
targetForce = newTargetForce;
|
||||
User = c.Character;
|
||||
}
|
||||
|
||||
//notify all clients of the changed state
|
||||
|
||||
@@ -31,9 +31,22 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private ulong serverEventId = 0;
|
||||
public override void ServerAppendExtraData(ref object[] extraData)
|
||||
{
|
||||
//ensuring the uniqueness of this event is
|
||||
//required for the fabricator to sync correctly;
|
||||
//otherwise, the event manager would incorrectly
|
||||
//assume that the client actually has the latest state
|
||||
Array.Resize(ref extraData, 4);
|
||||
extraData[2] = serverEventId;
|
||||
extraData[3] = State;
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write((byte)State);
|
||||
FabricatorState stateAtEvent = (FabricatorState)extraData[3];
|
||||
msg.Write((byte)stateAtEvent);
|
||||
msg.Write(timeUntilReady);
|
||||
int itemIndex = fabricatedItem == null ? -1 : fabricationRecipes.IndexOf(fabricatedItem);
|
||||
msg.WriteRangedInteger(itemIndex, -1, fabricationRecipes.Count - 1);
|
||||
|
||||
@@ -23,6 +23,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
GameServer.Log(GameServer.CharacterLogName(c.Character) + (newIsActive ? " turned on " : " turned off ") + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
if (pumpSpeedLockTimer <= 0.0f)
|
||||
{
|
||||
targetLevel = null;
|
||||
}
|
||||
|
||||
FlowPercentage = newFlowPercentage;
|
||||
IsActive = newIsActive;
|
||||
|
||||
@@ -22,13 +22,12 @@ namespace Barotrauma.Items.Components
|
||||
bool autoPilot = msg.ReadBoolean();
|
||||
bool dockingButtonClicked = msg.ReadBoolean();
|
||||
Vector2 newSteeringInput = targetVelocity;
|
||||
bool maintainPos = false;
|
||||
Vector2? newPosToMaintain = null;
|
||||
bool headingToStart = false;
|
||||
|
||||
if (autoPilot)
|
||||
{
|
||||
maintainPos = msg.ReadBoolean();
|
||||
bool maintainPos = msg.ReadBoolean();
|
||||
if (maintainPos)
|
||||
{
|
||||
newPosToMaintain = new Vector2(
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class MemoryComponent : ItemComponent
|
||||
{
|
||||
private CoroutineHandle sendStateCoroutine;
|
||||
private string lastSentValue;
|
||||
private float sendStateTimer;
|
||||
|
||||
partial void OnStateChanged()
|
||||
{
|
||||
sendStateTimer = 0.5f;
|
||||
if (sendStateCoroutine == null)
|
||||
{
|
||||
sendStateCoroutine = CoroutineManager.StartCoroutine(SendStateAfterDelay());
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<object> SendStateAfterDelay()
|
||||
{
|
||||
while (sendStateTimer > 0.0f)
|
||||
{
|
||||
sendStateTimer -= CoroutineManager.DeltaTime;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
if (item.Removed || GameMain.NetworkMember == null)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
sendStateCoroutine = null;
|
||||
if (lastSentValue != Value) { item.CreateServerEvent(this); }
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(Value);
|
||||
lastSentValue = Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,6 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
case NetEntityEvent.Type.Upgrade:
|
||||
{
|
||||
if (extraData.Length > 0 && extraData[1] is Upgrade upgrade)
|
||||
{
|
||||
var upgradeTargets = upgrade.TargetComponents;
|
||||
@@ -163,7 +162,6 @@ namespace Barotrauma
|
||||
: $"Failed to write a network event for the item \"{Name}\". No upgrade specified.";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
errorMsg = "Failed to write a network event for the item \"" + Name + "\" - \"" + eventType + "\" is not a valid entity event type for items.";
|
||||
break;
|
||||
@@ -382,7 +380,10 @@ namespace Barotrauma
|
||||
int index = components.IndexOf(ic);
|
||||
if (index == -1) return;
|
||||
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.ComponentState, index });
|
||||
object[] extraData = new object[] { NetEntityEvent.Type.ComponentState, index };
|
||||
ic.ServerAppendExtraData(ref extraData);
|
||||
|
||||
GameMain.Server.CreateEntityEvent(this, extraData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -11,6 +12,8 @@ namespace Barotrauma
|
||||
private float lastSentVolume, lastSentOxygen, lastSentFireCount;
|
||||
private float sendUpdateTimer;
|
||||
|
||||
private bool decalsChanged;
|
||||
|
||||
public override bool IsMouseOn(Vector2 position)
|
||||
{
|
||||
return false;
|
||||
@@ -29,18 +32,40 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (decalsChanged)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { false });
|
||||
lastSentVolume = waterVolume;
|
||||
lastSentOxygen = OxygenPercentage;
|
||||
lastSentFireCount = FireSources.Count;
|
||||
sendUpdateTimer = NetConfig.HullUpdateInterval;
|
||||
decalsChanged = false;
|
||||
return;
|
||||
}
|
||||
|
||||
sendUpdateTimer -= deltaTime;
|
||||
//update client hulls if the amount of water has changed by >10%
|
||||
//or if oxygen percentage has changed by 5%
|
||||
if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f ||
|
||||
Math.Abs(lastSentOxygen - OxygenPercentage) > 5f ||
|
||||
lastSentFireCount != FireSources.Count ||
|
||||
FireSources.Count > 0 ||
|
||||
if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f || Math.Abs(lastSentOxygen - OxygenPercentage) > 5f ||
|
||||
lastSentFireCount != FireSources.Count || FireSources.Count > 0 ||
|
||||
pendingSectionUpdates.Count > 0 ||
|
||||
sendUpdateTimer < -NetConfig.SparseHullUpdateInterval)
|
||||
{
|
||||
if (sendUpdateTimer < 0.0f)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this);
|
||||
if (pendingSectionUpdates.Count > 0)
|
||||
{
|
||||
foreach (int pendingSectionUpdate in pendingSectionUpdates)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { true, pendingSectionUpdate } );
|
||||
}
|
||||
pendingSectionUpdates.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this);
|
||||
}
|
||||
|
||||
lastSentVolume = waterVolume;
|
||||
lastSentOxygen = OxygenPercentage;
|
||||
lastSentFireCount = FireSources.Count;
|
||||
@@ -70,65 +95,122 @@ namespace Barotrauma
|
||||
message.WriteRangedSingle(MathHelper.Clamp(fireSource.Size.X / rect.Width, 0.0f, 1.0f), 0, 1.0f, 8);
|
||||
}
|
||||
}
|
||||
|
||||
message.Write(extraData != null);
|
||||
if (extraData != null)
|
||||
{
|
||||
message.Write((bool)extraData[0]);
|
||||
|
||||
// Section update
|
||||
if ((bool)extraData[0])
|
||||
{
|
||||
int sectorToUpdate = (int)extraData[1];
|
||||
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
|
||||
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
|
||||
message.WriteRangedInteger(sectorToUpdate, 0, BackgroundSections.Count - 1);
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
message.WriteRangedSingle(BackgroundSections[i].ColorStrength, 0.0f, 1.0f, 8);
|
||||
message.Write(BackgroundSections[i].Color.PackedValue);
|
||||
}
|
||||
}
|
||||
else // Decal update
|
||||
{
|
||||
message.WriteRangedInteger(decals.Count, 0, MaxDecalsPerHull);
|
||||
foreach (Decal decal in decals)
|
||||
{
|
||||
message.Write(decal.Prefab.UIntIdentifier);
|
||||
float normalizedXPos = MathHelper.Clamp(MathUtils.InverseLerp(rect.X, rect.Right, decal.Position.X), 0.0f, 1.0f);
|
||||
float normalizedYPos = MathHelper.Clamp(MathUtils.InverseLerp(rect.Y - rect.Height, rect.Y, decal.Position.Y), 0.0f, 1.0f);
|
||||
message.WriteRangedSingle(normalizedXPos, 0.0f, 1.0f, 8);
|
||||
message.WriteRangedSingle(normalizedYPos, 0.0f, 1.0f, 8);
|
||||
message.WriteRangedSingle(decal.Scale, 0f, 2f, 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//used when clients use the water/fire console commands
|
||||
//used when clients use the water/fire console commands or section / decal updates are received
|
||||
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
|
||||
{
|
||||
float newWaterVolume = msg.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
|
||||
|
||||
bool hasFireSources = msg.ReadBoolean();
|
||||
int fireSourceCount = 0;
|
||||
List<Vector3> newFireSources = new List<Vector3>();
|
||||
if (hasFireSources)
|
||||
bool hasExtraData = msg.ReadBoolean();
|
||||
if (hasExtraData)
|
||||
{
|
||||
fireSourceCount = msg.ReadRangedInteger(0, 16);
|
||||
int sectorToUpdate = msg.ReadRangedInteger(0, BackgroundSections.Count - 1);
|
||||
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
|
||||
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
float colorStrength = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
Color color = new Color(msg.ReadUInt32());
|
||||
|
||||
//TODO: verify the client is close enough to this hull to paint it, that the sprayer is functional and that the color matches
|
||||
if (c.Character != null && c.Character.AllowInput && c.Character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() != null))
|
||||
{
|
||||
BackgroundSections[i].SetColorStrength(colorStrength);
|
||||
BackgroundSections[i].SetColor(color);
|
||||
}
|
||||
}
|
||||
//add to pending updates to notify other clients as well
|
||||
pendingSectionUpdates.Add(sectorToUpdate);
|
||||
}
|
||||
else
|
||||
{
|
||||
float newWaterVolume = msg.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
|
||||
|
||||
bool hasFireSources = msg.ReadBoolean();
|
||||
int fireSourceCount = 0;
|
||||
List<Vector3> newFireSources = new List<Vector3>();
|
||||
if (hasFireSources)
|
||||
{
|
||||
fireSourceCount = msg.ReadRangedInteger(0, 16);
|
||||
for (int i = 0; i < fireSourceCount; i++)
|
||||
{
|
||||
newFireSources.Add(new Vector3(
|
||||
MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
|
||||
MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
|
||||
msg.ReadRangedSingle(0.0f, 1.0f, 8)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!c.HasPermission(ClientPermissions.ConsoleCommands) ||
|
||||
!c.PermittedConsoleCommands.Any(command => command.names.Contains("fire") || command.names.Contains("editfire")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WaterVolume = newWaterVolume;
|
||||
|
||||
for (int i = 0; i < fireSourceCount; i++)
|
||||
{
|
||||
newFireSources.Add(new Vector3(
|
||||
MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
|
||||
MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
|
||||
msg.ReadRangedSingle(0.0f, 1.0f, 8)));
|
||||
Vector2 pos = new Vector2(
|
||||
rect.X + rect.Width * newFireSources[i].X,
|
||||
rect.Y - rect.Height + (rect.Height * newFireSources[i].Y));
|
||||
float size = newFireSources[i].Z * rect.Width;
|
||||
|
||||
var newFire = i < FireSources.Count ?
|
||||
FireSources[i] :
|
||||
new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true);
|
||||
newFire.Position = pos;
|
||||
newFire.Size = new Vector2(size, newFire.Size.Y);
|
||||
|
||||
//ignore if the fire wasn't added to this room (invalid position)?
|
||||
if (!FireSources.Contains(newFire))
|
||||
{
|
||||
newFire.Remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!c.HasPermission(ClientPermissions.ConsoleCommands) ||
|
||||
!c.PermittedConsoleCommands.Any(command => command.names.Contains("fire") || command.names.Contains("editfire")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WaterVolume = newWaterVolume;
|
||||
|
||||
for (int i = 0; i < fireSourceCount; i++)
|
||||
{
|
||||
Vector2 pos = new Vector2(
|
||||
rect.X + rect.Width * newFireSources[i].X,
|
||||
rect.Y - rect.Height + (rect.Height * newFireSources[i].Y));
|
||||
float size = newFireSources[i].Z * rect.Width;
|
||||
|
||||
var newFire = i < FireSources.Count ?
|
||||
FireSources[i] :
|
||||
new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true);
|
||||
newFire.Position = pos;
|
||||
newFire.Size = new Vector2(size, newFire.Size.Y);
|
||||
|
||||
//ignore if the fire wasn't added to this room (invalid position)?
|
||||
if (!FireSources.Contains(newFire))
|
||||
for (int i = FireSources.Count - 1; i >= fireSourceCount; i--)
|
||||
{
|
||||
newFire.Remove();
|
||||
continue;
|
||||
FireSources[i].Remove();
|
||||
if (i < FireSources.Count)
|
||||
{
|
||||
FireSources.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = FireSources.Count - 1; i >= fireSourceCount; i--)
|
||||
{
|
||||
FireSources[i].Remove();
|
||||
if (i < FireSources.Count)
|
||||
{
|
||||
FireSources.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -10,15 +11,16 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
private static UInt16 LastIdentifier = 0;
|
||||
|
||||
public BannedPlayer(string name, string ip, string reason, DateTime? expirationTime)
|
||||
public BannedPlayer(string name, string endPoint, string reason, DateTime? expirationTime)
|
||||
{
|
||||
this.Name = name;
|
||||
this.IP = ip;
|
||||
this.EndPoint = endPoint;
|
||||
ParseEndPointAsSteamId();
|
||||
this.Reason = reason;
|
||||
this.ExpirationTime = expirationTime;
|
||||
this.UniqueIdentifier = LastIdentifier; LastIdentifier++;
|
||||
|
||||
this.IsRangeBan = IP.IndexOf(".x") > -1;
|
||||
this.IsRangeBan = EndPoint.IndexOf(".x") > -1;
|
||||
}
|
||||
|
||||
public BannedPlayer(string name, ulong steamID, string reason, DateTime? expirationTime)
|
||||
@@ -31,27 +33,27 @@ namespace Barotrauma.Networking
|
||||
|
||||
this.IsRangeBan = false;
|
||||
|
||||
this.IP = "";
|
||||
this.EndPoint = "";
|
||||
}
|
||||
|
||||
public bool CompareTo(string ipCompare)
|
||||
public bool CompareTo(string endpointCompare)
|
||||
{
|
||||
if (string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(IP)) { return false; }
|
||||
if (string.IsNullOrEmpty(EndPoint) || string.IsNullOrEmpty(EndPoint)) { return false; }
|
||||
if (!IsRangeBan)
|
||||
{
|
||||
return ipCompare == IP;
|
||||
return endpointCompare == EndPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
int rangeBanIndex = IP.IndexOf(".x");
|
||||
if (ipCompare.Length < rangeBanIndex) return false;
|
||||
return ipCompare.Substring(0, rangeBanIndex) == IP.Substring(0, rangeBanIndex);
|
||||
int rangeBanIndex = EndPoint.IndexOf(".x");
|
||||
if (endpointCompare.Length < rangeBanIndex) return false;
|
||||
return endpointCompare.Substring(0, rangeBanIndex) == EndPoint.Substring(0, rangeBanIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CompareTo(IPAddress ipCompare)
|
||||
{
|
||||
if (string.IsNullOrEmpty(IP) || ipCompare == null) { return false; }
|
||||
if (string.IsNullOrEmpty(EndPoint) || ipCompare == null) { return false; }
|
||||
if (ipCompare.IsIPv4MappedToIPv6 && CompareTo(ipCompare.MapToIPv4NoThrow().ToString()))
|
||||
{
|
||||
return true;
|
||||
@@ -123,7 +125,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
reason = string.Empty;
|
||||
if (IPAddress.IsLoopback(IP)) { return false; }
|
||||
var bannedPlayer = bannedPlayers.Find(bp => bp.CompareTo(IP) || (steamID > 0 && bp.SteamID == steamID));
|
||||
var bannedPlayer = bannedPlayers.Find(bp =>
|
||||
bp.CompareTo(IP) ||
|
||||
(steamID > 0 && bp.SteamID == steamID) ||
|
||||
(SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID));
|
||||
reason = bannedPlayer?.Reason;
|
||||
return bannedPlayer != null;
|
||||
}
|
||||
@@ -142,7 +147,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
reason = string.Empty;
|
||||
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
|
||||
var bannedPlayer = bannedPlayers.Find(bp => steamID > 0 && bp.SteamID == steamID);
|
||||
var bannedPlayer = bannedPlayers.Find(bp =>
|
||||
steamID > 0 &&
|
||||
(bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID));
|
||||
reason = bannedPlayer?.Reason;
|
||||
return bannedPlayer != null;
|
||||
}
|
||||
@@ -153,9 +160,9 @@ namespace Barotrauma.Networking
|
||||
BanPlayer(name, ipStr, 0, reason, duration);
|
||||
}
|
||||
|
||||
public void BanPlayer(string name, string ip, string reason, TimeSpan? duration)
|
||||
public void BanPlayer(string name, string endPoint, string reason, TimeSpan? duration)
|
||||
{
|
||||
BanPlayer(name, ip, 0, reason, duration);
|
||||
BanPlayer(name, endPoint, 0, reason, duration);
|
||||
}
|
||||
|
||||
public void BanPlayer(string name, ulong steamID, string reason, TimeSpan? duration)
|
||||
@@ -163,9 +170,9 @@ namespace Barotrauma.Networking
|
||||
BanPlayer(name, "", steamID, reason, duration);
|
||||
}
|
||||
|
||||
private void BanPlayer(string name, string ip, ulong steamID, string reason, TimeSpan? duration)
|
||||
private void BanPlayer(string name, string endPoint, ulong steamID, string reason, TimeSpan? duration)
|
||||
{
|
||||
var existingBan = bannedPlayers.Find(bp => bp.IP == ip && bp.SteamID == steamID);
|
||||
var existingBan = bannedPlayers.Find(bp => bp.EndPoint == endPoint && bp.SteamID == steamID);
|
||||
if (existingBan != null)
|
||||
{
|
||||
if (!duration.HasValue) return;
|
||||
@@ -190,9 +197,9 @@ namespace Barotrauma.Networking
|
||||
expirationTime = DateTime.Now + duration.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ip))
|
||||
if (!string.IsNullOrEmpty(endPoint))
|
||||
{
|
||||
bannedPlayers.Add(new BannedPlayer(name, ip, reason, expirationTime));
|
||||
bannedPlayers.Add(new BannedPlayer(name, endPoint, reason, expirationTime));
|
||||
}
|
||||
else if (steamID > 0)
|
||||
{
|
||||
@@ -221,12 +228,15 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public void UnbanIP(string ip)
|
||||
public void UnbanEndPoint(string endPoint)
|
||||
{
|
||||
var player = bannedPlayers.Find(bp => bp.IP == ip);
|
||||
ulong steamId = SteamManager.SteamIDStringToUInt64(endPoint);
|
||||
var player = bannedPlayers.Find(bp =>
|
||||
bp.EndPoint == endPoint ||
|
||||
(steamId != 0 && steamId == SteamManager.SteamIDStringToUInt64(bp.EndPoint)));
|
||||
if (player == null)
|
||||
{
|
||||
DebugConsole.Log("Could not unban IP \"" + ip + "\". Matching player not found.");
|
||||
DebugConsole.Log("Could not unban endpoint \"" + endPoint + "\". Matching player not found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -246,10 +256,10 @@ namespace Barotrauma.Networking
|
||||
|
||||
private void RangeBan(BannedPlayer banned)
|
||||
{
|
||||
banned.IP = ToRange(banned.IP);
|
||||
banned.EndPoint = ToRange(banned.EndPoint);
|
||||
|
||||
BannedPlayer bp;
|
||||
while ((bp = bannedPlayers.Find(x => banned.CompareTo(x.IP))) != null)
|
||||
while ((bp = bannedPlayers.Find(x => banned.CompareTo(x.EndPoint))) != null)
|
||||
{
|
||||
//remove all specific bans that are now covered by the rangeban
|
||||
bannedPlayers.Remove(bp);
|
||||
@@ -270,7 +280,7 @@ namespace Barotrauma.Networking
|
||||
foreach (BannedPlayer banned in bannedPlayers)
|
||||
{
|
||||
string line = banned.Name;
|
||||
line += "," + ((banned.SteamID > 0) ? banned.SteamID.ToString() : banned.IP);
|
||||
line += "," + ((banned.SteamID > 0) ? SteamManager.SteamIDUInt64ToString(banned.SteamID) : banned.EndPoint);
|
||||
line += "," + (banned.ExpirationTime.HasValue ? banned.ExpirationTime.Value.ToString() : "");
|
||||
if (!string.IsNullOrWhiteSpace(banned.Reason)) line += "," + banned.Reason;
|
||||
|
||||
@@ -314,7 +324,7 @@ namespace Barotrauma.Networking
|
||||
outMsg.Write(bannedPlayer.IsRangeBan); outMsg.WritePadBits();
|
||||
if (c.Connection == GameMain.Server.OwnerConnection)
|
||||
{
|
||||
outMsg.Write(bannedPlayer.IP);
|
||||
outMsg.Write(bannedPlayer.EndPoint);
|
||||
outMsg.Write(bannedPlayer.SteamID);
|
||||
}
|
||||
}
|
||||
@@ -347,7 +357,7 @@ namespace Barotrauma.Networking
|
||||
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
|
||||
if (bannedPlayer != null)
|
||||
{
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.IP + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.EndPoint + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
RemoveBan(bannedPlayer);
|
||||
}
|
||||
}
|
||||
@@ -358,7 +368,7 @@ namespace Barotrauma.Networking
|
||||
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
|
||||
if (bannedPlayer != null)
|
||||
{
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " rangebanned " + bannedPlayer.Name + " (" + bannedPlayer.IP + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " rangebanned " + bannedPlayer.Name + " (" + bannedPlayer.EndPoint + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
RangeBan(bannedPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ namespace Barotrauma.Networking
|
||||
public readonly Dictionary<UInt16, double> EntityEventLastSent = new Dictionary<UInt16, double>();
|
||||
|
||||
//when was a position update for a given entity last sent to the client
|
||||
// key = entity id, value = NetTime.Now when sending
|
||||
public readonly Dictionary<UInt16, float> PositionUpdateLastSent = new Dictionary<UInt16, float>();
|
||||
// key = entity, value = NetTime.Now when sending
|
||||
public readonly Dictionary<Entity, float> PositionUpdateLastSent = new Dictionary<Entity, float>();
|
||||
public readonly Queue<Entity> PendingPositionUpdates = new Queue<Entity>();
|
||||
|
||||
public bool ReadyToStart;
|
||||
@@ -145,19 +145,9 @@ namespace Barotrauma.Networking
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool EndpointMatches(string endpoint)
|
||||
public bool EndpointMatches(string endPoint)
|
||||
{
|
||||
if (Connection is LidgrenConnection lidgrenConn)
|
||||
{
|
||||
if (lidgrenConn.IPEndPoint?.Address == null) { return false; }
|
||||
if ((lidgrenConn.IPEndPoint?.Address.IsIPv4MappedToIPv6 ?? false) &&
|
||||
lidgrenConn.IPEndPoint?.Address.MapToIPv4NoThrow().ToString() == endpoint)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Connection.EndPointString == endpoint;
|
||||
return Connection.EndpointMatches(endPoint);
|
||||
}
|
||||
|
||||
public void SetPermissions(ClientPermissions permissions, List<DebugConsole.Command> permittedConsoleCommands)
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to initiate file transfer (file \"" + filePath + "\" not found.");
|
||||
DebugConsole.ThrowError("Failed to initiate file transfer (file \"" + filePath + "\" not found).\n" + Environment.StackTrace);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,7 +282,9 @@ namespace Barotrauma.Networking
|
||||
Client newClient = new Client(clName, GetNewClientID());
|
||||
newClient.InitClientSync();
|
||||
newClient.Connection = connection;
|
||||
newClient.Connection.Status = NetworkConnectionStatus.Connected;
|
||||
newClient.SteamID = connection.SteamID;
|
||||
newClient.Language = connection.Language;
|
||||
ConnectedClients.Add(newClient);
|
||||
|
||||
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(newClient));
|
||||
@@ -382,14 +384,12 @@ namespace Barotrauma.Networking
|
||||
for (int i = Character.CharacterList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Character character = Character.CharacterList[i];
|
||||
if (character.IsDead || !character.ClientDisconnected) continue;
|
||||
if (character.IsDead || !character.ClientDisconnected) { continue; }
|
||||
|
||||
character.KillDisconnectedTimer += deltaTime;
|
||||
character.SetStun(1.0f);
|
||||
|
||||
Client owner = connectedClients.Find(c =>
|
||||
c.Name == character.OwnerClientName &&
|
||||
c.EndpointMatches(character.OwnerClientEndPoint));
|
||||
Client owner = connectedClients.Find(c => c.EndpointMatches(character.OwnerClientEndPoint));
|
||||
|
||||
if ((OwnerConnection == null || owner?.Connection != OwnerConnection) && character.KillDisconnectedTimer > serverSettings.KillDisconnectedTime)
|
||||
{
|
||||
@@ -397,8 +397,7 @@ namespace Barotrauma.Networking
|
||||
continue;
|
||||
}
|
||||
|
||||
if (owner != null &&
|
||||
owner.InGame && !owner.NeedsMidRoundSync &&
|
||||
if (owner != null && owner.InGame && !owner.NeedsMidRoundSync &&
|
||||
(!serverSettings.AllowSpectating || !owner.SpectateOnly))
|
||||
{
|
||||
SetClientCharacter(owner, character);
|
||||
@@ -436,16 +435,21 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (Level.Loaded?.EndOutpost != null)
|
||||
{
|
||||
bool charactersInsideOutpost = connectedClients.Any(c =>
|
||||
int charactersInsideOutpost = connectedClients.Count(c =>
|
||||
c.Character != null &&
|
||||
!c.Character.IsDead &&
|
||||
!c.Character.IsDead && !c.Character.IsUnconscious &&
|
||||
c.Character.Submarine == Level.Loaded.EndOutpost);
|
||||
int charactersOutsideOutpost = connectedClients.Count(c =>
|
||||
c.Character != null &&
|
||||
!c.Character.IsDead && !c.Character.IsUnconscious &&
|
||||
c.Character.Submarine != Level.Loaded.EndOutpost);
|
||||
|
||||
//level finished if the sub is docked to the outpost
|
||||
//or very close and someone from the crew made it inside the outpost
|
||||
subAtLevelEnd =
|
||||
Submarine.MainSub.DockedTo.Contains(Level.Loaded.EndOutpost) ||
|
||||
(Submarine.MainSub.AtEndPosition && charactersInsideOutpost);
|
||||
(Submarine.MainSub.AtEndPosition && charactersInsideOutpost > 0) ||
|
||||
(charactersInsideOutpost > charactersOutsideOutpost);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -464,7 +468,7 @@ namespace Barotrauma.Networking
|
||||
endRoundDelay = 5.0f;
|
||||
endRoundTimer += deltaTime;
|
||||
}
|
||||
else if (serverSettings.EndRoundAtLevelEnd && subAtLevelEnd && !(GameMain.GameSession?.GameMode is CampaignMode))
|
||||
else if (subAtLevelEnd && !(GameMain.GameSession?.GameMode is CampaignMode))
|
||||
{
|
||||
endRoundDelay = 5.0f;
|
||||
endRoundTimer += deltaTime;
|
||||
@@ -493,7 +497,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
Log("Ending round (entire crew dead)", ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
else if (serverSettings.EndRoundAtLevelEnd && subAtLevelEnd)
|
||||
else if (subAtLevelEnd)
|
||||
{
|
||||
Log("Ending round (submarine reached the end of the level)", ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
@@ -628,7 +632,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (character.healthUpdateTimer <= 0.0f)
|
||||
{
|
||||
character.healthUpdateTimer = character.HealthUpdateInterval;
|
||||
if (!character.HealthUpdatePending)
|
||||
{
|
||||
character.healthUpdateTimer = character.HealthUpdateInterval;
|
||||
}
|
||||
character.HealthUpdatePending = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1532,8 +1540,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (!character.Enabled) continue;
|
||||
|
||||
if (!character.Enabled) { continue; }
|
||||
if (c.SpectatePos == null)
|
||||
{
|
||||
if (c.Character != null && Vector2.DistanceSquared(character.WorldPosition, c.Character.WorldPosition) >= NetConfig.DisableCharacterDistSqr)
|
||||
@@ -1543,17 +1550,24 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Vector2.DistanceSquared(character.WorldPosition, c.SpectatePos.Value) >= NetConfig.DisableCharacterDistSqr)
|
||||
if (character != c.Character && Vector2.DistanceSquared(character.WorldPosition, c.SpectatePos.Value) >= NetConfig.DisableCharacterDistSqr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
float updateInterval = character.GetPositionUpdateInterval(c);
|
||||
c.PositionUpdateLastSent.TryGetValue(character.ID, out float lastSent);
|
||||
if (lastSent > Lidgren.Network.NetTime.Now - updateInterval) { continue; }
|
||||
|
||||
if (!c.PendingPositionUpdates.Contains(character)) c.PendingPositionUpdates.Enqueue(character);
|
||||
c.PositionUpdateLastSent.TryGetValue(character, out float lastSent);
|
||||
if (lastSent > NetTime.Now)
|
||||
{
|
||||
//sent in the future -> can't be right, remove
|
||||
c.PositionUpdateLastSent.Remove(character);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lastSent > NetTime.Now - updateInterval) { continue; }
|
||||
}
|
||||
if (!c.PendingPositionUpdates.Contains(character)) { c.PendingPositionUpdates.Enqueue(character); }
|
||||
}
|
||||
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
@@ -1568,16 +1582,24 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (item.PositionUpdateInterval == float.PositiveInfinity) { continue; }
|
||||
float updateInterval = item.GetPositionUpdateInterval(c);
|
||||
c.PositionUpdateLastSent.TryGetValue(item.ID, out float lastSent);
|
||||
if (lastSent > Lidgren.Network.NetTime.Now - updateInterval) { continue; }
|
||||
if (!c.PendingPositionUpdates.Contains(item)) c.PendingPositionUpdates.Enqueue(item);
|
||||
c.PositionUpdateLastSent.TryGetValue(item, out float lastSent);
|
||||
if (lastSent > NetTime.Now)
|
||||
{
|
||||
//sent in the future -> can't be right, remove
|
||||
c.PositionUpdateLastSent.Remove(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lastSent > NetTime.Now - updateInterval) { continue; }
|
||||
}
|
||||
if (!c.PendingPositionUpdates.Contains(item)) { c.PendingPositionUpdates.Enqueue(item); }
|
||||
}
|
||||
}
|
||||
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME);
|
||||
|
||||
outmsg.Write((float)Lidgren.Network.NetTime.Now);
|
||||
outmsg.Write((float)NetTime.Now);
|
||||
|
||||
outmsg.Write((byte)ServerNetObject.SYNC_IDS);
|
||||
outmsg.Write(c.LastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server
|
||||
@@ -1636,7 +1658,7 @@ namespace Barotrauma.Networking
|
||||
outmsg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||
outmsg.WritePadBits();
|
||||
|
||||
c.PositionUpdateLastSent[entity.ID] = (float)Lidgren.Network.NetTime.Now;
|
||||
c.PositionUpdateLastSent[entity] = (float)NetTime.Now;
|
||||
c.PendingPositionUpdates.Dequeue();
|
||||
}
|
||||
positionUpdateBytes = outmsg.LengthBytes - positionUpdateBytes;
|
||||
@@ -1713,7 +1735,7 @@ namespace Barotrauma.Networking
|
||||
outmsg.Write(client.SteamID);
|
||||
outmsg.Write(client.NameID);
|
||||
outmsg.Write(client.Name);
|
||||
outmsg.Write(client.Character == null || !gameStarted ? (client.PreferredJob ?? "") : "");
|
||||
outmsg.Write(client.Character?.Info?.Job != null && gameStarted ? client.Character.Info.Job.Prefab.Identifier : (client.PreferredJob ?? ""));
|
||||
outmsg.Write(client.Character == null || !gameStarted ? (ushort)0 : client.Character.ID);
|
||||
if (c.HasPermission(ClientPermissions.ServerLog))
|
||||
{
|
||||
@@ -2099,11 +2121,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode;
|
||||
bool missionAllowRespawn = campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn);
|
||||
bool missionAllowRespawn = GameMain.GameSession.Campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn);
|
||||
bool outpostAllowRespawn = GameMain.GameSession.Campaign != null && Level.Loaded?.Type == LevelData.LevelType.Outpost;
|
||||
|
||||
if (serverSettings.AllowRespawn && missionAllowRespawn)
|
||||
if (serverSettings.AllowRespawn && (missionAllowRespawn || outpostAllowRespawn))
|
||||
{
|
||||
respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle ? selectedShuttle : null);
|
||||
respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle && !outpostAllowRespawn ? selectedShuttle : null);
|
||||
}
|
||||
|
||||
Level.Loaded?.SpawnNPCs();
|
||||
@@ -2298,6 +2321,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
if (GameMain.Server?.ServerSettings?.Voting != null)
|
||||
{
|
||||
GameMain.Server.ServerSettings.Voting.ResetVotes(GameMain.Server.ConnectedClients);
|
||||
}
|
||||
|
||||
GameMain.GameScreen.Select();
|
||||
|
||||
Log("Round started.", ServerLog.MessageType.ServerMessage);
|
||||
@@ -2332,7 +2360,8 @@ namespace Barotrauma.Networking
|
||||
msg.Write(gameSession.GameMode.Preset.Identifier);
|
||||
|
||||
bool missionAllowRespawn = campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn);
|
||||
msg.Write(serverSettings.AllowRespawn && missionAllowRespawn);
|
||||
bool outpostAllowRespawn = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost;
|
||||
msg.Write(missionAllowRespawn || outpostAllowRespawn);
|
||||
msg.Write(serverSettings.AllowDisguises);
|
||||
msg.Write(serverSettings.AllowRewiring);
|
||||
msg.Write(serverSettings.AllowRagdollButton);
|
||||
@@ -2470,23 +2499,6 @@ namespace Barotrauma.Networking
|
||||
traitorResult.ServerWrite(msg);
|
||||
}
|
||||
|
||||
// used to check if client and server mismatch upgrades
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
msg.Write(true);
|
||||
Dictionary<string, int> dict = UpgradeManager.GetMetadataLevels(campaign?.CampaignMetadata);
|
||||
msg.Write((ushort)dict.Count);
|
||||
foreach (var (key, value) in dict)
|
||||
{
|
||||
msg.Write(key);
|
||||
msg.Write((byte)value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(false);
|
||||
}
|
||||
|
||||
foreach (Client client in connectedClients)
|
||||
{
|
||||
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
||||
@@ -2544,12 +2556,18 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!Client.IsValidName(newName, serverSettings))
|
||||
{
|
||||
SendDirectChatMessage("Could not change your name to \"" + newName + "\" (the name contains disallowed symbols).", c, ChatMessageType.MessageBox);
|
||||
SendDirectChatMessage($"ServerMessage.NameChangeFailedSymbols~[newname]={newName}", c, ChatMessageType.ServerMessageBox);
|
||||
return false;
|
||||
}
|
||||
if (Homoglyphs.Compare(newName.ToLower(), ServerName.ToLower()))
|
||||
{
|
||||
SendDirectChatMessage("Could not change your name to \"" + newName + "\" (too similar to the server's name).", c, ChatMessageType.MessageBox);
|
||||
SendDirectChatMessage($"ServerMessage.NameChangeFailedServerTooSimilar~[newname]={newName}", c, ChatMessageType.ServerMessageBox);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c.KickVoteCount > 0)
|
||||
{
|
||||
SendDirectChatMessage($"ServerMessage.NameChangeFailedVoteKick~[newname]={newName}", c, ChatMessageType.ServerMessageBox);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2557,11 +2575,11 @@ namespace Barotrauma.Networking
|
||||
Client nameTaken = ConnectedClients.Find(c2 => c != c2 && Homoglyphs.Compare(c2.Name.ToLower(), newName.ToLower()));
|
||||
if (nameTaken != null)
|
||||
{
|
||||
SendDirectChatMessage("Could not change your name to \"" + newName + "\" (too similar to the name of the client \"" + nameTaken.Name + "\").", c, ChatMessageType.MessageBox);
|
||||
SendDirectChatMessage($"ServerMessage.NameChangeFailedClientTooSimilar~[newname]={newName}~[takenname]={nameTaken.Name}", c, ChatMessageType.ServerMessageBox);
|
||||
return false;
|
||||
}
|
||||
|
||||
SendChatMessage("Player \"" + c.Name + "\" has changed their name to \"" + newName + "\".", ChatMessageType.Server);
|
||||
SendChatMessage($"ServerMessage.NameChangeSuccessful~[oldname]={c.Name}~[newname]={newName}", ChatMessageType.Server);
|
||||
c.Name = newName;
|
||||
c.Connection.Name = newName;
|
||||
return true;
|
||||
@@ -2633,16 +2651,13 @@ namespace Barotrauma.Networking
|
||||
string targetMsg = DisconnectReason.Banned.ToString();
|
||||
DisconnectClient(client, $"ServerMessage.BannedFromServer~[client]={client.Name}", targetMsg, reason, PlayerConnectionChangeType.Banned);
|
||||
|
||||
if (client.SteamID == 0 || range)
|
||||
if (client.Connection is LidgrenConnection lidgrenConn && (client.SteamID == 0 || range))
|
||||
{
|
||||
string ip = "";
|
||||
if (client.Connection is LidgrenConnection lidgrenConn)
|
||||
{
|
||||
ip = lidgrenConn.IPEndPoint.Address.IsIPv4MappedToIPv6 ?
|
||||
lidgrenConn.IPEndPoint.Address.MapToIPv4NoThrow().ToString() :
|
||||
lidgrenConn.IPEndPoint.Address.ToString();
|
||||
if (range) { ip = BanList.ToRange(ip); }
|
||||
}
|
||||
ip = lidgrenConn.IPEndPoint.Address.IsIPv4MappedToIPv6 ?
|
||||
lidgrenConn.IPEndPoint.Address.MapToIPv4NoThrow().ToString() :
|
||||
lidgrenConn.IPEndPoint.Address.ToString();
|
||||
if (range) { ip = BanList.ToRange(ip); }
|
||||
|
||||
serverSettings.BanList.BanPlayer(client.Name, ip, reason, duration);
|
||||
}
|
||||
@@ -2652,11 +2667,11 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnbanPlayer(string playerName, string playerIP)
|
||||
public override void UnbanPlayer(string playerName, string playerEndPoint)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(playerIP))
|
||||
if (!string.IsNullOrEmpty(playerEndPoint))
|
||||
{
|
||||
serverSettings.BanList.UnbanIP(playerIP);
|
||||
serverSettings.BanList.UnbanEndPoint(playerEndPoint);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(playerName))
|
||||
{
|
||||
|
||||
+103
-340
@@ -8,41 +8,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
class LidgrenServerPeer : ServerPeer
|
||||
{
|
||||
private readonly ServerSettings serverSettings;
|
||||
|
||||
private NetPeerConfiguration netPeerConfiguration;
|
||||
private NetServer netServer;
|
||||
|
||||
private class PendingClient
|
||||
{
|
||||
public string Name;
|
||||
public int OwnerKey;
|
||||
public NetConnection Connection;
|
||||
public ConnectionInitialization InitializationStep;
|
||||
public double UpdateTime;
|
||||
public double TimeOut;
|
||||
public int Retries;
|
||||
public UInt64? SteamID;
|
||||
public Int32? PasswordSalt;
|
||||
public bool AuthSessionStarted;
|
||||
|
||||
public PendingClient(NetConnection conn)
|
||||
{
|
||||
OwnerKey = 0;
|
||||
Connection = conn;
|
||||
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
|
||||
Retries = 0;
|
||||
SteamID = null;
|
||||
PasswordSalt = null;
|
||||
UpdateTime = Timing.TotalTime + Timing.Step * 3.0;
|
||||
TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
AuthSessionStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<LidgrenConnection> connectedClients;
|
||||
private readonly List<PendingClient> pendingClients;
|
||||
|
||||
private readonly List<NetIncomingMessage> incomingLidgrenMessages;
|
||||
|
||||
public LidgrenServerPeer(int? ownKey, ServerSettings settings)
|
||||
@@ -51,7 +19,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
netServer = null;
|
||||
|
||||
connectedClients = new List<LidgrenConnection>();
|
||||
connectedClients = new List<NetworkConnection>();
|
||||
pendingClients = new List<PendingClient>();
|
||||
|
||||
incomingLidgrenMessages = new List<NetIncomingMessage>();
|
||||
@@ -168,7 +136,16 @@ namespace Barotrauma.Networking
|
||||
for (int i = 0; i < pendingClients.Count; i++)
|
||||
{
|
||||
PendingClient pendingClient = pendingClients[i];
|
||||
UpdatePendingClient(pendingClient, deltaTime);
|
||||
|
||||
var connection = pendingClient.Connection as LidgrenConnection;
|
||||
if (connection.NetConnection.Status == NetConnectionStatus.InitiatedConnect ||
|
||||
connection.NetConnection.Status == NetConnectionStatus.ReceivedInitiation ||
|
||||
connection.NetConnection.Status == NetConnectionStatus.RespondedAwaitingApproval ||
|
||||
connection.NetConnection.Status == NetConnectionStatus.RespondedConnect)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
UpdatePendingClient(pendingClient);
|
||||
if (i >= pendingClients.Count || pendingClients[i] != pendingClient) { i--; }
|
||||
}
|
||||
|
||||
@@ -214,11 +191,11 @@ namespace Barotrauma.Networking
|
||||
return;
|
||||
}
|
||||
|
||||
PendingClient pendingClient = pendingClients.Find(c => c.Connection == inc.SenderConnection);
|
||||
PendingClient pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
|
||||
|
||||
if (pendingClient == null)
|
||||
{
|
||||
pendingClient = new PendingClient(inc.SenderConnection);
|
||||
pendingClient = new PendingClient(new LidgrenConnection("PENDING", inc.SenderConnection, 0));
|
||||
pendingClients.Add(pendingClient);
|
||||
}
|
||||
|
||||
@@ -229,7 +206,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
PendingClient pendingClient = pendingClients.Find(c => c.Connection == inc.SenderConnection);
|
||||
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
|
||||
|
||||
byte incByte = inc.ReadByte();
|
||||
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
|
||||
@@ -237,11 +214,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (isConnectionInitializationStep && pendingClient != null)
|
||||
{
|
||||
ReadConnectionInitializationStep(pendingClient, inc);
|
||||
ReadConnectionInitializationStep(pendingClient, new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
|
||||
}
|
||||
else if (!isConnectionInitializationStep)
|
||||
{
|
||||
LidgrenConnection conn = connectedClients.Find(c => c.NetConnection == inc.SenderConnection);
|
||||
LidgrenConnection conn = connectedClients.Find(c => (c is LidgrenConnection l) && l.NetConnection == inc.SenderConnection) as LidgrenConnection;
|
||||
if (conn == null)
|
||||
{
|
||||
if (pendingClient != null)
|
||||
@@ -278,7 +255,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
case NetConnectionStatus.Disconnected:
|
||||
string disconnectMsg;
|
||||
LidgrenConnection conn = connectedClients.Find(c => c.NetConnection == inc.SenderConnection);
|
||||
LidgrenConnection conn = connectedClients.Select(c => c as LidgrenConnection).FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
|
||||
if (conn != null)
|
||||
{
|
||||
if (conn == OwnerConnection)
|
||||
@@ -295,7 +272,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingClient pendingClient = pendingClients.Find(c => c.Connection == inc.SenderConnection);
|
||||
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
|
||||
if (pendingClient != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}");
|
||||
@@ -305,303 +282,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadConnectionInitializationStep(PendingClient pendingClient, NetIncomingMessage inc)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
|
||||
|
||||
//DebugConsole.NewMessage(initializationStep+" "+pendingClient.InitializationStep);
|
||||
|
||||
if (pendingClient.InitializationStep != initializationStep) return;
|
||||
|
||||
pendingClient.UpdateTime = Timing.TotalTime + Timing.Step;
|
||||
|
||||
switch (initializationStep)
|
||||
{
|
||||
case ConnectionInitialization.SteamTicketAndVersion:
|
||||
string name = Client.SanitizeName(inc.ReadString());
|
||||
int ownKey = inc.ReadInt32();
|
||||
UInt64 steamId = inc.ReadUInt64();
|
||||
UInt16 ticketLength = inc.ReadUInt16();
|
||||
byte[] ticket = inc.ReadBytes(ticketLength);
|
||||
|
||||
if (!Client.IsValidName(name, serverSettings))
|
||||
{
|
||||
if (OwnerConnection != null ||
|
||||
!IPAddress.IsLoopback(pendingClient.Connection.RemoteEndPoint.Address.MapToIPv4NoThrow()) &&
|
||||
ownerKey == null || ownKey == 0 && ownKey != ownerKey)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string version = inc.ReadString();
|
||||
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
|
||||
if (!isCompatibleVersion)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
|
||||
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
|
||||
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
|
||||
DebugConsole.NewMessage(name + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
|
||||
if (nameTaken != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
int contentPackageCount = inc.ReadVariableInt32();
|
||||
List<ClientContentPackage> clientContentPackages = new List<ClientContentPackage>();
|
||||
for (int i = 0; i < contentPackageCount; i++)
|
||||
{
|
||||
string packageName = inc.ReadString();
|
||||
string packageHash = inc.ReadString();
|
||||
clientContentPackages.Add(new ClientContentPackage(packageName, packageHash));
|
||||
}
|
||||
|
||||
//check if the client is missing any of our packages
|
||||
List<ContentPackage> missingPackages = new List<ContentPackage>();
|
||||
foreach (ContentPackage serverContentPackage in GameMain.SelectedPackages)
|
||||
{
|
||||
if (!serverContentPackage.HasMultiplayerIncompatibleContent) continue;
|
||||
bool packageFound = clientContentPackages.Any(cp => cp.Name == serverContentPackage.Name && cp.Hash == serverContentPackage.MD5hash.Hash);
|
||||
if (!packageFound) { missingPackages.Add(serverContentPackage); }
|
||||
}
|
||||
|
||||
//check if the client is using packages we don't have
|
||||
List<ClientContentPackage> redundantPackages = new List<ClientContentPackage>();
|
||||
foreach (ClientContentPackage clientContentPackage in clientContentPackages)
|
||||
{
|
||||
bool packageFound = GameMain.SelectedPackages.Any(cp => cp.Name == clientContentPackage.Name && cp.MD5hash.Hash == clientContentPackage.Hash);
|
||||
if (!packageFound) { redundantPackages.Add(clientContentPackage); }
|
||||
}
|
||||
|
||||
if (missingPackages.Count == 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
|
||||
$"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") 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)));
|
||||
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
|
||||
$"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
if (redundantPackages.Count == 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
|
||||
$"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr(redundantPackages[0])}");
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (using an incompatible content package " + GetPackageStr(redundantPackages[0]) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
if (redundantPackages.Count > 1)
|
||||
{
|
||||
List<string> packageStrs = new List<string>();
|
||||
redundantPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
|
||||
RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
|
||||
$"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (using incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingClient.SteamID == null)
|
||||
{
|
||||
bool requireSteamAuth = GameMain.Config.RequireSteamAuthentication;
|
||||
#if DEBUG
|
||||
requireSteamAuth = false;
|
||||
#endif
|
||||
|
||||
//steam auth cannot be done (SteamManager not initialized or no ticket given),
|
||||
//but it's not required either -> let the client join without auth
|
||||
if ((!Steam.SteamManager.IsInitialized || (ticket?.Length ?? 0) == 0) &&
|
||||
!requireSteamAuth)
|
||||
{
|
||||
pendingClient.Name = name;
|
||||
pendingClient.OwnerKey = ownKey;
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
|
||||
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: " + authSessionStartState.ToString());
|
||||
return;
|
||||
}
|
||||
pendingClient.SteamID = steamId;
|
||||
pendingClient.Name = name;
|
||||
pendingClient.OwnerKey = ownKey;
|
||||
pendingClient.AuthSessionStarted = true;
|
||||
}
|
||||
}
|
||||
else //TODO: could remove since this seems impossible
|
||||
{
|
||||
if (pendingClient.SteamID != steamId)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ConnectionInitialization.Password:
|
||||
int pwLength = inc.ReadByte();
|
||||
byte[] incPassword = new byte[pwLength];
|
||||
inc.ReadBytes(incPassword, 0, pwLength);
|
||||
if (pendingClient.PasswordSalt == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Received password message from client without salt");
|
||||
return;
|
||||
}
|
||||
if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
|
||||
{
|
||||
pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingClient.Retries++;
|
||||
if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
|
||||
{
|
||||
string banMsg = "Failed to enter correct password too many times";
|
||||
if (pendingClient.SteamID != null)
|
||||
{
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID.Value, banMsg, null);
|
||||
}
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.Connection.RemoteEndPoint.Address, banMsg, null);
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
break;
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
pendingClient.InitializationStep = ConnectionInitialization.Success;
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void UpdatePendingClient(PendingClient pendingClient, float deltaTime)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
if (serverSettings.BanList.IsBanned(pendingClient.Connection.RemoteEndPoint.Address, pendingClient.SteamID ?? 0, out string banReason))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
|
||||
return;
|
||||
}
|
||||
|
||||
//DebugConsole.NewMessage("pending client status: " + pendingClient.InitializationStep);
|
||||
|
||||
if (connectedClients.Count >= serverSettings.MaxPlayers)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
|
||||
}
|
||||
|
||||
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
|
||||
{
|
||||
LidgrenConnection newConnection = new LidgrenConnection(pendingClient.Name, pendingClient.Connection, pendingClient.SteamID ?? 0)
|
||||
{
|
||||
Status = NetworkConnectionStatus.Connected
|
||||
};
|
||||
connectedClients.Add(newConnection);
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
if (OwnerConnection == null &&
|
||||
IPAddress.IsLoopback(pendingClient.Connection.RemoteEndPoint.Address.MapToIPv4NoThrow()) &&
|
||||
ownerKey != null && pendingClient.OwnerKey != 0 && pendingClient.OwnerKey == ownerKey)
|
||||
{
|
||||
ownerKey = null;
|
||||
OwnerConnection = newConnection;
|
||||
}
|
||||
|
||||
OnInitializationComplete?.Invoke(newConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
pendingClient.TimeOut -= deltaTime;
|
||||
if (pendingClient.TimeOut < 0.0)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
}
|
||||
|
||||
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
|
||||
pendingClient.UpdateTime = Timing.TotalTime + 1.0;
|
||||
|
||||
NetOutgoingMessage outMsg = netServer.CreateMessage();
|
||||
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
|
||||
outMsg.Write((byte)pendingClient.InitializationStep);
|
||||
switch (pendingClient.InitializationStep)
|
||||
{
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
var mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
|
||||
outMsg.WriteVariableInt32(mpContentPackages.Count);
|
||||
for (int i = 0; i < mpContentPackages.Count; i++)
|
||||
{
|
||||
outMsg.Write(mpContentPackages[i].MD5hash.Hash);
|
||||
}
|
||||
break;
|
||||
case ConnectionInitialization.Password:
|
||||
outMsg.Write(pendingClient.PasswordSalt == null); outMsg.WritePadBits();
|
||||
if (pendingClient.PasswordSalt == null)
|
||||
{
|
||||
pendingClient.PasswordSalt = CryptoRandom.Instance.Next();
|
||||
outMsg.Write(pendingClient.PasswordSalt.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
outMsg.Write(pendingClient.Retries);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if DEBUG
|
||||
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Server.SimulatedDuplicatesChance;
|
||||
netPeerConfiguration.SimulatedMinimumLatency = GameMain.Server.SimulatedMinimumLatency;
|
||||
netPeerConfiguration.SimulatedRandomLatency = GameMain.Server.SimulatedRandomLatency;
|
||||
netPeerConfiguration.SimulatedLoss = GameMain.Server.SimulatedLoss;
|
||||
#endif
|
||||
NetSendResult result = netServer.SendMessage(outMsg, pendingClient.Connection, NetDeliveryMethod.ReliableUnordered);
|
||||
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
|
||||
{
|
||||
DebugConsole.NewMessage("Failed to send initialization step " + pendingClient.InitializationStep.ToString() + " to pending client: " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
|
||||
}
|
||||
//DebugConsole.NewMessage("sent update to pending client: " + pendingClient.InitializationStep);
|
||||
}
|
||||
|
||||
private void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
if (pendingClients.Contains(pendingClient))
|
||||
{
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
if (pendingClient.AuthSessionStarted)
|
||||
{
|
||||
Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value);
|
||||
pendingClient.SteamID = null;
|
||||
pendingClient.AuthSessionStarted = false;
|
||||
}
|
||||
|
||||
pendingClient.Connection.Disconnect(reason + "/" + msg);
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeSteamServerCallbacks()
|
||||
{
|
||||
Steamworks.SteamServer.OnValidateAuthTicketResponse += OnAuthChange;
|
||||
@@ -618,7 +298,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (status != Steamworks.AuthResponse.OK)
|
||||
{
|
||||
LidgrenConnection connection = connectedClients.Find(c => c.SteamID == steamID);
|
||||
LidgrenConnection connection = connectedClients.Find(c => c.SteamID == steamID) as LidgrenConnection;
|
||||
if (connection != null)
|
||||
{
|
||||
Disconnect(connection, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication status changed: " + status.ToString());
|
||||
@@ -627,7 +307,8 @@ namespace Barotrauma.Networking
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverSettings.BanList.IsBanned(pendingClient.Connection.RemoteEndPoint.Address, steamID, out string banReason))
|
||||
LidgrenConnection pendingConnection = pendingClient.Connection as LidgrenConnection;
|
||||
if (serverSettings.BanList.IsBanned(pendingConnection.NetConnection.RemoteEndPoint.Address, steamID, out string banReason))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
|
||||
return;
|
||||
@@ -698,5 +379,87 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
lidgrenConn.NetConnection.Disconnect(msg ?? "Disconnected");
|
||||
}
|
||||
|
||||
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
|
||||
{
|
||||
LidgrenConnection lidgrenConn = conn as LidgrenConnection;
|
||||
NetDeliveryMethod lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
|
||||
switch (deliveryMethod)
|
||||
{
|
||||
case DeliveryMethod.Unreliable:
|
||||
lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
|
||||
break;
|
||||
case DeliveryMethod.Reliable:
|
||||
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableUnordered;
|
||||
break;
|
||||
case DeliveryMethod.ReliableOrdered:
|
||||
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableOrdered;
|
||||
break;
|
||||
}
|
||||
|
||||
NetOutgoingMessage lidgrenMsg = netServer.CreateMessage();
|
||||
lidgrenMsg.Write(msg.Buffer, 0, msg.LengthBytes);
|
||||
NetSendResult result = netServer.SendMessage(lidgrenMsg, lidgrenConn.NetConnection, lidgrenDeliveryMethod);
|
||||
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
|
||||
{
|
||||
DebugConsole.NewMessage("Failed to send message to " + conn.Name + ": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckOwnership(PendingClient pendingClient)
|
||||
{
|
||||
LidgrenConnection l = pendingClient.Connection as LidgrenConnection;
|
||||
if (OwnerConnection == null &&
|
||||
IPAddress.IsLoopback(l.NetConnection.RemoteEndPoint.Address.MapToIPv4NoThrow()) &&
|
||||
ownerKey != null && pendingClient.OwnerKey != 0 && pendingClient.OwnerKey == ownerKey)
|
||||
{
|
||||
ownerKey = null;
|
||||
OwnerConnection = pendingClient.Connection;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket)
|
||||
{
|
||||
if (pendingClient.SteamID == null)
|
||||
{
|
||||
bool requireSteamAuth = GameMain.Config.RequireSteamAuthentication;
|
||||
#if DEBUG
|
||||
requireSteamAuth = false;
|
||||
#endif
|
||||
|
||||
//steam auth cannot be done (SteamManager not initialized or no ticket given),
|
||||
//but it's not required either -> let the client join without auth
|
||||
if ((!Steam.SteamManager.IsInitialized || (ticket?.Length ?? 0) == 0) &&
|
||||
!requireSteamAuth)
|
||||
{
|
||||
pendingClient.Connection.Name = name;
|
||||
pendingClient.Name = name;
|
||||
pendingClient.OwnerKey = ownKey;
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
|
||||
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: " + authSessionStartState.ToString());
|
||||
return;
|
||||
}
|
||||
pendingClient.SteamID = steamId;
|
||||
pendingClient.Connection.Name = name;
|
||||
pendingClient.Name = name;
|
||||
pendingClient.OwnerKey = ownKey;
|
||||
pendingClient.AuthSessionStarted = true;
|
||||
}
|
||||
}
|
||||
else //TODO: could remove since this seems impossible
|
||||
{
|
||||
if (pendingClient.SteamID != steamId)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+240
-21
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -7,26 +8,6 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
abstract class ServerPeer
|
||||
{
|
||||
protected struct ClientContentPackage
|
||||
{
|
||||
public string Name;
|
||||
public string Hash;
|
||||
|
||||
public ClientContentPackage(string name, string hash)
|
||||
{
|
||||
Name = name; Hash = hash;
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetPackageStr(ContentPackage contentPackage)
|
||||
{
|
||||
return "\"" + contentPackage.Name + "\" (hash " + contentPackage.MD5hash.ShortHash + ")";
|
||||
}
|
||||
protected string GetPackageStr(ClientContentPackage contentPackage)
|
||||
{
|
||||
return "\"" + contentPackage.Name + "\" (hash " + Md5Hash.GetShortHash(contentPackage.Hash) + ")";
|
||||
}
|
||||
|
||||
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
|
||||
public delegate void DisconnectCallback(NetworkConnection connection, string reason);
|
||||
public delegate void InitializationCompleteCallback(NetworkConnection connection);
|
||||
@@ -48,7 +29,245 @@ namespace Barotrauma.Networking
|
||||
public abstract void Start();
|
||||
public abstract void Close(string msg = null);
|
||||
public abstract void Update(float deltaTime);
|
||||
|
||||
|
||||
protected class PendingClient
|
||||
{
|
||||
public string Name;
|
||||
public int OwnerKey;
|
||||
public NetworkConnection Connection;
|
||||
public ConnectionInitialization InitializationStep;
|
||||
public double UpdateTime;
|
||||
public double TimeOut;
|
||||
public int Retries;
|
||||
public UInt64? SteamID;
|
||||
public Int32? PasswordSalt;
|
||||
public bool AuthSessionStarted;
|
||||
|
||||
public PendingClient(NetworkConnection conn)
|
||||
{
|
||||
OwnerKey = 0;
|
||||
Connection = conn;
|
||||
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
|
||||
Retries = 0;
|
||||
SteamID = null;
|
||||
PasswordSalt = null;
|
||||
UpdateTime = Timing.TotalTime + Timing.Step * 3.0;
|
||||
TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
AuthSessionStarted = false;
|
||||
}
|
||||
|
||||
public void Heartbeat()
|
||||
{
|
||||
TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
}
|
||||
}
|
||||
protected List<NetworkConnection> connectedClients;
|
||||
protected List<PendingClient> pendingClients;
|
||||
|
||||
protected ServerSettings serverSettings;
|
||||
|
||||
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
|
||||
{
|
||||
pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
|
||||
|
||||
if (pendingClient.InitializationStep != initializationStep) return;
|
||||
|
||||
pendingClient.UpdateTime = Timing.TotalTime + Timing.Step;
|
||||
|
||||
switch (initializationStep)
|
||||
{
|
||||
case ConnectionInitialization.SteamTicketAndVersion:
|
||||
string name = Client.SanitizeName(inc.ReadString());
|
||||
int ownerKey = inc.ReadInt32();
|
||||
UInt64 steamId = inc.ReadUInt64();
|
||||
UInt16 ticketLength = inc.ReadUInt16();
|
||||
byte[] ticketBytes = inc.ReadBytes(ticketLength);
|
||||
|
||||
if (!Client.IsValidName(name, serverSettings))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
string version = inc.ReadString();
|
||||
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
|
||||
if (!isCompatibleVersion)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
|
||||
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
|
||||
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
|
||||
DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
string language = inc.ReadString();
|
||||
pendingClient.Connection.Language = language;
|
||||
|
||||
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
|
||||
if (nameTaken != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pendingClient.AuthSessionStarted)
|
||||
{
|
||||
ProcessAuthTicket(name, ownerKey, steamId, pendingClient, ticketBytes);
|
||||
}
|
||||
break;
|
||||
case ConnectionInitialization.Password:
|
||||
int pwLength = inc.ReadByte();
|
||||
byte[] incPassword = inc.ReadBytes(pwLength);
|
||||
if (pendingClient.PasswordSalt == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Received password message from client without salt");
|
||||
return;
|
||||
}
|
||||
if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
|
||||
{
|
||||
pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingClient.Retries++;
|
||||
if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
|
||||
{
|
||||
string banMsg = "Failed to enter correct password too many times";
|
||||
BanPendingClient(pendingClient, banMsg, null);
|
||||
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
break;
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
pendingClient.InitializationStep = ConnectionInitialization.Success;
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket);
|
||||
|
||||
protected void BanPendingClient(PendingClient pendingClient, string banReason, TimeSpan? duration)
|
||||
{
|
||||
if (pendingClient.Connection is LidgrenConnection l)
|
||||
{
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, l.NetConnection.RemoteEndPoint.Address, banReason, duration);
|
||||
}
|
||||
else if (pendingClient.Connection is SteamP2PConnection s)
|
||||
{
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, s.SteamID, banReason, duration);
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsPendingClientBanned(PendingClient pendingClient, out string banReason)
|
||||
{
|
||||
if (pendingClient.Connection is LidgrenConnection l)
|
||||
{
|
||||
return serverSettings.BanList.IsBanned(l.NetConnection.RemoteEndPoint.Address, out banReason);
|
||||
}
|
||||
else if (pendingClient.Connection is SteamP2PConnection s)
|
||||
{
|
||||
return serverSettings.BanList.IsBanned(s.SteamID, out banReason);
|
||||
}
|
||||
banReason = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg);
|
||||
|
||||
protected void UpdatePendingClient(PendingClient pendingClient)
|
||||
{
|
||||
if (IsPendingClientBanned(pendingClient, out string banReason))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectedClients.Count >= serverSettings.MaxPlayers - 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
|
||||
}
|
||||
|
||||
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
|
||||
{
|
||||
NetworkConnection newConnection = pendingClient.Connection;
|
||||
connectedClients.Add(newConnection);
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
CheckOwnership(pendingClient);
|
||||
|
||||
OnInitializationComplete?.Invoke(newConnection);
|
||||
}
|
||||
|
||||
pendingClient.TimeOut -= Timing.Step;
|
||||
if (pendingClient.TimeOut < 0.0)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
}
|
||||
|
||||
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
|
||||
pendingClient.UpdateTime = Timing.TotalTime + 1.0;
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep |
|
||||
PacketHeader.IsServerMessage));
|
||||
outMsg.Write((byte)pendingClient.InitializationStep);
|
||||
switch (pendingClient.InitializationStep)
|
||||
{
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
outMsg.Write(GameMain.Server.ServerName);
|
||||
|
||||
var mpContentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
|
||||
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count);
|
||||
for (int i = 0; i < mpContentPackages.Count; i++)
|
||||
{
|
||||
outMsg.Write(mpContentPackages[i].Name);
|
||||
outMsg.Write(mpContentPackages[i].MD5hash.Hash);
|
||||
outMsg.Write(mpContentPackages[i].SteamWorkshopId);
|
||||
}
|
||||
break;
|
||||
case ConnectionInitialization.Password:
|
||||
outMsg.Write(pendingClient.PasswordSalt == null); outMsg.WritePadBits();
|
||||
if (pendingClient.PasswordSalt == null)
|
||||
{
|
||||
pendingClient.PasswordSalt = Lidgren.Network.CryptoRandom.Instance.Next();
|
||||
outMsg.Write(pendingClient.PasswordSalt.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
outMsg.Write(pendingClient.Retries);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
SendMsgInternal(pendingClient.Connection, DeliveryMethod.Reliable, outMsg);
|
||||
}
|
||||
|
||||
protected virtual void CheckOwnership(PendingClient pendingClient) { }
|
||||
|
||||
protected void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
|
||||
{
|
||||
if (pendingClients.Contains(pendingClient))
|
||||
{
|
||||
Disconnect(pendingClient.Connection, reason + "/" + msg);
|
||||
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
if (pendingClient.AuthSessionStarted)
|
||||
{
|
||||
Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value);
|
||||
pendingClient.SteamID = null;
|
||||
pendingClient.AuthSessionStarted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod);
|
||||
public abstract void Disconnect(NetworkConnection conn, string msg = null);
|
||||
|
||||
+29
-280
@@ -10,50 +10,17 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
private bool started;
|
||||
|
||||
private ServerSettings serverSettings;
|
||||
|
||||
public UInt64 OwnerSteamID
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private class PendingClient
|
||||
{
|
||||
public string Name;
|
||||
public ConnectionInitialization InitializationStep;
|
||||
public double UpdateTime;
|
||||
public double TimeOut;
|
||||
public int Retries;
|
||||
public UInt64 SteamID;
|
||||
public Int32? PasswordSalt;
|
||||
public bool AuthSessionStarted;
|
||||
|
||||
public PendingClient(UInt64 steamId)
|
||||
{
|
||||
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
|
||||
Retries = 0;
|
||||
SteamID = steamId;
|
||||
PasswordSalt = null;
|
||||
UpdateTime = Timing.TotalTime+Timing.Step*3.0;
|
||||
TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
AuthSessionStarted = false;
|
||||
}
|
||||
|
||||
public void Heartbeat()
|
||||
{
|
||||
TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
private List<SteamP2PConnection> connectedClients;
|
||||
private List<PendingClient> pendingClients;
|
||||
|
||||
public SteamP2PServerPeer(UInt64 steamId, ServerSettings settings)
|
||||
{
|
||||
serverSettings = settings;
|
||||
|
||||
connectedClients = new List<SteamP2PConnection>();
|
||||
connectedClients = new List<NetworkConnection>();
|
||||
pendingClients = new List<PendingClient>();
|
||||
|
||||
ownerKey = null;
|
||||
@@ -114,10 +81,11 @@ namespace Barotrauma.Networking
|
||||
//backwards for loop so we can remove elements while iterating
|
||||
for (int i = connectedClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
connectedClients[i].Decay(deltaTime);
|
||||
if (connectedClients[i].Timeout < 0.0)
|
||||
SteamP2PConnection conn = connectedClients[i] as SteamP2PConnection;
|
||||
conn.Decay(deltaTime);
|
||||
if (conn.Timeout < 0.0)
|
||||
{
|
||||
Disconnect(connectedClients[i], "Timed out");
|
||||
Disconnect(conn, "Timed out");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +140,7 @@ namespace Barotrauma.Networking
|
||||
if (senderSteamId != OwnerSteamID) //sender is remote, handle disconnects and heartbeats
|
||||
{
|
||||
PendingClient pendingClient = pendingClients.Find(c => c.SteamID == senderSteamId);
|
||||
SteamP2PConnection connectedClient = connectedClients.Find(c => c.SteamID == senderSteamId);
|
||||
SteamP2PConnection connectedClient = connectedClients.Find(c => c.SteamID == senderSteamId) as SteamP2PConnection;
|
||||
|
||||
pendingClient?.Heartbeat();
|
||||
connectedClient?.Heartbeat();
|
||||
@@ -210,6 +178,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else if (isConnectionInitializationStep)
|
||||
{
|
||||
|
||||
if (pendingClient != null)
|
||||
{
|
||||
ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null));
|
||||
@@ -219,7 +188,7 @@ namespace Barotrauma.Networking
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
|
||||
if (initializationStep == ConnectionInitialization.ConnectionStarted)
|
||||
{
|
||||
pendingClients.Add(new PendingClient(senderSteamId));
|
||||
pendingClients.Add(new PendingClient(new SteamP2PConnection("PENDING", senderSteamId)) { SteamID = senderSteamId });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,7 +221,7 @@ namespace Barotrauma.Networking
|
||||
string ownerName = inc.ReadString();
|
||||
OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID)
|
||||
{
|
||||
Status = NetworkConnectionStatus.Connected
|
||||
Language = GameMain.Config.Language
|
||||
};
|
||||
|
||||
OnInitializationComplete?.Invoke(OwnerConnection);
|
||||
@@ -273,246 +242,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
|
||||
|
||||
//DebugConsole.NewMessage(initializationStep+" "+pendingClient.InitializationStep);
|
||||
|
||||
if (pendingClient.InitializationStep != initializationStep) return;
|
||||
|
||||
pendingClient.UpdateTime = Timing.TotalTime+Timing.Step;
|
||||
|
||||
switch (initializationStep)
|
||||
{
|
||||
case ConnectionInitialization.SteamTicketAndVersion:
|
||||
string name = Client.SanitizeName(inc.ReadString());
|
||||
UInt64 steamId = inc.ReadUInt64();
|
||||
UInt16 ticketLength = inc.ReadUInt16();
|
||||
inc.BitPosition += ticketLength * 8; //skip ticket, owner handles steam authentication
|
||||
|
||||
if (!Client.IsValidName(name, serverSettings))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
string version = inc.ReadString();
|
||||
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
|
||||
if (!isCompatibleVersion)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
|
||||
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
|
||||
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
|
||||
DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
|
||||
if (nameTaken != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
int contentPackageCount = (int)inc.ReadVariableUInt32();
|
||||
List<ClientContentPackage> clientContentPackages = new List<ClientContentPackage>();
|
||||
for (int i = 0; i < contentPackageCount; i++)
|
||||
{
|
||||
string packageName = inc.ReadString();
|
||||
string packageHash = inc.ReadString();
|
||||
clientContentPackages.Add(new ClientContentPackage(packageName, packageHash));
|
||||
}
|
||||
|
||||
//check if the client is missing any of our packages
|
||||
List<ContentPackage> missingPackages = new List<ContentPackage>();
|
||||
foreach (ContentPackage serverContentPackage in GameMain.SelectedPackages)
|
||||
{
|
||||
if (!serverContentPackage.HasMultiplayerIncompatibleContent) continue;
|
||||
bool packageFound = clientContentPackages.Any(cp => cp.Name == serverContentPackage.Name && cp.Hash == serverContentPackage.MD5hash.Hash);
|
||||
if (!packageFound) { missingPackages.Add(serverContentPackage); }
|
||||
}
|
||||
|
||||
//check if the client is using packages we don't have
|
||||
List<ClientContentPackage> redundantPackages = new List<ClientContentPackage>();
|
||||
foreach (ClientContentPackage clientContentPackage in clientContentPackages)
|
||||
{
|
||||
bool packageFound = GameMain.SelectedPackages.Any(cp => cp.Name == clientContentPackage.Name && cp.MD5hash.Hash == clientContentPackage.Hash);
|
||||
if (!packageFound) { redundantPackages.Add(clientContentPackage); }
|
||||
}
|
||||
|
||||
if (missingPackages.Count == 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
|
||||
$"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID + ") 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)));
|
||||
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
|
||||
$"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
if (redundantPackages.Count == 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
|
||||
$"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr(redundantPackages[0])}");
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using an incompatible content package " + GetPackageStr(redundantPackages[0]) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
if (redundantPackages.Count > 1)
|
||||
{
|
||||
List<string> packageStrs = new List<string>();
|
||||
redundantPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
|
||||
RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
|
||||
$"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pendingClient.AuthSessionStarted)
|
||||
{
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
|
||||
pendingClient.Name = name;
|
||||
pendingClient.AuthSessionStarted = true;
|
||||
}
|
||||
break;
|
||||
case ConnectionInitialization.Password:
|
||||
int pwLength = inc.ReadByte();
|
||||
byte[] incPassword = inc.ReadBytes(pwLength);
|
||||
if (pendingClient.PasswordSalt == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Received password message from client without salt");
|
||||
return;
|
||||
}
|
||||
if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
|
||||
{
|
||||
pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingClient.Retries++;
|
||||
if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
|
||||
{
|
||||
string banMsg = "Failed to enter correct password too many times";
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID, banMsg, null);
|
||||
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
break;
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
pendingClient.InitializationStep = ConnectionInitialization.Success;
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void UpdatePendingClient(PendingClient pendingClient)
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
if (serverSettings.BanList.IsBanned(pendingClient.SteamID, out string banReason))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
|
||||
return;
|
||||
}
|
||||
|
||||
//DebugConsole.NewMessage("pending client status: " + pendingClient.InitializationStep);
|
||||
|
||||
if (connectedClients.Count >= serverSettings.MaxPlayers - 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
|
||||
}
|
||||
|
||||
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
|
||||
{
|
||||
SteamP2PConnection newConnection = new SteamP2PConnection(pendingClient.Name, pendingClient.SteamID)
|
||||
{
|
||||
Status = NetworkConnectionStatus.Connected
|
||||
};
|
||||
connectedClients.Add(newConnection);
|
||||
pendingClients.Remove(pendingClient);
|
||||
OnInitializationComplete?.Invoke(newConnection);
|
||||
}
|
||||
|
||||
pendingClient.TimeOut -= Timing.Step;
|
||||
if (pendingClient.TimeOut < 0.0)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
}
|
||||
|
||||
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
|
||||
pendingClient.UpdateTime = Timing.TotalTime + 1.0;
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write(pendingClient.SteamID);
|
||||
outMsg.Write((byte)DeliveryMethod.Reliable);
|
||||
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep |
|
||||
PacketHeader.IsServerMessage));
|
||||
outMsg.Write((byte)pendingClient.InitializationStep);
|
||||
switch (pendingClient.InitializationStep)
|
||||
{
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
var mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
|
||||
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count);
|
||||
for (int i = 0; i < mpContentPackages.Count; i++)
|
||||
{
|
||||
outMsg.Write(mpContentPackages[i].MD5hash.Hash);
|
||||
}
|
||||
break;
|
||||
case ConnectionInitialization.Password:
|
||||
outMsg.Write(pendingClient.PasswordSalt == null); outMsg.WritePadBits();
|
||||
if (pendingClient.PasswordSalt == null)
|
||||
{
|
||||
pendingClient.PasswordSalt = Lidgren.Network.CryptoRandom.Instance.Next();
|
||||
outMsg.Write(pendingClient.PasswordSalt.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
outMsg.Write(pendingClient.Retries);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] msgToSend = (byte[])outMsg.Buffer.Clone();
|
||||
Array.Resize(ref msgToSend, outMsg.LengthBytes);
|
||||
ChildServerRelay.Write(msgToSend);
|
||||
}
|
||||
|
||||
private void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
if (pendingClients.Contains(pendingClient))
|
||||
{
|
||||
SendDisconnectMessage(pendingClient.SteamID, reason + "/" + msg);
|
||||
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
if (pendingClient.AuthSessionStarted)
|
||||
{
|
||||
Steam.SteamManager.StopAuthSession(pendingClient.SteamID);
|
||||
pendingClient.SteamID = 0;
|
||||
pendingClient.AuthSessionStarted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeSteamServerCallbacks()
|
||||
{
|
||||
throw new InvalidOperationException("Called InitializeSteamServerCallbacks on SteamP2PServerPeer!");
|
||||
@@ -582,5 +311,25 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
Disconnect(conn, msg, true);
|
||||
}
|
||||
|
||||
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
|
||||
{
|
||||
IWriteMessage msgToSend = new WriteOnlyMessage();
|
||||
msgToSend.Write(conn.SteamID);
|
||||
msgToSend.Write((byte)deliveryMethod);
|
||||
msgToSend.Write(msg.Buffer, 0, msg.LengthBytes);
|
||||
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
|
||||
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
|
||||
ChildServerRelay.Write(bufToSend);
|
||||
}
|
||||
|
||||
protected override void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket)
|
||||
{
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
|
||||
pendingClient.Connection.Name = name;
|
||||
pendingClient.Name = name;
|
||||
pendingClient.AuthSessionStarted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -9,12 +8,27 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
partial class RespawnManager : Entity, IServerSerializable
|
||||
{
|
||||
private List<Client> GetClientsToRespawn()
|
||||
private DateTime despawnTime;
|
||||
|
||||
private IEnumerable<Client> GetClientsToRespawn()
|
||||
{
|
||||
return networkMember.ConnectedClients.FindAll(c =>
|
||||
c.InGame &&
|
||||
(!c.SpectateOnly || (!GameMain.Server.ServerSettings.AllowSpectating && GameMain.Server.OwnerConnection != c.Connection)) &&
|
||||
(c.Character == null || c.Character.IsDead));
|
||||
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
|
||||
foreach (Client c in networkMember.ConnectedClients)
|
||||
{
|
||||
if (!c.InGame) { continue; }
|
||||
if (c.SpectateOnly && (GameMain.Server.ServerSettings.AllowSpectating || GameMain.Server.OwnerConnection == c.Connection)) { continue; }
|
||||
if (c.Character != null && !c.Character.IsDead) { continue; }
|
||||
|
||||
//don't allow respawning if the client has previously disconnected and their corpse is still present on the server
|
||||
var matchingData = campaign?.GetClientCharacterData(c);
|
||||
if (matchingData != null && matchingData.HasSpawned &&
|
||||
Character.CharacterList.Any(c => c.Info == matchingData.CharacterInfo && c.CauseOfDeath?.Type == CauseOfDeathType.Disconnected))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return c;
|
||||
}
|
||||
}
|
||||
|
||||
private List<CharacterInfo> GetBotsToRespawn()
|
||||
@@ -56,7 +70,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
private bool RespawnPending()
|
||||
{
|
||||
int characterToRespawnCount = GetClientsToRespawn().Count;
|
||||
int characterToRespawnCount = GetClientsToRespawn().Count();
|
||||
int totalCharacterCount = GameMain.Server.ConnectedClients.Count;
|
||||
return (float)characterToRespawnCount >= Math.Max((float)totalCharacterCount * GameMain.Server.ServerSettings.MinRespawnRatio, 1.0f);
|
||||
}
|
||||
@@ -82,15 +96,9 @@ namespace Barotrauma.Networking
|
||||
if (RespawnShuttle == null) { return; }
|
||||
|
||||
RespawnShuttle.Velocity = Vector2.Zero;
|
||||
|
||||
if (shuttleSteering != null)
|
||||
{
|
||||
shuttleSteering.AutoPilot = false;
|
||||
shuttleSteering.MaintainPos = false;
|
||||
}
|
||||
}
|
||||
|
||||
partial void DispatchShuttle()
|
||||
private void DispatchShuttle()
|
||||
{
|
||||
if (RespawnShuttle != null)
|
||||
{
|
||||
@@ -118,13 +126,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
RespawnShuttle.SetPosition(spawnPos);
|
||||
RespawnShuttle.Velocity = Vector2.Zero;
|
||||
if (shuttleSteering != null)
|
||||
{
|
||||
shuttleSteering.AutoPilot = true;
|
||||
shuttleSteering.MaintainPos = true;
|
||||
shuttleSteering.PosToMaintain = RespawnShuttle.WorldPosition;
|
||||
shuttleSteering.UnsentChanges = true;
|
||||
}
|
||||
RespawnShuttle.NeutralizeBallast();
|
||||
RespawnShuttle.EnableMaintainPosition();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -219,12 +222,20 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
var respawnSub = RespawnShuttle ?? Submarine.MainSub;
|
||||
|
||||
var clients = GetClientsToRespawn();
|
||||
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
|
||||
|
||||
var clients = GetClientsToRespawn().ToList();
|
||||
foreach (Client c in clients)
|
||||
{
|
||||
//get rid of the existing character
|
||||
c.Character?.DespawnNow();
|
||||
|
||||
var matchingData = campaign?.GetClientCharacterData(c);
|
||||
if (matchingData != null && !matchingData.HasSpawned)
|
||||
{
|
||||
c.CharacterInfo = matchingData.CharacterInfo;
|
||||
}
|
||||
|
||||
//all characters are in Team 1 in game modes/missions with only one team.
|
||||
//if at some point we add a game mode with multiple teams where respawning is possible, this needs to be reworked
|
||||
c.TeamID = Character.TeamType.Team1;
|
||||
@@ -238,7 +249,10 @@ namespace Barotrauma.Networking
|
||||
GameMain.Server.AssignJobs(clients);
|
||||
foreach (Client c in clients)
|
||||
{
|
||||
c.CharacterInfo.Job = new Job(c.AssignedJob.First, c.AssignedJob.Second);
|
||||
if (campaign?.GetClientCharacterData(c) == null || c.CharacterInfo.Job == null)
|
||||
{
|
||||
c.CharacterInfo.Job = new Job(c.AssignedJob.First, c.AssignedJob.Second);
|
||||
}
|
||||
}
|
||||
|
||||
//the spawnpoints where the characters will spawn
|
||||
@@ -314,19 +328,37 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
//give the character the items they would've gotten if they had spawned in the main sub
|
||||
character.GiveJobItems(mainSubSpawnPoints[i]);
|
||||
var characterData = campaign?.GetClientCharacterData(clients[i]);
|
||||
if (characterData == null || characterData.HasSpawned)
|
||||
{
|
||||
//give the character the items they would've gotten if they had spawned in the main sub
|
||||
character.GiveJobItems(mainSubSpawnPoints[i]);
|
||||
if (campaign != null)
|
||||
{
|
||||
characterData = campaign.SetClientCharacterData(clients[i]);
|
||||
characterData.HasSpawned = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
characterData.SpawnInventoryItems(character.Info, character.Inventory);
|
||||
characterData.ApplyHealthData(character.Info, character);
|
||||
character.GiveIdCardTags(mainSubSpawnPoints[i]);
|
||||
characterData.HasSpawned = true;
|
||||
}
|
||||
|
||||
//add the ID card tags they should've gotten when spawning in the shuttle
|
||||
foreach (Item item in character.Inventory.Items)
|
||||
{
|
||||
if (item == null || item.Prefab.Identifier != "idcard") continue;
|
||||
if (item == null || item.Prefab.Identifier != "idcard") { continue; }
|
||||
foreach (string s in shuttleSpawnPoints[i].IdCardTags)
|
||||
{
|
||||
item.AddTag(s);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(shuttleSpawnPoints[i].IdCardDesc))
|
||||
{
|
||||
item.Description = shuttleSpawnPoints[i].IdCardDesc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ namespace Barotrauma.Steam
|
||||
|
||||
private static void InitializeProjectSpecific() { isInitialized = true; }
|
||||
|
||||
private static void UpdateProjectSpecific(float deltaTime) { }
|
||||
|
||||
public static bool CreateServer(Networking.GameServer server, bool isPublic)
|
||||
{
|
||||
isInitialized = true;
|
||||
@@ -17,8 +15,8 @@ namespace Barotrauma.Steam
|
||||
Steamworks.SteamServerInit options = new Steamworks.SteamServerInit("Barotrauma", "Barotrauma")
|
||||
{
|
||||
GamePort = (ushort)server.Port,
|
||||
QueryPort = (ushort)server.QueryPort,
|
||||
Secure = false
|
||||
QueryPort = isPublic ? (ushort)server.QueryPort : (ushort)0,
|
||||
Mode = isPublic ? Steamworks.InitServerMode.Authentication : Steamworks.InitServerMode.NoAuthentication
|
||||
};
|
||||
//options.QueryShareGamePort();
|
||||
|
||||
@@ -46,7 +44,7 @@ namespace Barotrauma.Steam
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentPackages = GameMain.Config.SelectedContentPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
|
||||
var contentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
|
||||
|
||||
// These server state variables may be changed at any time. Note that there is no longer a mechanism
|
||||
// to send the player count. The player count is maintained by steam and you should use the player
|
||||
@@ -55,12 +53,13 @@ namespace Barotrauma.Steam
|
||||
Steamworks.SteamServer.MaxPlayers = server.ServerSettings.MaxPlayers;
|
||||
Steamworks.SteamServer.Passworded = server.ServerSettings.HasPassword;
|
||||
Steamworks.SteamServer.MapName = GameMain.NetLobbyScreen?.SelectedSub?.DisplayName ?? "";
|
||||
Steamworks.SteamServer.SetKey("haspassword", server.ServerSettings.HasPassword.ToString());
|
||||
Steamworks.SteamServer.SetKey("message", GameMain.Server.ServerSettings.ServerMessageText);
|
||||
Steamworks.SteamServer.SetKey("version", GameMain.Version.ToString());
|
||||
Steamworks.SteamServer.SetKey("playercount", GameMain.Server.ConnectedClients.Count.ToString());
|
||||
Steamworks.SteamServer.SetKey("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
|
||||
Steamworks.SteamServer.SetKey("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.MD5hash.Hash)));
|
||||
Steamworks.SteamServer.SetKey("contentpackageurl", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopUrl ?? "")));
|
||||
Steamworks.SteamServer.SetKey("contentpackageid", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopId)));
|
||||
Steamworks.SteamServer.SetKey("usingwhitelist", (server.ServerSettings.Whitelist != null && server.ServerSettings.Whitelist.Enabled).ToString());
|
||||
Steamworks.SteamServer.SetKey("modeselectionmode", server.ServerSettings.ModeSelectionMode.ToString());
|
||||
Steamworks.SteamServer.SetKey("subselectionmode", server.ServerSettings.SubSelectionMode.ToString());
|
||||
@@ -70,6 +69,7 @@ namespace Barotrauma.Steam
|
||||
Steamworks.SteamServer.SetKey("traitors", server.ServerSettings.TraitorsEnabled.ToString());
|
||||
Steamworks.SteamServer.SetKey("gamestarted", server.GameStarted.ToString());
|
||||
Steamworks.SteamServer.SetKey("gamemode", server.ServerSettings.GameModeIdentifier);
|
||||
Steamworks.SteamServer.SetKey("playstyle", server.ServerSettings.PlayStyle.ToString());
|
||||
|
||||
Steamworks.SteamServer.DedicatedServer = true;
|
||||
|
||||
|
||||
@@ -82,20 +82,18 @@ namespace Barotrauma
|
||||
switch (voteType)
|
||||
{
|
||||
case VoteType.Sub:
|
||||
string subName = inc.ReadString();
|
||||
SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
|
||||
int equalityCheckVal = inc.ReadInt32();
|
||||
SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == equalityCheckVal);
|
||||
sender.SetVote(voteType, sub);
|
||||
break;
|
||||
|
||||
case VoteType.Mode:
|
||||
string modeIdentifier = inc.ReadString();
|
||||
GameModePreset mode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier);
|
||||
if (!mode.Votable) break;
|
||||
|
||||
if (!mode.Votable) { break; }
|
||||
sender.SetVote(voteType, mode);
|
||||
break;
|
||||
case VoteType.EndRound:
|
||||
if (!sender.HasSpawned) return;
|
||||
if (!sender.HasSpawned) { return; }
|
||||
sender.SetVote(voteType, inc.ReadBoolean());
|
||||
|
||||
GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound));
|
||||
|
||||
@@ -43,6 +43,8 @@ namespace Barotrauma
|
||||
Console.WriteLine("Barotrauma Dedicated Server " + GameMain.Version +
|
||||
" (" + AssemblyInfo.GetBuildString() + ", branch " + AssemblyInfo.GetGitBranch() + ", revision " + AssemblyInfo.GetGitRevision() + ")");
|
||||
|
||||
string executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
|
||||
Directory.SetCurrentDirectory(executableDir);
|
||||
Game = new GameMain(args);
|
||||
|
||||
Game.Run();
|
||||
@@ -102,9 +104,9 @@ namespace Barotrauma
|
||||
{
|
||||
sb.AppendLine("Language: " + (GameMain.Config.Language ?? "none"));
|
||||
}
|
||||
if (GameMain.SelectedPackages != null)
|
||||
if (GameMain.Config.AllEnabledPackages != null)
|
||||
{
|
||||
sb.AppendLine("Selected content packages: " + (!GameMain.SelectedPackages.Any() ? "None" : string.Join(", ", GameMain.SelectedPackages.Select(c => c.Name))));
|
||||
sb.AppendLine("Selected content packages: " + (!GameMain.Config.AllEnabledPackages.Any() ? "None" : string.Join(", ", GameMain.Config.AllEnabledPackages.Select(c => c.Name))));
|
||||
}
|
||||
sb.AppendLine("Level seed: " + ((Level.Loaded == null) ? "no level loaded" : Level.Loaded.Seed));
|
||||
sb.AppendLine("Loaded submarine: " + ((Submarine.MainSub == null) ? "None" : Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash + ")"));
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace Barotrauma
|
||||
|
||||
private bool WereAllTargetsInfected()
|
||||
{
|
||||
if (targetWasInfected == null) { return false; }
|
||||
|
||||
for (int i = 0; i < targetWasInfected.Length; i++)
|
||||
{
|
||||
if (targetWasInfected[i]) continue;
|
||||
@@ -52,8 +54,12 @@ namespace Barotrauma
|
||||
poisonName = TextManager.FormatServerMessage(poisonId) ?? poisonId;
|
||||
|
||||
Targets = traitor.Mission.FindKillTarget(traitor.Character, Filter, targetCount, targetPercentage);
|
||||
if (Targets == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
targetWasInfected = new bool[Targets.Count];
|
||||
return Targets != null && !Targets.All(t => t.IsDead);
|
||||
return !Targets.All(t => t.IsDead);
|
||||
}
|
||||
|
||||
public GoalInjectTarget(TraitorMission.CharacterFilter filter, string poisonId, string afflictionId, int targetCount, float targetPercentage) : base()
|
||||
|
||||
@@ -137,6 +137,10 @@ namespace Barotrauma
|
||||
startCountdown = MathHelper.Lerp(server.ServerSettings.TraitorsMinRestartDelay, server.ServerSettings.TraitorsMaxRestartDelay, (float)RandomDouble());
|
||||
return;
|
||||
}
|
||||
if (Character.CharacterList.Count(c => !c.IsDead && c.TeamID == Character.TeamType.Team1 || c.TeamID == Character.TeamType.Team2) <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (GameMain.GameSession.Mission is CombatMission)
|
||||
{
|
||||
var teamIds = new[] { Character.TeamType.Team1, Character.TeamType.Team2 };
|
||||
|
||||
Reference in New Issue
Block a user