feat: Upgrade server with HyNB v2.4 modular architecture, minigames, admin GM web panel, companion summons, and car radio
This commit is contained in:
1 parent
74e2963a12
commit
3e5adf1dc5
1571 files changed
+336468
-6348
No files matched your search
@@ -19,7 +19,20 @@ public sealed class UxContractAttribute : Attribute
|
||||
public enum UxCountEncoding
|
||||
{
|
||||
Int32,
|
||||
Int7
|
||||
/// <summary>
|
||||
/// Biased 7-bit varint: the wire carries <c>count + 1</c>. Use for gadget/native-family
|
||||
/// <b>lists</b> — SyncGadgetGridAOIDecrease(68956797) only renders when its empty sector /
|
||||
/// index lists are written <c>01 01</c>, and that is live-verified.
|
||||
/// </summary>
|
||||
Int7,
|
||||
/// <summary>
|
||||
/// Raw 7-bit varint: the wire carries <c>count</c> itself. Use for gadget/native-family
|
||||
/// <b>dictionaries</b> — MobilePlatformSyncInfo.Players is the measured case: <c>01 01</c>
|
||||
/// makes the client read one entry that is not there and drop the whole 68705244 push
|
||||
/// (`ReadBuffer invalid size=8 remain=5`), while <c>01 00</c> parses. The donor handoff
|
||||
/// contradicts itself on this byte (16D vs 16E/16F); the client log is the authority.
|
||||
/// </summary>
|
||||
Raw7
|
||||
}
|
||||
|
||||
public enum UxObjectEncoding
|
||||
@@ -43,6 +56,13 @@ public sealed class UxCollectionAttribute : Attribute
|
||||
public UxCountEncoding Count { get; set; } = UxCountEncoding.Int7;
|
||||
public UxObjectEncoding ItemObjectEncoding { get; set; } = UxObjectEncoding.Auto;
|
||||
public UxObjectEncoding ValueObjectEncoding { get; set; } = UxObjectEncoding.Auto;
|
||||
|
||||
/// <summary>
|
||||
/// The single "is present" byte written before the count. The aether family treats any
|
||||
/// non-zero byte as present; the gadget/native family was verified with 0x01 (handoff 16D),
|
||||
/// so contracts in that family set this explicitly to stay byte-identical to the captures.
|
||||
/// </summary>
|
||||
public byte Marker { get; set; } = 0xFF;
|
||||
}
|
||||
|
||||
/// <summary>UX byte buffers are nullable complex values with either Int32 or biased 7-bit lengths.</summary>
|
||||
@@ -239,8 +259,9 @@ public static class UxSerializer
|
||||
|
||||
private static void WriteList(UxWriter writer, Type itemType, IList? list, FieldInfo? field)
|
||||
{
|
||||
var marker = field?.GetCustomAttribute<UxCollectionAttribute>()?.Marker ?? (byte)0xFF;
|
||||
if (list is null) { writer.U8(0x00); return; }
|
||||
writer.U8(0xFF);
|
||||
writer.U8(marker);
|
||||
WriteCount(writer, list.Count, field);
|
||||
var itemEncoding = field?.GetCustomAttribute<UxCollectionAttribute>()?.ItemObjectEncoding ?? UxObjectEncoding.Auto;
|
||||
foreach (var item in list)
|
||||
@@ -260,8 +281,9 @@ public static class UxSerializer
|
||||
|
||||
private static void WriteDictionary(UxWriter writer, Type dictType, IDictionary? dict, FieldInfo? field)
|
||||
{
|
||||
var marker = field?.GetCustomAttribute<UxCollectionAttribute>()?.Marker ?? (byte)0xFF;
|
||||
if (dict is null) { writer.U8(0x00); return; }
|
||||
writer.U8(0xFF);
|
||||
writer.U8(marker);
|
||||
WriteCount(writer, dict.Count, field, defaultEncoding: UxCountEncoding.Int32);
|
||||
var args = dictType.GetGenericArguments();
|
||||
var valueEncoding = field?.GetCustomAttribute<UxCollectionAttribute>()?.ValueObjectEncoding ?? UxObjectEncoding.Auto;
|
||||
@@ -289,13 +311,23 @@ public static class UxSerializer
|
||||
private static void WriteCount(UxWriter writer, int count, FieldInfo? field, UxCountEncoding defaultEncoding = UxCountEncoding.Int7)
|
||||
{
|
||||
var encoding = field?.GetCustomAttribute<UxCollectionAttribute>()?.Count ?? defaultEncoding;
|
||||
if (encoding == UxCountEncoding.Int7) writer.Int7(count); else writer.I32(count);
|
||||
switch (encoding)
|
||||
{
|
||||
case UxCountEncoding.Int7: writer.Int7(count); break;
|
||||
case UxCountEncoding.Raw7: writer.Raw7(count); break;
|
||||
default: writer.I32(count); break;
|
||||
}
|
||||
}
|
||||
|
||||
private static int ReadCount(UxReader reader, FieldInfo? field, UxCountEncoding defaultEncoding = UxCountEncoding.Int7)
|
||||
{
|
||||
var encoding = field?.GetCustomAttribute<UxCollectionAttribute>()?.Count ?? defaultEncoding;
|
||||
return encoding == UxCountEncoding.Int7 ? reader.Int7() : reader.I32();
|
||||
return encoding switch
|
||||
{
|
||||
UxCountEncoding.Int7 => reader.Int7(),
|
||||
UxCountEncoding.Raw7 => reader.Raw7(),
|
||||
_ => reader.I32(),
|
||||
};
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, FieldInfo[]> FieldCache = new();
|
||||
|
||||
@@ -73,6 +73,9 @@ public sealed class UxReader
|
||||
|
||||
public int Int7() => Math.Max(0, ReadVarInt() - 1);
|
||||
|
||||
/// <summary>Gadget / native family: the varint is the count itself, no bias.</summary>
|
||||
public int Raw7() => ReadVarInt();
|
||||
|
||||
public string UxString() => UxStringNullable() ?? string.Empty;
|
||||
|
||||
public string? UxStringNullable()
|
||||
|
||||
@@ -44,6 +44,15 @@ public sealed class UxWriter
|
||||
return this;
|
||||
}
|
||||
|
||||
// Gadget / native family: the count byte is consumed as-is by the client reader, so a
|
||||
// zero-length list is a literal 0 (handoff 16D: empty dict = `01 00`).
|
||||
public UxWriter Raw7(int value)
|
||||
{
|
||||
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value));
|
||||
WriteVarInt(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] ToArray() => _ms.ToArray();
|
||||
|
||||
private void WriteVarInt(int value)
|
||||
|
||||
Reference in new issue
Block a user