diff --git a/Database Updates/003_live_required_schema.sql b/Database Updates/003_live_required_schema.sql index f1e795b7..5d8b5462 100644 --- a/Database Updates/003_live_required_schema.sql +++ b/Database Updates/003_live_required_schema.sql @@ -435,6 +435,16 @@ ON DUPLICATE KEY UPDATE `triggers_talking_furniture` = VALUES(`triggers_talking_furniture`); INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES + ('commands.description.acc_modtool_room_info', 'Allows viewing room information in the moderation tool.'), + ('commands.description.cmd_add_youtube_playlist', ':add_youtube '), + ('commands.description.cmd_disablemassmentions', ':disablemassmentions'), + ('commands.description.cmd_disablementions', ':disablementions'), + ('commands.description.cmd_give_prefix', ':giveprefix [icon] [effect]'), + ('commands.description.cmd_hidewired', ':hidewired'), + ('commands.description.cmd_list_prefixes', ':listprefixes '), + ('commands.description.cmd_remove_prefix', ':removeprefix '), + ('commands.description.cmd_setroom_template', ':setroom_template'), + ('commands.description.cmd_update_youtube_playlists', ':update_youtube'), ('commands.keys.cmd_setroom_template', 'setroom_template;set_room_template'), ('commands.succes.cmd_setroom_template.verify', 'Copy the current room "%roomname%" to room_templates? Type :setroom_template %generic.yes% to confirm.'), ('commands.succes.cmd_setroom_template', 'Room saved as template id %id% with %items% items (%skipped% skipped - item_id not in items_base).'), diff --git a/Database Updates/009_mentions_wordfilter.sql b/Database Updates/009_mentions_wordfilter.sql index 2d4935b6..49d08368 100644 --- a/Database Updates/009_mentions_wordfilter.sql +++ b/Database Updates/009_mentions_wordfilter.sql @@ -37,6 +37,10 @@ VALUES ON DUPLICATE KEY UPDATE `comment` = VALUES(`comment`); +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES + ('commands.description.cmd_disablementions', ':disablementions'), + ('commands.description.cmd_disablemassmentions', ':disablemassmentions'); + -- ---------------------------------------------------------------------------- -- 3. Emulator settings: cooldowns, caps and alias lists diff --git a/Database Updates/Own_Database_RunFirst/011_HotelLogin.sql b/Database Updates/Own_Database_RunFirst/011_HotelLogin.sql index 666a9b5f..639f8652 100644 --- a/Database Updates/Own_Database_RunFirst/011_HotelLogin.sql +++ b/Database Updates/Own_Database_RunFirst/011_HotelLogin.sql @@ -49,6 +49,7 @@ INSERT INTO `permission_definitions` (`permission_key`, `rank_7`, `comment`) VAL ON DUPLICATE KEY UPDATE `rank_7` = VALUES(`rank_7`); INSERT INTO `emulator_texts` (`key`, `value`) VALUES + ('commands.description.cmd_setroom_template', ':setroom_template'), ('commands.keys.cmd_setroom_template', 'setroom_template;set_room_template'), ('commands.succes.cmd_setroom_template.verify', 'Copy the current room "%roomname%" to room_templates? Type :setroom_template %generic.yes% to confirm.'), ('commands.succes.cmd_setroom_template', 'Room saved as template id %id% with %items% items (%skipped% skipped - item_id not in items_base).'), diff --git a/Database Updates/Own_Database_RunFirst/016_custom_prefixes_setup.sql b/Database Updates/Own_Database_RunFirst/016_custom_prefixes_setup.sql index dd48a23f..080376ff 100644 --- a/Database Updates/Own_Database_RunFirst/016_custom_prefixes_setup.sql +++ b/Database Updates/Own_Database_RunFirst/016_custom_prefixes_setup.sql @@ -301,6 +301,7 @@ INSERT IGNORE INTO `custom_prefixes_catalog` INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES -- GivePrefix command + ('commands.description.cmd_give_prefix', ':giveprefix [icon] [effect]'), ('commands.keys.cmd_give_prefix', 'giveprefix'), ('commands.error.cmd_give_prefix.usage', 'Usage: :giveprefix [icon] [effect]'), ('commands.error.cmd_give_prefix.invalid_color', 'Invalid color format. Use hex format (#FF0000).'), @@ -308,12 +309,14 @@ INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_give_prefix.user_not_found', 'User not found or not online.'), ('commands.succes.cmd_give_prefix', 'Prefix {%prefix%} successfully given to %user%!'), -- ListPrefixes command + ('commands.description.cmd_list_prefixes', ':listprefixes '), ('commands.keys.cmd_list_prefixes', 'listprefixes'), ('commands.error.cmd_list_prefixes.usage', 'Usage: :listprefixes '), ('commands.error.cmd_list_prefixes.user_not_found', 'User not found or not online.'), ('commands.succes.cmd_list_prefixes.header', 'Prefixes of %user%:'), ('commands.succes.cmd_list_prefixes.empty', '%user% has no prefixes.'), -- RemovePrefix command + ('commands.description.cmd_remove_prefix', ':removeprefix '), ('commands.keys.cmd_remove_prefix', 'removeprefix'), ('commands.error.cmd_remove_prefix.usage', 'Usage: :removeprefix '), ('commands.error.cmd_remove_prefix.user_not_found', 'User not found or not online.'), diff --git a/Default Database/FullDatabase.sql b/Default Database/FullDatabase.sql index c7e85d40..ff7f9c3b 100644 --- a/Default Database/FullDatabase.sql +++ b/Default Database/FullDatabase.sql @@ -15355,7 +15355,9 @@ INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.cmd_promote_offer.list', 'All available offers (%amount%):
%list%'), ('commands.cmd_promote_offer.list.entry', '%id%: %title% %description%'), ('commands.description.acc_debug', ':test [header] i:1 s:a b:1'), + ('commands.description.acc_modtool_room_info', 'Allows viewing room information in the moderation tool.'), ('commands.description.cmd_about', ':about'), + ('commands.description.cmd_add_youtube_playlist', ':add_youtube '), ('commands.description.cmd_alert', ':alert '), ('commands.description.cmd_allow_trading', 'Enables / Disables the tradelock for a user.'), ('commands.description.cmd_badge', ':badge '), @@ -15379,6 +15381,8 @@ INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.description.cmd_danceall', ':danceall '), ('commands.description.cmd_diagonal', ':diagonal'), ('commands.description.cmd_disable_effects', ':disableffects'), + ('commands.description.cmd_disablemassmentions', ':disablemassmentions'), + ('commands.description.cmd_disablementions', ':disablementions'), ('commands.description.cmd_disconnect', ':disconnect '), ('commands.description.cmd_duckets', ':duckets '), ('commands.description.cmd_ejectall', ':ejectall'), @@ -15395,11 +15399,13 @@ INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.description.cmd_freeze_bots', ':freezebots'), ('commands.description.cmd_furnidata', ':furnidata'), ('commands.description.cmd_gift', ':gift '), + ('commands.description.cmd_give_prefix', ':giveprefix [icon] [effect]'), ('commands.description.cmd_give_rank', ':giverank '), ('commands.description.cmd_ha', ':ha '), ('commands.description.cmd_hal', ':hal '), ('commands.description.cmd_hand_item', ':handitem '), ('commands.description.cmd_happyhour', ':happyhour'), + ('commands.description.cmd_hidewired', ':hidewired'), ('commands.description.cmd_hoverboard', ':hoverboard'), ('commands.description.cmd_hug', ':hug '), ('commands.description.cmd_invisible', ':invisible'), @@ -15408,6 +15414,7 @@ INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.description.cmd_kill', ':kill '), ('commands.description.cmd_kiss', ':kiss '), ('commands.description.cmd_lay', ':lay'), + ('commands.description.cmd_list_prefixes', ':listprefixes '), ('commands.description.cmd_machine_ban', ':machineban [reason]'), ('commands.description.cmd_massbadge', ':massbadge '), ('commands.description.cmd_masscredits', ':masscredits '), @@ -15429,6 +15436,7 @@ INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.description.cmd_push', ':push '), ('commands.description.cmd_redeem', ':redeem'), ('commands.description.cmd_reload_room', ':reload_room'), + ('commands.description.cmd_remove_prefix', ':removeprefix '), ('commands.description.cmd_roomalert', ':roomalert '), ('commands.description.cmd_roombadge', ':roombadge '), ('commands.description.cmd_roomcredits', ':roomcredits '), @@ -15444,6 +15452,7 @@ INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.description.cmd_set', ':set info'), ('commands.description.cmd_setmax', ':setmax '), ('commands.description.cmd_setpublic', ':setpublic'), + ('commands.description.cmd_setroom_template', ':setroom_template'), ('commands.description.cmd_setrotation', ':rot;rotation'), ('commands.description.cmd_setspeed', ':setspeed '), ('commands.description.cmd_setstate', ':ss'), @@ -15486,6 +15495,7 @@ INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.description.cmd_update_polls', ':update_polls'), ('commands.description.cmd_update_texts', ':update_texts'), ('commands.description.cmd_update_wordfilter', ':update_word_filter'), + ('commands.description.cmd_update_youtube_playlists', ':update_youtube'), ('commands.description.cmd_userinfo', ':userinfo '), ('commands.description.cmd_welcome', ':welcome '), ('commands.description.cmd_word_quiz', ':wordquiz '), diff --git a/Emulator/src/main/java/com/eu/habbo/core/TextsManager.java b/Emulator/src/main/java/com/eu/habbo/core/TextsManager.java index 5c27eedb..02f634e1 100644 --- a/Emulator/src/main/java/com/eu/habbo/core/TextsManager.java +++ b/Emulator/src/main/java/com/eu/habbo/core/TextsManager.java @@ -52,6 +52,10 @@ public class TextsManager { return this.texts.getProperty(key, defaultValue); } + public String getValueQuietly(String key, String defaultValue) { + return this.texts.getProperty(key, defaultValue); + } + public boolean getBoolean(String key) { return this.getBoolean(key, false); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandsCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandsCommand.java index 0d0e0a48..6d9f80c8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandsCommand.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandsCommand.java @@ -18,7 +18,7 @@ public class CommandsCommand extends Command { for (Command c : commands) { String textKey = "commands.description." + c.permission; - String commandText = Emulator.getTexts().getValue(textKey, ""); + String commandText = Emulator.getTexts().getValueQuietly(textKey, ""); String commandLine = ":" + c.keys[0]; String description = ""; diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/commands/AvailableCommandsComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/commands/AvailableCommandsComposer.java index 0f8e00f5..dab605b5 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/commands/AvailableCommandsComposer.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/commands/AvailableCommandsComposer.java @@ -23,10 +23,10 @@ public class AvailableCommandsComposer extends MessageComposer { for (Command cmd : this.commands) { this.response.appendString(cmd.keys[0]); this.response.appendString( - Emulator.getTexts().getValue("commands.description." + cmd.permission, cmd.permission) + Emulator.getTexts().getValueQuietly("commands.description." + cmd.permission, cmd.permission) ); } return this.response; } -} \ No newline at end of file +} diff --git a/Emulator/src/test/java/com/eu/habbo/core/CommandDescriptionTextsContractTest.java b/Emulator/src/test/java/com/eu/habbo/core/CommandDescriptionTextsContractTest.java new file mode 100644 index 00000000..b9966823 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/core/CommandDescriptionTextsContractTest.java @@ -0,0 +1,45 @@ +package com.eu.habbo.core; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; + +class CommandDescriptionTextsContractTest { + private static final Path FULL_DATABASE = Path.of("../Default Database/FullDatabase.sql"); + private static final Path LIVE_SCHEMA_UPDATE = Path.of("../Database Updates/003_live_required_schema.sql"); + + private static final List REQUIRED_DESCRIPTION_KEYS = List.of( + "commands.description.acc_modtool_room_info", + "commands.description.cmd_add_youtube_playlist", + "commands.description.cmd_disablemassmentions", + "commands.description.cmd_disablementions", + "commands.description.cmd_give_prefix", + "commands.description.cmd_hidewired", + "commands.description.cmd_list_prefixes", + "commands.description.cmd_remove_prefix", + "commands.description.cmd_setroom_template", + "commands.description.cmd_update_youtube_playlists" + ); + + @Test + void fullDatabaseDefinesCommandDescriptionsUsedByCommandsList() throws IOException { + assertContainsAllDescriptionKeys(Files.readString(FULL_DATABASE), "FullDatabase.sql"); + } + + @Test + void liveSchemaUpdateBackfillsCommandDescriptionsForExistingDatabases() throws IOException { + assertContainsAllDescriptionKeys(Files.readString(LIVE_SCHEMA_UPDATE), "003_live_required_schema.sql"); + } + + private static void assertContainsAllDescriptionKeys(String source, String fileName) { + for (String key : REQUIRED_DESCRIPTION_KEYS) { + assertTrue(source.contains("'" + key + "'"), + fileName + " must define " + key + " to avoid TextsManager missing-key logs"); + } + } +} diff --git a/Emulator/src/test/java/com/eu/habbo/core/CommandTextLookupContractTest.java b/Emulator/src/test/java/com/eu/habbo/core/CommandTextLookupContractTest.java new file mode 100644 index 00000000..a688f8ce --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/core/CommandTextLookupContractTest.java @@ -0,0 +1,35 @@ +package com.eu.habbo.core; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; + +class CommandTextLookupContractTest { + private static final Path TEXTS_MANAGER = Path.of("src/main/java/com/eu/habbo/core/TextsManager.java"); + private static final Path COMMANDS_COMMAND = Path.of("src/main/java/com/eu/habbo/habbohotel/commands/CommandsCommand.java"); + private static final Path AVAILABLE_COMMANDS_COMPOSER = Path.of( + "src/main/java/com/eu/habbo/messages/outgoing/commands/AvailableCommandsComposer.java"); + + @Test + void textsManagerExposesQuietFallbackLookupForOptionalTexts() throws IOException { + String source = Files.readString(TEXTS_MANAGER); + + assertTrue(source.contains("public String getValueQuietly(String key, String defaultValue)")); + assertTrue(source.contains("return this.texts.getProperty(key, defaultValue);")); + } + + @Test + void commandListsUseQuietDescriptionLookups() throws IOException { + String commandsCommand = Files.readString(COMMANDS_COMMAND); + String availableCommandsComposer = Files.readString(AVAILABLE_COMMANDS_COMPOSER); + + assertTrue(commandsCommand.contains("getValueQuietly(textKey, \"\")"), + ":commands should not log an error when an optional command description is missing"); + assertTrue(availableCommandsComposer.contains("getValueQuietly(\"commands.description.\" + cmd.permission, cmd.permission)"), + "available commands composer should not log an error when an optional command description is missing"); + } +}