feat: Upgrade server with HyNB v2.4 modular architecture, minigames, admin GM web panel, companion summons, and car radio

This commit is contained in:
CAtlas0784 committed 2026-10-29 02:44:24 +07:00
1 parent 74e2963a12
commit 3e5adf1dc5
1571 files changed
+336468 -6348

No files matched your search

+37 -5
View File
@@ -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();
+3
View File
@@ -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()
+9
View File
@@ -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)