diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e1c79c57..e58eea533 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,24 @@
## Changelog
+0.9.0:
+- Now compatible with Dark Fog (DSP 0.10.x) with enemies disabled
+- @phantomgamers: fix compilation after update and overall fixes/cleanup
+- @phantomgamers: fix UIVirtualStarmap patches
+- @phantomgamers: reviewing code from other contributers
+- @starfi5h: fix runtime issues after the update and overall fixes/cleanup
+- @starfi5h: improve UI and Keybinding
+- @starfi5h: rework Wireless Power Tower syncing
+- @starfi5h: add syncing for Battlefield Analysis Base
+- @mmjr: disable dark fog switch in lobby and prevent df enabled saves to be loaded.
+- @ajh16: sync dark fog lobby settings
+- @highrizk: sync storage filters
+- @highrizk: update serializer and fix broken packets
+- @highrizk: add serialization support and unit tests for dictionaries
+- @zzarek: add turret UI syncing
+- @sp00ktober: add syncing for new mecha settings and features
+- @sp00ktober: sync mecha and battle base construction drones
+- @sp00ktober: overall NRE fixes
+
0.8.14:
- @starfi5h: Fix mecha animation when player count > 2
- @starfi5h: Fix UIPerformance save test in multiplayer
diff --git a/Directory.Build.props b/Directory.Build.props
index 40eebb5d9..11e81d6a2 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -20,7 +20,7 @@
false
true
- $(PluginOutputDirectory)
+ $(PluginOutputDirectory)
net472
latest
true
diff --git a/Nebula.sln b/Nebula.sln
index ed2b3f52e..6c9204d7b 100644
--- a/Nebula.sln
+++ b/Nebula.sln
@@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NebulaAPI", "NebulaAPI\Nebu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "discord_game_sdk_dotnet", "dep\discord_game_sdk\discord_game_sdk_dotnet.csproj", "{E691A758-2D19-47EF-9410-3C78D99E2488}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NebulaTests", "NebulaTests\NebulaTests.csproj", "{2F4901A9-8BDB-49A7-AD89-9E38B0B3350A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -61,6 +63,10 @@ Global
{E691A758-2D19-47EF-9410-3C78D99E2488}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E691A758-2D19-47EF-9410-3C78D99E2488}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E691A758-2D19-47EF-9410-3C78D99E2488}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F4901A9-8BDB-49A7-AD89-9E38B0B3350A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F4901A9-8BDB-49A7-AD89-9E38B0B3350A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F4901A9-8BDB-49A7-AD89-9E38B0B3350A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F4901A9-8BDB-49A7-AD89-9E38B0B3350A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/NebulaAPI/DataStructures/IMechaData.cs b/NebulaAPI/DataStructures/IMechaData.cs
index 7a21aed54..ee32d8b38 100644
--- a/NebulaAPI/DataStructures/IMechaData.cs
+++ b/NebulaAPI/DataStructures/IMechaData.cs
@@ -8,7 +8,7 @@ namespace NebulaAPI.DataStructures;
public interface IMechaData : INetSerializable
{
- int SandCount { get; set; }
+ long SandCount { get; set; }
double CoreEnergy { get; set; }
double ReactorEnergy { get; set; }
StorageComponent Inventory { get; set; }
@@ -16,4 +16,8 @@ public interface IMechaData : INetSerializable
StorageComponent ReactorStorage { get; set; }
StorageComponent WarpStorage { get; set; }
MechaForge Forge { get; set; }
+ ConstructionModuleComponent ConstructionModule { get; set; }
+ IMechaFightData FightData { get; set; }
+
+ public void UpdateMech(Player destination);
}
diff --git a/NebulaAPI/DataStructures/IMechaFightData.cs b/NebulaAPI/DataStructures/IMechaFightData.cs
new file mode 100644
index 000000000..745427002
--- /dev/null
+++ b/NebulaAPI/DataStructures/IMechaFightData.cs
@@ -0,0 +1,36 @@
+using NebulaAPI.Interfaces;
+
+namespace NebulaAPI.DataStructures
+{
+ public interface IMechaFightData
+ {
+ // TODO: what about spaceSector? Will it be synced in dynamically? Or should it be stored?
+ bool AutoReplenishFuel { get; set; }
+ bool AutoReplenishAmmo { get; set; }
+ bool AutoReplenishHangar { get; set; }
+ int Hp { get; set; }
+ long EnergyShieldEnergy { get; set; }
+ int AmmoItemId { get; set; }
+ int AmmoInc { get; set; }
+ int AmmoBulletCount { get; set; }
+ int AmmoSelectSlot { get; set; }
+ int AmmoMuzzleFire { get; set; }
+ int AmmoRoundFire { get; set; }
+ int AmmoMuzzleIndex { get; set; }
+ bool LaserActive { get; set; }
+ bool LaserRecharging { get; set; }
+ long LaserEnergy { get; set; }
+ int LaserFire { get; set; }
+ int BombFire { get; set; }
+ StorageComponent AmmoStorage { get; set; }
+ StorageComponent BombStorage { get; set; }
+ EnemyHatredTarget AmmoHatredTarget { get; set; }
+ EnemyHatredTarget LaserHatredTarget { get; set; }
+ StorageComponent FighterStorage { get; set; }
+ CombatModuleComponent GroundCombatModule { get; set; }
+ CombatModuleComponent SpaceCombatModule { get; set; }
+ public void Serialize(INetDataWriter writer);
+ public void Deserialize(INetDataReader reader);
+ public void UpdateMech(Player destination);
+ }
+}
diff --git a/NebulaAPI/DataStructures/IPlayerTechBonuses.cs b/NebulaAPI/DataStructures/IPlayerTechBonuses.cs
index 18b3828f5..2d48b8d45 100644
--- a/NebulaAPI/DataStructures/IPlayerTechBonuses.cs
+++ b/NebulaAPI/DataStructures/IPlayerTechBonuses.cs
@@ -31,10 +31,22 @@ public interface IPlayerTechBonuses : INetSerializable
float maxWarpSpeed { get; set; }
float buildArea { get; set; }
int droneCount { get; set; }
- float droneSpeed { get; set; }
- int droneMovement { get; set; }
int inventorySize { get; set; }
bool deliveryPackageUnlocked { get; set; }
int deliveryPackageColCount { get; set; }
int deliveryPackageStackSizeMultiplier { get; set; }
+ double instantBuildEnergy { get; set; }
+ int hpMaxUpgrade { get; set; }
+ bool energyShieldUnlocked { get; set; }
+ float energyShieldRadius { get; set; }
+ long energyShieldCapacity { get; set; }
+ long laserEnergyCapacity { get; set; }
+ float laserLocalAttackRange { get; set; }
+ float laserSpaceAttackRange { get; set; }
+ int laserLocalEnergyCost { get; set; }
+ int laserSpaceEnergyCost { get; set; }
+ int laserLocalDamage { get; set; }
+ int laserSpaceDamage { get; set; }
+ int groundFleetCount { get; set; }
+ int spaceFleetCount { get; set; }
}
diff --git a/NebulaAPI/GameState/IFactoryManager.cs b/NebulaAPI/GameState/IFactoryManager.cs
index 63bba542a..3b5d9ea2b 100644
--- a/NebulaAPI/GameState/IFactoryManager.cs
+++ b/NebulaAPI/GameState/IFactoryManager.cs
@@ -44,8 +44,4 @@ public interface IFactoryManager : IDisposable
int GetNextPrebuildId(int planetId);
int GetNextPrebuildId(PlanetFactory factory);
-
- void OnNewSetInserterPickTarget(int objId, int otherObjId, int inserterId, int offset, Vector3 pointPos);
-
- void OnNewSetInserterInsertTarget(int objId, int otherObjId, int inserterId, int offset, Vector3 pointPos);
}
diff --git a/NebulaAPI/Interfaces/INetDataReader.cs b/NebulaAPI/Interfaces/INetDataReader.cs
new file mode 100644
index 000000000..263c1ca4b
--- /dev/null
+++ b/NebulaAPI/Interfaces/INetDataReader.cs
@@ -0,0 +1,151 @@
+// #pragma once
+// #ifndef INetDataReader.cs_H_
+// #define INetDataReader.cs_H_
+//
+// #endif
+
+using System;
+using System.Net;
+using System.Runtime.CompilerServices;
+
+namespace NebulaAPI.Interfaces;
+
+public interface INetDataReader
+{
+ byte[] RawData
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ int RawDataSize
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ int UserDataOffset
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ int UserDataSize
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ bool IsNull
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ int Position
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ bool EndOfData
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ int AvailableBytes
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ void SkipBytes(int count);
+ void SetPosition(int position);
+ void SetSource(INetDataWriter dataWriter);
+ void SetSource(byte[] source);
+ void SetSource(byte[] source, int offset, int maxSize);
+ IPEndPoint GetNetEndPoint();
+ byte GetByte();
+ sbyte GetSByte();
+ T[] GetArray(int size);
+ bool[] GetBoolArray();
+ ushort[] GetUShortArray();
+ short[] GetShortArray();
+ int[] GetIntArray();
+ uint[] GetUIntArray();
+ float[] GetFloatArray();
+ double[] GetDoubleArray();
+ long[] GetLongArray();
+ ulong[] GetULongArray();
+ string[] GetStringArray();
+
+ ///
+ /// Note that "maxStringLength" only limits the number of characters in a string, not its size in bytes.
+ /// Strings that exceed this parameter are returned as empty
+ ///
+ string[] GetStringArray(int maxStringLength);
+
+ bool GetBool();
+ char GetChar();
+ ushort GetUShort();
+ short GetShort();
+ long GetLong();
+ ulong GetULong();
+ int GetInt();
+ uint GetUInt();
+ float GetFloat();
+ double GetDouble();
+
+ ///
+ /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
+ ///
+ /// "string.Empty" if value > "maxLength"
+ string GetString(int maxLength);
+
+ string GetString();
+ ArraySegment GetBytesSegment(int count);
+ ArraySegment GetRemainingBytesSegment();
+ T Get() where T : struct, INetSerializable;
+ T Get(Func constructor) where T : class, INetSerializable;
+ byte[] GetRemainingBytes();
+ void GetBytes(byte[] destination, int start, int count);
+ void GetBytes(byte[] destination, int count);
+ sbyte[] GetSBytesWithLength();
+ byte[] GetBytesWithLength();
+ byte PeekByte();
+ sbyte PeekSByte();
+ bool PeekBool();
+ char PeekChar();
+ ushort PeekUShort();
+ short PeekShort();
+ long PeekLong();
+ ulong PeekULong();
+ int PeekInt();
+ uint PeekUInt();
+ float PeekFloat();
+ double PeekDouble();
+
+ ///
+ /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
+ ///
+ string PeekString(int maxLength);
+
+ string PeekString();
+ bool TryGetByte(out byte result);
+ bool TryGetSByte(out sbyte result);
+ bool TryGetBool(out bool result);
+ bool TryGetChar(out char result);
+ bool TryGetShort(out short result);
+ bool TryGetUShort(out ushort result);
+ bool TryGetInt(out int result);
+ bool TryGetUInt(out uint result);
+ bool TryGetLong(out long result);
+ bool TryGetULong(out ulong result);
+ bool TryGetFloat(out float result);
+ bool TryGetDouble(out double result);
+ bool TryGetString(out string result);
+ bool TryGetStringArray(out string[] result);
+ bool TryGetBytesWithLength(out byte[] result);
+ void Clear();
+}
diff --git a/NebulaAPI/Interfaces/INetDataWriter.cs b/NebulaAPI/Interfaces/INetDataWriter.cs
new file mode 100644
index 000000000..f92789ed4
--- /dev/null
+++ b/NebulaAPI/Interfaces/INetDataWriter.cs
@@ -0,0 +1,85 @@
+// #pragma once
+// #ifndef INetDataWriter.cs_H_
+// #define INetDataWriter.cs_H_
+//
+// #endif
+
+using System;
+using System.Net;
+using System.Runtime.CompilerServices;
+
+namespace NebulaAPI.Interfaces;
+
+public interface INetDataWriter
+{
+ int Capacity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ byte[] Data
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ int Length
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ }
+
+ void ResizeIfNeed(int newSize);
+ void EnsureFit(int additionalSize);
+ void Reset(int size);
+ void Reset();
+ byte[] CopyData();
+
+ ///
+ /// Sets position of NetDataWriter to rewrite previous values
+ ///
+ /// new byte position
+ /// previous position of data writer
+ int SetPosition(int position);
+
+ void Put(float value);
+ void Put(double value);
+ void Put(long value);
+ void Put(ulong value);
+ void Put(int value);
+ void Put(uint value);
+ void Put(char value);
+ void Put(ushort value);
+ void Put(short value);
+ void Put(sbyte value);
+ void Put(byte value);
+ void Put(byte[] data, int offset, int length);
+ void Put(byte[] data);
+ void Put(bool value);
+ void Put(IPEndPoint endPoint);
+ void Put(string value);
+
+ ///
+ /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
+ ///
+ void Put(string value, int maxLength);
+
+ void Put(T obj) where T : INetSerializable;
+ void PutSBytesWithLength(sbyte[] data, int offset, int length);
+ void PutSBytesWithLength(sbyte[] data);
+ void PutBytesWithLength(byte[] data, int offset, int length);
+ void PutBytesWithLength(byte[] data);
+ void PutArray(Array arr, int sz);
+ void PutArray(float[] value);
+ void PutArray(double[] value);
+ void PutArray(long[] value);
+ void PutArray(ulong[] value);
+ void PutArray(int[] value);
+ void PutArray(uint[] value);
+ void PutArray(ushort[] value);
+ void PutArray(short[] value);
+ void PutArray(bool[] value);
+ void PutArray(string[] value);
+ void PutArray(string[] value, int strMaxLength);
+}
diff --git a/NebulaAPI/Interfaces/INetSerializable.cs b/NebulaAPI/Interfaces/INetSerializable.cs
index 777597aaa..0c55052df 100644
--- a/NebulaAPI/Interfaces/INetSerializable.cs
+++ b/NebulaAPI/Interfaces/INetSerializable.cs
@@ -13,141 +13,3 @@ public interface INetSerializable
void Deserialize(INetDataReader reader);
}
-
-public interface INetDataWriter
-{
- void Put(float value);
-
- void Put(double value);
-
- void Put(long value);
-
- void Put(ulong value);
-
- void Put(int value);
-
- void Put(uint value);
-
- void Put(char value);
-
- void Put(ushort value);
-
- void Put(short value);
-
- void Put(sbyte value);
-
- void Put(byte value);
-
- void Put(byte[] data, int offset, int length);
-
- void Put(byte[] data);
-
- void PutSBytesWithLength(sbyte[] data, int offset, int length);
-
- void PutSBytesWithLength(sbyte[] data);
-
- void PutBytesWithLength(byte[] data, int offset, int length);
-
- void PutBytesWithLength(byte[] data);
-
- void Put(bool value);
-
- void PutArray(float[] value);
-
- void PutArray(double[] value);
-
- void PutArray(long[] value);
-
- void PutArray(ulong[] value);
-
- void PutArray(int[] value);
-
- void PutArray(uint[] value);
-
- void PutArray(ushort[] value);
-
- void PutArray(short[] value);
-
- void PutArray(bool[] value);
-
- void PutArray(string[] value);
-
- void PutArray(string[] value, int maxLength);
-
- void Put(IPEndPoint endPoint);
-
- void Put(string value);
-
- void Put(string value, int maxLength);
-
- void Put(T obj) where T : INetSerializable;
-}
-
-public interface INetDataReader
-{
- IPEndPoint GetNetEndPoint();
-
- byte GetByte();
-
- sbyte GetSByte();
-
- bool[] GetBoolArray();
-
- ushort[] GetUShortArray();
-
- short[] GetShortArray();
-
- long[] GetLongArray();
-
- ulong[] GetULongArray();
-
- int[] GetIntArray();
-
- uint[] GetUIntArray();
-
- float[] GetFloatArray();
-
- double[] GetDoubleArray();
-
- string[] GetStringArray();
-
- string[] GetStringArray(int maxStringLength);
-
- bool GetBool();
-
- char GetChar();
-
- ushort GetUShort();
-
- short GetShort();
-
- long GetLong();
-
- ulong GetULong();
-
- int GetInt();
-
- uint GetUInt();
-
- float GetFloat();
-
- double GetDouble();
-
- string GetString(int maxLength);
-
- string GetString();
-
- ArraySegment GetRemainingBytesSegment();
-
- T Get() where T : INetSerializable, new();
-
- byte[] GetRemainingBytes();
-
- void GetBytes(byte[] destination, int start, int count);
-
- void GetBytes(byte[] destination, int count);
-
- sbyte[] GetSBytesWithLength();
-
- byte[] GetBytesWithLength();
-}
diff --git a/NebulaModel/DataStructures/MechaData.cs b/NebulaModel/DataStructures/MechaData.cs
index 117d05c04..64e162491 100644
--- a/NebulaModel/DataStructures/MechaData.cs
+++ b/NebulaModel/DataStructures/MechaData.cs
@@ -5,6 +5,7 @@
using NebulaAPI.Interfaces;
using NebulaAPI.Packets;
using NebulaModel.Packets.Players;
+using static NebulaModel.Networking.BinaryUtils;
#endregion
@@ -15,27 +16,28 @@ public class MechaData : IMechaData
{
public MechaData()
{
- //This is needed for the serialization and deserialization
+ // This is needed for the serialization and deserialization
Forge = new MechaForge { tasks = [] };
TechBonuses = new PlayerTechBonuses();
}
- public MechaData(int sandCount, double coreEnergy, double reactorEnergy, StorageComponent inventory,
- DeliveryPackage deliveryPackage, StorageComponent reactorStorage, StorageComponent warpStorage, MechaForge forge)
+ public MechaData(Player player)
{
- SandCount = sandCount;
- CoreEnergy = coreEnergy;
- ReactorEnergy = reactorEnergy;
- ReactorStorage = reactorStorage;
- WarpStorage = warpStorage;
- Forge = forge;
- Inventory = inventory;
- DeliveryPackage = deliveryPackage;
+ SandCount = player.sandCount;
+ CoreEnergy = player.mecha.coreEnergy;
+ ReactorEnergy = player.mecha.reactorEnergy;
+ ReactorStorage = player.mecha.reactorStorage;
+ WarpStorage = player.mecha.warpStorage;
+ Forge = player.mecha.forge;
+ Inventory = player.package;
+ DeliveryPackage = player.deliveryPackage;
+ ConstructionModule = player.mecha.constructionModule;
+ FightData = new MechaFightData(player);
TechBonuses = new PlayerTechBonuses();
}
public PlayerTechBonuses TechBonuses { get; set; }
- public int SandCount { get; set; }
+ public long SandCount { get; set; }
public double CoreEnergy { get; set; }
public double ReactorEnergy { get; set; }
public StorageComponent Inventory { get; set; }
@@ -43,6 +45,8 @@ public MechaData(int sandCount, double coreEnergy, double reactorEnergy, Storage
public StorageComponent ReactorStorage { get; set; }
public StorageComponent WarpStorage { get; set; }
public MechaForge Forge { get; set; }
+ public ConstructionModuleComponent ConstructionModule { get; set; }
+ public IMechaFightData FightData { get; set; }
public void Serialize(INetDataWriter writer)
{
@@ -55,6 +59,7 @@ public void Serialize(INetDataWriter writer)
{
return;
}
+ FightData.Serialize(writer);
using var ms = new MemoryStream();
using (var wr = new BinaryWriter(ms))
{
@@ -63,6 +68,7 @@ public void Serialize(INetDataWriter writer)
ReactorStorage.Export(wr);
WarpStorage.Export(wr);
Forge.Export(wr);
+ ConstructionModule.Export(wr);
}
var export = ms.ToArray();
writer.Put(export.Length);
@@ -72,14 +78,16 @@ public void Serialize(INetDataWriter writer)
public void Deserialize(INetDataReader reader)
{
TechBonuses = new PlayerTechBonuses();
+ FightData = new MechaFightData();
Inventory = new StorageComponent(4);
DeliveryPackage = new DeliveryPackage();
DeliveryPackage.Init();
ReactorStorage = new StorageComponent(4);
WarpStorage = new StorageComponent(1);
Forge = new MechaForge { tasks = [], extraItems = new ItemBundle() };
+ ConstructionModule = new ConstructionModuleComponent();
TechBonuses.Deserialize(reader);
- SandCount = reader.GetInt();
+ SandCount = reader.GetLong();
CoreEnergy = reader.GetDouble();
ReactorEnergy = reader.GetDouble();
var isPayloadPresent = reader.GetBool();
@@ -87,6 +95,7 @@ public void Deserialize(INetDataReader reader)
{
return;
}
+ FightData.Deserialize(reader);
var mechaLength = reader.GetInt();
var mechaBytes = new byte[mechaLength];
reader.GetBytes(mechaBytes, mechaLength);
@@ -97,6 +106,29 @@ public void Deserialize(INetDataReader reader)
ReactorStorage.Import(br);
WarpStorage.Import(br);
Forge.Import(br);
+ ConstructionModule.Import(br);
+ }
+
+ public void UpdateMech(Player destination)
+ {
+ destination.package = Inventory;
+ using (var ms = new MemoryStream())
+ {
+ var bw = new BinaryWriter(ms);
+ DeliveryPackage.Export(bw);
+ ms.Seek(0, SeekOrigin.Begin);
+ var br = new BinaryReader(ms);
+ destination.deliveryPackage.Import(br);
+ DeliveryPackage = destination.deliveryPackage;
+ }
+ destination.mecha.coreEnergy = CoreEnergy;
+ destination.mecha.reactorEnergy = ReactorEnergy;
+ destination.mecha.forge = Forge;
+ destination.mecha.reactorStorage = ReactorStorage;
+ destination.mecha.warpStorage = WarpStorage;
+ destination.mecha.constructionModule = ConstructionModule;
+ FightData.UpdateMech(destination);
+ destination.SetSandCount(SandCount);
}
public void Import(INetDataReader reader, int revision)
@@ -108,6 +140,7 @@ public void Import(INetDataReader reader, int revision)
ReactorStorage = new StorageComponent(4);
WarpStorage = new StorageComponent(1);
Forge = new MechaForge { tasks = [], extraItems = new ItemBundle() };
+ ConstructionModule = new ConstructionModuleComponent();
TechBonuses.Import(reader, revision);
SandCount = reader.GetInt();
CoreEnergy = reader.GetDouble();
@@ -117,6 +150,10 @@ public void Import(INetDataReader reader, int revision)
{
return;
}
+ if (revision >= 8)
+ {
+ FightData.Deserialize(reader);
+ }
var mechaLength = reader.GetInt();
var mechaBytes = new byte[mechaLength];
reader.GetBytes(mechaBytes, mechaLength);
@@ -130,5 +167,9 @@ public void Import(INetDataReader reader, int revision)
ReactorStorage.Import(br);
WarpStorage.Import(br);
Forge.Import(br);
+ if (revision >= 8)
+ {
+ ConstructionModule.Import(br);
+ }
}
}
diff --git a/NebulaModel/DataStructures/MechaFightData.cs b/NebulaModel/DataStructures/MechaFightData.cs
new file mode 100644
index 000000000..58659b637
--- /dev/null
+++ b/NebulaModel/DataStructures/MechaFightData.cs
@@ -0,0 +1,190 @@
+#region
+using System.IO;
+using NebulaAPI.DataStructures;
+using NebulaAPI.Interfaces;
+#endregion
+
+namespace NebulaModel.DataStructures
+{
+ internal class MechaFightData : IMechaFightData
+ {
+ public MechaFightData()
+ {
+ // This is needed for the serialization and deserialization
+ AmmoStorage = new StorageComponent(3);
+ BombStorage = new StorageComponent(1);
+ FighterStorage = new StorageComponent(5);
+ GroundCombatModule = new CombatModuleComponent();
+ SpaceCombatModule = new CombatModuleComponent();
+
+ GroundCombatModule.Reset();
+ SpaceCombatModule.Reset();
+ GroundCombatModule.Init(GameMain.data);
+ SpaceCombatModule.Init(GameMain.data);
+ GroundCombatModule.Setup(1, GameMain.data);
+ SpaceCombatModule.Setup(3, GameMain.data);
+ }
+ public MechaFightData(Player player)
+ {
+ AutoReplenishFuel = player.mecha.autoReplenishFuel;
+ AutoReplenishAmmo = player.mecha.autoReplenishAmmo;
+ AutoReplenishHangar = player.mecha.autoReplenishHangar;
+ Hp = player.mecha.hp;
+ EnergyShieldEnergy = player.mecha.energyShieldEnergy;
+ AmmoItemId = player.mecha.ammoItemId;
+ AmmoInc = player.mecha.ammoInc;
+ AmmoBulletCount = player.mecha.ammoBulletCount;
+ AmmoSelectSlot = player.mecha.ammoSelectSlot;
+ AmmoMuzzleFire = player.mecha.ammoMuzzleFire;
+ AmmoRoundFire = player.mecha.ammoRoundFire;
+ AmmoMuzzleIndex = player.mecha.ammoMuzzleIndex;
+ LaserActive = player.mecha.laserActive;
+ LaserRecharging = player.mecha.laserRecharging;
+ LaserEnergy = player.mecha.laserEnergy;
+ LaserFire = player.mecha.laserFire;
+ BombFire = player.mecha.bombFire;
+ AmmoStorage = player.mecha.ammoStorage;
+ BombStorage = player.mecha.bombStorage;
+ AmmoHatredTarget = player.mecha.ammoHatredTarget;
+ LaserHatredTarget = player.mecha.laserHatredTarget;
+ FighterStorage = player.mecha.fighterStorage;
+ GroundCombatModule = player.mecha.groundCombatModule;
+ SpaceCombatModule = player.mecha.spaceCombatModule;
+ }
+ public bool AutoReplenishFuel { get; set; }
+ public bool AutoReplenishAmmo { get; set; }
+ public bool AutoReplenishHangar { get; set; }
+ public int Hp { get; set; }
+ public long EnergyShieldEnergy { get; set; }
+ public int AmmoItemId { get; set; }
+ public int AmmoInc { get; set; }
+ public int AmmoBulletCount { get; set; }
+ public int AmmoSelectSlot { get; set; }
+ public int AmmoMuzzleFire { get; set; }
+ public int AmmoRoundFire { get; set; }
+ public int AmmoMuzzleIndex { get; set; }
+ public bool LaserActive { get; set; }
+ public bool LaserRecharging { get; set; }
+ public long LaserEnergy { get; set; }
+ public int LaserFire { get; set; }
+ public int BombFire { get; set; }
+ public StorageComponent AmmoStorage { get; set; }
+ public StorageComponent BombStorage { get; set; }
+ public EnemyHatredTarget AmmoHatredTarget { get; set; }
+ public EnemyHatredTarget LaserHatredTarget { get; set; }
+ public StorageComponent FighterStorage { get; set; }
+ public CombatModuleComponent GroundCombatModule { get; set; }
+ public CombatModuleComponent SpaceCombatModule { get; set; }
+ public void Serialize(INetDataWriter writer)
+ {
+ writer.Put(AutoReplenishFuel);
+ writer.Put(AutoReplenishAmmo);
+ writer.Put(AutoReplenishHangar);
+ writer.Put(Hp);
+ writer.Put(EnergyShieldEnergy);
+ writer.Put(AmmoItemId);
+ writer.Put(AmmoInc);
+ writer.Put(AmmoBulletCount);
+ writer.Put(AmmoSelectSlot);
+ writer.Put(AmmoMuzzleFire);
+ writer.Put(AmmoRoundFire);
+ writer.Put(AmmoMuzzleIndex);
+ writer.Put(LaserActive);
+ writer.Put(LaserRecharging);
+ writer.Put(LaserEnergy);
+ writer.Put(LaserFire);
+ writer.Put(BombFire);
+
+ using var ms = new MemoryStream();
+ using (var wr = new BinaryWriter(ms))
+ {
+ AmmoStorage.Export(wr);
+ BombStorage.Export(wr);
+ AmmoHatredTarget.Export(wr);
+ LaserHatredTarget.Export(wr);
+ FighterStorage.Export(wr);
+ GroundCombatModule.Export(wr);
+ SpaceCombatModule.Export(wr);
+ }
+ var export = ms.ToArray();
+ writer.Put(export.Length);
+ writer.Put(export);
+ }
+ public void Deserialize(INetDataReader reader)
+ {
+ AmmoStorage = new StorageComponent(3);
+ BombStorage = new StorageComponent(1);
+ AmmoHatredTarget = default(EnemyHatredTarget);
+ LaserHatredTarget = default(EnemyHatredTarget);
+ FighterStorage = new StorageComponent(5);
+ GroundCombatModule = new CombatModuleComponent();
+ SpaceCombatModule = new CombatModuleComponent();
+
+ AutoReplenishFuel = reader.GetBool();
+ AutoReplenishAmmo = reader.GetBool();
+ AutoReplenishHangar = reader.GetBool();
+ Hp = reader.GetInt();
+ EnergyShieldEnergy = reader.GetLong();
+ AmmoItemId = reader.GetInt();
+ AmmoInc = reader.GetInt();
+ AmmoBulletCount = reader.GetInt();
+ AmmoSelectSlot = reader.GetInt();
+ AmmoMuzzleFire = reader.GetInt();
+ AmmoRoundFire = reader.GetInt();
+ AmmoMuzzleIndex = reader.GetInt();
+ LaserActive = reader.GetBool();
+ LaserRecharging = reader.GetBool();
+ LaserEnergy = reader.GetLong();
+ LaserFire = reader.GetInt();
+ BombFire = reader.GetInt();
+
+ if (Hp == 0)
+ {
+ // prevent instant death, which can happen when a player joins for the first time and then exits again before sending the first mecha data update.
+ // when the host saves in this situation, the Hp would be set to 0 and on every next join the client would be insta killed. lol
+ Hp = GameMain.mainPlayer.mecha.hpMaxApplied;
+ }
+
+ var fightLength = reader.GetInt();
+ var fightBytes = new byte[fightLength];
+ reader.GetBytes(fightBytes, fightLength);
+ using var ms = new MemoryStream(fightBytes);
+ using var br = new BinaryReader(ms);
+
+ AmmoStorage.Import(br);
+ BombStorage.Import(br);
+ AmmoHatredTarget.Import(br);
+ LaserHatredTarget.Import(br);
+ FighterStorage.Import(br);
+ GroundCombatModule.Import(br);
+ SpaceCombatModule.Import(br);
+ }
+ public void UpdateMech(Player destination)
+ {
+ destination.mecha.autoReplenishFuel = AutoReplenishFuel;
+ destination.mecha.autoReplenishAmmo = AutoReplenishAmmo;
+ destination.mecha.autoReplenishHangar = AutoReplenishHangar;
+ destination.mecha.hp = Hp;
+ destination.mecha.energyShieldEnergy = EnergyShieldEnergy;
+ destination.mecha.ammoItemId = AmmoItemId;
+ destination.mecha.ammoInc = AmmoInc;
+ destination.mecha.ammoBulletCount = AmmoBulletCount;
+ destination.mecha.ammoSelectSlot = AmmoSelectSlot;
+ destination.mecha.ammoMuzzleFire = AmmoMuzzleFire;
+ destination.mecha.ammoRoundFire = AmmoRoundFire;
+ destination.mecha.ammoMuzzleIndex = AmmoMuzzleIndex;
+ destination.mecha.laserActive = LaserActive;
+ destination.mecha.laserRecharging = LaserRecharging;
+ destination.mecha.laserEnergy = LaserEnergy;
+ destination.mecha.laserFire = LaserFire;
+ destination.mecha.bombFire = BombFire;
+ destination.mecha.ammoStorage = AmmoStorage;
+ destination.mecha.bombStorage = BombStorage;
+ destination.mecha.ammoHatredTarget = AmmoHatredTarget;
+ destination.mecha.laserHatredTarget = LaserHatredTarget;
+ destination.mecha.fighterStorage = FighterStorage;
+ destination.mecha.groundCombatModule = GroundCombatModule;
+ destination.mecha.spaceCombatModule = SpaceCombatModule;
+ }
+ }
+}
diff --git a/NebulaModel/DataStructures/PlayerPosition.cs b/NebulaModel/DataStructures/PlayerPosition.cs
new file mode 100644
index 000000000..a65eb02c3
--- /dev/null
+++ b/NebulaModel/DataStructures/PlayerPosition.cs
@@ -0,0 +1,15 @@
+using UnityEngine;
+
+namespace NebulaModel.DataStructures;
+
+public class PlayerPosition
+{
+ public PlayerPosition(Vector3 position, int planetId)
+ {
+ Position = position;
+ PlanetId = planetId;
+ }
+
+ public Vector3 Position { get; set; }
+ public int PlanetId { get; set; }
+}
diff --git a/NebulaModel/NebulaModel.csproj b/NebulaModel/NebulaModel.csproj
index 9a118cda4..52902530e 100644
--- a/NebulaModel/NebulaModel.csproj
+++ b/NebulaModel/NebulaModel.csproj
@@ -1,8 +1,8 @@
-
-
+
+
@@ -15,6 +15,6 @@
-
+
\ No newline at end of file
diff --git a/NebulaModel/NetworkProvider.cs b/NebulaModel/NetworkProvider.cs
index 3748650fc..bf7f68ebd 100644
--- a/NebulaModel/NetworkProvider.cs
+++ b/NebulaModel/NetworkProvider.cs
@@ -12,11 +12,11 @@ public abstract class NetworkProvider : INetworkProvider
{
protected NetworkProvider(IPlayerManager playerManager)
{
- PacketProcessor = new NetPacketProcessor();
+ PacketProcessor = new NebulaNetPacketProcessor();
PlayerManager = playerManager;
}
- public NetPacketProcessor PacketProcessor { get; set; }
+ public NebulaNetPacketProcessor PacketProcessor { get; set; }
public abstract void Dispose();
diff --git a/NebulaModel/Networking/NebulaConnection.cs b/NebulaModel/Networking/NebulaConnection.cs
index c769f1495..a7f21b5dc 100644
--- a/NebulaModel/Networking/NebulaConnection.cs
+++ b/NebulaModel/Networking/NebulaConnection.cs
@@ -15,13 +15,13 @@ namespace NebulaModel.Networking;
public class NebulaConnection : INebulaConnection
{
- private readonly NetPacketProcessor packetProcessor;
+ private readonly NebulaNetPacketProcessor packetProcessor;
private readonly EndPoint peerEndpoint;
private readonly WebSocket peerSocket;
private readonly Queue pendingPackets = new();
private bool enable = true;
- public NebulaConnection(WebSocket peerSocket, EndPoint peerEndpoint, NetPacketProcessor packetProcessor)
+ public NebulaConnection(WebSocket peerSocket, EndPoint peerEndpoint, NebulaNetPacketProcessor packetProcessor)
{
this.peerEndpoint = peerEndpoint;
this.peerSocket = peerSocket;
diff --git a/NebulaModel/Networking/PacketUtils.cs b/NebulaModel/Networking/PacketUtils.cs
index 32cf724fe..1837b57c8 100644
--- a/NebulaModel/Networking/PacketUtils.cs
+++ b/NebulaModel/Networking/PacketUtils.cs
@@ -2,6 +2,7 @@
using System;
using System.Linq;
+using System.Linq.Expressions;
using System.Reflection;
using NebulaAPI;
using NebulaAPI.Packets;
@@ -29,19 +30,27 @@ public static void RegisterAllPacketNestedTypesInAssembly(Assembly assembly, Net
{
Log.Debug($"Registering Nested Type: {type.Name}");
}
+
if (type.IsClass)
{
var registerMethod = packetProcessor.GetType().GetMethods()
.Where(m => m.Name == nameof(NetPacketProcessor.RegisterNestedType))
.FirstOrDefault(m =>
- m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.Name.Equals(typeof(Func<>).Name))
+ m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.Name.Equals(typeof(Func<>).Name))!
.MakeGenericMethod(type);
- var delegateMethod = packetProcessor.GetType().GetMethod(nameof(NetPacketProcessor.CreateNestedClassInstance))
+ var constructorMethod = typeof(Activator)
+ .GetMethods()
+ .Where((info => info.GetParameters().Length == 0))
+ .FirstOrDefault()!
.MakeGenericMethod(type);
+
+ // create a Func delegate from the object's constructor to pass into NetPacketProcessor.RegisterNestedType
var funcType = typeof(Func<>).MakeGenericType(type);
- var callback = Delegate.CreateDelegate(funcType, packetProcessor, delegateMethod);
- registerMethod.Invoke(packetProcessor, new object[] { callback });
+ var constructorDelegate = Delegate.CreateDelegate(funcType, constructorMethod);
+
+ // Invoke NetPacketProcessor.RegisterNestedType(Func constructor)
+ registerMethod.Invoke(packetProcessor, new object[] { constructorDelegate });
}
else if (type.IsValueType)
{
@@ -66,8 +75,10 @@ private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck)
{
return true;
}
+
toCheck = toCheck.BaseType;
}
+
return false;
}
diff --git a/NebulaModel/Networking/Serialization/FastBitConverter.cs b/NebulaModel/Networking/Serialization/FastBitConverter.cs
deleted file mode 100644
index 3081d4d7c..000000000
--- a/NebulaModel/Networking/Serialization/FastBitConverter.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-#region
-
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-
-#endregion
-
-namespace NebulaModel.Networking.Serialization;
-
-public static class FastBitConverter
-{
- private static void WriteLittleEndian(IList buffer, int offset, ulong data)
- {
-#if BIGENDIAN
- buffer[offset + 7] = (byte)(data);
- buffer[offset + 6] = (byte)(data >> 8);
- buffer[offset + 5] = (byte)(data >> 16);
- buffer[offset + 4] = (byte)(data >> 24);
- buffer[offset + 3] = (byte)(data >> 32);
- buffer[offset + 2] = (byte)(data >> 40);
- buffer[offset + 1] = (byte)(data >> 48);
- buffer[offset] = (byte)(data >> 56);
-#else
- buffer[offset] = (byte)data;
- buffer[offset + 1] = (byte)(data >> 8);
- buffer[offset + 2] = (byte)(data >> 16);
- buffer[offset + 3] = (byte)(data >> 24);
- buffer[offset + 4] = (byte)(data >> 32);
- buffer[offset + 5] = (byte)(data >> 40);
- buffer[offset + 6] = (byte)(data >> 48);
- buffer[offset + 7] = (byte)(data >> 56);
-#endif
- }
-
- private static void WriteLittleEndian(IList buffer, int offset, int data)
- {
-#if BIGENDIAN
- buffer[offset + 3] = (byte)(data);
- buffer[offset + 2] = (byte)(data >> 8);
- buffer[offset + 1] = (byte)(data >> 16);
- buffer[offset] = (byte)(data >> 24);
-#else
- buffer[offset] = (byte)data;
- buffer[offset + 1] = (byte)(data >> 8);
- buffer[offset + 2] = (byte)(data >> 16);
- buffer[offset + 3] = (byte)(data >> 24);
-#endif
- }
-
- private static void WriteLittleEndian(IList buffer, int offset, short data)
- {
-#if BIGENDIAN
- buffer[offset + 1] = (byte)(data);
- buffer[offset] = (byte)(data >> 8);
-#else
- buffer[offset] = (byte)data;
- buffer[offset + 1] = (byte)(data >> 8);
-#endif
- }
-
- public static void GetBytes(byte[] bytes, int startIndex, double value)
- {
- var ch = new ConverterHelperDouble { Adouble = value };
- WriteLittleEndian(bytes, startIndex, ch.Along);
- }
-
- public static void GetBytes(byte[] bytes, int startIndex, float value)
- {
- var ch = new ConverterHelperFloat { Afloat = value };
- WriteLittleEndian(bytes, startIndex, ch.Aint);
- }
-
- public static void GetBytes(IEnumerable bytes, int startIndex, short value)
- {
- WriteLittleEndian(bytes as IList, startIndex, value);
- }
-
- public static void GetBytes(IEnumerable bytes, int startIndex, ushort value)
- {
- WriteLittleEndian(bytes as IList, startIndex, (short)value);
- }
-
- public static void GetBytes(byte[] bytes, int startIndex, int value)
- {
- WriteLittleEndian(bytes, startIndex, value);
- }
-
- public static void GetBytes(byte[] bytes, int startIndex, uint value)
- {
- WriteLittleEndian(bytes, startIndex, (int)value);
- }
-
- public static void GetBytes(byte[] bytes, int startIndex, long value)
- {
- WriteLittleEndian(bytes, startIndex, (ulong)value);
- }
-
- public static void GetBytes(byte[] bytes, int startIndex, ulong value)
- {
- WriteLittleEndian(bytes, startIndex, value);
- }
-
- [StructLayout(LayoutKind.Explicit)]
- private struct ConverterHelperDouble
- {
- [FieldOffset(0)] public ulong Along;
-
- [FieldOffset(0)] public double Adouble;
- }
-
- [StructLayout(LayoutKind.Explicit)]
- private struct ConverterHelperFloat
- {
- [FieldOffset(0)] public int Aint;
-
- [FieldOffset(0)] public float Afloat;
- }
-}
diff --git a/NebulaModel/Networking/Serialization/LiteNetLib/FastBitConverter.cs b/NebulaModel/Networking/Serialization/LiteNetLib/FastBitConverter.cs
new file mode 100644
index 000000000..7bf53c652
--- /dev/null
+++ b/NebulaModel/Networking/Serialization/LiteNetLib/FastBitConverter.cs
@@ -0,0 +1,174 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace NebulaModel.Networking.Serialization
+{
+ public static class FastBitConverter
+ {
+#if (LITENETLIB_UNSAFE || LITENETLIB_UNSAFELIB || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER) && !BIGENDIAN
+#if LITENETLIB_UNSAFE
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe void GetBytes(byte[] bytes, int startIndex, T value) where T : unmanaged
+ {
+ int size = sizeof(T);
+ if (bytes.Length < startIndex + size)
+ ThrowIndexOutOfRangeException();
+#if LITENETLIB_UNSAFELIB || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER
+ Unsafe.As(ref bytes[startIndex]) = value;
+#else
+ fixed (byte* ptr = &bytes[startIndex])
+ {
+#if UNITY_ANDROID
+ // On some android systems, assigning *(T*)ptr throws a NRE if
+ // the ptr isn't aligned (i.e. if Position is 1,2,3,5, etc.).
+ // Here we have to use memcpy.
+ //
+ // => we can't get a pointer of a struct in C# without
+ // marshalling allocations
+ // => instead, we stack allocate an array of type T and use that
+ // => stackalloc avoids GC and is very fast. it only works for
+ // value types, but all blittable types are anyway.
+ T* valueBuffer = stackalloc T[1] { value };
+ UnsafeUtility.MemCpy(ptr, valueBuffer, size);
+#else
+ *(T*)ptr = value;
+#endif
+ }
+#endif
+ }
+#else
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, T value) where T : unmanaged
+ {
+ if (bytes.Length < startIndex + Unsafe.SizeOf())
+ ThrowIndexOutOfRangeException();
+ Unsafe.As(ref bytes[startIndex]) = value;
+ }
+#endif
+
+ private static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
+#else
+ [StructLayout(LayoutKind.Explicit)]
+ private struct ConverterHelperDouble
+ {
+ [FieldOffset(0)]
+ public ulong Along;
+
+ [FieldOffset(0)]
+ public double Adouble;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ private struct ConverterHelperFloat
+ {
+ [FieldOffset(0)]
+ public int Aint;
+
+ [FieldOffset(0)]
+ public float Afloat;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WriteLittleEndian(byte[] buffer, int offset, ulong data)
+ {
+#if BIGENDIAN
+ buffer[offset + 7] = (byte)(data);
+ buffer[offset + 6] = (byte)(data >> 8);
+ buffer[offset + 5] = (byte)(data >> 16);
+ buffer[offset + 4] = (byte)(data >> 24);
+ buffer[offset + 3] = (byte)(data >> 32);
+ buffer[offset + 2] = (byte)(data >> 40);
+ buffer[offset + 1] = (byte)(data >> 48);
+ buffer[offset ] = (byte)(data >> 56);
+#else
+ buffer[offset] = (byte)(data);
+ buffer[offset + 1] = (byte)(data >> 8);
+ buffer[offset + 2] = (byte)(data >> 16);
+ buffer[offset + 3] = (byte)(data >> 24);
+ buffer[offset + 4] = (byte)(data >> 32);
+ buffer[offset + 5] = (byte)(data >> 40);
+ buffer[offset + 6] = (byte)(data >> 48);
+ buffer[offset + 7] = (byte)(data >> 56);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WriteLittleEndian(byte[] buffer, int offset, int data)
+ {
+#if BIGENDIAN
+ buffer[offset + 3] = (byte)(data);
+ buffer[offset + 2] = (byte)(data >> 8);
+ buffer[offset + 1] = (byte)(data >> 16);
+ buffer[offset ] = (byte)(data >> 24);
+#else
+ buffer[offset] = (byte)(data);
+ buffer[offset + 1] = (byte)(data >> 8);
+ buffer[offset + 2] = (byte)(data >> 16);
+ buffer[offset + 3] = (byte)(data >> 24);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void WriteLittleEndian(byte[] buffer, int offset, short data)
+ {
+#if BIGENDIAN
+ buffer[offset + 1] = (byte)(data);
+ buffer[offset ] = (byte)(data >> 8);
+#else
+ buffer[offset] = (byte)(data);
+ buffer[offset + 1] = (byte)(data >> 8);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, double value)
+ {
+ ConverterHelperDouble ch = new ConverterHelperDouble { Adouble = value };
+ WriteLittleEndian(bytes, startIndex, ch.Along);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, float value)
+ {
+ ConverterHelperFloat ch = new ConverterHelperFloat { Afloat = value };
+ WriteLittleEndian(bytes, startIndex, ch.Aint);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, short value)
+ {
+ WriteLittleEndian(bytes, startIndex, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, ushort value)
+ {
+ WriteLittleEndian(bytes, startIndex, (short)value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, int value)
+ {
+ WriteLittleEndian(bytes, startIndex, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, uint value)
+ {
+ WriteLittleEndian(bytes, startIndex, (int)value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, long value)
+ {
+ WriteLittleEndian(bytes, startIndex, (ulong)value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void GetBytes(byte[] bytes, int startIndex, ulong value)
+ {
+ WriteLittleEndian(bytes, startIndex, value);
+ }
+#endif
+ }
+}
diff --git a/NebulaModel/Networking/Serialization/LiteNetLib/NetDataReader.cs b/NebulaModel/Networking/Serialization/LiteNetLib/NetDataReader.cs
new file mode 100644
index 000000000..3427b072e
--- /dev/null
+++ b/NebulaModel/Networking/Serialization/LiteNetLib/NetDataReader.cs
@@ -0,0 +1,674 @@
+using System;
+using System.Net;
+using System.Runtime.CompilerServices;
+using NebulaAPI.Interfaces;
+
+namespace NebulaModel.Networking.Serialization
+{
+ public class NetDataReader : INetDataReader
+ {
+ protected byte[] _data;
+ protected int _position;
+ protected int _dataSize;
+ private int _offset;
+
+ public byte[] RawData
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data;
+ }
+ public int RawDataSize
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _dataSize;
+ }
+ public int UserDataOffset
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _offset;
+ }
+ public int UserDataSize
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _dataSize - _offset;
+ }
+ public bool IsNull
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data == null;
+ }
+ public int Position
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _position;
+ }
+ public bool EndOfData
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _position == _dataSize;
+ }
+ public int AvailableBytes
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _dataSize - _position;
+ }
+
+ public void SkipBytes(int count)
+ {
+ _position += count;
+ }
+
+ public void SetPosition(int position)
+ {
+ _position = position;
+ }
+
+ public void SetSource(INetDataWriter dataWriter)
+ {
+ _data = dataWriter.Data;
+ _position = 0;
+ _offset = 0;
+ _dataSize = dataWriter.Length;
+ }
+
+ public void SetSource(byte[] source)
+ {
+ _data = source;
+ _position = 0;
+ _offset = 0;
+ _dataSize = source.Length;
+ }
+
+ public void SetSource(byte[] source, int offset, int maxSize)
+ {
+ _data = source;
+ _position = offset;
+ _offset = offset;
+ _dataSize = maxSize;
+ }
+
+ public NetDataReader()
+ {
+
+ }
+
+ public NetDataReader(INetDataWriter writer)
+ {
+ SetSource(writer);
+ }
+
+ public NetDataReader(byte[] source)
+ {
+ SetSource(source);
+ }
+
+ public NetDataReader(byte[] source, int offset, int maxSize)
+ {
+ SetSource(source, offset, maxSize);
+ }
+
+ #region GetMethods
+ public IPEndPoint GetNetEndPoint()
+ {
+ string host = GetString(1000);
+ int port = GetInt();
+ return NetUtils.MakeEndPoint(host, port);
+ }
+
+ public byte GetByte()
+ {
+ byte res = _data[_position];
+ _position++;
+ return res;
+ }
+
+ public sbyte GetSByte()
+ {
+ return (sbyte)GetByte();
+ }
+
+ public T[] GetArray(int size)
+ {
+ int length = BitConverter.ToInt32(_data, _position);
+ _position += 4;
+ T[] result = new T[length];
+ length *= size;
+ Buffer.BlockCopy(_data, _position, result, 0, length);
+ _position += length;
+ return result;
+ }
+
+ public bool[] GetBoolArray()
+ {
+ return GetArray(1);
+ }
+
+ public ushort[] GetUShortArray()
+ {
+ return GetArray(2);
+ }
+
+ public short[] GetShortArray()
+ {
+ return GetArray(2);
+ }
+
+ public int[] GetIntArray()
+ {
+ return GetArray(4);
+ }
+
+ public uint[] GetUIntArray()
+ {
+ return GetArray(4);
+ }
+
+ public float[] GetFloatArray()
+ {
+ return GetArray(4);
+ }
+
+ public double[] GetDoubleArray()
+ {
+ return GetArray(8);
+ }
+
+ public long[] GetLongArray()
+ {
+ return GetArray(8);
+ }
+
+ public ulong[] GetULongArray()
+ {
+ return GetArray(8);
+ }
+
+ public string[] GetStringArray()
+ {
+ ushort length = GetUShort();
+ string[] arr = new string[length];
+ for (int i = 0; i < length; i++)
+ {
+ arr[i] = GetString();
+ }
+ return arr;
+ }
+
+ ///
+ /// Note that "maxStringLength" only limits the number of characters in a string, not its size in bytes.
+ /// Strings that exceed this parameter are returned as empty
+ ///
+ public string[] GetStringArray(int maxStringLength)
+ {
+ ushort length = GetUShort();
+ string[] arr = new string[length];
+ for (int i = 0; i < length; i++)
+ {
+ arr[i] = GetString(maxStringLength);
+ }
+ return arr;
+ }
+
+ public bool GetBool()
+ {
+ return GetByte() == 1;
+ }
+
+ public char GetChar()
+ {
+ return (char)GetUShort();
+ }
+
+ public ushort GetUShort()
+ {
+ ushort result = BitConverter.ToUInt16(_data, _position);
+ _position += 2;
+ return result;
+ }
+
+ public short GetShort()
+ {
+ short result = BitConverter.ToInt16(_data, _position);
+ _position += 2;
+ return result;
+ }
+
+ public long GetLong()
+ {
+ long result = BitConverter.ToInt64(_data, _position);
+ _position += 8;
+ return result;
+ }
+
+ public ulong GetULong()
+ {
+ ulong result = BitConverter.ToUInt64(_data, _position);
+ _position += 8;
+ return result;
+ }
+
+ public int GetInt()
+ {
+ int result = BitConverter.ToInt32(_data, _position);
+ _position += 4;
+ return result;
+ }
+
+ public uint GetUInt()
+ {
+ uint result = BitConverter.ToUInt32(_data, _position);
+ _position += 4;
+ return result;
+ }
+
+ public float GetFloat()
+ {
+ float result = BitConverter.ToSingle(_data, _position);
+ _position += 4;
+ return result;
+ }
+
+ public double GetDouble()
+ {
+ double result = BitConverter.ToDouble(_data, _position);
+ _position += 8;
+ return result;
+ }
+
+ ///
+ /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
+ ///
+ /// "string.Empty" if value > "maxLength"
+ public string GetString(int maxLength)
+ {
+ ushort size = GetUShort();
+ if (size == 0)
+ {
+ return string.Empty;
+ }
+
+ int actualSize = size - 1;
+ if (actualSize >= NetDataWriter.StringBufferMaxLength)
+ {
+ return null;
+ }
+
+ ArraySegment data = GetBytesSegment(actualSize);
+
+ return (maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(data.Array, data.Offset, data.Count) > maxLength) ?
+ string.Empty :
+ NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count);
+ }
+
+ public string GetString()
+ {
+ ushort size = GetUShort();
+ if (size == 0)
+ {
+ return string.Empty;
+ }
+
+ int actualSize = size - 1;
+ if (actualSize >= NetDataWriter.StringBufferMaxLength)
+ {
+ return null;
+ }
+
+ ArraySegment data = GetBytesSegment(actualSize);
+
+ return NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count);
+ }
+
+ public ArraySegment GetBytesSegment(int count)
+ {
+ ArraySegment segment = new ArraySegment(_data, _position, count);
+ _position += count;
+ return segment;
+ }
+
+ public ArraySegment GetRemainingBytesSegment()
+ {
+ ArraySegment segment = new ArraySegment(_data, _position, AvailableBytes);
+ _position = _data.Length;
+ return segment;
+ }
+
+ public T Get() where T : struct, INetSerializable
+ {
+ var obj = default(T);
+ obj.Deserialize(this);
+ return obj;
+ }
+
+ public T Get(Func constructor) where T : class, INetSerializable
+ {
+ var obj = constructor();
+ obj.Deserialize(this);
+ return obj;
+ }
+
+ public byte[] GetRemainingBytes()
+ {
+ byte[] outgoingData = new byte[AvailableBytes];
+ Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes);
+ _position = _data.Length;
+ return outgoingData;
+ }
+
+ public void GetBytes(byte[] destination, int start, int count)
+ {
+ Buffer.BlockCopy(_data, _position, destination, start, count);
+ _position += count;
+ }
+
+ public void GetBytes(byte[] destination, int count)
+ {
+ Buffer.BlockCopy(_data, _position, destination, 0, count);
+ _position += count;
+ }
+
+ public sbyte[] GetSBytesWithLength()
+ {
+ return GetArray(1);
+ }
+
+ public byte[] GetBytesWithLength()
+ {
+ return GetArray(1);
+ }
+ #endregion
+
+ #region PeekMethods
+
+ public byte PeekByte()
+ {
+ return _data[_position];
+ }
+
+ public sbyte PeekSByte()
+ {
+ return (sbyte)_data[_position];
+ }
+
+ public bool PeekBool()
+ {
+ return _data[_position] == 1;
+ }
+
+ public char PeekChar()
+ {
+ return (char)PeekUShort();
+ }
+
+ public ushort PeekUShort()
+ {
+ return BitConverter.ToUInt16(_data, _position);
+ }
+
+ public short PeekShort()
+ {
+ return BitConverter.ToInt16(_data, _position);
+ }
+
+ public long PeekLong()
+ {
+ return BitConverter.ToInt64(_data, _position);
+ }
+
+ public ulong PeekULong()
+ {
+ return BitConverter.ToUInt64(_data, _position);
+ }
+
+ public int PeekInt()
+ {
+ return BitConverter.ToInt32(_data, _position);
+ }
+
+ public uint PeekUInt()
+ {
+ return BitConverter.ToUInt32(_data, _position);
+ }
+
+ public float PeekFloat()
+ {
+ return BitConverter.ToSingle(_data, _position);
+ }
+
+ public double PeekDouble()
+ {
+ return BitConverter.ToDouble(_data, _position);
+ }
+
+ ///
+ /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
+ ///
+ public string PeekString(int maxLength)
+ {
+ ushort size = PeekUShort();
+ if (size == 0)
+ {
+ return string.Empty;
+ }
+
+ int actualSize = size - 1;
+ if (actualSize >= NetDataWriter.StringBufferMaxLength)
+ {
+ return null;
+ }
+
+ return (maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(_data, _position + 2, actualSize) > maxLength) ?
+ string.Empty :
+ NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, actualSize);
+ }
+
+ public string PeekString()
+ {
+ ushort size = PeekUShort();
+ if (size == 0)
+ {
+ return string.Empty;
+ }
+
+ int actualSize = size - 1;
+ if (actualSize >= NetDataWriter.StringBufferMaxLength)
+ {
+ return null;
+ }
+
+ return NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, actualSize);
+ }
+ #endregion
+
+ #region TryGetMethods
+ public bool TryGetByte(out byte result)
+ {
+ if (AvailableBytes >= 1)
+ {
+ result = GetByte();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetSByte(out sbyte result)
+ {
+ if (AvailableBytes >= 1)
+ {
+ result = GetSByte();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetBool(out bool result)
+ {
+ if (AvailableBytes >= 1)
+ {
+ result = GetBool();
+ return true;
+ }
+ result = false;
+ return false;
+ }
+
+ public bool TryGetChar(out char result)
+ {
+ if (!TryGetUShort(out ushort uShortValue))
+ {
+ result = '\0';
+ return false;
+ }
+ result = (char)uShortValue;
+ return true;
+ }
+
+ public bool TryGetShort(out short result)
+ {
+ if (AvailableBytes >= 2)
+ {
+ result = GetShort();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetUShort(out ushort result)
+ {
+ if (AvailableBytes >= 2)
+ {
+ result = GetUShort();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetInt(out int result)
+ {
+ if (AvailableBytes >= 4)
+ {
+ result = GetInt();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetUInt(out uint result)
+ {
+ if (AvailableBytes >= 4)
+ {
+ result = GetUInt();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetLong(out long result)
+ {
+ if (AvailableBytes >= 8)
+ {
+ result = GetLong();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetULong(out ulong result)
+ {
+ if (AvailableBytes >= 8)
+ {
+ result = GetULong();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetFloat(out float result)
+ {
+ if (AvailableBytes >= 4)
+ {
+ result = GetFloat();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetDouble(out double result)
+ {
+ if (AvailableBytes >= 8)
+ {
+ result = GetDouble();
+ return true;
+ }
+ result = 0;
+ return false;
+ }
+
+ public bool TryGetString(out string result)
+ {
+ if (AvailableBytes >= 2)
+ {
+ ushort strSize = PeekUShort();
+ if (AvailableBytes >= strSize + 1)
+ {
+ result = GetString();
+ return true;
+ }
+ }
+ result = null;
+ return false;
+ }
+
+ public bool TryGetStringArray(out string[] result)
+ {
+ if (!TryGetUShort(out ushort strArrayLength)) {
+ result = null;
+ return false;
+ }
+
+ result = new string[strArrayLength];
+ for (int i = 0; i < strArrayLength; i++)
+ {
+ if (!TryGetString(out result[i]))
+ {
+ result = null;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public bool TryGetBytesWithLength(out byte[] result)
+ {
+ if (AvailableBytes >= 4)
+ {
+ int length = PeekInt();
+ if (length >= 0 && AvailableBytes >= 4 + length)
+ {
+ result = GetBytesWithLength();
+ return true;
+ }
+ }
+ result = null;
+ return false;
+ }
+ #endregion
+
+ public void Clear()
+ {
+ _position = 0;
+ _dataSize = 0;
+ _data = null;
+ }
+ }
+}
diff --git a/NebulaModel/Networking/Serialization/LiteNetLib/NetDataWriter.cs b/NebulaModel/Networking/Serialization/LiteNetLib/NetDataWriter.cs
new file mode 100644
index 000000000..13fb1e67d
--- /dev/null
+++ b/NebulaModel/Networking/Serialization/LiteNetLib/NetDataWriter.cs
@@ -0,0 +1,386 @@
+using System;
+using System.Net;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading;
+using NebulaAPI.Interfaces;
+
+namespace NebulaModel.Networking.Serialization
+{
+ public class NetDataWriter : INetDataWriter
+ {
+ protected byte[] _data;
+ protected int _position;
+ private const int InitialSize = 64;
+ private readonly bool _autoResize;
+
+ public int Capacity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data.Length;
+ }
+
+ public byte[] Data
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data;
+ }
+
+ public int Length
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _position;
+ }
+
+ public static readonly ThreadLocal uTF8Encoding = new ThreadLocal(() => new UTF8Encoding(false, true));
+ public const int StringBufferMaxLength = 65535;
+ private readonly byte[] _stringBuffer = new byte[StringBufferMaxLength];
+
+ public NetDataWriter() : this(true, InitialSize)
+ {
+ }
+
+ public NetDataWriter(bool autoResize) : this(autoResize, InitialSize)
+ {
+ }
+
+ public NetDataWriter(bool autoResize, int initialSize)
+ {
+ _data = new byte[initialSize];
+ _autoResize = autoResize;
+ }
+
+ ///
+ /// Creates NetDataWriter from existing ByteArray
+ ///
+ /// Source byte array
+ /// Copy array to new location or use existing
+ public static NetDataWriter FromBytes(byte[] bytes, bool copy)
+ {
+ if (copy)
+ {
+ var netDataWriter = new NetDataWriter(true, bytes.Length);
+ netDataWriter.Put(bytes);
+ return netDataWriter;
+ }
+ return new NetDataWriter(true, 0) {_data = bytes, _position = bytes.Length};
+ }
+
+ ///
+ /// Creates NetDataWriter from existing ByteArray (always copied data)
+ ///
+ /// Source byte array
+ /// Offset of array
+ /// Length of array
+ public static NetDataWriter FromBytes(byte[] bytes, int offset, int length)
+ {
+ var netDataWriter = new NetDataWriter(true, bytes.Length);
+ netDataWriter.Put(bytes, offset, length);
+ return netDataWriter;
+ }
+
+ public static NetDataWriter FromString(string value)
+ {
+ var netDataWriter = new NetDataWriter();
+ netDataWriter.Put(value);
+ return netDataWriter;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ResizeIfNeed(int newSize)
+ {
+ if (_data.Length < newSize)
+ {
+ Array.Resize(ref _data, Math.Max(newSize, _data.Length * 2));
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void EnsureFit(int additionalSize)
+ {
+ if (_data.Length < _position + additionalSize)
+ {
+ Array.Resize(ref _data, Math.Max(_position + additionalSize, _data.Length * 2));
+ }
+ }
+
+ public void Reset(int size)
+ {
+ ResizeIfNeed(size);
+ _position = 0;
+ }
+
+ public void Reset()
+ {
+ _position = 0;
+ }
+
+ public byte[] CopyData()
+ {
+ byte[] resultData = new byte[_position];
+ Buffer.BlockCopy(_data, 0, resultData, 0, _position);
+ return resultData;
+ }
+
+ ///
+ /// Sets position of NetDataWriter to rewrite previous values
+ ///
+ /// new byte position
+ /// previous position of data writer
+ public int SetPosition(int position)
+ {
+ int prevPosition = _position;
+ _position = position;
+ return prevPosition;
+ }
+
+ public void Put(float value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 4);
+ FastBitConverter.GetBytes(_data, _position, value);
+ _position += 4;
+ }
+
+ public void Put(double value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 8);
+ FastBitConverter.GetBytes(_data, _position, value);
+ _position += 8;
+ }
+
+ public void Put(long value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 8);
+ FastBitConverter.GetBytes(_data, _position, value);
+ _position += 8;
+ }
+
+ public void Put(ulong value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 8);
+ FastBitConverter.GetBytes(_data, _position, value);
+ _position += 8;
+ }
+
+ public void Put(int value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 4);
+ FastBitConverter.GetBytes(_data, _position, value);
+ _position += 4;
+ }
+
+ public void Put(uint value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 4);
+ FastBitConverter.GetBytes(_data, _position, value);
+ _position += 4;
+ }
+
+ public void Put(char value)
+ {
+ Put((ushort)value);
+ }
+
+ public void Put(ushort value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 2);
+ FastBitConverter.GetBytes(_data, _position, value);
+ _position += 2;
+ }
+
+ public void Put(short value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 2);
+ FastBitConverter.GetBytes(_data, _position, value);
+ _position += 2;
+ }
+
+ public void Put(sbyte value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 1);
+ _data[_position] = (byte)value;
+ _position++;
+ }
+
+ public void Put(byte value)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 1);
+ _data[_position] = value;
+ _position++;
+ }
+
+ public void Put(byte[] data, int offset, int length)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + length);
+ Buffer.BlockCopy(data, offset, _data, _position, length);
+ _position += length;
+ }
+
+ public void Put(byte[] data)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + data.Length);
+ Buffer.BlockCopy(data, 0, _data, _position, data.Length);
+ _position += data.Length;
+ }
+
+ public void PutSBytesWithLength(sbyte[] data, int offset, int length)
+ {
+ const int intSize = 4;
+ if (_autoResize)
+ ResizeIfNeed(_position + intSize + length);
+ FastBitConverter.GetBytes(_data, _position, length);
+ Buffer.BlockCopy(data, offset, _data, _position + intSize, length);
+ _position += intSize + length;
+ }
+
+ public void PutSBytesWithLength(sbyte[] data)
+ {
+ PutArray(data, 1);
+ }
+
+ public void PutBytesWithLength(byte[] data, int offset, int length)
+ {
+ if (_autoResize)
+ ResizeIfNeed(_position + 2 + length);
+ FastBitConverter.GetBytes(_data, _position, length);
+ Buffer.BlockCopy(data, offset, _data, _position + 2, length);
+ _position += 2 + length;
+ }
+
+ public void PutBytesWithLength(byte[] data)
+ {
+ PutArray(data, 1);
+ }
+
+ public void Put(bool value)
+ {
+ Put((byte)(value ? 1 : 0));
+ }
+
+ public void PutArray(Array arr, int sz)
+ {
+ const int intSize = 4;
+ int length = arr == null ? 0 : arr.Length;
+ sz *= length;
+ if (_autoResize)
+ ResizeIfNeed(_position + sz + intSize);
+ FastBitConverter.GetBytes(_data, _position, length);
+ if (arr != null)
+ Buffer.BlockCopy(arr, 0, _data, _position + intSize, sz);
+ _position += sz + intSize;
+ }
+
+ public void PutArray(float[] value)
+ {
+ PutArray(value, 4);
+ }
+
+ public void PutArray(double[] value)
+ {
+ PutArray(value, 8);
+ }
+
+ public void PutArray(long[] value)
+ {
+ PutArray(value, 8);
+ }
+
+ public void PutArray(ulong[] value)
+ {
+ PutArray(value, 8);
+ }
+
+ public void PutArray(int[] value)
+ {
+ PutArray(value, 4);
+ }
+
+ public void PutArray(uint[] value)
+ {
+ PutArray(value, 4);
+ }
+
+ public void PutArray(ushort[] value)
+ {
+ PutArray(value, 2);
+ }
+
+ public void PutArray(short[] value)
+ {
+ PutArray(value, 2);
+ }
+
+ public void PutArray(bool[] value)
+ {
+ PutArray(value, 1);
+ }
+
+ public void PutArray(string[] value)
+ {
+ int strArrayLength = value == null ? 0 : value.Length;
+ Put(strArrayLength);
+ for (int i = 0; i < strArrayLength; i++)
+ Put(value[i]);
+ }
+
+ public void PutArray(string[] value, int strMaxLength)
+ {
+ ushort strArrayLength = value == null ? (ushort)0 : (ushort)value.Length;
+ Put(strArrayLength);
+ for (int i = 0; i < strArrayLength; i++)
+ Put(value[i], strMaxLength);
+ }
+
+ public void Put(IPEndPoint endPoint)
+ {
+ Put(endPoint.Address.ToString());
+ Put(endPoint.Port);
+ }
+
+ public void Put(string value)
+ {
+ Put(value, 0);
+ }
+
+ ///
+ /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
+ ///
+ public void Put(string value, int maxLength)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ Put((ushort)0);
+ return;
+ }
+
+ int length = maxLength > 0 && value.Length > maxLength ? maxLength : value.Length;
+ int size = uTF8Encoding.Value.GetBytes(value, 0, length, _stringBuffer, 0);
+
+ if (size == 0 || size >= StringBufferMaxLength)
+ {
+ Put((ushort)0);
+ return;
+ }
+
+ Put(checked((ushort)(size + 1)));
+ Put(_stringBuffer, 0, size);
+ }
+
+ public void Put(T obj) where T : INetSerializable
+ {
+ obj.Serialize(this);
+ }
+ }
+}
diff --git a/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs b/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs
new file mode 100644
index 000000000..b1875cd0a
--- /dev/null
+++ b/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Collections.Generic;
+using NebulaAPI.Interfaces;
+using NebulaAPI.Packets;
+using NebulaModel.Logger;
+
+namespace NebulaModel.Networking.Serialization
+{
+ public class NetPacketProcessor
+ {
+ private static class HashCache
+ {
+ public static readonly ulong Id;
+
+ //FNV-1 64 bit hash
+ static HashCache()
+ {
+ ulong hash = 14695981039346656037UL; //offset
+ string typeName = typeof(T).ToString();
+ for (var i = 0; i < typeName.Length; i++)
+ {
+ hash ^= typeName[i];
+ hash *= 1099511628211UL; //prime
+ }
+ Id = hash;
+ }
+ }
+
+ protected delegate void SubscribeDelegate(NetDataReader reader, object userData);
+
+ protected NetSerializer _netSerializer;
+ protected readonly Dictionary _callbacks = [];
+ private readonly Dictionary _callbacksDebugInfo = [];
+
+ public NetPacketProcessor()
+ {
+ _netSerializer = new NetSerializer();
+ }
+
+ public NetPacketProcessor(int maxStringLength)
+ {
+ _netSerializer = new NetSerializer(maxStringLength);
+ }
+
+ protected virtual ulong GetHash()
+ {
+ return HashCache.Id;
+ }
+
+ protected virtual SubscribeDelegate GetCallbackFromData(NetDataReader reader)
+ {
+ ulong hash = reader.GetULong();
+ if (!_callbacks.TryGetValue(hash, out var action))
+ {
+ Log.Warn($"Unknown packet hash: {hash}");
+ throw new ParseException("Undefined packet in NetDataReader");
+ }
+#if DEBUG
+ if (_callbacksDebugInfo.TryGetValue(hash, out var packetType))
+ {
+ if (!packetType.IsDefined(typeof(HidePacketInDebugLogsAttribute), false))
+ {
+ Log.Debug($"Packet Recv >> {packetType.Name}, Size: {reader.UserDataSize}");
+ }
+ }
+ else
+ {
+ Log.Debug($"Packet Recv >> Unregistered hash {hash}, Size: {reader.UserDataSize}");
+ }
+#endif
+ return action;
+ }
+
+ protected virtual void WriteHash(NetDataWriter writer)
+ {
+ writer.Put(GetHash());
+ }
+
+ ///
+ /// Register nested property type
+ ///
+ /// INetSerializable structure
+ public void RegisterNestedType() where T : struct, INetSerializable
+ {
+ _netSerializer.RegisterNestedType();
+ }
+
+ ///
+ /// Register nested property type
+ ///
+ ///
+ ///
+ public void RegisterNestedType(Action writeDelegate, Func readDelegate)
+ {
+ _netSerializer.RegisterNestedType(writeDelegate, readDelegate);
+ }
+
+ ///
+ /// Register nested property type
+ ///
+ /// INetSerializable class
+ public void RegisterNestedType(Func constructor) where T : class, INetSerializable
+ {
+ _netSerializer.RegisterNestedType(constructor);
+ }
+
+ ///
+ /// Reads all available data from NetDataReader and calls OnReceive delegates
+ ///
+ /// NetDataReader with packets data
+ public void ReadAllPackets(NetDataReader reader)
+ {
+ while (reader.AvailableBytes > 0)
+ ReadPacket(reader);
+ }
+
+ ///
+ /// Reads all available data from NetDataReader and calls OnReceive delegates
+ ///
+ /// NetDataReader with packets data
+ /// Argument that passed to OnReceivedEvent
+ /// Malformed packet
+ public void ReadAllPackets(NetDataReader reader, object userData)
+ {
+ while (reader.AvailableBytes > 0)
+ ReadPacket(reader, userData);
+ }
+
+ ///
+ /// Reads one packet from NetDataReader and calls OnReceive delegate
+ ///
+ /// NetDataReader with packet
+ /// Malformed packet
+ public void ReadPacket(NetDataReader reader)
+ {
+ ReadPacket(reader, null);
+ }
+
+ public void Write(NetDataWriter writer, T packet) where T : class, new()
+ {
+ WriteHash(writer);
+ _netSerializer.Serialize(writer, packet);
+ }
+
+ public void WriteNetSerializable(NetDataWriter writer, ref T packet) where T : INetSerializable
+ {
+ WriteHash(writer);
+ packet.Serialize(writer);
+ }
+
+ ///
+ /// Reads one packet from NetDataReader and calls OnReceive delegate
+ ///
+ /// NetDataReader with packet
+ /// Argument that passed to OnReceivedEvent
+ /// Malformed packet
+ public void ReadPacket(NetDataReader reader, object userData)
+ {
+ GetCallbackFromData(reader)(reader, userData);
+ }
+
+ ///
+ /// Register and subscribe to packet receive event
+ ///
+ /// event that will be called when packet deserialized with ReadPacket method
+ /// Method that constructs packet instead of slow Activator.CreateInstance
+ /// 's fields are not supported, or it has no fields
+ public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new()
+ {
+ _netSerializer.Register();
+ _callbacks[GetHash()] = (reader, userData) =>
+ {
+ var reference = packetConstructor();
+ _netSerializer.Deserialize(reader, reference);
+ onReceive(reference);
+ };
+ _callbacksDebugInfo[GetHash()] = typeof(T);
+ }
+
+ ///
+ /// Register and subscribe to packet receive event (with userData)
+ ///
+ /// event that will be called when packet deserialized with ReadPacket method
+ /// Method that constructs packet instead of slow Activator.CreateInstance
+ /// 's fields are not supported, or it has no fields
+ public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new()
+ {
+ _netSerializer.Register();
+ _callbacks[GetHash()] = (reader, userData) =>
+ {
+ var reference = packetConstructor();
+ _netSerializer.Deserialize(reader, reference);
+ onReceive(reference, (TUserData)userData);
+ };
+ _callbacksDebugInfo[GetHash()] = typeof(T);
+ }
+
+ ///
+ /// Register and subscribe to packet receive event
+ /// This method will overwrite last received packet class on receive (less garbage)
+ ///
+ /// event that will be called when packet deserialized with ReadPacket method
+ /// 's fields are not supported, or it has no fields
+ public void SubscribeReusable(Action onReceive) where T : class, new()
+ {
+ _netSerializer.Register();
+ var reference = new T();
+ _callbacks[GetHash()] = (reader, userData) =>
+ {
+ _netSerializer.Deserialize(reader, reference);
+ onReceive(reference);
+ };
+ _callbacksDebugInfo[GetHash()] = typeof(T);
+ }
+
+ ///
+ /// Register and subscribe to packet receive event
+ /// This method will overwrite last received packet class on receive (less garbage)
+ ///
+ /// event that will be called when packet deserialized with ReadPacket method
+ /// 's fields are not supported, or it has no fields
+ public void SubscribeReusable(Action onReceive) where T : class, new()
+ {
+ _netSerializer.Register();
+ var reference = new T();
+ _callbacks[GetHash()] = (reader, userData) =>
+ {
+ _netSerializer.Deserialize(reader, reference);
+ onReceive(reference, (TUserData)userData);
+ };
+ _callbacksDebugInfo[GetHash()] = typeof(T);
+ }
+
+ public void SubscribeNetSerializable(
+ Action onReceive,
+ Func packetConstructor) where T : INetSerializable
+ {
+ _callbacks[GetHash()] = (reader, userData) =>
+ {
+ var pkt = packetConstructor();
+ pkt.Deserialize(reader);
+ onReceive(pkt, (TUserData)userData);
+ };
+ _callbacksDebugInfo[GetHash()] = typeof(T);
+ }
+
+ public void SubscribeNetSerializable(
+ Action onReceive,
+ Func packetConstructor) where T : INetSerializable
+ {
+ _callbacks[GetHash()] = (reader, userData) =>
+ {
+ var pkt = packetConstructor();
+ pkt.Deserialize(reader);
+ onReceive(pkt);
+ };
+ _callbacksDebugInfo[GetHash()] = typeof(T);
+ }
+
+ public void SubscribeNetSerializable(
+ Action onReceive) where T : INetSerializable, new()
+ {
+ var reference = new T();
+ _callbacks[GetHash()] = (reader, userData) =>
+ {
+ reference.Deserialize(reader);
+ onReceive(reference, (TUserData)userData);
+ };
+ _callbacksDebugInfo[GetHash()] = typeof(T);
+ }
+
+ public void SubscribeNetSerializable(
+ Action onReceive) where T : INetSerializable, new()
+ {
+ var reference = new T();
+ _callbacks[GetHash()] = (reader, userData) =>
+ {
+ reference.Deserialize(reader);
+ onReceive(reference);
+ };
+ _callbacksDebugInfo[GetHash()] = typeof(T);
+ }
+
+ ///
+ /// Remove any subscriptions by type
+ ///
+ /// Packet type
+ /// true if remove is success
+ public bool RemoveSubscription()
+ {
+ _callbacksDebugInfo.Remove(GetHash());
+ return _callbacks.Remove(GetHash());
+ }
+ }
+}
diff --git a/NebulaModel/Networking/Serialization/LiteNetLib/NetSerializer.cs b/NebulaModel/Networking/Serialization/LiteNetLib/NetSerializer.cs
new file mode 100644
index 000000000..946fe044d
--- /dev/null
+++ b/NebulaModel/Networking/Serialization/LiteNetLib/NetSerializer.cs
@@ -0,0 +1,739 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Reflection;
+using System.Runtime.Serialization;
+using NebulaAPI.Interfaces;
+
+namespace NebulaModel.Networking.Serialization
+{
+ public class InvalidTypeException : ArgumentException
+ {
+ public InvalidTypeException(string message) : base(message) { }
+ }
+
+ public class ParseException : Exception
+ {
+ public ParseException(string message) : base(message) { }
+ }
+
+ public class NetSerializer
+ {
+ public enum CallType
+ {
+ Basic,
+ Array,
+ List
+ }
+
+ public abstract class FastCall
+ {
+ public CallType Type;
+ public virtual void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { Type = type; }
+ public abstract void Read(T inf, NetDataReader r);
+ public abstract void Write(T inf, NetDataWriter w);
+ public abstract void ReadArray(T inf, NetDataReader r);
+ public abstract void WriteArray(T inf, NetDataWriter w);
+ public abstract void ReadList(T inf, NetDataReader r);
+ public abstract void WriteList(T inf, NetDataWriter w);
+ }
+
+ protected abstract class FastCallSpecific : FastCall
+ {
+ protected Func Getter;
+ protected Action Setter;
+ protected Func GetterArr;
+ protected Action SetterArr;
+ protected Func> GetterList;
+ protected Action> SetterList;
+
+ public override void ReadArray(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); }
+ public override void WriteArray(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); }
+ public override void ReadList(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); }
+ public override void WriteList(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); }
+
+ protected TProperty[] ReadArrayHelper(TClass inf, NetDataReader r)
+ {
+ ushort count = r.GetUShort();
+ var arr = GetterArr(inf);
+ arr = arr == null || arr.Length != count ? new TProperty[count] : arr;
+ SetterArr(inf, arr);
+ return arr;
+ }
+
+ protected TProperty[] WriteArrayHelper(TClass inf, NetDataWriter w)
+ {
+ var arr = GetterArr(inf);
+ w.Put((ushort)arr.Length);
+ return arr;
+ }
+
+ protected List ReadListHelper(TClass inf, NetDataReader r, out int len)
+ {
+ len = r.GetUShort();
+ var list = GetterList(inf);
+ if (list == null)
+ {
+ list = new List(len);
+ SetterList(inf, list);
+ }
+ return list;
+ }
+
+ protected List WriteListHelper(TClass inf, NetDataWriter w, out int len)
+ {
+ var list = GetterList(inf);
+ if (list == null)
+ {
+ len = 0;
+ w.Put(0);
+ return null;
+ }
+ len = list.Count;
+ w.Put((ushort)len);
+ return list;
+ }
+
+ public override void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type)
+ {
+ base.Init(getMethod, setMethod, type);
+ switch (type)
+ {
+ case CallType.Array:
+ GetterArr = (Func)Delegate.CreateDelegate(typeof(Func), getMethod);
+ SetterArr = (Action)Delegate.CreateDelegate(typeof(Action), setMethod);
+ break;
+ case CallType.List:
+ GetterList = (Func>)Delegate.CreateDelegate(typeof(Func>), getMethod);
+ SetterList = (Action>)Delegate.CreateDelegate(typeof(Action>), setMethod);
+ break;
+ default:
+ Getter = (Func)Delegate.CreateDelegate(typeof(Func), getMethod);
+ Setter = (Action)Delegate.CreateDelegate(typeof(Action), setMethod);
+ break;
+ }
+ }
+ }
+
+ protected abstract class FastCallSpecificAuto : FastCallSpecific
+ {
+ protected abstract void ElementRead(NetDataReader r, out TProperty prop);
+ protected abstract void ElementWrite(NetDataWriter w, ref TProperty prop);
+
+ public override void Read(TClass inf, NetDataReader r)
+ {
+ ElementRead(r, out var elem);
+ Setter(inf, elem);
+ }
+
+ public override void Write(TClass inf, NetDataWriter w)
+ {
+ var elem = Getter(inf);
+ ElementWrite(w, ref elem);
+ }
+
+ public override void ReadArray(TClass inf, NetDataReader r)
+ {
+ var arr = ReadArrayHelper(inf, r);
+ for (int i = 0; i < arr.Length; i++)
+ ElementRead(r, out arr[i]);
+ }
+
+ public override void WriteArray(TClass inf, NetDataWriter w)
+ {
+ var arr = WriteArrayHelper(inf, w);
+ for (int i = 0; i < arr.Length; i++)
+ ElementWrite(w, ref arr[i]);
+ }
+ }
+
+ private sealed class FastCallStatic : FastCallSpecific
+ {
+ private readonly Action _writer;
+ private readonly Func _reader;
+
+ public FastCallStatic(Action write, Func read)
+ {
+ _writer = write;
+ _reader = read;
+ }
+
+ public override void Read(TClass inf, NetDataReader r) { Setter(inf, _reader(r)); }
+ public override void Write(TClass inf, NetDataWriter w) { _writer(w, Getter(inf)); }
+
+ public override void ReadList(TClass inf, NetDataReader r)
+ {
+ var list = ReadListHelper(inf, r, out int len);
+ int listCount = list.Count;
+ for (int i = 0; i < len; i++)
+ {
+ if (i < listCount)
+ list[i] = _reader(r);
+ else
+ list.Add(_reader(r));
+ }
+ if (len < listCount)
+ list.RemoveRange(len, listCount - len);
+ }
+
+ public override void WriteList(TClass inf, NetDataWriter w)
+ {
+ var list = WriteListHelper(inf, w, out int len);
+ for (int i = 0; i < len; i++)
+ _writer(w, list[i]);
+ }
+
+ public override void ReadArray(TClass inf, NetDataReader r)
+ {
+ var arr = ReadArrayHelper(inf, r);
+ int len = arr.Length;
+ for (int i = 0; i < len; i++)
+ arr[i] = _reader(r);
+ }
+
+ public override void WriteArray(TClass inf, NetDataWriter w)
+ {
+ var arr = WriteArrayHelper(inf, w);
+ int len = arr.Length;
+ for (int i = 0; i < len; i++)
+ _writer(w, arr[i]);
+ }
+ }
+
+ private sealed class FastCallStruct : FastCallSpecific where TProperty : struct, INetSerializable
+ {
+ private TProperty _p;
+
+ public override void Read(TClass inf, NetDataReader r)
+ {
+ _p.Deserialize(r);
+ Setter(inf, _p);
+ }
+
+ public override void Write(TClass inf, NetDataWriter w)
+ {
+ _p = Getter(inf);
+ _p.Serialize(w);
+ }
+
+ public override void ReadList(TClass inf, NetDataReader r)
+ {
+ var list = ReadListHelper(inf, r, out int len);
+ int listCount = list.Count;
+ for (int i = 0; i < len; i++)
+ {
+ var itm = default(TProperty);
+ itm.Deserialize(r);
+ if(i < listCount)
+ list[i] = itm;
+ else
+ list.Add(itm);
+ }
+ if (len < listCount)
+ list.RemoveRange(len, listCount - len);
+ }
+
+ public override void WriteList(TClass inf, NetDataWriter w)
+ {
+ var list = WriteListHelper(inf, w, out int len);
+ for (int i = 0; i < len; i++)
+ list[i].Serialize(w);
+ }
+
+ public override void ReadArray(TClass inf, NetDataReader r)
+ {
+ var arr = ReadArrayHelper(inf, r);
+ int len = arr.Length;
+ for (int i = 0; i < len; i++)
+ arr[i].Deserialize(r);
+ }
+
+ public override void WriteArray(TClass inf, NetDataWriter w)
+ {
+ var arr = WriteArrayHelper(inf, w);
+ int len = arr.Length;
+ for (int i = 0; i < len; i++)
+ arr[i].Serialize(w);
+ }
+ }
+
+ private sealed class FastCallClass : FastCallSpecific where TProperty : class, INetSerializable
+ {
+ private readonly Func _constructor;
+ public FastCallClass(Func constructor) { _constructor = constructor; }
+
+ public override void Read(TClass inf, NetDataReader r)
+ {
+ var p = _constructor();
+ p.Deserialize(r);
+ Setter(inf, p);
+ }
+
+ public override void Write(TClass inf, NetDataWriter w)
+ {
+ var p = Getter(inf);
+ p?.Serialize(w);
+ }
+
+ public override void ReadList(TClass inf, NetDataReader r)
+ {
+ var list = ReadListHelper(inf, r, out int len);
+ int listCount = list.Count;
+ for (int i = 0; i < len; i++)
+ {
+ if (i < listCount)
+ {
+ list[i].Deserialize(r);
+ }
+ else
+ {
+ var itm = _constructor();
+ itm.Deserialize(r);
+ list.Add(itm);
+ }
+ }
+ if (len < listCount)
+ list.RemoveRange(len, listCount - len);
+ }
+
+ public override void WriteList(TClass inf, NetDataWriter w)
+ {
+ var list = WriteListHelper(inf, w, out int len);
+ for (int i = 0; i < len; i++)
+ list[i].Serialize(w);
+ }
+
+ public override void ReadArray(TClass inf, NetDataReader r)
+ {
+ var arr = ReadArrayHelper(inf, r);
+ int len = arr.Length;
+ for (int i = 0; i < len; i++)
+ {
+ arr[i] = _constructor();
+ arr[i].Deserialize(r);
+ }
+ }
+
+ public override void WriteArray(TClass inf, NetDataWriter w)
+ {
+ var arr = WriteArrayHelper(inf, w);
+ int len = arr.Length;
+ for (int i = 0; i < len; i++)
+ arr[i].Serialize(w);
+ }
+ }
+
+ protected class IntSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetInt()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetIntArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class UIntSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUInt()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUIntArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class ShortSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetShort()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetShortArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class UShortSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUShort()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUShortArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class LongSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetLong()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetLongArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class ULongSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetULong()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetULongArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class ByteSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetByte()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBytesWithLength()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutBytesWithLength(GetterArr(inf)); }
+ }
+
+ protected class SByteSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetSByte()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetSBytesWithLength()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutSBytesWithLength(GetterArr(inf)); }
+ }
+
+ protected class FloatSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetFloat()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetFloatArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class DoubleSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetDouble()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetDoubleArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class BoolSerializer : FastCallSpecific
+ {
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetBool()); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBoolArray()); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
+ }
+
+ protected class CharSerializer : FastCallSpecificAuto
+ {
+ protected override void ElementWrite(NetDataWriter w, ref char prop) { w.Put(prop); }
+ protected override void ElementRead(NetDataReader r, out char prop) { prop = r.GetChar(); }
+ }
+
+ protected class IPEndPointSerializer : FastCallSpecificAuto
+ {
+ protected override void ElementWrite(NetDataWriter w, ref IPEndPoint prop) { w.Put(prop); }
+ protected override void ElementRead(NetDataReader r, out IPEndPoint prop) { prop = r.GetNetEndPoint(); }
+ }
+
+ protected class StringSerializer : FastCallSpecific
+ {
+ private readonly int _maxLength;
+ public StringSerializer(int maxLength) { _maxLength = maxLength > 0 ? maxLength : short.MaxValue; }
+ public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetString(_maxLength)); }
+ public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf), _maxLength); }
+ public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetStringArray(_maxLength)); }
+ public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf), _maxLength); }
+ }
+
+ protected class EnumByteSerializer : FastCall
+ {
+ protected readonly PropertyInfo Property;
+ protected readonly Type PropertyType;
+ public EnumByteSerializer(PropertyInfo property, Type propertyType)
+ {
+ Property = property;
+ PropertyType = propertyType;
+ }
+ public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetByte()), null); }
+ public override void Write(T inf, NetDataWriter w) { w.Put((byte)Property.GetValue(inf, null)); }
+ public override void ReadArray(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: Enum[]"); }
+ public override void WriteArray(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: Enum[]"); }
+ public override void ReadList(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List"); }
+ public override void WriteList(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List"); }
+ }
+
+ protected class EnumIntSerializer : EnumByteSerializer
+ {
+ public EnumIntSerializer(PropertyInfo property, Type propertyType) : base(property, propertyType) { }
+ public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetInt()), null); }
+ public override void Write(T inf, NetDataWriter w) { w.Put((int)Property.GetValue(inf, null)); }
+ }
+
+ public sealed class ClassInfo
+ {
+ public static ClassInfo Instance;
+ private readonly FastCall[] _serializers;
+ private readonly int _membersCount;
+
+ public ClassInfo(List> serializers)
+ {
+ _membersCount = serializers.Count;
+ _serializers = serializers.ToArray();
+ }
+
+ public void Write(T obj, NetDataWriter writer)
+ {
+ for (int i = 0; i < _membersCount; i++)
+ {
+ var s = _serializers[i];
+ if (s.Type == CallType.Basic)
+ s.Write(obj, writer);
+ else if (s.Type == CallType.Array)
+ s.WriteArray(obj, writer);
+ else
+ s.WriteList(obj, writer);
+ }
+ }
+
+ public void Read(T obj, NetDataReader reader)
+ {
+ for (int i = 0; i < _membersCount; i++)
+ {
+ var s = _serializers[i];
+ if (s.Type == CallType.Basic)
+ s.Read(obj, reader);
+ else if(s.Type == CallType.Array)
+ s.ReadArray(obj, reader);
+ else
+ s.ReadList(obj, reader);
+ }
+ }
+ }
+
+ protected abstract class CustomType
+ {
+ public abstract FastCall Get();
+ }
+
+ private sealed class CustomTypeStruct : CustomType where TProperty : struct, INetSerializable
+ {
+ public override FastCall Get() { return new FastCallStruct(); }
+ }
+
+ private sealed class CustomTypeClass : CustomType where TProperty : class, INetSerializable
+ {
+ private readonly Func _constructor;
+ public CustomTypeClass(Func constructor) { _constructor = constructor; }
+ public override FastCall Get() { return new FastCallClass(_constructor); }
+ }
+
+ private sealed class CustomTypeStatic : CustomType
+ {
+ private readonly Action _writer;
+ private readonly Func _reader;
+ public CustomTypeStatic(Action writer, Func reader)
+ {
+ _writer = writer;
+ _reader = reader;
+ }
+ public override FastCall Get() { return new FastCallStatic(_writer, _reader); }
+ }
+
+ ///
+ /// Register custom property type
+ ///
+ /// INetSerializable structure
+ public void RegisterNestedType() where T : struct, INetSerializable
+ {
+ _registeredTypes.Add(typeof(T), new CustomTypeStruct());
+ }
+
+ ///
+ /// Register custom property type
+ ///
+ /// INetSerializable class
+ public void RegisterNestedType(Func constructor) where T : class, INetSerializable
+ {
+ _registeredTypes.Add(typeof(T), new CustomTypeClass(constructor));
+ }
+
+ ///
+ /// Register custom property type
+ ///
+ /// Any packet
+ /// custom type writer
+ /// custom type reader
+ public void RegisterNestedType(Action writer, Func reader)
+ {
+ _registeredTypes.Add(typeof(T), new CustomTypeStatic(writer, reader));
+ }
+
+ private NetDataWriter _writer;
+ protected readonly int _maxStringLength;
+ protected readonly Dictionary _registeredTypes = new Dictionary();
+
+ public NetSerializer() : this(0)
+ {
+ }
+
+ public NetSerializer(int maxStringLength)
+ {
+ _maxStringLength = maxStringLength;
+ }
+
+ protected virtual ClassInfo RegisterInternal()
+ {
+ if (ClassInfo.Instance != null)
+ return ClassInfo.Instance;
+
+ Type t = typeof(T);
+ var props = t.GetProperties(
+ BindingFlags.Instance |
+ BindingFlags.Public |
+ BindingFlags.GetProperty |
+ BindingFlags.SetProperty);
+ var serializers = new List>();
+ for (int i = 0; i < props.Length; i++)
+ {
+ var property = props[i];
+ var propertyType = property.PropertyType;
+
+ var elementType = propertyType.IsArray ? propertyType.GetElementType() : propertyType;
+ var callType = propertyType.IsArray ? CallType.Array : CallType.Basic;
+
+ if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>))
+ {
+ elementType = propertyType.GetGenericArguments()[0];
+ callType = CallType.List;
+ }
+
+ if (Attribute.IsDefined(property, typeof(IgnoreDataMemberAttribute)))
+ continue;
+
+ var getMethod = property.GetGetMethod();
+ var setMethod = property.GetSetMethod();
+ if (getMethod == null || setMethod == null)
+ continue;
+
+ FastCall serialzer = null;
+ if (propertyType.IsEnum)
+ {
+ var underlyingType = Enum.GetUnderlyingType(propertyType);
+ if (underlyingType == typeof(byte))
+ serialzer = new EnumByteSerializer(property, propertyType);
+ else if (underlyingType == typeof(int))
+ serialzer = new EnumIntSerializer(property, propertyType);
+ else
+ throw new InvalidTypeException("Not supported enum underlying type: " + underlyingType.Name);
+ }
+ else if (elementType == typeof(string))
+ serialzer = new StringSerializer(_maxStringLength);
+ else if (elementType == typeof(bool))
+ serialzer = new BoolSerializer();
+ else if (elementType == typeof(byte))
+ serialzer = new ByteSerializer();
+ else if (elementType == typeof(sbyte))
+ serialzer = new SByteSerializer();
+ else if (elementType == typeof(short))
+ serialzer = new ShortSerializer();
+ else if (elementType == typeof(ushort))
+ serialzer = new UShortSerializer();
+ else if (elementType == typeof(int))
+ serialzer = new IntSerializer();
+ else if (elementType == typeof(uint))
+ serialzer = new UIntSerializer();
+ else if (elementType == typeof(long))
+ serialzer = new LongSerializer();
+ else if (elementType == typeof(ulong))
+ serialzer = new ULongSerializer();
+ else if (elementType == typeof(float))
+ serialzer = new FloatSerializer();
+ else if (elementType == typeof(double))
+ serialzer = new DoubleSerializer();
+ else if (elementType == typeof(char))
+ serialzer = new CharSerializer();
+ else if (elementType == typeof(IPEndPoint))
+ serialzer = new IPEndPointSerializer();
+ else
+ {
+ _registeredTypes.TryGetValue(elementType, out var customType);
+ if (customType != null)
+ serialzer = customType.Get();
+ }
+
+ if (serialzer != null)
+ {
+ serialzer.Init(getMethod, setMethod, callType);
+ serializers.Add(serialzer);
+ }
+ else
+ {
+ throw new InvalidTypeException("Unknown property type: " + propertyType.FullName);
+ }
+ }
+ ClassInfo.Instance = new ClassInfo(serializers);
+ return ClassInfo.Instance;
+ }
+
+ /// 's fields are not supported, or it has no fields
+ public virtual void Register()
+ {
+ RegisterInternal();
+ }
+
+ ///
+ /// Reads packet with known type
+ ///
+ /// NetDataReader with packet
+ /// Returns packet if packet in reader is matched type
+ /// 's fields are not supported, or it has no fields
+ public T Deserialize(NetDataReader reader) where T : class, new()
+ {
+ var info = RegisterInternal();
+ var result = new T();
+ try
+ {
+ info.Read(result, reader);
+ }
+ catch
+ {
+ return null;
+ }
+ return result;
+ }
+
+ ///
+ /// Reads packet with known type (non alloc variant)
+ ///
+ /// NetDataReader with packet
+ /// Deserialization target
+ /// Returns true if packet in reader is matched type
+ /// 's fields are not supported, or it has no fields
+ public bool Deserialize(NetDataReader reader, T target) where T : class, new()
+ {
+ var info = RegisterInternal();
+ try
+ {
+ info.Read(target, reader);
+ }
+ catch
+ {
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Serialize object to NetDataWriter (fast)
+ ///
+ /// Serialization target NetDataWriter
+ /// Object to serialize
+ ///