Implement handshake Hello NetControlMessage, recv and send
This commit is contained in:
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user