Implement handshake Hello NetControlMessage, recv and send

This commit is contained in:
AeonLucid
2022-01-12 07:26:25 +01:00
parent 0a27bdd3c0
commit 65a28b32ef
50 changed files with 2768 additions and 156 deletions
@@ -0,0 +1,11 @@
using Prospect.Unreal.Net.Channels;
namespace Prospect.Unreal.Net.Packets.Bunch;
public class FControlChannelOutBunch : FOutBunch
{
public FControlChannelOutBunch(UChannel inChannel, bool bClose) : base(inChannel, bClose)
{
bReliable = true;
}
}
@@ -0,0 +1,161 @@
using Prospect.Unreal.Core.Names;
using Prospect.Unreal.Net.Channels;
using Prospect.Unreal.Serialization;
namespace Prospect.Unreal.Net.Packets.Bunch;
public class FOutBunch : FNetBitWriter
{
public FOutBunch() : base(0)
{
ChName = UnrealNames.FNames[UnrealNameKey.None];
}
public FOutBunch(UChannel inChannel, bool bInClose) : base(
inChannel.Connection!.PackageMap!,
inChannel.Connection.GetMaxSingleBunchSizeBits())
{
Next = null;
Channel = inChannel;
Time = 0;
ChIndex = inChannel.ChIndex;
ChType = inChannel.ChType;
ChName = inChannel.ChName;
ChSequence = 0;
PacketId = 0;
ReceivedAck = false;
bOpen = false;
bClose = bInClose;
bDormant = false;
bIsReplicationPaused = false;
bReliable = false;
bPartial = false;
bPartialInitial = false;
bPartialFinal = false;
bHasPackageMapExports = false;
bHasMustBeMappedGUIDs = false;
CloseReason = EChannelCloseReason.Destroyed;
// Match the byte swapping settings of the connection
// TODO: SetByteSwapping(Channel->Connection->bNeedsByteSwapping);
// Reserve channel and set bunch info.
if (Channel.NumOutRec >= UNetConnection.ReliableBuffer - 1 + (bClose ? 1 : 0))
{
SetOverflowed(-1);
}
}
public FOutBunch(UPackageMap inPackageMap, long maxBits) : base(inPackageMap, maxBits)
{
Next = null;
Channel = null;
Time = 0;
ChIndex = 0;
ChType = EChannelType.CHTYPE_None;
ChName = UnrealNames.FNames[UnrealNameKey.None];
ChSequence = 0;
PacketId = 0;
ReceivedAck = false;
bOpen = false;
bClose = false;
bDormant = false;
bIsReplicationPaused = false;
bReliable = false;
bPartial = false;
bPartialInitial = false;
bPartialFinal = false;
bHasPackageMapExports = false;
bHasMustBeMappedGUIDs = false;
CloseReason = EChannelCloseReason.Destroyed;
}
public FOutBunch(FOutBunch bunch) : base(bunch)
{
Next = bunch.Next;
Channel = bunch.Channel;
Time = bunch.Time;
ChIndex = bunch.ChIndex;
ChType = bunch.ChType;
ChName = bunch.ChName;
ChSequence = bunch.ChSequence;
PacketId = bunch.PacketId;
ReceivedAck = bunch.ReceivedAck;
bOpen = bunch.bOpen;
bClose = bunch.bClose;
bDormant = bunch.bDormant;
bIsReplicationPaused = bunch.bIsReplicationPaused;
bReliable = bunch.bReliable;
bPartial = bunch.bPartial;
bPartialInitial = bunch.bPartialInitial;
bPartialFinal = bunch.bPartialFinal;
bHasPackageMapExports = bunch.bHasPackageMapExports;
bHasMustBeMappedGUIDs = bunch.bHasMustBeMappedGUIDs;
CloseReason = bunch.CloseReason;
}
public FOutBunch? Next { get; set; }
public UChannel? Channel { get; set; }
public double Time { get; set; }
public int ChIndex { get; set; }
public EChannelType ChType { get; set; }
public FName ChName { get; set; }
public int ChSequence { get; set; }
public int PacketId { get; set; }
public bool ReceivedAck { get; set; }
public bool bOpen { get; set; }
public bool bClose { get; set; }
/// <summary>
/// Close, but go dormant.
/// </summary>
public bool bDormant { get; set; }
/// <summary>
/// Replication on this channel is being paused by the server.
/// </summary>
public bool bIsReplicationPaused { get; set; }
public bool bReliable { get; set; }
/// <summary>
/// Not a complete bunch
/// </summary>
public bool bPartial { get; set; }
/// <summary>
/// The first bunch of a partial bunch
/// </summary>
public bool bPartialInitial { get; set; }
/// <summary>
/// The final bunch of a partial bunch
/// </summary>
public bool bPartialFinal { get; set; }
/// <summary>
/// This bunch has networkGUID name/id pairs.
/// </summary>
public bool bHasPackageMapExports { get; set; }
/// <summary>
/// This bunch has guids that must be mapped before we can process this bunch.
/// </summary>
public bool bHasMustBeMappedGUIDs { get; set; }
public EChannelCloseReason CloseReason { get; set; }
public List<FNetworkGUID> ExportNetGUIDs { get; } = new List<FNetworkGUID>();
public List<ulong> NetFieldExports { get; } = new List<ulong>();
}
@@ -0,0 +1,16 @@
namespace Prospect.Unreal.Net.Packets.Control;
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal class NetControlMessageAttribute : Attribute
{
public NetControlMessageAttribute(string name, int index, params Type[] args)
{
Name = name;
Index = index;
Args = args;
}
public string Name { get; }
public int Index { get; }
public Type[] Args { get; }
}
@@ -0,0 +1,28 @@
using Prospect.Unreal.Core;
using Prospect.Unreal.Net;
using Prospect.Unreal.Net.Packets.Control;
[assembly: NetControlMessage("Hello", 0, typeof(byte), typeof(uint), typeof(FString))]
[assembly: NetControlMessage("Welcome", 1, typeof(FString), typeof(FString), typeof(FString))]
[assembly: NetControlMessage("Upgrade", 2, typeof(uint))]
[assembly: NetControlMessage("Challenge", 3, typeof(FString))]
[assembly: NetControlMessage("Netspeed", 4, typeof(int))]
[assembly: NetControlMessage("Login", 5, typeof(FString), typeof(FString), typeof(FUniqueNetIdRepl), typeof(FString))]
[assembly: NetControlMessage("Failure", 6, typeof(FString))]
[assembly: NetControlMessage("Join", 9)]
[assembly: NetControlMessage("JoinSplit", 10, typeof(FString), typeof(FUniqueNetIdRepl))]
[assembly: NetControlMessage("Skip", 12, typeof(FGuid))]
[assembly: NetControlMessage("Abort", 13, typeof(FGuid))]
[assembly: NetControlMessage("PCSwap", 15, typeof(int))]
[assembly: NetControlMessage("ActorChannelFailure", 16, typeof(int))]
[assembly: NetControlMessage("DebugText", 17, typeof(FString))]
[assembly: NetControlMessage("NetGUIDAssign", 18, typeof(FNetworkGUID), typeof(FString))]
[assembly: NetControlMessage("SecurityViolation", 19, typeof(FString))]
[assembly: NetControlMessage("GameSpecific", 20, typeof(byte), typeof(FString))]
[assembly: NetControlMessage("EncryptionAck", 21)]
[assembly: NetControlMessage("DestructionInfo", 22)]
[assembly: NetControlMessage("BeaconWelcome", 25)]
[assembly: NetControlMessage("BeaconJoin", 26, typeof(FString), typeof(FUniqueNetIdRepl))]
[assembly: NetControlMessage("BeaconAssignGUID", 27, typeof(FNetworkGUID))]
[assembly: NetControlMessage("BeaconNetGUIDAck", 28, typeof(FString))]
@@ -14,7 +14,9 @@ public class FNetPacketNotify
public const int MaxSequenceHistoryLength = 256;
private readonly Queue<FSentAckData> _ackRecord = new Queue<FSentAckData>();
private uint _writtenHistoryWordCount;
private SequenceNumber _writtenInAckSeq;
private readonly SequenceHistory _inSeqHistory = new SequenceHistory();
private SequenceNumber _inSeq;
private SequenceNumber _inAckSeq;
@@ -33,6 +35,71 @@ public class FNetPacketNotify
_outAckSeq = new SequenceNumber((ushort)(initialOutSeq.Value - 1));
}
public SequenceNumber GetInSeq()
{
return _inSeq;
}
public SequenceNumber GetInAckSeq()
{
return _inAckSeq;
}
public SequenceNumber GetOutSeq()
{
return _outSeq;
}
public SequenceNumber GetOutAckSeq()
{
return _outAckSeq;
}
/// <summary>
/// These methods must always write and read the exact same number of bits, that is the reason for not using WriteInt/WrittedWrappedInt
/// </summary>
public bool WriteHeader(FBitWriter writer, bool bRefresh)
{
// we always write at least 1 word
var currentHistoryWorkCount = Math.Clamp((GetCurrentSequenceHistoryLength() + SequenceHistory.BitsPerWord - 1) / SequenceHistory.BitsPerWord, 1, SequenceHistory.WordCount);
// We can only do a refresh if we do not need more space for the history
if (bRefresh && (currentHistoryWorkCount > _writtenHistoryWordCount))
{
return false;
}
// How many words of ack data should we write? If this is a refresh we must write the same size as the original header
_writtenHistoryWordCount = bRefresh ? _writtenHistoryWordCount : currentHistoryWorkCount;
// This is the last InAck we have acknowledged at this time
_writtenInAckSeq = _inAckSeq;
var seq = _outSeq;
var ackedSeq = _inAckSeq;
// Pack data into a uint
var packedHeader = FPackedHeader.Pack(seq, ackedSeq, _writtenHistoryWordCount - 1);
// Write packed header
writer.WriteUInt32(packedHeader);
// Write ack history
_inSeqHistory.Write(writer, _writtenHistoryWordCount);
return true;
}
private uint GetCurrentSequenceHistoryLength()
{
if (_inAckSeq.GreaterEq(_inAckSeqAck))
{
return (uint) SequenceNumber.Diff(_inAckSeq, _inAckSeqAck);
}
return SequenceHistory.Size;
}
public bool ReadHeader(ref FNotificationHeader data, FBitReader reader)
{
var packedHeader = reader.ReadUInt32();
@@ -173,4 +240,13 @@ public class FNetPacketNotify
_inSeqHistory.AddDeliveryStatus(bReportAcked);
}
}
public SequenceNumber CommitAndIncrementOutSeq()
{
// Add entry to the ack-record so that we can update the InAckSeqAck when we received the ack for this OutSeq.
_ackRecord.Enqueue(new FSentAckData(_outSeq, _writtenInAckSeq));
_writtenHistoryWordCount = 0;
return _outSeq.IncrementAndGet();
}
}
@@ -9,6 +9,17 @@ public class FPackedHeader
public const int HistoryWordCountMask = (1 << HistoryWordCountBits) - 1;
public const int AckSeqShift = HistoryWordCountBits;
public const int SeqShift = AckSeqShift + FNetPacketNotify.SequenceNumberBits;
public static uint Pack(SequenceNumber seq, SequenceNumber ackedSeq, uint historyWordCount)
{
uint packed = 0;
packed |= (uint)(seq.Value << SeqShift);
packed |= (uint)(ackedSeq.Value << AckSeqShift);
packed |= (uint)(historyWordCount & HistoryWordCountMask);
return packed;
}
public static SequenceNumber GetSeq(uint packed)
{
@@ -57,4 +57,13 @@ public readonly struct SequenceHistory
_storage[i] = reader.ReadUInt32();
}
}
public void Write(FBitWriter writer, uint numWords)
{
numWords = Math.Min(numWords, WordCount);
for (var i = 0; i < numWords; i++)
{
writer.WriteUInt32(_storage[i]);
}
}
}