mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 15:08:47 -07:00
feat: DorkNet.Quest, a sideload patcher for the 2023 Quest client
Patches the Quest build of Rec Room 2023.03.21 (20230317 / 2128210, the
same appVersion as the PC client) to connect to a DorkNet server, using
one native hook library loaded via DT_NEEDED on libmain.so.
- host rewrite onto <svc>.<ServerHost> or a {service}-dev.example.com template
- Photon app ids and region injection (PhotonRegion must match PC)
- TLS / pinning / image-signature bypass, integrity callbacks neutralised
- no-Meta-account build; the server treats platform id 0/1/blank as a
placeholder and keys those accounts by device id
- build.ps1 -Obb packages a DorkNet-<ver>.xapk with one-command
installers for Windows and macOS/Linux; app is named via AppName
- README, docs/quest.md and NOTICE updated; credits RecFlare and
And64InlineHook (MIT)
Tested on a Quest 2: all hooks arm, requests reach the dev server.
This commit is contained in:
@@ -423,6 +423,38 @@ public class PlayerService(DorkNetDbContext db, RoomService rooms, ILogger<Playe
|
||||
outfitsFixed, inventoryFixed, colorsFixed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True for a platform id that identifies nobody: blank, "0" (no platform
|
||||
/// session) or "1". A Quest client patched without its own Meta app
|
||||
/// (DorkNet.Quest, no-Meta mode) has no platform session, so it reports
|
||||
/// the same placeholder user id "1" from every headset. Treating that as a
|
||||
/// real id would list every Quest player's account in every other Quest
|
||||
/// player's remembered-logins prompt and, with no device id, merge them
|
||||
/// into one synthetic account. Placeholders are never stored on an
|
||||
/// account, never matched by the cached-login lookup and never used to
|
||||
/// synthesise a device id — those players are keyed by deviceId alone.
|
||||
/// </summary>
|
||||
public static bool IsPlaceholderPlatformId(string? platformId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(platformId)) return true;
|
||||
var trimmed = platformId.Trim();
|
||||
return trimmed == "0" || trimmed == "1";
|
||||
}
|
||||
|
||||
/// <summary>Lookup-only counterpart to
|
||||
/// <see cref="GetOrCreateByDeviceAsync"/>. Returns the existing player
|
||||
/// row for the given deviceId, or null if none exists — never creates
|
||||
/// a new account. Callers that want to enforce the admin "signups
|
||||
/// disabled" toggle use this to gate the device-id login fallback
|
||||
/// without accidentally minting an account.</summary>
|
||||
public async Task<PlayerEntity?> GetByDeviceAsync(string? deviceId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(deviceId)) return null;
|
||||
return await db.Players
|
||||
.Include(p => p.Avatar)
|
||||
.FirstOrDefaultAsync(p => p.DeviceId == deviceId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Look up an existing account by deviceId, or create a new one if no
|
||||
/// account is bound to that device yet. This is the canonical
|
||||
@@ -440,26 +472,13 @@ public class PlayerService(DorkNetDbContext db, RoomService rooms, ILogger<Playe
|
||||
/// - When deviceId is empty (rare paths), fall back to a generated
|
||||
/// synthetic id so we never create accidentally-shared accounts.
|
||||
/// </summary>
|
||||
/// <summary>Lookup-only counterpart to
|
||||
/// <see cref="GetOrCreateByDeviceAsync"/>. Returns the existing player
|
||||
/// row for the given deviceId, or null if none exists — never creates
|
||||
/// a new account. Callers that want to enforce the admin "signups
|
||||
/// disabled" toggle use this to gate the device-id login fallback
|
||||
/// without accidentally minting an account.</summary>
|
||||
public async Task<PlayerEntity?> GetByDeviceAsync(string? deviceId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(deviceId)) return null;
|
||||
return await db.Players
|
||||
.Include(p => p.Avatar)
|
||||
.FirstOrDefaultAsync(p => p.DeviceId == deviceId);
|
||||
}
|
||||
|
||||
public async Task<PlayerEntity> GetOrCreateByDeviceAsync(
|
||||
string? deviceId,
|
||||
int platform = 0,
|
||||
string? platformId = null,
|
||||
string? displayName = null)
|
||||
{
|
||||
if (IsPlaceholderPlatformId(platformId)) platformId = null;
|
||||
if (string.IsNullOrWhiteSpace(deviceId))
|
||||
{
|
||||
// Synthesize a stable id from the platformId or generate one so
|
||||
@@ -559,6 +578,7 @@ public class PlayerService(DorkNetDbContext db, RoomService rooms, ILogger<Playe
|
||||
string? platformId = null,
|
||||
string? displayName = null)
|
||||
{
|
||||
if (IsPlaceholderPlatformId(platformId)) platformId = null;
|
||||
// Synthesise deviceId / platformId for empty inputs so the
|
||||
// audit trail still lands somewhere meaningful.
|
||||
var effectiveDeviceId = string.IsNullOrWhiteSpace(deviceId)
|
||||
@@ -646,7 +666,7 @@ public class PlayerService(DorkNetDbContext db, RoomService rooms, ILogger<Playe
|
||||
if (player is null) return;
|
||||
player.LastSeenAt = DateTime.UtcNow;
|
||||
if (platform != 0) player.LastPlatform = platform;
|
||||
if (!string.IsNullOrWhiteSpace(platformId)) player.LastPlatformId = platformId;
|
||||
if (!IsPlaceholderPlatformId(platformId)) player.LastPlatformId = platformId!;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@@ -663,7 +683,7 @@ public class PlayerService(DorkNetDbContext db, RoomService rooms, ILogger<Playe
|
||||
/// </summary>
|
||||
public async Task<List<PlayerEntity>> GetCachedLoginsAsync(int platform, string? platformId, int take = 10)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(platformId))
|
||||
if (IsPlaceholderPlatformId(platformId))
|
||||
return [];
|
||||
return await db.Players
|
||||
.Where(p => p.LastPlatform == platform && p.LastPlatformId == platformId)
|
||||
|
||||
Reference in New Issue
Block a user