Skip to content

Commit b2759aa

Browse files
committed
Merge branch 'more-bug-fixes' into developer
2 parents 4d234ff + 44fbae2 commit b2759aa

520 files changed

Lines changed: 40665 additions & 31601 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Basis Server/BasisNetworkClientConsole/BasisNetworkClientConsole/ClientManager.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,10 @@ public async Task StartClientsAsync()
181181
BNL.Log($"Connecting: {name} ({identity.Did})");
182182
}
183183

184-
await Task.Delay(1, cts.Token);
184+
if (ConfigManager.ClientConnectIntervalMs > 0)
185+
{
186+
await Task.Delay(ConfigManager.ClientConnectIntervalMs, cts.Token);
187+
}
185188
}
186189
}
187190
public async Task ReconnectClientAsync(int index)

Basis Server/BasisNetworkClientConsole/BasisNetworkClientConsole/ConfigManager.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public static class ConfigManager
88
public static string Ip = "localhost";
99
public static int Port = 4296;
1010
public static int ClientCount = 250;
11+
public static int ClientConnectIntervalMs = 1;
1112

1213
public static string AvatarPassword = "default_avatar_password";
1314
public static string AvatarUrl = "http://localhost/avatar";
@@ -180,6 +181,8 @@ public static void LoadOrCreateConfigXml(string filePath)
180181
new XElement("Port", Port),
181182
new XComment(" Number of simulated clients to spawn for load testing. int (>= 1); higher counts need more CPU, memory and sockets. "),
182183
new XElement("ClientCount", ClientCount),
184+
new XComment(" Delay in ms between starting each simulated client's connection, controlling how fast the crowd ramps up. 0 or less starts them as fast as the loop runs. int. "),
185+
new XElement("ClientConnectIntervalMs", ClientConnectIntervalMs),
183186
new XComment(" Avatar unlock password/key sent with the avatar; used to decrypt the (encrypted .BEE) bundle at <AvatarUrl>. string. "),
184187
new XElement("AvatarPassword", AvatarPassword),
185188
new XComment(" Avatar source each fake client advertises. For AvatarLoadMode 0 this is the (encrypted .BEE) bundle download URL. string. "),
@@ -276,6 +279,7 @@ public static void LoadOrCreateConfigXml(string filePath)
276279
Ip = ReadString(root, "Ip", Ip);
277280
Port = ReadInt(root, "Port", Port);
278281
ClientCount = ReadInt(root, "ClientCount", ClientCount);
282+
ClientConnectIntervalMs = ReadInt(root, "ClientConnectIntervalMs", ClientConnectIntervalMs);
279283

280284
AvatarPassword = ReadString(root, "AvatarPassword", AvatarPassword);
281285
AvatarUrl = ReadString(root, "AvatarUrl", AvatarUrl);

