Initial upload

This commit is contained in:
myawired
2026-05-03 14:53:43 -04:00
commit 7a46a17131
819 changed files with 607914 additions and 0 deletions
+195
View File
@@ -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;
}
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace Rec_rewild_live_rewrite.api.dbs
{
public class ClubDB
{
}
}
+124
View File
@@ -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;
}
}
}
+412
View File
@@ -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 = ""
};
}
}
+207
View File
@@ -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
}
}
}
+411
View File
@@ -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}");
}
});
}
}
+153
View File
@@ -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();
}
}
}
+94
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1218
View File
File diff suppressed because it is too large Load Diff
+151
View File
@@ -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" }
};
}
}