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
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b77290f5e7 | |||
| b14730d37f | |||
| 9126396973 | |||
| d321ff3b85 | |||
| 7f38a25eef | |||
| 4820ab15f3 | |||
| 8d989e7a19 | |||
| 1f7ec96e1c | |||
| 969f177108 | |||
| e485c2747c | |||
| d99a51899b | |||
| 29677a19be | |||
| 21ee36e089 | |||
| 4e47dbee16 | |||
| e7ba4d0926 | |||
| 67d2f52f64 |
@@ -23,6 +23,10 @@ SET NAMES utf8mb4;
|
||||
ALTER TABLE `emulator_settings`
|
||||
ADD COLUMN IF NOT EXISTS `comment` TEXT NULL DEFAULT '' AFTER `value`;
|
||||
|
||||
ALTER TABLE catalog_pages
|
||||
ADD COLUMN IF NOT EXISTS `catalog_mode` ENUM('NORMAL', 'BUILDER', 'BOTH') NOT NULL DEFAULT 'NORMAL' AFTER `includes`;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `wired_emulator_settings` (
|
||||
`key` VARCHAR(255) NOT NULL,
|
||||
`value` TEXT NOT NULL,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
-- ============================================================
|
||||
-- Fix: acc_supporttool wrongly granted to VIP / wrongly denied to Super Mod
|
||||
-- ============================================================
|
||||
-- The default permission_definitions seed shipped acc_supporttool
|
||||
-- with rank pattern (0, 1, 1, 1, 1, 0, 1) — i.e. rank_2 (VIP) and
|
||||
-- rank_3 (X, junior helper) had ALLOWED, while rank_6 (Super Mod)
|
||||
-- did NOT. That's two bugs:
|
||||
--
|
||||
-- * VIP users see the ModTools button on the toolbar and can
|
||||
-- open Room/User info windows. The actual sanction endpoints
|
||||
-- still gate on ACC_SUPPORTTOOL server-side so they can't
|
||||
-- actually moderate, but the UI exposure is wrong and lets a
|
||||
-- VIP request user info / room info / chatlogs they have no
|
||||
-- business reading.
|
||||
-- * Super Mod is denied the tool entirely, which is obviously
|
||||
-- unintended given the rank name.
|
||||
--
|
||||
-- Intended pattern: only Support (4) and up — (0, 0, 0, 1, 1, 1, 1).
|
||||
--
|
||||
-- Run on existing deployments to align with the corrected default
|
||||
-- seed in `Default Database/FullDatabase.sql`. Idempotent.
|
||||
|
||||
UPDATE `permission_definitions`
|
||||
SET `rank_1` = 0,
|
||||
`rank_2` = 0,
|
||||
`rank_3` = 0,
|
||||
`rank_4` = 1,
|
||||
`rank_5` = 1,
|
||||
`rank_6` = 1,
|
||||
`rank_7` = 1
|
||||
WHERE `permission_key` = 'acc_supporttool';
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE catalog_club_offers
|
||||
ADD COLUMN IF NOT EXISTS giftable ENUM('0','1') NOT NULL DEFAULT '0';
|
||||
|
||||
INSERT INTO emulator_texts (`key`, `value`)
|
||||
VALUES ('prereg.reward.you.received', 'You have recived:'),
|
||||
('generic.days', 'days');
|
||||
@@ -28598,7 +28598,7 @@ INSERT INTO `permission_definitions` (`permission_key`, `max_value`, `comment`,
|
||||
('acc_staff_chat', 1, 'Grants access to the in-game Staff Chat group buddy: receives broadcasts from other staff and can broadcast to anyone holding this permission.', 0, 0, 0, 0, 0, 0, 1),
|
||||
('acc_staff_pick', 1, 'Allows using staff item pick-up actions that bypass normal room ownership restrictions.', 0, 0, 0, 0, 0, 0, 1),
|
||||
('acc_superwired', 1, 'Allows saving advanced wired data without the normal wordfilter and reward payload restrictions applied to regular users.', 0, 0, 0, 0, 0, 0, 1),
|
||||
('acc_supporttool', 1, 'Allows opening and using the support/moderation tool interface.', 0, 1, 1, 1, 1, 0, 1),
|
||||
('acc_supporttool', 1, 'Allows opening and using the support/moderation tool interface.', 0, 0, 0, 1, 1, 1, 1),
|
||||
('acc_trade_anywhere', 1, 'Allows starting trades outside the normal trade-enabled areas.', 0, 0, 0, 0, 0, 0, 1),
|
||||
('acc_unignorable', 1, 'Prevents the account from being ignored by other users through the ignore system.', 0, 0, 0, 0, 0, 0, 0),
|
||||
('acc_unkickable', 1, 'Prevents the user from being kicked by normal moderation or room commands.', 0, 0, 0, 0, 0, 0, 1),
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.eu.habbo</groupId>
|
||||
<artifactId>Habbo</artifactId>
|
||||
<version>4.2.12</version>
|
||||
<version>4.2.15</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
@@ -72,7 +72,11 @@ public class ClubOffer implements ISerialize {
|
||||
this.type = OfferType.fromDatabase(set.getString("type"));
|
||||
this.vip = this.type == OfferType.VIP;
|
||||
this.deal = set.getString("deal").equals("1");
|
||||
this.giftable = set.getString("giftable").equals("1");
|
||||
boolean giftable = false;
|
||||
try {
|
||||
giftable = "1".equals(set.getString("giftable"));
|
||||
} catch (SQLException ignored) {}
|
||||
this.giftable = giftable;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
|
||||
@@ -651,6 +651,10 @@ public class ModToolManager {
|
||||
sender.getClient().sendResponse(new ModToolIssueHandledComposer(ModToolIssueHandledComposer.ABUSIVE));
|
||||
}
|
||||
|
||||
// Reporter (the user who opened the CFH) gets their abusive
|
||||
// counter bumped — the legacy stat shown in the User Info table.
|
||||
bumpUserSettingCounter(issue.senderId, "cfh_abusive");
|
||||
|
||||
this.updateTicketToMods(issue);
|
||||
|
||||
this.removeTicket(issue);
|
||||
@@ -737,4 +741,38 @@ public class ModToolManager {
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments a single integer counter on `users_settings` for the
|
||||
* given user. Used by the moderation sanction handlers to bump the
|
||||
* legacy counters that `ModToolUserInfoComposer` surfaces (cfh_warnings,
|
||||
* cfh_bans, cfh_abusive, tradelock_amount) — historically these were
|
||||
* only ever incremented by the CFH submission path, so a user could
|
||||
* accumulate any number of bans/mutes without the User Info table
|
||||
* reflecting it.
|
||||
*
|
||||
* Restricted to a whitelisted column name to keep the dynamic SQL
|
||||
* safe; the caller passes a Permission-style constant.
|
||||
*/
|
||||
public static void bumpUserSettingCounter(int userId, String column) {
|
||||
switch (column) {
|
||||
case "cfh_warnings":
|
||||
case "cfh_bans":
|
||||
case "cfh_abusive":
|
||||
case "tradelock_amount":
|
||||
break;
|
||||
default:
|
||||
LOGGER.warn("Refusing to bump unrecognized user_settings column: {}", column);
|
||||
return;
|
||||
}
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"UPDATE users_settings SET " + column + " = " + column + " + 1 WHERE user_id = ?")) {
|
||||
statement.setInt(1, userId);
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception bumping {} for user {}", column, userId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+173
-8
@@ -2,10 +2,8 @@ package com.eu.habbo.messages.incoming.catalog;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.achievements.AchievementManager;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogItem;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogLimitedConfiguration;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogManager;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogPage;
|
||||
import com.eu.habbo.habbohotel.catalog.*;
|
||||
import com.eu.habbo.habbohotel.catalog.layouts.*;
|
||||
import com.eu.habbo.habbohotel.items.FurnitureType;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.*;
|
||||
@@ -14,6 +12,7 @@ import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboBadge;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.users.subscriptions.Subscription;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.catalog.*;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer;
|
||||
@@ -22,16 +21,14 @@ import com.eu.habbo.messages.outgoing.generic.alerts.GenericAlertComposer;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.HotelWillCloseInMinutesComposer;
|
||||
import com.eu.habbo.messages.outgoing.inventory.AddHabboItemComposer;
|
||||
import com.eu.habbo.messages.outgoing.inventory.InventoryRefreshComposer;
|
||||
import com.eu.habbo.messages.outgoing.users.UserClubComposer;
|
||||
import com.eu.habbo.threading.runnables.ShutdownEmulator;
|
||||
import gnu.trove.map.hash.THashMap;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.*;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class CatalogBuyItemAsGiftEvent extends MessageHandler {
|
||||
@@ -82,6 +79,12 @@ public class CatalogBuyItemAsGiftEvent extends MessageHandler {
|
||||
|
||||
int userId = 0;
|
||||
|
||||
CatalogPage clubGiftPage = Emulator.getGameEnvironment().getCatalogManager().catalogPages.get(pageId);
|
||||
if (this.isClubOfferPage(clubGiftPage)) {
|
||||
this.handleClubOfferGift(clubGiftPage, itemId, username);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Emulator.getGameEnvironment().getCatalogManager().giftWrappers.containsKey(spriteId)
|
||||
&& !Emulator.getGameEnvironment().getCatalogManager().giftFurnis.containsKey(spriteId)) {
|
||||
LOGGER.error("DEBUG GIFT: invalid spriteId for gift wrapper/furni -> {}", spriteId);
|
||||
@@ -554,4 +557,166 @@ public class CatalogBuyItemAsGiftEvent extends MessageHandler {
|
||||
this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isClubOfferPage(CatalogPage page) {
|
||||
return page instanceof ClubBuyLayout
|
||||
|| page instanceof VipBuyLayout
|
||||
|| page instanceof BuildersClubFrontPageLayout
|
||||
|| page instanceof BuildersClubAddonsLayout
|
||||
|| page instanceof BuildersClubLoyaltyLayout;
|
||||
}
|
||||
|
||||
private int getClubOfferWindowId(CatalogPage page) {
|
||||
if (page instanceof BuildersClubAddonsLayout) {
|
||||
return ClubOffer.WINDOW_BUILDERS_CLUB_ADDONS;
|
||||
}
|
||||
|
||||
if (page instanceof BuildersClubFrontPageLayout || page instanceof BuildersClubLoyaltyLayout) {
|
||||
return ClubOffer.WINDOW_BUILDERS_CLUB;
|
||||
}
|
||||
|
||||
return ClubOffer.WINDOW_HABBO_CLUB;
|
||||
}
|
||||
|
||||
private void handleClubOfferGift(CatalogPage page, int offerId, String username) {
|
||||
ClubOffer offer = Emulator.getGameEnvironment().getCatalogManager().clubOffers.get(offerId);
|
||||
|
||||
if (offer == null || !offer.belongsToWindow(this.getClubOfferWindowId(page))) {
|
||||
this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!offer.isGiftable()) {
|
||||
this.client.sendResponse(new AlertPurchaseUnavailableComposer(AlertPurchaseUnavailableComposer.ILLEGAL));
|
||||
return;
|
||||
}
|
||||
|
||||
if (offer.isBuildersClubAddon()) {
|
||||
this.client.sendResponse(new AlertPurchaseUnavailableComposer(AlertPurchaseUnavailableComposer.ILLEGAL));
|
||||
return;
|
||||
}
|
||||
|
||||
int totalCredits = offer.getCredits();
|
||||
int totalPoints = offer.getPoints();
|
||||
|
||||
if (totalCredits > this.client.getHabbo().getHabboInfo().getCredits()
|
||||
|| totalPoints > this.client.getHabbo().getHabboInfo().getCurrencyAmount(offer.getPointsType())) {
|
||||
this.client.sendResponse(new AlertPurchaseUnavailableComposer(AlertPurchaseUnavailableComposer.ILLEGAL));
|
||||
return;
|
||||
}
|
||||
|
||||
Habbo recipient = Emulator.getGameEnvironment().getHabboManager().getHabbo(username);
|
||||
int recipientId = 0;
|
||||
|
||||
if (recipient != null) {
|
||||
recipientId = recipient.getHabboInfo().getId();
|
||||
} else {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT id FROM users WHERE username = ?")) {
|
||||
statement.setString(1, username);
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (set.next()) recipientId = set.getInt(1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception while resolving club gift recipient", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (recipientId == 0) {
|
||||
this.client.sendResponse(new GiftReceiverNotFoundComposer());
|
||||
return;
|
||||
}
|
||||
|
||||
if (recipientId == this.client.getHabbo().getHabboInfo().getId()) {
|
||||
this.client.sendResponse(new AlertPurchaseUnavailableComposer(AlertPurchaseUnavailableComposer.ILLEGAL));
|
||||
return;
|
||||
}
|
||||
|
||||
String subscriptionType = offer.isBuildersClubSubscription() ? Subscription.BUILDERS_CLUB : Subscription.HABBO_CLUB;
|
||||
int duration = offer.getDays() * 86400;
|
||||
|
||||
boolean extended;
|
||||
if (recipient != null) {
|
||||
extended = (recipient.getHabboStats().createSubscription(subscriptionType, duration) != null);
|
||||
} else {
|
||||
extended = this.extendOfflineSubscription(recipientId, subscriptionType, duration);
|
||||
}
|
||||
|
||||
if (!extended) {
|
||||
this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
if (totalCredits > 0 && !this.client.getHabbo().hasPermission(Permission.ACC_INFINITE_CREDITS)) {
|
||||
this.client.getHabbo().giveCredits(-totalCredits);
|
||||
}
|
||||
|
||||
if (totalPoints > 0) {
|
||||
if (offer.getPointsType() == 0 && !this.client.getHabbo().hasPermission(Permission.ACC_INFINITE_PIXELS)) {
|
||||
this.client.getHabbo().givePixels(-totalPoints);
|
||||
} else if (!this.client.getHabbo().hasPermission(Permission.ACC_INFINITE_POINTS)) {
|
||||
this.client.getHabbo().givePoints(offer.getPointsType(), -totalPoints);
|
||||
}
|
||||
}
|
||||
|
||||
if (recipient != null) {
|
||||
recipient.getClient().sendResponse(new UserClubComposer(recipient, subscriptionType, UserClubComposer.RESPONSE_TYPE_NORMAL));
|
||||
|
||||
String prefix = Emulator.getTexts().getValue("prereg.reward.you.received", "You have received:");
|
||||
String daysWord = Emulator.getTexts().getValue("generic.days", "days");
|
||||
String clubLabel = offer.isBuildersClubSubscription() ? "Builders Club" : "HC";
|
||||
String giftDescription = clubLabel + " (" + offer.getDays() + " " + daysWord + ")";
|
||||
THashMap<String, String> keys = new THashMap<>();
|
||||
keys.put("display", "BUBBLE");
|
||||
keys.put("image", "${image.library.url}notifications/gift.gif");
|
||||
keys.put("message", prefix + " " + giftDescription);
|
||||
recipient.getClient().sendResponse(new BubbleAlertComposer(BubbleAlertKeys.RECEIVED_GIFT.key, keys));
|
||||
}
|
||||
|
||||
if (this.client.getHabbo().getHabboInfo().getId() != recipientId) {
|
||||
AchievementManager.progressAchievement(
|
||||
this.client.getHabbo(),
|
||||
Emulator.getGameEnvironment().getAchievementManager().getAchievement("GiftGiver")
|
||||
);
|
||||
}
|
||||
|
||||
this.client.sendResponse(new PurchaseOKComposer(null));
|
||||
}
|
||||
|
||||
private boolean extendOfflineSubscription(int userId, String subscriptionType, int duration) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
try (PreparedStatement select = connection.prepareStatement(
|
||||
"SELECT id, duration FROM users_subscriptions WHERE user_id = ? AND subscription_type = ? AND active = 1 ORDER BY id DESC LIMIT 1")) {
|
||||
select.setInt(1, userId);
|
||||
select.setString(2, subscriptionType);
|
||||
try (ResultSet set = select.executeQuery()) {
|
||||
if (set.next()) {
|
||||
int subId = set.getInt("id");
|
||||
int existing = set.getInt("duration");
|
||||
try (PreparedStatement update = connection.prepareStatement(
|
||||
"UPDATE users_subscriptions SET duration = ? WHERE id = ?")) {
|
||||
update.setInt(1, existing + duration);
|
||||
update.setInt(2, subId);
|
||||
update.executeUpdate();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try (PreparedStatement insert = connection.prepareStatement(
|
||||
"INSERT INTO users_subscriptions (user_id, subscription_type, timestamp_start, duration, active) VALUES (?, ?, ?, ?, 1)",
|
||||
Statement.RETURN_GENERATED_KEYS)) {
|
||||
insert.setInt(1, userId);
|
||||
insert.setString(2, subscriptionType);
|
||||
insert.setInt(3, Emulator.getIntUnixTimestamp());
|
||||
insert.setInt(4, duration);
|
||||
insert.executeUpdate();
|
||||
return true;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception while extending offline subscription", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -36,8 +36,21 @@ public class CatalogAdminCreatePageEvent extends MessageHandler {
|
||||
pageLayout = CatalogPageLayouts.default_3x3;
|
||||
}
|
||||
|
||||
if (parentId != -1 && Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(parentId) == null) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "Parent page not found: " + parentId));
|
||||
return;
|
||||
}
|
||||
|
||||
if (iconType < 0) iconType = 0;
|
||||
if (minRank < 1) minRank = 1;
|
||||
if (orderNum < 0) orderNum = 0;
|
||||
if (caption == null) caption = "";
|
||||
if (caption2 == null) caption2 = "";
|
||||
if (caption.length() > 128) caption = caption.substring(0, 128);
|
||||
if (caption2.length() > 25) caption2 = caption2.substring(0, 25);
|
||||
|
||||
CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().createCatalogPage(
|
||||
caption, caption2, 0, iconType, pageLayout, minRank, parentId, pageType, catalogMode
|
||||
caption, caption2, 0, iconType, pageLayout, minRank, parentId, pageType, catalogMode
|
||||
);
|
||||
|
||||
if (page == null) {
|
||||
|
||||
+43
-7
@@ -1,6 +1,7 @@
|
||||
package com.eu.habbo.messages.incoming.catalog.catalogadmin;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogPage;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogPageType;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
@@ -11,6 +12,9 @@ import java.sql.PreparedStatement;
|
||||
|
||||
public class CatalogAdminMovePageEvent extends MessageHandler {
|
||||
|
||||
private static final int MAX_PARENT_WALK = 64;
|
||||
private static final int ROOT_PARENT_ID = -1;
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
if (!this.client.getHabbo().hasPermission(Permission.ACC_CATALOGFURNI)) {
|
||||
@@ -24,12 +28,10 @@ public class CatalogAdminMovePageEvent extends MessageHandler {
|
||||
CatalogPageType pageType = CatalogPageType.fromString(this.packet.readString());
|
||||
String tableName = (pageType == CatalogPageType.BUILDER) ? "catalog_pages_bc" : "catalog_pages";
|
||||
|
||||
// Special values: -1 = toggle enabled, -2 = toggle visible
|
||||
if (newParentId == -1) {
|
||||
// Toggle enabled
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"UPDATE " + tableName + " SET enabled = IF(enabled = '1', '0', '1') WHERE id = ?")) {
|
||||
"UPDATE " + tableName + " SET enabled = IF(enabled = '1', '0', '1') WHERE id = ?")) {
|
||||
statement.setInt(1, pageId);
|
||||
statement.execute();
|
||||
}
|
||||
@@ -38,21 +40,43 @@ public class CatalogAdminMovePageEvent extends MessageHandler {
|
||||
}
|
||||
|
||||
if (newParentId == -2) {
|
||||
// Toggle visible
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"UPDATE " + tableName + " SET visible = IF(visible = '1', '0', '1') WHERE id = ?")) {
|
||||
"UPDATE " + tableName + " SET visible = IF(visible = '1', '0', '1') WHERE id = ?")) {
|
||||
statement.setInt(1, pageId);
|
||||
statement.execute();
|
||||
}
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(true, "Visibility toggled"));
|
||||
return;
|
||||
}
|
||||
|
||||
CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(pageId, pageType);
|
||||
if (page == null) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "Page not found: " + pageId));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newParentId == pageId) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "A page cannot be its own parent"));
|
||||
return;
|
||||
}
|
||||
|
||||
CatalogPage parent = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(newParentId);
|
||||
if (parent == null) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "Parent page not found: " + newParentId));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.wouldCreateCycle(pageId, newParentId)) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "Refusing to move: that would create a cycle"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newIndex < 0) newIndex = 0;
|
||||
|
||||
// Normal move: update parent and order
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"UPDATE " + tableName + " SET parent_id = ?, order_num = ? WHERE id = ?")) {
|
||||
"UPDATE " + tableName + " SET parent_id = ?, order_num = ? WHERE id = ?")) {
|
||||
statement.setInt(1, newParentId);
|
||||
statement.setInt(2, newIndex);
|
||||
statement.setInt(3, pageId);
|
||||
@@ -61,4 +85,16 @@ public class CatalogAdminMovePageEvent extends MessageHandler {
|
||||
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(true, "Page moved"));
|
||||
}
|
||||
|
||||
private boolean wouldCreateCycle(int pageId, int parentId) {
|
||||
int current = parentId;
|
||||
for (int hops = 0; hops < MAX_PARENT_WALK; hops++) {
|
||||
if (current == ROOT_PARENT_ID) return false;
|
||||
if (current == pageId) return true;
|
||||
CatalogPage parent = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(current);
|
||||
if (parent == null) return false;
|
||||
current = parent.getParentId();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+62
-1
@@ -2,6 +2,7 @@ package com.eu.habbo.messages.incoming.catalog.catalogadmin;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogPage;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogPageLayouts;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogPageType;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
@@ -12,6 +13,14 @@ import java.sql.PreparedStatement;
|
||||
|
||||
public class CatalogAdminSavePageEvent extends MessageHandler {
|
||||
|
||||
private static final int MAX_CAPTION_LENGTH = 128;
|
||||
private static final int MAX_CAPTION_SAVE_LENGTH = 25;
|
||||
private static final int MAX_HEADLINE_LENGTH = 1024;
|
||||
private static final int MAX_TEASER_LENGTH = 64;
|
||||
private static final int MAX_TEXT_LENGTH = 8192;
|
||||
private static final int MAX_PARENT_WALK = 64;
|
||||
private static final int ROOT_PARENT_ID = -1;
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
if (!this.client.getHabbo().hasPermission(Permission.ACC_CATALOGFURNI)) {
|
||||
@@ -34,7 +43,6 @@ public class CatalogAdminSavePageEvent extends MessageHandler {
|
||||
String textDetails = this.packet.readString();
|
||||
CatalogPageType pageType = CatalogPageType.fromString(this.packet.readString());
|
||||
CatalogPageType catalogMode = CatalogPageType.fromString(this.packet.readString());
|
||||
|
||||
CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(pageId, pageType);
|
||||
|
||||
if (page == null) {
|
||||
@@ -42,6 +50,41 @@ public class CatalogAdminSavePageEvent extends MessageHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
CatalogPageLayouts.valueOf(layout);
|
||||
} catch (IllegalArgumentException | NullPointerException e) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "Invalid layout: " + layout));
|
||||
return;
|
||||
}
|
||||
|
||||
if (parentId != ROOT_PARENT_ID) {
|
||||
if (parentId == pageId) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "A page cannot be its own parent"));
|
||||
return;
|
||||
}
|
||||
|
||||
CatalogPage parent = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(parentId);
|
||||
if (parent == null) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "Parent page not found: " + parentId));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.wouldCreateCycle(pageId, parentId)) {
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(false, "Refusing to re-parent: that would create a cycle"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (iconType < 0) iconType = 0;
|
||||
if (minRank < 1) minRank = 1;
|
||||
if (orderNum < 0) orderNum = 0;
|
||||
|
||||
caption = this.clampLength(caption, MAX_CAPTION_LENGTH);
|
||||
caption2 = this.clampLength(caption2, MAX_CAPTION_SAVE_LENGTH);
|
||||
headline = this.clampLength(headline, MAX_HEADLINE_LENGTH);
|
||||
teaser = this.clampLength(teaser, MAX_TEASER_LENGTH);
|
||||
textDetails = this.clampLength(textDetails, MAX_TEXT_LENGTH);
|
||||
|
||||
String query = (pageType == CatalogPageType.BUILDER)
|
||||
? "UPDATE catalog_pages_bc SET caption = ?, page_layout = ?, icon_image = ?, visible = ?, enabled = ?, order_num = ?, parent_id = ?, page_headline = ?, page_teaser = ?, page_text_details = ? WHERE id = ?"
|
||||
: "UPDATE catalog_pages SET caption = ?, caption_save = ?, page_layout = ?, icon_image = ?, min_rank = ?, visible = ?, enabled = ?, order_num = ?, parent_id = ?, page_headline = ?, page_teaser = ?, page_text_details = ?, catalog_mode = ? WHERE id = ?";
|
||||
@@ -82,4 +125,22 @@ public class CatalogAdminSavePageEvent extends MessageHandler {
|
||||
|
||||
this.client.sendResponse(new CatalogAdminResultComposer(true, "Page saved"));
|
||||
}
|
||||
|
||||
private boolean wouldCreateCycle(int pageId, int parentId) {
|
||||
int current = parentId;
|
||||
for (int hops = 0; hops < MAX_PARENT_WALK; hops++) {
|
||||
if (current == ROOT_PARENT_ID) return false;
|
||||
if (current == pageId) return true;
|
||||
CatalogPage parent = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(current);
|
||||
if (parent == null) return false;
|
||||
current = parent.getParentId();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String clampLength(String value, int max) {
|
||||
if (value == null) return "";
|
||||
if (value.length() <= max) return value;
|
||||
return value.substring(0, max);
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -1,6 +1,7 @@
|
||||
package com.eu.habbo.messages.incoming.modtool;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolManager;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctionItem;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctions;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
@@ -47,6 +48,8 @@ public class ModToolSanctionAlertEvent extends MessageHandler {
|
||||
} else {
|
||||
habbo.alert(message);
|
||||
}
|
||||
|
||||
ModToolManager.bumpUserSettingCounter(userId, "cfh_warnings");
|
||||
} else {
|
||||
this.client.sendResponse(new ModToolIssueHandledComposer(Emulator.getTexts().getValue("generic.user.not_found").replace("%user%", Emulator.getConfig().getValue("hotel.player.name"))));
|
||||
}
|
||||
|
||||
+2
@@ -2,6 +2,7 @@ package com.eu.habbo.messages.incoming.modtool;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolBanType;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolManager;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctionItem;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctions;
|
||||
import com.eu.habbo.habbohotel.modtool.ScripterManager;
|
||||
@@ -73,6 +74,7 @@ public class ModToolSanctionBanEvent extends MessageHandler {
|
||||
Emulator.getGameEnvironment().getModToolManager().ban(userId, this.client.getHabbo(), message, duration, ModToolBanType.ACCOUNT, cfhTopic);
|
||||
}
|
||||
|
||||
ModToolManager.bumpUserSettingCounter(userId, "cfh_bans");
|
||||
} else {
|
||||
ScripterManager.scripterDetected(this.client, Emulator.getTexts().getValue("scripter.warning.modtools.ban").replace("%username%", this.client.getHabbo().getHabboInfo().getUsername()));
|
||||
}
|
||||
|
||||
+3
@@ -1,6 +1,7 @@
|
||||
package com.eu.habbo.messages.incoming.modtool;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolManager;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctionItem;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctionLevelItem;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctions;
|
||||
@@ -59,6 +60,8 @@ public class ModToolSanctionMuteEvent extends MessageHandler {
|
||||
habbo.alert(message);
|
||||
this.client.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_mute.muted").replace("%user%", habbo.getHabboInfo().getUsername()));
|
||||
}
|
||||
|
||||
ModToolManager.bumpUserSettingCounter(userId, "cfh_warnings");
|
||||
} else {
|
||||
this.client.sendResponse(new ModToolIssueHandledComposer(Emulator.getTexts().getValue("generic.user.not_found").replace("%user%", Emulator.getConfig().getValue("hotel.player.name"))));
|
||||
}
|
||||
|
||||
+3
@@ -1,6 +1,7 @@
|
||||
package com.eu.habbo.messages.incoming.modtool;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolManager;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctionItem;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctions;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
@@ -49,6 +50,8 @@ public class ModToolSanctionTradeLockEvent extends MessageHandler {
|
||||
habbo.getHabboStats().setAllowTrade(false);
|
||||
habbo.alert(message);
|
||||
}
|
||||
|
||||
ModToolManager.bumpUserSettingCounter(userId, "tradelock_amount");
|
||||
} else {
|
||||
this.client.sendResponse(new ModToolIssueHandledComposer(Emulator.getTexts().getValue("generic.user.not_found").replace("%user%", Emulator.getConfig().getValue("hotel.player.name"))));
|
||||
}
|
||||
|
||||
+3
-1
@@ -41,6 +41,7 @@ public class CatalogPagesListComposer extends MessageComposer {
|
||||
this.response.appendBoolean(true);
|
||||
this.response.appendInt(0);
|
||||
this.response.appendInt(-1);
|
||||
this.response.appendInt(-1);
|
||||
this.response.appendString("root");
|
||||
this.response.appendString("");
|
||||
this.response.appendInt(0);
|
||||
@@ -68,7 +69,8 @@ public class CatalogPagesListComposer extends MessageComposer {
|
||||
|
||||
this.response.appendBoolean(category.isVisible());
|
||||
this.response.appendInt(category.getIconImage());
|
||||
this.response.appendInt(category.isEnabled() ? category.getId() : -1);
|
||||
this.response.appendInt(category.isEnabled() || this.hasPermission ? category.getId() : -1);
|
||||
this.response.appendInt(category.getParentId());
|
||||
this.response.appendString(category.getPageName());
|
||||
this.response.appendString(category.getCaption() + (this.hasPermission ? " (" + category.getId() + ")" : ""));
|
||||
|
||||
|
||||
+100
-21
@@ -12,10 +12,13 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
public class ModToolUserInfoComposer extends MessageComposer {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ModToolUserInfoComposer.class);
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
|
||||
private final ResultSet set;
|
||||
private final boolean hideMail;
|
||||
@@ -29,37 +32,30 @@ public class ModToolUserInfoComposer extends MessageComposer {
|
||||
protected ServerMessage composeInternal() {
|
||||
this.response.init(Outgoing.ModToolUserInfoComposer);
|
||||
try {
|
||||
int totalBans = 0;
|
||||
int userId = this.set.getInt("user_id");
|
||||
String machineId = this.set.getString("machine_id");
|
||||
int now = Emulator.getIntUnixTimestamp();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) AS amount FROM bans WHERE user_id = ?")) {
|
||||
statement.setInt(1, this.set.getInt("user_id"));
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (set.next()) {
|
||||
totalBans = set.getInt("amount");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
int totalBans = countBansForUser(userId);
|
||||
int lastPurchaseTimestamp = fetchLastPurchaseTimestamp(userId);
|
||||
int tradeLockExpiryTimestamp = fetchActiveTradeLockExpiry(userId, now);
|
||||
int identityRelatedBanCount = countIdentityRelatedBans(userId, machineId);
|
||||
|
||||
this.response.appendInt(this.set.getInt("user_id"));
|
||||
this.response.appendInt(userId);
|
||||
this.response.appendString(this.set.getString("username"));
|
||||
this.response.appendString(this.set.getString("look"));
|
||||
this.response.appendInt((Emulator.getIntUnixTimestamp() - this.set.getInt("account_created")) / 60);
|
||||
this.response.appendInt((this.set.getInt("online") == 1 ? 0 : Emulator.getIntUnixTimestamp() - this.set.getInt("last_online")) / 60);
|
||||
this.response.appendInt((now - this.set.getInt("account_created")) / 60);
|
||||
this.response.appendInt((this.set.getInt("online") == 1 ? 0 : now - this.set.getInt("last_online")) / 60);
|
||||
this.response.appendBoolean(this.set.getInt("online") == 1);
|
||||
this.response.appendInt(this.set.getInt("cfh_send"));
|
||||
this.response.appendInt(this.set.getInt("cfh_abusive"));
|
||||
this.response.appendInt(this.set.getInt("cfh_warnings"));
|
||||
this.response.appendInt(totalBans); // Number of bans
|
||||
this.response.appendInt(this.set.getInt("tradelock_amount"));
|
||||
this.response.appendString(""); //Trading lock expiry timestamp
|
||||
this.response.appendString(""); //Last Purchase Timestamp
|
||||
this.response.appendInt(this.set.getInt("user_id")); //Personal Identification #
|
||||
this.response.appendInt(0); // Number of account bans
|
||||
this.response.appendString(formatUnixTimestamp(tradeLockExpiryTimestamp)); // Trading lock expiry timestamp
|
||||
this.response.appendString(formatUnixTimestamp(lastPurchaseTimestamp)); // Last Purchase Timestamp
|
||||
this.response.appendInt(userId); //Personal Identification #
|
||||
this.response.appendInt(identityRelatedBanCount); // Number of account bans on the same machine_id
|
||||
this.response.appendString(this.hideMail ? "" : this.set.getString("mail"));
|
||||
this.response.appendString("Rank (" + this.set.getInt("rank_id") + "): " + this.set.getString("rank_name")); //user_class_txt
|
||||
|
||||
@@ -90,4 +86,87 @@ public class ModToolUserInfoComposer extends MessageComposer {
|
||||
public ResultSet getSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
private static int countBansForUser(int userId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) AS amount FROM bans WHERE user_id = ?")) {
|
||||
statement.setInt(1, userId);
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (set.next()) return set.getInt("amount");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Most recent purchase timestamp from logs_shop_purchases for this
|
||||
* user. Returns 0 when the user has never bought anything (in which
|
||||
* case the wire field stays empty and the client shows the empty
|
||||
* placeholder).
|
||||
*/
|
||||
private static int fetchLastPurchaseTimestamp(int userId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT MAX(`timestamp`) AS ts FROM logs_shop_purchases WHERE user_id = ?")) {
|
||||
statement.setInt(1, userId);
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (set.next()) return set.getInt("ts");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Latest active trade-lock expiry from the sanctions table. Only
|
||||
* locks expiring in the future are considered — past entries don't
|
||||
* count. Returns 0 when no active lock exists.
|
||||
*/
|
||||
private static int fetchActiveTradeLockExpiry(int userId, int now) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT MAX(trade_locked_until) AS expiry FROM sanctions WHERE habbo_id = ? AND trade_locked_until > ?")) {
|
||||
statement.setInt(1, userId);
|
||||
statement.setInt(2, now);
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (set.next()) return set.getInt("expiry");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of OTHER user accounts that have been banned from the same
|
||||
* machine_id as this user. An empty machine_id (default '') is
|
||||
* ignored — never matches anything by definition. Self is excluded
|
||||
* because the user's own bans are already counted under banCount.
|
||||
*/
|
||||
private static int countIdentityRelatedBans(int userId, String machineId) {
|
||||
if (machineId == null || machineId.isEmpty()) return 0;
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT COUNT(DISTINCT user_id) AS amount FROM bans WHERE machine_id = ? AND user_id != ?")) {
|
||||
statement.setString(1, machineId);
|
||||
statement.setInt(2, userId);
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (set.next()) return set.getInt("amount");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wire format for date fields is `yyyy-MM-dd HH:mm`. A 0 timestamp
|
||||
* is rendered as an empty string so the client falls back to its
|
||||
* empty-state placeholder.
|
||||
*/
|
||||
private static String formatUnixTimestamp(int timestamp) {
|
||||
if (timestamp <= 0) return "";
|
||||
return DATE_FORMAT.format(new Date(timestamp * 1000L));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user