Basis Server/BasisNetworkClientConsole/BasisNetworkClientConsole/FakePoseGenerator.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,6 @@ public static void WriteBoneRotations(byte[] dst, int byteOffset, BitQuality qua
149149

150150
for (int slot = 0; slot < WireBoneCount; slot++)
151151
{
152-
int bitsPerComp = bpc[slot];
153-
int totalBits = 2 + 3 * bitsPerComp;
154-
155152
// Every slot animates every frame — a load-test sender must produce fresh
156153
// rotation bits per send like a real tracked human, not a frozen statue.
157154
int idx = slot * 4;
@@ -162,7 +159,10 @@ public static void WriteBoneRotations(byte[] dst, int byteOffset, BitQuality qua
162159
QuatMul(bx, by, bz, bw, dx, dy, dz, dw, out float rx, out float ry, out float rz, out float rw);
163160
Normalize(ref rx, ref ry, ref rz, ref rw);
164161

165-
ulong packed = BasisBoneRotationCompression.EncodeSmallestThree(rx, ry, rz, rw, bitsPerComp, ranges[slot]);
162+
int totalBits = BasisBoneRotationCompression.BoneFieldWidth(quality, slot);
163+
ulong packed = BasisBoneRotationCompression.BONE_DOF[slot] == 3
164+
? BasisBoneRotationCompression.EncodeSmallestThree(rx, ry, rz, rw, bpc[slot], ranges[slot])
165+
: BasisBoneRotationCompression.EncodeRestricted(rx, ry, rz, rw, slot, quality);
166166

167167
BasisBoneRotationCompression.WriteBits(dst, baseBit + offsets[slot], packed, totalBits);
168168
}
@@ -352,8 +352,13 @@ private static void GetIdleDelta(int slot, double t, float phase, out float dx,
352352
frequency *= 1f + 0.07f * (slot % 3);
353353
float angle = amplitude * MathF.Sin((float)(t * frequency * TwoPi + p * 1.1f + slot * 0.61f));
354354

355-
// Cycle the rotation axis per slot so motion isn't uniformly single-axis.
356-
switch (slot % 3)
355+
// Restricted slots (v52) only carry their anatomical axes on the wire, so
356+
// animate the hinge axis — motion on a dropped axis would encode to silence.
357+
// Full 3-DOF slots keep the per-slot axis cycle so motion isn't single-axis.
358+
int axisCode = BasisBoneRotationCompression.BONE_DOF[slot] == 3
359+
? slot % 3
360+
: BasisBoneRotationCompression.BONE_AXIS_A[slot];
361+
switch (axisCode)
357362
{
358363
case 0: AxisAngleToQuat(1, 0, 0, angle, out dx, out dy, out dz, out dw); break;
359364
case 1: AxisAngleToQuat(0, 1, 0, angle, out dx, out dy, out dz, out dw); break;

Basis Server/BasisNetworkCore/BasisNetworkVersion.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ namespace Basis.Network.Core
22
{
33
public class BasisNetworkVersion
44
{
5-
public static ushort ServerVersion = 51;
5+
// 52: restricted-DOF bone encoding — 2-DOF limb/extremity joints and 1-DOF toes ship
6+
// quantized angles instead of smallest-three quaternions (wire-format change).
7+
public static ushort ServerVersion = 52;
68
}
79
}

Basis Server/BasisNetworkCore/BasisServerConfiguration.cs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,24 @@ public class Configuration
104104
/// When false, clients upload full keyframes only (legacy behavior).
105105
/// </summary>
106106
public bool EnableUplinkAvatarDelta = true;
107+
/// <summary>
108+
/// Hold shared images in server RAM so a joining player is handed them immediately, instead of
109+
/// the original sharer having to re-upload every image to each arrival. Costs memory; saves the
110+
/// sharer's uplink and gets pictures on the wall far sooner in a busy instance.
111+
/// </summary>
112+
public bool ImageCacheEnabled = true;
113+
/// <summary>
114+
/// Ceiling on the image cache, in megabytes. Set 0 to hold nothing (equivalent to disabling the
115+
/// cache). This is a hard cap on retained payloads, not a target.
116+
/// </summary>
117+
public int ImageCacheMaxMegabytes = 512;
118+
/// <summary>
119+
/// Floor on one player's slice of the cache, in megabytes. The buffer is divided evenly between
120+
/// everyone currently holding images so nobody can crowd anyone else out; without a floor a busy
121+
/// instance would shrink each share below a single image and cache nothing at all. An owner over
122+
/// their share evicts their own oldest image, never another player's.
123+
/// </summary>
124+
public int ImageCacheMinimumPerOwnerMegabytes = 32;
107125
public bool EnableBSRProfiling = false;
108126
/// <summary>
109127
/// Worker cap for the BSR tick's parallel phases (send loop, message processing, distance
@@ -345,8 +363,43 @@ public void ProcessEnvironmentalOverrides()
345363
ApplyEnvironmentalOverridesTo(this);
346364
}
347365

366+
/// <summary>
367+
/// Settings established once during boot — socket binds, the transport stack, the health and
368+
/// API listeners, the console, and disk support. Editing one persists and takes effect on the
369+
/// next start; everything else is re-applied live by NetworkServer.ApplyLiveConfiguration.
370+
/// </summary>
371+
private static readonly string[] RestartOnlyFields =
372+
{
373+
nameof(SetPort),
374+
nameof(IPv4Address),
375+
nameof(IPv6Address),
376+
nameof(OverrideAutoDiscoveryOfIpv),
377+
nameof(NetworkStackId),
378+
nameof(HasFileSupport),
379+
nameof(EnableStatistics),
380+
nameof(EnableConsole),
381+
nameof(HealthCheckHost),
382+
nameof(HealthCheckPort),
383+
nameof(HealthPath),
384+
nameof(ApiEnabled),
385+
nameof(ApiHost),
386+
nameof(ApiPort),
387+
nameof(ApiKey),
388+
};
389+
390+
/// <summary>Whether a field only takes effect after a restart. See <see cref="RestartOnlyFields"/>.</summary>
391+
public static bool RequiresRestart(string fieldName) =>
392+
Array.IndexOf(RestartOnlyFields, fieldName) >= 0;
393+
394+
/// <summary>
395+
/// Settings a connected client is told about at join time only, so an edit reaches new joiners
396+
/// but leaves the existing crowd on the value they connected with.
397+
/// </summary>
398+
public static bool AppliesToNewJoinsOnly(string fieldName) =>
399+
fieldName == nameof(BSRSlowestSendRate);
400+
348401
/// <summary>Field names whose values must never reach the log.</summary>
349-
private static bool IsSecretFieldName(string fieldName)
402+
public static bool IsSecretFieldName(string fieldName)
350403
{
351404
if (string.IsNullOrEmpty(fieldName)) return false;
352405
return fieldName.IndexOf("password", StringComparison.OrdinalIgnoreCase) >= 0

Basis Server/BasisNetworkCore/Compression/BasisAvatarChannelMap.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,26 @@ private static BasisAvatarChannelLayout Build(BasisAvatarBitPacking.BitQuality q
174174
{
175175
fieldFirst[BasisAvatarDeltaCompression.BoneFieldStart + slot] = channels.Count;
176176
int b = rotBase + fieldOffsets[slot];
177-
// Smallest-three: the 2-bit index selects which component was dropped, so it changes
178-
// what the other three MEAN. Differencing across an index change is nonsense — Raw.
179-
channels.Add(new BasisAvatarChannel(b, 2, BasisChannelKind.Raw));
180-
for (int c = 0; c < 3; c++)
181-
channels.Add(new BasisAvatarChannel(b + 2 + c * bpc[slot], bpc[slot], BasisChannelKind.Delta));
177+
switch (BasisBoneRotationCompression.BONE_DOF[slot])
178+
{
179+
case 3:
180+
// Smallest-three: the 2-bit index selects which component was dropped, so it
181+
// changes what the other three MEAN. Differencing across an index change is
182+
// nonsense — Raw.
183+
channels.Add(new BasisAvatarChannel(b, 2, BasisChannelKind.Raw));
184+
for (int c = 0; c < 3; c++)
185+
channels.Add(new BasisAvatarChannel(b + 2 + c * bpc[slot], bpc[slot], BasisChannelKind.Delta));
186+
break;
187+
case 2:
188+
// Hinge + twist angles: uniformly quantized scalars, both deltable.
189+
int hingeBits = BasisBoneRotationCompression.HingeBits(q);
190+
channels.Add(new BasisAvatarChannel(b, hingeBits, BasisChannelKind.Delta));
191+
channels.Add(new BasisAvatarChannel(b + hingeBits, BasisBoneRotationCompression.TwistBits(q), BasisChannelKind.Delta));
192+
break;
193+
default:
194+
channels.Add(new BasisAvatarChannel(b, BasisBoneRotationCompression.SingleAxisBits(q), BasisChannelKind.Delta));
195+
break;
196+
}
182197
}
183198

184199
for (int f = 0; f < BasisBoneRotationCompression.FingerChannelCount; f++)

0 commit comments

Comments
 (0)