Merge branch 'dev' of https://github.com/Regalis11/Barotrauma.git into unstable-tests
This commit is contained in:
@@ -56,9 +56,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (Type.HasFlag(ChatMessageType.Server) || Type.HasFlag(ChatMessageType.Error) || Type.HasFlag(ChatMessageType.ServerLog))
|
||||
{
|
||||
if (translatedText == null || translatedText.Length == 0)
|
||||
if (translatedText.IsNullOrEmpty())
|
||||
{
|
||||
translatedText = TextManager.GetServerMessage(Text);
|
||||
translatedText = TextManager.GetServerMessage(Text).Value;
|
||||
}
|
||||
|
||||
return translatedText;
|
||||
@@ -214,8 +214,12 @@ namespace Barotrauma.Networking
|
||||
public static string ApplyDistanceEffect(string message, ChatMessageType type, Character sender, Character receiver)
|
||||
{
|
||||
if (sender == null) { return ""; }
|
||||
|
||||
string spokenMsg = ApplyDistanceEffect(receiver, sender, message, SpeakRange * (1.0f - sender.SpeechImpediment / 100.0f), 3.0f);
|
||||
float range = SpeakRange;
|
||||
if (type == ChatMessageType.Default && sender.SpeechImpediment > 0)
|
||||
{
|
||||
range *= 1.0f - sender.SpeechImpediment / 100.0f;
|
||||
}
|
||||
string spokenMsg = ApplyDistanceEffect(receiver, sender, message, range, 3.0f);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.Extensions;
|
||||
@@ -18,6 +19,13 @@ namespace Barotrauma.Networking
|
||||
private static PipeType writeStream;
|
||||
private static PipeType readStream;
|
||||
|
||||
private enum WriteStatus : byte
|
||||
{
|
||||
Success = 0x00,
|
||||
Heartbeat = 0x01,
|
||||
Crash = 0xFF
|
||||
}
|
||||
|
||||
private static ManualResetEvent writeManualResetEvent;
|
||||
|
||||
private static volatile bool shutDown;
|
||||
@@ -29,6 +37,8 @@ namespace Barotrauma.Networking
|
||||
private static int readIncTotal;
|
||||
|
||||
private static ConcurrentQueue<byte[]> msgsToWrite;
|
||||
private static ConcurrentQueue<string> errorsToWrite;
|
||||
|
||||
private static ConcurrentQueue<byte[]> msgsToRead;
|
||||
|
||||
private static Thread readThread;
|
||||
@@ -44,6 +54,8 @@ namespace Barotrauma.Networking
|
||||
readTempBytes = new byte[ReadBufferSize];
|
||||
|
||||
msgsToWrite = new ConcurrentQueue<byte[]>();
|
||||
errorsToWrite = new ConcurrentQueue<string>();
|
||||
|
||||
msgsToRead = new ConcurrentQueue<byte[]>();
|
||||
|
||||
shutDown = false;
|
||||
@@ -127,9 +139,11 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
static partial void HandleCrashString(string str);
|
||||
|
||||
private static void UpdateRead()
|
||||
{
|
||||
Span<byte> msgLengthSpan = stackalloc byte[2];
|
||||
Span<byte> msgLengthSpan = stackalloc byte[3];
|
||||
while (!shutDown)
|
||||
{
|
||||
CheckPipeConnected(nameof(readStream), readStream);
|
||||
@@ -154,13 +168,26 @@ namespace Barotrauma.Networking
|
||||
if (!readBytes(msgLengthSpan)) { shutDown = true; break; }
|
||||
|
||||
int msgLength = msgLengthSpan[0] | (msgLengthSpan[1] << 8);
|
||||
WriteStatus writeStatus = (WriteStatus)msgLengthSpan[2];
|
||||
|
||||
if (msgLength > 0)
|
||||
{
|
||||
byte[] msg = new byte[msgLength];
|
||||
if (!readBytes(msg.AsSpan())) { shutDown = true; break; }
|
||||
|
||||
msgsToRead.Enqueue(msg);
|
||||
switch (writeStatus)
|
||||
{
|
||||
case WriteStatus.Success:
|
||||
msgsToRead.Enqueue(msg);
|
||||
break;
|
||||
case WriteStatus.Heartbeat:
|
||||
//do nothing
|
||||
break;
|
||||
case WriteStatus.Crash:
|
||||
HandleCrashString(Encoding.UTF8.GetString(msg));
|
||||
shutDown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Yield();
|
||||
@@ -173,9 +200,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
CheckPipeConnected(nameof(writeStream), writeStream);
|
||||
|
||||
bool msgAvailable; byte[] msg;
|
||||
byte[] msg;
|
||||
|
||||
void writeMsg()
|
||||
void writeMsg(WriteStatus writeStatus)
|
||||
{
|
||||
// It's SUPER IMPORTANT that this stack allocation
|
||||
// remains in this local function and is never inlined,
|
||||
@@ -183,11 +210,12 @@ namespace Barotrauma.Networking
|
||||
// when the function returns; placing it in the loop
|
||||
// this method is based around would lead to a stack
|
||||
// overflow real quick!
|
||||
Span<byte> bytesToWrite = stackalloc byte[2 + msg.Length];
|
||||
Span<byte> bytesToWrite = stackalloc byte[3 + msg.Length];
|
||||
|
||||
bytesToWrite[0] = (byte)(msg.Length & 0xFF);
|
||||
bytesToWrite[1] = (byte)((msg.Length >> 8) & 0xFF);
|
||||
Span<byte> msgSlice = bytesToWrite.Slice(2, msg.Length);
|
||||
bytesToWrite[2] = (byte)writeStatus;
|
||||
Span<byte> msgSlice = bytesToWrite.Slice(3, msg.Length);
|
||||
|
||||
msg.AsSpan().CopyTo(msgSlice);
|
||||
|
||||
@@ -209,15 +237,20 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
msgAvailable = msgsToWrite.TryDequeue(out msg);
|
||||
while (msgAvailable)
|
||||
while (errorsToWrite.TryDequeue(out var error))
|
||||
{
|
||||
writeMsg();
|
||||
msg = Encoding.UTF8.GetBytes(error);
|
||||
writeMsg(WriteStatus.Crash);
|
||||
shutDown = true;
|
||||
}
|
||||
|
||||
while (msgsToWrite.TryDequeue(out msg))
|
||||
{
|
||||
writeMsg(WriteStatus.Success);
|
||||
|
||||
if (shutDown) { break; }
|
||||
|
||||
msgAvailable = msgsToWrite.TryDequeue(out msg);
|
||||
}
|
||||
|
||||
if (!shutDown)
|
||||
{
|
||||
writeManualResetEvent.Reset();
|
||||
@@ -226,7 +259,7 @@ namespace Barotrauma.Networking
|
||||
if (shutDown) { return; }
|
||||
|
||||
//heartbeat to keep the other end alive
|
||||
msg = Array.Empty<byte>(); writeMsg();
|
||||
msg = Array.Empty<byte>(); writeMsg(WriteStatus.Heartbeat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,25 @@ using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
[NetworkSerialize]
|
||||
struct TempClient : INetSerializableStruct
|
||||
{
|
||||
public string Name;
|
||||
public Identifier PreferredJob;
|
||||
public CharacterTeamType PreferredTeam;
|
||||
public UInt16 NameID;
|
||||
public UInt64 SteamID;
|
||||
public byte ID;
|
||||
public UInt16 CharacterID;
|
||||
public float Karma;
|
||||
public bool Muted;
|
||||
public bool InGame;
|
||||
public bool HasPermissions;
|
||||
public bool IsOwner;
|
||||
public bool AllowKicking;
|
||||
public bool IsDownloading;
|
||||
}
|
||||
|
||||
partial class Client : IDisposable
|
||||
{
|
||||
public const int MaxNameLength = 32;
|
||||
@@ -15,11 +34,11 @@ namespace Barotrauma.Networking
|
||||
public UInt64 SteamID;
|
||||
public UInt64 OwnerSteamID;
|
||||
|
||||
public string Language;
|
||||
public LanguageIdentifier Language;
|
||||
|
||||
public UInt16 Ping;
|
||||
|
||||
public string PreferredJob;
|
||||
public Identifier PreferredJob;
|
||||
|
||||
public CharacterTeamType TeamID;
|
||||
|
||||
@@ -120,7 +139,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasPermissions = false;
|
||||
public bool HasPermissions => Permissions != ClientPermissions.None;
|
||||
|
||||
public VoipQueue VoipQueue
|
||||
{
|
||||
@@ -148,7 +167,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
private List<Client> kickVoters;
|
||||
|
||||
public HashSet<string> GivenAchievements = new HashSet<string>();
|
||||
public HashSet<Identifier> GivenAchievements = new HashSet<Identifier>();
|
||||
|
||||
public ClientPermissions Permissions = ClientPermissions.None;
|
||||
public List<DebugConsole.Command> PermittedConsoleCommands
|
||||
|
||||
@@ -22,27 +22,28 @@ namespace Barotrauma.Networking
|
||||
ManageSettings = 0x200,
|
||||
ManagePermissions = 0x400,
|
||||
KarmaImmunity = 0x800,
|
||||
BuyItems = 0x1000,
|
||||
ManageMoney = 0x1000,
|
||||
SellInventoryItems = 0x2000,
|
||||
SellSubItems = 0x4000,
|
||||
CampaignStore = 0x8000,
|
||||
All = 0xFFFF
|
||||
ManageMap = 0x8000,
|
||||
ManageHires = 0x10000,
|
||||
All = 0x1FFFF
|
||||
}
|
||||
|
||||
class PermissionPreset
|
||||
{
|
||||
public static List<PermissionPreset> List = new List<PermissionPreset>();
|
||||
|
||||
public readonly string Name;
|
||||
public readonly string Description;
|
||||
public readonly LocalizedString Name;
|
||||
public readonly LocalizedString Description;
|
||||
public readonly ClientPermissions Permissions;
|
||||
public readonly List<DebugConsole.Command> PermittedCommands;
|
||||
|
||||
public PermissionPreset(XElement element)
|
||||
{
|
||||
string name = element.GetAttributeString("name", "");
|
||||
Name = TextManager.Get("permissionpresetname." + name, true) ?? name;
|
||||
Description = TextManager.Get("permissionpresetdescription." + name, true) ?? element.GetAttributeString("description", "");
|
||||
Name = TextManager.Get("permissionpresetname." + name).Fallback(name);
|
||||
Description = TextManager.Get("permissionpresetdescription." + name) .Fallback(element.GetAttributeString("description", ""));
|
||||
|
||||
string permissionsStr = element.GetAttributeString("permissions", "");
|
||||
if (!Enum.TryParse(permissionsStr, out Permissions))
|
||||
@@ -53,7 +54,7 @@ namespace Barotrauma.Networking
|
||||
PermittedCommands = new List<DebugConsole.Command>();
|
||||
if (Permissions.HasFlag(ClientPermissions.ConsoleCommands))
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("command", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
string commandName = subElement.GetAttributeString("name", "");
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Steamworks.ServerList;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -117,7 +118,7 @@ namespace Barotrauma
|
||||
|
||||
class CharacterSpawnInfo : IEntitySpawnInfo
|
||||
{
|
||||
public readonly string identifier;
|
||||
public readonly Identifier Identifier;
|
||||
public readonly CharacterInfo CharacterInfo;
|
||||
|
||||
public readonly Vector2 Position;
|
||||
@@ -125,30 +126,32 @@ namespace Barotrauma
|
||||
|
||||
private readonly Action<Character> onSpawned;
|
||||
|
||||
public CharacterSpawnInfo(string identifier, Vector2 worldPosition, Action<Character> onSpawn = null)
|
||||
public CharacterSpawnInfo(Identifier identifier, Vector2 worldPosition, Action<Character> onSpawn = null)
|
||||
{
|
||||
this.identifier = identifier ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null.");
|
||||
this.Identifier = identifier;
|
||||
if (identifier.IsEmpty) { throw new ArgumentException($"{nameof(CharacterSpawnInfo)} identifier cannot be null."); }
|
||||
Position = worldPosition;
|
||||
this.onSpawned = onSpawn;
|
||||
}
|
||||
|
||||
public CharacterSpawnInfo(string identifier, Vector2 position, Submarine sub, Action<Character> onSpawn = null)
|
||||
public CharacterSpawnInfo(Identifier identifier, Vector2 position, Submarine sub, Action<Character> onSpawn = null)
|
||||
{
|
||||
this.identifier = identifier ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null.");
|
||||
this.Identifier = identifier;
|
||||
if (identifier.IsEmpty) { throw new ArgumentException($"{nameof(CharacterSpawnInfo)} identifier cannot be null."); }
|
||||
Position = position;
|
||||
Submarine = sub;
|
||||
this.onSpawned = onSpawn;
|
||||
}
|
||||
|
||||
public CharacterSpawnInfo(string identifier, Vector2 position, CharacterInfo characterInfo, Action<Character> onSpawn = null) : this (identifier, position, onSpawn)
|
||||
public CharacterSpawnInfo(Identifier identifier, Vector2 position, CharacterInfo characterInfo, Action<Character> onSpawn = null) : this (identifier, position, onSpawn)
|
||||
{
|
||||
CharacterInfo = characterInfo;
|
||||
}
|
||||
|
||||
public Entity Spawn()
|
||||
{
|
||||
var character = string.IsNullOrEmpty(identifier) ? null :
|
||||
Character.Create(identifier,
|
||||
var character = Identifier.IsEmpty ? null :
|
||||
Character.Create(Identifier,
|
||||
Submarine == null ? Position : Submarine.Position + Position,
|
||||
ToolBox.RandomSeed(8), CharacterInfo, createNetworkEvent: false);
|
||||
return character;
|
||||
@@ -194,49 +197,60 @@ namespace Barotrauma
|
||||
private readonly Queue<IEntitySpawnInfo> spawnQueue;
|
||||
private readonly Queue<Entity> removeQueue;
|
||||
|
||||
public class SpawnOrRemove
|
||||
public abstract class SpawnOrRemove : NetEntityEvent.IData
|
||||
{
|
||||
public readonly Entity Entity;
|
||||
public UInt16 ID => Entity.ID;
|
||||
|
||||
public readonly UInt16 InventoryID;
|
||||
|
||||
public readonly UInt16 OriginalID, OriginalInventoryID;
|
||||
|
||||
public readonly byte OriginalItemContainerIndex;
|
||||
|
||||
public readonly bool Remove = false;
|
||||
public readonly byte ItemContainerIndex;
|
||||
public readonly int SlotIndex;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
(Remove ? "Remove" : "Spawn") + "(" +
|
||||
"(" +
|
||||
((Entity as MapEntity)?.Name ?? "[NULL]") +
|
||||
$", {OriginalID}, {OriginalInventoryID})";
|
||||
$", {ID}, {InventoryID}, {SlotIndex})";
|
||||
}
|
||||
|
||||
public SpawnOrRemove(Entity entity, bool remove)
|
||||
protected SpawnOrRemove(Entity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
OriginalID = entity.ID;
|
||||
if (entity is Item item && item.ParentInventory?.Owner != null)
|
||||
if (!(entity is Item { ParentInventory: { Owner: { } } } item)) { return; }
|
||||
|
||||
InventoryID = item.ParentInventory.Owner.ID;
|
||||
SlotIndex = item.ParentInventory.FindIndex(item);
|
||||
//find the index of the ItemContainer this item is inside to get the item to
|
||||
//spawn in the correct inventory in multi-inventory items like fabricators
|
||||
if (item.Container == null) { return; }
|
||||
|
||||
foreach (ItemComponent component in item.Container.Components)
|
||||
{
|
||||
OriginalInventoryID = item.ParentInventory.Owner.ID;
|
||||
//find the index of the ItemContainer this item is inside to get the item to
|
||||
//spawn in the correct inventory in multi-inventory items like fabricators
|
||||
if (item.Container != null)
|
||||
if (component is ItemContainer container &&
|
||||
container.Inventory == item.ParentInventory)
|
||||
{
|
||||
foreach (ItemComponent component in item.Container.Components)
|
||||
{
|
||||
if (component is ItemContainer container &&
|
||||
container.Inventory == item.ParentInventory)
|
||||
{
|
||||
OriginalItemContainerIndex = (byte)item.Container.GetComponentIndex(component);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ItemContainerIndex = (byte)item.Container.GetComponentIndex(component);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Remove = remove;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SpawnEntity : SpawnOrRemove
|
||||
{
|
||||
public SpawnEntity(Entity entity) : base(entity) { }
|
||||
public override string ToString()
|
||||
=> $"Spawn {base.ToString()}";
|
||||
}
|
||||
|
||||
public sealed class RemoveEntity : SpawnOrRemove
|
||||
{
|
||||
public RemoveEntity(Entity entity) : base(entity) { }
|
||||
public override string ToString()
|
||||
=> $"Remove {base.ToString()}";
|
||||
}
|
||||
|
||||
public EntitySpawner()
|
||||
: base(null, Entity.EntitySpawnerID)
|
||||
@@ -250,7 +264,7 @@ namespace Barotrauma
|
||||
return "EntitySpawner";
|
||||
}
|
||||
|
||||
public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 worldPosition, float? condition = null, int? quality = null, Action<Item> onSpawned = null)
|
||||
public void AddItemToSpawnQueue(ItemPrefab itemPrefab, Vector2 worldPosition, float? condition = null, int? quality = null, Action<Item> onSpawned = null)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (itemPrefab == null)
|
||||
@@ -263,7 +277,7 @@ namespace Barotrauma
|
||||
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, onSpawned, condition, quality));
|
||||
}
|
||||
|
||||
public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, Submarine sub, float? condition = null, int? quality = null, Action<Item> onSpawned = null)
|
||||
public void AddItemToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, Submarine sub, float? condition = null, int? quality = null, Action<Item> onSpawned = null)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (itemPrefab == null)
|
||||
@@ -276,7 +290,7 @@ namespace Barotrauma
|
||||
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, onSpawned, condition, quality));
|
||||
}
|
||||
|
||||
public void AddToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, float? condition = null, int? quality = null, Action<Item> onSpawned = null, bool spawnIfInventoryFull = true, bool ignoreLimbSlots = false, InvSlotType slot = InvSlotType.None)
|
||||
public void AddItemToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, float? condition = null, int? quality = null, Action<Item> onSpawned = null, bool spawnIfInventoryFull = true, bool ignoreLimbSlots = false, InvSlotType slot = InvSlotType.None)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (itemPrefab == null)
|
||||
@@ -294,10 +308,10 @@ namespace Barotrauma
|
||||
});
|
||||
}
|
||||
|
||||
public void AddToSpawnQueue(string speciesName, Vector2 worldPosition, Action<Character> onSpawn = null)
|
||||
public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 worldPosition, Action<Character> onSpawn = null)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (string.IsNullOrEmpty(speciesName))
|
||||
if (speciesName.IsEmpty)
|
||||
{
|
||||
string errorMsg = "Attempted to add an empty/null species name to entity spawn queue.\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
@@ -307,10 +321,10 @@ namespace Barotrauma
|
||||
spawnQueue.Enqueue(new CharacterSpawnInfo(speciesName, worldPosition, onSpawn));
|
||||
}
|
||||
|
||||
public void AddToSpawnQueue(string speciesName, Vector2 position, Submarine sub, Action<Character> onSpawn = null)
|
||||
public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 position, Submarine sub, Action<Character> onSpawn = null)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (string.IsNullOrEmpty(speciesName))
|
||||
if (speciesName.IsEmpty)
|
||||
{
|
||||
string errorMsg = "Attempted to add an empty/null species name to entity spawn queue.\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
@@ -320,10 +334,10 @@ namespace Barotrauma
|
||||
spawnQueue.Enqueue(new CharacterSpawnInfo(speciesName, position, sub, onSpawn));
|
||||
}
|
||||
|
||||
public void AddToSpawnQueue(string speciesName, Vector2 worldPosition, CharacterInfo characterInfo, Action<Character> onSpawn = null)
|
||||
public void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 worldPosition, CharacterInfo characterInfo, Action<Character> onSpawn = null)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (string.IsNullOrEmpty(speciesName))
|
||||
if (speciesName.IsEmpty)
|
||||
{
|
||||
string errorMsg = "Attempted to add an empty/null species name to entity spawn queue.\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
@@ -333,10 +347,11 @@ namespace Barotrauma
|
||||
spawnQueue.Enqueue(new CharacterSpawnInfo(speciesName, worldPosition, characterInfo, onSpawn));
|
||||
}
|
||||
|
||||
public void AddToRemoveQueue(Entity entity)
|
||||
public void AddEntityToRemoveQueue(Entity entity)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (removeQueue.Contains(entity) || entity.Removed || entity == null || entity.IdFreed) { return; }
|
||||
if (entity is Item item) { AddItemToRemoveQueue(item); return; }
|
||||
if (entity is Character)
|
||||
{
|
||||
Character character = entity as Character;
|
||||
@@ -352,7 +367,7 @@ namespace Barotrauma
|
||||
removeQueue.Enqueue(entity);
|
||||
}
|
||||
|
||||
public void AddToRemoveQueue(Item item)
|
||||
public void AddItemToRemoveQueue(Item item)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (removeQueue.Contains(item) || item.Removed) { return; }
|
||||
@@ -364,7 +379,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (containedItem != null)
|
||||
{
|
||||
AddToRemoveQueue(containedItem);
|
||||
AddItemToRemoveQueue(containedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,20 +407,19 @@ namespace Barotrauma
|
||||
|
||||
public void Update(bool createNetworkEvents = true)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (GameMain.NetworkMember is { IsClient: true }) { return; }
|
||||
while (spawnQueue.Count > 0)
|
||||
{
|
||||
var entitySpawnInfo = spawnQueue.Dequeue();
|
||||
|
||||
var spawnedEntity = entitySpawnInfo.Spawn();
|
||||
if (spawnedEntity != null)
|
||||
{
|
||||
if (createNetworkEvents)
|
||||
{
|
||||
CreateNetworkEventProjSpecific(spawnedEntity, false);
|
||||
}
|
||||
entitySpawnInfo.OnSpawned(spawnedEntity);
|
||||
if (spawnedEntity == null) { continue; }
|
||||
|
||||
if (createNetworkEvents)
|
||||
{
|
||||
CreateNetworkEventProjSpecific(new SpawnEntity(spawnedEntity));
|
||||
}
|
||||
entitySpawnInfo.OnSpawned(spawnedEntity);
|
||||
}
|
||||
|
||||
while (removeQueue.Count > 0)
|
||||
@@ -417,13 +431,13 @@ namespace Barotrauma
|
||||
}
|
||||
if (createNetworkEvents)
|
||||
{
|
||||
CreateNetworkEventProjSpecific(removedEntity, true);
|
||||
CreateNetworkEventProjSpecific(new RemoveEntity(removedEntity));
|
||||
}
|
||||
removedEntity.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
partial void CreateNetworkEventProjSpecific(Entity entity, bool remove);
|
||||
partial void CreateNetworkEventProjSpecific(SpawnOrRemove spawnOrRemove);
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
|
||||
enum FileTransferType
|
||||
{
|
||||
Submarine, CampaignSave
|
||||
Submarine, CampaignSave, Mod
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,28 +3,41 @@
|
||||
interface INetSerializable { }
|
||||
|
||||
/// <summary>
|
||||
/// Interface for entities that the clients can send information of to the server
|
||||
/// Interface for entities that the clients can send events to the server
|
||||
/// </summary>
|
||||
interface IClientSerializable : INetSerializable
|
||||
{
|
||||
#if CLIENT
|
||||
void ClientWrite(IWriteMessage msg, object[] extraData = null);
|
||||
void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null);
|
||||
#endif
|
||||
#if SERVER
|
||||
void ServerRead(ClientNetObject type, IReadMessage msg, Client c);
|
||||
void ServerEventRead(IReadMessage msg, Client c);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for entities that the server can send information of to the clients
|
||||
/// Interface for entities that the server can send events to the clients
|
||||
/// </summary>
|
||||
interface IServerSerializable : INetSerializable
|
||||
{
|
||||
#if SERVER
|
||||
void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null);
|
||||
void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null);
|
||||
#endif
|
||||
#if CLIENT
|
||||
void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime);
|
||||
void ClientEventRead(IReadMessage msg, float sendingTime);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for entities that handle ServerNetObject.ENTITY_POSITION
|
||||
/// </summary>
|
||||
interface IServerPositionSync : IServerSerializable
|
||||
{
|
||||
#if SERVER
|
||||
void ServerWritePosition(IWriteMessage msg, Client c);
|
||||
#endif
|
||||
#if CLIENT
|
||||
void ClientReadPosition(IReadMessage msg, float sendingTime);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
@@ -42,6 +44,13 @@ namespace Barotrauma
|
||||
public int NumberOfBits = 8;
|
||||
public bool IncludeColorAlpha = false;
|
||||
public int ArrayMaxSize = ushort.MaxValue;
|
||||
|
||||
public readonly int OrderKey;
|
||||
|
||||
public NetworkSerialize([CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
OrderKey = lineNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,6 +61,7 @@ namespace Barotrauma
|
||||
public readonly struct ReadWriteBehavior
|
||||
{
|
||||
public delegate dynamic? ReadDelegate(IReadMessage inc, Type type, NetworkSerialize attribute);
|
||||
|
||||
public delegate void WriteDelegate(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg);
|
||||
|
||||
public readonly ReadDelegate ReadAction;
|
||||
@@ -64,6 +74,58 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct CachedReflectedVariable
|
||||
{
|
||||
public delegate object? GetValueDelegate(object? obj);
|
||||
|
||||
public delegate void SetValueDelegate(object? obj, object? value);
|
||||
|
||||
public readonly Type Type;
|
||||
public readonly ReadWriteBehavior Behavior;
|
||||
public readonly NetworkSerialize Attribute;
|
||||
public readonly SetValueDelegate SetValue;
|
||||
public readonly GetValueDelegate GetValue;
|
||||
public readonly bool HasOwnAttribute;
|
||||
|
||||
public CachedReflectedVariable(MemberInfo info, ReadWriteBehavior behavior, Type baseClassType)
|
||||
{
|
||||
Behavior = behavior;
|
||||
|
||||
switch (info)
|
||||
{
|
||||
case PropertyInfo pi:
|
||||
Type = pi.PropertyType;
|
||||
GetValue = pi.GetValue;
|
||||
SetValue = pi.SetValue;
|
||||
break;
|
||||
case FieldInfo fi:
|
||||
Type = fi.FieldType;
|
||||
GetValue = fi.GetValue;
|
||||
SetValue = fi.SetValue;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Expected {nameof(FieldInfo)} or {nameof(PropertyInfo)} but found {info.GetType()}.", nameof(info));
|
||||
}
|
||||
|
||||
if (info.GetCustomAttribute<NetworkSerialize>() is { } ownAttriute)
|
||||
{
|
||||
HasOwnAttribute = true;
|
||||
Attribute = ownAttriute;
|
||||
}
|
||||
else if (baseClassType.GetCustomAttribute<NetworkSerialize>() is { } globalAttribute)
|
||||
{
|
||||
HasOwnAttribute = false;
|
||||
Attribute = globalAttribute;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to serialize \"{Type}\" in \"{baseClassType}\" because it has no {nameof(NetworkSerialize)} attribute.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, ImmutableArray<CachedReflectedVariable>> CachedVariables = new Dictionary<Type, ImmutableArray<CachedReflectedVariable>>();
|
||||
|
||||
private static readonly ImmutableDictionary<Type, ReadWriteBehavior> TypeBehaviors = new Dictionary<Type, ReadWriteBehavior>
|
||||
{
|
||||
{ typeof(Boolean), new ReadWriteBehavior(ReadBoolean, WriteDynamic) },
|
||||
@@ -77,16 +139,15 @@ namespace Barotrauma
|
||||
{ typeof(Single), new ReadWriteBehavior(ReadSingle, WriteSingle) },
|
||||
{ typeof(Double), new ReadWriteBehavior(ReadDouble, WriteDynamic) },
|
||||
{ typeof(String), new ReadWriteBehavior(ReadString, WriteDynamic) },
|
||||
{ typeof(Identifier), new ReadWriteBehavior(ReadIdentifier, WriteDynamic) },
|
||||
{ typeof(Color), new ReadWriteBehavior(ReadColor, WriteColor) },
|
||||
{ typeof(Vector2), new ReadWriteBehavior(ReadVector2, WriteVector2) }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private static readonly ReadWriteBehavior InvalidReadWriteBehavior = new ReadWriteBehavior(ReadInvalid, WriteInvalid);
|
||||
|
||||
private static readonly ImmutableDictionary<Predicate<Type>, ReadWriteBehavior> TypePredicates = new Dictionary<Predicate<Type>, ReadWriteBehavior>
|
||||
{
|
||||
// Arrays
|
||||
{ type => type.BaseType?.IsAssignableFrom(typeof(Array)) ?? false, new ReadWriteBehavior(ReadArray, WriteArray) },
|
||||
{ type => typeof(Array).IsAssignableFrom(type.BaseType), new ReadWriteBehavior(ReadArray, WriteArray) },
|
||||
|
||||
// Nested INetSerializableStructs
|
||||
{ type => typeof(INetSerializableStruct).IsAssignableFrom(type), new ReadWriteBehavior(ReadINetSerializableStruct, WriteINetSerializableStruct) },
|
||||
@@ -94,13 +155,79 @@ namespace Barotrauma
|
||||
// Enums
|
||||
{ type => type.IsEnum, new ReadWriteBehavior(ReadEnum, WriteEnum) },
|
||||
|
||||
// Nullable / Optional types
|
||||
{ type => Nullable.GetUnderlyingType(type) != null, new ReadWriteBehavior(ReadNullable, WriteNullable) }
|
||||
// Nullable
|
||||
{ type => Nullable.GetUnderlyingType(type) != null, new ReadWriteBehavior(ReadNullable, WriteNullable) },
|
||||
|
||||
// Option
|
||||
{ type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Option<>), new ReadWriteBehavior(ReadOption, WriteOption) }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private static void WriteInvalid(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg) => throw new InvalidOperationException($"Type {obj?.GetType()} cannot be serialized. Did you forget to implement INetSerializableStruct?");
|
||||
private static readonly ReadWriteBehavior InvalidReadWriteBehavior = new ReadWriteBehavior(ReadInvalid, WriteInvalid);
|
||||
|
||||
private static dynamic ReadInvalid(IReadMessage inc, Type type, NetworkSerialize attribute) => throw new InvalidOperationException($"Type {type} cannot be deserialized. Did you forget to implement INetSerializableStruct?");
|
||||
private static readonly Dictionary<Type, MethodInfo> cachedSomeCreateMethods = new Dictionary<Type, MethodInfo>();
|
||||
private static readonly Dictionary<Type, MethodInfo> cachedNoneCreateMethod = new Dictionary<Type, MethodInfo>();
|
||||
|
||||
private static void WriteInvalid(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg) =>
|
||||
throw new SerializationException($"Type {obj?.GetType()} cannot be serialized. Did you forget to implement {nameof(INetSerializableStruct)}?");
|
||||
|
||||
private static dynamic ReadInvalid(IReadMessage inc, Type type, NetworkSerialize attribute) => throw new SerializationException($"Type {type} cannot be deserialized. Did you forget to implement {nameof(INetSerializableStruct)}?");
|
||||
|
||||
private static void WriteOption(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg)
|
||||
{
|
||||
if (obj is null) { throw new ArgumentNullException(nameof(obj), "Tried to write 'null' into a non-nullable type"); }
|
||||
|
||||
Type type = obj.GetType();
|
||||
Type optionType = type.GetGenericTypeDefinition();
|
||||
Type underlyingType = type.GetGenericArguments()[0];
|
||||
|
||||
if (optionType == typeof(None<>))
|
||||
{
|
||||
msg.Write(false);
|
||||
}
|
||||
else if (optionType == typeof(Some<>))
|
||||
{
|
||||
msg.Write(true);
|
||||
if (TryFindBehavior(underlyingType, out ReadWriteBehavior behavior))
|
||||
{
|
||||
behavior.WriteAction(obj.Value, attribute, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(obj), "Option type was neither None or Some");
|
||||
}
|
||||
}
|
||||
|
||||
private static dynamic? ReadOption(IReadMessage inc, Type type, NetworkSerialize attribute)
|
||||
{
|
||||
Type underlyingType = type.GetGenericArguments()[0];
|
||||
bool hasValue = inc.ReadBoolean();
|
||||
if (!hasValue)
|
||||
{
|
||||
return GetCreateMethod(typeof(None<>), underlyingType, cachedNoneCreateMethod).Invoke(null, null);
|
||||
}
|
||||
|
||||
if (TryFindBehavior(underlyingType, out ReadWriteBehavior behavior))
|
||||
{
|
||||
dynamic? value = behavior.ReadAction(inc, underlyingType, attribute);
|
||||
return GetCreateMethod(typeof(Some<>), underlyingType, cachedSomeCreateMethods).Invoke(null, new[] { value });
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Could not find suitable behavior for type {underlyingType} in {nameof(ReadOption)}");
|
||||
|
||||
static MethodInfo GetCreateMethod(Type optionType, Type type, Dictionary<Type, MethodInfo> cache)
|
||||
{
|
||||
if (cache.TryGetValue(type, out MethodInfo? foundInfo))
|
||||
{
|
||||
return foundInfo;
|
||||
}
|
||||
|
||||
Type genericType = optionType.MakeGenericType(type);
|
||||
MethodInfo info = genericType.GetMethod("Create", BindingFlags.Static | BindingFlags.Public)!;
|
||||
cache.Add(type, info);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteNullable(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg)
|
||||
{
|
||||
@@ -152,9 +279,9 @@ namespace Barotrauma
|
||||
Range<int> range = GetEnumRange(type);
|
||||
int enumIndex = inc.ReadRangedInteger(range.Start, range.End);
|
||||
|
||||
foreach (dynamic e in Enum.GetValues(type))
|
||||
foreach (dynamic? e in Enum.GetValues(type))
|
||||
{
|
||||
if (Convert.ChangeType(e, e.GetTypeCode()) == enumIndex) { return e; }
|
||||
if (Convert.ChangeType(e, e!.GetTypeCode()) == enumIndex) { return e; }
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"An enum {type} with value {enumIndex} could not be found in {nameof(ReadEnum)}");
|
||||
@@ -213,9 +340,9 @@ namespace Barotrauma
|
||||
|
||||
msg.WriteRangedInteger(array.Length, 0, attribute.ArrayMaxSize);
|
||||
|
||||
foreach (dynamic o in array)
|
||||
foreach (dynamic? o in array)
|
||||
{
|
||||
if (TryFindBehavior(o.GetType(), out ReadWriteBehavior behavior))
|
||||
if (TryFindBehavior(o!.GetType(), out ReadWriteBehavior behavior))
|
||||
{
|
||||
behavior.WriteAction(o, attribute, msg);
|
||||
}
|
||||
@@ -286,6 +413,8 @@ namespace Barotrauma
|
||||
|
||||
private static dynamic ReadString(IReadMessage inc, Type type, NetworkSerialize attribute) => inc.ReadString();
|
||||
|
||||
private static dynamic ReadIdentifier(IReadMessage inc, Type type, NetworkSerialize attribute) => inc.ReadIdentifier();
|
||||
|
||||
private static dynamic ReadColor(IReadMessage inc, Type type, NetworkSerialize attribute) => attribute.IncludeColorAlpha ? inc.ReadColorR8G8B8A8() : inc.ReadColorR8G8B8();
|
||||
|
||||
private static void WriteColor(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg)
|
||||
@@ -345,7 +474,7 @@ namespace Barotrauma
|
||||
return new Range<int>(values.Min(), values.Max());
|
||||
}
|
||||
|
||||
public static bool TryFindBehavior(Type type, out ReadWriteBehavior behavior)
|
||||
private static bool TryFindBehavior(Type type, out ReadWriteBehavior behavior)
|
||||
{
|
||||
if (TypeBehaviors.TryGetValue(type, out behavior)) { return true; }
|
||||
|
||||
@@ -361,6 +490,46 @@ namespace Barotrauma
|
||||
behavior = InvalidReadWriteBehavior;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ImmutableArray<CachedReflectedVariable> GetPropertiesAndFields(Type type, Type baseClassType)
|
||||
{
|
||||
if (CachedVariables.TryGetValue(type, out var cached)) { return cached; }
|
||||
|
||||
List<CachedReflectedVariable> variables = new List<CachedReflectedVariable>();
|
||||
|
||||
IEnumerable<PropertyInfo> propertyInfos = type.GetProperties().Where(HasAttribute);
|
||||
IEnumerable<FieldInfo> fieldInfos = type.GetFields().Where(HasAttribute);
|
||||
|
||||
foreach (PropertyInfo info in propertyInfos)
|
||||
{
|
||||
if (TryFindBehavior(info.PropertyType, out ReadWriteBehavior behavior))
|
||||
{
|
||||
variables.Add(new CachedReflectedVariable(info, behavior, baseClassType));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SerializationException($"Unable to serialize type \"{type}\".");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FieldInfo info in fieldInfos)
|
||||
{
|
||||
if (TryFindBehavior(info.FieldType, out ReadWriteBehavior behavior))
|
||||
{
|
||||
variables.Add(new CachedReflectedVariable(info, behavior, baseClassType));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SerializationException($"Unable to serialize type \"{type}\".");
|
||||
}
|
||||
}
|
||||
|
||||
ImmutableArray<CachedReflectedVariable> array = variables.All(v => v.HasOwnAttribute) ? variables.OrderBy(v => v.Attribute.OrderKey).ToImmutableArray() : variables.ToImmutableArray();
|
||||
CachedVariables.Add(type, array);
|
||||
return array;
|
||||
|
||||
bool HasAttribute(MemberInfo info) => (info.GetCustomAttribute<NetworkSerialize>() ?? baseClassType.GetCustomAttribute<NetworkSerialize>()) != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -408,8 +577,8 @@ namespace Barotrauma
|
||||
/// <see cref="String">string</see><br/>
|
||||
/// <see cref="Microsoft.Xna.Framework.Color"/><br/>
|
||||
/// <see cref="Microsoft.Xna.Framework.Vector2"/><br/>
|
||||
/// In addition arrays, enums and <see cref="Nullable{T}"/> are supported.<br/>
|
||||
/// Using <see cref="Nullable{T}"/> will make the field or property optional
|
||||
/// In addition arrays, enums, <see cref="Nullable{T}"/> and <see cref="Option{T}"/> are supported.<br/>
|
||||
/// Using <see cref="Nullable{T}"/> or <see cref="Option{T}"/> will make the field or property optional.
|
||||
/// </remarks>
|
||||
/// <seealso cref="NetworkSerialize"/>
|
||||
public interface INetSerializableStruct
|
||||
@@ -446,38 +615,11 @@ namespace Barotrauma
|
||||
object? newObject = Activator.CreateInstance(type);
|
||||
if (newObject is null) { return default!; }
|
||||
|
||||
PropertyInfo[] properties = type.GetProperties();
|
||||
foreach (PropertyInfo info in properties)
|
||||
var properties = NetSerializableProperties.GetPropertiesAndFields(type, type);
|
||||
foreach (NetSerializableProperties.CachedReflectedVariable property in properties)
|
||||
{
|
||||
NetworkSerialize? attribute = GetAttribute(info, newObject);
|
||||
if (attribute is null) { continue; }
|
||||
|
||||
if (NetSerializableProperties.TryFindBehavior(info.PropertyType, out var behavior))
|
||||
{
|
||||
object? value = behavior.ReadAction(inc, info.PropertyType, attribute);
|
||||
info.SetValue(newObject, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Unsupported property type \"{info.PropertyType}\" in {newObject}!");
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo[] fields = type.GetFields();
|
||||
foreach (FieldInfo info in fields)
|
||||
{
|
||||
NetworkSerialize? attribute = GetAttribute(info, newObject);
|
||||
if (attribute is null) { continue; }
|
||||
|
||||
if (NetSerializableProperties.TryFindBehavior(info.FieldType, out var behavior))
|
||||
{
|
||||
object? value = behavior.ReadAction(inc, info.FieldType, attribute);
|
||||
info.SetValue(newObject, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Unsupported field type \"{info.FieldType}\" in {newObject}!");
|
||||
}
|
||||
NetworkSerialize attribute = property.Attribute;
|
||||
property.SetValue(newObject, property.Behavior.ReadAction(inc, property.Type, attribute));
|
||||
}
|
||||
|
||||
return newObject;
|
||||
@@ -509,39 +651,34 @@ namespace Barotrauma
|
||||
/// <param name="msg">Outgoing network message</param>
|
||||
public void Write(IWriteMessage msg)
|
||||
{
|
||||
PropertyInfo[] properties = GetType().GetProperties();
|
||||
foreach (PropertyInfo info in properties)
|
||||
Type type = GetType();
|
||||
var properties = NetSerializableProperties.GetPropertiesAndFields(type, type);
|
||||
foreach (NetSerializableProperties.CachedReflectedVariable property in properties)
|
||||
{
|
||||
NetworkSerialize? attribute = GetAttribute(info, this);
|
||||
if (attribute is null) { continue; }
|
||||
|
||||
if (NetSerializableProperties.TryFindBehavior(info.PropertyType, out var behavior))
|
||||
{
|
||||
behavior.WriteAction(info.GetValue(this), attribute, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unsupported property type \"{info.PropertyType}\" in {this}");
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo[] fields = GetType().GetFields();
|
||||
foreach (FieldInfo info in fields)
|
||||
{
|
||||
NetworkSerialize? attribute = GetAttribute(info, this);
|
||||
if (attribute is null) { continue; }
|
||||
|
||||
if (NetSerializableProperties.TryFindBehavior(info.FieldType, out var behavior))
|
||||
{
|
||||
behavior.WriteAction(info.GetValue(this), attribute, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unsupported field type \"{info.FieldType}\" in {this}");
|
||||
}
|
||||
NetworkSerialize attribute = property.Attribute;
|
||||
property.Behavior.WriteAction(property.GetValue(this), attribute, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static NetworkSerialize? GetAttribute(MemberInfo info, object baseClass) => info.GetCustomAttribute<NetworkSerialize>() ?? baseClass.GetType().GetCustomAttribute<NetworkSerialize>();
|
||||
public static class WriteOnlyMessageExtensions
|
||||
{
|
||||
#if CLIENT
|
||||
public static IWriteMessage WithHeader(this IWriteMessage msg, ClientPacketHeader header)
|
||||
{
|
||||
msg.Write((byte)header);
|
||||
return msg;
|
||||
}
|
||||
#elif SERVER
|
||||
public static IWriteMessage WithHeader(this IWriteMessage msg, ServerPacketHeader header)
|
||||
{
|
||||
msg.Write((byte)header);
|
||||
return msg;
|
||||
}
|
||||
#endif
|
||||
public static void Write(this IWriteMessage msg, INetSerializableStruct serializableStruct)
|
||||
{
|
||||
serializableStruct.Write(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,105 +13,105 @@ namespace Barotrauma
|
||||
|
||||
public string Name => "KarmaManager";
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; }
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool ResetKarmaBetweenRounds { get; set; }
|
||||
|
||||
[Serialize(0.1f, true)]
|
||||
[Serialize(0.1f, IsPropertySaveable.Yes)]
|
||||
public float KarmaDecay { get; set; }
|
||||
|
||||
[Serialize(50.0f, true)]
|
||||
[Serialize(50.0f, IsPropertySaveable.Yes)]
|
||||
public float KarmaDecayThreshold { get; set; }
|
||||
|
||||
[Serialize(0.15f, true)]
|
||||
[Serialize(0.15f, IsPropertySaveable.Yes)]
|
||||
public float KarmaIncrease { get; set; }
|
||||
|
||||
[Serialize(50.0f, true)]
|
||||
[Serialize(50.0f, IsPropertySaveable.Yes)]
|
||||
public float KarmaIncreaseThreshold { get; set; }
|
||||
|
||||
[Serialize(0.05f, true)]
|
||||
[Serialize(0.05f, IsPropertySaveable.Yes)]
|
||||
public float StructureRepairKarmaIncrease { get; set; }
|
||||
|
||||
[Serialize(0.1f, true)]
|
||||
[Serialize(0.1f, IsPropertySaveable.Yes)]
|
||||
public float StructureDamageKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(15.0f, true)]
|
||||
[Serialize(15.0f, IsPropertySaveable.Yes)]
|
||||
public float MaxStructureDamageKarmaDecreasePerSecond { get; set; }
|
||||
|
||||
[Serialize(0.03f, true)]
|
||||
[Serialize(0.03f, IsPropertySaveable.Yes)]
|
||||
public float ItemRepairKarmaIncrease { get; set; }
|
||||
|
||||
[Serialize(0.5f, true)]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes)]
|
||||
public float ReactorOverheatKarmaDecrease { get; set; }
|
||||
[Serialize(30.0f, true)]
|
||||
[Serialize(30.0f, IsPropertySaveable.Yes)]
|
||||
public float ReactorMeltdownKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(0.1f, true)]
|
||||
[Serialize(0.1f, IsPropertySaveable.Yes)]
|
||||
public float DamageEnemyKarmaIncrease { get; set; }
|
||||
[Serialize(0.2f, true)]
|
||||
[Serialize(0.2f, IsPropertySaveable.Yes)]
|
||||
public float HealFriendlyKarmaIncrease { get; set; }
|
||||
[Serialize(0.25f, true)]
|
||||
[Serialize(0.25f, IsPropertySaveable.Yes)]
|
||||
public float DamageFriendlyKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(0.25f, true)]
|
||||
[Serialize(0.25f, IsPropertySaveable.Yes)]
|
||||
public float StunFriendlyKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(0.3f, true)]
|
||||
[Serialize(0.3f, IsPropertySaveable.Yes)]
|
||||
public float StunFriendlyKarmaDecreaseThreshold { get; set; }
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
public float ExtinguishFireKarmaIncrease { get; set; }
|
||||
|
||||
[Serialize(defaultValue: 15.0f, true)]
|
||||
[Serialize(defaultValue: 15.0f, IsPropertySaveable.Yes)]
|
||||
public float DangerousItemStealKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(defaultValue: false, true)]
|
||||
[Serialize(defaultValue: false, IsPropertySaveable.Yes)]
|
||||
public bool DangerousItemStealBots { get; set; }
|
||||
|
||||
[Serialize(defaultValue: 0.05f, true)]
|
||||
[Serialize(defaultValue: 0.05f, IsPropertySaveable.Yes)]
|
||||
public float BallastFloraKarmaIncrease { get; set; }
|
||||
|
||||
|
||||
private int allowedWireDisconnectionsPerMinute;
|
||||
[Serialize(5, true)]
|
||||
[Serialize(5, IsPropertySaveable.Yes)]
|
||||
public int AllowedWireDisconnectionsPerMinute
|
||||
{
|
||||
get { return allowedWireDisconnectionsPerMinute; }
|
||||
set { allowedWireDisconnectionsPerMinute = Math.Max(0, value); }
|
||||
}
|
||||
|
||||
[Serialize(6.0f, true)]
|
||||
[Serialize(6.0f, IsPropertySaveable.Yes)]
|
||||
public float WireDisconnectionKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(0.15f, true)]
|
||||
[Serialize(0.15f, IsPropertySaveable.Yes)]
|
||||
public float SteerSubKarmaIncrease { get; set; }
|
||||
|
||||
[Serialize(15.0f, true)]
|
||||
[Serialize(15.0f, IsPropertySaveable.Yes)]
|
||||
public float SpamFilterKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(40.0f, true)]
|
||||
[Serialize(40.0f, IsPropertySaveable.Yes)]
|
||||
public float HerpesThreshold { get; set; }
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
public float KickBanThreshold { get; set; }
|
||||
|
||||
[Serialize(0, true)]
|
||||
[Serialize(0, IsPropertySaveable.Yes)]
|
||||
public int KicksBeforeBan { get; set; }
|
||||
|
||||
[Serialize(10.0f, true)]
|
||||
[Serialize(10.0f, IsPropertySaveable.Yes)]
|
||||
public float KarmaNotificationInterval { get; set; }
|
||||
|
||||
[Serialize(120.0f, true)]
|
||||
[Serialize(120.0f, IsPropertySaveable.Yes)]
|
||||
public float AllowedRetaliationTime { get; set; }
|
||||
|
||||
[Serialize(5.0f, true)]
|
||||
[Serialize(5.0f, IsPropertySaveable.Yes)]
|
||||
public float DangerousItemContainKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(defaultValue: true, true)]
|
||||
[Serialize(defaultValue: true, IsPropertySaveable.Yes)]
|
||||
public bool IsDangerousItemContainKarmaDecreaseIncremental { get; set; }
|
||||
|
||||
[Serialize(30.0f, true)]
|
||||
[Serialize(30.0f, IsPropertySaveable.Yes)]
|
||||
public float MaxDangerousItemContainKarmaDecrease { get; set; }
|
||||
|
||||
private readonly AfflictionPrefab herpesAffliction;
|
||||
@@ -141,7 +141,7 @@ namespace Barotrauma
|
||||
if (doc?.Root != null)
|
||||
{
|
||||
Presets["custom"] = doc.Root;
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
foreach (var subElement in doc.Root.Elements())
|
||||
{
|
||||
string presetName = subElement.GetAttributeString("name", "");
|
||||
Presets[presetName.ToLowerInvariant()] = subElement;
|
||||
|
||||
@@ -13,8 +13,6 @@ namespace Barotrauma.Networking
|
||||
public const int ServerNameMaxLength = 60;
|
||||
public const int ServerMessageMaxLength = 2000;
|
||||
|
||||
public static string MasterServerUrl = GameMain.Config.MasterServerUrl;
|
||||
|
||||
public const float MaxPhysicsBodyVelocity = 64.0f;
|
||||
public const float MaxPhysicsBodyAngularVelocity = 16.0f;
|
||||
|
||||
|
||||
+6
-62
@@ -4,47 +4,16 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
abstract class NetEntityEvent
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
Invalid,
|
||||
ComponentState,
|
||||
InventoryState,
|
||||
Status,
|
||||
Treatment,
|
||||
ApplyStatusEffect,
|
||||
ChangeProperty,
|
||||
Control,
|
||||
UpdateSkills,
|
||||
Combine,
|
||||
SetAttackTarget,
|
||||
ExecuteAttack,
|
||||
Upgrade,
|
||||
AssignCampaignInteraction,
|
||||
TeamChange,
|
||||
ObjectiveManagerState,
|
||||
AddToCrew,
|
||||
UpdateExperience,
|
||||
UpdateTalents,
|
||||
UpdateMoney,
|
||||
UpdatePermanentStats,
|
||||
}
|
||||
public interface IData { }
|
||||
|
||||
public readonly Entity Entity;
|
||||
public readonly UInt16 ID;
|
||||
|
||||
public UInt16 EntityID
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public UInt16 EntityID => Entity.ID;
|
||||
|
||||
//arbitrary extra data that will be passed to the Write method of the serializable entity
|
||||
//(the index of an itemcomponent for example)
|
||||
public object[] Data
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public IData Data { get; private set; }
|
||||
|
||||
public bool Sent;
|
||||
|
||||
@@ -52,43 +21,18 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
this.ID = id;
|
||||
this.Entity = serializableEntity as Entity;
|
||||
RefreshEntityID();
|
||||
}
|
||||
|
||||
public void RefreshEntityID()
|
||||
{
|
||||
this.EntityID = this.Entity is Entity entity ? entity.ID : Entity.NullEntityID;
|
||||
}
|
||||
|
||||
public void SetData(object[] data)
|
||||
public void SetData(IData data)
|
||||
{
|
||||
this.Data = data;
|
||||
}
|
||||
|
||||
public bool IsDuplicate(NetEntityEvent other)
|
||||
{
|
||||
if (other.Entity != this.Entity) return false;
|
||||
if (other.Entity != this.Entity) { return false; }
|
||||
|
||||
if (Data != null && other.Data != null)
|
||||
{
|
||||
if (Data.Length != other.Data.Length) return false;
|
||||
|
||||
for (int i = 0; i < Data.Length; i++)
|
||||
{
|
||||
if (Data[i] == null)
|
||||
{
|
||||
if (other.Data[i] != null) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (other.Data[i] == null) return false;
|
||||
if (!Data[i].Equals(other.Data[i])) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return Data == other.Data;
|
||||
return Equals(Data, other.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -38,7 +38,6 @@ namespace Barotrauma.Networking
|
||||
//(otherwise the clients might read the next event in the message and think its ID
|
||||
//is consecutive to the previous one, even though we skipped over this broken event)
|
||||
tempBuffer.Write(Entity.NullEntityID);
|
||||
tempBuffer.WritePadBits();
|
||||
eventCount++;
|
||||
continue;
|
||||
}
|
||||
@@ -53,7 +52,6 @@ namespace Barotrauma.Networking
|
||||
tempBuffer.Write(e.EntityID);
|
||||
tempBuffer.WriteVariableUInt32((uint)tempEventBuffer.LengthBytes);
|
||||
tempBuffer.Write(tempEventBuffer.Buffer, 0, tempEventBuffer.LengthBytes);
|
||||
tempBuffer.WritePadBits();
|
||||
sentEvents.Add(e);
|
||||
|
||||
eventCount++;
|
||||
@@ -61,6 +59,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (eventCount > 0)
|
||||
{
|
||||
msg.WritePadBits();
|
||||
msg.Write(eventsToSync[0].ID);
|
||||
msg.Write((byte)eventCount);
|
||||
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
enum ClientPacketHeader
|
||||
public enum ClientPacketHeader
|
||||
{
|
||||
UPDATE_LOBBY, //update state in lobby
|
||||
UPDATE_INGAME, //update state ingame
|
||||
@@ -31,8 +31,11 @@ namespace Barotrauma.Networking
|
||||
ERROR, //tell the server that an error occurred
|
||||
CREW, //hiring UI
|
||||
MEDICAL, //medical clinic
|
||||
TRANSFER_MONEY, // wallet transfers
|
||||
REWARD_DISTRIBUTION, // wallet reward distribution
|
||||
READY_CHECK,
|
||||
READY_TO_SPAWN,
|
||||
|
||||
LUA_NET_MESSAGE
|
||||
}
|
||||
enum ClientNetObject
|
||||
@@ -52,7 +55,7 @@ namespace Barotrauma.Networking
|
||||
MISSING_ENTITY //client can't find an entity of a certain ID
|
||||
}
|
||||
|
||||
enum ServerPacketHeader
|
||||
public enum ServerPacketHeader
|
||||
{
|
||||
AUTH_RESPONSE, //tell the player if they require a password to log in
|
||||
AUTH_FAILURE, //the server won't authorize player yet, however connection is still alive
|
||||
@@ -83,6 +86,7 @@ namespace Barotrauma.Networking
|
||||
CREW, //anything related to managing bots in multiplayer
|
||||
MEDICAL, //medical clinic
|
||||
READY_CHECK, //start, end and update a ready check
|
||||
MONEY,
|
||||
|
||||
LUA_NET_MESSAGE
|
||||
}
|
||||
@@ -116,7 +120,8 @@ namespace Barotrauma.Networking
|
||||
StartRound,
|
||||
PurchaseAndSwitchSub,
|
||||
PurchaseSub,
|
||||
SwitchSub
|
||||
SwitchSub,
|
||||
TransferMoney
|
||||
}
|
||||
|
||||
public enum ReadyCheckState
|
||||
@@ -171,7 +176,7 @@ namespace Barotrauma.Networking
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public abstract void CreateEntityEvent(INetSerializable entity, object[] extraData = null);
|
||||
public abstract void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData = null);
|
||||
|
||||
#if DEBUG
|
||||
public Dictionary<string, long> messageCount = new Dictionary<string, long>();
|
||||
@@ -179,11 +184,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
protected ServerSettings serverSettings;
|
||||
|
||||
public Voting Voting { get; protected set; }
|
||||
|
||||
protected TimeSpan updateInterval;
|
||||
protected DateTime updateTimer;
|
||||
|
||||
public int EndVoteCount, EndVoteMax, SubmarineVoteYesCount, SubmarineVoteNoCount, SubmarineVoteMax;
|
||||
|
||||
protected bool gameStarted;
|
||||
|
||||
protected RespawnManager respawnManager;
|
||||
|
||||
@@ -11,17 +11,17 @@ namespace Barotrauma.Networking
|
||||
public readonly Character TargetCharacter;
|
||||
|
||||
//which entity is this order referring to (hull, reactor, railgun controller, etc)
|
||||
public readonly ISpatialEntity TargetEntity;
|
||||
public ISpatialEntity TargetEntity => Order.TargetSpatialEntity;
|
||||
|
||||
//additional instructions (power up, fire at will, etc)
|
||||
public readonly string OrderOption;
|
||||
public Identifier OrderOption => Order.Option;
|
||||
|
||||
public readonly int OrderPriority;
|
||||
public int OrderPriority => Order.ManualPriority;
|
||||
|
||||
/// <summary>
|
||||
/// Used when the order targets a wall
|
||||
/// </summary>
|
||||
public int? WallSectionIndex { get; set; }
|
||||
public int? WallSectionIndex => Order.WallSectionIndex;
|
||||
|
||||
public bool IsNewOrder { get; }
|
||||
|
||||
@@ -29,55 +29,50 @@ namespace Barotrauma.Networking
|
||||
/// Same as calling <see cref="OrderChatMessage.OrderChatMessage(Order, string, int, string, ISpatialEntity, Character, Character)"/>,
|
||||
/// but the text parameter is set using <see cref="Order.GetChatMessage(string, string, bool, string)"/>
|
||||
/// </summary>
|
||||
public OrderChatMessage(Order order, string orderOption, int priority, ISpatialEntity targetEntity, Character targetCharacter, Character sender, bool isNewOrder = true)
|
||||
: this(order, orderOption, priority,
|
||||
order?.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.DisplayName, targetCharacter == sender, orderOption, isNewOrder),
|
||||
targetEntity, targetCharacter, sender, isNewOrder)
|
||||
public OrderChatMessage(Order order, Character targetCharacter, Character sender, bool isNewOrder = true)
|
||||
: this(order,
|
||||
order?.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.DisplayName?.Value, givingOrderToSelf: targetCharacter == sender, orderOption: order.Option, isNewOrder: isNewOrder),
|
||||
targetCharacter, sender, isNewOrder)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public OrderChatMessage(Order order, string orderOption, int priority, string text, ISpatialEntity targetEntity,
|
||||
Character targetCharacter, Character sender, bool isNewOrder = true)
|
||||
public OrderChatMessage(Order order, string text, Character targetCharacter, Character sender, bool isNewOrder = true)
|
||||
: base(sender?.Name, text, ChatMessageType.Order, sender, GameMain.NetworkMember.ConnectedClients.Find(c => c.Character == sender))
|
||||
{
|
||||
Order = order;
|
||||
OrderOption = orderOption;
|
||||
OrderPriority = priority;
|
||||
TargetCharacter = targetCharacter;
|
||||
TargetEntity = targetEntity;
|
||||
IsNewOrder = isNewOrder;
|
||||
}
|
||||
|
||||
public static void WriteOrder(IWriteMessage msg, Order order, Character targetCharacter, ISpatialEntity targetEntity,
|
||||
string orderOption, int orderPriority, int? wallSectionIndex, bool isNewOrder)
|
||||
public static void WriteOrder(IWriteMessage msg, Order order, Character targetCharacter, bool isNewOrder)
|
||||
{
|
||||
msg.Write((byte)Order.PrefabList.IndexOf(order.Prefab));
|
||||
msg.Write(order.Prefab.Identifier);
|
||||
msg.Write(targetCharacter == null ? (UInt16)0 : targetCharacter.ID);
|
||||
msg.Write(targetEntity is Entity ? (targetEntity as Entity).ID : (UInt16)0);
|
||||
msg.Write(order.TargetSpatialEntity is Entity ? (order.TargetEntity as Entity).ID : (UInt16)0);
|
||||
|
||||
// The option of a Dismiss order is written differently so we know what order we target
|
||||
// now that the game supports multiple current orders simultaneously
|
||||
if (order.Prefab.Identifier != "dismissed")
|
||||
if (!order.IsDismissal)
|
||||
{
|
||||
msg.Write((byte)Array.IndexOf(order.Prefab.Options, orderOption));
|
||||
msg.Write((byte)order.Options.IndexOf(order.Option));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(orderOption))
|
||||
if (order.Option != Identifier.Empty)
|
||||
{
|
||||
msg.Write(true);
|
||||
string[] dismissedOrder = orderOption.Split('.');
|
||||
string[] dismissedOrder = order.Option.Value.Split('.');
|
||||
msg.Write((byte)dismissedOrder.Length);
|
||||
if (dismissedOrder.Length > 0)
|
||||
{
|
||||
string dismissedOrderIdentifier = dismissedOrder[0];
|
||||
var orderPrefab = Order.GetPrefab(dismissedOrderIdentifier);
|
||||
msg.Write((byte)Order.PrefabList.IndexOf(orderPrefab));
|
||||
Identifier dismissedOrderIdentifier = dismissedOrder[0].ToIdentifier();
|
||||
var orderPrefab = OrderPrefab.Prefabs[dismissedOrderIdentifier];
|
||||
msg.Write(dismissedOrderIdentifier);
|
||||
if (dismissedOrder.Length > 1)
|
||||
{
|
||||
string dismissedOrderOption = dismissedOrder[1];
|
||||
msg.Write((byte)Array.IndexOf(orderPrefab.Options, dismissedOrderOption));
|
||||
Identifier dismissedOrderOption = dismissedOrder[1].ToIdentifier();
|
||||
msg.Write((byte)orderPrefab.Options.IndexOf(dismissedOrderOption));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,9 +84,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write((byte)orderPriority);
|
||||
msg.Write((byte)order.ManualPriority);
|
||||
msg.Write((byte)order.TargetType);
|
||||
if (order.TargetType == Order.OrderTargetType.Position && targetEntity is OrderTarget orderTarget)
|
||||
if (order.TargetType == Order.OrderTargetType.Position && order.TargetSpatialEntity is OrderTarget orderTarget)
|
||||
{
|
||||
msg.Write(true);
|
||||
msg.Write(orderTarget.Position.X);
|
||||
@@ -103,7 +98,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write(false);
|
||||
if (order.TargetType == Order.OrderTargetType.WallSection)
|
||||
{
|
||||
msg.Write((byte)(wallSectionIndex ?? order.WallSectionIndex ?? 0));
|
||||
msg.Write((byte)(order.WallSectionIndex ?? 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,14 +107,14 @@ namespace Barotrauma.Networking
|
||||
|
||||
private void WriteOrder(IWriteMessage msg)
|
||||
{
|
||||
WriteOrder(msg, Order, TargetCharacter, TargetEntity, OrderOption, OrderPriority, WallSectionIndex, IsNewOrder);
|
||||
WriteOrder(msg, Order, TargetCharacter, IsNewOrder);
|
||||
}
|
||||
|
||||
public struct OrderMessageInfo
|
||||
{
|
||||
public int OrderIndex { get; }
|
||||
public Order OrderPrefab { get; }
|
||||
public string OrderOption { get; }
|
||||
public Identifier OrderIdentifier { get; }
|
||||
public OrderPrefab OrderPrefab => OrderPrefab.Prefabs[OrderIdentifier];
|
||||
public Identifier OrderOption { get; }
|
||||
public int? OrderOptionIndex { get; }
|
||||
public Character TargetCharacter { get; }
|
||||
public Order.OrderTargetType TargetType { get; }
|
||||
@@ -129,11 +124,10 @@ namespace Barotrauma.Networking
|
||||
public int Priority { get; }
|
||||
public bool IsNewOrder { get; }
|
||||
|
||||
public OrderMessageInfo(int orderIndex, Order orderPrefab, string orderOption, int? orderOptionIndex, Character targetCharacter,
|
||||
public OrderMessageInfo(Identifier orderIdentifier, Identifier orderOption, int? orderOptionIndex, Character targetCharacter,
|
||||
Order.OrderTargetType targetType, Entity targetEntity, OrderTarget targetPosition, int? wallSectionIndex, int orderPriority, bool isNewOrder)
|
||||
{
|
||||
OrderIndex = orderIndex;
|
||||
OrderPrefab = orderPrefab;
|
||||
OrderIdentifier = orderIdentifier;
|
||||
OrderOption = orderOption;
|
||||
OrderOptionIndex = orderOptionIndex;
|
||||
TargetCharacter = targetCharacter;
|
||||
@@ -148,21 +142,20 @@ namespace Barotrauma.Networking
|
||||
|
||||
public static OrderMessageInfo ReadOrder(IReadMessage msg)
|
||||
{
|
||||
int orderIndex = msg.ReadByte();
|
||||
Identifier orderIdentifier = msg.ReadIdentifier();
|
||||
ushort targetCharacterId = msg.ReadUInt16();
|
||||
Character targetCharacter = targetCharacterId != Entity.NullEntityID ? Entity.FindEntityByID(targetCharacterId) as Character : null;
|
||||
ushort targetEntityId = msg.ReadUInt16();
|
||||
Entity targetEntity = targetEntityId != Entity.NullEntityID ? Entity.FindEntityByID(targetEntityId) : null;
|
||||
|
||||
Order orderPrefab = null;
|
||||
int? optionIndex = null;
|
||||
string orderOption = null;
|
||||
Identifier orderOption = Identifier.Empty;
|
||||
// The option of a Dismiss order is written differently so we know what order we target
|
||||
// now that the game supports multiple current orders simultaneously
|
||||
if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count)
|
||||
if (orderIdentifier != Identifier.Empty)
|
||||
{
|
||||
orderPrefab = Order.PrefabList[orderIndex];
|
||||
if (orderPrefab.Identifier != "dismissed")
|
||||
var orderPrefab = OrderPrefab.Prefabs[orderIdentifier];
|
||||
if (!orderPrefab.IsDismissal)
|
||||
{
|
||||
optionIndex = msg.ReadByte();
|
||||
}
|
||||
@@ -172,11 +165,11 @@ namespace Barotrauma.Networking
|
||||
int identifierCount = msg.ReadByte();
|
||||
if (identifierCount > 0)
|
||||
{
|
||||
int dismissedOrderIndex = msg.ReadByte();
|
||||
Order dismissedOrderPrefab = null;
|
||||
if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count)
|
||||
Identifier dismissedOrderIdentifier = msg.ReadIdentifier();
|
||||
OrderPrefab dismissedOrderPrefab = null;
|
||||
if (dismissedOrderIdentifier != Identifier.Empty)
|
||||
{
|
||||
dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex];
|
||||
dismissedOrderPrefab = OrderPrefab.Prefabs[dismissedOrderIdentifier];
|
||||
orderOption = dismissedOrderPrefab.Identifier;
|
||||
}
|
||||
if (identifierCount > 1)
|
||||
@@ -187,7 +180,7 @@ namespace Barotrauma.Networking
|
||||
var options = dismissedOrderPrefab.Options;
|
||||
if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length)
|
||||
{
|
||||
orderOption += $".{options[dismissedOrderOptionIndex]}";
|
||||
orderOption = $"{orderOption.Value}.{options[dismissedOrderOptionIndex]}".ToIdentifier();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,9 +210,8 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
bool isNewOrder = msg.ReadBoolean();
|
||||
|
||||
return new OrderMessageInfo(orderIndex, orderPrefab, orderOption, optionIndex, targetCharacter,
|
||||
orderTargetType, targetEntity, orderTargetPosition, wallSectionIndex, orderPriority, isNewOrder);
|
||||
return new OrderMessageInfo(orderIdentifier, orderOption, optionIndex, targetCharacter,
|
||||
orderTargetType, targetEntity, orderTargetPosition, wallSectionIndex, orderPriority, isNewOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Barotrauma.Networking
|
||||
Double ReadDouble();
|
||||
UInt32 ReadVariableUInt32();
|
||||
String ReadString();
|
||||
Identifier ReadIdentifier();
|
||||
Microsoft.Xna.Framework.Color ReadColorR8G8B8();
|
||||
Microsoft.Xna.Framework.Color ReadColorR8G8B8A8();
|
||||
int ReadRangedInteger(int min, int max);
|
||||
|
||||
+2
-1
@@ -19,11 +19,12 @@ namespace Barotrauma.Networking
|
||||
void WriteColorR8G8B8A8(Microsoft.Xna.Framework.Color val);
|
||||
void WriteVariableUInt32(UInt32 val);
|
||||
void Write(string val);
|
||||
void Write(Identifier val);
|
||||
void WriteRangedInteger(int val, int min, int max);
|
||||
void WriteRangedSingle(Single val, Single min, Single max, int bitCount);
|
||||
void Write(byte[] val, int startIndex, int length);
|
||||
|
||||
void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int outLength);
|
||||
void PrepareForSending(ref byte[] outBuf, bool compressPastThreshold, out bool isCompressed, out int outLength);
|
||||
|
||||
int BitPosition { get; set; }
|
||||
int BytePosition { get; }
|
||||
|
||||
@@ -454,6 +454,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
lengthBits = value;
|
||||
seekPos = seekPos > lengthBits ? lengthBits : seekPos;
|
||||
MsgWriter.EnsureBufferSize(ref buf, lengthBits);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,6 +541,11 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Identifier val)
|
||||
{
|
||||
Write(val.Value);
|
||||
}
|
||||
|
||||
public void WriteRangedInteger(int val, int min, int max)
|
||||
{
|
||||
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
|
||||
@@ -555,9 +561,9 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.WriteBytes(ref buf, ref seekPos, val, startPos, length);
|
||||
}
|
||||
|
||||
public void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int length)
|
||||
public void PrepareForSending(ref byte[] outBuf, bool compressPastThreshold, out bool isCompressed, out int length)
|
||||
{
|
||||
if (LengthBytes <= MsgConstants.CompressionThreshold)
|
||||
if (LengthBytes <= MsgConstants.CompressionThreshold || !compressPastThreshold)
|
||||
{
|
||||
isCompressed = false;
|
||||
if (LengthBytes > outBuf.Length) { Array.Resize(ref outBuf, LengthBytes); }
|
||||
@@ -648,7 +654,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
get
|
||||
{
|
||||
return lengthBits / 8;
|
||||
return (LengthBits + 7) / 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,6 +770,11 @@ namespace Barotrauma.Networking
|
||||
return MsgReader.ReadString(buf, ref seekPos);
|
||||
}
|
||||
|
||||
public Identifier ReadIdentifier()
|
||||
{
|
||||
return ReadString().ToIdentifier();
|
||||
}
|
||||
|
||||
public Color ReadColorR8G8B8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8(buf, ref seekPos);
|
||||
@@ -773,7 +784,6 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8A8(buf, ref seekPos);
|
||||
}
|
||||
|
||||
|
||||
public int ReadRangedInteger(int min, int max)
|
||||
{
|
||||
@@ -857,7 +867,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
get
|
||||
{
|
||||
return (LengthBits + ((8 - (LengthBits % 8)) % 8)) / 8;
|
||||
return (LengthBits + 7) / 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -938,6 +948,10 @@ namespace Barotrauma.Networking
|
||||
MsgWriter.Write(ref buf, ref seekPos, val);
|
||||
}
|
||||
|
||||
public void Write(Identifier val)
|
||||
{
|
||||
Write(val.Value);
|
||||
}
|
||||
|
||||
public void WriteRangedInteger(int val, int min, int max)
|
||||
{
|
||||
@@ -1019,6 +1033,11 @@ namespace Barotrauma.Networking
|
||||
return MsgReader.ReadString(buf, ref seekPos);
|
||||
}
|
||||
|
||||
public Identifier ReadIdentifier()
|
||||
{
|
||||
return ReadString().ToIdentifier();
|
||||
}
|
||||
|
||||
public Color ReadColorR8G8B8()
|
||||
{
|
||||
return MsgReader.ReadColorR8G8B8(buf, ref seekPos);
|
||||
@@ -1044,7 +1063,7 @@ namespace Barotrauma.Networking
|
||||
return MsgReader.ReadBytes(buf, ref seekPos, numberOfBytes);
|
||||
}
|
||||
|
||||
public void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int outLength)
|
||||
public void PrepareForSending(ref byte[] outBuf, bool compressPastThreshold, out bool isCompressed, out int outLength)
|
||||
{
|
||||
throw new InvalidOperationException("ReadWriteMessages are not to be sent");
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ namespace Barotrauma.Networking
|
||||
protected set;
|
||||
}
|
||||
|
||||
public string Language
|
||||
public LanguageIdentifier Language
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ namespace Barotrauma.Networking
|
||||
//remove respawn items that have been left in the shuttle
|
||||
if (respawnItems.Contains(item))
|
||||
{
|
||||
Spawner.AddToRemoveQueue(item);
|
||||
Spawner.AddItemToRemoveQueue(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.Submarine != RespawnShuttle) { continue; }
|
||||
hull.OxygenPercentage = 100.0f;
|
||||
@@ -308,12 +308,12 @@ namespace Barotrauma.Networking
|
||||
c.Kill(CauseOfDeathType.Unknown, null, true);
|
||||
c.Enabled = false;
|
||||
|
||||
Spawner.AddToRemoveQueue(c);
|
||||
Spawner.AddEntityToRemoveQueue(c);
|
||||
if (c.Inventory != null)
|
||||
{
|
||||
foreach (Item item in c.Inventory.AllItems)
|
||||
{
|
||||
Spawner.AddToRemoveQueue(item);
|
||||
Spawner.AddItemToRemoveQueue(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,7 +335,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public static Affliction GetRespawnPenaltyAffliction()
|
||||
{
|
||||
var respawnPenaltyAffliction = AfflictionPrefab.List.FirstOrDefault(a => a.AfflictionType.Equals("respawnpenalty", StringComparison.OrdinalIgnoreCase));
|
||||
var respawnPenaltyAffliction = AfflictionPrefab.Prefabs.First(a => a.AfflictionType == "respawnpenalty");
|
||||
return respawnPenaltyAffliction?.Instantiate(10.0f);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,23 +10,20 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
private struct LogMessage
|
||||
{
|
||||
public readonly string Text;
|
||||
public readonly string SanitizedText;
|
||||
public readonly RichString Text;
|
||||
public readonly MessageType Type;
|
||||
public readonly List<RichTextData> RichData;
|
||||
|
||||
public LogMessage(string text, MessageType type)
|
||||
{
|
||||
if (type.HasFlag(MessageType.Chat))
|
||||
{
|
||||
Text = $"[{DateTime.Now}]\n {text}";
|
||||
text = $"[{DateTime.Now}]\n {text}";
|
||||
}
|
||||
else
|
||||
{
|
||||
Text = $"[{DateTime.Now}]\n {TextManager.GetServerMessage(text)}";
|
||||
text = $"[{DateTime.Now}]\n {TextManager.GetServerMessage(text)}";
|
||||
}
|
||||
RichData = RichTextData.GetRichTextData(Text, out SanitizedText);
|
||||
|
||||
Text = RichString.Rich(text);
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
@@ -41,6 +38,7 @@ namespace Barotrauma.Networking
|
||||
Wiring,
|
||||
ServerMessage,
|
||||
ConsoleUsage,
|
||||
Money,
|
||||
Karma,
|
||||
Talent,
|
||||
Error,
|
||||
@@ -56,9 +54,10 @@ namespace Barotrauma.Networking
|
||||
{ MessageType.Wiring, new Color(255, 157, 85) },
|
||||
{ MessageType.ServerMessage, new Color(157, 225, 160) },
|
||||
{ MessageType.ConsoleUsage, new Color(0, 162, 232) },
|
||||
{ MessageType.Money, Color.Green },
|
||||
{ MessageType.Karma, new Color(75, 88, 255) },
|
||||
{ MessageType.Talent, new Color(125, 125, 255) },
|
||||
{ MessageType.Error, Color.Red },
|
||||
{ MessageType.Error, Color.Red }
|
||||
};
|
||||
|
||||
private readonly Dictionary<MessageType, string> messageTypeName = new Dictionary<MessageType, string>
|
||||
@@ -71,6 +70,7 @@ namespace Barotrauma.Networking
|
||||
{ MessageType.Wiring, "Wiring" },
|
||||
{ MessageType.ServerMessage, "ServerMessage" },
|
||||
{ MessageType.ConsoleUsage, "ConsoleUsage" },
|
||||
{ MessageType.Money, "Money" },
|
||||
{ MessageType.Karma, "Karma" },
|
||||
{ MessageType.Talent, "Talent" },
|
||||
{ MessageType.Error, "Error" }
|
||||
@@ -113,7 +113,7 @@ namespace Barotrauma.Networking
|
||||
var newText = new LogMessage(line, messageType);
|
||||
|
||||
#if SERVER
|
||||
DebugConsole.NewMessage(newText.SanitizedText, messageColor[messageType]); //TODO: REMOVE
|
||||
DebugConsole.NewMessage(newText.Text.SanitizedValue, messageColor[messageType]); //TODO: REMOVE
|
||||
#endif
|
||||
|
||||
lines.Enqueue(newText);
|
||||
@@ -173,7 +173,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(filePath, unsavedLines.Select(l => l.SanitizedText));
|
||||
File.WriteAllLines(filePath, unsavedLines.Select(l => l.Text.SanitizedValue));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using Barotrauma.IO;
|
||||
@@ -97,11 +98,8 @@ namespace Barotrauma.Networking
|
||||
private readonly SerializableProperty property;
|
||||
private readonly string typeString;
|
||||
private readonly object parentObject;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return property.Name; }
|
||||
}
|
||||
|
||||
public Identifier Name => property.Name.ToIdentifier();
|
||||
|
||||
public object Value
|
||||
{
|
||||
@@ -266,7 +264,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
};
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
@@ -280,8 +278,6 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ServerLog = new ServerLog(serverName);
|
||||
|
||||
Voting = new Voting();
|
||||
|
||||
Whitelist = new WhiteList();
|
||||
BanList = new BanList();
|
||||
|
||||
@@ -314,7 +310,7 @@ namespace Barotrauma.Networking
|
||||
if (typeName != null || property.PropertyType.IsEnum)
|
||||
{
|
||||
NetPropertyData netPropertyData = new NetPropertyData(this, property, typeName);
|
||||
UInt32 key = ToolBox.StringToUInt32Hash(property.Name, md5);
|
||||
UInt32 key = ToolBox.IdentifierToUint32Hash(netPropertyData.Name, md5);
|
||||
if (key == 0) { key++; } //0 is reserved to indicate the end of the netproperties section of a message
|
||||
if (netProperties.ContainsKey(key)){ throw new Exception("Hashing collision in ServerSettings.netProperties: " + netProperties[key] + " has same key as " + property.Name + " (" + key.ToString() + ")"); }
|
||||
netProperties.Add(key, netPropertyData);
|
||||
@@ -331,7 +327,7 @@ namespace Barotrauma.Networking
|
||||
if (typeName != null || property.PropertyType.IsEnum)
|
||||
{
|
||||
NetPropertyData netPropertyData = new NetPropertyData(networkMember.KarmaManager, property, typeName);
|
||||
UInt32 key = ToolBox.StringToUInt32Hash(property.Name, md5);
|
||||
UInt32 key = ToolBox.IdentifierToUint32Hash(netPropertyData.Name, md5);
|
||||
if (netProperties.ContainsKey(key)) { throw new Exception("Hashing collision in ServerSettings.netProperties: " + netProperties[key] + " has same key as " + property.Name + " (" + key.ToString() + ")"); }
|
||||
netProperties.Add(key, netPropertyData);
|
||||
}
|
||||
@@ -383,9 +379,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public ServerLog ServerLog;
|
||||
|
||||
public Voting Voting;
|
||||
|
||||
public Dictionary<string, bool> MonsterEnabled { get; private set; }
|
||||
public Dictionary<Identifier, bool> MonsterEnabled { get; private set; }
|
||||
|
||||
public const int MaxExtraCargoItemsOfType = 10;
|
||||
public const int MaxExtraCargoItemTypes = 20;
|
||||
@@ -408,63 +402,63 @@ namespace Barotrauma.Networking
|
||||
|
||||
public WhiteList Whitelist { get; private set; }
|
||||
|
||||
[Serialize(20, true)]
|
||||
[Serialize(20, IsPropertySaveable.Yes)]
|
||||
public int TickRate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool RandomizeSeed
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool UseRespawnShuttle
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(300.0f, true)]
|
||||
[Serialize(300.0f, IsPropertySaveable.Yes)]
|
||||
public float RespawnInterval
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(180.0f, true)]
|
||||
[Serialize(180.0f, IsPropertySaveable.Yes)]
|
||||
public float MaxTransportTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.2f, true)]
|
||||
[Serialize(0.2f, IsPropertySaveable.Yes)]
|
||||
public float MinRespawnRatio
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(60.0f, true)]
|
||||
[Serialize(60.0f, IsPropertySaveable.Yes)]
|
||||
public float AutoRestartInterval
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool StartWhenClientsReady
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.8f, true)]
|
||||
[Serialize(0.8f, IsPropertySaveable.Yes)]
|
||||
public float StartWhenClientsReadyRatio
|
||||
{
|
||||
get;
|
||||
@@ -472,7 +466,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
private bool allowSpectating;
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowSpectating
|
||||
{
|
||||
get { return allowSpectating; }
|
||||
@@ -484,21 +478,28 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool SaveServerLogs
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowModDownloads
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = true;
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowRagdollButton
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowFileTransfers
|
||||
{
|
||||
get;
|
||||
@@ -506,7 +507,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
private bool voiceChatEnabled;
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool VoiceChatEnabled
|
||||
{
|
||||
get { return voiceChatEnabled; }
|
||||
@@ -519,7 +520,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
private PlayStyle playstyleSelection;
|
||||
[Serialize(PlayStyle.Casual, true)]
|
||||
[Serialize(PlayStyle.Casual, IsPropertySaveable.Yes)]
|
||||
public PlayStyle PlayStyle
|
||||
{
|
||||
get { return playstyleSelection; }
|
||||
@@ -530,14 +531,14 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(Barotrauma.LosMode.Opaque, true)]
|
||||
[Serialize(Barotrauma.LosMode.Opaque, IsPropertySaveable.Yes)]
|
||||
public LosMode LosMode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(800, true)]
|
||||
[Serialize(800, IsPropertySaveable.Yes)]
|
||||
public int LinesPerLogFile
|
||||
{
|
||||
get
|
||||
@@ -572,37 +573,23 @@ namespace Barotrauma.Networking
|
||||
#endif
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowVoteKick
|
||||
{
|
||||
get
|
||||
{
|
||||
return Voting.AllowVoteKick;
|
||||
}
|
||||
set
|
||||
{
|
||||
Voting.AllowVoteKick = value;
|
||||
}
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowEndVoting
|
||||
{
|
||||
get
|
||||
{
|
||||
return Voting.AllowEndVoting;
|
||||
}
|
||||
set
|
||||
{
|
||||
Voting.AllowEndVoting = value;
|
||||
}
|
||||
get; set;
|
||||
}
|
||||
|
||||
private bool allowRespawn;
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowRespawn
|
||||
{
|
||||
get { return allowRespawn; ; }
|
||||
get { return allowRespawn; }
|
||||
set
|
||||
{
|
||||
if (allowRespawn == value) { return; }
|
||||
@@ -611,28 +598,28 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0, true)]
|
||||
[Serialize(0, IsPropertySaveable.Yes)]
|
||||
public int BotCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(16, true)]
|
||||
[Serialize(16, IsPropertySaveable.Yes)]
|
||||
public int MaxBotCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(BotSpawnMode.Normal, true)]
|
||||
[Serialize(BotSpawnMode.Normal, IsPropertySaveable.Yes)]
|
||||
public BotSpawnMode BotSpawnMode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool DisableBotConversations
|
||||
{
|
||||
get;
|
||||
@@ -645,76 +632,76 @@ namespace Barotrauma.Networking
|
||||
set { selectedLevelDifficulty = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowDisguises
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowRewiring
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool LockAllDefaultWires
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool AllowLinkingWifiToChat
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowFriendlyFire
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool DestructibleOutposts
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool KillableNPCs
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool BanAfterWrongPassword
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(3, true)]
|
||||
[Serialize(3, IsPropertySaveable.Yes)]
|
||||
public int MaxPasswordRetriesBeforeBan
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize("", true)]
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string SelectedSubmarine
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Serialize("", true)]
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string SelectedShuttle
|
||||
{
|
||||
get;
|
||||
@@ -722,7 +709,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
private YesNoMaybe traitorsEnabled;
|
||||
[Serialize(YesNoMaybe.No, true)]
|
||||
[Serialize(YesNoMaybe.No, IsPropertySaveable.Yes)]
|
||||
public YesNoMaybe TraitorsEnabled
|
||||
{
|
||||
get { return traitorsEnabled; }
|
||||
@@ -734,35 +721,35 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(defaultValue: 1, isSaveable: true)]
|
||||
[Serialize(defaultValue: 1, isSaveable: IsPropertySaveable.Yes)]
|
||||
public int TraitorsMinPlayerCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(defaultValue: 90.0f, isSaveable: true)]
|
||||
[Serialize(defaultValue: 90.0f, isSaveable: IsPropertySaveable.Yes)]
|
||||
public float TraitorsMinStartDelay
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(defaultValue: 180.0f, isSaveable: true)]
|
||||
[Serialize(defaultValue: 180.0f, isSaveable: IsPropertySaveable.Yes)]
|
||||
public float TraitorsMaxStartDelay
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(defaultValue: 30.0f, isSaveable: true)]
|
||||
[Serialize(defaultValue: 30.0f, isSaveable: IsPropertySaveable.Yes)]
|
||||
public float TraitorsMinRestartDelay
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(defaultValue: 90.0f, isSaveable: true)]
|
||||
[Serialize(defaultValue: 90.0f, isSaveable: IsPropertySaveable.Yes)]
|
||||
public float TraitorsMaxRestartDelay
|
||||
{
|
||||
get;
|
||||
@@ -770,69 +757,69 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
private SelectionMode subSelectionMode;
|
||||
[Serialize(SelectionMode.Manual, true)]
|
||||
[Serialize(SelectionMode.Manual, IsPropertySaveable.Yes)]
|
||||
public SelectionMode SubSelectionMode
|
||||
{
|
||||
get { return subSelectionMode; }
|
||||
set
|
||||
{
|
||||
subSelectionMode = value;
|
||||
Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
|
||||
AllowSubVoting = subSelectionMode == SelectionMode.Vote;
|
||||
ServerDetailsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private SelectionMode modeSelectionMode;
|
||||
[Serialize(SelectionMode.Manual, true)]
|
||||
[Serialize(SelectionMode.Manual, IsPropertySaveable.Yes)]
|
||||
public SelectionMode ModeSelectionMode
|
||||
{
|
||||
get { return modeSelectionMode; }
|
||||
set
|
||||
{
|
||||
modeSelectionMode = value;
|
||||
Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
|
||||
AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
|
||||
ServerDetailsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public BanList BanList { get; private set; }
|
||||
|
||||
[Serialize(0.6f, true)]
|
||||
[Serialize(0.6f, IsPropertySaveable.Yes)]
|
||||
public float EndVoteRequiredRatio
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.6f, true)]
|
||||
public float SubmarineVoteRequiredRatio
|
||||
[Serialize(0.6f, IsPropertySaveable.Yes)]
|
||||
public float VoteRequiredRatio
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(30f, true)]
|
||||
public float SubmarineVoteTimeout
|
||||
[Serialize(30f, IsPropertySaveable.Yes)]
|
||||
public float VoteTimeout
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.6f, true)]
|
||||
[Serialize(0.6f, IsPropertySaveable.Yes)]
|
||||
public float KickVoteRequiredRatio
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(300.0f, true)]
|
||||
[Serialize(300.0f, IsPropertySaveable.Yes)]
|
||||
public float KillDisconnectedTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(600.0f, true)]
|
||||
[Serialize(600.0f, IsPropertySaveable.Yes)]
|
||||
public float KickAFKTime
|
||||
{
|
||||
get;
|
||||
@@ -840,7 +827,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
private bool karmaEnabled;
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool KarmaEnabled
|
||||
{
|
||||
get { return karmaEnabled; }
|
||||
@@ -854,7 +841,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
private string karmaPreset = "default";
|
||||
[Serialize("default", true)]
|
||||
[Serialize("default", IsPropertySaveable.Yes)]
|
||||
public string KarmaPreset
|
||||
{
|
||||
get { return karmaPreset; }
|
||||
@@ -869,21 +856,21 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("sandbox", true)]
|
||||
public string GameModeIdentifier
|
||||
[Serialize("sandbox", IsPropertySaveable.Yes)]
|
||||
public Identifier GameModeIdentifier
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize("All", true)]
|
||||
[Serialize("All", IsPropertySaveable.Yes)]
|
||||
public string MissionType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(8, true)]
|
||||
[Serialize(8, IsPropertySaveable.Yes)]
|
||||
public int MaxPlayers
|
||||
{
|
||||
get { return maxPlayers; }
|
||||
@@ -896,21 +883,21 @@ namespace Barotrauma.Networking
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(60f * 60.0f, true)]
|
||||
[Serialize(60f * 60.0f, IsPropertySaveable.Yes)]
|
||||
public float AutoBanTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(60.0f * 60.0f * 24.0f, true)]
|
||||
[Serialize(60.0f * 60.0f * 24.0f, IsPropertySaveable.Yes)]
|
||||
public float MaxAutoBanTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool RadiationEnabled
|
||||
{
|
||||
get;
|
||||
@@ -919,13 +906,66 @@ namespace Barotrauma.Networking
|
||||
|
||||
private int maxMissionCount = CampaignSettings.DefaultMaxMissionCount;
|
||||
|
||||
[Serialize(CampaignSettings.DefaultMaxMissionCount, true)]
|
||||
[Serialize(CampaignSettings.DefaultMaxMissionCount, IsPropertySaveable.Yes)]
|
||||
public int MaxMissionCount
|
||||
{
|
||||
get { return maxMissionCount; }
|
||||
set { maxMissionCount = MathHelper.Clamp(value, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit); }
|
||||
}
|
||||
|
||||
private bool allowSubVoting;
|
||||
//Don't serialize: the value is set based on SubSelectionMode
|
||||
public bool AllowSubVoting
|
||||
{
|
||||
get { return allowSubVoting; }
|
||||
set
|
||||
{
|
||||
if (value == allowSubVoting) { return; }
|
||||
allowSubVoting = value;
|
||||
#if CLIENT
|
||||
GameMain.NetLobbyScreen.SubList.Enabled = value ||
|
||||
(GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.SelectSub));
|
||||
var subVotesLabel = GameMain.NetLobbyScreen.Frame.FindChild("subvotes", true) as GUITextBlock;
|
||||
subVotesLabel.Visible = value;
|
||||
var subVisButton = GameMain.NetLobbyScreen.SubVisibilityButton;
|
||||
subVisButton.RectTransform.AbsoluteOffset
|
||||
= new Point(value ? (int)(subVotesLabel.TextSize.X + subVisButton.Rect.Width) : 0, 0);
|
||||
|
||||
GameMain.Client?.Voting.UpdateVoteTexts(null, VoteType.Sub);
|
||||
GameMain.NetLobbyScreen.SubList.Deselect();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private bool allowModeVoting;
|
||||
//Don't serialize: the value is set based on ModeSelectionMode
|
||||
public bool AllowModeVoting
|
||||
{
|
||||
get { return allowModeVoting; }
|
||||
set
|
||||
{
|
||||
if (value == allowModeVoting) { return; }
|
||||
allowModeVoting = value;
|
||||
#if CLIENT
|
||||
GameMain.NetLobbyScreen.ModeList.Enabled =
|
||||
value ||
|
||||
(GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.SelectMode));
|
||||
GameMain.NetLobbyScreen.Frame.FindChild("modevotes", true).Visible = value;
|
||||
// Disable modes that cannot be voted on
|
||||
foreach (var guiComponent in GameMain.NetLobbyScreen.ModeList.Content.Children)
|
||||
{
|
||||
if (guiComponent is GUIFrame frame)
|
||||
{
|
||||
frame.CanBeFocused = !allowModeVoting || ((GameModePreset)frame.UserData).Votable;
|
||||
}
|
||||
}
|
||||
GameMain.Client?.Voting.UpdateVoteTexts(null, VoteType.Mode);
|
||||
GameMain.NetLobbyScreen.ModeList.Deselect();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetPassword(string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(password))
|
||||
@@ -965,45 +1005,54 @@ namespace Barotrauma.Networking
|
||||
/// <summary>
|
||||
/// A list of int pairs that represent the ranges of UTF-16 codes allowed in client names
|
||||
/// </summary>
|
||||
public List<Pair<int, int>> AllowedClientNameChars
|
||||
public List<Range<int>> AllowedClientNameChars
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new List<Pair<int, int>>();
|
||||
} = new List<Range<int>>();
|
||||
|
||||
private void InitMonstersEnabled()
|
||||
{
|
||||
//monster spawn settings
|
||||
if (MonsterEnabled == null)
|
||||
if (MonsterEnabled is null || MonsterEnabled.Count != CharacterPrefab.Prefabs.Count())
|
||||
{
|
||||
List<string> monsterNames1 = CharacterPrefab.Prefabs.Select(p => p.Identifier).ToList();
|
||||
|
||||
MonsterEnabled = new Dictionary<string, bool>();
|
||||
foreach (string s in monsterNames1)
|
||||
{
|
||||
if (!MonsterEnabled.ContainsKey(s)) MonsterEnabled.Add(s, true);
|
||||
}
|
||||
MonsterEnabled = CharacterPrefab.Prefabs.Select(p => (p.Identifier, true)).ToDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<Identifier> ExtractAndSortKeys(IReadOnlyDictionary<Identifier, bool> monsterEnabled)
|
||||
=> monsterEnabled.Keys
|
||||
.OrderBy(k => CharacterPrefab.Prefabs[k].UintIdentifier)
|
||||
.ToImmutableArray();
|
||||
|
||||
public void ReadMonsterEnabled(IReadMessage inc)
|
||||
{
|
||||
InitMonstersEnabled();
|
||||
List<string> monsterNames = MonsterEnabled.Keys.ToList();
|
||||
foreach (string s in monsterNames)
|
||||
var monsterNames = ExtractAndSortKeys(MonsterEnabled);
|
||||
uint receivedMonsterCount = inc.ReadVariableUInt32();
|
||||
if (monsterNames.Count != receivedMonsterCount)
|
||||
{
|
||||
MonsterEnabled[s] = inc.ReadBoolean();
|
||||
inc.BitPosition += (int)receivedMonsterCount;
|
||||
DebugConsole.AddWarning($"Expected monster count {monsterNames.Count}, got {receivedMonsterCount}");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Identifier s in monsterNames)
|
||||
{
|
||||
MonsterEnabled[s] = inc.ReadBoolean();
|
||||
}
|
||||
}
|
||||
inc.ReadPadBits();
|
||||
}
|
||||
|
||||
public void WriteMonsterEnabled(IWriteMessage msg, Dictionary<string, bool> monsterEnabled = null)
|
||||
public void WriteMonsterEnabled(IWriteMessage msg, Dictionary<Identifier, bool> monsterEnabled = null)
|
||||
{
|
||||
//monster spawn settings
|
||||
if (monsterEnabled == null) monsterEnabled = MonsterEnabled;
|
||||
|
||||
List<string> monsterNames = monsterEnabled.Keys.ToList();
|
||||
foreach (string s in monsterNames)
|
||||
InitMonstersEnabled();
|
||||
monsterEnabled ??= MonsterEnabled;
|
||||
var monsterNames = ExtractAndSortKeys(monsterEnabled);
|
||||
msg.WriteVariableUInt32((uint)monsterNames.Count);
|
||||
foreach (Identifier s in monsterNames)
|
||||
{
|
||||
msg.Write(monsterEnabled[s]);
|
||||
}
|
||||
@@ -1018,7 +1067,7 @@ namespace Barotrauma.Networking
|
||||
Dictionary<ItemPrefab, int> extraCargo = new Dictionary<ItemPrefab, int>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
string prefabIdentifier = msg.ReadString();
|
||||
Identifier prefabIdentifier = msg.ReadIdentifier();
|
||||
byte amount = msg.ReadByte();
|
||||
|
||||
if (MapEntityPrefab.Find(null, prefabIdentifier, showErrorMessages: false) is ItemPrefab itemPrefab && amount > 0)
|
||||
@@ -1044,7 +1093,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write((UInt32)ExtraCargo.Count);
|
||||
foreach (KeyValuePair<ItemPrefab, int> kvp in ExtraCargo)
|
||||
{
|
||||
msg.Write(kvp.Key.Identifier ?? "");
|
||||
msg.Write(kvp.Key.Identifier);
|
||||
msg.Write((byte)kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,296 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#if USE_STEAM
|
||||
namespace Barotrauma.Steam
|
||||
{
|
||||
static partial class SteamManager
|
||||
{
|
||||
public const int STEAMP2P_OWNER_PORT = 30000;
|
||||
|
||||
public const uint AppID = 602960;
|
||||
|
||||
private static readonly List<string> initializationErrors = new List<string>();
|
||||
public static IEnumerable<string> InitializationErrors
|
||||
{
|
||||
get { return initializationErrors; }
|
||||
}
|
||||
|
||||
public const string MetadataFileName = "filelist.xml";
|
||||
|
||||
public const string CopyIndicatorFileName = ".copying";
|
||||
|
||||
private static readonly Dictionary<string, int> tagCommonness = new Dictionary<string, int>()
|
||||
{
|
||||
{ "submarine", 10 },
|
||||
{ "item", 10 },
|
||||
{ "monster", 8 },
|
||||
{ "art", 8 },
|
||||
{ "mission", 8 },
|
||||
{ "event set", 8 },
|
||||
{ "total conversion", 5 },
|
||||
{ "environment", 5 },
|
||||
{ "item assembly", 5 },
|
||||
{ "language", 5 }
|
||||
};
|
||||
|
||||
private static readonly List<string> popularTags = new List<string>();
|
||||
public static IEnumerable<string> PopularTags
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isInitialized) { return Enumerable.Empty<string>(); }
|
||||
return popularTags;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool isInitialized;
|
||||
public static bool IsInitialized => isInitialized;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
InitializeProjectSpecific();
|
||||
}
|
||||
|
||||
public static ulong GetSteamID()
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Steamworks.SteamClient.SteamId;
|
||||
}
|
||||
|
||||
public static string GetUsername()
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return Steamworks.SteamClient.Name;
|
||||
}
|
||||
|
||||
private static Steamworks.AuthTicket currentTicket = null;
|
||||
public static Steamworks.AuthTicket GetAuthSessionTicket()
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
currentTicket?.Cancel();
|
||||
currentTicket = Steamworks.SteamUser.GetAuthSessionTicket();
|
||||
return currentTicket;
|
||||
}
|
||||
|
||||
public static bool OverlayCustomURL(string url)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Steamworks.SteamFriends.OpenWebOverlay(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool UnlockAchievement(string achievementIdentifier)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugConsole.Log("Unlocked achievement \"" + achievementIdentifier + "\"");
|
||||
|
||||
var achievements = Steamworks.SteamUserStats.Achievements.ToList();
|
||||
int achIndex = achievements.FindIndex(ach => ach.Identifier == achievementIdentifier);
|
||||
bool unlocked = achIndex >= 0 ? achievements[achIndex].Trigger() : false;
|
||||
if (!unlocked)
|
||||
{
|
||||
//can be caused by an incorrect identifier, but also happens during normal gameplay:
|
||||
//SteamAchievementManager tries to unlock achievements that may or may not exist
|
||||
//(discovered[whateverbiomewasentered], kill[withwhateveritem], kill[somemonster] etc) so that we can add
|
||||
//some types of new achievements without the need for client-side changes.
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Failed to unlock achievement \"" + achievementIdentifier + "\".");
|
||||
#endif
|
||||
}
|
||||
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
public static bool IncrementStat(string statName, int increment)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid) { return false; }
|
||||
DebugConsole.Log("Incremented stat \"" + statName + "\" by " + increment);
|
||||
bool success = Steamworks.SteamUserStats.AddStat(statName, increment);
|
||||
if (!success)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Failed to increment stat \"" + statName + "\".");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
StoreStats();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public static bool IncrementStat(string statName, float increment)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid) { return false; }
|
||||
DebugConsole.Log("Incremented stat \"" + statName + "\" by " + increment);
|
||||
bool success = Steamworks.SteamUserStats.AddStat(statName, increment);
|
||||
if (!success)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Failed to increment stat \"" + statName + "\".");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
StoreStats();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public static int GetStatInt(string statName)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid) { return 0; }
|
||||
return Steamworks.SteamUserStats.GetStatInt(statName);
|
||||
}
|
||||
|
||||
public static bool StoreStats()
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid) { return false; }
|
||||
DebugConsole.Log("Storing Steam stats...");
|
||||
bool success = Steamworks.SteamUserStats.StoreStats();
|
||||
if (!success)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Failed to store Steam stats.");
|
||||
#endif
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public static bool TryGetUnlockedAchievements(out List<Steamworks.Data.Achievement> achievements)
|
||||
{
|
||||
if (!isInitialized || !Steamworks.SteamClient.IsValid)
|
||||
{
|
||||
achievements = null;
|
||||
return false;
|
||||
}
|
||||
achievements = Steamworks.SteamUserStats.Achievements.Where(a => a.State).ToList();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Update(float deltaTime)
|
||||
{
|
||||
if (!isInitialized) { return; }
|
||||
|
||||
if (Steamworks.SteamClient.IsValid) { Steamworks.SteamClient.RunCallbacks(); }
|
||||
if (Steamworks.SteamServer.IsValid) { Steamworks.SteamServer.RunCallbacks(); }
|
||||
|
||||
SteamAchievementManager.Update(deltaTime);
|
||||
}
|
||||
|
||||
public static void ShutDown()
|
||||
{
|
||||
if (!isInitialized) { return; }
|
||||
|
||||
if (Steamworks.SteamClient.IsValid) { Steamworks.SteamClient.Shutdown(); }
|
||||
if (Steamworks.SteamServer.IsValid) { Steamworks.SteamServer.Shutdown(); }
|
||||
isInitialized = false;
|
||||
}
|
||||
|
||||
public static IEnumerable<ulong> ParseWorkshopIds(string workshopIdData)
|
||||
{
|
||||
string[] workshopIds = workshopIdData.Split(',');
|
||||
foreach (string id in workshopIds)
|
||||
{
|
||||
if (ulong.TryParse(id, out ulong idCast))
|
||||
{
|
||||
yield return idCast;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<ulong> WorkshopUrlsToIds(IEnumerable<string> urls)
|
||||
{
|
||||
return urls.Select((u) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(u))
|
||||
{
|
||||
return (ulong)0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetWorkshopItemIDFromUrl(u);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static ulong GetWorkshopItemIDFromUrl(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(url);
|
||||
string idStr = HttpUtility.ParseQueryString(uri.Query)["id"];
|
||||
if (ulong.TryParse(idStr, out ulong id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to get Workshop item ID from the url \"" + url + "\"!", e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static UInt64 SteamIDStringToUInt64(string str)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(str)) { return 0; }
|
||||
UInt64 retVal;
|
||||
if (str.StartsWith("STEAM64_", StringComparison.InvariantCultureIgnoreCase)) { str = str.Substring(8); }
|
||||
if (UInt64.TryParse(str, out retVal) && retVal >(1<<52)) { return retVal; }
|
||||
if (!str.StartsWith("STEAM_", StringComparison.InvariantCultureIgnoreCase)) { return 0; }
|
||||
string[] split = str.Substring(6).Split(':');
|
||||
if (split.Length != 3) { return 0; }
|
||||
|
||||
if (!UInt64.TryParse(split[0], out UInt64 universe)) { return 0; }
|
||||
if (!UInt64.TryParse(split[1], out UInt64 y)) { return 0; }
|
||||
if (!UInt64.TryParse(split[2], out UInt64 accountNumber)) { return 0; }
|
||||
|
||||
UInt64 accountInstance = 1; UInt64 accountType = 1;
|
||||
|
||||
return (universe << 56) | (accountType << 52) | (accountInstance << 32) | (accountNumber << 1) | y;
|
||||
}
|
||||
|
||||
public static string SteamIDUInt64ToString(UInt64 uint64)
|
||||
{
|
||||
UInt64 y = uint64 & 0x1;
|
||||
UInt64 accountNumber = (uint64 >> 1) & 0x7fffffff;
|
||||
UInt64 universe = (uint64 >> 56) & 0xff;
|
||||
|
||||
string retVal = "STEAM_" + universe.ToString() + ":" + y.ToString() + ":" + accountNumber.ToString();
|
||||
|
||||
if (SteamIDStringToUInt64(retVal) != uint64) { return "STEAM64_" + uint64.ToString(); }
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,19 +1,11 @@
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Voting
|
||||
{
|
||||
private bool allowSubVoting, allowModeVoting;
|
||||
|
||||
public bool AllowVoteKick = true;
|
||||
|
||||
public bool AllowEndVoting = true;
|
||||
|
||||
public bool VoteRunning = false;
|
||||
|
||||
public enum VoteState { None = 0, Started = 1, Running = 2, Passed = 3, Failed = 4 };
|
||||
|
||||
private IReadOnlyDictionary<T, int> GetVoteCounts<T>(VoteType voteType, List<Client> voters)
|
||||
@@ -39,12 +31,12 @@ namespace Barotrauma
|
||||
|
||||
public T HighestVoted<T>(VoteType voteType, List<Client> voters)
|
||||
{
|
||||
if (voteType == VoteType.Sub && !AllowSubVoting) return default(T);
|
||||
if (voteType == VoteType.Mode && !AllowModeVoting) return default(T);
|
||||
if (voteType == VoteType.Sub && !GameMain.NetworkMember.ServerSettings.AllowSubVoting) { return default; }
|
||||
if (voteType == VoteType.Mode && !GameMain.NetworkMember.ServerSettings.AllowModeVoting) { return default; }
|
||||
|
||||
IReadOnlyDictionary<T, int> voteList = GetVoteCounts<T>(voteType, voters);
|
||||
|
||||
T selected = default(T);
|
||||
T selected = default;
|
||||
int highestVotes = 0;
|
||||
foreach (KeyValuePair<T, int> votable in voteList)
|
||||
{
|
||||
@@ -71,11 +63,13 @@ namespace Barotrauma
|
||||
{
|
||||
client.ResetVotes();
|
||||
}
|
||||
|
||||
GameMain.NetworkMember.EndVoteCount = 0;
|
||||
GameMain.NetworkMember.EndVoteMax = 0;
|
||||
|
||||
#if CLIENT
|
||||
foreach (VoteType voteType in Enum.GetValues(typeof(VoteType)))
|
||||
{
|
||||
SetVoteCountYes(voteType, 0);
|
||||
SetVoteCountNo(voteType, 0);
|
||||
SetVoteCountMax(voteType, 0);
|
||||
}
|
||||
UpdateVoteTexts(connectedClients, VoteType.Mode);
|
||||
UpdateVoteTexts(connectedClients, VoteType.Sub);
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user