mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
Import march-2020-03-10 from private repo (scrubbed 2026-05-27)
Targets Rec Room 2020.03.10 client (also serves 2020.03.06).
Re-import after adding:
- DorkNet.ClientMod/dorknet-clientmod.json.template — config schema
the launcher's manifest-driven patcher renders into UserData/.
- tools/build-patcher-zip.ps1 — produces dorknet-clientpatch-{key}.zip
for v1-march-* Release uploads.
- .github/workflows/release.yml — on tag push publishes both
dorknet-server-march_2020_03_10-win-x64.zip and
dorknet-clientpatch-march_2020_03_10.zip as Release assets.
Same scrub rules as december; tools/build-patcher-zip.ps1 added to
the maintainer-side allow-list so it survives.
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DorkNet.Models.Config;
|
||||
|
||||
// Required keys per RecNet.RecRoomConfig.Deserialize disassembly (RVA 0x1146FA0).
|
||||
// Required-throw keys: ShareBaseUrl, LevelProgressionMaps[].Level, [].RequiredXp.
|
||||
// Required-object keys (GetObjectKey, accept null): ServerMaintenance,
|
||||
// AutoMicMutingConfig.
|
||||
//
|
||||
// NOTE: the level entry uses "RequiredXp" (lowercase 'p') per the IL string
|
||||
// literal — NOT "RequiredXP". A capital P here would throw KeyNotFoundException.
|
||||
public class RecRoomConfig
|
||||
{
|
||||
[JsonPropertyName("MessageOfTheDay")]
|
||||
public string MessageOfTheDay { get; set; } = "Welcome to the private server!";
|
||||
|
||||
[JsonPropertyName("CdnBaseUri")]
|
||||
public string CdnBaseUri { get; set; } = string.Empty;
|
||||
|
||||
// Required by client (string, GetKey). Used for share-link generation.
|
||||
[JsonPropertyName("ShareBaseUrl")]
|
||||
public string ShareBaseUrl { get; set; } = "https://rec.net/";
|
||||
|
||||
// GetObjectKey<ServerMaintenanceDTO>. Required key. The deserializer
|
||||
// accepts null without throwing, but the C# property getter on the
|
||||
// client doesn't null-check before use during boot. Send a real DTO
|
||||
// with StartsInMinutes far in the future = "no upcoming maintenance".
|
||||
[JsonPropertyName("ServerMaintenance")]
|
||||
public ServerMaintenanceDTO? ServerMaintenance { get; set; } = new();
|
||||
|
||||
// GetObjectKey<AutoMicMutingConfig>. Required by the client AND
|
||||
// dereferenced by AudioManager.Initialize during PostLogin boot —
|
||||
// null causes:
|
||||
// NullReferenceException at RecNet.Config.get_AutoMicMutingConfig
|
||||
// at AudioManager.Initialize
|
||||
// which kills the boot sequence with "Error initializing Core Systems".
|
||||
// Must be a real object with all 8 float fields populated.
|
||||
[JsonPropertyName("AutoMicMutingConfig")]
|
||||
public AutoMicMutingConfig AutoMicMutingConfig { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("PhotonConfig")]
|
||||
public PhotonConfig PhotonConfig { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("MatchmakingParams")]
|
||||
public MatchmakingParams MatchmakingParams { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("LevelProgressionMaps")]
|
||||
public List<LevelProgressionEntry> LevelProgressionMaps { get; set; } = LevelProgressionEntry.Default();
|
||||
|
||||
[JsonPropertyName("DailyObjectives")]
|
||||
public List<List<DailyObjective>> DailyObjectives { get; set; } = DailyObjective.DefaultSets();
|
||||
|
||||
[JsonPropertyName("ConfigTable")]
|
||||
public Dictionary<string, string> ConfigTable { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("ServiceUrls")]
|
||||
public Dictionary<string, string> ServiceUrls { get; set; } = [];
|
||||
}
|
||||
|
||||
public class PhotonConfig
|
||||
{
|
||||
[JsonPropertyName("AppId")]
|
||||
public string AppId { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("VoiceAppId")]
|
||||
public string VoiceAppId { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("CloudRegion")]
|
||||
public string CloudRegion { get; set; } = "us";
|
||||
|
||||
[JsonPropertyName("CrcCheckEnabled")]
|
||||
public bool CrcCheckEnabled { get; set; } = false;
|
||||
|
||||
[JsonPropertyName("EnableServerTracingAfterDisconnect")]
|
||||
public bool EnableServerTracingAfterDisconnect { get; set; } = false;
|
||||
}
|
||||
|
||||
public class MatchmakingParams
|
||||
{
|
||||
[JsonPropertyName("MaxPlayersPerRoom")]
|
||||
public int MaxPlayersPerRoom { get; set; } = 8;
|
||||
|
||||
[JsonPropertyName("MinPlayersToStart")]
|
||||
public int MinPlayersToStart { get; set; } = 1;
|
||||
}
|
||||
|
||||
public class LevelProgressionEntry
|
||||
{
|
||||
[JsonPropertyName("Level")]
|
||||
public int Level { get; set; }
|
||||
|
||||
// Note the lowercase 'p' — the client's RecRoomConfig.Deserialize calls
|
||||
// Util.GetKey<int>("RequiredXp", ...). PascalCase XP would miss.
|
||||
[JsonPropertyName("RequiredXp")]
|
||||
public int RequiredXp { get; set; }
|
||||
|
||||
public static List<LevelProgressionEntry> Default()
|
||||
{
|
||||
var entries = new List<LevelProgressionEntry>();
|
||||
for (int i = 0; i <= 30; i++)
|
||||
entries.Add(new LevelProgressionEntry { Level = i, RequiredXp = i * 500 });
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
// Inner item shape for RecRoomConfig.DailyObjectives, verified by the
|
||||
// disassembled iteration body — only "type" and "score" are read. Other
|
||||
// fields the client doesn't read get ignored, so we only send these two.
|
||||
public class DailyObjective
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public int Type { get; set; }
|
||||
|
||||
[JsonPropertyName("score")]
|
||||
public int Score { get; set; }
|
||||
|
||||
public static List<List<DailyObjective>> DefaultSets()
|
||||
{
|
||||
var sets = new List<List<DailyObjective>>();
|
||||
for (int day = 0; day < 7; day++)
|
||||
{
|
||||
sets.Add(
|
||||
[
|
||||
new DailyObjective { Type = 1, Score = 50 },
|
||||
new DailyObjective { Type = 2, Score = 30 },
|
||||
new DailyObjective { Type = 3, Score = 20 },
|
||||
]);
|
||||
}
|
||||
return sets;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RecNet.AutoMicMutingConfig — dump.cs class, Deserialize at RVA 0xFA3AE0.
|
||||
/// All 8 float fields are required (Util.GetKey<float>). Defaults are
|
||||
/// "feature effectively disabled" — high thresholds, no force-mute.
|
||||
/// </summary>
|
||||
public class AutoMicMutingConfig
|
||||
{
|
||||
[JsonPropertyName("MicSpamVolumeThreshold")]
|
||||
public float MicSpamVolumeThreshold { get; set; } = 1.0f;
|
||||
|
||||
[JsonPropertyName("MicVolumeSampleInterval")]
|
||||
public float MicVolumeSampleInterval { get; set; } = 0.1f;
|
||||
|
||||
[JsonPropertyName("MicVolumeSampleRollingWindowLength")]
|
||||
public float MicVolumeSampleRollingWindowLength { get; set; } = 5.0f;
|
||||
|
||||
[JsonPropertyName("MicSpamSamplePercentageForWarning")]
|
||||
public float MicSpamSamplePercentageForWarning { get; set; } = 1.0f;
|
||||
|
||||
[JsonPropertyName("MicSpamSamplePercentageForWarningToEnd")]
|
||||
public float MicSpamSamplePercentageForWarningToEnd { get; set; } = 0.5f;
|
||||
|
||||
[JsonPropertyName("MicSpamSamplePercentageForForceMute")]
|
||||
public float MicSpamSamplePercentageForForceMute { get; set; } = 1.0f;
|
||||
|
||||
[JsonPropertyName("MicSpamSamplePercentageForForceMuteToEnd")]
|
||||
public float MicSpamSamplePercentageForForceMuteToEnd { get; set; } = 0.5f;
|
||||
|
||||
[JsonPropertyName("MicSpamWarningStateVolumeMultiplier")]
|
||||
public float MicSpamWarningStateVolumeMultiplier { get; set; } = 1.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RecNet.ServerMaintenanceDTO — dump.cs, Deserialize at RVA 0xAF5450.
|
||||
/// Single required key: StartsInMinutes (int). A large positive value
|
||||
/// means "maintenance window is far away" = effectively never.
|
||||
/// </summary>
|
||||
public class ServerMaintenanceDTO
|
||||
{
|
||||
[JsonPropertyName("StartsInMinutes")]
|
||||
public int StartsInMinutes { get; set; } = int.MaxValue;
|
||||
}
|
||||
Reference in New Issue
Block a user