yea
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FemRec2023.Classes;
|
||||
using FemRec2023.Classes.DBs;
|
||||
using FemRec2023.Classes.DBs.DBClasses;
|
||||
using FemRec2023.Auth;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace FemRec2023.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
public class APIController : ControllerBase
|
||||
{
|
||||
[HttpGet("/")]
|
||||
public IActionResult GetNS()
|
||||
{
|
||||
string url = ServerConfig.BaseURL;
|
||||
return Ok(new
|
||||
{
|
||||
Accounts = url + "/acc",
|
||||
API = url,
|
||||
Auth = url + "/auth",
|
||||
BugReporting = url,
|
||||
Cards = url,
|
||||
CDN = url + "/cdn",
|
||||
Chat = url,
|
||||
Clubs = url,
|
||||
CMS = url,
|
||||
Commerce = url,
|
||||
Data = url,
|
||||
DataCollection = url,
|
||||
Discovery = url,
|
||||
Econ = url,
|
||||
GameLogs = url,
|
||||
Geo = url,
|
||||
Images = url + "/imageserver",
|
||||
Leaderboard = url,
|
||||
Link = url,
|
||||
Lists = url,
|
||||
Matchmaking = url + "/match",
|
||||
Moderation = url,
|
||||
Notifications = url + "/noti",
|
||||
PlatformNotifications = url,
|
||||
PlayerSettings = url,
|
||||
RoomComments = url,
|
||||
Rooms = url + "/roomserver",
|
||||
Storage = url,
|
||||
Strings = url,
|
||||
StringsCDN = url,
|
||||
Studio = url,
|
||||
Thorn = url,
|
||||
Videos = url,
|
||||
WWW = url
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("api/versioncheck/v4")]
|
||||
public IActionResult VersionCheck()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
ValidVersion = 0,
|
||||
VersionStatus = 0,
|
||||
UpdateNotificationStage = 0,
|
||||
IsVersionIslanded = false,
|
||||
IsCrossPlayDisabled = false
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("api/gameconfigs/v1/all")]
|
||||
public IActionResult GetGameConfigs()
|
||||
{
|
||||
string path = Path.Combine(Program.dataDir, "APIS", "GameConfigs.json");
|
||||
return System.IO.File.Exists(path) ? Content(System.IO.File.ReadAllText(path), "application/json") : NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("api/config/v1/amplitude")]
|
||||
public IActionResult GetAmplitude()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
AmplitudeKey = "cb2fb2ecb9953512c29af5bca58f2b4a",
|
||||
UseRudderStack = true,
|
||||
RudderStackKey = "23NiJHIgu3koaGNCZIiuYvIQNCu",
|
||||
UseStatSig = true,
|
||||
StatSigKey = "client-SBZkOrjD3r1Cat3f3W8K6sBd11WKlXZXIlCWj6l4Aje",
|
||||
StatSigEnvironment = 0
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("api/avatar/v1/defaultunlocked")]
|
||||
public IActionResult GetDefaultUnlocked()
|
||||
{
|
||||
return Ok(ServerConfig.Bracket);
|
||||
}
|
||||
|
||||
[HttpGet("api/avatar/v1/defaultbaseavataritems")]
|
||||
public IActionResult GetDefaultBaseAvatarItems()
|
||||
{
|
||||
return Ok(ServerConfig.Bracket);
|
||||
}
|
||||
|
||||
[HttpGet("/api/objectives/v1/myprogress")]
|
||||
public IActionResult GetMyObjectiveProgress()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Objectives = new List<object>(),
|
||||
ObjectiveGroups = new List<object>()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("/api/avatar/v2")]
|
||||
public IActionResult GetMyAvatar()
|
||||
{
|
||||
var player = AuthStuff.GetCurrentPlayer(Request);
|
||||
if (player == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(player.Player.PlayerExtra.Avatar);
|
||||
}
|
||||
|
||||
[HttpPost("/api/avatar/v2/set")]
|
||||
public IActionResult SetMyAvatar([FromBody] PlayerDBClasses.Avatar request)
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(PlayerDB.SetAvatar((long)id, request));
|
||||
}
|
||||
|
||||
[HttpGet("/api/avatar/v4/items")]
|
||||
public IActionResult GetMyAvatarItems()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
string path = Path.Combine(Program.dataDir, "APIS", "Items", "AvatarItems.json");
|
||||
return System.IO.File.Exists(path) ? Content(System.IO.File.ReadAllText(path), "application/json") : NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("/api/PlayerReporting/v1/moderationBlockDetails")]
|
||||
public IActionResult GetMyModerationBlockDetails()
|
||||
{
|
||||
var player = AuthStuff.GetCurrentPlayer(Request);
|
||||
if (player == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(player.Player.PlayerExtra.ModerationBlockDetails);
|
||||
}
|
||||
|
||||
[HttpGet("/api/relationships/v2/get")]
|
||||
public IActionResult GetMyRelationships()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(ServerConfig.Bracket);
|
||||
}
|
||||
|
||||
[HttpGet("/api/messages/v2/get")]
|
||||
public IActionResult GetMyMessages()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(ServerConfig.Bracket);
|
||||
}
|
||||
|
||||
[HttpGet("/playersettings")]
|
||||
public IActionResult GetMySettings()
|
||||
{
|
||||
var player = AuthStuff.GetCurrentPlayer(Request);
|
||||
if (player == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(player.Player.PlayerExtra.Settings);
|
||||
}
|
||||
|
||||
[HttpPut("/playersettings")]
|
||||
public IActionResult SetMySettings([FromForm] string key, [FromForm] string value)
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
PlayerDB.SetPlayerSetting(key, value ?? "", (long)id);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpGet("/econ/customAvatarItems/v1/owned")]
|
||||
public IActionResult GetMyOwnedCustomAvatarItems([FromQuery] int skip, [FromQuery] int take)
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Results = Array.Empty<object>(),
|
||||
TotalResults = 0
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("/api/checklist/v1/current")]
|
||||
public IActionResult GetMyChecklist()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(ServerConfig.Bracket);
|
||||
}
|
||||
|
||||
[HttpGet("/api/players/v2/progression/bulk")]
|
||||
public IActionResult GetProgressionForPlayers([FromQuery] List<long> id)
|
||||
{
|
||||
var authId = AuthStuff.GetPlayerId(Request);
|
||||
if (authId == null)
|
||||
return Unauthorized();
|
||||
|
||||
if (id == null || id.Count == 0)
|
||||
return Ok(new List<PlayerDBClasses.PlayerProgressionDTO>());
|
||||
|
||||
var progressions = PlayerDB.GetProgressionBulk(id);
|
||||
|
||||
return Ok(progressions);
|
||||
}
|
||||
|
||||
[HttpGet("/api/playerReputation/v2/bulk")]
|
||||
public IActionResult GetReputationBulk([FromQuery] List<long> id)
|
||||
{
|
||||
var authId = AuthStuff.GetPlayerId(Request);
|
||||
if (authId == null)
|
||||
return Unauthorized();
|
||||
|
||||
if (id == null || id.Count == 0)
|
||||
return Ok(new List<PlayerDBClasses.Reputation>());
|
||||
|
||||
var results = PlayerDB.GetReputationBulk(id);
|
||||
return Ok(results);
|
||||
}
|
||||
|
||||
[HttpPost("/api/PlayerReporting/v1/hile")] // todo log to channel
|
||||
public IActionResult PlayerReportingHile()
|
||||
{
|
||||
var authId = AuthStuff.GetPlayerId(Request);
|
||||
if (authId == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
[HttpGet("api/config/v2")]
|
||||
public IActionResult GetConfigV2()
|
||||
{
|
||||
string path = Path.Combine(Program.dataDir, "APIS", "ConfigV2.json");
|
||||
return System.IO.File.Exists(path) ? Content(System.IO.File.ReadAllText(path), "application/json") : NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("/api/announcement/v1/get")]
|
||||
[HttpGet("/api/PlayerReporting/v1/voteToKickReasons")]
|
||||
[HttpGet("/api/avatar/v3/saved")]
|
||||
[HttpGet("/api/equipment/v2/getUnlocked")]
|
||||
[HttpGet("/api/consumables/v2/getUnlocked")]
|
||||
[HttpGet("/api/images/v2/named")]
|
||||
[HttpGet("/api/avatar/v2/gifts")]
|
||||
[HttpGet("/api/gamerewards/v1/pending")]
|
||||
[HttpGet("/api/roomkeys/v1/mine")]
|
||||
[HttpGet("/cdn/config/LoadingScreenTipData")]
|
||||
[HttpGet("/api/roomcurrencies/v1/currencies")]
|
||||
[HttpGet("/api/inventions/v2/mine")]
|
||||
public IActionResult TodoImplement()
|
||||
{
|
||||
return Ok(ServerConfig.Bracket);
|
||||
}
|
||||
|
||||
[HttpGet("/api/playerevents/v1/all")]
|
||||
public IActionResult GetAllPlayerEvents()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
Created = Array.Empty<object>(),
|
||||
Responses = Array.Empty<object>()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("/api/customAvatarItems/v1/isCreationAllowedForAccount")]
|
||||
public IActionResult GetCustomAvatarItemsIsCreationAllowedForAccount()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
success = true,
|
||||
value = (object?)null
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("/api/customAvatarItems/v1/isCreationEnabled")]
|
||||
[HttpGet("/api/customAvatarItems/v1/isRenderingEnabled")]
|
||||
public IActionResult CustomAvatarItemsIsEnabled()
|
||||
{
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
[HttpGet("/api/roomconsumables/v1/roomConsumable/room/{roomId}")]
|
||||
public IActionResult GetRoomConsumablesForRoom(long roomId)
|
||||
{
|
||||
return Ok(ServerConfig.Bracket);
|
||||
}
|
||||
|
||||
[HttpPost("/api/sanitize/v1")]
|
||||
public IActionResult SanitizeV1([FromBody] SanitizeRequest request)
|
||||
{
|
||||
return Ok(JsonSerializer.Serialize(request.Value));
|
||||
}
|
||||
|
||||
[HttpPost("/api/sanitize/v1/isPure")]
|
||||
public IActionResult SanitizeV1IsPure()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
IsPure = true
|
||||
});
|
||||
}
|
||||
|
||||
public class SanitizeRequest
|
||||
{
|
||||
public string Value { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FemRec2023.Classes;
|
||||
using FemRec2023.Classes.DBs;
|
||||
using FemRec2023.Classes.DBs.DBClasses;
|
||||
using FemRec2023.Auth;
|
||||
|
||||
namespace FemRec2023.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("/acc")]
|
||||
public class AccountController : ControllerBase
|
||||
{
|
||||
[HttpGet("account/bulk")]
|
||||
public IActionResult GetAccountsBulk([FromQuery] List<long> id)
|
||||
{
|
||||
return Ok(PlayerDB.GetAccountsBulk(id));
|
||||
}
|
||||
|
||||
[HttpGet("account/me")]
|
||||
public IActionResult GetAccountMe()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
var account = PlayerDB.GetAccountMe(id.Value);
|
||||
return account != null ? Ok(account) : NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FemRec2023.Classes;
|
||||
using FemRec2023.Classes.DBs;
|
||||
using FemRec2023.Classes.DBs.DBClasses;
|
||||
using FemRec2023.Auth;
|
||||
|
||||
namespace FemRec2023.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("/auth")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
[HttpGet("eac/challenge")]
|
||||
public IActionResult GetEACChallenge()
|
||||
{
|
||||
string challenge = $"\"e\"";
|
||||
return Ok(challenge);
|
||||
}
|
||||
|
||||
[HttpGet("cachedlogin/forplatformid/{platform}/{platformId}")]
|
||||
public IActionResult GetCachedLogins(PlayerDBClasses.Platforms platform, ulong platformId)
|
||||
{
|
||||
if (PlayerDB.GetLogins(platform, platformId, out var accounts) && accounts.Count > 0)
|
||||
{
|
||||
return Ok(accounts);
|
||||
}
|
||||
|
||||
var newPlayer = PlayerDB.CreateAccount(platform, platformId, false); // todo use connect token create acc grant type instead but ts is for now
|
||||
|
||||
var newCachedLogin = new List<PlayerDBClasses.CachedLogins>
|
||||
{
|
||||
new PlayerDBClasses.CachedLogins
|
||||
{
|
||||
accountId = newPlayer.PlayerId,
|
||||
lastLoginTime = newPlayer.Player.LastLoginAt,
|
||||
platform = platform,
|
||||
platformId = platformId.ToString(),
|
||||
requirePassword = false
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(newCachedLogin);
|
||||
}
|
||||
|
||||
[HttpPost("connect/token")]
|
||||
public async Task<IActionResult> ConnectToken(
|
||||
[FromForm] string grant_type,
|
||||
[FromForm] long account_id,
|
||||
[FromForm] string client_id,
|
||||
[FromForm] string client_secret,
|
||||
[FromForm] PlayerDBClasses.Platforms platform,
|
||||
[FromForm] ulong platform_id,
|
||||
[FromForm] string device_id,
|
||||
[FromForm] PlayerDBClasses.DeviceClasses? device_class,
|
||||
[FromForm] DateTime? time,
|
||||
[FromForm] int? ver,
|
||||
[FromForm] string build_key,
|
||||
[FromForm] string asid,
|
||||
[FromForm] string eac_challenge,
|
||||
[FromForm] string eac_response,
|
||||
[FromForm] string platform_auth
|
||||
)
|
||||
{
|
||||
switch (grant_type)
|
||||
{
|
||||
case "cached_login":
|
||||
{
|
||||
string token = AuthStuff.Encode(account_id);
|
||||
Console.WriteLine("bitch try to login: who? here: " + account_id);
|
||||
return Ok(new
|
||||
{
|
||||
access_token = token,
|
||||
error = "",
|
||||
error_description = "",
|
||||
refresh_token = "eeeeeeeeeeee",
|
||||
key = ""
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
namespace FemRec2023.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
public class ImageController : ControllerBase
|
||||
{
|
||||
[Route("/imageserver/{*img_path}")]
|
||||
public async Task<IActionResult> ImgServer(string img_path, int width = 0, int height = 0, string sig = "p1")
|
||||
{
|
||||
img_path = Uri.UnescapeDataString(img_path ?? "").TrimStart('/');
|
||||
|
||||
HttpContext.Response.Headers.Append("content-signature", "key-id=KEY:RSA:p1.rec.net; data=IWwe/pZ5vWWqNSkSM/54isgDxlZkdrP0sUrppKCbNktO2yCOTjq746xWiiLsueGuVcAGQqkjeRTimxolHckS/YXSYkEJxtiCXbLlsRia2DyAqtWVkGWsfczzFhp/56U66FVzolTspPCvjScOVlGO7dDIK7sJ+ndcRauWjsQsC6g3e7rUc6uwY099a6gy7sw6xr5BFZQSz8wg+fqyHYD/Sc4nQQVOTFZNNASqbJYhpNhEMXRnafCMuLl8a3mkGwvy3t4q2D/7SM48xrGZjEV47qNx1A91KCe28XVToFh4BzwEUU8nZ0d+KwV79MGarLo1cY8igc8FcoThKcovI4ClOg==");
|
||||
HttpContext.Response.Headers.Append("Content-Disposition", $"inline; filename=\"{Path.GetFileName(img_path)}\"");
|
||||
HttpContext.Response.Headers.Append("Access-Control-Allow-Origin", "*");
|
||||
HttpContext.Response.Headers.Append("Access-Control-Allow-Headers", "Content-Type, Authorization, Cache-Control");
|
||||
HttpContext.Response.Headers.Append("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
Response.Headers["Cache-Control"] = "public, max-age=14400";
|
||||
|
||||
var etag = $"\"{img_path.GetHashCode()}\"";
|
||||
HttpContext.Response.Headers.Append("ETag", etag);
|
||||
|
||||
bool cropSquare = HttpContext.Request.Query.ContainsKey("cropsquare") &&
|
||||
(HttpContext.Request.Query["cropsquare"].ToString().ToLower() == "true" ||
|
||||
HttpContext.Request.Query["cropsquare"].ToString() == "1");
|
||||
|
||||
string baseImagesPath = Path.Combine(Program.dataDir, "Images");
|
||||
string foundLocalPath = null;
|
||||
|
||||
if (Directory.Exists(baseImagesPath))
|
||||
{
|
||||
string directPath = Path.Combine(baseImagesPath, img_path);
|
||||
if (System.IO.File.Exists(directPath))
|
||||
{
|
||||
foundLocalPath = directPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
var subDirectories = Directory.EnumerateDirectories(baseImagesPath, "*", SearchOption.AllDirectories);
|
||||
foreach (var dir in subDirectories)
|
||||
{
|
||||
string testPath = Path.Combine(dir, img_path);
|
||||
if (System.IO.File.Exists(testPath))
|
||||
{
|
||||
foundLocalPath = testPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundLocalPath != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var imageBytes = await System.IO.File.ReadAllBytesAsync(foundLocalPath);
|
||||
imageBytes = await ProcessImageAsync(imageBytes, cropSquare, width, height);
|
||||
return File(imageBytes, "image/png");
|
||||
}
|
||||
catch
|
||||
{
|
||||
var fallbackBytes = await System.IO.File.ReadAllBytesAsync(foundLocalPath);
|
||||
return File(fallbackBytes, "image/png");
|
||||
}
|
||||
}
|
||||
|
||||
string recNetLocalPath = Path.Combine(baseImagesPath, "RecNet", img_path);
|
||||
|
||||
try
|
||||
{
|
||||
using HttpClient client = new();
|
||||
byte[] data = await client.GetByteArrayAsync($"https://img.rec.net/{img_path}");
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(recNetLocalPath)!);
|
||||
await System.IO.File.WriteAllBytesAsync(recNetLocalPath, data);
|
||||
|
||||
try
|
||||
{
|
||||
var processed = await ProcessImageAsync(data, cropSquare, width, height);
|
||||
return File(processed, GetMimeType(img_path));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return File(data, GetMimeType(img_path));
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
Console.WriteLine($"[rec_net fetch failed] {ex.Message}");
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
private static string GetMimeType(string filePath)
|
||||
{
|
||||
var provider = new Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider();
|
||||
if (!provider.TryGetContentType(filePath, out var contentType))
|
||||
{
|
||||
contentType = "image/png";
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
||||
private static async Task<byte[]> ProcessImageAsync(byte[] imageBytes, bool cropSquare, int width, int height)
|
||||
{
|
||||
using var ms = new MemoryStream(imageBytes);
|
||||
using var image = await SixLabors.ImageSharp.Image.LoadAsync<Rgba32>(ms);
|
||||
|
||||
if (cropSquare)
|
||||
{
|
||||
int size = Math.Min(image.Width, image.Height);
|
||||
int x = (image.Width - size) / 2;
|
||||
int y = (image.Height - size) / 2;
|
||||
image.Mutate(ctx => ctx.Crop(new Rectangle(x, y, size, size)));
|
||||
|
||||
int targetSize = width > 0 ? width : height;
|
||||
if (targetSize > 0)
|
||||
{
|
||||
image.Mutate(ctx => ctx.Resize(new ResizeOptions
|
||||
{
|
||||
Size = new Size(targetSize, targetSize),
|
||||
Mode = ResizeMode.Max,
|
||||
Sampler = KnownResamplers.Lanczos3
|
||||
}));
|
||||
}
|
||||
}
|
||||
else if (width > 0 || height > 0)
|
||||
{
|
||||
int resizeWidth = width;
|
||||
int resizeHeight = height;
|
||||
|
||||
if (width > 0 && height == 0)
|
||||
{
|
||||
resizeHeight = (int)((double)image.Height / image.Width * width);
|
||||
}
|
||||
else if (height > 0 && width == 0)
|
||||
{
|
||||
resizeWidth = (int)((double)image.Width / image.Height * height);
|
||||
}
|
||||
|
||||
image.Mutate(ctx => ctx.Resize(resizeWidth, resizeHeight));
|
||||
}
|
||||
|
||||
using var output = new MemoryStream();
|
||||
await image.SaveAsync(output, new PngEncoder());
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FemRec2023.Classes;
|
||||
using FemRec2023.Classes.DBs;
|
||||
using FemRec2023.Classes.DBs.DBClasses;
|
||||
using FemRec2023.Auth;
|
||||
|
||||
namespace FemRec2023.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("/match")]
|
||||
public class MatchController : ControllerBase
|
||||
{
|
||||
[HttpGet("player")]
|
||||
public IActionResult GetPlayerHeartbeatBulk(List<long> id)
|
||||
{
|
||||
var playerId = AuthStuff.GetPlayerId(Request);
|
||||
if (playerId == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(PlayerDB.GetPlayerHeartbeatsBulk(id));
|
||||
}
|
||||
|
||||
[HttpPost("player/login")]
|
||||
[HttpPost("player/exclusivelogin")]
|
||||
public IActionResult PlayerLogin()
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("player/logout")]
|
||||
public IActionResult PlayerLogout()
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("player/heartbeat")]
|
||||
public IActionResult GetPlayerHeartbeat()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(PlayerDB.GetPlayerHeartbeat((long)id));
|
||||
}
|
||||
|
||||
[HttpPost("matchmake/none")]
|
||||
public IActionResult MatchmakeNone()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(PlayerDB.GetPlayerHeartbeat((long)id));
|
||||
}
|
||||
|
||||
[HttpPost("matchmake/dorm")]
|
||||
public IActionResult MatchmakeDorm()
|
||||
{
|
||||
var player = AuthStuff.GetCurrentPlayer(Request);
|
||||
if (player == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(Sessions.CreateDorm((long)player.PlayerId, player.Player.Username));
|
||||
}
|
||||
|
||||
[HttpPost("matchmake/room/{roomId}")]
|
||||
public IActionResult MatchmakeRoomRoomId(long roomId)
|
||||
{
|
||||
var player = AuthStuff.GetCurrentPlayer(Request);
|
||||
if (player == null)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok(Sessions.CreateRoom((long)player.PlayerId, roomId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FemRec2023.Classes;
|
||||
using FemRec2023.Classes.DBs;
|
||||
using FemRec2023.Classes.DBs.DBClasses;
|
||||
using FemRec2023.Auth;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using static FemRec2023.Classes.DBs.DBClasses.PlayerDBClasses;
|
||||
|
||||
namespace FemRec2023.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("/noti")]
|
||||
public class NotiController : ControllerBase
|
||||
{
|
||||
public static ConcurrentDictionary<string, WebSocket> WebSockets { get; } = new();
|
||||
public static ConcurrentDictionary<long, HashSet<string>> PlayerConnections { get; } = new();
|
||||
|
||||
[HttpPost("hub/v1/negotiate")]
|
||||
public IActionResult Negotiate()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
string connectionId = Guid.NewGuid().ToString("N");
|
||||
|
||||
var response = new
|
||||
{
|
||||
negotiateVersion = 0,
|
||||
connectionId = connectionId,
|
||||
availableTransports = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
transport = "WebSockets",
|
||||
transferFormats = new[] { "Text", "Binary" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[Route("hub/v1")]
|
||||
public async Task HandleHub([FromQuery] string id)
|
||||
{
|
||||
var playerId = AuthStuff.GetPlayerId(Request);
|
||||
if (playerId == null)
|
||||
{
|
||||
Console.WriteLine($"[WebSocket] Player is unauthorized");
|
||||
HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
Console.WriteLine($"[WebSocket] IsWebSocketRequest is false");
|
||||
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
using var socket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
Console.WriteLine($"[WebSocket] Player {playerId} connected with connection {id}");
|
||||
|
||||
PlayerConnections.AddOrUpdate(
|
||||
(long)playerId,
|
||||
_ => new HashSet<string> { id },
|
||||
(_, hs) =>
|
||||
{
|
||||
lock (hs) { hs.Add(id); }
|
||||
return hs;
|
||||
}
|
||||
);
|
||||
|
||||
await HandleConnectionAsync((long)playerId, id, socket);
|
||||
}
|
||||
|
||||
private static async Task HandleConnectionAsync(long playerId, string connectionId, WebSocket socket)
|
||||
{
|
||||
using var pingCts = new CancellationTokenSource();
|
||||
|
||||
try
|
||||
{
|
||||
WebSockets[connectionId] = socket;
|
||||
|
||||
await SendHandshakeAsync(socket);
|
||||
|
||||
_ = Task.Run(() => PingLoopAsync(socket, pingCts.Token));
|
||||
|
||||
var buffer = new byte[4096];
|
||||
while (socket.State == WebSocketState.Open)
|
||||
{
|
||||
var result = await socket.ReceiveAsync(buffer, CancellationToken.None);
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
break;
|
||||
|
||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count).TrimEnd('\x1e');
|
||||
Console.WriteLine($"Player ({playerId}) sent: {message}");
|
||||
|
||||
await HandleClientMessageAsync(socket, message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Error:{connectionId}] {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
pingCts.Cancel();
|
||||
WebSockets.TryRemove(connectionId, out _);
|
||||
|
||||
if (PlayerConnections.TryGetValue(playerId, out var connections))
|
||||
{
|
||||
lock (connections)
|
||||
{
|
||||
connections.Remove(connectionId);
|
||||
|
||||
if (connections.Count == 0)
|
||||
PlayerConnections.TryRemove(playerId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
if (socket.State is WebSocketState.Open or WebSocketState.CloseReceived)
|
||||
{
|
||||
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing connection", CancellationToken.None);
|
||||
}
|
||||
|
||||
Console.WriteLine($"[WebSocket] Player {playerId} disconnected from connection {connectionId}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleClientMessageAsync(WebSocket socket, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(message);
|
||||
var root = doc.RootElement;
|
||||
|
||||
if (root.TryGetProperty("type", out var typeProp) && typeProp.GetInt32() == 1)
|
||||
{
|
||||
string target = root.GetProperty("target").GetString() ?? string.Empty;
|
||||
string invocationId = root.GetProperty("invocationId").GetString() ?? string.Empty;
|
||||
|
||||
if (target == "SubscribeToPlayers")
|
||||
{
|
||||
var response = new
|
||||
{
|
||||
type = 3,
|
||||
invocationId = invocationId,
|
||||
result = (object?)null
|
||||
};
|
||||
await SendJsonAsync(socket, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[ParseError] {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SendHandshakeAsync(WebSocket socket)
|
||||
{
|
||||
var handshake = new { protocol = "json", version = 1 };
|
||||
await SendJsonAsync(socket, handshake);
|
||||
}
|
||||
|
||||
private static async Task PingLoopAsync(WebSocket socket, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!token.IsCancellationRequested && socket.State == WebSocketState.Open)
|
||||
{
|
||||
var ping = new { type = 6 };
|
||||
await SendJsonAsync(socket, ping);
|
||||
await Task.Delay(10000, token);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException) { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[PingError] {ex.Message}");
|
||||
}
|
||||
}
|
||||
public static async Task SendNotificationToPlayer(long playerId, object message)
|
||||
{
|
||||
if (!PlayerConnections.TryGetValue(playerId, out var connections))
|
||||
return;
|
||||
|
||||
foreach (var connectionId in connections)
|
||||
{
|
||||
if (WebSockets.TryGetValue(connectionId, out var socket) && socket.State == WebSocketState.Open)
|
||||
{
|
||||
await SendJsonAsync(socket, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SendJsonAsync(WebSocket socket, object obj)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(obj) + "\x1e";
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
await socket.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FemRec2023.Classes;
|
||||
using FemRec2023.Classes.DBs;
|
||||
using FemRec2023.Classes.DBs.DBClasses;
|
||||
using FemRec2023.Auth;
|
||||
|
||||
namespace FemRec2023.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("/roomserver")]
|
||||
public class RoomController : ControllerBase
|
||||
{
|
||||
[HttpGet("rooms")]
|
||||
public async Task<IActionResult> GetRoomBy([FromQuery] string? name)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
var room = RoomDB.GetRoomByName(name);
|
||||
return room != null ? Ok(room) : NotFound();
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("rooms/{roomId}")]
|
||||
public async Task<IActionResult> GetRoomById(long roomId)
|
||||
{
|
||||
var room = RoomDB.GetRoom(roomId);
|
||||
return room != null ? Ok(room) : NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("rooms/bulk")]
|
||||
public async Task<IActionResult> GetRoomsByNames([FromQuery] List<string> name)
|
||||
{
|
||||
var rooms = RoomDB.GetRoomsByNames(name);
|
||||
return Ok(rooms);
|
||||
}
|
||||
|
||||
[HttpGet("photon_access_token")]
|
||||
public async Task<IActionResult> GetPhotonAccessToken()
|
||||
{
|
||||
var id = AuthStuff.GetPlayerId(Request);
|
||||
if (id == null)
|
||||
return Unauthorized();
|
||||
|
||||
var permissions = new List<object>
|
||||
{
|
||||
new { Override = true, Permission = "CAN_USE_ROOM_RESET_BUTTON", Role = 0, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_USE_DELETE_ALL_BUTTON", Role = 0, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_SAVE_INVENTIONS", Role = 0, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_SPAWN_INVENTIONS", Role = 0, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_USE_PLAY_GIZMOS_TOGGLE", Role = 0, Type = 0, Value = "True" },
|
||||
new { Override = false, Permission = "CAN_USE_MAKER_PEN", Role = 30, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_USE_ROOM_RESET_BUTTON", Role = 30, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_USE_DELETE_ALL_BUTTON", Role = 30, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_SAVE_INVENTIONS", Role = 30, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_SPAWN_INVENTIONS", Role = 30, Type = 0, Value = "True" },
|
||||
new { Override = true, Permission = "CAN_USE_PLAY_GIZMOS_TOGGLE", Role = 30, Type = 0, Value = "True" }
|
||||
};
|
||||
|
||||
var heartbeat = PlayerDB.GetPlayerHeartbeat((long)id);
|
||||
var response = new
|
||||
{
|
||||
Permissions = permissions.ToArray(),
|
||||
PhotonAccessToken = "",
|
||||
RoomInstanceId = heartbeat?.roomInstance?.roomInstanceId
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpGet("rooms/hot")]
|
||||
public IActionResult HotRooms(string tag, int skip = 0, int take = 30)
|
||||
{
|
||||
var (results, total) = RoomDB.GetHotRooms(tag, skip, take);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Results = results ?? new List<RoomDBClasses.Room>(),
|
||||
TotalResults = total
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("rooms/{roomId}/interactionby/me")]
|
||||
public IActionResult GetInteractionByMe(long roomId)
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
Cheered = false,
|
||||
Favorited = false,
|
||||
LastVisitedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user