From aaad94f954ef218310791d55be33ac36834b47e9 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 02:14:02 +0200 Subject: [PATCH] fix(rcon): upsert offline pixel grants RCON GivePixels previously used an UPDATE for offline users, so users without an existing users_currency type 0 row received no pixels while the command still returned success. Match the GivePoints and housekeeping paths with an upsert and add a contract test that keeps offline pixel grants creating missing currency rows. --- .../eu/habbo/messages/rcon/GivePixels.java | 9 +++---- .../messages/rcon/GivePixelsContractTest.java | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 Emulator/src/test/java/com/eu/habbo/messages/rcon/GivePixelsContractTest.java diff --git a/Emulator/src/main/java/com/eu/habbo/messages/rcon/GivePixels.java b/Emulator/src/main/java/com/eu/habbo/messages/rcon/GivePixels.java index cf499056..3e0f228d 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/rcon/GivePixels.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/rcon/GivePixels.java @@ -26,10 +26,11 @@ public class GivePixels extends RCONMessage { if (habbo != null) { habbo.givePixels(object.pixels); } else { - try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("UPDATE users_currency SET users_currency.amount = users_currency.amount + ? WHERE users_currency.user_id = ? AND users_currency.type = 0")) { - statement.setInt(1, object.pixels); - statement.setInt(2, object.user_id); - statement.execute(); + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO users_currency (`user_id`, `type`, `amount`) VALUES (?, 0, ?) ON DUPLICATE KEY UPDATE amount = amount + ?")) { + statement.setInt(1, object.user_id); + statement.setInt(2, object.pixels); + statement.setInt(3, object.pixels); + statement.executeUpdate(); } catch (SQLException e) { this.status = RCONMessage.SYSTEM_ERROR; LOGGER.error("Caught SQL exception", e); diff --git a/Emulator/src/test/java/com/eu/habbo/messages/rcon/GivePixelsContractTest.java b/Emulator/src/test/java/com/eu/habbo/messages/rcon/GivePixelsContractTest.java new file mode 100644 index 00000000..af404d5c --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/messages/rcon/GivePixelsContractTest.java @@ -0,0 +1,24 @@ +package com.eu.habbo.messages.rcon; + +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class GivePixelsContractTest { + private static String givePixelsSource() throws Exception { + return Files.readString(Path.of("src/main/java/com/eu/habbo/messages/rcon/GivePixels.java")); + } + + @Test + void offlinePixelGrantCreatesMissingCurrencyRow() throws Exception { + String source = givePixelsSource(); + + assertTrue(source.contains("INSERT INTO users_currency"), + "Offline RCON pixel grants must create the users_currency type 0 row when it is missing"); + assertTrue(source.contains("ON DUPLICATE KEY UPDATE"), + "Offline RCON pixel grants should increment existing rows with an upsert"); + } +}