mirror of
https://git.recroomarchive.org/RecRoomArchive/RRAC.git
synced 2026-09-08 22:51:26 -07:00
Initialize repository
Added basic info to get in game for like...August 2016 ;-; It's not much but it's a start
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
using RecRoomArchive.Models.API.Players;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace RecRoomArchive.Services
|
||||
{
|
||||
public class AccountService
|
||||
{
|
||||
public bool AccountExists()
|
||||
{
|
||||
return File.Exists("data/profile.json");
|
||||
}
|
||||
|
||||
public bool CreateAccount(string? username = null)
|
||||
{
|
||||
PopulateServerData();
|
||||
|
||||
if (string.IsNullOrEmpty(username))
|
||||
{
|
||||
username = GetRandomUsername();
|
||||
}
|
||||
|
||||
Console.WriteLine($"Creating account for {username}");
|
||||
|
||||
var profile = new BaseProfile
|
||||
{
|
||||
Id = (ulong)RandomNumberGenerator.GetInt32(1000, 9999999),
|
||||
Username = username,
|
||||
DisplayName = username
|
||||
};
|
||||
|
||||
File.WriteAllText("data/profile.json", JsonSerializer.Serialize(profile));
|
||||
return File.Exists("data/profile.json");
|
||||
}
|
||||
|
||||
public BaseProfile? GetSelfAccount()
|
||||
{
|
||||
return JsonSerializer.Deserialize<BaseProfile>(File.ReadAllText("data/profile.json"));
|
||||
}
|
||||
|
||||
private static string GetRandomUsername()
|
||||
{
|
||||
int randomFourDigits = RandomNumberGenerator.GetInt32(1000, 9999);
|
||||
return $"RRA-User_{randomFourDigits}";
|
||||
}
|
||||
|
||||
private static void PopulateServerData()
|
||||
{
|
||||
string basePath = "data";
|
||||
|
||||
string[] directories = ["rooms", "images", "blobs"];
|
||||
string[] files = [];
|
||||
|
||||
Directory.CreateDirectory(basePath);
|
||||
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(basePath, directory));
|
||||
}
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
string fullPath = Path.Combine(basePath, file);
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
File.WriteAllText(fullPath, string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
namespace RecRoomArchive.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to get the appVersion from the client to determine how to run the server
|
||||
/// </summary>
|
||||
public class AppVersionService
|
||||
{
|
||||
/// <summary>
|
||||
/// AppVersion reference
|
||||
/// </summary>
|
||||
private static string? AppVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The AppVersion as it would want to be seen by the game
|
||||
/// </summary>
|
||||
private static string? FullAppVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The BuildTimestamp of the build, this is a long that when converted to ticks, will be the accurate time at which the game was built.
|
||||
/// This only exists on some builds so I wouldn't rely on it too much
|
||||
/// </summary>
|
||||
private static DateTime? BuildTimestamp { get; set; }
|
||||
/// <summary>
|
||||
/// The BuildTimestamp as it would want to be seen by the game
|
||||
/// </summary>
|
||||
private static long? FullBuildTimestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// To store the AppVersion of the current build
|
||||
/// </summary>
|
||||
/// <param name="appVersion">The version of the game</param>
|
||||
/// <returns>If the operation was a success</returns>
|
||||
public async Task<bool> StoreAppVersion(string appVersion)
|
||||
{
|
||||
if (appVersion == null)
|
||||
return false;
|
||||
|
||||
// To remove any _EA's or .01's or just any weird Rec Room bullshit from the build's name
|
||||
var standardizedAppVersion = await ParseAppVersion(appVersion);
|
||||
|
||||
// idrk if storing both of these is overkill
|
||||
FullAppVersion = appVersion;
|
||||
AppVersion = standardizedAppVersion;
|
||||
|
||||
Console.WriteLine($"appVersion: {FullAppVersion}, standardizedAppVersion: {AppVersion}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To store the BuildTimestamp of the current build
|
||||
/// </summary>
|
||||
/// <param name="buildTimestamp">The BuildTimestamp of the game</param>
|
||||
/// <returns>If the operation was a success</returns>
|
||||
public async Task<bool> StoreBuildTimestamp(long buildTimestamp)
|
||||
{
|
||||
if (buildTimestamp == 0)
|
||||
return false;
|
||||
|
||||
DateTime buildTimestampDateTime = new(buildTimestamp);
|
||||
|
||||
// same here
|
||||
FullBuildTimestamp = buildTimestamp;
|
||||
BuildTimestamp = buildTimestampDateTime;
|
||||
|
||||
Console.WriteLine($"buildTimestamp: {FullBuildTimestamp}, buildTimestampDateTime: {BuildTimestamp}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the appVersion to "remove any _EA's or .01's or just any weird Rec Room bullshit from the build's name"
|
||||
/// </summary>
|
||||
/// <param name="appVersion">The version of the game</param>
|
||||
/// <returns>The standardized string of the appVersion</returns>
|
||||
public async Task<string> ParseAppVersion(string appVersion)
|
||||
{
|
||||
// To remove any _EA's or .01's or just any weird Rec Room bullshit from the build's name
|
||||
int index = appVersion.IndexOfAny(['_', '.']);
|
||||
string standardizedAppVersion = index >= 0 ? appVersion[..index] : appVersion;
|
||||
|
||||
return standardizedAppVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
|
||||
namespace RecRoomArchive.Services
|
||||
{
|
||||
public class AuthorizationService
|
||||
{
|
||||
private static readonly string Key = "hello diddy blud. you need to replace me. or not, really it doesnt matter...I forgot how long a token like this has to be so I'm just gonna run my mouth. Hi. This is RecRoomArchive. You may wonder why we have JWT tokens in a localhost server, and that's because 2020 requires it and stuff. and 2019. Especially 2021. I wonder if I will be doing 2021 or not...";
|
||||
|
||||
public string GenerateToken(ulong id)
|
||||
{
|
||||
JwtSecurityTokenHandler handler = new();
|
||||
|
||||
List<Claim> claims = new List<Claim>()
|
||||
{
|
||||
new(ClaimTypes.NameIdentifier, id.ToString()),
|
||||
new(ClaimTypes.Role, "gameClient")
|
||||
};
|
||||
|
||||
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor()
|
||||
{
|
||||
Subject = new ClaimsIdentity(claims),
|
||||
Expires = DateTime.UtcNow.Add(TimeSpan.FromHours(12)),
|
||||
Issuer = "https://recroomarchive.org/",
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Key)), SecurityAlgorithms.HmacSha256)
|
||||
};
|
||||
|
||||
JwtSecurityToken token = handler.CreateJwtSecurityToken(tokenDescriptor);
|
||||
return handler.WriteToken(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
using RecRoomArchive.Models.API.Config;
|
||||
|
||||
namespace RecRoomArchive.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// I don't even want to explain this one. I'm sorry
|
||||
/// </summary>
|
||||
public class ConfigService(MessageOfTheDayService motdService, IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
private readonly MessageOfTheDayService _motdService = motdService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
|
||||
/// <summary>
|
||||
/// I'm only doing this because I think that having the full Rec Room config in a controller would be very very ugly.
|
||||
/// </summary>
|
||||
/// <returns>RecRoomConfig</returns>
|
||||
public async Task<RecRoomConfig> GetRecRoomConfig()
|
||||
{
|
||||
var request = _httpContextAccessor.HttpContext?.Request;
|
||||
var host = $"{request!.Scheme}://{request.Host}";
|
||||
|
||||
return new RecRoomConfig()
|
||||
{
|
||||
MessageOfTheDay = await _motdService.GetMessageOfTheDay(),
|
||||
CdnBaseUri = host,
|
||||
MatchmakingParams = new MatchmakingParams(),
|
||||
LevelProgressionMaps =
|
||||
[
|
||||
new LevelProgressionMap(0, 0),
|
||||
new LevelProgressionMap(1, 10),
|
||||
new LevelProgressionMap(2, 10),
|
||||
new LevelProgressionMap(3, 10),
|
||||
new LevelProgressionMap(4, 20),
|
||||
new LevelProgressionMap(5, 20),
|
||||
new LevelProgressionMap(6, 20),
|
||||
new LevelProgressionMap(7, 20),
|
||||
new LevelProgressionMap(8, 20),
|
||||
new LevelProgressionMap(9, 20),
|
||||
new LevelProgressionMap(10, 20),
|
||||
new LevelProgressionMap(11, 45),
|
||||
new LevelProgressionMap(12, 45),
|
||||
new LevelProgressionMap(13, 45),
|
||||
new LevelProgressionMap(14, 45),
|
||||
new LevelProgressionMap(15, 45),
|
||||
new LevelProgressionMap(16, 45),
|
||||
new LevelProgressionMap(17, 45),
|
||||
new LevelProgressionMap(18, 45),
|
||||
new LevelProgressionMap(19, 45),
|
||||
new LevelProgressionMap(20, 45),
|
||||
new LevelProgressionMap(21, 115),
|
||||
new LevelProgressionMap(22, 115),
|
||||
new LevelProgressionMap(23, 115),
|
||||
new LevelProgressionMap(24, 115),
|
||||
new LevelProgressionMap(25, 115),
|
||||
new LevelProgressionMap(26, 115),
|
||||
new LevelProgressionMap(27, 115),
|
||||
new LevelProgressionMap(28, 115),
|
||||
new LevelProgressionMap(29, 115),
|
||||
new LevelProgressionMap(30, 115)
|
||||
],
|
||||
DailyObjectives =
|
||||
[
|
||||
[
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.QuestGames_Scifi1,
|
||||
Score = 1
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.QuestEnemyKills_Scifi1,
|
||||
Score = 10
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.ArenaGames,
|
||||
Score = 1
|
||||
}
|
||||
],
|
||||
[
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.PaintballCTFGames,
|
||||
Score = 2
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.FinishActivity,
|
||||
Score = 1
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.CheerAPlayer,
|
||||
Score = 1
|
||||
}
|
||||
],
|
||||
[
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.DodgeballGames,
|
||||
Score = 2
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.DodgeballWins,
|
||||
Score = 2
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.CheerAPlayer,
|
||||
Score = 1
|
||||
}
|
||||
],
|
||||
[
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.PaintballTeamBattleWins,
|
||||
Score = 2
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.PaintballTeamBattleGames,
|
||||
Score = 20
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.FinishActivity,
|
||||
Score = 1
|
||||
}
|
||||
],
|
||||
[
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.QuestGames_Goblin1,
|
||||
Score = 1
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.QuestEnemyKills_Goblin1,
|
||||
Score = 10
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.CheerAPlayer,
|
||||
Score = 1
|
||||
}
|
||||
],
|
||||
[
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.PaintballAnyModeGames,
|
||||
Score = 2
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.PaintballAnyModeHits,
|
||||
Score = 20
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.ArenaGames,
|
||||
Score = 1
|
||||
}
|
||||
],
|
||||
[
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.QuestGames_Goblin2,
|
||||
Score = 1
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.QuestEnemyKills_Goblin2,
|
||||
Score = 10
|
||||
},
|
||||
new DailyObjective()
|
||||
{
|
||||
Type = ObjectiveType.FinishActivity,
|
||||
Score = 1
|
||||
}
|
||||
]
|
||||
],
|
||||
ConfigTable = [],
|
||||
PhotonConfig = new PhotonConfig()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace RecRoomArchive.Services
|
||||
{
|
||||
public class FileService
|
||||
{
|
||||
private static void PopulateServerData()
|
||||
{
|
||||
string basePath = "data";
|
||||
|
||||
string[] directories = ["rooms", "images", "blobs"];
|
||||
string[] files = ["rooms.json", "avatar.json", "settings.json", "profile.json"];
|
||||
|
||||
Directory.CreateDirectory(basePath);
|
||||
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(basePath, directory));
|
||||
}
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
string fullPath = Path.Combine(basePath, file);
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
File.WriteAllText(fullPath, string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
public string? GetData(string name)
|
||||
{
|
||||
var path = $"data/{name}";
|
||||
|
||||
if (!File.Exists(path))
|
||||
return null;
|
||||
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
public void SetData(string name, string data)
|
||||
{
|
||||
var path = $"data/{name}";
|
||||
|
||||
File.WriteAllText(path, data);
|
||||
}
|
||||
|
||||
public bool FileExists(string name)
|
||||
{
|
||||
var path = $"data/{name}";
|
||||
|
||||
if (!File.Exists(path))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace RecRoomArchive.Services
|
||||
{
|
||||
public class ImageService(FileService fileService)
|
||||
{
|
||||
private static readonly HttpClient httpClient = new();
|
||||
|
||||
private readonly FileService _fileService = fileService;
|
||||
|
||||
public async Task<Stream?> GetImage(string imageName)
|
||||
{
|
||||
var path = $"images/{imageName}";
|
||||
|
||||
if (!_fileService.FileExists(path))
|
||||
{
|
||||
var response = await httpClient.GetAsync($"https://cdn.rec.net/img/{imageName}");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return null;
|
||||
|
||||
return await response.Content.ReadAsStreamAsync();
|
||||
}
|
||||
|
||||
return new FileStream(Path.Combine("data", path), FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
|
||||
public async Task<string> SaveImageAsync(Stream imageStream)
|
||||
{
|
||||
var imageName = $"{GetRandomFileName()}.jpg";
|
||||
|
||||
var path = ("data/images");
|
||||
var fullPath = Path.Combine(path, imageName);
|
||||
|
||||
await using var fileStream = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 81920, useAsync: true);
|
||||
|
||||
await imageStream.CopyToAsync(fileStream);
|
||||
|
||||
return imageName;
|
||||
}
|
||||
|
||||
private static string GetRandomFileName()
|
||||
{
|
||||
return Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('+', '-').Replace('/', '_').TrimEnd('=');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using RecRoomArchive.Models.API.Activities;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace RecRoomArchive.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service used to get the message of the day as it is used in multiple areas of the server
|
||||
/// </summary>
|
||||
public class MessageOfTheDayService
|
||||
{
|
||||
/// <summary>
|
||||
/// HttpClient for making requests to Gitea
|
||||
/// </summary>
|
||||
private static readonly HttpClient httpClient = new HttpClient();
|
||||
|
||||
/// <summary>
|
||||
/// MessageOfTheDay reference
|
||||
/// </summary>
|
||||
private static string? MessageOfTheDay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message of the day from Gitea. If the URL cannot be resolved, it will fall back to "Welcome to RecRoomArchive!"
|
||||
/// </summary>
|
||||
/// <returns>String related to the Message of the Day</returns>
|
||||
public async Task<string> GetMessageOfTheDay(string? version = null)
|
||||
{
|
||||
// I wouldn't want to re-request the MOTD from the server a bunch of times...
|
||||
if (string.IsNullOrEmpty(MessageOfTheDay))
|
||||
{
|
||||
var motd = await httpClient.GetAsync($"https://git.recroomarchive.org/RecRoomArchive/RRAC/raw/branch/main/MOTD");
|
||||
if (!motd.IsSuccessStatusCode)
|
||||
return "Welcome to RecRoomArchive!";
|
||||
|
||||
MessageOfTheDay = await motd.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
return MessageOfTheDay;
|
||||
}
|
||||
|
||||
// move out of motdservice
|
||||
private DateTime CharadesWordsLastFetchedAt { get; set; }
|
||||
private List<CharadesWord> CachedCharadesWords { get; set; } = [];
|
||||
|
||||
public async Task <List<CharadesWord>> GetCharadesWordsList()
|
||||
{
|
||||
if (CharadesWordsLastFetchedAt - DateTime.UtcNow > TimeSpan.FromMinutes(30))
|
||||
return CachedCharadesWords;
|
||||
|
||||
var request = await httpClient.GetAsync("https://git.recroomarchive.org/RecRoomArchive/RRAC/raw/branch/main/CharadesWords");
|
||||
if (!request.IsSuccessStatusCode)
|
||||
return [];
|
||||
|
||||
var words = await request.Content.ReadAsStringAsync();
|
||||
if (words == null)
|
||||
return [];
|
||||
|
||||
CachedCharadesWords = JsonSerializer.Deserialize<List<CharadesWord>>(words)!;
|
||||
|
||||
return CachedCharadesWords;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user