You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
🆕 Groups Forums
- Please run the 002_forum_groups.sql !
This commit is contained in:
@@ -0,0 +1 @@
|
||||
ALTER TABLE `guild_forum_views` ADD UNIQUE KEY `user_guild` (`user_id`, `guild_id`);
|
||||
@@ -210,6 +210,19 @@ public class ForumThread implements Runnable, ISerialize {
|
||||
guildThreads.add(thread);
|
||||
}
|
||||
|
||||
public static void clearCacheForGuild(int guildId) {
|
||||
synchronized (guildThreadsCache) {
|
||||
THashSet<ForumThread> threads = guildThreadsCache.remove(guildId);
|
||||
if (threads != null) {
|
||||
synchronized (forumThreadsCache) {
|
||||
for (ForumThread thread : threads) {
|
||||
forumThreadsCache.remove(thread.threadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
for (THashSet<ForumThread> threads : guildThreadsCache.values()) {
|
||||
for (ForumThread thread : threads) {
|
||||
|
||||
@@ -572,6 +572,7 @@ public class PacketManager {
|
||||
this.registerHandler(Incoming.GuildForumModerateMessageEvent, GuildForumModerateMessageEvent.class);
|
||||
this.registerHandler(Incoming.GuildForumModerateThreadEvent, GuildForumModerateThreadEvent.class);
|
||||
this.registerHandler(Incoming.GuildForumThreadUpdateEvent, GuildForumThreadUpdateEvent.class);
|
||||
this.registerHandler(Incoming.GuildForumMarkAsReadEvent, GuildForumMarkAsReadEvent.class);
|
||||
this.registerHandler(Incoming.GetHabboGuildBadgesMessageEvent, GetHabboGuildBadgesMessageEvent.class);
|
||||
|
||||
// this.registerHandler(Incoming.GuildForumDataEvent, GuildForumModerateMessageEvent.class);
|
||||
|
||||
+56
@@ -6,9 +6,20 @@ import com.eu.habbo.habbohotel.guilds.GuildState;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.habbohotel.guilds.forums.ForumThread;
|
||||
import com.eu.habbo.messages.incoming.guilds.forums.GuildForumListEvent;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import com.eu.habbo.plugin.events.guilds.GuildChangedSettingsEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class GuildChangeSettingsEvent extends MessageHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildChangeSettingsEvent.class);
|
||||
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 500;
|
||||
@@ -31,6 +42,24 @@ public class GuildChangeSettingsEvent extends MessageHandler {
|
||||
guild.setState(GuildState.valueOf(settingsEvent.state));
|
||||
guild.setRights(settingsEvent.rights);
|
||||
|
||||
// Read forum toggle
|
||||
boolean forumEnabled = this.packet.readBoolean();
|
||||
boolean wasForumEnabled = guild.hasForum();
|
||||
|
||||
if (forumEnabled != wasForumEnabled) {
|
||||
guild.setForum(forumEnabled);
|
||||
|
||||
if (!forumEnabled) {
|
||||
// Delete all threads and comments for this guild
|
||||
ForumThread.clearCacheForGuild(guildId);
|
||||
deleteForumData(guildId);
|
||||
}
|
||||
|
||||
// Invalidate caches
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
GuildForumListEvent.invalidateActiveForumsCache();
|
||||
}
|
||||
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(guild.getRoomId());
|
||||
if(room != null) {
|
||||
room.refreshGuild(guild);
|
||||
@@ -42,4 +71,31 @@ public class GuildChangeSettingsEvent extends MessageHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteForumData(int guildId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
// Delete comments for all threads in this guild
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guilds_forums_comments` WHERE `thread_id` IN (SELECT `id` FROM `guilds_forums_threads` WHERE `guild_id` = ?)")) {
|
||||
statement.setInt(1, guildId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
// Delete all threads for this guild
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guilds_forums_threads` WHERE `guild_id` = ?")) {
|
||||
statement.setInt(1, guildId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
// Delete forum view records for this guild
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guild_forum_views` WHERE `guild_id` = ?")) {
|
||||
statement.setInt(1, guildId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to delete forum data for guild " + guildId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+43
-2
@@ -14,6 +14,7 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class GuildForumListEvent extends MessageHandler {
|
||||
@Override
|
||||
@@ -23,6 +24,26 @@ public class GuildForumListEvent extends MessageHandler {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildForumListEvent.class);
|
||||
|
||||
// Cache for active forums list (shared across all users)
|
||||
private static volatile THashSet<Guild> activeForumsCache = null;
|
||||
private static volatile long activeForumsCachedAt = 0;
|
||||
private static final long ACTIVE_FORUMS_TTL = 30 * 60 * 1000; // 30 minutes
|
||||
|
||||
// Cache for user's forum list
|
||||
private static final ConcurrentHashMap<Integer, long[]> myForumsCache = new ConcurrentHashMap<>(); // userId -> {cachedAt}
|
||||
private static final ConcurrentHashMap<Integer, THashSet<Guild>> myForumsData = new ConcurrentHashMap<>();
|
||||
private static final long MY_FORUMS_TTL = 10 * 60 * 1000; // 10 minutes
|
||||
|
||||
public static void invalidateActiveForumsCache() {
|
||||
activeForumsCache = null;
|
||||
activeForumsCachedAt = 0;
|
||||
}
|
||||
|
||||
public static void invalidateMyForumsCache(int userId) {
|
||||
myForumsCache.remove(userId);
|
||||
myForumsData.remove(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
int mode = this.packet.readInt();
|
||||
@@ -50,12 +71,18 @@ public class GuildForumListEvent extends MessageHandler {
|
||||
}
|
||||
|
||||
private THashSet<Guild> getActiveForums() {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (activeForumsCache != null && (now - activeForumsCachedAt) < ACTIVE_FORUMS_TTL) {
|
||||
return activeForumsCache;
|
||||
}
|
||||
|
||||
THashSet<Guild> guilds = new THashSet<Guild>();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT `guilds`.`id`, SUM(`guilds_forums_threads`.`posts_count`) AS `post_count` " +
|
||||
"FROM `guilds_forums_threads` " +
|
||||
"LEFT JOIN `guilds` ON `guilds`.`id` = `guilds_forums_threads`.`guild_id` " +
|
||||
"WHERE `guilds`.`read_forum` = 'EVERYONE' AND `guilds_forums_threads`.`created_at` > ? " +
|
||||
"WHERE `guilds`.`forum` = '1' AND `guilds_forums_threads`.`created_at` > ? " +
|
||||
"GROUP BY `guilds`.`id` " +
|
||||
"ORDER BY `post_count` DESC LIMIT 100")) {
|
||||
statement.setInt(1, Emulator.getIntUnixTimestamp() - 7 * 24 * 60 * 60);
|
||||
@@ -73,10 +100,21 @@ public class GuildForumListEvent extends MessageHandler {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(500));
|
||||
}
|
||||
|
||||
activeForumsCache = guilds;
|
||||
activeForumsCachedAt = now;
|
||||
|
||||
return guilds;
|
||||
}
|
||||
|
||||
private THashSet<Guild> getMyForums(int userId) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
long[] cached = myForumsCache.get(userId);
|
||||
if (cached != null && (now - cached[0]) < MY_FORUMS_TTL) {
|
||||
THashSet<Guild> data = myForumsData.get(userId);
|
||||
if (data != null) return data;
|
||||
}
|
||||
|
||||
THashSet<Guild> guilds = new THashSet<Guild>();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT `guilds`.`id` FROM `guilds_members` " +
|
||||
@@ -97,6 +135,9 @@ public class GuildForumListEvent extends MessageHandler {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(500));
|
||||
}
|
||||
|
||||
myForumsCache.put(userId, new long[]{now});
|
||||
myForumsData.put(userId, guilds);
|
||||
|
||||
return guilds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.eu.habbo.messages.incoming.guilds.forums;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class GuildForumMarkAsReadEvent extends MessageHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildForumMarkAsReadEvent.class);
|
||||
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 500;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
int count = this.packet.readInt();
|
||||
int userId = this.client.getHabbo().getHabboInfo().getId();
|
||||
int timestamp = Emulator.getIntUnixTimestamp();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
int guildId = this.packet.readInt();
|
||||
this.packet.readInt(); // messageId (not used, we track by timestamp)
|
||||
this.packet.readBoolean(); // isRead
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(
|
||||
"INSERT INTO `guild_forum_views` (`user_id`, `guild_id`, `timestamp`) VALUES (?, ?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE `timestamp` = ?"
|
||||
)) {
|
||||
statement.setInt(1, userId);
|
||||
statement.setInt(2, guildId);
|
||||
statement.setInt(3, timestamp);
|
||||
statement.setInt(4, timestamp);
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
|
||||
// Invalidate caches so next request gets fresh data
|
||||
GuildForumDataComposer.invalidateLastSeenCache(userId, guildId);
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
-7
@@ -10,12 +10,21 @@ import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadMessagesComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadsComposer;
|
||||
import com.eu.habbo.messages.outgoing.handshake.ConnectionErrorComposer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
|
||||
public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildForumModerateThreadEvent.class);
|
||||
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 500;
|
||||
@@ -26,8 +35,6 @@ public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
int guildId = packet.readInt();
|
||||
int threadId = packet.readInt();
|
||||
int state = packet.readInt();
|
||||
// STATE 20 - HIDDEN_BY_GUILD_ADMIN = HIDDEN BY GUILD ADMINS/ HOTEL MODERATORS
|
||||
// STATE 1 = VISIBLE THREAD
|
||||
|
||||
Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(guildId);
|
||||
ForumThread thread = ForumThread.getById(threadId);
|
||||
@@ -57,18 +64,22 @@ public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Restrict state 20 (staff hidden) to staff only
|
||||
if (state == 20 && !hasStaffPerms) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
// State 20 = permanent delete (thread + comments removed from DB)
|
||||
if (state == 20) {
|
||||
deleteThread(threadId);
|
||||
ForumThread.clearCacheForGuild(guildId);
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
|
||||
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FORUMS_THREAD_HIDDEN.key).compose());
|
||||
this.client.sendResponse(new GuildForumThreadsComposer(guild, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
thread.setState(ForumThreadState.fromValue(state)); // sets state as defined in the packet
|
||||
thread.setState(ForumThreadState.fromValue(state));
|
||||
thread.run();
|
||||
|
||||
switch (state) {
|
||||
case 10:
|
||||
case 20:
|
||||
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FORUMS_THREAD_HIDDEN.key).compose());
|
||||
break;
|
||||
case 1:
|
||||
@@ -79,4 +90,22 @@ public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
this.client.sendResponse(new GuildForumThreadMessagesComposer(thread));
|
||||
this.client.sendResponse(new GuildForumThreadsComposer(guild, 0));
|
||||
}
|
||||
|
||||
private void deleteThread(int threadId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guilds_forums_comments` WHERE `thread_id` = ?")) {
|
||||
statement.setInt(1, threadId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guilds_forums_threads` WHERE `id` = ?")) {
|
||||
statement.setInt(1, threadId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to delete thread " + threadId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -9,6 +9,7 @@ import com.eu.habbo.habbohotel.guilds.forums.ForumThreadComment;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumAddCommentComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadMessagesComposer;
|
||||
import com.eu.habbo.messages.outgoing.handshake.ConnectionErrorComposer;
|
||||
|
||||
@@ -65,6 +66,7 @@ public class GuildForumPostThreadEvent extends MessageHandler {
|
||||
|
||||
this.client.getHabbo().getHabboStats().forumPostsCount += 1;
|
||||
thread.setPostsCount(thread.getPostsCount() + 1);
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
this.client.sendResponse(new GuildForumThreadMessagesComposer(thread));
|
||||
return;
|
||||
}
|
||||
@@ -100,6 +102,7 @@ public class GuildForumPostThreadEvent extends MessageHandler {
|
||||
thread.setUpdatedAt(Emulator.getIntUnixTimestamp());
|
||||
this.client.getHabbo().getHabboStats().forumPostsCount += 1;
|
||||
thread.setPostsCount(thread.getPostsCount() + 1);
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
this.client.sendResponse(new GuildForumAddCommentComposer(comment));
|
||||
} else {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(500));
|
||||
|
||||
-17
@@ -2,9 +2,6 @@ package com.eu.habbo.messages.incoming.guilds.forums;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.guilds.Guild;
|
||||
import com.eu.habbo.habbohotel.guilds.GuildMember;
|
||||
import com.eu.habbo.habbohotel.guilds.SettingsState;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadsComposer;
|
||||
@@ -28,20 +25,6 @@ public class GuildForumThreadsEvent extends MessageHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enforce read permissions
|
||||
boolean isStaff = this.client.getHabbo().hasPermission(Permission.ACC_MODTOOL_TICKET_Q);
|
||||
if (!isStaff && guild.canReadForum() != SettingsState.EVERYONE) {
|
||||
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, this.client.getHabbo().getHabboInfo().getId());
|
||||
if (guild.canReadForum() == SettingsState.MEMBERS && member == null) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
if (guild.canReadForum() == SettingsState.ADMINS && (member == null || member.getRank().type >= com.eu.habbo.habbohotel.guilds.GuildRank.MEMBER.type)) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.client.sendResponse(new GuildForumDataComposer(guild, this.client.getHabbo()));
|
||||
this.client.sendResponse(new GuildForumThreadsComposer(guild, index));
|
||||
}
|
||||
|
||||
-13
@@ -4,7 +4,6 @@ import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.guilds.Guild;
|
||||
import com.eu.habbo.habbohotel.guilds.GuildMember;
|
||||
import com.eu.habbo.habbohotel.guilds.GuildRank;
|
||||
import com.eu.habbo.habbohotel.guilds.SettingsState;
|
||||
import com.eu.habbo.habbohotel.guilds.forums.ForumThread;
|
||||
import com.eu.habbo.habbohotel.guilds.forums.ForumThreadState;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
@@ -48,18 +47,6 @@ public class GuildForumThreadsMessagesEvent extends MessageHandler {
|
||||
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, this.client.getHabbo().getHabboInfo().getId());
|
||||
boolean isGuildAdministrator = (guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || (member != null && member.getRank().equals(GuildRank.ADMIN)));
|
||||
|
||||
// Enforce read permissions
|
||||
if (!hasStaffPermissions && guild.canReadForum() != SettingsState.EVERYONE) {
|
||||
if (guild.canReadForum() == SettingsState.MEMBERS && member == null) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
if (guild.canReadForum() == SettingsState.ADMINS && !isGuildAdministrator) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (thread.getState() != ForumThreadState.HIDDEN_BY_GUILD_ADMIN || hasStaffPermissions || isGuildAdministrator) {
|
||||
this.client.sendResponse(new GuildForumCommentsComposer(guildId, threadId, index, thread.getComments(limit, index)));
|
||||
this.client.sendResponse(new GuildForumDataComposer(guild, this.client.getHabbo()));
|
||||
|
||||
@@ -62,6 +62,7 @@ public class GuildManageComposer extends MessageComposer {
|
||||
}
|
||||
this.response.appendString(this.guild.getBadge());
|
||||
this.response.appendInt(this.guild.getMemberCount());
|
||||
this.response.appendBoolean(this.guild.hasForum());
|
||||
return this.response;
|
||||
}
|
||||
|
||||
|
||||
+76
-27
@@ -20,11 +20,20 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
public class GuildForumDataComposer extends MessageComposer {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildForumDataComposer.class);
|
||||
|
||||
// Cache for user last-seen timestamps: key = "userId:guildId", value = {timestamp, cachedAt}
|
||||
private static final ConcurrentHashMap<String, long[]> lastSeenCache = new ConcurrentHashMap<>();
|
||||
private static final long LAST_SEEN_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
// Cache for unread counts: key = "guildId:lastSeenAt", value = {count, cachedAt}
|
||||
private static final ConcurrentHashMap<String, long[]> unreadCache = new ConcurrentHashMap<>();
|
||||
private static final long UNREAD_CACHE_TTL = 2 * 60 * 1000; // 2 minutes
|
||||
|
||||
public final Guild guild;
|
||||
public Habbo habbo;
|
||||
|
||||
@@ -33,13 +42,77 @@ public class GuildForumDataComposer extends MessageComposer {
|
||||
this.habbo = habbo;
|
||||
}
|
||||
|
||||
public static void invalidateLastSeenCache(int userId, int guildId) {
|
||||
lastSeenCache.remove(userId + ":" + guildId);
|
||||
}
|
||||
|
||||
public static void invalidateUnreadCache(int guildId) {
|
||||
unreadCache.entrySet().removeIf(entry -> entry.getKey().startsWith(guildId + ":"));
|
||||
}
|
||||
|
||||
private static int getLastSeenAt(int userId, int guildId) {
|
||||
String key = userId + ":" + guildId;
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
long[] cached = lastSeenCache.get(key);
|
||||
if (cached != null && (now - cached[1]) < LAST_SEEN_CACHE_TTL) {
|
||||
return (int) cached[0];
|
||||
}
|
||||
|
||||
int lastSeenAt = 0;
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT `timestamp` FROM `guild_forum_views` WHERE `user_id` = ? AND `guild_id` = ? LIMIT 1"
|
||||
)) {
|
||||
statement.setInt(1, userId);
|
||||
statement.setInt(2, guildId);
|
||||
ResultSet set = statement.executeQuery();
|
||||
if (set.next()) {
|
||||
lastSeenAt = set.getInt("timestamp");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
|
||||
lastSeenCache.put(key, new long[]{lastSeenAt, now});
|
||||
return lastSeenAt;
|
||||
}
|
||||
|
||||
private static int getUnreadCount(int guildId, int lastSeenAt) {
|
||||
String key = guildId + ":" + lastSeenAt;
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
long[] cached = unreadCache.get(key);
|
||||
if (cached != null && (now - cached[1]) < UNREAD_CACHE_TTL) {
|
||||
return (int) cached[0];
|
||||
}
|
||||
|
||||
int newComments = 0;
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT COUNT(*) FROM `guilds_forums_comments` " +
|
||||
"JOIN `guilds_forums_threads` ON `guilds_forums_threads`.`id` = `guilds_forums_comments`.`thread_id` " +
|
||||
"WHERE `guilds_forums_threads`.`guild_id` = ? AND `guilds_forums_comments`.`created_at` > ?"
|
||||
)) {
|
||||
statement.setInt(1, guildId);
|
||||
statement.setInt(2, lastSeenAt);
|
||||
|
||||
ResultSet set = statement.executeQuery();
|
||||
if (set.next()) {
|
||||
newComments = set.getInt(1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
|
||||
unreadCache.put(key, new long[]{newComments, now});
|
||||
return newComments;
|
||||
}
|
||||
|
||||
public static void serializeForumData(ServerMessage response, Guild guild, Habbo habbo) {
|
||||
|
||||
final THashSet<ForumThread> forumThreads = ForumThread.getByGuildId(guild.getId());
|
||||
int lastSeenAt = 0;
|
||||
int lastSeenAt = getLastSeenAt(habbo.getHabboInfo().getId(), guild.getId());
|
||||
|
||||
int totalComments = 0;
|
||||
int newComments = 0;
|
||||
int totalThreads = 0;
|
||||
ForumThreadComment lastComment = null;
|
||||
|
||||
@@ -55,31 +128,7 @@ public class GuildForumDataComposer extends MessageComposer {
|
||||
}
|
||||
}
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT COUNT(*) " +
|
||||
"FROM guilds_forums_threads A " +
|
||||
"JOIN ( " +
|
||||
"SELECT * " +
|
||||
"FROM `guilds_forums_comments` " +
|
||||
"WHERE `id` IN ( " +
|
||||
"SELECT id " +
|
||||
"FROM `guilds_forums_comments` B " +
|
||||
"ORDER BY B.`id` ASC " +
|
||||
") " +
|
||||
"ORDER BY `id` DESC " +
|
||||
") B ON A.`id` = B.`thread_id` " +
|
||||
"WHERE A.`guild_id` = ? AND B.`created_at` > ?"
|
||||
)) {
|
||||
statement.setInt(1, guild.getId());
|
||||
statement.setInt(2, lastSeenAt);
|
||||
|
||||
ResultSet set = statement.executeQuery();
|
||||
while (set.next()) {
|
||||
newComments = set.getInt(1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
int newComments = getUnreadCount(guild.getId(), lastSeenAt);
|
||||
|
||||
response.appendInt(guild.getId());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user