mirror of
https://github.com/myawired/2025-RecRoom-Server-E12354.git
synced 2026-07-17 16:11:15 +10:00
Initial upload
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
using System.Security.Principal;
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
using Rec_rewild_live_rewrite.api.server;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
using RewildLiveSource.Controllers;
|
||||
using static Rec_rewild_live_rewrite.api.server.Moderation_Detail;
|
||||
|
||||
namespace RewildLiveSource.api.admin
|
||||
{
|
||||
internal static class DevChatCommands
|
||||
{
|
||||
public static string? ParseCommand(string msg, string context, ulong playerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
return null;
|
||||
|
||||
if (!msg.StartsWith("!"))
|
||||
return null;
|
||||
|
||||
if (!PlayerDB.CheckDevFlag(playerId))
|
||||
return "You are not allowed to use dev commands.";
|
||||
|
||||
string[] parts = msg.Trim().Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
string command = parts[0].ToLowerInvariant();
|
||||
string args = parts.Length > 1 ? parts[1] : "";
|
||||
|
||||
return command switch
|
||||
{
|
||||
"!bring_all" => HandleBringAll(args),
|
||||
"!goto" => HandleGoto(args, playerId),
|
||||
"!help" => HandleHelp(),
|
||||
"!add_room_role" => HandleAddRoomRole(args, playerId),
|
||||
"!remove_room_role" => HandleRemoveRoomRole(args, playerId),
|
||||
"!ban" => HandleBan(args, playerId),
|
||||
"!kick" => HandleKick(args),
|
||||
_ => "Unknown command. Use !help"
|
||||
};
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// BRING ALL
|
||||
// ------------------------------------------------------------
|
||||
private static string HandleBringAll(string msg)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
return "Usage: !bring_all Room.Subroom [private]";
|
||||
|
||||
bool isPrivate = false;
|
||||
msg = msg.Trim();
|
||||
|
||||
if (msg.EndsWith(" private", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
isPrivate = true;
|
||||
msg = msg[..^8].Trim();
|
||||
}
|
||||
|
||||
string room;
|
||||
string subroom = "";
|
||||
|
||||
var split = msg.Split('.', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
room = split[0];
|
||||
if (split.Length == 2)
|
||||
subroom = split[1];
|
||||
|
||||
var session = Sessions.CreateRoom(room, subroom, isPrivate);
|
||||
|
||||
foreach (var (playerId, _) in NotificationsController.WebSockets.ToArray())
|
||||
{
|
||||
try
|
||||
{
|
||||
HeartbeatDB.UpdatePlayerHeartbeat(playerId, session);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to move player {playerId}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ❗ Fallback ONLY if you don't have online tracking
|
||||
// for (ulong id = 1; id <= PlayerDB.GetMaxPlayerId(); id++)
|
||||
// HeartbeatDB.UpdatePlayerHeartbeat(id, session);
|
||||
|
||||
return $"Sent all players to {(isPrivate ? "PRIVATE " : "")}{room}" +
|
||||
(string.IsNullOrEmpty(subroom) ? "" : $".{subroom}");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// GOTO
|
||||
// ------------------------------------------------------------
|
||||
private static string HandleGoto(string arg, ulong playerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
return "Usage: !goto Room.Subroom";
|
||||
|
||||
string room;
|
||||
string subroom = "";
|
||||
|
||||
var split = arg.Split('.', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
room = split[0];
|
||||
if (split.Length == 2)
|
||||
subroom = split[1];
|
||||
|
||||
var session = Sessions.CreateRoom(room, subroom);
|
||||
HeartbeatDB.UpdatePlayerHeartbeat(playerId, session);
|
||||
|
||||
return string.IsNullOrEmpty(subroom)
|
||||
? $"Teleporting you to {room}"
|
||||
: $"Teleporting you to {room}.{subroom}";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// HELP
|
||||
// ------------------------------------------------------------
|
||||
private static string HandleHelp()
|
||||
{
|
||||
return @"
|
||||
!bring_all <Room.Subroom> [private]
|
||||
Sends all players to the specified room.
|
||||
Example: !bring_all RecCenter.Home private
|
||||
|
||||
!goto <Room.Subroom>
|
||||
Teleports you to a room.
|
||||
Example: !goto RecCenter.Home
|
||||
|
||||
!add_room_role <PlayerName|me>, <RoomName>, <Role>
|
||||
Roles: None, Banned, Host, Moderator, CoOwner, TemporaryCoOwner, Creator
|
||||
|
||||
!remove_room_role <PlayerName|me>, <RoomName>
|
||||
Removes ALL roles for a player in a room.
|
||||
|
||||
!ban <PlayerName>, <DurationSeconds>, <Category>, [Message]
|
||||
Example: !ban ThriftyEmu, 3600, Cheating, Please follow the rules
|
||||
".Trim();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// ADD ROOM ROLE
|
||||
// ------------------------------------------------------------
|
||||
private static string HandleAddRoomRole(string args, ulong callerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(args))
|
||||
return "Usage: !add_room_role PlayerName, RoomName, RoleName";
|
||||
|
||||
var split = args.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
ulong targetId;
|
||||
string roomName;
|
||||
string roleName;
|
||||
|
||||
if (split.Length == 2)
|
||||
{
|
||||
targetId = callerId;
|
||||
roomName = split[0].Trim();
|
||||
roleName = split[1].Trim();
|
||||
}
|
||||
else if (split.Length == 3)
|
||||
{
|
||||
string playerName = split[0].Trim();
|
||||
roomName = split[1].Trim();
|
||||
roleName = split[2].Trim();
|
||||
|
||||
targetId = playerName.Equals("me", StringComparison.OrdinalIgnoreCase)
|
||||
? callerId
|
||||
: PlayerDB.GetPlayerIdByName(playerName);
|
||||
|
||||
if (targetId == 0)
|
||||
return $"Player '{playerName}' not found.";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Invalid format. Use: !add_room_role PlayerName, RoomName, RoleName";
|
||||
}
|
||||
|
||||
if (!Enum.TryParse<RoomDBClasses.Role_data>(roleName, true, out var role))
|
||||
return $"Invalid role '{roleName}'.";
|
||||
|
||||
bool result = RoomDB.AddRoleToRoom(roomName, (int)targetId, role);
|
||||
|
||||
if (!result)
|
||||
return $"Room '{roomName}' not found.";
|
||||
|
||||
if (role == RoomDBClasses.Role_data.Creator)
|
||||
RoomDB.SetRoomCreatorId(targetId, roomName);
|
||||
|
||||
return $"Added role {role} to room {roomName} for player ID {targetId}.";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// REMOVE ROOM ROLE
|
||||
// ------------------------------------------------------------
|
||||
private static string HandleRemoveRoomRole(string args, ulong callerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(args))
|
||||
return "Usage: !remove_room_role PlayerName, RoomName";
|
||||
|
||||
var split = args.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (split.Length != 2)
|
||||
return "Invalid format. Use: !remove_room_role PlayerName, RoomName";
|
||||
|
||||
string playerName = split[0].Trim();
|
||||
string roomName = split[1].Trim();
|
||||
|
||||
ulong targetId = playerName.Equals("me", StringComparison.OrdinalIgnoreCase)
|
||||
? callerId
|
||||
: PlayerDB.GetPlayerIdByName(playerName);
|
||||
|
||||
if (targetId == 0)
|
||||
return $"Player '{playerName}' not found.";
|
||||
|
||||
bool result = RoomDB.RemoveRoleFromRoom(roomName, (int)targetId);
|
||||
|
||||
return result
|
||||
? $"Removed all roles from room '{roomName}' for player '{playerName}'."
|
||||
: $"Room '{roomName}' not found or no roles assigned.";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// BAN
|
||||
// ------------------------------------------------------------
|
||||
private static string HandleBan(string args, ulong devid)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(args))
|
||||
return "Usage: !ban PlayerName, DurationSeconds, Category, Message";
|
||||
|
||||
var split = args.Split(',', 4, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (split.Length < 3)
|
||||
return "Invalid format. Use: !ban PlayerName, DurationSeconds, Category, Message";
|
||||
|
||||
string playerName = split[0].Trim();
|
||||
string durationStr = split[1].Trim();
|
||||
string categoryStr = split[2].Trim();
|
||||
string message = split.Length == 4 ? split[3].Trim() : "";
|
||||
|
||||
ulong playerId = PlayerDB.GetPlayerIdByName(playerName);
|
||||
if (playerId == 0)
|
||||
return $"Player '{playerName}' not found.";
|
||||
|
||||
if (!int.TryParse(durationStr, out int duration) || duration < 0)
|
||||
return "Invalid duration. Must be a non-negative number.";
|
||||
|
||||
if (!Enum.TryParse<Moderation_Detail.ReportCategory>(categoryStr, true, out var category))
|
||||
category = Moderation_Detail.ReportCategory.Moderator;
|
||||
|
||||
PlayerDB.Set_moderation_block(
|
||||
playerId,
|
||||
category,
|
||||
duration,
|
||||
gameSessionId: -1,
|
||||
isHostKick: false,
|
||||
message: message,
|
||||
playerIdReporter: null,
|
||||
isBan: true,
|
||||
bannedByPlayerId: devid
|
||||
);
|
||||
|
||||
return $"Player '{playerName}' banned for {duration} seconds ({category}).";
|
||||
}
|
||||
|
||||
private static string HandleKick(string args)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(args))
|
||||
return "Usage: !kick PlayerName";
|
||||
|
||||
var split = args.Split(',', 4, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (split.Length < 3)
|
||||
return "Invalid format. Use: !kick PlayerName";
|
||||
|
||||
string playerName = split[0].Trim();
|
||||
|
||||
ulong playerId = PlayerDB.GetPlayerIdByName(playerName);
|
||||
if (playerId == 0)
|
||||
return $"Player '{playerName}' not found.";
|
||||
|
||||
PlayerDB.Set_moderation_block(
|
||||
playerId,
|
||||
ReportCategory.Moderator,
|
||||
duration: 1,
|
||||
gameSessionId: -1,
|
||||
isHostKick: true,
|
||||
message: "You have been kicked.",
|
||||
playerIdReporter: null,
|
||||
isBan: false,
|
||||
bannedByPlayerId: 0
|
||||
);
|
||||
|
||||
return $"Player '{playerName}' has been kicked to their Dorm Room.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
using Rec_rewild_live_rewrite.api.server;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.DiscordBot
|
||||
{
|
||||
public class BotCommands : InteractionModuleBase<SocketInteractionContext>
|
||||
{
|
||||
|
||||
[SlashCommand("get-user", "Get Rec Rewild user by ID or name.")]
|
||||
public async Task GetUserByIdOrName(string userInput)
|
||||
{
|
||||
try
|
||||
{
|
||||
player_data? player;
|
||||
|
||||
if (ulong.TryParse(userInput, out ulong playerId))
|
||||
{
|
||||
player = PlayerDB.GetFullAccount(playerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
player = PlayerDB.GetFullAccountByName(userInput);
|
||||
}
|
||||
|
||||
if (player == null)
|
||||
{
|
||||
await RespondAsync("No user found with the provided ID or name.", ephemeral: true);
|
||||
return;
|
||||
}
|
||||
|
||||
var bio = $"{player.player_Extra.Bio}" ?? "This player hasn't written a bio yet!";
|
||||
if (string.IsNullOrEmpty(bio))
|
||||
{
|
||||
bio = "This player hasn't written a bio yet!";
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle($"{player.player.Username}")
|
||||
.WithDescription(
|
||||
$"Username: **@{player.player.Username}**\n" +
|
||||
$"Display Name: **{player.player.DisplayName}**\n" +
|
||||
$"Level: **{player.player.Level}**\n" +
|
||||
$"Bio: ```{bio}```\n" +
|
||||
$"This account is linked to the Discord User: <@{player.player_Extra.DiscordId}>")
|
||||
|
||||
.WithColor(Color.DarkGreen)
|
||||
//.AddField("test", player.playerid.ToString(), true)
|
||||
//.WithImageUrl(player.profile_image_url ?? "https://default-image-url.com")
|
||||
.WithThumbnailUrl($"https://recrewild.oldrec.net/img/{player.player.profileImage}?cropSquare=true")
|
||||
.Build();
|
||||
|
||||
await RespondAsync(embed: embed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await RespondAsync($"Uh Oh Something Went Wrong! Please try again.", ephemeral: true);
|
||||
}
|
||||
}
|
||||
|
||||
[SlashCommand("leaderboard", "Display the leaderboard for Rec Rewild rooms.")]
|
||||
public async Task ShowLeaderboardAsync(string roomname, int statchannel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var room = RoomDB.GetRoomByName(roomname);
|
||||
if (room == null)
|
||||
{
|
||||
await RespondAsync("Room not found!", ephemeral: true);
|
||||
return;
|
||||
}
|
||||
|
||||
var leaderboard = LeaderboardDB.GetNearbyScores(1, statchannel, room.RoomId, 0);
|
||||
if (leaderboard == null || leaderboard.Count == 0)
|
||||
{
|
||||
await RespondAsync("There is no leaderboard or there are no scores for this room!", ephemeral: true);
|
||||
return;
|
||||
}
|
||||
|
||||
var embedBuilder = new EmbedBuilder()
|
||||
.WithTitle($"Rec Rewild - Top Players in ^{roomname} Leaderboard")
|
||||
.WithDescription("Here are the top players.")
|
||||
.WithColor(Color.DarkGreen)
|
||||
.WithFooter("Keep playing to climb the ranks!");
|
||||
|
||||
int rank = 1;
|
||||
foreach (var score in leaderboard)
|
||||
{
|
||||
var player = PlayerDB.GetFullAccount((ulong)score.PlayerId);
|
||||
if (player == null) continue;
|
||||
|
||||
string rankSuffix = rank switch
|
||||
{
|
||||
1 => "st",
|
||||
2 => "nd",
|
||||
3 => "rd",
|
||||
_ => "th"
|
||||
};
|
||||
|
||||
embedBuilder.AddField(
|
||||
$"{rank}{rankSuffix} - @{player.player.Username} (Score: {score.Score})",
|
||||
true
|
||||
);
|
||||
|
||||
rank++;
|
||||
}
|
||||
|
||||
var embed = embedBuilder.Build();
|
||||
await RespondAsync(embed: embed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await RespondAsync($"Uh Oh Something Went Wrong! Please try again.", ephemeral: true);
|
||||
}
|
||||
}
|
||||
|
||||
[SlashCommand("create", "Create your Rec Rewild account.")]
|
||||
public async Task CreateAccount(string platformid)
|
||||
{
|
||||
string CreateErrorMessage =
|
||||
"Invalid platform ID provided. Please use [Steam ID](https://media.discordapp.net/attachments/1449894388464615647/1472652698808488007/SteamIDTutorial.png?ex=69935a33&is=699208b3&hm=ed4b9b9810afb627a3279655c4ea712ad19d95d01bbb4adbe0cf5111833c19ce&)";
|
||||
|
||||
ulong allowedChannelId = 1449894388464615647; // command allowed channel
|
||||
ulong logChannelId = 1473888914841407488; // staff log channel
|
||||
|
||||
// Local logging function inside this method
|
||||
async Task Log(string message)
|
||||
{
|
||||
var logChannel = Context.Client.GetChannel(logChannelId) as IMessageChannel;
|
||||
if (logChannel != null)
|
||||
await logChannel.SendMessageAsync(message);
|
||||
}
|
||||
|
||||
// Check allowed channel
|
||||
if (Context.Channel.Id != allowedChannelId)
|
||||
{
|
||||
await Log($"⚠️ **Wrong Channel Attempt**\nUser: <@{Context.User.Id}> (`{Context.User.Id}`)\nChannel: <#{Context.Channel.Id}>\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
await RespondAsync($"❌ You can only use this command in <#{allowedChannelId}>.", ephemeral: true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate platform ID format
|
||||
if (!ulong.TryParse(platformid, out ulong platformId))
|
||||
{
|
||||
await Log($"❌ **Invalid Platform ID Format**\nUser: <@{Context.User.Id}> (`{Context.User.Id}`)\nInput: `{platformid}`\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
await RespondAsync(CreateErrorMessage, ephemeral: false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!platformid.StartsWith("7656"))
|
||||
{
|
||||
await Log($"❌ **Invalid Steam ID Prefix**\nUser: <@{Context.User.Id}> (`{Context.User.Id}`)\nInput: `{platformid}`\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
await RespondAsync(CreateErrorMessage, ephemeral: false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Context == null || Context.User == null)
|
||||
{
|
||||
throw new Exception("Context or Context.User is null");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var guildUser = Context.User as IGuildUser;
|
||||
ulong DiscordUserId = guildUser?.Id ?? Context.User.Id;
|
||||
Console.WriteLine($"Creating account for Discord User ID: {DiscordUserId} with Platform ID: {platformId}");
|
||||
|
||||
HashSet<ulong> blockedDiscordIds = new()
|
||||
{
|
||||
1176571557414445137,
|
||||
1193883666946998383,
|
||||
1344328341951615106
|
||||
};
|
||||
|
||||
HashSet<ulong> blockedPlatformIds = new()
|
||||
{
|
||||
76561199485696854
|
||||
};
|
||||
|
||||
// Duplicate checks
|
||||
if (PlayerDB.GetAccountCountByPlatformId(platformId) > 0)
|
||||
{
|
||||
await Log($"⚠️ **Duplicate Platform Attempt**\nUser: <@{DiscordUserId}> (`{DiscordUserId}`)\nPlatform ID: `{platformId}`\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
await RespondAsync("You already have an account!", ephemeral: false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlayerDB.GetAccountCountByDiscordId(DiscordUserId) > 0)
|
||||
{
|
||||
await Log($"⚠️ **Duplicate Discord Attempt**\nUser: <@{DiscordUserId}> (`{DiscordUserId}`)\nPlatform ID: `{platformId}`\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
await RespondAsync("You already have an account!", ephemeral: false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Blocked users
|
||||
if (blockedDiscordIds.Contains(DiscordUserId) || blockedPlatformIds.Contains(platformId))
|
||||
{
|
||||
await Log($"🚫 **Blocked Account Creation Attempt**\nUser: <@{DiscordUserId}> (`{DiscordUserId}`)\nPlatform ID: `{platformId}`\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
await RespondAsync("You require manual verification. Contact @e12354 on Discord.", ephemeral: false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create account
|
||||
account_data acc;
|
||||
PlayerDB.CreateAccount(Platforms.Steam, platformId, Utils.GetRandomString(30), DiscordUserId, out acc);
|
||||
var random_pass = Utils.GetRandomString(30);
|
||||
PlayerDB.SetPlayerPassword(random_pass, acc.accountId, log: false);
|
||||
|
||||
string username = guildUser?.Nickname
|
||||
?? Context.User.GlobalName
|
||||
?? Context.User.Username
|
||||
?? Utils.GetRandomName();
|
||||
string sanitizedUsername = Regex.Replace(username, @"[^a-zA-Z0-9]", "");
|
||||
if (string.IsNullOrWhiteSpace(sanitizedUsername))
|
||||
sanitizedUsername = Utils.GetRandomName();
|
||||
|
||||
bool success = PlayerDB.SetPlayerUsername(sanitizedUsername, acc.accountId);
|
||||
PlayerDB.SetDisplayName(sanitizedUsername, acc.accountId);
|
||||
if (!success)
|
||||
{
|
||||
sanitizedUsername += new Random().Next(100000, 999999).ToString();
|
||||
PlayerDB.SetPlayerUsername(sanitizedUsername, acc.accountId);
|
||||
PlayerDB.SetDisplayName(sanitizedUsername, acc.accountId);
|
||||
}
|
||||
|
||||
var account = PlayerDB.GetFullAccount(acc.accountId);
|
||||
|
||||
// Log successful creation
|
||||
await Log($"🆕 **Account Created Successfully**\nUser: <@{DiscordUserId}> (`{DiscordUserId}`)\nPlatform ID: `{platformId}`\nAccount ID: `{acc.accountId}`\nUsername: `{account.player.Username}`\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
|
||||
// DM user
|
||||
try
|
||||
{
|
||||
await Context.User.SendMessageAsync(
|
||||
$"✅ **Rec Rewild account created!**\n\n" +
|
||||
$"**Username:** `{account.player.Username}`\n" +
|
||||
$"**Password:** `{random_pass}`\n\n" +
|
||||
$"⚠️ Keep this password secret. Sharing this password will result in a ban."
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Log($"⚠️ **DM Failed After Account Creation**\nUser: <@{DiscordUserId}> (`{DiscordUserId}`)\nAccount ID: `{acc.accountId}`\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
await RespondAsync("Account created, but I couldn't DM you. Please enable DMs from this server. After you do launch the build.", ephemeral: false);
|
||||
return;
|
||||
}
|
||||
|
||||
await RespondAsync("📩 Check your DMs!", ephemeral: false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Log($"💥 **Account Creation Exception**\nUser: <@{Context.User.Id}> (`{Context.User.Id}`)\nError: `{ex.Message}`\nTime: <t:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}:F>");
|
||||
Console.WriteLine(ex);
|
||||
await RespondAsync("Uh oh! Something went wrong. Please try again.", ephemeral: false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Discord.Interactions;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class DiscordBot
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly InteractionService _commands;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private static string BOT_TOKEN = "REPLACEME";
|
||||
|
||||
public DiscordBot(IServiceProvider services)
|
||||
{
|
||||
_client = new DiscordSocketClient();
|
||||
_commands = new InteractionService(_client.Rest);
|
||||
_serviceProvider = services;
|
||||
}
|
||||
|
||||
public async Task RunAsync()
|
||||
{
|
||||
_client.Log += Log;
|
||||
_client.Ready += Ready;
|
||||
_client.InteractionCreated += HandleInteraction;
|
||||
|
||||
await _client.LoginAsync(TokenType.Bot, BOT_TOKEN);
|
||||
await _client.StartAsync();
|
||||
|
||||
await Task.Delay(-1); // Keeps the bot running
|
||||
}
|
||||
|
||||
private async Task Ready()
|
||||
{
|
||||
Console.WriteLine("Bot is connected and ready!");
|
||||
|
||||
// Now that the bot is ready, register commands
|
||||
await RegisterCommandsAsync();
|
||||
}
|
||||
|
||||
private async Task RegisterCommandsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine("Starting to register commands...");
|
||||
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
|
||||
Console.WriteLine("Modules loaded for command registration.");
|
||||
|
||||
// Register commands for a specific guild (faster propagation)
|
||||
await _commands.RegisterCommandsToGuildAsync(1253525152503955497);
|
||||
Console.WriteLine("Commands registered to guild successfully.");
|
||||
|
||||
// Optionally, register commands globally (can take time to propagate)
|
||||
//await _commands.RegisterCommandsGloballyAsync();
|
||||
//Console.WriteLine("Global commands registered successfully.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Task Log(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task HandleInteraction(SocketInteraction interaction)
|
||||
{
|
||||
// Handle the interaction using the InteractionService
|
||||
var context = new SocketInteractionContext(_client, interaction);
|
||||
await _commands.ExecuteCommandAsync(context, _serviceProvider);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
using System.Security.Principal;
|
||||
using static Rec_rewild_live_rewrite.api.WebsocketEvents;
|
||||
using Rec_rewild_live_rewrite.api.server;
|
||||
using Microsoft.IdentityModel.Tokens.Experimental;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api
|
||||
{
|
||||
public class WebsocketEvents
|
||||
{
|
||||
public static Reponse create_player_updated_account2_Response(string id, account_data_me account)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = id,
|
||||
Msg = account
|
||||
};
|
||||
}
|
||||
|
||||
public static Reponse create_sub(string id, ulong playerid, int membershipType)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = id,
|
||||
Msg = new Sub
|
||||
{
|
||||
creatorAccountId = playerid,
|
||||
clubId = 1,
|
||||
membershipType = membershipType,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Reponse createResponse_levelupdate(ulong playerid, int level, int xp)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = "PlayerProgressionLevelUpdate",
|
||||
Msg = new
|
||||
{
|
||||
PlayerId = playerid,
|
||||
Level = level,
|
||||
XP = xp,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Reponse CreateResponseReputationUpdate(ulong playerId)
|
||||
{
|
||||
var acc = PlayerDB.GetFullAccount(playerId);
|
||||
return new Reponse
|
||||
{
|
||||
Id = "ReputationUpdate",
|
||||
Msg = new
|
||||
{
|
||||
AccountId = playerId,
|
||||
IsCheerful = acc.player.PlayerReputation.IsCheerful,
|
||||
Noteriety = acc.player.PlayerReputation.Noteriety,
|
||||
SelectedCheer = acc.player.PlayerReputation.SelectedCheer,
|
||||
CheerCredit = acc.player.PlayerReputation.CheerCredit,
|
||||
CheerGeneral = acc.player.PlayerReputation.CheerGeneral,
|
||||
CheerHelpful = acc.player.PlayerReputation.CheerHelpful,
|
||||
CheerCreative = acc.player.PlayerReputation.CheerCreative,
|
||||
CheerGreatHost = acc.player.PlayerReputation.CheerGreatHost,
|
||||
CheerSportsman = acc.player.PlayerReputation.CheerSportsman,
|
||||
SubscriberCount = acc.player_Extra.sub_count,
|
||||
SubscribedCount = 0,
|
||||
RecRoomDeveloper = PlayerDB.CheckDevFlag(acc.player.Id)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Reponse CreateMessageResponse(MessageData messageData)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = "2",
|
||||
Msg = messageData
|
||||
};
|
||||
}
|
||||
|
||||
public static Reponse CreateLogout(ulong id)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = "6", // 6 = Logout
|
||||
Msg = new
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Reponse CreateBan(Moderation_Detail.ModerationBlockDetails block)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = "22",
|
||||
Msg = block
|
||||
};
|
||||
}
|
||||
|
||||
public class Reponse
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
|
||||
public object? Msg { get; set; }
|
||||
}
|
||||
|
||||
public enum ResponseResults
|
||||
{
|
||||
RelationshipChanged = 1,
|
||||
MessageReceived,
|
||||
MessageDeleted,
|
||||
PresenceHeartbeatResponse,
|
||||
RefreshLogin,
|
||||
Logout,
|
||||
SubscriptionUpdateProfile = 11,
|
||||
SubscriptionUpdatePresence,
|
||||
SubscriptionUpdateGameSession,
|
||||
SubscriptionUpdateRoom = 15,
|
||||
SubscriptionUpdateRoomPlaylist,
|
||||
ModerationQuitGame = 20,
|
||||
ModerationUpdateRequired,
|
||||
ModerationKick,
|
||||
ModerationKickAttemptFailed,
|
||||
ModerationRoomBan,
|
||||
ServerMaintenance,
|
||||
GiftPackageReceived = 30,
|
||||
GiftPackageReceivedImmediate,
|
||||
GiftPackageRewardSelectionReceived,
|
||||
ProfileJuniorStatusUpdate = 40,
|
||||
RelationshipsInvalid = 50,
|
||||
StorefrontBalanceAdd = 60,
|
||||
StorefrontBalanceUpdate,
|
||||
StorefrontBalancePurchase,
|
||||
ConsumableMappingAdded = 70,
|
||||
ConsumableMappingRemoved,
|
||||
PlayerEventCreated = 80,
|
||||
PlayerEventUpdated,
|
||||
PlayerEventDeleted,
|
||||
PlayerEventResponseChanged,
|
||||
PlayerEventResponseDeleted,
|
||||
PlayerEventStateChanged,
|
||||
ChatMessageReceived = 90,
|
||||
CommunityBoardUpdate = 95,
|
||||
CommunityBoardAnnouncementUpdate,
|
||||
InventionModerationStateChanged = 100,
|
||||
FreeGiftButtonItemsAdded = 110,
|
||||
LocalRoomKeyCreated = 120,
|
||||
LocalRoomKeyDeleted
|
||||
}
|
||||
|
||||
public enum MessageType
|
||||
{
|
||||
GameInvite,
|
||||
GameInviteDeclined,
|
||||
GameJoinFailed,
|
||||
PartyActivitySwitch,
|
||||
FriendInvite,
|
||||
VoteToKick,
|
||||
GameInviteV2,
|
||||
PartyActivitySwitchV2,
|
||||
RequestGameInvite = 10,
|
||||
RequestGameInviteDeclined,
|
||||
FriendStatusOnline = 20,
|
||||
TextMessage = 30,
|
||||
FriendRequestAccepted = 40,
|
||||
PlayerCheer = 50,
|
||||
PlayerCheerAnonymous,
|
||||
RoomCoOwnerAdded = 60,
|
||||
RoomCoOwnerRemoved,
|
||||
RoomCoOwnerInvited,
|
||||
CreatorPublishedNewRoom = 70,
|
||||
PlayerAttendingEvent = 80,
|
||||
PlayerEventInvitation,
|
||||
GroupInvitation = 90,
|
||||
PlayerJoinedGroup,
|
||||
CoachMessage = 100
|
||||
}
|
||||
|
||||
|
||||
public static Reponse createResponse_msg(string message, ulong playerid)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = "2",
|
||||
Msg = new
|
||||
{
|
||||
FromPlayerId = (long)playerid,
|
||||
Id = rng.Next(1, 0x7ffffff),
|
||||
SentTime = DateTime.UtcNow,
|
||||
Type = 100,
|
||||
Data = message,
|
||||
RoomId = 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static readonly Random rng = new Random();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
using System.Xml.Linq;
|
||||
using LiteDB;
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api.server;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
using static Rec_rewild_live_rewrite.api.server.Classes.db_class.ChatDBClasses;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.dbs
|
||||
{
|
||||
public class ChatDB
|
||||
{
|
||||
public static LiteDatabase ChatDBFile = new LiteDatabase(Environment.CurrentDirectory + "/Data/DBs/Chat.db");
|
||||
private static ILiteCollection<ChatThread> Threads => ChatDBFile.GetCollection<ChatThread>("threads");
|
||||
private static ILiteCollection<ChatMessage> Messages => ChatDBFile.GetCollection<ChatMessage>("messages");
|
||||
|
||||
static ChatDB()
|
||||
{
|
||||
Threads.EnsureIndex(x => x.ChatThreadId, unique: true);
|
||||
Threads.EnsureIndex(x => x.PlayerIds);
|
||||
Messages.EnsureIndex(x => x.ChatThreadId);
|
||||
Messages.EnsureIndex(x => x.ChatMessageId, unique: true);
|
||||
}
|
||||
|
||||
public static ChatThread CreateThread(List<ulong> memberIds, ulong creatorid, string? name = null)
|
||||
{
|
||||
long newThreadId = Threads.Count() + 1;
|
||||
|
||||
ChatThread thread = new ChatThread
|
||||
{
|
||||
ChatThreadId = newThreadId,
|
||||
PlayerIds = memberIds,
|
||||
ChatThreadName = name,
|
||||
IsFavorited = false,
|
||||
SnoozedUntil = null,
|
||||
LastReadMessageId = 1,
|
||||
Messages = new List<ChatMessage>()
|
||||
};
|
||||
|
||||
Threads.Insert(thread);
|
||||
|
||||
var messageJson = new MessageJson
|
||||
{
|
||||
Type = MessageContentType.Text,
|
||||
Version = 1,
|
||||
Data = $"Player <@U{creatorid}> started a chat"
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(messageJson);
|
||||
|
||||
AddMessage(
|
||||
threadId: newThreadId,
|
||||
senderId: -5,
|
||||
jsonContents: json
|
||||
);
|
||||
|
||||
// IMPORTANT FIX → Load thread again with messages included
|
||||
return GetThread(newThreadId);
|
||||
}
|
||||
|
||||
|
||||
public static ChatMessage AddMessage(long threadId, int senderId, string jsonContents)
|
||||
{
|
||||
// if senderId is -5 then it will do the chat welcome
|
||||
|
||||
var thread = GetThread(threadId);
|
||||
if (thread == null)
|
||||
return null;
|
||||
|
||||
long newMessageId = Messages.Count() + 1;
|
||||
|
||||
ChatMessage msg = new ChatMessage
|
||||
{
|
||||
ChatMessageId = newMessageId,
|
||||
ChatThreadId = threadId,
|
||||
SenderPlayerId = senderId,
|
||||
TimeSent = DateTime.UtcNow,
|
||||
Contents = jsonContents,
|
||||
ModerationState = 0
|
||||
};
|
||||
|
||||
Messages.Insert(msg);
|
||||
|
||||
thread.Messages.Add(msg);
|
||||
thread.LastReadMessageId = newMessageId;
|
||||
Threads.Update(thread);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ChatThread RenameThread(long threadId, ulong senderId, string NewThreadName)
|
||||
{
|
||||
var thread = GetThread(threadId);
|
||||
if (thread == null)
|
||||
return null;
|
||||
|
||||
thread.ChatThreadName = NewThreadName;
|
||||
|
||||
var messageJson = new MessageJson
|
||||
{
|
||||
Type = MessageContentType.Text,
|
||||
Version = 1,
|
||||
Data = $"Player <@U{senderId}> renamed the chat"
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(messageJson);
|
||||
|
||||
var newmsg = AddMessage(
|
||||
threadId: threadId,
|
||||
senderId: -5,
|
||||
jsonContents: json
|
||||
);
|
||||
|
||||
var payload = new
|
||||
{
|
||||
Id = "ChatMessageReceived",
|
||||
Msg = new
|
||||
{
|
||||
chatMessageId = newmsg.ChatMessageId,
|
||||
chatThreadId = newmsg.ChatThreadId,
|
||||
senderPlayerId = newmsg.SenderPlayerId,
|
||||
timeSent = newmsg.TimeSent,
|
||||
contents = newmsg.Contents,
|
||||
moderationState = newmsg.ModerationState
|
||||
}
|
||||
};
|
||||
|
||||
Notifications.SendToPlayers(thread.PlayerIds, JsonConvert.SerializeObject(payload));
|
||||
Threads.Update(thread);
|
||||
return GetThread(threadId);
|
||||
}
|
||||
|
||||
public static ChatThread LeaveThread(long threadId, ulong senderId)
|
||||
{
|
||||
var thread = GetThread(threadId);
|
||||
if (thread == null)
|
||||
return null;
|
||||
|
||||
thread.PlayerIds.Remove(senderId);
|
||||
var messageJson = new MessageJson
|
||||
{
|
||||
Type = MessageContentType.Text,
|
||||
Version = 1,
|
||||
Data = $"Player <@U{senderId}> left"
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(messageJson);
|
||||
|
||||
var newmsg = AddMessage(
|
||||
threadId: threadId,
|
||||
senderId: -5,
|
||||
jsonContents: json
|
||||
);
|
||||
|
||||
var payload = new
|
||||
{
|
||||
Id = "ChatMessageReceived",
|
||||
Msg = new
|
||||
{
|
||||
chatMessageId = newmsg.ChatMessageId,
|
||||
chatThreadId = newmsg.ChatThreadId,
|
||||
senderPlayerId = newmsg.SenderPlayerId,
|
||||
timeSent = newmsg.TimeSent,
|
||||
contents = newmsg.Contents,
|
||||
moderationState = newmsg.ModerationState
|
||||
}
|
||||
};
|
||||
|
||||
Notifications.SendToPlayers(thread.PlayerIds, JsonConvert.SerializeObject(payload));
|
||||
|
||||
Threads.Update(thread);
|
||||
return GetThread(threadId);
|
||||
}
|
||||
|
||||
public static ChatThread? GetThread(long threadId)
|
||||
{
|
||||
var thread = Threads.FindOne(x => x.ChatThreadId == threadId);
|
||||
if (thread == null) return null;
|
||||
|
||||
thread.Messages = Messages.Find(x => x.ChatThreadId == threadId).OrderBy(x => x.ChatMessageId).ToList();
|
||||
return thread;
|
||||
}
|
||||
|
||||
public static List<ChatThread> GetThreadsForPlayer(ulong playerId, int maxCount)
|
||||
{
|
||||
var list = Threads.Find(x => x.PlayerIds.Contains(playerId)).OrderByDescending(x => x.LastReadMessageId).Take(maxCount).ToList();
|
||||
|
||||
foreach (var t in list)
|
||||
{
|
||||
t.Messages = Messages.Find(x => x.ChatThreadId == t.ChatThreadId).OrderByDescending(x => x.ChatMessageId).Take(1).ToList(); // Load only the latest message
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Rec_rewild_live_rewrite.api.dbs
|
||||
{
|
||||
public class ClubDB
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using Discord;
|
||||
using LiteDB;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
using static Rec_rewild_live_rewrite.api.server.Classes.db_class.EventDBClasses;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.dbs
|
||||
{
|
||||
public class EventDB
|
||||
{
|
||||
public static LiteDatabase EventDBFile = new LiteDatabase(Environment.CurrentDirectory + "/Data/DBs/Events.db");
|
||||
private static ILiteCollection<FullPlayerEvent> Events => EventDBFile.GetCollection<FullPlayerEvent>("events");
|
||||
|
||||
private static ulong GetNextId()
|
||||
{
|
||||
var last = Events.Query()
|
||||
.OrderByDescending(x => x.PlayerEventId)
|
||||
.FirstOrDefault();
|
||||
|
||||
return last == null ? 1UL : last.PlayerEventId + 1;
|
||||
}
|
||||
|
||||
public static FullPlayerEvent? GetFullEventById(ulong eventId)
|
||||
{
|
||||
return Events.FindById(eventId);
|
||||
}
|
||||
|
||||
public static FullPlayerEvent? SetEventName(ulong eventId, string NewName)
|
||||
{
|
||||
var ev = Events.FindById(eventId);
|
||||
if (ev == null)
|
||||
return null;
|
||||
|
||||
ev.Name = NewName;
|
||||
Events.Update(ev);
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
public static FullPlayerEvent? SetEventImageName(ulong eventId, string NewImageName)
|
||||
{
|
||||
var ev = Events.FindById(eventId);
|
||||
if (ev == null)
|
||||
return null;
|
||||
|
||||
ev.ImageName = NewImageName;
|
||||
Events.Update(ev);
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
public static List<FullPlayerEvent> SearchEventByQuery(string? query)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return Events.Query()
|
||||
.OrderBy(e => e.StartTime)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
query = query.Trim();
|
||||
|
||||
bool isTagSearch =
|
||||
query.StartsWith("#") ||
|
||||
query.StartsWith("%23");
|
||||
|
||||
if (isTagSearch)
|
||||
{
|
||||
string tag = query
|
||||
.Replace("%23", "")
|
||||
.TrimStart('#')
|
||||
.ToLower();
|
||||
|
||||
return Events
|
||||
.Find(Query.EQ("Tags.Tag", tag))
|
||||
.OrderBy(e => e.StartTime)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
string nameQuery = query.ToLower();
|
||||
|
||||
return Events.Query()
|
||||
.Where(e =>
|
||||
e.Name != null &&
|
||||
e.Name.ToLower().Contains(nameQuery))
|
||||
.OrderBy(e => e.StartTime)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static FullPlayerEvent CreateEvent(ulong creatorPlayerId, EventDBClasses.CreateEventRequest request)
|
||||
{
|
||||
var ev = new FullPlayerEvent
|
||||
{
|
||||
PlayerEventId = GetNextId(),
|
||||
CreatorPlayerId = creatorPlayerId,
|
||||
RoomId = request.RoomId,
|
||||
SubRoomId = request.SubRoomId,
|
||||
ClubId = request.ClubId,
|
||||
Name = request.Name,
|
||||
Description = request.Description,
|
||||
ImageName = request.ImageName,
|
||||
Tags = request.Tags?.Select(t => new EventDBClasses.FullTag
|
||||
{
|
||||
Tag = t,
|
||||
Type = EventDBClasses.TagType.Unknown1
|
||||
}).ToList(),
|
||||
StartTime = request.StartTime,
|
||||
EndTime = request.EndTime,
|
||||
AttendeeCount = 0,
|
||||
State = 0,
|
||||
Accessibility = request.Accessibility,
|
||||
IsMultiInstance = request.IsMultiInstance,
|
||||
SupportMultiInstanceRoomChat = request.SupportMultiInstanceRoomChat,
|
||||
DefaultBroadcastPermissions = (EventDBClasses.BroadcastPermissionsEnum)request.DefaultBroadcastPermissions,
|
||||
CanRequestBroadcastPermissions = (EventDBClasses.BroadcastPermissionsEnum)request.CanRequestBroadcastPermissions,
|
||||
Responses = new List<EventDBClasses.FullEventResponses>()
|
||||
};
|
||||
|
||||
Events.Insert(ev);
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
using System.Net.WebSockets;
|
||||
using LiteDB;
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api.server;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
using Rec_rewild_live_rewrite.Controllers;
|
||||
using static Rec_rewild_live_rewrite.api.WebsocketEvents;
|
||||
using static Rec_rewild_live_rewrite.Controllers.AccountsController;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.dbs
|
||||
{
|
||||
public class FriendsDB
|
||||
{
|
||||
public static LiteDatabase PlayerFriendDBFile = new LiteDatabase(Environment.CurrentDirectory + "/Data/DBs/PlayersFriends.db");
|
||||
|
||||
|
||||
// ==========================
|
||||
// GET ALL RELATIONSHIPS
|
||||
// ==========================
|
||||
public static List<RelationshipDetail> GetRelationships(ulong playerId)
|
||||
{
|
||||
var col = PlayerFriendDBFile.GetCollection<YourFriendData>("PlayersFriends");
|
||||
|
||||
List<RelationshipDetail> list = new();
|
||||
|
||||
foreach (var data in col.FindAll())
|
||||
{
|
||||
if (data.Relationship.OtherPlayerID != playerId)
|
||||
continue;
|
||||
|
||||
list.Add(new RelationshipDetail
|
||||
{
|
||||
Id = data.Relationship.Id,
|
||||
PlayerID = data.Relationship.PlayerID,
|
||||
OtherPlayerID = data.Relationship.OtherPlayerID,
|
||||
RelationshipType = data.Relationship.RelationshipType,
|
||||
Muted = data.Relationship.Muted,
|
||||
Ignored = data.Relationship.Ignored,
|
||||
Favorited = data.Relationship.Favorited,
|
||||
VoiceVolume = data.Relationship.VoiceVolume
|
||||
});
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ==========================
|
||||
// SEND FRIEND REQUEST
|
||||
// ==========================
|
||||
public static RelationshipDetail SendFriendRequest(ulong fromPlayer, ulong toPlayer)
|
||||
{
|
||||
var col = PlayerFriendDBFile.GetCollection<YourFriendData>("PlayersFriends");
|
||||
|
||||
// ---- A -> B ----
|
||||
var relA = col.FindOne(x =>
|
||||
x.PlayerID == fromPlayer &&
|
||||
x.Relationship.OtherPlayerID == toPlayer
|
||||
);
|
||||
|
||||
if (relA == null)
|
||||
{
|
||||
relA = new YourFriendData
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
PlayerID = fromPlayer,
|
||||
Relationship = new RelationshipDetail
|
||||
{
|
||||
Id = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
PlayerID = fromPlayer,
|
||||
OtherPlayerID = toPlayer,
|
||||
RelationshipType = RelationshipType.None,
|
||||
Favorited = ReciprocalStatus.None,
|
||||
Ignored = ReciprocalStatus.None,
|
||||
Muted = ReciprocalStatus.None,
|
||||
VoiceVolume = 100
|
||||
}
|
||||
};
|
||||
|
||||
col.Insert(relA);
|
||||
}
|
||||
|
||||
// ---- B -> A ----
|
||||
var relB = col.FindOne(x =>
|
||||
x.PlayerID == toPlayer &&
|
||||
x.Relationship.OtherPlayerID == fromPlayer
|
||||
);
|
||||
|
||||
if (relB == null)
|
||||
{
|
||||
relB = new YourFriendData
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
PlayerID = toPlayer,
|
||||
Relationship = new RelationshipDetail
|
||||
{
|
||||
Id = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
PlayerID = toPlayer,
|
||||
OtherPlayerID = fromPlayer,
|
||||
RelationshipType = RelationshipType.None,
|
||||
Favorited = ReciprocalStatus.None,
|
||||
Ignored = ReciprocalStatus.None,
|
||||
Muted = ReciprocalStatus.None,
|
||||
VoiceVolume = 100
|
||||
}
|
||||
};
|
||||
|
||||
col.Insert(relB);
|
||||
}
|
||||
|
||||
|
||||
// Update types
|
||||
relA.Relationship.RelationshipType = RelationshipType.FriendRequestSent;
|
||||
relB.Relationship.RelationshipType = RelationshipType.FriendRequestReceived;
|
||||
|
||||
col.Update(relA);
|
||||
col.Update(relB);
|
||||
|
||||
//todo: send ws event to anounce the player that it got a new friend request
|
||||
|
||||
// Websocket pushes
|
||||
Notifications.SendToPlayer(
|
||||
fromPlayer,
|
||||
JsonConvert.SerializeObject(CreateResponseFriendRequestSent(toPlayer))
|
||||
);
|
||||
|
||||
Notifications.SendToPlayer(
|
||||
toPlayer,
|
||||
JsonConvert.SerializeObject(CreateResponseFriendRequestReceived(fromPlayer))
|
||||
);
|
||||
|
||||
Notifications.SendToPlayer(
|
||||
fromPlayer,
|
||||
JsonConvert.SerializeObject(CreateResponseFriendRequestMsg(toPlayer))
|
||||
);
|
||||
return relA.Relationship;
|
||||
}
|
||||
|
||||
public static RelationshipDetail? AcceptFriendRequest(ulong fromPlayer, ulong toPlayer)
|
||||
{
|
||||
var col = PlayerFriendDBFile.GetCollection<YourFriendData>("PlayersFriends");
|
||||
|
||||
// Request MUST exist in both directions (pending)
|
||||
var relA = col.FindOne(x => x.PlayerID == fromPlayer && x.Relationship.OtherPlayerID == toPlayer);
|
||||
var relB = col.FindOne(x => x.PlayerID == toPlayer && x.Relationship.OtherPlayerID == fromPlayer);
|
||||
|
||||
// Request cannot be accepted if it doesn't exist
|
||||
if (relA == null || relA.Relationship == null ||
|
||||
relB == null || relB.Relationship == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set both sides to Friend
|
||||
relA.Relationship.RelationshipType = RelationshipType.Friend;
|
||||
relB.Relationship.RelationshipType = RelationshipType.Friend;
|
||||
|
||||
col.Update(relA);
|
||||
col.Update(relB);
|
||||
|
||||
// Send WebSocket events
|
||||
Notifications.SendToPlayer(fromPlayer,
|
||||
JsonConvert.SerializeObject(CreateResponseFriendAccepted(toPlayer, fromPlayer)));
|
||||
|
||||
Notifications.SendToPlayer(toPlayer,
|
||||
JsonConvert.SerializeObject(CreateResponseFriendAccepted(fromPlayer, toPlayer)));
|
||||
|
||||
Notifications.SendToPlayer(fromPlayer,
|
||||
JsonConvert.SerializeObject(CreateResponseFriendAcceptedMsg(toPlayer)));
|
||||
|
||||
// return the accepted version
|
||||
return relA.Relationship;
|
||||
}
|
||||
|
||||
|
||||
private static WebsocketEvents.Reponse CreateResponseFriendAcceptedMsg(ulong fromPlayer) => new WebsocketEvents.Reponse
|
||||
{
|
||||
Id = "2",
|
||||
Msg = new
|
||||
{
|
||||
Id = (ulong)new Random().Next(),
|
||||
FromPlayerId = fromPlayer,
|
||||
SentTime = DateTime.UtcNow,
|
||||
Type = WebsocketEvents.MessageType.FriendRequestAccepted,
|
||||
Data = "",
|
||||
RoomId = (ulong?)null,
|
||||
PlayerEventId = (ulong?)null,
|
||||
}
|
||||
};
|
||||
|
||||
public static void DeleteFriends(ulong playerId)
|
||||
{
|
||||
if (playerId == 0) return;
|
||||
var col = PlayerFriendDBFile.GetCollection<YourFriendData>("PlayersFriends");
|
||||
col.DeleteMany(x => x.PlayerID == playerId);
|
||||
}
|
||||
|
||||
public static Reponse CreateResponseFriendAccepted(ulong playerid, ulong fromplayerid)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = "1",
|
||||
Msg = new RelationshipDetail()
|
||||
{
|
||||
Id = (ulong)new Random().Next(),
|
||||
PlayerID = playerid,
|
||||
OtherPlayerID = fromplayerid,
|
||||
Favorited = ReciprocalStatus.None,
|
||||
Ignored = ReciprocalStatus.None,
|
||||
Muted = ReciprocalStatus.None,
|
||||
RelationshipType = RelationshipType.Friend,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Reponse CreateResponseFriendRemoved(ulong playerid, ulong fromplayerid)
|
||||
{
|
||||
return new Reponse
|
||||
{
|
||||
Id = "1",
|
||||
Msg = new RelationshipDetail()
|
||||
{
|
||||
Id = (ulong)new Random().Next(),
|
||||
PlayerID = playerid,
|
||||
OtherPlayerID = fromplayerid,
|
||||
Favorited = ReciprocalStatus.None,
|
||||
Ignored = ReciprocalStatus.None,
|
||||
Muted = ReciprocalStatus.None,
|
||||
RelationshipType = RelationshipType.None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static WebsocketEvents.Reponse CreateResponseFriendRequestReceived(ulong fromPlayer) => new WebsocketEvents.Reponse
|
||||
{
|
||||
Id = "1",
|
||||
Msg = new RelationshipDetail
|
||||
{
|
||||
Id = (ulong)new Random().Next(),
|
||||
PlayerID = fromPlayer,
|
||||
OtherPlayerID = fromPlayer,
|
||||
RelationshipType = RelationshipType.FriendRequestReceived,
|
||||
Favorited = 0,
|
||||
Muted = 0,
|
||||
Ignored = 0,
|
||||
VoiceVolume = 100
|
||||
}
|
||||
};
|
||||
|
||||
private static WebsocketEvents.Reponse CreateResponseFriendRequestMsg(ulong fromPlayer) => new WebsocketEvents.Reponse
|
||||
{
|
||||
Id = "2",
|
||||
Msg = new
|
||||
{
|
||||
Id = (ulong)new Random().Next(),
|
||||
FromPlayerId = fromPlayer,
|
||||
SentTime = DateTime.UtcNow,
|
||||
Type = WebsocketEvents.MessageType.FriendInvite,
|
||||
Data = "",
|
||||
RoomId = (ulong?)null,
|
||||
PlayerEventId = (ulong?)null,
|
||||
}
|
||||
};
|
||||
|
||||
public static RelationshipDetail RemoveFriend(ulong playerA, ulong playerB)
|
||||
{
|
||||
var col = PlayerFriendDBFile.GetCollection<YourFriendData>("PlayersFriends");
|
||||
|
||||
// A -> B
|
||||
var relA = col.FindOne(x => x.PlayerID == playerA &&
|
||||
x.Relationship.OtherPlayerID == playerB);
|
||||
|
||||
// B -> A
|
||||
var relB = col.FindOne(x => x.PlayerID == playerB &&
|
||||
x.Relationship.OtherPlayerID == playerA);
|
||||
|
||||
// If they were never connected, return empty relationship
|
||||
if (relA == null && relB == null)
|
||||
{
|
||||
return new RelationshipDetail
|
||||
{
|
||||
PlayerID = playerA,
|
||||
OtherPlayerID = playerB,
|
||||
RelationshipType = RelationshipType.None
|
||||
};
|
||||
}
|
||||
|
||||
// Delete A->B
|
||||
if (relA != null)
|
||||
col.Delete(relA.Id);
|
||||
|
||||
// Delete B->A
|
||||
if (relB != null)
|
||||
col.Delete(relB.Id);
|
||||
|
||||
// Send WS update to player A
|
||||
Notifications.SendToPlayer(
|
||||
playerA,
|
||||
JsonConvert.SerializeObject(
|
||||
CreateResponseFriendRemoved(playerA, playerB)
|
||||
)
|
||||
);
|
||||
|
||||
// Send WS update to player B
|
||||
Notifications.SendToPlayer(
|
||||
playerB,
|
||||
JsonConvert.SerializeObject(
|
||||
CreateResponseFriendRemoved(playerB, playerA)
|
||||
)
|
||||
);
|
||||
|
||||
// Return final relationship state
|
||||
return new RelationshipDetail
|
||||
{
|
||||
PlayerID = playerA,
|
||||
OtherPlayerID = playerB,
|
||||
RelationshipType = RelationshipType.None,
|
||||
Favorited = ReciprocalStatus.None,
|
||||
Ignored = ReciprocalStatus.None,
|
||||
Muted = ReciprocalStatus.None,
|
||||
VoiceVolume = 100
|
||||
};
|
||||
}
|
||||
|
||||
public static RelationshipDetail AddFriend(ulong playerA, ulong playerB)
|
||||
{
|
||||
var col = PlayerFriendDBFile.GetCollection<YourFriendData>("PlayersFriends");
|
||||
|
||||
// A -> B
|
||||
var relA = col.FindOne(x => x.PlayerID == playerA &&
|
||||
x.Relationship.OtherPlayerID == playerB);
|
||||
|
||||
if (relA == null)
|
||||
{
|
||||
relA = new YourFriendData
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
PlayerID = playerA,
|
||||
Relationship = new RelationshipDetail
|
||||
{
|
||||
Id = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
PlayerID = playerA,
|
||||
OtherPlayerID = playerB,
|
||||
Favorited = ReciprocalStatus.None,
|
||||
Ignored = ReciprocalStatus.None,
|
||||
Muted = ReciprocalStatus.None,
|
||||
VoiceVolume = 100
|
||||
}
|
||||
};
|
||||
col.Insert(relA);
|
||||
}
|
||||
|
||||
// B -> A
|
||||
var relB = col.FindOne(x => x.PlayerID == playerB &&
|
||||
x.Relationship.OtherPlayerID == playerA);
|
||||
|
||||
if (relB == null)
|
||||
{
|
||||
relB = new YourFriendData
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
PlayerID = playerB,
|
||||
Relationship = new RelationshipDetail
|
||||
{
|
||||
Id = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
PlayerID = playerB,
|
||||
OtherPlayerID = playerA,
|
||||
Favorited = ReciprocalStatus.None,
|
||||
Ignored = ReciprocalStatus.None,
|
||||
Muted = ReciprocalStatus.None,
|
||||
VoiceVolume = 100
|
||||
}
|
||||
};
|
||||
col.Insert(relB);
|
||||
}
|
||||
|
||||
// Set both sides to Friend
|
||||
relA.Relationship.RelationshipType = RelationshipType.Friend;
|
||||
relB.Relationship.RelationshipType = RelationshipType.Friend;
|
||||
|
||||
col.Update(relA);
|
||||
col.Update(relB);
|
||||
|
||||
// WebSocket updates
|
||||
Notifications.SendToPlayer(
|
||||
playerA,
|
||||
JsonConvert.SerializeObject(CreateResponseFriendAccepted(playerA, playerB))
|
||||
);
|
||||
|
||||
Notifications.SendToPlayer(
|
||||
playerB,
|
||||
JsonConvert.SerializeObject(CreateResponseFriendAccepted(playerB, playerA))
|
||||
);
|
||||
|
||||
return relA.Relationship;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// {"type":1,"target":"Notification","arguments":["{\"id\":\"2\",\"msg\":{\"id\":16212007930,\"fromPlayerId\":1764227894,\"sentTime\":\"2025-06-23T17:20:53.72092Z\",\"type\":4,\"data\":\"\",\"roomId\":null,\"playerEventId\":null}}"]}
|
||||
|
||||
private static WebsocketEvents.Reponse CreateResponseFriendRequestSent(ulong toPlayer) => new WebsocketEvents.Reponse
|
||||
{
|
||||
Id = "50",
|
||||
Msg = ""
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using LiteDB;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.dbs
|
||||
{
|
||||
public class HeartbeatDB
|
||||
{
|
||||
private static readonly LiteDatabase _db = new($"{Environment.CurrentDirectory}/Data/DBs/Heartbeats.db");
|
||||
private static readonly ILiteCollection<PlayerHeartbeatData> _col =
|
||||
_db.GetCollection<PlayerHeartbeatData>("players_heartbeat");
|
||||
|
||||
public static void Setup()
|
||||
{
|
||||
_col.EnsureIndex(x => x.playerid, unique: true);
|
||||
}
|
||||
|
||||
public class PlayerHeartbeatData
|
||||
{
|
||||
[BsonId]
|
||||
public ulong iD { get; set; }
|
||||
public ulong playerid { get; set; }
|
||||
public Heartbeat Heartbeat { get; set; }
|
||||
}
|
||||
|
||||
private static Heartbeat CreateDefaultHeartbeat(ulong playerid, Platforms platform, DeviceClasses deviceClasses) =>
|
||||
new()
|
||||
{
|
||||
playerId = (long)playerid,
|
||||
appVersion = "20250731.01",
|
||||
deviceClass = -1,
|
||||
isOnline = false,
|
||||
platform = 0,
|
||||
roomInstance = null,
|
||||
statusVisibility = 4,
|
||||
vrMovementMode = 0,
|
||||
};
|
||||
|
||||
public static Heartbeat GetPlayerHeartbeat(ulong playerid)
|
||||
{
|
||||
var data = _col.FindOne(x => x.playerid == playerid);
|
||||
return data?.Heartbeat ?? CreateDefaultHeartbeat(playerid, Platforms.Steam, DeviceClasses.Screen);
|
||||
}
|
||||
|
||||
public static string GetPlayerHeartbeatRoomName(ulong playerid)
|
||||
{
|
||||
var data = _col.FindOne(x => x.playerid == playerid);
|
||||
return data?.Heartbeat?.roomInstance?.name ?? "nowhere!";
|
||||
}
|
||||
|
||||
public static bool CreatePlayerHeartbeat(ulong playerid, out Heartbeat heartbeat, Platforms platform, DeviceClasses deviceClasses)
|
||||
{
|
||||
if (_col.Exists(x => x.playerid == playerid))
|
||||
{
|
||||
heartbeat = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
heartbeat = CreateDefaultHeartbeat(playerid, platform, deviceClasses);
|
||||
_col.Insert(new PlayerHeartbeatData
|
||||
{
|
||||
iD = (ulong)_col.LongCount() + 1,
|
||||
playerid = playerid,
|
||||
Heartbeat = heartbeat
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void UpdateStatusVisibility(ulong playerid, int visibility)
|
||||
{
|
||||
var data = _col.FindOne(x => x.playerid == playerid);
|
||||
if (data != null)
|
||||
{
|
||||
data.Heartbeat.statusVisibility = visibility;
|
||||
_col.Update(data);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPlayerCurrentRoomName(ulong playerid)
|
||||
{
|
||||
var data = _col.FindOne(x => x.playerid == playerid);
|
||||
return data?.Heartbeat?.roomInstance?.name ?? "not in a room.";
|
||||
}
|
||||
|
||||
public static void ClearPlayersHeartbeats()
|
||||
{
|
||||
foreach (var data in _col.FindAll())
|
||||
{
|
||||
UpdatePlayerHeartbeat(data.playerid, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClearPlayerHeartbeat(ulong playerId) =>
|
||||
UpdatePlayerHeartbeat(playerId, null, false);
|
||||
|
||||
public static void UpdatePlayerHeartbeat(
|
||||
ulong playerid,
|
||||
RoomInstance? roomInstance,
|
||||
bool online = true,
|
||||
Platforms platform = Platforms.All,
|
||||
DeviceClasses deviceClasses = DeviceClasses.Unknown)
|
||||
{
|
||||
var data = _col.FindOne(x => x.playerid == playerid);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var hb = data.Heartbeat;
|
||||
hb.roomInstance = online ? roomInstance : null;
|
||||
hb.isOnline = online;
|
||||
|
||||
_col.Update(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new heartbeat and assign the current roomInstance
|
||||
var hb = CreateDefaultHeartbeat(playerid, platform, deviceClasses);
|
||||
hb.roomInstance = roomInstance;
|
||||
hb.isOnline = online;
|
||||
|
||||
_col.Insert(new PlayerHeartbeatData
|
||||
{
|
||||
iD = (ulong)_col.LongCount() + 1,
|
||||
playerid = playerid,
|
||||
Heartbeat = hb
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class Heartbeat
|
||||
{
|
||||
public long serverTime { get; set; }
|
||||
public long playerId { get; set; }
|
||||
public int statusVisibility { get; set; }
|
||||
public int platform { get; set; }
|
||||
public int deviceClass { get; set; }
|
||||
public int vrMovementMode { get; set; }
|
||||
public RoomInstance roomInstance { get; set; } // Can be null
|
||||
public string lastOnline { get; set; } // ISO 8601 string
|
||||
public bool isOnline { get; set; }
|
||||
public string appVersion { get; set; }
|
||||
}
|
||||
|
||||
public class FullRoomInstance
|
||||
{
|
||||
public RoomInstance? roomInstance { get; set; }
|
||||
public string? correlationId { get; set; }
|
||||
}
|
||||
|
||||
public class RoomInstance
|
||||
{
|
||||
public long roomInstanceId { get; set; }
|
||||
public long roomId { get; set; }
|
||||
public long subRoomId { get; set; }
|
||||
public string? location { get; set; }
|
||||
public int roomInstanceType { get; set; }
|
||||
public string? name { get; set; }
|
||||
public int maxCapacity { get; set; }
|
||||
public bool isFull { get; set; }
|
||||
public bool isPrivate { get; set; }
|
||||
public bool isInProgress { get; set; }
|
||||
[JsonIgnore]
|
||||
public string? photonRoomIdYeee { get; set; } // im crack
|
||||
public string? voiceServerId { get; set; }
|
||||
public string? voiceAuthId { get; set; }
|
||||
public int matchmakingPolicy { get; set; }
|
||||
}
|
||||
|
||||
public enum MatchmakingErrorCode
|
||||
{
|
||||
Success,
|
||||
NoSuchGame,
|
||||
PlayerNotOnline,
|
||||
InsufficientSpace,
|
||||
EventNotStarted,
|
||||
EventAlreadyFinished,
|
||||
EventCreatorNotReady,
|
||||
BlockedFromRoom,
|
||||
ProfileLocked,
|
||||
NoBirthday,
|
||||
MarkedForDelete,
|
||||
JuniorNotAllowed,
|
||||
Banned,
|
||||
AlreadyInBestInstance,
|
||||
InsufficientRelationship,
|
||||
UpdateRequired = 16,
|
||||
AlreadyInTargetInstance,
|
||||
RegistrationRequired,
|
||||
UGCNotAllowed,
|
||||
NoSuchRoom,
|
||||
RoomCreatorNotReady,
|
||||
RoomIsNotActive,
|
||||
RoomBlockedByCreator,
|
||||
RoomBlockingCreator,
|
||||
RoomIsPrivate,
|
||||
RoomInstanceIsPrivate,
|
||||
DeviceClassNotSupported = 30,
|
||||
DeviceClassNotSupportedByRoomOwner,
|
||||
VRMovementModeNotSupportedByRoomOwner,
|
||||
EventIsPrivate = 35,
|
||||
RoomInviteExpired = 40,
|
||||
NoAvailableRegion = 45,
|
||||
NotorietyTooPoor = 50,
|
||||
BannedFromRoom = 55
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
using LiteDB;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public static class ImageMetadataDB
|
||||
{
|
||||
private static readonly string DbPath = Path.Combine("Data", "DBs", "Images.db");
|
||||
|
||||
public class FullSavedImage
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public ImageAccessibility Accessibility { get; set; }
|
||||
public bool AccessibilityLocked { get; set; } = false;
|
||||
public int CheerCount { get; set; } = 0;
|
||||
public int CommentCount { get; set; } = 0;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public string? Description { get; set; }
|
||||
public string ImageName { get; set; }
|
||||
public ulong PlayerEventId { get; set; }
|
||||
public ulong PlayerId { get; set; }
|
||||
public int RoomId { get; set; }
|
||||
//public string Description { get; set; } = "";
|
||||
public SavedImageTypeEnum Type { get; set; }
|
||||
public List<ulong> TaggedPlayerIds { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ImageV6
|
||||
{
|
||||
public ImageAccessibility Accessibility { get; set; }
|
||||
public bool AccessibilityLocked { get; set; } = false;
|
||||
public int CheerCount { get; set; } = 0;
|
||||
public int CommentCount { get; set; } = 0;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public string? Description { get; set; }
|
||||
public int Id { get; set; }
|
||||
public string ImageName { get; set; }
|
||||
public ulong PlayerEventId { get; set; }
|
||||
public ulong PlayerId { get; set; }
|
||||
public int RoomId { get; set; }
|
||||
public List<ulong> TaggedPlayerIds { get; set; } = new();
|
||||
public SavedImageTypeEnum Type { get; set; }
|
||||
}
|
||||
|
||||
public class ImagesPlayer
|
||||
{
|
||||
public ImageAccessibility Accessibility { get; set; }
|
||||
public bool AccessibilityLocked { get; set; } = false;
|
||||
public int CheerCount { get; set; } = 0;
|
||||
public int CommentCount { get; set; } = 0;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public string? Description { get; set; }
|
||||
public string ImageName { get; set; }
|
||||
public ulong PlayerEventId { get; set; }
|
||||
public ulong PlayerId { get; set; }
|
||||
public int RoomId { get; set; }
|
||||
public int SavedImageId { get; set; }
|
||||
public SavedImageTypeEnum SavedImageType { get; set; }
|
||||
}
|
||||
|
||||
public class ImageMetadata
|
||||
{
|
||||
public List<ulong> playerIds { get; set; } = new();
|
||||
public SavedImageTypeEnum savedImageType { get; set; }
|
||||
public int roomId { get; set; }
|
||||
public ImageAccessibility accessibility { get; set; }
|
||||
public string? description { get; set; }
|
||||
}
|
||||
|
||||
public enum SavedImageTypeEnum
|
||||
{
|
||||
None, // 0
|
||||
ShareCamera, // 1
|
||||
OutfitThumbnail, // 2
|
||||
RoomThumbnail, // 3
|
||||
ProfileThumbnail, // 4
|
||||
InventionThumbnail, // 5
|
||||
PlayerEventThumbnail, // 6
|
||||
RoomLoadScreen // 7
|
||||
}
|
||||
|
||||
public enum ImageAccessibility
|
||||
{
|
||||
Private = 0,
|
||||
Public = 1,
|
||||
}
|
||||
|
||||
public class Cheer
|
||||
{
|
||||
public ObjectId Id { get; set; }
|
||||
public int SavedImageId { get; set; }
|
||||
public ulong PlayerId { get; set; }
|
||||
}
|
||||
|
||||
public class CheerRequest
|
||||
{
|
||||
public int SavedImageId { get; set; }
|
||||
public bool Cheer { get; set; }
|
||||
}
|
||||
|
||||
// PERF: Keep a thread-safe singleton LiteDatabase instance to avoid reopening files constantly.
|
||||
private static readonly Lazy<LiteDatabase> _dbInstance = new(() => new LiteDatabase(DbPath));
|
||||
public static LiteDatabase GetDb() => _dbInstance.Value;
|
||||
|
||||
public static FullSavedImage InsertImage(FullSavedImage image)
|
||||
{
|
||||
var db = GetDb();
|
||||
var col = db.GetCollection<FullSavedImage>("images");
|
||||
col.EnsureIndex(x => x.Id, true);
|
||||
|
||||
// If the image doesn't already have an ID, assign the next sequential ID.
|
||||
if (image.Id == 0)
|
||||
{
|
||||
// Get the highest existing ID and increment by 1.
|
||||
// PERF: This uses descending order to get the latest ID efficiently.
|
||||
var last = col.Query()
|
||||
.OrderByDescending(x => x.Id)
|
||||
.Limit(1)
|
||||
.FirstOrDefault();
|
||||
|
||||
image.Id = (last?.Id ?? 0) + 1;
|
||||
}
|
||||
|
||||
col.Insert(image);
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
public static List<FullSavedImage> GetRoomImages(
|
||||
ulong playerId,
|
||||
int roomId,
|
||||
int sort = 0,
|
||||
int filter = 0,
|
||||
int take = 50,
|
||||
int skip = 0)
|
||||
{
|
||||
var db = GetDb();
|
||||
var col = db.GetCollection<FullSavedImage>("images");
|
||||
|
||||
// Base query: only images in this room
|
||||
var query = col.Query()
|
||||
.Where(x => x.RoomId == roomId);
|
||||
|
||||
// Apply filter conditions
|
||||
switch (filter)
|
||||
{
|
||||
case 2: // Only show player's private images
|
||||
query = query.Where(x => x.PlayerId == playerId);
|
||||
break;
|
||||
|
||||
case 3: // Only images that have been cheered
|
||||
query = query.Where(x => x.CheerCount > 0);
|
||||
break;
|
||||
|
||||
// filter == 1 will be handled in sorting
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
if (sort == 0 && filter == 1)
|
||||
{
|
||||
// Special case: newest first when sort=0 & filter=1
|
||||
query = query.OrderByDescending(x => x.CreatedAt);
|
||||
}
|
||||
else
|
||||
{
|
||||
query = sort switch
|
||||
{
|
||||
1 => query.OrderByDescending(x => x.CreatedAt), // Newest first
|
||||
2 => query.OrderByDescending(x => x.CheerCount), // Most cheered
|
||||
3 => query.OrderByDescending(x => x.CommentCount),// Most commented
|
||||
_ => query.OrderBy(x => x.CreatedAt), // Default: oldest first
|
||||
};
|
||||
}
|
||||
|
||||
// Apply pagination
|
||||
return query
|
||||
.Skip(skip)
|
||||
.Limit(take)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
public static List<FullSavedImage> GetGlobalImages()
|
||||
{
|
||||
var db = GetDb();
|
||||
var col = db.GetCollection<FullSavedImage>("images");
|
||||
|
||||
return col.Query()
|
||||
.Where(x => x.Accessibility == ImageAccessibility.Public)
|
||||
.OrderByDescending(x => x.CheerCount)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
public static List<ImagesPlayer> GetPlayerImages(ulong playeridthatrequesting, ulong playerid)
|
||||
{
|
||||
var db = GetDb();
|
||||
var col = db.GetCollection<FullSavedImage>("images");
|
||||
|
||||
var query = col.Query().Where(x => x.PlayerId == playerid);
|
||||
|
||||
if (playeridthatrequesting != playerid)
|
||||
{
|
||||
query = query.Where(x => x.Accessibility == ImageAccessibility.Public);
|
||||
}
|
||||
|
||||
return query.ToEnumerable()
|
||||
.Select(MapToImagesPlayer)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static ImagesPlayer MapToImagesPlayer(FullSavedImage x)
|
||||
{
|
||||
return new ImagesPlayer
|
||||
{
|
||||
Accessibility = x.Accessibility,
|
||||
AccessibilityLocked = x.AccessibilityLocked,
|
||||
CheerCount = x.CheerCount,
|
||||
CommentCount = x.CommentCount,
|
||||
CreatedAt = x.CreatedAt,
|
||||
Description = x.Description,
|
||||
ImageName = x.ImageName,
|
||||
PlayerEventId = x.PlayerEventId,
|
||||
PlayerId = x.PlayerId,
|
||||
RoomId = x.RoomId,
|
||||
SavedImageId = x.Id,
|
||||
SavedImageType = x.Type
|
||||
};
|
||||
}
|
||||
|
||||
public static void UpdateImageCheerCount(int savedImageId, int cheerCount)
|
||||
{
|
||||
var db = GetDb();
|
||||
var col = db.GetCollection<FullSavedImage>("images");
|
||||
var img = col.FindById(savedImageId);
|
||||
if (img != null)
|
||||
{
|
||||
img.CheerCount = cheerCount;
|
||||
col.Update(img);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ToggleCheer(int savedImageId, ulong playerId, bool cheer)
|
||||
{
|
||||
var db = GetDb();
|
||||
var col = db.GetCollection<Cheer>("cheers");
|
||||
|
||||
var existing = col.FindOne(x => x.SavedImageId == savedImageId && x.PlayerId == playerId);
|
||||
|
||||
if (cheer && existing == null)
|
||||
{
|
||||
col.Insert(new Cheer { SavedImageId = savedImageId, PlayerId = playerId });
|
||||
}
|
||||
else if (!cheer && existing != null)
|
||||
{
|
||||
col.Delete(existing.Id);
|
||||
}
|
||||
|
||||
return col.Exists(x => x.SavedImageId == savedImageId && x.PlayerId == playerId);
|
||||
}
|
||||
|
||||
public static int GetCheerCount(int savedImageId)
|
||||
{
|
||||
var db = GetDb();
|
||||
return db.GetCollection<Cheer>("cheers").Count(x => x.SavedImageId == savedImageId);
|
||||
}
|
||||
|
||||
public static bool HasUserCheered(int savedImageId, ulong playerId)
|
||||
{
|
||||
var db = GetDb();
|
||||
return db.GetCollection<Cheer>("cheers")
|
||||
.Exists(x => x.SavedImageId == savedImageId && x.PlayerId == playerId);
|
||||
}
|
||||
|
||||
private static readonly string OldDir = Path.Combine("Data", "images_metadata");
|
||||
|
||||
public static ImageV6 GetImageByName(string name)
|
||||
{
|
||||
var db = GetDb();
|
||||
var col = db.GetCollection<FullSavedImage>("images");
|
||||
var record = col.FindOne(x => x.ImageName == name);
|
||||
|
||||
if (record == null) return null;
|
||||
|
||||
return new ImageV6
|
||||
{
|
||||
Accessibility = record.Accessibility,
|
||||
AccessibilityLocked = record.AccessibilityLocked,
|
||||
CheerCount = record.CheerCount,
|
||||
CommentCount = record.CommentCount,
|
||||
CreatedAt = record.CreatedAt,
|
||||
Description = record.Description,
|
||||
Id = record.Id,
|
||||
ImageName = record.ImageName,
|
||||
PlayerEventId = record.PlayerEventId,
|
||||
PlayerId = record.PlayerId,
|
||||
RoomId = record.RoomId,
|
||||
TaggedPlayerIds = record.TaggedPlayerIds,
|
||||
Type = record.Type,
|
||||
};
|
||||
}
|
||||
|
||||
public static void DeleteImagesFromPlayerId(ulong playerId)
|
||||
{
|
||||
var db = GetDb();
|
||||
var imageCol = db.GetCollection<FullSavedImage>("images");
|
||||
var cheerCol = db.GetCollection<Cheer>("cheers");
|
||||
|
||||
var imagesToDelete = imageCol.Query().Where(x => x.PlayerId == playerId).ToList();
|
||||
|
||||
if (imagesToDelete.Count == 0)
|
||||
return;
|
||||
|
||||
foreach (var img in imagesToDelete)
|
||||
{
|
||||
imageCol.Delete(img.Id);
|
||||
cheerCol.DeleteMany(c => c.SavedImageId == img.Id);
|
||||
}
|
||||
|
||||
Console.WriteLine($"[Delete] Removed {imagesToDelete.Count} images and their cheers for PlayerId {playerId}");
|
||||
}
|
||||
|
||||
public static List<FullSavedImage> GetImagesByIds(List<int> ids)
|
||||
{
|
||||
if (ids == null || ids.Count == 0)
|
||||
return new List<FullSavedImage>();
|
||||
|
||||
var db = GetDb();
|
||||
var col = db.GetCollection<FullSavedImage>("images");
|
||||
|
||||
// Efficient LiteDB WHERE query using Contains()
|
||||
return col.Query()
|
||||
.Where(x => ids.Contains(x.Id))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
public static void ImportOldJson()
|
||||
{
|
||||
if (!Directory.Exists(OldDir))
|
||||
{
|
||||
Console.WriteLine($"[Import] Old directory not found: {OldDir}");
|
||||
return;
|
||||
}
|
||||
|
||||
var db = GetDb();
|
||||
var imageCol = db.GetCollection<FullSavedImage>("images");
|
||||
var cheerCol = db.GetCollection<Cheer>("cheers");
|
||||
imageCol.EnsureIndex(x => x.Id, true);
|
||||
|
||||
// PERF: Parallelize JSON import for multiple files.
|
||||
var files = Directory.GetFiles(OldDir, "*.json");
|
||||
var cheerFiles = files.Where(f => f.EndsWith("_cheers.json")).ToArray();
|
||||
var imageFiles = files.Except(cheerFiles).ToArray();
|
||||
|
||||
Parallel.ForEach(imageFiles, file =>
|
||||
{
|
||||
string fileName = Path.GetFileName(file);
|
||||
try
|
||||
{
|
||||
string json = File.ReadAllText(file);
|
||||
var images = JsonConvert.DeserializeObject<List<FullSavedImage>>(json);
|
||||
if (images == null) return;
|
||||
|
||||
foreach (var img in images)
|
||||
{
|
||||
if (!imageCol.Exists(x => x.Id == img.Id))
|
||||
{
|
||||
imageCol.Insert(img);
|
||||
Console.WriteLine($"[Import] Imported image {img.Id} from {fileName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[Import] Skipped duplicate image {img.Id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Import] Failed to import {fileName}: {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
Parallel.ForEach(cheerFiles, file =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileName = Path.GetFileName(file);
|
||||
int savedImageId = int.Parse(fileName.Replace("_cheers.json", ""));
|
||||
|
||||
string json = File.ReadAllText(file);
|
||||
var cheerList = JsonConvert.DeserializeObject<List<ulong>>(json);
|
||||
if (cheerList == null) return;
|
||||
|
||||
foreach (var pid in cheerList)
|
||||
{
|
||||
if (!cheerCol.Exists(x => x.SavedImageId == savedImageId && x.PlayerId == pid))
|
||||
{
|
||||
cheerCol.Insert(new Cheer { SavedImageId = savedImageId, PlayerId = pid });
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"[Import] Imported {cheerList.Count} cheers for image {savedImageId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Import] Failed to import cheers from {file}: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using LiteDB;
|
||||
using static Rec_rewild_live_rewrite.api.server.Classes.db_class.LeaderboardDBClasses;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.dbs
|
||||
{
|
||||
public class LeaderboardDB
|
||||
{
|
||||
private static readonly LiteDatabase DB = new LiteDatabase(
|
||||
Environment.CurrentDirectory + "/Data/DBs/Leaderboard.db"
|
||||
);
|
||||
|
||||
private static ILiteCollection<Entry> Entries =>
|
||||
DB.GetCollection<Entry>("leaderboard");
|
||||
|
||||
static LeaderboardDB()
|
||||
{
|
||||
// Short index names to avoid LiteDB 32-char name limit
|
||||
Entries.EnsureIndex("pid", x => x.PlayerId);
|
||||
Entries.EnsureIndex("sc", x => x.StatChannel);
|
||||
Entries.EnsureIndex("rid", x => x.RoomId);
|
||||
|
||||
// Score index for sorting
|
||||
Entries.EnsureIndex("score", x => x.Score);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------
|
||||
// Add or Update Stat
|
||||
// ------------------------------------------
|
||||
public static RequestResult SetStat(ulong playerId, int statChannel, ulong roomId, int newValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entry = Entries.FindOne(x =>
|
||||
x.PlayerId == (int)playerId &&
|
||||
x.StatChannel == statChannel &&
|
||||
x.RoomId == (long)roomId
|
||||
);
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
entry = new Entry
|
||||
{
|
||||
PlayerId = (int)playerId,
|
||||
StatChannel = statChannel,
|
||||
RoomId = (long)roomId,
|
||||
Score = newValue
|
||||
};
|
||||
Entries.Insert(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.Score = newValue;
|
||||
Entries.Update(entry);
|
||||
}
|
||||
|
||||
RecomputeRanks(statChannel, roomId);
|
||||
return RequestResult.Success;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return RequestResult.InvalidStat;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Recalculate ranks for a specific leaderboard
|
||||
// ------------------------------------------
|
||||
private static void RecomputeRanks(int statChannel, ulong roomId)
|
||||
{
|
||||
var list = Entries.Find(x =>
|
||||
x.StatChannel == statChannel &&
|
||||
x.RoomId == (long)roomId
|
||||
)
|
||||
.OrderByDescending(x => x.Score)
|
||||
.ToList();
|
||||
|
||||
int rank = 1;
|
||||
foreach (var e in list)
|
||||
{
|
||||
e.Rank = rank++;
|
||||
Entries.Update(e);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Get Player Rank
|
||||
// ------------------------------------------
|
||||
public static Entry? GetPlayerRank(ulong playerId, int statChannel, ulong roomId)
|
||||
{
|
||||
return Entries.FindOne(x =>
|
||||
x.PlayerId == (int)playerId &&
|
||||
x.StatChannel == statChannel &&
|
||||
x.RoomId == (long)roomId
|
||||
);
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Nearby Scores Window
|
||||
// ------------------------------------------
|
||||
public static List<Entry> GetNearbyScores(
|
||||
ulong playerId,
|
||||
int statChannel,
|
||||
ulong roomId,
|
||||
int windowSize)
|
||||
{
|
||||
var me = GetPlayerRank(playerId, statChannel, roomId);
|
||||
if (me == null)
|
||||
{
|
||||
return Entries.Find(x =>
|
||||
x.StatChannel == statChannel &&
|
||||
x.RoomId == (long)roomId
|
||||
)
|
||||
.OrderBy(x => x.Rank)
|
||||
.Take(windowSize * 2 + 1)
|
||||
.ToList();
|
||||
|
||||
}
|
||||
|
||||
int start = Math.Max(me.Rank - windowSize, 1);
|
||||
int end = me.Rank + windowSize;
|
||||
|
||||
|
||||
return Entries.Find(x =>
|
||||
x.StatChannel == statChannel &&
|
||||
x.RoomId == (long)roomId &&
|
||||
x.Rank >= start &&
|
||||
x.Rank <= end
|
||||
)
|
||||
.OrderBy(x => x.Rank)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Range Get — GetRanks API
|
||||
// ------------------------------------------
|
||||
public static List<Entry> GetRanks(
|
||||
int statChannel,
|
||||
ulong roomId,
|
||||
int rankStart,
|
||||
int rankEnd)
|
||||
{
|
||||
return Entries.Find(x =>
|
||||
x.StatChannel == statChannel &&
|
||||
x.RoomId == (long)roomId &&
|
||||
x.Rank >= rankStart &&
|
||||
x.Rank <= rankEnd
|
||||
)
|
||||
.OrderBy(x => x.Rank)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using LiteDB;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.dbs
|
||||
{
|
||||
public static class LinkDB
|
||||
{
|
||||
private static readonly string _dbPath = "Data/DBs/Links.db";
|
||||
private static readonly string _collection = "actionlinks";
|
||||
|
||||
public static ActionLink Insert(ActionLink link)
|
||||
{
|
||||
using var db = new LiteDatabase(_dbPath);
|
||||
var col = db.GetCollection<ActionLink>(_collection);
|
||||
col.Insert(link);
|
||||
return link;
|
||||
}
|
||||
|
||||
public static ActionLink? Get(string linkIdOrCreatorCode)
|
||||
{
|
||||
using var db = new LiteDatabase(_dbPath);
|
||||
var col = db.GetCollection<ActionLink>(_collection);
|
||||
|
||||
ActionLink? link = col.FindById(linkIdOrCreatorCode);
|
||||
|
||||
// If not found by ID, try as a creator code (case-insensitive)
|
||||
if (link == null)
|
||||
{
|
||||
string code = linkIdOrCreatorCode.Trim().ToLowerInvariant();
|
||||
var player = PlayerDB.GetInfluencerByCreatorCode(code);
|
||||
return new ActionLink // {"creatorPlayerId":4213153,"data":"{\"Type\":6}","isValid":true}
|
||||
{
|
||||
creatorPlayerId = player.player.Id,
|
||||
data = JsonConvert.SerializeObject(new { Type = (int)LinkType.Influencer }),
|
||||
isValid = true
|
||||
};
|
||||
}
|
||||
|
||||
if (link == null)
|
||||
return null;
|
||||
|
||||
// Automatically invalidate expired links
|
||||
if (link.ExpiresAt < DateTime.UtcNow && link.isValid)
|
||||
{
|
||||
link.isValid = false;
|
||||
col.Update(link);
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
|
||||
public static bool Consume(string linkId, bool alwayValid = false)
|
||||
{
|
||||
using var db = new LiteDatabase(_dbPath);
|
||||
var col = db.GetCollection<ActionLink>(_collection);
|
||||
var link = col.FindById(linkId);
|
||||
if (link == null || !link.isValid || link.ExpiresAt < DateTime.UtcNow)
|
||||
return false;
|
||||
|
||||
if (!alwayValid)
|
||||
{
|
||||
link.isValid = false;
|
||||
}
|
||||
col.Update(link);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable<ActionLink> GetAll()
|
||||
{
|
||||
using var db = new LiteDatabase(_dbPath);
|
||||
var col = db.GetCollection<ActionLink>(_collection);
|
||||
return col.FindAll().ToList();
|
||||
}
|
||||
|
||||
public static void DeleteLinksFromPlayerId(ulong playerId)
|
||||
{
|
||||
using var db = new LiteDatabase(_dbPath);
|
||||
var col = db.GetCollection<ActionLink>(_collection);
|
||||
try
|
||||
{
|
||||
col.DeleteMany(x => x.creatorPlayerId == playerId);
|
||||
Console.WriteLine("deleted links from playerid " + playerId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2025
File diff suppressed because it is too large
Load Diff
+1218
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
using LiteDB;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.dbs
|
||||
{
|
||||
public class SettingsDB
|
||||
{
|
||||
private static readonly string _dbPath = Path.Combine(Environment.CurrentDirectory, "Data", "DBs", "Settings.db");
|
||||
private static readonly LiteDatabase _db = new LiteDatabase(_dbPath);
|
||||
|
||||
private static ILiteCollection<PlayerSetting> PlayerSettings => _db.GetCollection<PlayerSetting>("players_settings");
|
||||
|
||||
public static List<Setting> LoadSettings(ulong playerId)
|
||||
{
|
||||
return PlayerSettings.FindById(playerId)?.Settings ?? new List<Setting>();
|
||||
}
|
||||
|
||||
public static void SaveSettings(List<Setting> settings, ulong playerId)
|
||||
{
|
||||
if (playerId == 0) return;
|
||||
|
||||
var player = new PlayerSetting
|
||||
{
|
||||
PlayerId = playerId,
|
||||
Settings = settings
|
||||
};
|
||||
|
||||
PlayerSettings.Upsert(player); // ✅ SAFE
|
||||
}
|
||||
|
||||
|
||||
public static void SetPlayerSetting(string key, string value, ulong playerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key) || playerId == 0)
|
||||
return;
|
||||
|
||||
if (key is "SplitTestAssignedSegments" or "Growth.LastEmailPromptTime")
|
||||
return;
|
||||
|
||||
var col = PlayerSettings;
|
||||
|
||||
var player = col.FindById(playerId) ?? new PlayerSetting
|
||||
{
|
||||
PlayerId = playerId,
|
||||
Settings = CreateDefaultSettings()
|
||||
};
|
||||
|
||||
var setting = player.Settings.FirstOrDefault(s => s.Key == key);
|
||||
if (setting != null)
|
||||
setting.Value = value;
|
||||
else
|
||||
player.Settings.Add(new Setting { Key = key, Value = value });
|
||||
|
||||
col.Upsert(player); // 🔥 KEY LINE
|
||||
}
|
||||
|
||||
public static void DeletePlayerKey(string key, ulong playerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key) || playerId == 0)
|
||||
return;
|
||||
|
||||
var col = PlayerSettings;
|
||||
|
||||
var player = col.FindById(playerId);
|
||||
if (player == null || player.Settings == null)
|
||||
return;
|
||||
|
||||
player.Settings.RemoveAll(s => s.Key == key);
|
||||
|
||||
col.Upsert(player);
|
||||
}
|
||||
|
||||
public static string? GetPlayerSettingValue(string key, ulong playerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key) || playerId == 0)
|
||||
return null;
|
||||
|
||||
// ignored keys
|
||||
if (key == "SplitTestAssignedSegments")
|
||||
return null;
|
||||
|
||||
if (key == "Growth.LastEmailPromptTime")
|
||||
return null;
|
||||
|
||||
var settings = LoadSettings(playerId);
|
||||
var setting = settings.Find(s => s.Key == key);
|
||||
|
||||
return setting?.Value;
|
||||
}
|
||||
|
||||
public static void SetPlayerSettingFromJson(string jsonData, ulong playerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jsonData) || playerId == 0)
|
||||
return;
|
||||
|
||||
Setting? setting;
|
||||
try
|
||||
{
|
||||
setting = JsonConvert.DeserializeObject<Setting>(jsonData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to parse setting JSON: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (setting == null || string.IsNullOrEmpty(setting.Key) || setting.Key == "SplitTestAssignedSegments")
|
||||
return;
|
||||
|
||||
SetPlayerSetting(setting.Key, setting.Value ?? "", playerId);
|
||||
}
|
||||
|
||||
public static void DeleteSettings(ulong playerId)
|
||||
{
|
||||
if (playerId == 0) return;
|
||||
|
||||
var col = PlayerSettings;
|
||||
col.DeleteMany(x => x.PlayerId == playerId);
|
||||
}
|
||||
|
||||
public static List<Setting> CreateDefaultSettings() => new List<Setting>
|
||||
{
|
||||
new Setting { Key = "MOD_BLOCKED_TIME", Value = "0" },
|
||||
new Setting { Key = "MOD_BLOCKED_DURATION", Value = "0" },
|
||||
new Setting { Key = "PlayerSessionCount", Value = "0" },
|
||||
new Setting { Key = "ShowRoomCenter", Value = "1" },
|
||||
new Setting { Key = "QualitySettings", Value = "1" },
|
||||
new Setting { Key = "Recroom.OOBE", Value = "1" },
|
||||
new Setting { Key = "VoiceFilter", Value = "0" },
|
||||
new Setting { Key = "VIGNETTED_TELEPORT_ENABLED", Value = "0" },
|
||||
new Setting { Key = "CONTINUOUS_ROTATION_MODE", Value = "0" },
|
||||
new Setting { Key = "ROTATION_INCREMENT", Value = "0" },
|
||||
new Setting { Key = "ROTATE_IN_PLACE_ENABLED", Value = "0" },
|
||||
new Setting { Key = "TeleportBuffer", Value = "0" },
|
||||
new Setting { Key = "VoiceChat", Value = "1" },
|
||||
new Setting { Key = "PersonalBubble", Value = "0" },
|
||||
new Setting { Key = "ShowNames", Value = "1" },
|
||||
new Setting { Key = "H.264 plugin", Value = "1" },
|
||||
new Setting { Key = "Recroom.AccountCreation.HasStarted", Value = "true" },
|
||||
new Setting { Key = "Recroom.AccountCreation.HasFinished", Value = "false" },
|
||||
new Setting { Key = "Recroom.AccountCreation.HasChosenUsername", Value = "false" },
|
||||
new Setting { Key = "Recroom.AccountCreation.HasCreatedPassword", Value = "false" },
|
||||
new Setting { Key = "TUTORIAL_COMPLETE_MASK", Value = "0" },
|
||||
new Setting { Key = "BACKPACK_FAVORITE_TOOL", Value = "-1" }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Security.Principal;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
using Rec_rewild_live_rewrite.api.server;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
|
||||
public static class AuthHelper
|
||||
{
|
||||
public static (player_info? account, ContentResult? error) ValidateToken(HttpContext httpContext)
|
||||
{
|
||||
string token = httpContext.Request.Headers["Authorization"].ToString();
|
||||
string playerData = ClientSecurity.DecodeToken(token);
|
||||
|
||||
if (string.IsNullOrEmpty(playerData) || playerData.StartsWith("error_"))
|
||||
{
|
||||
return (null, new ContentResult
|
||||
{
|
||||
Content = "",
|
||||
ContentType = "application/json",
|
||||
StatusCode = 401
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
player_info account = JsonConvert.DeserializeObject<player_info>(playerData);
|
||||
return (account, null);
|
||||
}
|
||||
|
||||
public static (player_info? account, ContentResult? error) ValidateTokenDev(HttpContext httpContext)
|
||||
{
|
||||
string? temp2 = httpContext.Request.Cookies["Authorization"];
|
||||
string playerData = ClientSecurity.DecodeToken(temp2);
|
||||
if (string.IsNullOrEmpty(playerData) || playerData.StartsWith("error_"))
|
||||
{
|
||||
return (null, new ContentResult()
|
||||
{
|
||||
Content = "",
|
||||
ContentType = "application/json",
|
||||
StatusCode = 401
|
||||
});
|
||||
}
|
||||
|
||||
player_info account = JsonConvert.DeserializeObject<player_info>(playerData);
|
||||
|
||||
bool flag = PlayerDB.CheckDevFlag(account.Id);
|
||||
if (!flag)
|
||||
return (null, new ContentResult()
|
||||
{
|
||||
Content = "",
|
||||
ContentType = "application/json",
|
||||
StatusCode = 401
|
||||
});
|
||||
|
||||
|
||||
return (account, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Newtonsoft.Json;
|
||||
using LiteDB;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class ActionLink
|
||||
{
|
||||
[BsonId]
|
||||
[JsonIgnore]
|
||||
public string LinkId { get; set; } = Utils.GenerateShortCode(6);
|
||||
public ulong creatorPlayerId { get; set; }
|
||||
public string? data { get; set; }
|
||||
public bool isValid { get; set; }
|
||||
[JsonIgnore]
|
||||
public LinkType CodeType { get; set; }
|
||||
[JsonIgnore]
|
||||
public int ValidHours { get; set; }
|
||||
[JsonIgnore]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
[JsonIgnore]
|
||||
public DateTime ExpiresAt => CreatedAt.AddHours(ValidHours);
|
||||
[JsonIgnore]
|
||||
public int ExtraDataId { get; set; }
|
||||
}
|
||||
|
||||
public enum LinkType
|
||||
{
|
||||
Unknown = -1,
|
||||
Friend, // 0
|
||||
Referral, // 1
|
||||
Meetup, // 2
|
||||
Club, // 3
|
||||
PlayerEvent, // 4
|
||||
Room, // 5
|
||||
Influencer, // 6
|
||||
Photo // 7
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class Amplitude2023
|
||||
{
|
||||
public string? AmplitudeKey { get; set; }
|
||||
public bool? UseRudderStack { get; set; }
|
||||
public string? RudderStackKey { get; set; }
|
||||
public bool? UseStatSig { get; set; }
|
||||
public string? StatSigKey { get; set; }
|
||||
public int? StatSigEnvironment { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class Config
|
||||
{
|
||||
public string MessageOfTheDay { get; set; }
|
||||
public string CdnBaseUri { get; set; }
|
||||
public string ShareBaseUrl { get; set; }
|
||||
public List<LevelProgressionMap> LevelProgressionMaps { get; set; }
|
||||
public MatchmakingParams MatchmakingParams { get; set; }
|
||||
public List<List<DailyObjective>> DailyObjectives { get; set; }
|
||||
public ServerMaintenance ServerMaintainence { get; set; }
|
||||
public AutoMicMutingConfig AutoMicMutingConfig { get; set; }
|
||||
public StorefrontConfig StorefrontConfig { get; set; }
|
||||
public RoomKeyConfig RoomKeyConfig { get; set; }
|
||||
public RoomCurrencyConfig RoomCurrencyConfig { get; set; }
|
||||
public List<ConfigTableEntry> ConfigTable { get; set; }
|
||||
public PhotonConfig PhotonConfig { get; set; }
|
||||
}
|
||||
|
||||
public class LevelProgressionMap
|
||||
{
|
||||
public int Level { get; set; }
|
||||
public int RequiredXp { get; set; }
|
||||
public int? GiftDropId { get; set; }
|
||||
}
|
||||
|
||||
public class MatchmakingParams
|
||||
{
|
||||
public double PreferFullRoomsFrequency { get; set; }
|
||||
public double PreferEmptyRoomsFrequency { get; set; }
|
||||
}
|
||||
|
||||
public class DailyObjective
|
||||
{
|
||||
public int Type { get; set; }
|
||||
public int Score { get; set; }
|
||||
public int Xp { get; set; }
|
||||
}
|
||||
|
||||
public class ServerMaintenance
|
||||
{
|
||||
public int StartsInMinutes { get; set; }
|
||||
}
|
||||
|
||||
public class AutoMicMutingConfig
|
||||
{
|
||||
public double MicSpamVolumeThreshold { get; set; }
|
||||
public double MicVolumeSampleInterval { get; set; }
|
||||
public double MicVolumeSampleRollingWindowLength { get; set; }
|
||||
public double MicSpamSamplePercentageForWarning { get; set; }
|
||||
public double MicSpamSamplePercentageForWarningToEnd { get; set; }
|
||||
public double MicSpamSamplePercentageForForceMute { get; set; }
|
||||
public double MicSpamSamplePercentageForForceMuteToEnd { get; set; }
|
||||
public double MicSpamWarningStateVolumeMultiplier { get; set; }
|
||||
}
|
||||
|
||||
public class StorefrontConfig
|
||||
{
|
||||
public int MinPlayerLevelForGifting { get; set; }
|
||||
}
|
||||
|
||||
public class RoomKeyConfig
|
||||
{
|
||||
public int MaxKeysPerRoom { get; set; }
|
||||
}
|
||||
|
||||
public class RoomCurrencyConfig
|
||||
{
|
||||
public double AwardCurrencyCooldownSeconds { get; set; }
|
||||
}
|
||||
|
||||
public class ConfigTableEntry
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public class PhotonConfig
|
||||
{
|
||||
public string CloudRegion { get; set; }
|
||||
public bool CrcCheckEnabled { get; set; }
|
||||
public bool EnableServerTracingAfterDisconnect { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class CustomAvatarItems
|
||||
{
|
||||
public List<object>? Results { get; set; }
|
||||
public int TotalResults { get; set; }
|
||||
}
|
||||
|
||||
public class CreateCustomAvatarItemResponse
|
||||
{
|
||||
// {"name":"eeeeeeeeeeeeeeeeeeeee","Description":"eawwwwwwwwwwweweawewaewa","Price":1000,"BaseAvatarItemId":2184,"BaseAvatarItemColor":"#F55C1A","Accessibility":1}
|
||||
public string? Name { get; set; } // Custom shirt name
|
||||
public string? Description { get; set; } // Custom shirt description
|
||||
public int Price { get; set; } // This range from 1000 to 10,000
|
||||
public int BaseAvatarItemId { get; set; } // the avatar id from the avatar db
|
||||
public string? BaseAvatarItemColor { get; set; } // Hex color code default is #F55C1A
|
||||
public int Accessibility { get; set; } // enum 0 for me only, 1 for everyone
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class Hile
|
||||
{
|
||||
public enum HileTypes
|
||||
{
|
||||
Obscured = 0,
|
||||
Time = 1,
|
||||
Inject = 2,
|
||||
GiftCount = 3,
|
||||
Engine = 4,
|
||||
UnknownDll = 5,
|
||||
ImageSignature = 6,
|
||||
AvatarHack = 7,
|
||||
|
||||
NetworkCertificatePublicKey = 100,
|
||||
NetworkCertificateIssuer = 101,
|
||||
NetworkCertificateMissing = 102,
|
||||
NetworkCertificateMismatch = 103,
|
||||
|
||||
AutosaveChecksumMismatch = 150,
|
||||
AutosaveSubRoomIdMismatch = 151,
|
||||
AutosaveChecksumException = 152,
|
||||
|
||||
Photon_MissingHash = 200,
|
||||
Photon_CorruptHash = 201,
|
||||
Photon_DifferentHash = 203,
|
||||
|
||||
AppData_Runtime_LengthMismatch = 300,
|
||||
AppData_Runtime_LastWriteTimeMismatch = 301,
|
||||
AppData_Runtime_FileModified = 302,
|
||||
|
||||
AppData_Boot_InvalidSignature = 310,
|
||||
AppData_Boot_UnableToVerifySignatures = 311,
|
||||
|
||||
Config_MissingHash = 320,
|
||||
Config_DifferentHash = 321,
|
||||
|
||||
Photon_InstantiateTool = 400,
|
||||
|
||||
Memory_Hash_Mismatch = 500,
|
||||
|
||||
Driver_Invalid_Signature = 600
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class LockedItems
|
||||
{
|
||||
public List<string> AvatarItemDescriptions { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class MyObjectives
|
||||
{
|
||||
public List<object> Objectives { get; set; } = new List<object>();
|
||||
public List<object> ObjectiveGroups { get; set; } = new List<object>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class NS
|
||||
{
|
||||
public string Accounts { get; set; } = "";
|
||||
public string API { get; set; } = "";
|
||||
public string Auth { get; set; } = "";
|
||||
public string BugReporting { get; set; } = "";
|
||||
public string Cards { get; set; } = "";
|
||||
public string CDN { get; set; } = "";
|
||||
public string Chat { get; set; } = "";
|
||||
public string Clubs { get; set; } = "";
|
||||
public string CMS { get; set; } = "";
|
||||
public string Commerce { get; set; } = "";
|
||||
public string Data { get; set; } = "";
|
||||
public string DataCollection { get; set; } = "";
|
||||
public string Discovery { get; set; } = "";
|
||||
public string Econ { get; set; } = "";
|
||||
public string GameLogs { get; set; } = "";
|
||||
public string Geo { get; set; } = "";
|
||||
public string Images { get; set; } = "";
|
||||
public string Leaderboard { get; set; } = "";
|
||||
public string Link { get; set; } = "";
|
||||
public string Lists { get; set; } = "";
|
||||
public string Matchmaking { get; set; } = "";
|
||||
public string Moderation { get; set; } = "";
|
||||
public string Notifications { get; set; } = "";
|
||||
public string PlatformNotifications { get; set; } = "";
|
||||
public string PlayerSettings { get; set; } = "";
|
||||
public string RoomComments { get; set; } = "";
|
||||
public string Rooms { get; set; } = "";
|
||||
public string Storage { get; set; } = "";
|
||||
public string Strings { get; set; } = "";
|
||||
public string StringsCDN { get; set; } = "";
|
||||
public string Studio { get; set; } = "";
|
||||
public string Thorn { get; set; } = "";
|
||||
public string Videos { get; set; } = "";
|
||||
public string WWW { get; set; } = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class Progress
|
||||
{
|
||||
public int Level { get; set; }
|
||||
public ulong PlayerId { get; set; }
|
||||
public int XP { get; set; }
|
||||
|
||||
public static Progress GetPlayerLevel(ulong player_id)
|
||||
{
|
||||
var account = PlayerDB.GetFullAccount(player_id);
|
||||
if (account == null || account.player == null)
|
||||
{
|
||||
return new Progress
|
||||
{
|
||||
Level = 0,
|
||||
PlayerId = player_id,
|
||||
XP = 0
|
||||
};
|
||||
}
|
||||
|
||||
return new Progress
|
||||
{
|
||||
Level = account.player.Level,
|
||||
PlayerId = account.player.Id,
|
||||
XP = account.player.XP
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class RRPlus
|
||||
{
|
||||
public class SubscriptionResponse
|
||||
{
|
||||
[JsonPropertyOrder(1)]
|
||||
public Subscription subscription { get; set; }
|
||||
|
||||
[JsonPropertyOrder(2)]
|
||||
public ulong? platformAccountSubscribedPlayerId { get; set; }
|
||||
}
|
||||
|
||||
public class Subscription
|
||||
{
|
||||
[JsonPropertyOrder(1)] public int subscriptionId { get; set; }
|
||||
[JsonPropertyOrder(2)] public ulong recNetPlayerId { get; set; }
|
||||
[JsonPropertyOrder(3)] public int platformType { get; set; }
|
||||
[JsonPropertyOrder(4)] public string platformId { get; set; }
|
||||
[JsonPropertyOrder(5)] public string platformPurchaseId { get; set; }
|
||||
[JsonPropertyOrder(6)] public object? @event { get; set; }
|
||||
[JsonPropertyOrder(7)] public int type { get; set; }
|
||||
[JsonPropertyOrder(8)] public int level { get; set; }
|
||||
[JsonPropertyOrder(9)] public int period { get; set; }
|
||||
[JsonPropertyOrder(10)] public DateTime expirationDate { get; set; }
|
||||
[JsonPropertyOrder(11)] public bool isAutoRenewing { get; set; }
|
||||
[JsonPropertyOrder(12)] public DateTime createdAt { get; set; }
|
||||
[JsonPropertyOrder(13)] public DateTime modifiedAt { get; set; }
|
||||
}
|
||||
|
||||
public enum SubscriptionLevel
|
||||
{
|
||||
Gold,
|
||||
Platinum,
|
||||
}
|
||||
|
||||
public enum SubscriptionPeriod
|
||||
{
|
||||
Month,
|
||||
Year,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class Rep
|
||||
{
|
||||
public ulong AccountId { get; set; }
|
||||
public bool IsCheerful { get; set; }
|
||||
public int Noteriety { get; set; }
|
||||
public CheerCategoryEnum SelectedCheer { get; set; }
|
||||
public int CheerCredit { get; set; }
|
||||
public int CheerGeneral { get; set; }
|
||||
public int CheerHelpful { get; set; }
|
||||
public int CheerCreative { get; set; }
|
||||
public int CheerGreatHost { get; set; }
|
||||
public int CheerSportsman { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class Sanitize
|
||||
{
|
||||
public string Context { get; set; } = "RoomChat";
|
||||
public int Intent { get; set; } = 1;
|
||||
public bool PreRemoveBlockedCharacters { get; set; } = true;
|
||||
public string ReplacementChar { get; set; } = "*";
|
||||
public string Value { get; set; } = "";
|
||||
public int ruleset { get; set; } = 0;
|
||||
|
||||
private const char MaskChar = '*';
|
||||
|
||||
|
||||
// Words that should never be censored
|
||||
private static readonly HashSet<string> Whitelist = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"password",
|
||||
"pass",
|
||||
"class",
|
||||
"assistant",
|
||||
"account"
|
||||
};
|
||||
|
||||
// Compiled swear patterns
|
||||
private static readonly List<(string Word, Regex Pattern)> SwearPatterns;
|
||||
|
||||
static Sanitize()
|
||||
{
|
||||
var swears = File.ReadAllLines("Data/badwords.txt")
|
||||
.Select(x => x.Trim().ToLowerInvariant())
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
SwearPatterns = swears
|
||||
.Select(word => (word, BuildFuzzyRegex(word)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------
|
||||
* PUBLIC API
|
||||
* --------------------------------------------------*/
|
||||
|
||||
public static bool ContainsSwears(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return false;
|
||||
|
||||
if (ConfigRewild.AllowSwears)
|
||||
return false;
|
||||
|
||||
var normalized = Normalize(message);
|
||||
|
||||
return SwearPatterns.Any(p => p.Pattern.IsMatch(normalized));
|
||||
}
|
||||
|
||||
public static string SanitizeMessage(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return message;
|
||||
|
||||
var tokens = Regex.Split(message, @"(\W+)");
|
||||
var sb = new StringBuilder(message.Length);
|
||||
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
// Not a word (punctuation, spaces, etc.)
|
||||
if (!IsWord(token))
|
||||
{
|
||||
sb.Append(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
var normalized = Normalize(token);
|
||||
|
||||
// Ignore very short words
|
||||
if (normalized.Length < 3)
|
||||
{
|
||||
sb.Append(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Whitelist check (normalized!)
|
||||
if (Whitelist.Contains(normalized))
|
||||
{
|
||||
sb.Append(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool replaced = false;
|
||||
|
||||
foreach (var (_, pattern) in SwearPatterns)
|
||||
{
|
||||
// IMPORTANT: whole-word match only
|
||||
if (pattern.IsMatch(normalized))
|
||||
{
|
||||
sb.Append(new string(MaskChar, token.Length));
|
||||
Console.WriteLine($"[Sanitize] Censored word in message: '{token}'");
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!replaced)
|
||||
sb.Append(token);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------
|
||||
* INTERNAL HELPERS
|
||||
* --------------------------------------------------*/
|
||||
|
||||
private static bool IsWord(string input) =>
|
||||
input.Any(char.IsLetterOrDigit);
|
||||
|
||||
private static Regex BuildFuzzyRegex(string word)
|
||||
{
|
||||
// Example: f.{0,1}u.{0,1}c.{0,1}k
|
||||
var pattern = string.Join(".{0,1}",
|
||||
word.Select(c => Regex.Escape(c.ToString())));
|
||||
|
||||
// Force FULL match only
|
||||
return new Regex(
|
||||
$"^{pattern}$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled
|
||||
);
|
||||
}
|
||||
|
||||
private static string Normalize(string input)
|
||||
{
|
||||
input = input.ToLowerInvariant();
|
||||
|
||||
input = Regex.Replace(input, @"[0@]", "o");
|
||||
input = Regex.Replace(input, @"[1!|]", "i");
|
||||
input = Regex.Replace(input, @"[$5]", "s");
|
||||
input = Regex.Replace(input, @"3", "e");
|
||||
input = Regex.Replace(input, @"7", "t");
|
||||
|
||||
// Remove non letters
|
||||
input = Regex.Replace(input, @"[^a-z]", "");
|
||||
|
||||
// Collapse repeats (fuuuuuck → fuuck)
|
||||
input = Regex.Replace(input, @"(.)\1{2,}", "$1$1");
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class SteamAuthResponse
|
||||
{
|
||||
public SteamResponse? response { get; set; }
|
||||
}
|
||||
|
||||
public class SteamResponse
|
||||
{
|
||||
[JsonProperty("params")]
|
||||
public SteamParams? paramsData { get; set; }
|
||||
}
|
||||
|
||||
public class SteamParams
|
||||
{
|
||||
public string? result { get; set; }
|
||||
public string? steamid { get; set; }
|
||||
public string? ownersteamid { get; set; }
|
||||
public bool vacbanned { get; set; }
|
||||
public bool publisherbanned { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class Sub
|
||||
{
|
||||
public ulong creatorAccountId { get; set; }
|
||||
public ulong clubId { get; set; }
|
||||
public int membershipType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class SuccessResponse
|
||||
{
|
||||
public bool success { get; set; }
|
||||
|
||||
public object? value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes
|
||||
{
|
||||
public class VersionCheck
|
||||
{
|
||||
public VersionStatus ValidVersion { get; set; }
|
||||
public VersionStatus VersionStatus { get; set; }
|
||||
public UpdateNoti UpdateNotificationStage { get; set; }
|
||||
public bool IsVersionIslanded { get; set; }
|
||||
public bool IsCrossPlayDisabled { get; set; }
|
||||
}
|
||||
|
||||
public enum VersionStatus
|
||||
{
|
||||
ValidForPlay,
|
||||
UpdateRequired
|
||||
}
|
||||
|
||||
public enum UpdateNoti
|
||||
{
|
||||
None,
|
||||
Silent,
|
||||
Warn,
|
||||
Prompt,
|
||||
Require
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes.db_class
|
||||
{
|
||||
public class ChatDBClasses
|
||||
{
|
||||
public class ChatThread
|
||||
{
|
||||
public List<ChatMessage> Messages { get; set; }
|
||||
public long ChatThreadId { get; set; }
|
||||
public List<ulong> PlayerIds { get; set; }
|
||||
public long LastReadMessageId { get; set; }
|
||||
public string ChatThreadName { get; set; }
|
||||
public DateTime? SnoozedUntil { get; set; }
|
||||
public bool IsFavorited { get; set; }
|
||||
}
|
||||
|
||||
public class ChatMessage
|
||||
{
|
||||
public long ChatMessageId { get; set; }
|
||||
public long ChatThreadId { get; set; }
|
||||
public int SenderPlayerId { get; set; }
|
||||
public DateTime TimeSent { get; set; }
|
||||
public string Contents { get; set; }
|
||||
public int ModerationState { get; set; } // todo find the enum
|
||||
}
|
||||
|
||||
public class MessageJson
|
||||
{
|
||||
public MessageContentType Type { get; set; }
|
||||
public int Version { get; set; } // default ver is 2
|
||||
public string Data { get; set; }
|
||||
}
|
||||
|
||||
public struct SendMessageResponse
|
||||
{
|
||||
public ChatResults ChatResult { get; set; }
|
||||
public ChatThread ChatThread { get; set; }
|
||||
}
|
||||
|
||||
public class ChatPrivacySettings
|
||||
{
|
||||
public ulong playerId { get; set; }
|
||||
public ChatPrivacy directMessagePrivacySetting { get; set; } = ChatPrivacy.Friends;
|
||||
public ChatPrivacy groupChatPrivacySetting { get; set; } = ChatPrivacy.Friends;
|
||||
}
|
||||
|
||||
public enum ChatResults
|
||||
{
|
||||
Success,
|
||||
InvalidArguments,
|
||||
ThreadNotFound,
|
||||
MembershipNotFound,
|
||||
PlayerAlreadyOnThread,
|
||||
CannotMessagePlayer,
|
||||
InvalidCharacters,
|
||||
RecentlyLeftThread,
|
||||
ThreadTooLarge
|
||||
}
|
||||
|
||||
public enum ModerationState : byte
|
||||
{
|
||||
Active,
|
||||
Junior_Pending = 11,
|
||||
Moderation_Pending = 100,
|
||||
Moderation_Closed,
|
||||
Moderation_Banned,
|
||||
MarkedForDelete = 255
|
||||
}
|
||||
|
||||
public enum QueryMode
|
||||
{
|
||||
Latest,
|
||||
NewerThan,
|
||||
OlderThan
|
||||
}
|
||||
|
||||
public enum MessageContentType
|
||||
{
|
||||
Text,
|
||||
PartyInvite,
|
||||
Photo
|
||||
}
|
||||
|
||||
public enum ChatPrivacy
|
||||
{
|
||||
Friends,
|
||||
Favorites,
|
||||
NoOne
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes.db_class
|
||||
{
|
||||
public class ClubDBClasses
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using LiteDB;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes.db_class
|
||||
{
|
||||
public class EventDBClasses
|
||||
{
|
||||
public class CreateEventRequest
|
||||
{
|
||||
// {"RoomId":708,"SubRoomId":1444,"ClubId":null,"name":"awdadwdawdadwdawdadwd","Description":"awdadwdawdadwdawdadwdawdadwdawdadwdawdadwdawdadwdawdadwdawdadwdawdadwd","Tags":["grandopening"],"ImageName":"J9W8A0FUGWXG4CMBKFDJ46CUZHJOHP.png","StartTime":"2025-12-13T14:20:41.6244242Z","EndTime":"2025-12-13T14:50:41.6244242Z","Accessibility":1,"IsMultiInstance":false,"SupportMultiInstanceRoomChat":false,"DefaultBroadcastPermissions":0,"CanRequestBroadcastPermissions":0}
|
||||
public ulong RoomId { get; set; }
|
||||
public ulong? SubRoomId { get; set; }
|
||||
public ulong? ClubId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public List<string>? Tags { get; set; }
|
||||
public string? ImageName { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public DateTime EndTime { get; set; }
|
||||
public PlayerEventAccessibility Accessibility { get; set; }
|
||||
public bool IsMultiInstance { get; set; } // this is just multi instance room
|
||||
public bool SupportMultiInstanceRoomChat { get; set; } // this let player talk betweeen room instance using room chat
|
||||
public int DefaultBroadcastPermissions { get; set; } // idk what this is used for
|
||||
public int CanRequestBroadcastPermissions { get; set; } // idk what this is used for
|
||||
}
|
||||
|
||||
public class FullPlayerEvent
|
||||
{
|
||||
public List<FullTag>? Tags { get; set; }
|
||||
public List<FullEventResponses>? Responses { get; set; }
|
||||
[BsonId]
|
||||
public ulong PlayerEventId { get; set; }
|
||||
public ulong? CreatorPlayerId { get; set; }
|
||||
public string? ImageName { get; set; }
|
||||
public ulong RoomId { get; set; }
|
||||
public ulong? SubRoomId { get; set; }
|
||||
public ulong? ClubId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public DateTime EndTime { get; set; }
|
||||
public int AttendeeCount { get; set; }
|
||||
public int State { get; set; }
|
||||
public PlayerEventAccessibility Accessibility { get; set; }
|
||||
public bool IsMultiInstance { get; set; } // this is just multi instance room
|
||||
public bool SupportMultiInstanceRoomChat { get; set; } // this let player talk betweeen room instance using room chat
|
||||
public BroadcastPermissionsEnum DefaultBroadcastPermissions { get; set; } // idk what this is used for
|
||||
public BroadcastPermissionsEnum CanRequestBroadcastPermissions { get; set; } // idk what this is used for
|
||||
public ulong BroadcastingRoomInstanceId { get; set; }
|
||||
public DateTime? RecurrenceSchedule { get; set; }
|
||||
}
|
||||
|
||||
public class FullTag
|
||||
{
|
||||
public string? Tag { get; set; }
|
||||
public TagType Type { get; set; }
|
||||
}
|
||||
|
||||
public class FullEventResponses
|
||||
{
|
||||
public ulong PlayerEventResponseId { get; set; }
|
||||
public ulong PlayerEventId { get; set; }
|
||||
public ulong PlayerId { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public PlayerEventResponseType Type { get; set; } = PlayerEventResponseType.None;
|
||||
}
|
||||
|
||||
public enum PlayerEventResponseType
|
||||
{
|
||||
None = -1,
|
||||
Yes,
|
||||
Interested,
|
||||
No,
|
||||
Pending
|
||||
}
|
||||
|
||||
public enum BroadcastPermissionsEnum
|
||||
{
|
||||
None,
|
||||
RoomOwners = 256,
|
||||
All = 2147483647
|
||||
}
|
||||
|
||||
public enum PlayerEventAccessibility
|
||||
{
|
||||
Private,
|
||||
Public,
|
||||
Unlisted
|
||||
}
|
||||
|
||||
public enum TagType
|
||||
{
|
||||
Unknown1 = 0,
|
||||
Unknown2 = 1,
|
||||
Unknown3 = 2
|
||||
}
|
||||
|
||||
public enum EventResults
|
||||
{
|
||||
Success,
|
||||
HasModeratorClosedEvent,
|
||||
DoesNotExist,
|
||||
PlayerDoesNotExist,
|
||||
RoomDoesNotExist,
|
||||
StatusUnchanged,
|
||||
PrivateEvent,
|
||||
SomethingWentWrong,
|
||||
DoesNotOwnRoom,
|
||||
ResponseDoesNotExist,
|
||||
PlayerAlreadyInvited,
|
||||
EventDatesInvalid,
|
||||
EventTooLong,
|
||||
EventTooShort,
|
||||
InappropriateName,
|
||||
InappropriateDescription,
|
||||
SomeInvitesFailed,
|
||||
CannotInviteJunior,
|
||||
EventCountLimitReached,
|
||||
DoesNotOwnEvent,
|
||||
UnregisteredOrJuniorNotAllowed,
|
||||
InvalidClubPermissions,
|
||||
ImageDoesNotExist,
|
||||
SubRoomDoesNotExist,
|
||||
DoesNotOwnSubRoom,
|
||||
ModifyTagsFailed,
|
||||
RoomCapacityTooLow,
|
||||
BroadcastEventNotMultiInstance,
|
||||
PlayerNotAllowedToCreateMultiInstanceEvents,
|
||||
PlayerBannedFromEventCreation,
|
||||
EventIsModerationClosed,
|
||||
EventIsModerationPendingReview
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using LiteDB;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes.db_class
|
||||
{
|
||||
public class YourFriendData
|
||||
{
|
||||
[BsonId]
|
||||
public ObjectId? Id { get; set; }
|
||||
public ulong PlayerID { get; set; }
|
||||
public RelationshipDetail Relationship { get; set; }
|
||||
}
|
||||
|
||||
public class FriendData
|
||||
{
|
||||
[BsonId]
|
||||
public ObjectId? Id { get; set; }
|
||||
public string? FriendId { get; set; }
|
||||
public RelationshipType Relationship { get; set; }
|
||||
public FriendInfo FriendFlags { get; set; }
|
||||
public ReciprocalStatus Status { get; set; }
|
||||
}
|
||||
|
||||
public class RelationshipDetail
|
||||
{
|
||||
public ulong Id { get; set; }
|
||||
public ulong PlayerID { get; set; }
|
||||
public ulong OtherPlayerID { get; set; }
|
||||
public RelationshipType RelationshipType { get; set; }
|
||||
public ReciprocalStatus Muted { get; set; }
|
||||
public ReciprocalStatus Ignored { get; set; }
|
||||
public ReciprocalStatus Favorited { get; set; }
|
||||
public int VoiceVolume { get; set; } = 100;
|
||||
}
|
||||
|
||||
public enum RelationshipType
|
||||
{
|
||||
None,
|
||||
FriendRequestSent,
|
||||
FriendRequestReceived,
|
||||
Friend,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FriendInfo
|
||||
{
|
||||
None = 0,
|
||||
Favorited = 1,
|
||||
Muted = 2,
|
||||
Ignored = 4,
|
||||
Blocked = 5
|
||||
}
|
||||
|
||||
public enum ReciprocalStatus
|
||||
{
|
||||
None,
|
||||
Local,
|
||||
Remote,
|
||||
Mutual
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes.db_class
|
||||
{
|
||||
public class LeaderboardDBClasses
|
||||
{
|
||||
public class GetNearbyScoresRequest
|
||||
{
|
||||
// {"PlayerId":2,"StatChannel":2,"RoomId":29,"FilterType":1,"SortAscending":false,"WindowSize":10}
|
||||
public ulong PlayerId { get; set; }
|
||||
public int StatChannel { get; set; }
|
||||
public ulong RoomId { get; set; }
|
||||
public FilterTypeEnum FilterType { get; set; }
|
||||
public bool SortAscending { get; set; }
|
||||
public int WindowSize { get; set; }
|
||||
}
|
||||
|
||||
public class GetRanksRequest
|
||||
{
|
||||
// {"PlayerId":2,"StatChannel":2,"RoomId":29,"FilterType":0,"SortAscending":false,"RankStart":0,"RankEnd":9}
|
||||
public ulong PlayerId { get; set; }
|
||||
public int StatChannel { get; set; }
|
||||
public ulong RoomId { get; set; }
|
||||
public FilterTypeEnum FilterType { get; set; }
|
||||
public bool SortAscending { get; set; }
|
||||
public int RankStart { get; set; } // skip
|
||||
public int RankEnd { get; set; } // take
|
||||
}
|
||||
|
||||
public class CheckAndSetStatRequest
|
||||
{
|
||||
// {"StatChannel":2,"RoomId":29,"StatValue":1,"CurrentStatValue":null}
|
||||
public int StatChannel { get; set; }
|
||||
public ulong RoomId { get; set; }
|
||||
public int StatValue { get; set; }
|
||||
public int? CurrentStatValue { get; set; }
|
||||
}
|
||||
|
||||
public class Entry
|
||||
{
|
||||
[JsonIgnore]
|
||||
public int Id { get; set; } // LiteDB PK
|
||||
|
||||
public int PlayerId { get; set; }
|
||||
[JsonIgnore]
|
||||
public int StatChannel { get; set; }
|
||||
[JsonIgnore]
|
||||
public long RoomId { get; set; }
|
||||
|
||||
public int Score { get; set; }
|
||||
public int Rank { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class SingleLeaderboard
|
||||
{
|
||||
public List<Entry> Rows { get; set; }
|
||||
}
|
||||
|
||||
public class FullLeaderboard
|
||||
{
|
||||
public List<Entry> GlobalOverall { get; set; }
|
||||
public List<Entry> GlobalPeriodic { get; set; }
|
||||
public List<Entry> FriendsOverall { get; set; }
|
||||
public List<Entry> FriendsPeriodic { get; set; }
|
||||
public DateTime NextResetUTC { get; set; }
|
||||
}
|
||||
|
||||
public enum RequestResult
|
||||
{
|
||||
Success,
|
||||
InvalidStat,
|
||||
RedisConnectionError
|
||||
}
|
||||
|
||||
public enum FilterType
|
||||
{
|
||||
Global,
|
||||
Friends
|
||||
}
|
||||
|
||||
public enum Timeframe
|
||||
{
|
||||
AllTime,
|
||||
TwoWeeks
|
||||
}
|
||||
|
||||
public enum FilterTypeEnum
|
||||
{
|
||||
NearbyScoresGlobal,
|
||||
NearbyScoresFriends,
|
||||
Champions,
|
||||
NUM_VALUES // wat
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using LiteDB;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes.db_class
|
||||
{
|
||||
public class account_data
|
||||
{
|
||||
[JsonPropertyOrder(1)]
|
||||
public ulong accountId { get; set; }
|
||||
[JsonPropertyOrder(2)]
|
||||
public string? username { get; set; }
|
||||
[JsonPropertyOrder(3)]
|
||||
public string? displayName { get; set; }
|
||||
[JsonPropertyOrder(4)]
|
||||
public string? profileImage { get; set; }
|
||||
[JsonPropertyOrder(5)]
|
||||
public bool? isJunior { get; set; }
|
||||
[JsonPropertyOrder(6)]
|
||||
public long platforms { get; set; }
|
||||
[JsonPropertyOrder(7)]
|
||||
public long personalPronouns { get; set; }
|
||||
[JsonPropertyOrder(8)]
|
||||
public long identityFlags { get; set; }
|
||||
[JsonPropertyOrder(9)]
|
||||
public DateTime createdAt { get; set; }
|
||||
[JsonPropertyOrder(10)]
|
||||
public bool isMetaPlatformBlocked { get; set; } = false;
|
||||
}
|
||||
public class setting_data
|
||||
{
|
||||
public ulong accountId { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Val { get; set; }
|
||||
}
|
||||
|
||||
public class player_data
|
||||
{
|
||||
public ulong id { get; set; }
|
||||
public ulong playerid { get; set; }
|
||||
public Platforms platform { get; set; }
|
||||
public List<string>? deviceId { get; set; }
|
||||
public string? authtoken { get; set; }
|
||||
public string? password { get; set; }
|
||||
public player_info? player { get; set; }
|
||||
public player_extra? player_Extra { get; set; }
|
||||
public DevFlag dev_Flag { get; set; }
|
||||
}
|
||||
|
||||
public enum Platforms
|
||||
{
|
||||
All = -1,
|
||||
Steam,
|
||||
Oculus,
|
||||
PlayStation,
|
||||
Xbox,
|
||||
RecNet,
|
||||
IOS,
|
||||
GooglePlay,
|
||||
Standalone
|
||||
}
|
||||
|
||||
public class player_extra
|
||||
{
|
||||
public ulong? DormRoomID { get; set; }
|
||||
public List<string>? IpAddresses { get; set; }
|
||||
public ulong DiscordId { get; set; }
|
||||
public string? Bio { get; set; }
|
||||
public string? avatar { get; set; }
|
||||
public List<avatar_data_saved>? SavedOutfits { get; set; }
|
||||
public Moderation_Detail.ModerationBlockDetails? ModerationBlockDetails { get; set; }
|
||||
public List<ulong>? CheeredRooms { get; set; }
|
||||
public List<ulong>? FavoritedRooms { get; set; }
|
||||
public int sub_count { get; set; } = 0;
|
||||
public List<ulong> SubbedTo { get; set; } = new();
|
||||
public bool IsRecentRoomHistoryVisible { get; set; } = true;
|
||||
public Influencer? Influencer { get; set; }
|
||||
public CustomAvatarItems CustomAvatarItems { get; set; } = new CustomAvatarItems();
|
||||
public MyRoomData MyRoomData { get; set; } = new MyRoomData();
|
||||
public List<MessageData>? Messages { get; set; }
|
||||
public PlayerPhotoTaggingSetting PlayerPhotoTaggingSetting { get; set; } = PlayerPhotoTaggingSetting.Anyone;
|
||||
|
||||
}
|
||||
|
||||
public enum PlayerPhotoTaggingSetting
|
||||
{
|
||||
Anyone,
|
||||
Friends,
|
||||
NoOne
|
||||
}
|
||||
|
||||
public class SetPlayerPhotoTaggingSettingRequest
|
||||
{
|
||||
public PlayerPhotoTaggingSetting Setting { get; set; }
|
||||
}
|
||||
|
||||
public class MyRoomData
|
||||
{
|
||||
public List<string> owned_room_keys { get; set; } = new List<string>();
|
||||
public List<PlayerData> PlayerData { get; set; } = new List<PlayerData>();
|
||||
public List<My_Room_currencies> Roomcurrencies { get; set; } = new List<My_Room_currencies>();
|
||||
}
|
||||
|
||||
public class PlayerData
|
||||
{
|
||||
public ulong RoomId { get; set; }
|
||||
public string Data { get; set; }
|
||||
}
|
||||
|
||||
public class My_Room_currencies
|
||||
{
|
||||
public long RoomId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public long Value { get; set; }
|
||||
}
|
||||
|
||||
public class SubscribedToEntry
|
||||
{
|
||||
public ulong PlayerId { get; set; }
|
||||
}
|
||||
|
||||
public class avatar_data_saved
|
||||
{
|
||||
public ulong Slot { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
public string? ImageName { get; set; }
|
||||
|
||||
public List<CustomAvatarItemEntry>? CustomAvatarItems { get; set; }
|
||||
|
||||
public string? FaceFeatures { get; set; }
|
||||
public string? HairColor { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? SkinColor { get; set; }
|
||||
public string? OutfitSelections { get; set; }
|
||||
public string? OutfitSelectionsV2 { get; set; }
|
||||
public string? PreviewImageName { get; set; }
|
||||
}
|
||||
|
||||
public class CustomAvatarItemEntry
|
||||
{
|
||||
public string? CustomAvatarItemId { get; set; }
|
||||
public int BodyPart { get; set; }
|
||||
}
|
||||
|
||||
public class avatar_data
|
||||
{
|
||||
public string? OutfitSelections { get; set; }
|
||||
public string? FaceFeatures { get; set; }
|
||||
public string? SkinColor { get; set; }
|
||||
public string? HairColor { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class player_info
|
||||
{
|
||||
public ulong Id { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
public string? DisplayEmoji { get; set; }
|
||||
|
||||
public string? Email { get; set; }
|
||||
public int XP { get; set; }
|
||||
public int Level { get; set; }
|
||||
public int pronounFlags { get; set; }
|
||||
public int identityFlags { get; set; }
|
||||
public bool IsJunior { get; set; }
|
||||
public bool AvoidJuniors { get; set; }
|
||||
public mPlayerReputation? PlayerReputation { get; set; }
|
||||
public List<mPlatformID>? PlatformIds { get; set; }
|
||||
public DateTime created_at { get; set; }
|
||||
public DateTime last_login_time { get; set; }
|
||||
public List<ulong> recent_rooms { get; set; } = new List<ulong>();
|
||||
public string? bannerImage { get; set; }
|
||||
public string? profileImage { get; set; }
|
||||
}
|
||||
|
||||
public class AccountLogin
|
||||
{
|
||||
public DateTime createdAt { get; set; }
|
||||
public DateTime lastLoginTime { get; set; }
|
||||
public ulong accountId { get; set; }
|
||||
public Platforms platform { get; set; }
|
||||
public string? platformId { get; set; }
|
||||
}
|
||||
|
||||
public class mPlatformID
|
||||
{
|
||||
public Platforms Platform { get; set; }
|
||||
public ulong PlatformId { get; set; }
|
||||
}
|
||||
|
||||
public class mPlayerReputation
|
||||
{
|
||||
public bool IsCheerful { get; set; }
|
||||
public int Noteriety { get; set; }
|
||||
public int CheerGeneral { get; set; }
|
||||
public int CheerHelpful { get; set; }
|
||||
public int CheerGreatHost { get; set; }
|
||||
public int CheerSportsman { get; set; }
|
||||
public int CheerCreative { get; set; }
|
||||
public int CheerCredit { get; set; }
|
||||
public int SubscriberCount { get; set; }
|
||||
public int SubscribedCount { get; set; }
|
||||
public CheerCategoryEnum SelectedCheer { get; set; }
|
||||
}
|
||||
|
||||
public class DeleteMessagesRequest
|
||||
{
|
||||
public List<ulong> MessageIds { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public enum CheerCategoryEnum
|
||||
{
|
||||
None = -1,
|
||||
General = 0,
|
||||
Helpful = 10,
|
||||
Sportmanship = 20,
|
||||
GreatHost = 30,
|
||||
Creative = 40
|
||||
}
|
||||
|
||||
public class account_data_web
|
||||
{
|
||||
public ulong accountId { get; set; }
|
||||
public DateTime createdAt { get; set; }
|
||||
public string? displayName { get; set; }
|
||||
|
||||
public string? displayEmoji { get; set; }
|
||||
public long identityFlags { get; set; }
|
||||
public bool? isJunior { get; set; }
|
||||
public long personalPronouns { get; set; }
|
||||
public long platforms { get; set; }
|
||||
public string? profileImage { get; set; }
|
||||
public string? bannerImage { get; set; }
|
||||
public string? username { get; set; }
|
||||
}
|
||||
|
||||
public class account_data_me
|
||||
{
|
||||
public ulong accountId { get; set; }
|
||||
public int availableusernameChanges { get; set; } = 3;
|
||||
public string? bannerImage { get; set; }
|
||||
public DateTime createdAt { get; set; }
|
||||
|
||||
public string? email { get; set; }
|
||||
public string? displayName { get; set; }
|
||||
|
||||
public string? displayEmoji { get; set; }
|
||||
public long identityFlags { get; set; }
|
||||
|
||||
public bool hasBirthday { get; set; } = true;
|
||||
public string? Phone { get; set; }
|
||||
public bool? isJunior { get; set; }
|
||||
public long personalPronouns { get; set; }
|
||||
public long platforms { get; set; }
|
||||
public string? profileImage { get; set; }
|
||||
public bool TreatAsJunior { get; set; } = false;
|
||||
public string? username { get; set; }
|
||||
}
|
||||
|
||||
public class CustomAvatarItems
|
||||
{
|
||||
public bool isCreationAllowedForAccount { get; set; } = true;
|
||||
public bool isCreationEnabled { get; set; } = true;
|
||||
public bool isRenderingEnabled { get; set; } = true;
|
||||
|
||||
public List<string> ownedItems { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
public class Influencer
|
||||
{
|
||||
public string creatorCode { get; set; } = "";
|
||||
public bool isInfluencer { get; set; } = false;
|
||||
public ulong SupportingInfluencer { get; set; }
|
||||
}
|
||||
|
||||
public class MessageData
|
||||
{
|
||||
/*
|
||||
[
|
||||
{
|
||||
"Id": 15909836337,
|
||||
"FromPlayerId": 64495891,
|
||||
"SentTime": "2025-05-27T11:39:25.12Z",
|
||||
"Type": 200,
|
||||
"Data": "58129955",
|
||||
"RoomId": null,
|
||||
"PlayerEventId": null
|
||||
}
|
||||
]
|
||||
*/
|
||||
public ulong Id { get; set; }
|
||||
public ulong FromPlayerId { get; set; }
|
||||
public DateTime SentTime { get; set; }
|
||||
public WebsocketEvents.MessageType Type { get; set; }
|
||||
public string? Data { get; set; }
|
||||
public ulong? RoomId { get; set; }
|
||||
public ulong? PlayerEventId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public enum DeviceClasses
|
||||
{
|
||||
Unknown,
|
||||
VR,
|
||||
Screen,
|
||||
Mobile,
|
||||
VRLow,
|
||||
Quest2
|
||||
}
|
||||
|
||||
public class account_login<T>
|
||||
{
|
||||
public bool success { get; set; }
|
||||
public string? error { get; set; }
|
||||
[JsonIgnore]
|
||||
public ulong accid { get; set; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DevFlag
|
||||
{
|
||||
none = 0,
|
||||
screenshare = 1,
|
||||
dev = 2,
|
||||
mod = 4,
|
||||
owner = 8,
|
||||
junior = 16,
|
||||
keepsake = 32,
|
||||
newly_created_account = 64,
|
||||
restricted_account = 128,
|
||||
unused_2 = 256,
|
||||
unused_3 = 512,
|
||||
unused_4 = 1024,
|
||||
unused_5 = 2048,
|
||||
unused_6 = 4096,
|
||||
unused_7 = 8192,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
using LiteDB;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes.db_class
|
||||
{
|
||||
public class RoomDBClasses
|
||||
{
|
||||
public class Resultslist<T>
|
||||
{
|
||||
public List<T>? Results { get; set; }
|
||||
public long TotalResults { get; set; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum WarningMaskType
|
||||
{
|
||||
None = 0,
|
||||
Scary = 1,
|
||||
Mature = 2,
|
||||
FlashingLights = 4,
|
||||
IntenseMotion = 8,
|
||||
Violence = 16,
|
||||
Custom = 32,
|
||||
Reports = 64
|
||||
}
|
||||
|
||||
public class RoomRoot
|
||||
{
|
||||
public RoomDBClasses.RoomAccessibility Accessibility { get; set; }
|
||||
public int AgeRating { get; set; }
|
||||
public bool AutoLocalizeRoom { get; set; }
|
||||
public DateTime? BecameRRStudioRoomAt { get; set; }
|
||||
public bool CloningAllowed { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public ulong CreatorAccountId { get; set; }
|
||||
public string? CurrentSnapshotId { get; set; }
|
||||
public bool NeedsSnapshotId { get; set; }
|
||||
public string CustomWarning { get; set; }
|
||||
public string DataBlob { get; set; }
|
||||
// public string DataBlobHash { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool DisableMicAutoMute { get; set; }
|
||||
public bool DisableRoomComments { get; set; }
|
||||
public bool EncryptVoiceChat { get; set; }
|
||||
public bool ExcludeFromLists { get; set; }
|
||||
public bool ExcludeFromSearch { get; set; }
|
||||
public string ImageName { get; set; }
|
||||
public bool IsDeveloperOwned { get; set; }
|
||||
public bool IsDorm { get; set; }
|
||||
public bool IsJuniorCreated { get; set; }
|
||||
public bool IsPlacePlay { get; set; }
|
||||
public bool IsRRO { get; set; }
|
||||
public bool IsRecRoomApproved { get; set; }
|
||||
public bool LoadScreenLocked { get; set; }
|
||||
public List<LoadScreen> LoadScreens { get; set; }
|
||||
public LocalizationContext LocalizationContext { get; set; }
|
||||
public int MaxPlayerCalculationMode { get; set; }
|
||||
public int MaxPlayers { get; set; }
|
||||
public int MinLevel { get; set; }
|
||||
public int? MinUgcSubVersion { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int? PersistenceVersion { get; set; }
|
||||
public List<string> PromoExternalContent { get; set; }
|
||||
public List<string> PromoImages { get; set; }
|
||||
public int PublishState { get; set; }
|
||||
public DateTime PublishedAt { get; set; }
|
||||
public string RankedEntityId { get; set; }
|
||||
public object RankingContext { get; set; }
|
||||
public List<string> RestrictedCircuitsAllowListNames { get; set; }
|
||||
public List<RoleClass> Roles { get; set; }
|
||||
public RoomBanDetails RoomBanDetails { get; set; }
|
||||
[BsonId]
|
||||
public ulong RoomId { get; set; }
|
||||
public RoomState State { get; set; }
|
||||
public Stats Stats { get; set; }
|
||||
public List<SubRoom> SubRooms { get; set; }
|
||||
public bool SupportsJuniors { get; set; }
|
||||
public bool SupportsLevelVoting { get; set; }
|
||||
public bool SupportsMobile { get; set; }
|
||||
public bool SupportsQuest2 { get; set; }
|
||||
public bool SupportsScreens { get; set; }
|
||||
public bool SupportsTeleportVR { get; set; }
|
||||
public bool SupportsVRLow { get; set; }
|
||||
public bool SupportsWalkVR { get; set; }
|
||||
public List<TagClass> Tags { get; set; }
|
||||
public bool ToxmodEnabled { get; set; }
|
||||
public int? UgcSubVersion { get; set; }
|
||||
public int? UgcVersion { get; set; }
|
||||
public WarningMaskType WarningMask { get; set; }
|
||||
}
|
||||
|
||||
public class LoadScreen
|
||||
{
|
||||
public string ImageName { get; set; }
|
||||
public string Subtitle { get; set; }
|
||||
public string Title { get; set; }
|
||||
}
|
||||
|
||||
public class LocalizationContext
|
||||
{
|
||||
public List<string> LocalizedFields { get; set; }
|
||||
public string Scope { get; set; }
|
||||
public string TargetLocale { get; set; }
|
||||
}
|
||||
|
||||
public class RoleClass
|
||||
{
|
||||
public long AccountId { get; set; }
|
||||
public int InvitedRole { get; set; }
|
||||
public long? LastChangedByAccountId { get; set; }
|
||||
public RoomDBClasses.Role_data Role { get; set; }
|
||||
}
|
||||
|
||||
public class RoomBanDetails
|
||||
{
|
||||
public long AccountId { get; set; }
|
||||
public DateTime? BanEndTime { get; set; }
|
||||
public DateTime BanStartTime { get; set; }
|
||||
public long BannedByAccountId { get; set; }
|
||||
public string Reason { get; set; }
|
||||
public int Status { get; set; }
|
||||
public long? UnbannedByAccountId { get; set; }
|
||||
}
|
||||
|
||||
public class Stats
|
||||
{
|
||||
public int CheerCount { get; set; }
|
||||
public int FavoriteCount { get; set; }
|
||||
public int VisitCount { get; set; }
|
||||
public int VisitorCount { get; set; }
|
||||
}
|
||||
|
||||
public class SubRoom
|
||||
{
|
||||
[BsonId]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
public ObjectId? Id { get; set; }
|
||||
|
||||
public CurrentSave? CurrentSave { get; set; }
|
||||
[JsonIgnore]
|
||||
public List<CurrentSave> AllSaves { get; set; } = new();
|
||||
public RoomAccessibility Accessibility { get; set; }
|
||||
public long? CreatorAccountId { get; set; }
|
||||
public bool IsSandbox { get; set; }
|
||||
public int LastModeratedSaveModerationState { get; set; }
|
||||
public int MaxPlayers { get; set; }
|
||||
public string Name { get; set; }
|
||||
public ulong RoomId { get; set; }
|
||||
public bool ShouldAutoStageSaves { get; set; }
|
||||
public long? StagedSubRoomDataSaveId { get; set; }
|
||||
public ulong SubRoomId { get; set; }
|
||||
public string UnitySceneId { get; set; }
|
||||
}
|
||||
|
||||
public class CurrentSave
|
||||
{
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public string DataBlob { get; set; }
|
||||
//public string DataBlobHash { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int ModerationState { get; set; }
|
||||
public int OMVersion { get; set; }
|
||||
public int PersistenceVersion { get; set; }
|
||||
public List<string> ReferencedUnityAssetIds { get; set; }
|
||||
public ulong? SavedByAccountId { get; set; }
|
||||
public DeviceClasses? SavedOnDeviceClass { get; set; }
|
||||
public Platforms? SavedOnPlatform { get; set; }
|
||||
public ulong SubRoomDataSaveId { get; set; }
|
||||
public ulong SubRoomId { get; set; }
|
||||
public int UgcSubVersion { get; set; }
|
||||
public string UnityAssetId { get; set; } // optional, may be null
|
||||
}
|
||||
|
||||
public class TagClass
|
||||
{
|
||||
public string Tag { get; set; }
|
||||
public int Type { get; set; }
|
||||
}
|
||||
|
||||
public class SubroomSaveResults
|
||||
{
|
||||
public List<CurrentSave> Results { get; set; } = new();
|
||||
public int TotalCount { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class RoomFileData
|
||||
{
|
||||
public RoomData? RoomData { get; set; }
|
||||
public SubRoomData? SubRoomData { get; set; }
|
||||
public string? InventionUsage { get; set; }
|
||||
public int PersistenceVersion { get; set; }
|
||||
public int OMVersion { get; set; }
|
||||
public int UgcSubVersion { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool AutoPublish { get; set; }
|
||||
}
|
||||
|
||||
public class RoomData
|
||||
{
|
||||
public string? Filename { get; set; }
|
||||
public string? Hash { get; set; }
|
||||
public string? OwnershipProof { get; set; }
|
||||
}
|
||||
|
||||
public class SubRoomData
|
||||
{
|
||||
public string? Filename { get; set; }
|
||||
public string? Hash { get; set; }
|
||||
public string? OwnershipProof { get; set; }
|
||||
}
|
||||
|
||||
public enum RoomAccessibility
|
||||
{
|
||||
Private,
|
||||
Public,
|
||||
Unlisted,
|
||||
Dev_only,
|
||||
Dev_Unlisted,
|
||||
}
|
||||
|
||||
public enum RoomState
|
||||
{
|
||||
Active,
|
||||
PendingJunior = 11,
|
||||
Moderation_PendingReview = 100,
|
||||
Moderation_Closed,
|
||||
MarkedForDelete = 1000
|
||||
}
|
||||
|
||||
public class Tags
|
||||
{
|
||||
public string? Tag { get; set; }
|
||||
public int Type { get; set; }
|
||||
}
|
||||
public class LoadScreens
|
||||
{
|
||||
public string? ImageName { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Subtitle { get; set; }
|
||||
}
|
||||
|
||||
public enum Role_data : byte
|
||||
{
|
||||
None,
|
||||
Banned,
|
||||
Host = 10,
|
||||
Moderator = 20,
|
||||
CoOwner = 30,
|
||||
TemporaryCoOwner,
|
||||
Creator = 255
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using LiteDB;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server.Classes.db_class
|
||||
{
|
||||
public class PlayerSetting
|
||||
{
|
||||
[BsonId]
|
||||
public ulong PlayerId { get; set; }
|
||||
public List<Setting> Settings { get; set; } = new List<Setting>();
|
||||
}
|
||||
|
||||
public class Setting
|
||||
{
|
||||
public string? Key { get; set; }
|
||||
public string? Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using JWT;
|
||||
using JWT.Algorithms;
|
||||
using JWT.Exceptions;
|
||||
using JWT.Serializers;
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server;
|
||||
|
||||
public class ClientSecurity
|
||||
{
|
||||
|
||||
public static string key = ConfigRewild.Key;
|
||||
|
||||
public static string CreateToken(ulong userId, List<string> roles, string request_url, Platforms platform)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var payload = new Dictionary<string, object>
|
||||
{
|
||||
{ "sub", userId },
|
||||
{ "role", roles },
|
||||
{ "iat", now.ToUnixTimeSeconds() },
|
||||
{ "iss", $"{request_url}" },
|
||||
{ "client_id", $"recnet" },
|
||||
{ "exp", now.AddHours(24).ToUnixTimeSeconds() },
|
||||
{ "platform", platform }
|
||||
};
|
||||
|
||||
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
|
||||
IJsonSerializer serializer = new JsonNetSerializer();
|
||||
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
|
||||
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
|
||||
|
||||
return encoder.Encode(payload, key);
|
||||
}
|
||||
|
||||
public static string Createphotonacesstoken(ulong userId)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var heartbeat = HeartbeatDB.GetPlayerHeartbeat(userId);
|
||||
string photonRoomId = heartbeat.roomInstance.photonRoomIdYeee ?? "recrewild2025";
|
||||
var payload = new Dictionary<string, object>
|
||||
{
|
||||
{ "sub", userId.ToString() },
|
||||
{ "scope", "makerpen" },
|
||||
{ "aud", photonRoomId },
|
||||
};
|
||||
|
||||
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
|
||||
IJsonSerializer serializer = new JsonNetSerializer();
|
||||
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
|
||||
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
|
||||
|
||||
return encoder.Encode(payload, key);
|
||||
}
|
||||
|
||||
public static string Createphotonauthtoken(ulong userId)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var heartbeat = HeartbeatDB.GetPlayerHeartbeat(userId);
|
||||
string photonRoomId = heartbeat.roomInstance.photonRoomIdYeee ?? "recrewild2025";
|
||||
var payload = new Dictionary<string, object>
|
||||
{
|
||||
{ "sub", userId.ToString() },
|
||||
{ "rn.platid", "76561199000000000" },
|
||||
{ "rn.plat", "0" },
|
||||
{ "rn.deviceclass", "2" },
|
||||
{ "rn.env", "prod" },
|
||||
{ "exp", "1754316353" },
|
||||
{ "aud", photonRoomId },
|
||||
};
|
||||
|
||||
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
|
||||
IJsonSerializer serializer = new JsonNetSerializer();
|
||||
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
|
||||
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
|
||||
|
||||
return encoder.Encode(payload, key);
|
||||
}
|
||||
|
||||
public static string RefreshToken(string temp_val, ulong accid, int platform, ulong platform_id, ulong ticketid, string request_url, Platforms currentplatform)
|
||||
{
|
||||
var account = PlayerDB.getfullaccount_token(accid, ticketid, 0);
|
||||
if (account == null)
|
||||
return "";
|
||||
|
||||
List<string> roles = new List<string> { "gameclient" };
|
||||
if ((account.dev_Flag & DevFlag.dev) == DevFlag.dev)
|
||||
{
|
||||
roles.Add("developer");
|
||||
roles.Add("moderator");
|
||||
roles.Add("screenshare");
|
||||
}
|
||||
if (account.player.Id == 314)
|
||||
{
|
||||
roles.Add("developer");
|
||||
roles.Add("moderator");
|
||||
roles.Add("screenshare");
|
||||
}
|
||||
|
||||
return CreateToken(accid, roles, request_url, currentplatform);
|
||||
}
|
||||
|
||||
|
||||
public static string RefreshToken_Web(ulong userId)
|
||||
{
|
||||
var account = PlayerDB.GetFullAccount(userId);
|
||||
if (account == null)
|
||||
return "";
|
||||
|
||||
List<string> roles = new List<string> { "gameclient" };
|
||||
if ((account.dev_Flag & DevFlag.dev) == DevFlag.dev)
|
||||
{
|
||||
roles.Add("developer");
|
||||
roles.Add("moderator");
|
||||
roles.Add("screenshare");
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var payload = new Dictionary<string, object>
|
||||
{
|
||||
{ "sub", userId },
|
||||
{ "role", roles },
|
||||
{ "iat", now.ToUnixTimeSeconds() },
|
||||
{ "exp", now.AddDays(30).ToUnixTimeSeconds() },
|
||||
{ "iss", "https://recrewild.oldrec.net" }
|
||||
};
|
||||
|
||||
string key = ClientSecurity.key;
|
||||
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
|
||||
IJsonSerializer serializer = new JsonNetSerializer();
|
||||
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
|
||||
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
|
||||
string tokenString = encoder.Encode(payload, key);
|
||||
|
||||
return tokenString;
|
||||
}
|
||||
public static string HashPassword(string password)
|
||||
{
|
||||
using (var rng = new RNGCryptoServiceProvider())
|
||||
{
|
||||
byte[] salt = new byte[16];
|
||||
rng.GetBytes(salt);
|
||||
|
||||
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100_000, System.Security.Cryptography.HashAlgorithmName.SHA256))
|
||||
{
|
||||
byte[] hash = pbkdf2.GetBytes(32);
|
||||
byte[] hashBytes = new byte[48];
|
||||
Array.Copy(salt, 0, hashBytes, 0, 16);
|
||||
Array.Copy(hash, 0, hashBytes, 16, 32);
|
||||
return Convert.ToBase64String(hashBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool VerifyPassword(string enteredPassword, string storedHash)
|
||||
{
|
||||
byte[] hashBytes = Convert.FromBase64String(storedHash);
|
||||
byte[] salt = new byte[16];
|
||||
Array.Copy(hashBytes, 0, salt, 0, 16);
|
||||
|
||||
using (var pbkdf2 = new Rfc2898DeriveBytes(enteredPassword, salt, 100_000, System.Security.Cryptography.HashAlgorithmName.SHA256))
|
||||
{
|
||||
byte[] hash = pbkdf2.GetBytes(32);
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if (hashBytes[i + 16] != hash[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static string DecodeToken(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
return "error_token_not_found";
|
||||
|
||||
string tokenString = token.Replace("Bearer ", "");
|
||||
|
||||
try
|
||||
{
|
||||
IJsonSerializer serializer = new JsonNetSerializer();
|
||||
IDateTimeProvider provider = new UtcDateTimeProvider();
|
||||
IJwtValidator validator = new JwtValidator(serializer, provider);
|
||||
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
|
||||
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
|
||||
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
|
||||
|
||||
var jsonPayload = decoder.Decode(tokenString, key, verify: true);
|
||||
var payload = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonPayload);
|
||||
|
||||
if (payload.TryGetValue("exp", out var expObj))
|
||||
{
|
||||
var expUnix = Convert.ToInt64(expObj);
|
||||
var nowUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
if (nowUnix > expUnix)
|
||||
{
|
||||
return "error_token_expired";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "error_token_no_expiration";
|
||||
}
|
||||
|
||||
|
||||
if (payload.TryGetValue("sub", out var subObj))
|
||||
{
|
||||
if (ulong.TryParse(subObj.ToString(), out ulong userId))
|
||||
{
|
||||
var playerData = PlayerDB.GetAccountById(userId);
|
||||
if (playerData != null)
|
||||
{
|
||||
Console.WriteLine($"player username: {playerData.Username}");
|
||||
return JsonConvert.SerializeObject(playerData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"player not found: {userId}");
|
||||
return "error_token_user_not_found";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "error_token_invalid_sub";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "error_token_no_sub";
|
||||
}
|
||||
}
|
||||
catch (SignatureVerificationException)
|
||||
{
|
||||
return "error_token_tampered";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error decoding token: {ex.Message}");
|
||||
return "error_token";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] GetHash(string inputString)
|
||||
{
|
||||
using (HashAlgorithm algorithm = SHA256.Create())
|
||||
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
|
||||
}
|
||||
|
||||
public static string GetHashString(string inputString)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in GetHash(inputString))
|
||||
sb.Append(b.ToString("X2"));
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static Random random = new Random();
|
||||
|
||||
public static string RandomString(int length)
|
||||
{
|
||||
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
return new string(Enumerable.Repeat(chars, length)
|
||||
.Select(s => s[random.Next(s.Length)]).ToArray());
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server
|
||||
{
|
||||
public class Images
|
||||
{
|
||||
public static readonly string ImgSavePath = Path.Combine(Environment.CurrentDirectory, "Data", "Images", "player_images");
|
||||
public static readonly string InvSavePath = Path.Combine(Environment.CurrentDirectory, "Data", "CDN", "InventionBlobs");
|
||||
public static readonly string RoomSavePath = Path.Combine(Environment.CurrentDirectory, "Data", "CDN", "RoomBlobs");
|
||||
public static readonly string CdnSavePath = Path.Combine(Environment.CurrentDirectory, "Data", "CDN", "DataBlobs");
|
||||
public static readonly string ImgSavePathPolaroid = Path.Combine(Environment.CurrentDirectory, "Data", "Images", "polaroid_images");
|
||||
public static readonly string ImgRelativePath = "player_images/";
|
||||
|
||||
|
||||
private static int IndexOf(byte[] haystack, byte[] needle, int startIndex = 0)
|
||||
{
|
||||
for (int i = startIndex; i <= haystack.Length - needle.Length; i++)
|
||||
{
|
||||
bool match = true;
|
||||
for (int j = 0; j < needle.Length; j++)
|
||||
{
|
||||
if (haystack[i + j] != needle[j])
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static rn_file SaveImageFile(byte[] request)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool parsed;
|
||||
byte[]? imageBytes = Utils.ParseData(request, "file.bin", out parsed);
|
||||
|
||||
if (!parsed || imageBytes == null || imageBytes.Length == 0)
|
||||
{
|
||||
return new rn_file
|
||||
{
|
||||
success = false,
|
||||
saved = false,
|
||||
error = "Failed to parse or empty file data",
|
||||
FileName = "error.png",
|
||||
rawfilename = "error.png"
|
||||
};
|
||||
}
|
||||
|
||||
if (!Directory.Exists(ImgSavePath))
|
||||
Directory.CreateDirectory(ImgSavePath);
|
||||
|
||||
string fileName = $"{ClientSecurity.RandomString(30)}.png";
|
||||
string fullPath = Path.Combine(ImgSavePath, fileName);
|
||||
|
||||
File.WriteAllBytes(fullPath, imageBytes);
|
||||
|
||||
return new rn_file
|
||||
{
|
||||
success = true,
|
||||
saved = true,
|
||||
FileName = fileName,
|
||||
rawfilename = fileName
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Images.SaveImageFile] Error: {ex}");
|
||||
return new rn_file
|
||||
{
|
||||
success = false,
|
||||
saved = false,
|
||||
error = ex.Message,
|
||||
FileName = "error.png",
|
||||
rawfilename = "error.png"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static rn_file SaveFileUnifiedv2(byte[] request, FileType_data fileType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (request == null || request.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Empty file data received.");
|
||||
return new rn_file { saved = false, success = false, error = "Empty file data." };
|
||||
}
|
||||
|
||||
string extension;
|
||||
string basePath;
|
||||
|
||||
switch (fileType)
|
||||
{
|
||||
case FileType_data.Image:
|
||||
case FileType_data.Unknown:
|
||||
extension = ".png";
|
||||
basePath = ImgSavePathPolaroid;
|
||||
break;
|
||||
|
||||
case FileType_data.Invention:
|
||||
extension = ".inv";
|
||||
basePath = InvSavePath;
|
||||
break;
|
||||
|
||||
case FileType_data.RoomSave:
|
||||
extension = ".room";
|
||||
basePath = RoomSavePath;
|
||||
break;
|
||||
|
||||
case FileType_data.RoomMetadata:
|
||||
extension = ".META";
|
||||
basePath = RoomSavePath;
|
||||
break;
|
||||
|
||||
case FileType_data.Holotar:
|
||||
extension = ".htr";
|
||||
basePath = CdnSavePath;
|
||||
break;
|
||||
|
||||
default:
|
||||
extension = ".dat";
|
||||
basePath = CdnSavePath;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(basePath))
|
||||
Directory.CreateDirectory(basePath);
|
||||
|
||||
string fname = $"{ClientSecurity.RandomString(40)}{extension}";
|
||||
string fullPath = Path.Combine(basePath, fname);
|
||||
|
||||
Console.WriteLine($"[Images] Saving {fileType} file to: {fullPath}");
|
||||
|
||||
using (var fileStream = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, useAsync: true))
|
||||
{
|
||||
fileStream.Write(request, 0, request.Length);
|
||||
}
|
||||
|
||||
return new rn_file
|
||||
{
|
||||
success = true,
|
||||
saved = true,
|
||||
FileName = fname,
|
||||
rawfilename = fname,
|
||||
FileType = fileType
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Images.SaveFileUnified_v2] Error saving {fileType}: {ex}");
|
||||
return new rn_file
|
||||
{
|
||||
success = false,
|
||||
saved = false,
|
||||
error = ex.Message,
|
||||
FileName = "error.file",
|
||||
rawfilename = "error.file"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class rn_file
|
||||
{
|
||||
public bool success { get; set; }
|
||||
public bool saved { get; set; }
|
||||
public string? error { get; set; }
|
||||
public string? FileName { get; set; }
|
||||
public string? rawfilename { get; set; }
|
||||
public FileType_data FileType { get; set; }
|
||||
}
|
||||
|
||||
public enum FileType_data
|
||||
{
|
||||
Unknown,
|
||||
RoomSave,
|
||||
Holotar,
|
||||
Image,
|
||||
Video,
|
||||
Invention,
|
||||
RoomMetadata
|
||||
}
|
||||
|
||||
public class FileUploadModel
|
||||
{
|
||||
public IFormFile? File { get; set; }
|
||||
public FileType_data FileType { get; set; } = FileType_data.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server
|
||||
{
|
||||
public class Moderation_Detail
|
||||
{
|
||||
public class ModerationBlockDetails
|
||||
{
|
||||
public Moderation_Detail.ReportCategory ReportCategory { get; set; } = Moderation_Detail.ReportCategory.Unknown;
|
||||
public int Duration { get; set; } = 0;
|
||||
public long GameSessionId { get; set; } = 0;
|
||||
public bool? IsHostKick { get; set; } = false;
|
||||
public string? Message { get; set; } = "";
|
||||
public ulong? PlayerIdReporter { get; set; } = null;
|
||||
public bool? IsBan { get; set; } = false;
|
||||
[JsonIgnore]
|
||||
public long ModerationSetUnixTime { get; set; } = 0;
|
||||
[JsonIgnore]
|
||||
public ulong BannedByPlayerId { get; set; } = 0;
|
||||
}
|
||||
|
||||
public enum ReportCategory
|
||||
{
|
||||
Moderator = -1,
|
||||
Unknown,
|
||||
DEPRECATED_MicrophoneAbuse,
|
||||
Harassment,
|
||||
Cheating,
|
||||
DEPRECATED_ImmatureBehavior,
|
||||
AFK,
|
||||
Misc,
|
||||
Underage,
|
||||
VoteKick = 10,
|
||||
CoC_Underage = 100,
|
||||
CoC_Sexual,
|
||||
CoC_Discrimination,
|
||||
CoC_Trolling,
|
||||
CoC_NameOrProfile,
|
||||
IssuingInaccurateReports = 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
using RewildLiveSource.Controllers;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server
|
||||
{
|
||||
public static class Notifications
|
||||
{
|
||||
private static readonly byte[] Separator = Encoding.UTF8.GetBytes("\u001e");
|
||||
|
||||
public static async Task SendToPlayer(ulong playerId, string message)
|
||||
{
|
||||
if (!NotificationsController.WebSockets.TryGetValue(playerId, out var socket) || socket == null)
|
||||
{
|
||||
Console.WriteLine($"[Notifications] No active WebSocket for player {playerId}");
|
||||
return;
|
||||
}
|
||||
|
||||
await SendAsync(socket, message, playerId);
|
||||
}
|
||||
|
||||
public static async Task SendToAll(string message)
|
||||
{
|
||||
var tasks = NotificationsController.WebSockets
|
||||
.Select(kv => SendAsync(kv.Value, message, kv.Key));
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
public static async Task SendToPlayers(List<ulong> playerIds, string message)
|
||||
{
|
||||
var tasks = new List<Task>();
|
||||
|
||||
foreach (var id in playerIds)
|
||||
{
|
||||
if (NotificationsController.WebSockets.TryGetValue(id, out var socket) && socket != null)
|
||||
tasks.Add(SendAsync(socket, message, id));
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private static async Task SendAsync(WebSocket socket, string message, ulong? playerId = null)
|
||||
{
|
||||
if (socket == null || socket.State != WebSocketState.Open)
|
||||
return;
|
||||
|
||||
var packet = new SockSignalR
|
||||
{
|
||||
type = MessageTypes.Idk,
|
||||
target = "Notification",
|
||||
arguments = new object[] { message },
|
||||
nonblocking = true,
|
||||
result = "200 OK"
|
||||
};
|
||||
|
||||
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(packet));
|
||||
var buffer = new byte[data.Length + Separator.Length];
|
||||
Buffer.BlockCopy(data, 0, buffer, 0, data.Length);
|
||||
Buffer.BlockCopy(Separator, 0, buffer, data.Length, Separator.Length);
|
||||
|
||||
Console.WriteLine($"[Notifications] Sending to {(playerId.HasValue ? playerId.ToString() : "unknown player")}: {buffer}");
|
||||
|
||||
try
|
||||
{
|
||||
await socket.SendAsync(
|
||||
new ArraySegment<byte>(buffer),
|
||||
WebSocketMessageType.Text,
|
||||
endOfMessage: true,
|
||||
cancellationToken: CancellationToken.None
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Notifications] Failed to send to {(playerId.HasValue ? playerId.ToString() : "unknown player")}: {ex.Message}");
|
||||
|
||||
try
|
||||
{
|
||||
await socket.CloseAsync(WebSocketCloseStatus.InternalServerError, "Send failed", CancellationToken.None);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> RefreshAccount(ulong accountId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var selfUpdate = JsonConvert.SerializeObject(WebsocketEvents.create_player_updated_account2_Response("SelfAccountUpdate", PlayerDB.GetAccountMeSafe(accountId)));
|
||||
var globalUpdate = JsonConvert.SerializeObject(WebsocketEvents.create_player_updated_account2_Response("AccountUpdate", PlayerDB.GetAccountMeSafe(accountId)));
|
||||
|
||||
var notifySelfTask = Notifications.SendToPlayer(accountId, selfUpdate);
|
||||
var notifyAllTask = Notifications.SendToAll(globalUpdate);
|
||||
|
||||
await Task.WhenAll(notifySelfTask, notifyAllTask);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to send notification: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class SockSignalR
|
||||
{
|
||||
public MessageTypes type { get; set; }
|
||||
public string? target { get; set; }
|
||||
public object[]? arguments { get; set; }
|
||||
public bool nonblocking { get; set; }
|
||||
public object? result { get; set; }
|
||||
}
|
||||
|
||||
private enum MessageTypes
|
||||
{
|
||||
Invocation,
|
||||
Idk
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
public static class RateLimit
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DateTime> _requests = new();
|
||||
|
||||
public static bool TryConsume(
|
||||
string key,
|
||||
int cooldownSeconds,
|
||||
out int retryAfterSeconds)
|
||||
{
|
||||
retryAfterSeconds = 0;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var last = _requests.GetOrAdd(key, _ => DateTime.MinValue);
|
||||
var elapsed = (now - last).TotalSeconds;
|
||||
|
||||
if (elapsed < cooldownSeconds)
|
||||
{
|
||||
retryAfterSeconds = (int)Math.Ceiling(cooldownSeconds - elapsed);
|
||||
return false;
|
||||
}
|
||||
|
||||
_requests[key] = now;
|
||||
|
||||
Cleanup(now);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void Cleanup(DateTime now)
|
||||
{
|
||||
if (_requests.Count < 10_000) return;
|
||||
|
||||
foreach (var kv in _requests)
|
||||
{
|
||||
if ((now - kv.Value).TotalMinutes > 10)
|
||||
_requests.TryRemove(kv.Key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using System.Security.Principal;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
using static Rec_rewild_live_rewrite.api.dbs.HeartbeatDB;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server
|
||||
{
|
||||
public class Sessions
|
||||
{
|
||||
private static readonly Random random = new Random();
|
||||
|
||||
public static RoomInstance? CreateRoom(string roomName, string sceneName = "", bool isPrivate = false)
|
||||
{
|
||||
//const string uuidPrefix = "rewild_live_2023";
|
||||
string formattedRoomName = $"^{roomName}";
|
||||
|
||||
if (!string.IsNullOrEmpty(sceneName))
|
||||
{
|
||||
Console.WriteLine($"[Sessions]: Finding room: \"{roomName}\" with scene id: \"{sceneName}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[Sessions]: Finding room: \"{roomName}\"");
|
||||
}
|
||||
|
||||
long gameSessionId = random.Next(0x1000, 0x7fffffff);
|
||||
|
||||
RoomDBClasses.RoomRoot roomData = RoomDB.GetRoomByName(roomName);
|
||||
if (roomData == null)
|
||||
{
|
||||
Console.WriteLine($"[Sessions]: Room \"{roomName}\" not found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Console.WriteLine($"[Sessions]: {roomName} found! Joining...");
|
||||
|
||||
var firstSubRoom = roomData.SubRooms?.FirstOrDefault();
|
||||
string dataBlob = firstSubRoom?.CurrentSave?.DataBlob ?? string.Empty;
|
||||
long instanceNumber = random.Next(1000, 999999999);
|
||||
Guid instanceId = Guid.NewGuid();
|
||||
|
||||
string photonSuffix = isPrivate
|
||||
? $"_private-room-{instanceNumber}-{instanceId}"
|
||||
: "room";
|
||||
|
||||
var session = new RoomInstance
|
||||
{
|
||||
roomInstanceId = instanceNumber,
|
||||
roomId = (long)roomData.RoomId,
|
||||
subRoomId = (long)roomData.SubRooms[0].SubRoomId,
|
||||
location = roomData.SubRooms[0].UnitySceneId,
|
||||
roomInstanceType = 0,
|
||||
name = formattedRoomName,
|
||||
maxCapacity = roomData.SubRooms[0].MaxPlayers,
|
||||
isFull = false,
|
||||
isPrivate = isPrivate,
|
||||
isInProgress = false,
|
||||
photonRoomIdYeee = photonSuffix,
|
||||
voiceServerId = null, // this not supported and game may explode when connecting to a server that dont exist
|
||||
voiceAuthId = null,
|
||||
matchmakingPolicy = 0
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(sceneName))
|
||||
{
|
||||
ApplySceneSettings(session, roomData, roomName, sceneName, isPrivate);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyRandomPublicSubroom(session, roomData, roomName, isPrivate);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
public static RoomInstance CreateDorm(ulong id, string name)
|
||||
{
|
||||
Random rng = new Random();
|
||||
|
||||
long RandomNumber = rng.Next(1000, 999999999);
|
||||
|
||||
ulong DormId = PlayerDB.GetDormId(id);
|
||||
|
||||
RoomDBClasses.RoomRoot dorm = RoomDB.GetRoom(DormId);
|
||||
if (dorm == null)
|
||||
{
|
||||
PlayerDB.CreateDormForPlayerId(id);
|
||||
}
|
||||
long instanceNumber = random.Next(1000, 999999999);
|
||||
Guid instanceId = Guid.NewGuid();
|
||||
|
||||
string photonSuffix = $"Rec-Rewild-Dorm-{instanceNumber}-{instanceId}";
|
||||
var session = new RoomInstance
|
||||
{
|
||||
roomInstanceId = RandomNumber,
|
||||
roomId = (long)DormId,
|
||||
subRoomId = (long?)dorm.SubRooms.FirstOrDefault()?.SubRoomId ?? 0,
|
||||
location = "76d98498-60a1-430c-ab76-b54a29b7a163",
|
||||
roomInstanceType = 0,
|
||||
name = $"@{name}'s Dorm",
|
||||
maxCapacity = 4,
|
||||
isFull = false,
|
||||
isPrivate = true,
|
||||
isInProgress = false,
|
||||
photonRoomIdYeee = photonSuffix,
|
||||
voiceServerId = null, // this not supported and game may explode when connecting to a server that dont exist
|
||||
voiceAuthId = null,
|
||||
matchmakingPolicy = 0
|
||||
};
|
||||
|
||||
RoomDB.IncreaseRoomVisit(DormId);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
private static void ApplySceneSettings(RoomInstance session, RoomDBClasses.RoomRoot roomData, string roomName, string sceneName, bool isPrivate)
|
||||
{
|
||||
foreach (var subroom in roomData.SubRooms)
|
||||
{
|
||||
if (subroom.Name.Equals(sceneName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
session.subRoomId = (long)subroom.SubRoomId;
|
||||
session.location = subroom.UnitySceneId;
|
||||
|
||||
if (!isPrivate)
|
||||
{
|
||||
//session.photonRoomId = $"{roomName}-hello-room-{sceneName}";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Scene \"{sceneName}\" not found in room \"{roomName}\".");
|
||||
}
|
||||
|
||||
private static void ApplyRandomPublicSubroom(RoomInstance session, RoomDBClasses.RoomRoot roomData, string roomName, bool isPrivate)
|
||||
{
|
||||
var publicSubrooms = roomData.SubRooms
|
||||
.Where(sub => sub.Accessibility == RoomDBClasses.RoomAccessibility.Public)
|
||||
.ToList();
|
||||
|
||||
if (publicSubrooms.Count == 0)
|
||||
{
|
||||
Console.WriteLine($"No public subrooms found for \"{roomName}\".");
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedSubroom = publicSubrooms[random.Next(publicSubrooms.Count)];
|
||||
|
||||
session.subRoomId = (long)selectedSubroom.SubRoomId;
|
||||
session.location = selectedSubroom.UnitySceneId;
|
||||
|
||||
if (!isPrivate)
|
||||
{
|
||||
//session.photonRoomId = $"{roomName}-hello-room-{selectedSubroom.name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server
|
||||
{
|
||||
public class Steam
|
||||
{
|
||||
private static readonly HttpClient _http = new HttpClient();
|
||||
|
||||
public static async Task<SteamAccountInfo?> GetAccountInfoAsync(string steamId64)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(steamId64))
|
||||
return null;
|
||||
|
||||
var url = $"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key={ConfigRewild.SteamAPIKey}&steamids={steamId64}";
|
||||
|
||||
var response = await _http.GetAsync(url);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return null;
|
||||
|
||||
var json = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var player = json["response"]?["players"]?.First;
|
||||
if (player == null)
|
||||
return null;
|
||||
|
||||
return new SteamAccountInfo
|
||||
{
|
||||
SteamId = steamId64,
|
||||
PersonaName = player["personaname"]?.ToString(),
|
||||
ProfileUrl = player["profileurl"]?.ToString(),
|
||||
Avatar = player["avatarfull"]?.ToString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class SteamAccountInfo
|
||||
{
|
||||
public string SteamId { get; set; } = "";
|
||||
public string? PersonaName { get; set; }
|
||||
public string? ProfileUrl { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using static Rec_rewild_live_rewrite.api.server.Classes.db_class.EventDBClasses;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server
|
||||
{
|
||||
|
||||
public class Utils
|
||||
{
|
||||
private static readonly Random rng = new Random();
|
||||
private static readonly char[] _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
|
||||
private static readonly byte[] Key = Convert.FromBase64String("f8Z7Xgkz7PzBvZ93YjNfYpNfwysNMLA5xkKYY+6IE1s=");
|
||||
private static readonly byte[] IV = Convert.FromBase64String("GxCptCkqNh8W2MnA7bAYzA==");
|
||||
|
||||
public static string EncryptAes(string plainText)
|
||||
{
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = Key;
|
||||
aes.IV = IV;
|
||||
using var encryptor = aes.CreateEncryptor();
|
||||
using var ms = new MemoryStream();
|
||||
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
|
||||
using (var sw = new StreamWriter(cs))
|
||||
sw.Write(plainText);
|
||||
return Convert.ToBase64String(ms.ToArray());
|
||||
}
|
||||
|
||||
public static string DecryptAes(string encryptedBase64)
|
||||
{
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = Key;
|
||||
aes.IV = IV;
|
||||
using var decryptor = aes.CreateDecryptor();
|
||||
using var ms = new MemoryStream(Convert.FromBase64String(encryptedBase64));
|
||||
using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
|
||||
using var sr = new StreamReader(cs);
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
|
||||
public static int Next(int min, int max)
|
||||
{
|
||||
byte[] randomBytes = new byte[4];
|
||||
RandomNumberGenerator.Fill(randomBytes);
|
||||
int secureRandomNumber = BitConverter.ToInt32(randomBytes, 0);
|
||||
while (secureRandomNumber < min && secureRandomNumber >= max)
|
||||
{
|
||||
secureRandomNumber = BitConverter.ToInt32(randomBytes, 0);
|
||||
}
|
||||
int randomNumber = Guid.NewGuid().ToByteArray().Sum(b => b) % max;
|
||||
while (randomNumber < min && randomNumber >= max)
|
||||
{
|
||||
randomNumber = Guid.NewGuid().ToByteArray().Sum(b => b) % max;
|
||||
}
|
||||
int randomNumber1 = Math.Abs(Environment.TickCount % max);
|
||||
int output = randomNumber1 + randomNumber + secureRandomNumber;
|
||||
output = Math.Min(output, min);
|
||||
output = Math.Max(output, max);
|
||||
return output;
|
||||
}
|
||||
|
||||
public static string GenerateShortCode(int length = 6)
|
||||
{
|
||||
return new string(Enumerable.Range(0, length)
|
||||
.Select(_ => _chars[rng.Next(_chars.Length)]).ToArray());
|
||||
}
|
||||
public static string GetRandomString(int len = 16)
|
||||
{
|
||||
const string chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789";
|
||||
return new string(Enumerable.Repeat(chars, len)
|
||||
.Select(s => s[rng.Next(s.Length)]).ToArray());
|
||||
}
|
||||
|
||||
|
||||
public static int IndexOf(byte[] haystack, byte[] needle, int startIndex = 0)
|
||||
{
|
||||
for (int i = startIndex; i <= haystack.Length - needle.Length; i++)
|
||||
{
|
||||
bool match = true;
|
||||
for (int j = 0; j < needle.Length; j++)
|
||||
{
|
||||
if (haystack[i + j] != needle[j])
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static byte[]? ParseData(byte[] data, string fieldName, out bool parsed)
|
||||
{
|
||||
parsed = false;
|
||||
|
||||
try
|
||||
{
|
||||
var boundaryStart = Encoding.ASCII.GetString(data, 0, Math.Min(200, data.Length))
|
||||
.Split('\n')[0]
|
||||
.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(boundaryStart))
|
||||
{
|
||||
Console.WriteLine("Failed to detect boundary");
|
||||
return null;
|
||||
}
|
||||
|
||||
string boundary = boundaryStart;
|
||||
string header = $"name=\"{fieldName}\"";
|
||||
int headerIndex = Utils.IndexOf(data, Encoding.ASCII.GetBytes(header));
|
||||
|
||||
if (headerIndex == -1)
|
||||
{
|
||||
Console.WriteLine($"Field {fieldName} not found in multipart data");
|
||||
return null;
|
||||
}
|
||||
|
||||
int startOfFile = IndexOf(data, new byte[] { 13, 10, 13, 10 }, headerIndex);
|
||||
if (startOfFile == -1)
|
||||
{
|
||||
Console.WriteLine("Malformed multipart content, missing CRLF after headers");
|
||||
return null;
|
||||
}
|
||||
|
||||
startOfFile += 4;
|
||||
|
||||
byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n" + boundary);
|
||||
int endOfFile = Utils.IndexOf(data, boundaryBytes, startOfFile);
|
||||
if (endOfFile == -1)
|
||||
{
|
||||
Console.WriteLine("Failed to locate boundary end");
|
||||
return null;
|
||||
}
|
||||
|
||||
int fileLength = endOfFile - startOfFile;
|
||||
if (fileLength <= 0)
|
||||
{
|
||||
Console.WriteLine("File length invalid");
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] fileBytes = new byte[fileLength];
|
||||
Buffer.BlockCopy(data, startOfFile, fileBytes, 0, fileLength);
|
||||
|
||||
parsed = true;
|
||||
return fileBytes;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Images.ParseData] Exception: {ex}");
|
||||
parsed = false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ContentResult Error(string error)
|
||||
{
|
||||
return new ContentResult()
|
||||
{
|
||||
Content = JsonConvert.SerializeObject(new
|
||||
{
|
||||
success = false,
|
||||
error = error,
|
||||
value = (string?)null
|
||||
}),
|
||||
ContentType = "application/json",
|
||||
StatusCode = 200
|
||||
};
|
||||
}
|
||||
public static ContentResult ErrorRoom(string errorId, string error)
|
||||
{
|
||||
return new ContentResult()
|
||||
{
|
||||
Content = JsonConvert.SerializeObject(new
|
||||
{
|
||||
Success = false,
|
||||
Value = (string?)null,
|
||||
ErrorId = errorId,
|
||||
Error = error,
|
||||
}),
|
||||
ContentType = "application/json",
|
||||
StatusCode = 200
|
||||
};
|
||||
}
|
||||
|
||||
public static ContentResult ErrorEvent(EventResults errorresult)
|
||||
{
|
||||
return new ContentResult()
|
||||
{
|
||||
Content = JsonConvert.SerializeObject(new
|
||||
{
|
||||
Result = errorresult,
|
||||
TagModifyResult = (string?)null,
|
||||
PlayerEvent = (string?)null,
|
||||
}),
|
||||
ContentType = "application/json",
|
||||
StatusCode = 200
|
||||
};
|
||||
}
|
||||
|
||||
public static List<string> Nouns = new List<string>()
|
||||
{
|
||||
"Erm", "Admin", "AuraPoints", "Fortniter", "RecCon", "Bat", "Bear", "Bee", "Buffalo", "Butterfly",
|
||||
"Capybara", "Cat", "Caterpillar", "Chameleon", "Cheetah", "Chicken", "Chimpanzee", "Cobra", "Coyote",
|
||||
"Crane", "Cricket", "Crow", "Deer", "Boi", "Dolphin", "Donkey", "Windows98", "pingul", "Eagle", "Echidna",
|
||||
"Elephant", "Elk", "dic", "Ferret", "Flamingo", "Fish", "memey", "Frog", "Gazelle", "Giraffe", "e12354",
|
||||
"Meme", "GorillaTag", "Hamster", "Jit", "Hippo", "Horse", "Hyena", "Iguana", "Jumbotron", "Jellyfish",
|
||||
"Gyat", "Kitten", "Top10BestFeet", "Youtube", "mac_os", "AAAAAAAAAA", "Blud", "Llama", "RecNet", "Monkey",
|
||||
"Moose", "Mouse", "Mule", "Newt", "VBucks", "Opposum", "Ostrich", "Otter", "Owl", "Oyster", "Panda",
|
||||
"Panther", "Parrot", "Penguin", "P34", "Pigeon", "Piranha", "Platypus", "Pony", "Minecraft", "Puppy",
|
||||
"Quail", "Emo", "bena.png", "Salmon", "Scorpion", "Seal", "Shark", "Sheep", "Sloth", "Snail", "Bozo",
|
||||
"Squirrel", "urmum", "NintendoSwitch", "Wii", "wat_time", "Roach", "Turtle", "Turkey", "Wii_U", "helpme",
|
||||
"Walrus", "Whale", "Github", "Yes_fade", "Zone"
|
||||
};
|
||||
|
||||
public static List<string> Adjectives = new List<string>()
|
||||
{
|
||||
"Adorable", "BitCoin", ":3", "Underground", "Aimless", "Alert", "FanumTaxed", "Alone", "Broken",
|
||||
"Beautiful", "Bored", "Brave", "Scratched", "Busy", "Calm", "Carefree", "Careless", "Caring", "Charming",
|
||||
"Cheerful", "Clever", "Clumsy", "Bugcrowd", "Cowardly", "Cozy", "Crabby", "Cranky", "Crawling",
|
||||
"Creaky", "Creative", "Crispy", "DexCryptic", "Doxxed", "Delightful", "Determined", "Burning", "Dull",
|
||||
"Eager", "Elated", "Emotional", "Enchanting", "Encouraging", "Endless", "Energetic", "Enthusiastic",
|
||||
"Excited", "FNAF", "Fantastic", "Fastidious", "Fine", "Fluttering", "Fragrant",
|
||||
"Friendly", "AAAAAAAAA", "Funny", "Fussy", "Fuzzy", "Generous", "Gentle", "Drunk", "Splooty", "Glorious",
|
||||
"Glowing", "Good", "Grand", "Great", "Greedy", "Grimy", "Happy", "Hardworking", "Hasty", "Healthy",
|
||||
"Heavy", "Helpful", "Hilarious", "Hopeful", "Icy", "Important", "Inquisitive", "Jolly", "Joyful",
|
||||
"Joyous", "Kind", "Lazy", "Lively", "Loud", "Lovely", "Loyal", "Lucky", "Luminous", "Lumpy", "Majestic",
|
||||
"Meek", "Melodic", "Mighty", "Moody", "Nice", "Nimble", "Odd", "Optimistic", "Perfect", "Pervasive",
|
||||
"Pleasant", "Plucky", "Plush", "Polite", "Practical", "Proud", "Quick", "Quiet", "Rapid", "Redolent",
|
||||
"Reliable", "Relieved", "Royal", "Skid", "Scared", "Hangar", "Sensible", "Sensitive", "Shining",
|
||||
"Shrill", "Silly", "Sincere", "Sizzling", "Sleepy", "Smiling", "Smooth", "Snug", "Soaring", "Sparkling",
|
||||
"Speedy", "Spiky", "Splendid", "Spoiled", "Steaming", "Still", "Strict", "Stuffed", "Sturdy",
|
||||
"Successful", "Surprised", "Swift", "Taciturn", "Tense", "Gay", "Trans", "Thrifty", "Tough",
|
||||
"Tricky", "Godly", "Twerking", "Racist", "Rec", "Victorious", "Wild", "Wise", "W",
|
||||
"Wonderful", "Worried", "Skibidi", "Zesty", "Zealous", "Skibiti", "Hot"
|
||||
};
|
||||
|
||||
public static List<string> imgs = new List<string>()
|
||||
{
|
||||
"DefaultProfileImage"
|
||||
/*"DefaultImg.png",
|
||||
"DefaultImgPink.png",
|
||||
"DefaultImgPurple.png",
|
||||
"DefaultImgRed.png",
|
||||
"DefaultImgRainbow.png",
|
||||
"DefaultImgGreen.png",
|
||||
"DefaultImgOrange.png",
|
||||
"DefaultImgYellow.png",
|
||||
"DefaultImgLight.png"*/
|
||||
};
|
||||
|
||||
public static string GetRandomName()
|
||||
{
|
||||
string adjective = Adjectives[rng.Next(Adjectives.Count)];
|
||||
string noun = Nouns[rng.Next(Nouns.Count)];
|
||||
int number = rng.Next(1000, 9999);
|
||||
return adjective + noun + number;
|
||||
}
|
||||
|
||||
public static string GetRandomImageName()
|
||||
{
|
||||
string img = imgs[rng.Next(imgs.Count)];
|
||||
return img;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using Newtonsoft.Json;
|
||||
using Rec_rewild_live_rewrite.api.dbs;
|
||||
using Rec_rewild_live_rewrite.api.server.Classes.db_class;
|
||||
using System.Text;
|
||||
|
||||
namespace Rec_rewild_live_rewrite.api.server
|
||||
{
|
||||
public class Webhook
|
||||
{
|
||||
private static readonly Random rng = new Random();
|
||||
|
||||
public static void Send(ulong accid, string type, string title, string description, long color, string? imageUrl = null, ulong? roomid = null)
|
||||
{
|
||||
string? webhookUrl = type.ToLower() switch
|
||||
{
|
||||
"prod" => "https://discord.com/api/webhooks/REPLACEME/REPLACEME",
|
||||
"admin" => "https://discord.com/api/webhooks/REPLACEME/REPLACEME",
|
||||
"roomie" => "https://discord.com/api/webhooks/REPLACEME/REPLACEME",
|
||||
"gaylog" => "https://discord.com/api/webhooks/REPLACEME/REPLACEME",
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(webhookUrl))
|
||||
{
|
||||
Console.WriteLine($"Invalid webhook type: {type}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
player_data account = PlayerDB.GetFullAccount(accid);
|
||||
bool dev = PlayerDB.CheckDevFlag(accid);
|
||||
RoomDBClasses.RoomRoot? room = roomid.HasValue ? RoomDB.GetRoom(roomid.Value) : null;
|
||||
|
||||
string authorName = account?.player?.Username ?? "Unknown User";
|
||||
if (dev)
|
||||
{
|
||||
//authorName += " [DEV]";
|
||||
}
|
||||
//string authorIcon = "https://example.com/icon.png";
|
||||
|
||||
var payload = JsonConvert.SerializeObject(new discord_content
|
||||
{
|
||||
content = "",
|
||||
attachments = new List<string>(),
|
||||
embeds = new List<discord_embed>
|
||||
{
|
||||
new discord_embed
|
||||
{
|
||||
title = title,
|
||||
description = description,
|
||||
color = (int)2067276,
|
||||
thumbnail = room != null && !string.IsNullOrEmpty(room.ImageName) ? new { url = $"https://recrewild.oldrec.net/img/{room.ImageName}" } : null,
|
||||
image = string.IsNullOrEmpty(imageUrl) ? null : new discord_embed_image { url = imageUrl },
|
||||
author = new discord_embed_author
|
||||
{
|
||||
name = authorName,
|
||||
icon_url = $"https://recrewild.oldrec.net/img/{account.player.profileImage}?width=80&cropSquare=true",
|
||||
url = $"https://recrewild.oldrec.net/rewild_net/account/user/{account.player.Username}"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Console.WriteLine(
|
||||
$"--- {title} ({type.ToUpper()}) ----------------------\n\n" +
|
||||
$"{description}\n\n" +
|
||||
$"----------------------------------");
|
||||
ulong number = (ulong)rng.Next(1000, 999999999);
|
||||
try
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
var content = new StringContent(payload, Encoding.UTF8, "application/json");
|
||||
var response = client.PostAsync(webhookUrl, content).Result;
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Console.WriteLine($"Failed to send webhook: {response.StatusCode} - {response.ReasonPhrase}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error sending webhook: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public class discord_content
|
||||
{
|
||||
public string? content { get; set; }
|
||||
public List<discord_embed>? embeds { get; set; }
|
||||
public List<string>? attachments { get; set; }
|
||||
}
|
||||
|
||||
public class discord_embed
|
||||
{
|
||||
public string? title { get; set; }
|
||||
public string? description { get; set; }
|
||||
public long color { get; set; }
|
||||
public object? thumbnail { get; set; }
|
||||
public discord_embed_image? image { get; set; }
|
||||
public discord_embed_author? author { get; set; }
|
||||
}
|
||||
|
||||
public class discord_embed_image
|
||||
{
|
||||
public string? url { get; set; }
|
||||
}
|
||||
public class discord_embed_author
|
||||
{
|
||||
public string? name { get; set; }
|
||||
public string? url { get; set; }
|
||||
public string? icon_url { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user