Unstable 0.17.1.0
This commit is contained in:
@@ -219,8 +219,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Steamworks.ServerList;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -196,51 +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 int OriginalSlotIndex;
|
||||
|
||||
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;
|
||||
OriginalSlotIndex = 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)
|
||||
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)
|
||||
@@ -397,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)
|
||||
@@ -422,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()
|
||||
{
|
||||
|
||||
@@ -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) },
|
||||
@@ -82,8 +144,6 @@ namespace Barotrauma
|
||||
{ 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
|
||||
@@ -99,15 +159,18 @@ namespace Barotrauma
|
||||
{ type => Nullable.GetUnderlyingType(type) != null, new ReadWriteBehavior(ReadNullable, WriteNullable) },
|
||||
|
||||
// Option
|
||||
{ type => type.GetGenericTypeDefinition() == typeof(Option<>), new ReadWriteBehavior(ReadOption, WriteOption) }
|
||||
{ type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Option<>), new ReadWriteBehavior(ReadOption, WriteOption) }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private static readonly ReadWriteBehavior InvalidReadWriteBehavior = new ReadWriteBehavior(ReadInvalid, WriteInvalid);
|
||||
|
||||
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 InvalidOperationException($"Type {obj?.GetType()} cannot be serialized. Did you forget to implement INetSerializableStruct?");
|
||||
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 InvalidOperationException($"Type {type} cannot be deserialized. Did you forget to implement 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)
|
||||
{
|
||||
@@ -131,7 +194,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Option type was neither None<> or Some<>");
|
||||
throw new ArgumentOutOfRangeException(nameof(obj), "Option type was neither None or Some");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +210,7 @@ namespace Barotrauma
|
||||
if (TryFindBehavior(underlyingType, out ReadWriteBehavior behavior))
|
||||
{
|
||||
dynamic? value = behavior.ReadAction(inc, underlyingType, attribute);
|
||||
return GetCreateMethod(typeof(Some<>), underlyingType, cachedSomeCreateMethods).Invoke(null, new []{ value });
|
||||
return GetCreateMethod(typeof(Some<>), underlyingType, cachedSomeCreateMethods).Invoke(null, new[] { value });
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Could not find suitable behavior for type {underlyingType} in {nameof(ReadOption)}");
|
||||
@@ -349,7 +412,7 @@ namespace Barotrauma
|
||||
private static dynamic ReadDouble(IReadMessage inc, Type type, NetworkSerialize attribute) => inc.ReadDouble();
|
||||
|
||||
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();
|
||||
@@ -411,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; }
|
||||
|
||||
@@ -427,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>
|
||||
@@ -512,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;
|
||||
@@ -575,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+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,9 +31,10 @@ namespace Barotrauma.Networking
|
||||
ERROR, //tell the server that an error occurred
|
||||
CREW, //hiring UI
|
||||
MEDICAL, //medical clinic
|
||||
MONEY, //wallet updates
|
||||
REWARD_DISTRIBUTION, // wallet reward distribution
|
||||
READY_CHECK,
|
||||
READY_TO_SPAWN
|
||||
|
||||
}
|
||||
enum ClientNetObject
|
||||
{
|
||||
@@ -52,7 +53,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
|
||||
@@ -82,6 +83,7 @@ namespace Barotrauma.Networking
|
||||
EVENTACTION,
|
||||
CREW, //anything related to managing bots in multiplayer
|
||||
MEDICAL, //medical clinic
|
||||
MONEY,
|
||||
READY_CHECK //start, end and update a ready check
|
||||
}
|
||||
enum ServerNetObject
|
||||
@@ -169,7 +171,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>();
|
||||
|
||||
@@ -654,7 +654,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
get
|
||||
{
|
||||
return lengthBits / 8;
|
||||
return (LengthBits + 7) / 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -867,7 +867,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
get
|
||||
{
|
||||
return (LengthBits + ((8 - (LengthBits % 8)) % 8)) / 8;
|
||||
return (LengthBits + 7) / 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user