mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 07:29:00 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,610 @@
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RadicadeServer.API.Notifications;
|
||||
using RadicadeServer.Auth;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.RecNet.Accounts;
|
||||
using RadicadeServer.RecNet.Enums.Messages;
|
||||
using RadicadeServer.RecNet.Enums.Players;
|
||||
using RadicadeServer.RecNet.Notifications;
|
||||
using RadicadeServer.RecNet.Rooms;
|
||||
using RadicadeServer.Utils;
|
||||
|
||||
namespace RadicadeServer.API.Matchmaking
|
||||
{
|
||||
[Route("matchmaking/player")]
|
||||
public class MatchmakingPlayerAPI : Controller
|
||||
{
|
||||
private readonly RecDBContext db;
|
||||
|
||||
public MatchmakingPlayerAPI(RecDBContext context)
|
||||
{
|
||||
db = context;
|
||||
}
|
||||
|
||||
[HttpPut("photonregionpings")]
|
||||
public IActionResult SendPhotonRegionPings()
|
||||
{
|
||||
return Ok(new { });
|
||||
}
|
||||
|
||||
[HttpPut("gameserverregionpings")]
|
||||
public IActionResult SendGameServerRegionPings()
|
||||
{
|
||||
return Ok(new { });
|
||||
}
|
||||
|
||||
[HttpGet("qos")]
|
||||
public IActionResult GetQos()
|
||||
{
|
||||
return Ok(new[]
|
||||
{
|
||||
new { id = "us-west1", address = "34.169.254.144:50000" },
|
||||
new { id = "europe-west1", address = "35.205.141.119:50000" },
|
||||
new { id = "asia-northeast1", address = "35.200.67.228:50000" },
|
||||
new { id = "us-east1", address = "34.73.244.122:50000" },
|
||||
new { id = "us-central1", address = "34.69.179.51:50000" },
|
||||
new { id = "northamerica-northeast1", address = "34.152.4.100:50000" }
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("connection-info"), Authorize]
|
||||
public async Task<IActionResult> GetConnectionInfo([FromQuery] long? roomInstanceId = null)
|
||||
{
|
||||
long? nullableUserId = User.GetUserId();
|
||||
if (!nullableUserId.HasValue)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
long userId = nullableUserId.Value;
|
||||
|
||||
string photonRoomId = string.Empty;
|
||||
var presence = await db.Presences.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.playerId == userId);
|
||||
if (presence?.roomInstance.HasValue == true)
|
||||
{
|
||||
var instance = await db.RoomInstances.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.roomInstanceId == presence.roomInstance.Value);
|
||||
photonRoomId = instance?.photonRoomId ?? string.Empty;
|
||||
}
|
||||
if (string.IsNullOrEmpty(photonRoomId) && roomInstanceId.HasValue)
|
||||
{
|
||||
var requested = await db.RoomInstances.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.roomInstanceId == roomInstanceId.Value);
|
||||
photonRoomId = requested?.photonRoomId ?? string.Empty;
|
||||
}
|
||||
|
||||
var platformId = User.GetPlatId() ?? string.Empty;
|
||||
var tokenHandler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(DBConfig.JwtKey);
|
||||
var now = DateTime.UtcNow;
|
||||
var photonToken = tokenHandler.WriteToken(tokenHandler.CreateToken(
|
||||
new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new System.Security.Claims.ClaimsIdentity(new[]
|
||||
{
|
||||
new System.Security.Claims.Claim("sub", userId.ToString()),
|
||||
new System.Security.Claims.Claim("rn.platid", platformId),
|
||||
new System.Security.Claims.Claim("rn.plat", User.FindFirst("rn.plat")?.Value ?? "0"),
|
||||
new System.Security.Claims.Claim("rn.deviceclass", User.FindFirst("rn.deviceclass")?.Value ?? "0"),
|
||||
new System.Security.Claims.Claim("rn.env", "prod")
|
||||
}),
|
||||
Issuer = DBConfig.TokenIssuer,
|
||||
Expires = now.AddHours(1),
|
||||
SigningCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(
|
||||
new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(key),
|
||||
Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature)
|
||||
}));
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
success = true,
|
||||
value = new
|
||||
{
|
||||
photonAuthToken = photonToken,
|
||||
photonRealtimeAppId = DBConfig.PhotonRealtimeAppId,
|
||||
photonVoiceAppId = DBConfig.PhotonVoiceAppId,
|
||||
photonChatAppId = DBConfig.PhotonChatAppId,
|
||||
photonRegion = DBConfig.PhotonRegion,
|
||||
photonRoomId,
|
||||
voiceConnectionInfo = string.Empty,
|
||||
voiceServerId = string.Empty,
|
||||
experiments = new
|
||||
{
|
||||
networkTransformSyncInterval = 10.0,
|
||||
shouldUseUnreliableOnChange = false,
|
||||
shouldAvoidDiscontinuityRPCs = true,
|
||||
shouldAvoidRedundantDiscontinuity = false,
|
||||
r2RuntimeStaticBaking = true,
|
||||
r2AutoEmbodiment = true,
|
||||
r2RuntimeStaticBakingMinShapeThreshold = 1,
|
||||
r2UseCheapReplicas = true,
|
||||
shouldUseGameServerNetworking = false
|
||||
}
|
||||
},
|
||||
error = (string?)null
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("avoidjuniors"), Authorize]
|
||||
public IActionResult GetAvoidJuniors()
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
[HttpPut("avoidjuniors"), Authorize]
|
||||
public IActionResult SetAvoidJuniors()
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
[HttpPut("statusvisibility"), Authorize]
|
||||
public async Task<IActionResult> SetStatusVisibility(
|
||||
StatusVisibility statusVisibility)
|
||||
{
|
||||
long? nullableUserId = User.GetUserId();
|
||||
|
||||
if (!nullableUserId.HasValue)
|
||||
{
|
||||
return Unauthorized("Missing user ID.");
|
||||
}
|
||||
|
||||
long userId = nullableUserId.Value;
|
||||
|
||||
var presence = await db.Presences
|
||||
.FirstOrDefaultAsync(x => x.playerId == userId);
|
||||
|
||||
if (presence == null)
|
||||
{
|
||||
return NotFound("Player presence does not exist.");
|
||||
}
|
||||
|
||||
presence.statusVisibility = statusVisibility;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await Notify.UpdateSubscriptions(userId);
|
||||
|
||||
return Ok(new { });
|
||||
}
|
||||
|
||||
[HttpPost("heartbeat"), Authorize]
|
||||
public async Task<IActionResult> Heartbeat()
|
||||
{
|
||||
long? nullableUserId = User.GetUserId();
|
||||
|
||||
if (!nullableUserId.HasValue)
|
||||
{
|
||||
return Unauthorized("Missing user ID.");
|
||||
}
|
||||
|
||||
long userId = nullableUserId.Value;
|
||||
|
||||
try
|
||||
{
|
||||
return await HeartbeatCore(userId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
return Ok(new PlayerHeartbeat
|
||||
{
|
||||
playerId = userId,
|
||||
isOnline = true,
|
||||
deviceClass = DeviceClass.Screen,
|
||||
statusVisibility = StatusVisibility.Public,
|
||||
roomInstance = null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IActionResult> HeartbeatCore(long userId)
|
||||
{
|
||||
var presence = await db.Presences
|
||||
.FirstOrDefaultAsync(x => x.playerId == userId);
|
||||
|
||||
if (presence == null)
|
||||
{
|
||||
|
||||
presence = new RealPresence { playerId = userId };
|
||||
db.Presences.Add(presence);
|
||||
|
||||
await MatchmakingUtils.EnsureDormPresenceAsync(db, userId, presence);
|
||||
}
|
||||
|
||||
RoomInstance? roomInstance = null;
|
||||
|
||||
if (presence.roomInstance.HasValue)
|
||||
{
|
||||
long roomInstanceId = presence.roomInstance.Value;
|
||||
|
||||
roomInstance = await db.RoomInstances
|
||||
.FirstOrDefaultAsync(
|
||||
x => x.roomInstanceId == roomInstanceId
|
||||
);
|
||||
|
||||
if (roomInstance == null)
|
||||
{
|
||||
presence.roomInstance = null;
|
||||
MatchmakingUtils.ClearInRoom(userId);
|
||||
}
|
||||
}
|
||||
|
||||
if (roomInstance != null)
|
||||
{
|
||||
roomInstance.eventId ??= 0;
|
||||
roomInstance.clubId ??= 0;
|
||||
roomInstance.roomCode ??= string.Empty;
|
||||
roomInstance.location ??= string.Empty;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (roomInstance != null)
|
||||
{
|
||||
var currentRoom = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == roomInstance.roomId);
|
||||
bool isOwnDorm = currentRoom != null && currentRoom.IsDorm && currentRoom.CreatorAccountId == userId;
|
||||
if (!isOwnDorm)
|
||||
{
|
||||
var ban = await db.ResolveBanAsync(userId, Request.GetClientIp());
|
||||
|
||||
if (ban != null && !await db.IsDeveloper(userId))
|
||||
{
|
||||
await PlayerUtils.EjectToDormAsync(db, userId);
|
||||
roomInstance = presence.roomInstance.HasValue
|
||||
? await db.RoomInstances.FirstOrDefaultAsync(x => x.roomInstanceId == presence.roomInstance.Value)
|
||||
: null;
|
||||
|
||||
await Notify.SendNotify(userId, new Notification
|
||||
{
|
||||
Id = NotificationType.ModerationKick,
|
||||
Msg = await PlayerUtils.BuildBanBlockDetailsAsync(db, ban, userId)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
presence.isOnline = true;
|
||||
presence.deviceClass = DeviceClass.Screen;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
if (roomInstance != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var xpRoom = await db.Rooms.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.RoomId == roomInstance.roomId);
|
||||
bool publicRoom = xpRoom != null && !xpRoom.IsDorm &&
|
||||
xpRoom.Accessibility == RadicadeServer.RecNet.Enums.Accessibility.Public;
|
||||
await RoomProgressionService.AwardRoomXpAsync(db, userId, roomInstance.roomId, publicRoom);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
var heartbeat = new PlayerHeartbeat
|
||||
{
|
||||
deviceClass = presence.deviceClass,
|
||||
isOnline = true,
|
||||
playerId = userId,
|
||||
roomInstance = roomInstance,
|
||||
statusVisibility = presence.statusVisibility
|
||||
};
|
||||
|
||||
if (roomInstance != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var presenceUpdate =
|
||||
await Presence.CreateAsync(db, presence, roomInstance);
|
||||
|
||||
await Notify.SendNotify(
|
||||
userId,
|
||||
new Notification
|
||||
{
|
||||
Id = "PresenceUpdate",
|
||||
Msg = presenceUpdate
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Notify.UpdateSubscriptions(userId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
return Ok(heartbeat);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpPost("notifydisconnect"), Authorize]
|
||||
public async Task<IActionResult> NotifyDisconnect(
|
||||
[FromForm(Name = "playerId")] long playerId = 0,
|
||||
[FromForm(Name = "loginLock")] string? loginLock = null)
|
||||
{
|
||||
var requesterId = User.GetUserId();
|
||||
if (!requesterId.HasValue) return Unauthorized();
|
||||
|
||||
long targetId = playerId > 0 ? playerId : 0;
|
||||
|
||||
|
||||
|
||||
if (targetId == 0 && !string.IsNullOrWhiteSpace(loginLock))
|
||||
{
|
||||
var byLock = await db.Presences.FirstOrDefaultAsync(x => x.LoginLock == loginLock);
|
||||
if (byLock != null) targetId = byLock.playerId;
|
||||
}
|
||||
|
||||
if (targetId == 0) targetId = requesterId.Value;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var ban = await db.ResolveBanAsync(targetId, Request.GetClientIp());
|
||||
if (ban == null) return Ok();
|
||||
|
||||
|
||||
|
||||
await PlayerUtils.EjectToDormAsync(db, targetId);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (targetId != requesterId.Value)
|
||||
{
|
||||
await Notify.SendNotify(targetId, new Notification
|
||||
{
|
||||
Id = NotificationType.ModerationKick,
|
||||
Msg = await PlayerUtils.BuildBanBlockDetailsAsync(db, ban, targetId)
|
||||
});
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("login"), Authorize]
|
||||
public async Task<IActionResult> Login(
|
||||
[FromForm(Name = "LoginLock")]
|
||||
string? loginLock = null,
|
||||
|
||||
[FromForm(Name = "identification")]
|
||||
string? identification = null,
|
||||
|
||||
[FromForm(Name = "JoinMode")]
|
||||
int joinMode = 0,
|
||||
|
||||
[FromForm(Name = "BypassMovementModeRestriction")]
|
||||
bool bypassMovementModeRestriction = false)
|
||||
{
|
||||
long? nullableUserId = User.GetUserId();
|
||||
|
||||
if (!nullableUserId.HasValue)
|
||||
{
|
||||
|
||||
return Unauthorized(new
|
||||
{
|
||||
error = "invalid_token",
|
||||
error_description = "Missing user ID."
|
||||
});
|
||||
}
|
||||
|
||||
long userId = nullableUserId.Value;
|
||||
|
||||
string? platformId = User.GetPlatId();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(platformId))
|
||||
{
|
||||
|
||||
return Unauthorized(new
|
||||
{
|
||||
error = "invalid_token",
|
||||
error_description = "Missing platform ID."
|
||||
});
|
||||
}
|
||||
|
||||
bool isWhitelisted;
|
||||
|
||||
try
|
||||
{
|
||||
isWhitelisted =
|
||||
await AuthShared.IsWhitelisted(platformId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
return StatusCode(
|
||||
StatusCodes.Status500InternalServerError,
|
||||
new
|
||||
{
|
||||
error = "server_error",
|
||||
error_description =
|
||||
"Whitelist check failed."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!isWhitelisted)
|
||||
{
|
||||
|
||||
return Unauthorized(new
|
||||
{
|
||||
error = "not_whitelisted",
|
||||
error_description =
|
||||
"This account is not whitelisted."
|
||||
});
|
||||
}
|
||||
|
||||
var presence = await db.Presences
|
||||
.FirstOrDefaultAsync(
|
||||
x => x.playerId == userId
|
||||
);
|
||||
|
||||
if (presence == null)
|
||||
{
|
||||
|
||||
presence = new RealPresence
|
||||
{
|
||||
playerId = userId
|
||||
};
|
||||
|
||||
db.Presences.Add(presence);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(loginLock))
|
||||
{
|
||||
presence.LoginLock = loginLock;
|
||||
}
|
||||
|
||||
presence.isOnline = true;
|
||||
presence.deviceClass = DeviceClass.Screen;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (!presence.roomInstance.HasValue)
|
||||
{
|
||||
await MatchmakingUtils.EnsureDormPresenceAsync(
|
||||
db,
|
||||
userId,
|
||||
presence);
|
||||
}
|
||||
else
|
||||
{
|
||||
presence.isOnline = true;
|
||||
}
|
||||
|
||||
if (presence.roomInstance.HasValue)
|
||||
{
|
||||
MatchmakingUtils.MarkInRoom(userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
MatchmakingUtils.ClearInRoom(userId);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
try
|
||||
{
|
||||
await Notify.UpdateSubscriptions(userId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
success = true,
|
||||
playerId = userId,
|
||||
accountId = userId,
|
||||
platformId,
|
||||
loginLock = presence.LoginLock ??
|
||||
string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("exclusivelogin"), Authorize]
|
||||
public Task<IActionResult> ExclusiveLogin(
|
||||
[FromForm(Name = "LoginLock")]
|
||||
string? loginLock = null,
|
||||
|
||||
[FromForm(Name = "identification")]
|
||||
string? identification = null,
|
||||
|
||||
[FromForm(Name = "JoinMode")]
|
||||
int joinMode = 0,
|
||||
|
||||
[FromForm(Name = "BypassMovementModeRestriction")]
|
||||
bool bypassMovementModeRestriction = false)
|
||||
{
|
||||
return Login(
|
||||
loginLock,
|
||||
identification,
|
||||
joinMode,
|
||||
bypassMovementModeRestriction);
|
||||
}
|
||||
|
||||
[HttpPost("logout"), Authorize]
|
||||
public async Task<IActionResult> Logout(
|
||||
[FromForm(Name = "LoginLock")]
|
||||
string? loginLock = null)
|
||||
{
|
||||
long? nullableUserId = User.GetUserId();
|
||||
|
||||
if (!nullableUserId.HasValue)
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
long userId = nullableUserId.Value;
|
||||
|
||||
var presence = await db.Presences
|
||||
.FirstOrDefaultAsync(
|
||||
x => x.playerId == userId
|
||||
);
|
||||
|
||||
if (presence == null)
|
||||
{
|
||||
MatchmakingUtils.ClearInRoom(userId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
presence.LoginLock = null;
|
||||
presence.isOnline = false;
|
||||
presence.roomInstance = null;
|
||||
|
||||
MatchmakingUtils.ClearInRoom(userId);
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var subscriberUpdate = Notify.UpdateSubscriptions(userId);
|
||||
try
|
||||
{
|
||||
await Notify.SendNotify(userId, new Notification
|
||||
{
|
||||
Id = "PresenceUpdate",
|
||||
Msg = await Presence.CreateAsync(db, presence, null)
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
await subscriberUpdate;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user