From 7c0d3c2405c33d467da8402e2eddecaa51498e0a Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 26 Mar 2026 09:17:35 +0100 Subject: [PATCH 1/7] Stop tracking .idea directory --- .idea/workspace.xml | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 004a2fc0..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - 1710516016645 - - - - \ No newline at end of file From 954d0d9703a9a882b7ecba3e008ead2f99d48a01 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 26 Mar 2026 09:18:24 +0100 Subject: [PATCH 2/7] Stop tracking .idea directory --- Emulator/.idea/.gitignore | 3 --- Emulator/.idea/compiler.xml | 13 ------------- Emulator/.idea/encodings.xml | 7 ------- Emulator/.idea/jarRepositories.xml | 25 ------------------------- Emulator/.idea/misc.xml | 12 ------------ 5 files changed, 60 deletions(-) delete mode 100644 Emulator/.idea/.gitignore delete mode 100644 Emulator/.idea/compiler.xml delete mode 100644 Emulator/.idea/encodings.xml delete mode 100644 Emulator/.idea/jarRepositories.xml delete mode 100644 Emulator/.idea/misc.xml diff --git a/Emulator/.idea/.gitignore b/Emulator/.idea/.gitignore deleted file mode 100644 index 26d33521..00000000 --- a/Emulator/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/Emulator/.idea/compiler.xml b/Emulator/.idea/compiler.xml deleted file mode 100644 index 3376806e..00000000 --- a/Emulator/.idea/compiler.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/Emulator/.idea/encodings.xml b/Emulator/.idea/encodings.xml deleted file mode 100644 index aa00ffab..00000000 --- a/Emulator/.idea/encodings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Emulator/.idea/jarRepositories.xml b/Emulator/.idea/jarRepositories.xml deleted file mode 100644 index 27bf0394..00000000 --- a/Emulator/.idea/jarRepositories.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/Emulator/.idea/misc.xml b/Emulator/.idea/misc.xml deleted file mode 100644 index 6568344f..00000000 --- a/Emulator/.idea/misc.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file From f2b0ba013827005b0cdc00dce89050ae0e56c666 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 26 Mar 2026 09:20:49 +0100 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=86=99=20Start=20working=20on=20SQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Database Updates/000_all_database_updates.sql | 415 +++++++ Database Updates/001_optimize_gameserver.sql | 1023 +++++++++++++++++ .../07012026_UpdateDatabase_to_4-0-1.sql | 152 --- .../09012026_UpdateDatabase_to_4-0-2.sql | 672 ----------- Database Updates/12012026_Battle Banzai.sql | 18 - Database Updates/12012026_Breeding Fixes.sql | 216 ---- Database Updates/12012026_ChatBubbles.sql | 15 - .../16032026_updateall_command.sql | 6 - Database Updates/17032026_allow_underpass.sql | 1 - Database Updates/19032026_hotel_timezone.sql | 22 - Database Updates/21022026_user_prefixes.sql | 12 - Database Updates/Default_Camera.sql | 71 -- .../UpdateDatabase_Allow_diagonale.sql | 1 - Database Updates/UpdateDatabase_BOT.sql | 4 - Database Updates/UpdateDatabase_Banners.sql | 4 - Database Updates/UpdateDatabase_DanceCMD.sql | 5 - Database Updates/UpdateDatabase_Happiness.sql | 4 - Database Updates/UpdateDatabase_Websocket.sql | 6 - .../UpdateDatabase_unignorable.sql | 2 - 19 files changed, 1438 insertions(+), 1211 deletions(-) create mode 100644 Database Updates/000_all_database_updates.sql create mode 100644 Database Updates/001_optimize_gameserver.sql delete mode 100644 Database Updates/07012026_UpdateDatabase_to_4-0-1.sql delete mode 100644 Database Updates/09012026_UpdateDatabase_to_4-0-2.sql delete mode 100644 Database Updates/12012026_Battle Banzai.sql delete mode 100644 Database Updates/12012026_Breeding Fixes.sql delete mode 100644 Database Updates/12012026_ChatBubbles.sql delete mode 100644 Database Updates/16032026_updateall_command.sql delete mode 100644 Database Updates/17032026_allow_underpass.sql delete mode 100644 Database Updates/19032026_hotel_timezone.sql delete mode 100644 Database Updates/21022026_user_prefixes.sql delete mode 100644 Database Updates/Default_Camera.sql delete mode 100644 Database Updates/UpdateDatabase_Allow_diagonale.sql delete mode 100644 Database Updates/UpdateDatabase_BOT.sql delete mode 100644 Database Updates/UpdateDatabase_Banners.sql delete mode 100644 Database Updates/UpdateDatabase_DanceCMD.sql delete mode 100644 Database Updates/UpdateDatabase_Happiness.sql delete mode 100644 Database Updates/UpdateDatabase_Websocket.sql delete mode 100644 Database Updates/UpdateDatabase_unignorable.sql diff --git a/Database Updates/000_all_database_updates.sql b/Database Updates/000_all_database_updates.sql new file mode 100644 index 00000000..c4b91c37 --- /dev/null +++ b/Database Updates/000_all_database_updates.sql @@ -0,0 +1,415 @@ +-- ============================================================================= +-- Consolidated Database Updates - All-in-One +-- ============================================================================= +-- This file combines ALL individual update scripts from SQL/Database Updates/ +-- into a single idempotent migration. Every statement is safe to re-run: +-- - ALTER TABLE ADD COLUMN IF NOT EXISTS (MariaDB 10.0+) +-- - ALTER TABLE CHANGE/MODIFY COLUMN IF EXISTS +-- - CREATE TABLE IF NOT EXISTS +-- - INSERT IGNORE / ON DUPLICATE KEY UPDATE for settings +-- - TRUNCATE + re-insert for reference data (breeding) +-- +-- Run order: This file FIRST, then 001_optimize_gameserver.sql +-- +-- Source files (in applied order): +-- 1. UpdateDatabase_Allow_diagonale.sql +-- 2. UpdateDatabase_BOT.sql +-- 3. UpdateDatabase_Banners.sql +-- 4. UpdateDatabase_DanceCMD.sql +-- 5. UpdateDatabase_Happiness.sql +-- 6. UpdateDatabase_Websocket.sql +-- 7. UpdateDatabase_unignorable.sql +-- 8. Default_Camera.sql +-- 9. 07012026_UpdateDatabase_to_4-0-1.sql +-- 10. 09012026_UpdateDatabase_to_4-0-2.sql +-- 11. 12012026_Battle Banzai.sql (same as #10, deduplicated) +-- 12. 12012026_Breeding Fixes.sql +-- 13. 12012026_ChatBubbles.sql +-- 14. 16032026_updateall_command.sql +-- 15. 17032026_allow_underpass.sql +-- 16. 19032026_hotel_timezone.sql +-- 17. 21022026_user_prefixes.sql +-- ============================================================================= + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; +SET @OLD_SQL_MODE = @@SQL_MODE; +SET SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO'; + + +-- ============================================================================= +-- From: UpdateDatabase_Allow_diagonale.sql +-- ============================================================================= +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) +VALUES ('pathfinder.diagonal.enabled', '1'); + + +-- ============================================================================= +-- From: UpdateDatabase_BOT.sql +-- ============================================================================= +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) +VALUES ('hotel.bot.limit.walking.distance', '1'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) +VALUES ('hotel.bot.limit.walking.distance.radius', '5'); + + +-- ============================================================================= +-- From: UpdateDatabase_Banners.sql +-- ============================================================================= +ALTER TABLE `users` + ADD COLUMN IF NOT EXISTS `background_id` INT(11) NOT NULL DEFAULT 0 AFTER `machine_id`, + ADD COLUMN IF NOT EXISTS `background_stand_id` INT(11) NOT NULL DEFAULT 0 AFTER `background_id`, + ADD COLUMN IF NOT EXISTS `background_overlay_id` INT(11) NOT NULL DEFAULT 0 AFTER `background_stand_id`; + + +-- ============================================================================= +-- From: UpdateDatabase_DanceCMD.sql +-- ============================================================================= +ALTER TABLE `permissions` + ADD COLUMN IF NOT EXISTS `cms_dance` ENUM('0','1') NULL DEFAULT '0' AFTER `cmd_credits`; + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) +VALUES ('commands.description.cmd_dance', 'dance around the world ! use 1 t/m 4 and 0 to stop'); + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) +VALUES ('commands.keys.cmd_dance', 'dance'); + + +-- ============================================================================= +-- From: UpdateDatabase_Happiness.sql +-- ============================================================================= +-- Rename key if the old one exists +UPDATE `emulator_texts` +SET `key` = 'generic.pet.happiness', `value` = 'Happiness' +WHERE `key` = 'generic.pet.happyness'; + +-- Rename columns (IF EXISTS prevents error if already renamed) +ALTER TABLE `pet_commands_data` + CHANGE COLUMN IF EXISTS `cost_happyness` `cost_happiness` int(11) NOT NULL DEFAULT '0'; + +ALTER TABLE `users_pets` + CHANGE COLUMN IF EXISTS `happyness` `happiness` int(11) NOT NULL DEFAULT '100'; + + +-- ============================================================================= +-- From: UpdateDatabase_Websocket.sql +-- ============================================================================= +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) +VALUES ('websockets.whitelist', 'localhost'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) +VALUES ('ws.nitro.host', '0.0.0.0'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) +VALUES ('ws.nitro.ip.header', ''); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) +VALUES ('ws.nitro.port', '2096'); + + +-- ============================================================================= +-- From: UpdateDatabase_unignorable.sql +-- ============================================================================= +ALTER TABLE `permissions` + ADD COLUMN IF NOT EXISTS `acc_unignorable` ENUM('0','1') NOT NULL DEFAULT '0'; + + +-- ============================================================================= +-- From: Default_Camera.sql +-- ============================================================================= +CREATE TABLE IF NOT EXISTS `camera_web` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `user_id` INT(11) NOT NULL, + `room_id` INT(11) NOT NULL DEFAULT 0, + `timestamp` INT(11) NOT NULL DEFAULT 0, + `url` VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + INDEX `idx_camera_web_user_id` (`user_id`), + INDEX `idx_camera_web_timestamp` (`timestamp`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Camera emulator settings +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES +('camera.url', 'http://localhost/camera/'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES +('imager.location.output.camera', '/path/to/www/camera/'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES +('imager.location.output.thumbnail', '/path/to/www/thumbnails/'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES +('camera.item_id', '0'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES +('camera.price.credits', '2'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES +('camera.price.points', '0'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES +('camera.price.points.publish', '1'); + +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES +('camera.extradata', '{"t":"%timestamp%","u":"%id%","m":"","s":"%room_id%","w":"%url%"}'); + +-- Camera emulator texts +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('camera.permission', 'You do not have permission to use the camera.'); + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('camera.wait', 'Please wait %seconds% more seconds before taking another photo.'); + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('camera.error.creation', 'An error occurred while processing your photo. Please try again.'); + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('camera.daily.limit', 'You have reached the daily photo limit. Try again tomorrow.'); + + +-- ============================================================================= +-- From: 07012026_UpdateDatabase_to_4-0-1.sql +-- ============================================================================= + +-- Wired abuse protection settings +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('wired.abuse.max.recursion.depth', '10') +ON DUPLICATE KEY UPDATE `key` = `key`; + +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('wired.abuse.max.events.per.window', '100') +ON DUPLICATE KEY UPDATE `key` = `key`; + +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('wired.abuse.rate.limit.window.ms', '10000') +ON DUPLICATE KEY UPDATE `key` = `key`; + +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('wired.abuse.ban.duration.ms', '600000') +ON DUPLICATE KEY UPDATE `key` = `key`; + +-- Wired abuse texts +INSERT INTO `emulator_texts` (`key`, `value`) VALUES +('wired.abuse.room.alert', 'Wired execution has been temporarily disabled in this room due to abuse detection. It will resume in %minutes% minutes.') +ON DUPLICATE KEY UPDATE `key` = `key`; + +INSERT INTO `emulator_texts` (`key`, `value`) VALUES +('wired.abuse.staff.title', 'Wired Abuse Detected') +ON DUPLICATE KEY UPDATE `key` = `key`; + +INSERT INTO `emulator_texts` (`key`, `value`) VALUES +('wired.abuse.staff.message', 'Room: %roomname%\nOwner: %owner%\nBanned for %minutes% minutes.') +ON DUPLICATE KEY UPDATE `key` = `key`; + +INSERT INTO `emulator_texts` (`key`, `value`) VALUES +('wired.abuse.staff.link', 'Go to Room') +ON DUPLICATE KEY UPDATE `key` = `key`; + +-- Wired tick resolution +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('wired.tick.resolution', '100') +ON DUPLICATE KEY UPDATE `key` = `key`; + +-- Wired engine configuration +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('wired.engine.enabled', '0'), +('wired.engine.exclusive', '0'), +('wired.engine.maxStepsPerStack', '100'), +('wired.engine.debug', '0') +ON DUPLICATE KEY UPDATE `key` = `key`; + +-- Wired tick system configuration +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('wired.tick.interval.ms', '50'), +('wired.tick.debug', '0'), +('wired.tick.thread.priority', '6') +ON DUPLICATE KEY UPDATE `key` = `key`; + +-- Pathfinder settings +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('pathfinder.click.delay', '0') +ON DUPLICATE KEY UPDATE `key` = `key`; + +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('pathfinder.retro-style.diagonals', '0') +ON DUPLICATE KEY UPDATE `key` = `key`; + +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('pathfinder.step.allow.falling', '1') +ON DUPLICATE KEY UPDATE `key` = `key`; + + +-- ============================================================================= +-- From: 09012026_UpdateDatabase_to_4-0-2.sql + 12012026_Battle Banzai.sql +-- (These two files are identical - deduplicated here) +-- ============================================================================= +INSERT INTO `emulator_settings` (`key`, `value`) VALUES +('hotel.banzai.fill.max_queue', '50'), +('hotel.banzai.fill.cooldown_ms', '100') +ON DUPLICATE KEY UPDATE `value` = VALUES(`value`); + + +-- ============================================================================= +-- From: 12012026_Breeding Fixes.sql +-- ============================================================================= + +-- Recreate pet_breeding with correct structure +CREATE TABLE IF NOT EXISTS `pet_breeding` ( + `pet_id` int(11) NOT NULL COMMENT 'Parent pet type', + `offspring_id` int(11) NOT NULL COMMENT 'Baby pet type', + PRIMARY KEY (`pet_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +TRUNCATE TABLE `pet_breeding`; + +INSERT INTO `pet_breeding` (`pet_id`, `offspring_id`) VALUES +(0, 29), -- Dog -> Baby Dog +(1, 28), -- Cat -> Baby Cat +(3, 25), -- Terrier -> Baby Terrier +(4, 24), -- Bear -> Baby Bear +(5, 30); -- Pig -> Baby Pig + +-- Recreate pet_breeding_races with correct structure +CREATE TABLE IF NOT EXISTS `pet_breeding_races` ( + `pet_type` int(11) NOT NULL COMMENT 'Baby pet type (offspring)', + `rarity_level` int(11) NOT NULL COMMENT '1=Common, 2=Uncommon, 3=Rare, 4=Epic', + `breed` int(11) NOT NULL COMMENT 'Visual breed/color variant', + PRIMARY KEY (`pet_type`, `rarity_level`, `breed`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +TRUNCATE TABLE `pet_breeding_races`; + +-- Baby Dog (29) - Offspring of Dog (0) +INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES +(29, 1, 0), (29, 1, 1), (29, 1, 2), (29, 1, 3), +(29, 1, 4), (29, 1, 5), (29, 1, 6), (29, 1, 7), +(29, 2, 8), (29, 2, 9), (29, 2, 10), (29, 2, 11), (29, 2, 12), +(29, 3, 13), (29, 3, 14), (29, 3, 15), (29, 3, 16), +(29, 4, 17), (29, 4, 18), (29, 4, 19); + +-- Baby Cat (28) - Offspring of Cat (1) +INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES +(28, 1, 0), (28, 1, 1), (28, 1, 2), (28, 1, 3), +(28, 1, 4), (28, 1, 5), (28, 1, 6), (28, 1, 7), +(28, 2, 8), (28, 2, 9), (28, 2, 10), (28, 2, 11), (28, 2, 12), +(28, 3, 13), (28, 3, 14), (28, 3, 15), (28, 3, 16), +(28, 4, 17), (28, 4, 18), (28, 4, 19); + +-- Baby Terrier (25) - Offspring of Terrier (3) +INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES +(25, 1, 0), (25, 1, 1), (25, 1, 2), (25, 1, 3), +(25, 1, 4), (25, 1, 5), (25, 1, 6), (25, 1, 7), +(25, 2, 8), (25, 2, 9), (25, 2, 10), (25, 2, 11), (25, 2, 12), +(25, 3, 13), (25, 3, 14), (25, 3, 15), (25, 3, 16), +(25, 4, 17), (25, 4, 18), (25, 4, 19); + +-- Baby Bear (24) - Offspring of Bear (4) +INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES +(24, 1, 0), (24, 1, 1), (24, 1, 2), (24, 1, 3), +(24, 1, 4), (24, 1, 5), (24, 1, 6), (24, 1, 7), +(24, 2, 8), (24, 2, 9), (24, 2, 10), (24, 2, 11), (24, 2, 12), +(24, 3, 13), (24, 3, 14), (24, 3, 15), (24, 3, 16), +(24, 4, 17), (24, 4, 18), (24, 4, 19); + +-- Baby Pig (30) - Offspring of Pig (5) +INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES +(30, 1, 0), (30, 1, 1), (30, 1, 2), (30, 1, 3), +(30, 1, 4), (30, 1, 5), (30, 1, 6), (30, 1, 7), +(30, 2, 8), (30, 2, 9), (30, 2, 10), (30, 2, 11), (30, 2, 12), +(30, 3, 13), (30, 3, 14), (30, 3, 15), (30, 3, 16), +(30, 4, 17), (30, 4, 18), (30, 4, 19); + +-- Fix pet_actions offspring_type values +UPDATE `pet_actions` SET `offspring_type` = 29 WHERE `pet_type` = 0; +UPDATE `pet_actions` SET `offspring_type` = 28 WHERE `pet_type` = 1; +UPDATE `pet_actions` SET `offspring_type` = 25 WHERE `pet_type` = 3; +UPDATE `pet_actions` SET `offspring_type` = 24 WHERE `pet_type` = 4; +UPDATE `pet_actions` SET `offspring_type` = 30 WHERE `pet_type` = 5; +UPDATE `pet_actions` SET `offspring_type` = -1 WHERE `pet_type` NOT IN (0, 1, 3, 4, 5); + +-- Fix items_base whitespace in interaction_type +UPDATE `items_base` SET `interaction_type` = TRIM(`interaction_type`); + +-- Ensure breeding nest items have correct interaction_type +UPDATE `items_base` SET `interaction_type` = 'breeding_nest' +WHERE `item_name` LIKE 'pet_breeding_%' AND `interaction_type` != 'breeding_nest'; + + +-- ============================================================================= +-- From: 12012026_ChatBubbles.sql +-- ============================================================================= +ALTER TABLE `permissions` + ADD COLUMN IF NOT EXISTS `cmd_update_chat_bubbles` ENUM('0','1') NOT NULL DEFAULT '0'; + +CREATE TABLE IF NOT EXISTS `chat_bubbles` ( + `type` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'Only 46 and higher will work', + `name` VARCHAR(255) NOT NULL DEFAULT '', + `permission` VARCHAR(255) NOT NULL DEFAULT '', + `overridable` TINYINT(1) NOT NULL DEFAULT 1, + `triggers_talking_furniture` TINYINT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('commands.keys.cmd_update_chat_bubbles', 'update_chat_bubbles'); + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('commands.success.cmd_update_chat_bubbles', 'Successfully updated chat bubbles'); + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('commands.description.cmd_update_chat_bubbles', ':update_chat_bubbles'); + + +-- ============================================================================= +-- From: 16032026_updateall_command.sql +-- ============================================================================= +ALTER TABLE `permissions` + ADD COLUMN IF NOT EXISTS `cmd_update_all` ENUM('0','1') NOT NULL DEFAULT '0' AFTER `cmd_update_achievements`; + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('commands.keys.cmd_update_all', 'update_all'); + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('commands.description.cmd_update_all', ':update_all'); + +INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES +('commands.succes.cmd_update_all', 'Successfully updated everything!'); + + +-- ============================================================================= +-- From: 17032026_allow_underpass.sql +-- ============================================================================= +ALTER TABLE `rooms` + ADD COLUMN IF NOT EXISTS `allow_underpass` ENUM('0','1') NOT NULL DEFAULT '0' AFTER `move_diagonally`; + + +-- ============================================================================= +-- From: 19032026_hotel_timezone.sql +-- ============================================================================= +INSERT IGNORE INTO `emulator_settings` (`key`, `value`) +VALUES ('hotel.timezone', 'Europe/Rome'); + + +-- ============================================================================= +-- From: 21022026_user_prefixes.sql +-- ============================================================================= +CREATE TABLE IF NOT EXISTS `user_prefixes` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `user_id` INT(11) NOT NULL, + `text` VARCHAR(50) NOT NULL, + `color` VARCHAR(255) NOT NULL DEFAULT '#FFFFFF', + `icon` VARCHAR(50) NOT NULL DEFAULT '', + `effect` VARCHAR(50) NOT NULL DEFAULT '', + `active` TINYINT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + INDEX `idx_user_id` (`user_id`), + INDEX `idx_user_active` (`user_id`, `active`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + +-- ============================================================================= +-- Done +-- ============================================================================= +SET FOREIGN_KEY_CHECKS = 1; +SET SQL_MODE = @OLD_SQL_MODE; diff --git a/Database Updates/001_optimize_gameserver.sql b/Database Updates/001_optimize_gameserver.sql new file mode 100644 index 00000000..ffd8840a --- /dev/null +++ b/Database Updates/001_optimize_gameserver.sql @@ -0,0 +1,1023 @@ +-- ============================================================================= +-- Gameserver Database Optimization Migration +-- ============================================================================= +-- This migration optimizes the gameserver tables (not website_* tables). +-- +-- IMPORTANT: This script is designed to run on a POPULATED database safely. +-- It uses IF NOT EXISTS / IF EXISTS where possible. +-- +-- What it does: +-- 1. Converts Aria/MyISAM tables to InnoDB (required for foreign keys) +-- 2. Fixes data type mismatches (unsigned/signed) so FKs can be created +-- 3. Adds missing primary keys and indexes +-- 4. Adds foreign key constraints for referential integrity +-- +-- BEFORE RUNNING: +-- - Back up your database! +-- - Run on a test environment first +-- - The script disables FK checks during migration to avoid ordering issues +-- ============================================================================= + +SET FOREIGN_KEY_CHECKS = 0; +SET @OLD_SQL_MODE = @@SQL_MODE; +SET SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO'; + +-- ============================================================================= +-- PHASE 1: Convert storage engines to InnoDB, ROW_FORMAT to DYNAMIC +-- ============================================================================= +-- Foreign keys require InnoDB. Converting Aria and MyISAM tables. +-- InnoDB does not support ROW_FORMAT=FIXED, so all tables get ROW_FORMAT=DYNAMIC. +-- Note: Aria tables lose PAGE_CHECKSUM (InnoDB has its own checksumming). + +-- Core emulator tables +ALTER TABLE IF EXISTS `achievements` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `bot_serves` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `catalog_clothing` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `catalog_club_offers` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `catalog_featured_pages` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `catalog_items_limited` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `chatlogs_private` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `chatlogs_room` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `commandlogs` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `emulator_errors` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; + +-- Items & marketplace +ALTER TABLE IF EXISTS `items` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `items_crackable` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `items_hoppers` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `items_presents` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `items_teleports` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `marketplace_items` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; + +-- Navigator & rooms +ALTER TABLE IF EXISTS `navigator_publiccats` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `navigator_publics` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `navigator_filter` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `navigator_flatcats` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `nux_gifts` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `rooms` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_bans` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_enter_log` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_game_scores` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_models` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_models_custom` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_mutes` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_promotions` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_rights` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_votes` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `room_wordfilter` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; + +-- Pets +ALTER TABLE IF EXISTS `pet_breeding` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `pet_breeding_races` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `pet_breeds` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `pet_drinks` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `pet_foods` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `pet_items` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; + +-- Polls +ALTER TABLE IF EXISTS `polls` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `polls_answers` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `polls_questions` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; + +-- Users +ALTER TABLE IF EXISTS `users_achievements` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `users_achievements_queue` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `users_clothing` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `users_currency` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `users_effects` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `users_favorite_rooms` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `users_navigator_settings` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `users_pets` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `users_recipes` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `user_window_settings` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; + +-- Misc +ALTER TABLE IF EXISTS `crafting_altars_recipes` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `crafting_recipes` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `crafting_recipes_ingredients` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `namechange_log` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `recycler_prizes` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `special_enables` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `vouchers` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `wordfilter` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `wired_rewards_given` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `youtube_playlists` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; +ALTER TABLE IF EXISTS `bots` ENGINE = InnoDB, ROW_FORMAT = DYNAMIC; + + +-- ============================================================================= +-- PHASE 2: Fix data type mismatches (unsigned vs signed) +-- ============================================================================= +-- Foreign keys require EXACT type matches including signedness. +-- Fix columns where referenced PK and referencing FK differ. + +-- 2a. users.id is int(11) SIGNED → fix unsigned user_id columns +ALTER TABLE IF EXISTS `logs_hc_payday` + MODIFY `user_id` int(11) DEFAULT NULL; + +ALTER TABLE IF EXISTS `logs_shop_purchases` + MODIFY `user_id` int(11) DEFAULT NULL; + +ALTER TABLE IF EXISTS `users_subscriptions` + MODIFY `user_id` int(11) DEFAULT NULL; + +-- 2b. items_base.id is int(11) UNSIGNED → fix signed FK columns to match +ALTER TABLE IF EXISTS `items` + MODIFY `item_id` int(11) unsigned DEFAULT 0; + +ALTER TABLE IF EXISTS `economy_furniture` + MODIFY `items_base_id` int(11) unsigned NOT NULL; + +ALTER TABLE IF EXISTS `items_crackable` + MODIFY `item_id` int(11) unsigned NOT NULL; + +-- 2c. guilds_forums_threads.id is int(10) UNSIGNED → fix signed FK columns +ALTER TABLE IF EXISTS `guilds_forums_comments` + MODIFY `thread_id` int(10) unsigned NOT NULL DEFAULT 0; + + +-- ============================================================================= +-- PHASE 3: Add missing primary keys +-- ============================================================================= +-- Tables without primary keys hurt performance and replication. + +-- bot_serves: no PK +ALTER TABLE IF EXISTS `bot_serves` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- chatlogs_room: no PK (high-volume log table, add auto-increment PK) +ALTER TABLE IF EXISTS `chatlogs_room` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- commandlogs: no PK +ALTER TABLE IF EXISTS `commandlogs` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- crafting_recipes_ingredients: no PK +ALTER TABLE IF EXISTS `crafting_recipes_ingredients` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- items_hoppers: no PK +ALTER TABLE IF EXISTS `items_hoppers` + ADD PRIMARY KEY (`item_id`); + +-- items_presents: no PK +ALTER TABLE IF EXISTS `items_presents` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- items_teleports: no PK +ALTER TABLE IF EXISTS `items_teleports` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- namechange_log: no PK +ALTER TABLE IF EXISTS `namechange_log` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- navigator_publics: no PK +ALTER TABLE IF EXISTS `navigator_publics` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- pet_breeding: no PK +ALTER TABLE IF EXISTS `pet_breeding` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- pet_breeding_races: no PK +ALTER TABLE IF EXISTS `pet_breeding_races` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- pet_drinks: no PK +ALTER TABLE IF EXISTS `pet_drinks` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- pet_foods: no PK +ALTER TABLE IF EXISTS `pet_foods` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- pet_items: no PK +ALTER TABLE IF EXISTS `pet_items` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- pet_vocals: no PK +ALTER TABLE IF EXISTS `pet_vocals` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- recycler_prizes: no PK +ALTER TABLE IF EXISTS `recycler_prizes` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- room_bans: no PK +ALTER TABLE IF EXISTS `room_bans` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- room_enter_log: no PK +ALTER TABLE IF EXISTS `room_enter_log` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- room_game_scores: no PK +ALTER TABLE IF EXISTS `room_game_scores` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- room_mutes: no PK +ALTER TABLE IF EXISTS `room_mutes` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- room_rights: no PK (use composite) +ALTER TABLE IF EXISTS `room_rights` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- room_trax: no PK +ALTER TABLE IF EXISTS `room_trax` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- room_trax_playlist: no PK +ALTER TABLE IF EXISTS `room_trax_playlist` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- room_votes: no PK (use composite unique) +ALTER TABLE IF EXISTS `room_votes` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- trax_playlist: no PK +ALTER TABLE IF EXISTS `trax_playlist` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- wired_rewards_given: no PK +ALTER TABLE IF EXISTS `wired_rewards_given` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- calendar_rewards_claimed: no PK +ALTER TABLE IF EXISTS `calendar_rewards_claimed` + ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; + +-- camera: no PK +ALTER TABLE IF EXISTS `camera` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, + ADD PRIMARY KEY (`id`); + + +-- ============================================================================= +-- PHASE 4: Add missing indexes +-- ============================================================================= +-- Adding indexes for columns commonly used in JOINs and WHERE clauses. + +-- bans: index on user_id for lookups +ALTER TABLE IF EXISTS `bans` + ADD INDEX `idx_bans_user_id` (`user_id`), + ADD INDEX `idx_bans_ip` (`ip`), + ADD INDEX `idx_bans_machine_id` (`machine_id`(64)); + +-- calendar_rewards: index on campaign_id +ALTER TABLE IF EXISTS `calendar_rewards` + ADD INDEX `idx_calendar_rewards_campaign_id` (`campaign_id`); + +-- calendar_rewards_claimed: indexes for lookups +ALTER TABLE IF EXISTS `calendar_rewards_claimed` + ADD INDEX `idx_cal_claimed_user_id` (`user_id`), + ADD INDEX `idx_cal_claimed_campaign_id` (`campaign_id`), + ADD INDEX `idx_cal_claimed_reward_id` (`reward_id`); + +-- camera: indexes +ALTER TABLE IF EXISTS `camera` + ADD INDEX `idx_camera_user_id` (`user_id`), + ADD INDEX `idx_camera_room_id` (`room_id`); + +-- guilds: index on user_id +ALTER TABLE IF EXISTS `guilds` + ADD INDEX `idx_guilds_user_id` (`user_id`), + ADD INDEX `idx_guilds_room_id` (`room_id`); + +-- guilds_forums_threads: index on guild_id +ALTER TABLE IF EXISTS `guilds_forums_threads` + ADD INDEX `idx_gft_guild_id` (`guild_id`), + ADD INDEX `idx_gft_opener_id` (`opener_id`); + +-- guilds_forums_comments: index on user_id +ALTER TABLE IF EXISTS `guilds_forums_comments` + ADD INDEX `idx_gfc_user_id` (`user_id`); + +-- guild_forum_views: indexes +ALTER TABLE IF EXISTS `guild_forum_views` + ADD INDEX `idx_gfv_user_id` (`user_id`), + ADD INDEX `idx_gfv_guild_id` (`guild_id`); + +-- items_crackable: index on item_id +ALTER TABLE IF EXISTS `items_crackable` + ADD INDEX `idx_items_crackable_item_id` (`item_id`); + +-- namechange_log: index on user_id +ALTER TABLE IF EXISTS `namechange_log` + ADD INDEX `idx_namechange_user_id` (`user_id`); + +-- messenger_friendrequests: indexes +ALTER TABLE IF EXISTS `messenger_friendrequests` + ADD INDEX `idx_fr_user_to_id` (`user_to_id`), + ADD INDEX `idx_fr_user_from_id` (`user_from_id`); + +-- room_bans: indexes +ALTER TABLE IF EXISTS `room_bans` + ADD INDEX `idx_room_bans_room_id` (`room_id`), + ADD INDEX `idx_room_bans_user_id` (`user_id`); + +-- room_mutes: indexes +ALTER TABLE IF EXISTS `room_mutes` + ADD INDEX `idx_room_mutes_room_id` (`room_id`), + ADD INDEX `idx_room_mutes_user_id` (`user_id`); + +-- room_votes: index on room_id +ALTER TABLE IF EXISTS `room_votes` + ADD INDEX `idx_room_votes_room_id` (`room_id`); + +-- sanctions: index on habbo_id +ALTER TABLE IF EXISTS `sanctions` + ADD INDEX `idx_sanctions_habbo_id` (`habbo_id`); + +-- shadowbans: index on user_id +ALTER TABLE IF EXISTS `shadowbans` + ADD INDEX `idx_shadowbans_user_id` (`user_id`); + +-- support_cfh_topics: index on category_id +ALTER TABLE IF EXISTS `support_cfh_topics` + ADD INDEX `idx_cfh_topics_category_id` (`category_id`); + +-- support_tickets: indexes +ALTER TABLE IF EXISTS `support_tickets` + ADD INDEX `idx_tickets_sender_id` (`sender_id`), + ADD INDEX `idx_tickets_reported_id` (`reported_id`), + ADD INDEX `idx_tickets_mod_id` (`mod_id`), + ADD INDEX `idx_tickets_room_id` (`room_id`); + +-- users_settings: already has user_id index, good + +-- voucher_history: indexes +ALTER TABLE IF EXISTS `voucher_history` + ADD INDEX `idx_vh_voucher_id` (`voucher_id`), + ADD INDEX `idx_vh_user_id` (`user_id`); + +-- ls_name_backgrounds_owned +ALTER TABLE IF EXISTS `ls_name_backgrounds_owned` + ADD INDEX `idx_lsnbo_user_id` (`user_id`), + ADD INDEX `idx_lsnbo_bg_id` (`name_background_id`); + +-- ls_name_colors_owned +ALTER TABLE IF EXISTS `ls_name_colors_owned` + ADD INDEX `idx_lsnco_user_id` (`user_id`), + ADD INDEX `idx_lsnco_color_id` (`name_color_id`); + +-- ls_prefixes_owned +ALTER TABLE IF EXISTS `ls_prefixes_owned` + ADD INDEX `idx_lspo_user_id` (`user_id`), + ADD INDEX `idx_lspo_prefix_id` (`prefix_id`); + +-- users_target_offer_purchases +ALTER TABLE IF EXISTS `users_target_offer_purchases` + ADD INDEX `idx_utop_offer_id` (`offer_id`); + +-- users_unlockable_commands +ALTER TABLE IF EXISTS `users_unlockable_commands` + ADD INDEX `idx_uuc_user_id` (`user_id`); + +-- command_category_permissions: index on category_id +ALTER TABLE IF EXISTS `command_category_permissions` + ADD INDEX `idx_ccp_category_id` (`category_id`); + + +-- ============================================================================= +-- PHASE 5: Foreign key constraints +-- ============================================================================= +-- Adding FK constraints for referential integrity. +-- Using appropriate ON DELETE actions: +-- CASCADE = child rows deleted when parent is deleted +-- SET NULL = child FK set to NULL when parent is deleted (column must be nullable) +-- RESTRICT = prevent parent deletion if children exist (default) +-- +-- NOTE: Log/archive tables (chatlogs, commandlogs, room_enter_log, etc.) +-- intentionally do NOT get FKs to avoid cascade-deleting historical data +-- and to keep high-volume inserts fast. +-- ============================================================================= + +-- --------------------------------------------------------------------------- +-- 5.1 Rooms → Users (owner) +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `rooms` + ADD CONSTRAINT `fk_rooms_owner` + FOREIGN KEY (`owner_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.2 Items → Users, Rooms, Items_base +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `items` + ADD CONSTRAINT `fk_items_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_items_item_base` + FOREIGN KEY (`item_id`) REFERENCES `items_base` (`id`) + ON DELETE CASCADE; + +-- Note: items.room_id = 0 means "in inventory", so we can't FK to rooms +-- unless we allow NULL instead of 0. Skipping this FK. + +-- --------------------------------------------------------------------------- +-- 5.3 Bots → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `bots` + ADD CONSTRAINT `fk_bots_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.4 Bans → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `bans` + ADD CONSTRAINT `fk_bans_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_bans_staff` + FOREIGN KEY (`user_staff_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.5 Guilds → Users, Rooms +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `guilds` + ADD CONSTRAINT `fk_guilds_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_guilds_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.6 Guild members → Guilds, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `guilds_members` + ADD CONSTRAINT `fk_gm_guild` + FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_gm_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.7 Guild forum threads → Guilds, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `guilds_forums_threads` + ADD CONSTRAINT `fk_gft_guild` + FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_gft_opener` + FOREIGN KEY (`opener_id`) REFERENCES `users` (`id`) + ON DELETE SET NULL; + +-- --------------------------------------------------------------------------- +-- 5.8 Guild forum comments → Threads, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `guilds_forums_comments` + ADD CONSTRAINT `fk_gfc_thread` + FOREIGN KEY (`thread_id`) REFERENCES `guilds_forums_threads` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_gfc_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.9 Guild forum views → Guilds, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `guild_forum_views` + ADD CONSTRAINT `fk_gfv_guild` + FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_gfv_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.10 Catalog items → Catalog pages +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `catalog_items` + ADD CONSTRAINT `fk_catitems_page` + FOREIGN KEY (`page_id`) REFERENCES `catalog_pages` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.11 Catalog items limited → Catalog items +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `catalog_items_limited` + ADD CONSTRAINT `fk_catitemsltd_catitem` + FOREIGN KEY (`catalog_item_id`) REFERENCES `catalog_items` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.12 Calendar rewards → Calendar campaigns +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `calendar_rewards` + ADD CONSTRAINT `fk_calrewards_campaign` + FOREIGN KEY (`campaign_id`) REFERENCES `calendar_campaigns` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.13 Calendar rewards claimed → Users, Campaigns, Rewards +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `calendar_rewards_claimed` + ADD CONSTRAINT `fk_calclaimed_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_calclaimed_campaign` + FOREIGN KEY (`campaign_id`) REFERENCES `calendar_campaigns` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_calclaimed_reward` + FOREIGN KEY (`reward_id`) REFERENCES `calendar_rewards` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.14 Users_settings → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_settings` + ADD CONSTRAINT `fk_usettings_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.15 Users_badges → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_badges` + ADD CONSTRAINT `fk_ubadges_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.16 Users_currency → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_currency` + ADD CONSTRAINT `fk_ucurrency_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.17 Users_effects → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_effects` + ADD CONSTRAINT `fk_ueffects_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.18 Users_clothing → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_clothing` + ADD CONSTRAINT `fk_uclothing_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.19 Users_favorite_rooms → Users, Rooms +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_favorite_rooms` + ADD CONSTRAINT `fk_ufavrooms_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_ufavrooms_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.20 Users_wardrobe → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_wardrobe` + ADD CONSTRAINT `fk_uwardrobe_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.21 Users_pets → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_pets` + ADD CONSTRAINT `fk_upets_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.22 Users_recipes → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_recipes` + ADD CONSTRAINT `fk_urecipes_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.23 Users_saved_searches → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_saved_searches` + ADD CONSTRAINT `fk_usavedsearches_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.24 Users_navigator_settings → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_navigator_settings` + ADD CONSTRAINT `fk_unavsettings_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.25 Users_achievements → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_achievements` + ADD CONSTRAINT `fk_uachievements_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.26 Users_achievements_queue → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_achievements_queue` + ADD CONSTRAINT `fk_uachqueue_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.27 Users_ignored → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_ignored` + ADD CONSTRAINT `fk_uignored_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_uignored_target` + FOREIGN KEY (`target_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.28 Users_subscriptions → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_subscriptions` + ADD CONSTRAINT `fk_usubs_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.29 Users_target_offer_purchases → Users, Catalog target offers +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_target_offer_purchases` + ADD CONSTRAINT `fk_utop_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_utop_offer` + FOREIGN KEY (`offer_id`) REFERENCES `catalog_target_offers` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.30 Users_unlockable_commands → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `users_unlockable_commands` + ADD CONSTRAINT `fk_uunlockable_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.31 User_window_settings → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `user_window_settings` + ADD CONSTRAINT `fk_uwinsettings_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.32 User_prefixes → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `user_prefixes` + ADD CONSTRAINT `fk_uprefixes_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.33 Messenger_friendships → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `messenger_friendships` + ADD CONSTRAINT `fk_mfriends_user_one` + FOREIGN KEY (`user_one_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_mfriends_user_two` + FOREIGN KEY (`user_two_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.34 Messenger_friendrequests → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `messenger_friendrequests` + ADD CONSTRAINT `fk_mfr_user_to` + FOREIGN KEY (`user_to_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_mfr_user_from` + FOREIGN KEY (`user_from_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.35 Messenger_categories → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `messenger_categories` + ADD CONSTRAINT `fk_mcat_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.36 Marketplace_items → Items, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `marketplace_items` + ADD CONSTRAINT `fk_market_item` + FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_market_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.37 Room_rights → Rooms, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_rights` + ADD CONSTRAINT `fk_rrights_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_rrights_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.38 Room_bans → Rooms, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_bans` + ADD CONSTRAINT `fk_rbans_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_rbans_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.39 Room_mutes → Rooms, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_mutes` + ADD CONSTRAINT `fk_rmutes_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_rmutes_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.40 Room_votes → Rooms, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_votes` + ADD CONSTRAINT `fk_rvotes_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_rvotes_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.41 Room_wordfilter → Rooms +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_wordfilter` + ADD CONSTRAINT `fk_rwf_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.42 Room_promotions → Rooms +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_promotions` + ADD CONSTRAINT `fk_rpromo_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.43 Room_trax → Rooms +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_trax` + ADD CONSTRAINT `fk_rtrax_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.44 Room_trax_playlist → Rooms +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_trax_playlist` + ADD CONSTRAINT `fk_rtraxpl_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.45 Rooms_for_sale → Rooms, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `rooms_for_sale` + ADD CONSTRAINT `fk_r4sale_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_r4sale_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.46 Room_trade_log → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_trade_log` + ADD CONSTRAINT `fk_rtlog_user_one` + FOREIGN KEY (`user_one_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_rtlog_user_two` + FOREIGN KEY (`user_two_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.47 Room_trade_log_items → Room_trade_log, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `room_trade_log_items` + ADD CONSTRAINT `fk_rtli_trade` + FOREIGN KEY (`id`) REFERENCES `room_trade_log` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_rtli_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.48 Polls_questions → Polls +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `polls_questions` + ADD CONSTRAINT `fk_pq_poll` + FOREIGN KEY (`poll_id`) REFERENCES `polls` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.49 Polls_answers → Polls, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `polls_answers` + ADD CONSTRAINT `fk_pa_poll` + FOREIGN KEY (`poll_id`) REFERENCES `polls` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_pa_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.50 Crafting_recipes_ingredients → Crafting_recipes, Items_base +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `crafting_recipes_ingredients` + ADD CONSTRAINT `fk_cri_recipe` + FOREIGN KEY (`recipe_id`) REFERENCES `crafting_recipes` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.51 Crafting_altars_recipes → Crafting_recipes +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `crafting_altars_recipes` + ADD CONSTRAINT `fk_car_recipe` + FOREIGN KEY (`recipe_id`) REFERENCES `crafting_recipes` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.52 Voucher_history → Vouchers, Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `voucher_history` + ADD CONSTRAINT `fk_vh_voucher` + FOREIGN KEY (`voucher_id`) REFERENCES `vouchers` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_vh_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.53 Support_cfh_topics → Support_cfh_categories +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `support_cfh_topics` + ADD CONSTRAINT `fk_cfhtopics_category` + FOREIGN KEY (`category_id`) REFERENCES `support_cfh_categories` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.54 Command_category_permissions → Command_categories +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `command_category_permissions` + ADD CONSTRAINT `fk_ccp_category` + FOREIGN KEY (`category_id`) REFERENCES `command_categories` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.55 Economy_furniture → Items_base, Economy_categories +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `economy_furniture` + ADD CONSTRAINT `fk_econfurni_itembase` + FOREIGN KEY (`items_base_id`) REFERENCES `items_base` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_econfurni_category` + FOREIGN KEY (`economy_categories_id`) REFERENCES `economy_categories` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.56 Sanctions → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `sanctions` + ADD CONSTRAINT `fk_sanctions_user` + FOREIGN KEY (`habbo_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.57 Camera_web → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `camera_web` + ADD CONSTRAINT `fk_cameraweb_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.58 LS ownership tables → Users, LS definition tables +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `ls_name_backgrounds_owned` + ADD CONSTRAINT `fk_lsnbo_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_lsnbo_bg` + FOREIGN KEY (`name_background_id`) REFERENCES `ls_name_backgrounds` (`id`) + ON DELETE CASCADE; + +ALTER TABLE IF EXISTS `ls_name_colors_owned` + ADD CONSTRAINT `fk_lsnco_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_lsnco_color` + FOREIGN KEY (`name_color_id`) REFERENCES `ls_name_colors` (`id`) + ON DELETE CASCADE; + +ALTER TABLE IF EXISTS `ls_prefixes_owned` + ADD CONSTRAINT `fk_lspo_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_lspo_prefix` + FOREIGN KEY (`prefix_id`) REFERENCES `ls_prefixes` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.59 Navigator_publics → Navigator_publiccats, Rooms +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `navigator_publics` + ADD CONSTRAINT `fk_navpub_cat` + FOREIGN KEY (`public_cat_id`) REFERENCES `navigator_publiccats` (`id`) + ON DELETE CASCADE, + ADD CONSTRAINT `fk_navpub_room` + FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) + ON DELETE CASCADE; + +-- --------------------------------------------------------------------------- +-- 5.60 GOTW winners → Users +-- --------------------------------------------------------------------------- +ALTER TABLE IF EXISTS `gotw_winners` + ADD CONSTRAINT `fk_gotw_user` + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) + ON DELETE CASCADE; + + +-- ============================================================================= +-- PHASE 6: Charset standardization +-- ============================================================================= +-- Standardize remaining utf8mb3 tables to utf8mb4 for full Unicode support. + +ALTER TABLE IF EXISTS `guilds` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `guilds_elements` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `groups_items` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `messenger_friendships` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `room_rights` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `soundtracks` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `users_achievements_queue` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `users_saved_searches` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `users_target_offer_purchases` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `wordfilter` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE IF EXISTS `logs_shop_purchases` + CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + + +-- ============================================================================= +-- Done - Re-enable foreign key checks +-- ============================================================================= +SET FOREIGN_KEY_CHECKS = 1; +SET SQL_MODE = @OLD_SQL_MODE; diff --git a/Database Updates/07012026_UpdateDatabase_to_4-0-1.sql b/Database Updates/07012026_UpdateDatabase_to_4-0-1.sql deleted file mode 100644 index 97072873..00000000 --- a/Database Updates/07012026_UpdateDatabase_to_4-0-1.sql +++ /dev/null @@ -1,152 +0,0 @@ --- Wired Abuse Protection Settings --- These settings control the wired abuse detection and rate limiting system - --- Maximum recursion depth to prevent infinite loops (e.g., collision + chase triggering each other) -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('wired.abuse.max.recursion.depth', '10'); - --- Maximum events of same type per room within the rate limit window before triggering a ban --- Set higher for rooms with many users and complex wired setups -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('wired.abuse.max.events.per.window', '100'); - --- Time window in milliseconds for counting rapid events --- Events are counted within this window to detect abuse patterns -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('wired.abuse.rate.limit.window.ms', '10000'); - --- Duration in milliseconds to ban wired execution in a room after abuse is detected --- Default: 600000 (10 minutes) -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('wired.abuse.ban.duration.ms', '600000'); - --- Wired Abuse Alert Texts --- Alert shown to all users in the room when wired is temporarily disabled -INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('wired.abuse.room.alert', 'Wired execution has been temporarily disabled in this room due to abuse detection. It will resume in %minutes% minutes.'); - --- Title for the staff bubble alert -INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('wired.abuse.staff.title', 'Wired Abuse Detected'); - --- Message for the staff bubble alert --- Available placeholders: %roomname%, %owner%, %minutes% -INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('wired.abuse.staff.message', 'Room: %roomname%\nOwner: %owner%\nBanned for %minutes% minutes.'); - --- Link text for the staff bubble alert (navigates to the room) -INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('wired.abuse.staff.link', 'Go to Room'); - --- Default tick resolution for wired timer triggers (in milliseconds) -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('wired.tick.resolution', '100'); - - --- ===================================================== --- Wired Engine Rewrite - Configuration Settings --- ===================================================== --- This SQL script adds the configuration options for the new --- context-driven wired engine architecture. --- --- Run this script after upgrading to enable the new wired system. --- ===================================================== - --- Insert new wired engine configuration settings -INSERT INTO `emulator_settings` (`key`, `value`) VALUES -('wired.engine.enabled', '0'), -('wired.engine.exclusive', '0'), -('wired.engine.maxStepsPerStack', '100'), -('wired.engine.debug', '0') -ON DUPLICATE KEY UPDATE `key` = `key`; - --- ===================================================== --- Configuration Options Explained: --- ===================================================== --- --- wired.engine.enabled --- Enable the new wired engine. When set to 1, the new engine --- runs alongside the legacy WiredHandler for parallel testing. --- Default: 0 (disabled) --- --- wired.engine.exclusive --- When set to 1, disables the legacy WiredHandler completely. --- Only enable this after thorough testing with parallel mode. --- Default: 0 (legacy handler still active) --- --- wired.engine.maxStepsPerStack --- Maximum number of steps (trigger checks + condition evaluations --- + effect executions) allowed per wired stack execution. --- Prevents infinite loops from misconfigured wired setups. --- Default: 100 --- --- wired.engine.debug --- Enable verbose debug logging for wired execution. --- Useful for troubleshooting wired stack behavior. --- Default: 0 (disabled) --- --- ===================================================== --- Migration Path: --- ===================================================== --- --- Phase 1: Parallel Testing --- UPDATE emulator_settings SET value = '1' WHERE `key` = 'wired.engine.enabled'; --- -- Test all wired functionality, compare behavior between old and new --- --- Phase 2: Switch to New Engine --- UPDATE emulator_settings SET value = '1' WHERE `key` = 'wired.engine.exclusive'; --- -- Legacy handler disabled, new engine handles all wired events --- --- Phase 3: Cleanup (after confirming stability) --- -- Remove legacy WiredHandler calls from codebase --- --- ===================================================== - - --- ===================================================== --- Wired Tick System - Configuration Settings --- ===================================================== --- This SQL script adds the configuration options for the new --- high-resolution wired tick system (50ms default). --- --- Run this script to configure the wired timer triggers. --- ===================================================== - --- Insert new wired tick system configuration settings -INSERT INTO `emulator_settings` (`key`, `value`) VALUES -('wired.tick.interval.ms', '50'), -('wired.tick.debug', '0'), -('wired.tick.thread.priority', '6') -ON DUPLICATE KEY UPDATE `key` = `key`; - --- ===================================================== --- Configuration Options Explained: --- ===================================================== --- --- wired.tick.interval.ms --- The tick interval in milliseconds for wired timer triggers. --- Lower values = more precise timing but higher CPU usage. --- Recommended: 50 (default), Range: 10-500 --- Default: 50 --- --- wired.tick.debug --- Enable verbose debug logging for wired tick operations. --- Logs each tick cycle and trigger execution. --- Warning: Very verbose, only enable for troubleshooting. --- Default: 0 (disabled) --- --- wired.tick.thread.priority --- Thread priority for the wired tick service (1-10). --- Higher priority = better timing accuracy under load. --- Java Thread priorities: MIN=1, NORM=5, MAX=10 --- Default: 6 (slightly above normal) --- --- ===================================================== --- Usage Examples: --- ===================================================== --- --- Increase tick resolution for competitive mini-games: --- UPDATE emulator_settings SET value = '25' WHERE `key` = 'wired.tick.interval.ms'; --- --- Reduce CPU usage on low-end servers: --- UPDATE emulator_settings SET value = '100' WHERE `key` = 'wired.tick.interval.ms'; --- --- Enable debug logging for troubleshooting: --- UPDATE emulator_settings SET value = '1' WHERE `key` = 'wired.tick.debug'; --- --- ===================================================== -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('pathfinder.click.delay', '0'); -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('pathfinder.retro-style.diagonals', '0'); -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('pathfinder.step.allow.falling', '1'); - diff --git a/Database Updates/09012026_UpdateDatabase_to_4-0-2.sql b/Database Updates/09012026_UpdateDatabase_to_4-0-2.sql deleted file mode 100644 index 9d094a91..00000000 --- a/Database Updates/09012026_UpdateDatabase_to_4-0-2.sql +++ /dev/null @@ -1,672 +0,0 @@ -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ===================================================== --- SECTION 1: Pet System Emulator Settings --- ===================================================== - -INSERT INTO `emulator_settings` (`key`, `value`) VALUES --- Core pet limits -('hotel.pets.max.room', '15'), -('hotel.pets.max.inventory', '25'), -('hotel.pets.name.length.min', '1'), -('hotel.pets.name.length.max', '15'), -('hotel.daily.respect.pets', '3'), - --- Command cooldown and spam prevention -('pet.command.cooldown_ms', '2000'), -('pet.command.max_same_spam', '3'), -('pet.command.spam_reset_ms', '10000'), -('pet.command.min_energy', '15'), -('pet.command.min_happiness', '10'), -('pet.command.base_obey_chance', '70'), - --- Pet behavior settings -('pet.behavior.autonomous_action_delay', '5000'), -('pet.behavior.idle_wander_min_ms', '10000'), -('pet.behavior.idle_wander_max_ms', '30000'), - --- Pet stats decay/recovery rates (per cycle) -('pet.stats.hunger_decay', '1'), -('pet.stats.thirst_decay', '1'), -('pet.stats.energy_decay', '1'), -('pet.stats.happiness_decay', '1'), -('pet.stats.energy_recovery', '5'), -('pet.stats.happiness_recovery', '1'), - --- Pet thresholds (below this = needs attention) -('pet.threshold.hungry', '50'), -('pet.threshold.thirsty', '50'), -('pet.threshold.tired', '30'), -('pet.threshold.sad', '30'), - --- Pet breeding -('pet.breeding.timeout_seconds', '120') -ON DUPLICATE KEY UPDATE `value` = VALUES(`value`); - --- ===================================================== --- SECTION 2: Pet Actions (Pet Type Definitions) --- ===================================================== - -DROP TABLE IF EXISTS `pet_actions`; -CREATE TABLE `pet_actions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `pet_type` int(11) NOT NULL, - `pet_name` varchar(32) NOT NULL DEFAULT '', - `offspring_type` int(11) NOT NULL DEFAULT -1, - `happy_actions` varchar(100) NOT NULL DEFAULT 'sml', - `tired_actions` varchar(100) NOT NULL DEFAULT 'trd', - `random_actions` varchar(100) NOT NULL DEFAULT 'lov', - `can_swim` enum('0','1') NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `pet_type` (`pet_type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -INSERT INTO `pet_actions` (`pet_type`, `pet_name`, `offspring_type`, `happy_actions`, `tired_actions`, `random_actions`, `can_swim`) VALUES -(0, 'Dog', 29, 'sml,wav,joy', 'trd,yng', 'lov,snf', '0'), -(1, 'Cat', 28, 'sml,pur', 'trd,yng', 'lov,lck', '0'), -(2, 'Crocodile', -1, 'sml', 'trd', 'lov,snp', '1'), -(3, 'Terrier', 25, 'sml,wav,joy', 'trd,yng', 'lov,snf', '0'), -(4, 'Bear', 24, 'sml,grw', 'trd,yng', 'lov', '0'), -(5, 'Pig', 30, 'sml,oink', 'trd,yng', 'lov,rol', '0'), -(6, 'Lion', -1, 'sml,ror', 'trd,yng', 'lov', '0'), -(7, 'Rhino', -1, 'sml', 'trd,yng', 'lov', '0'), -(8, 'Tarantula', -1, 'sml', 'trd', 'lov,crw', '0'), -(9, 'Turtle', -1, 'sml', 'trd', 'lov', '1'), -(10, 'Chick', -1, 'sml,chp', 'trd', 'lov,pck', '0'), -(11, 'Frog', -1, 'sml,crk', 'trd', 'lov,jmp', '1'), -(12, 'Dragon', -1, 'sml,flm', 'trd,smk', 'lov,fly', '0'), -(13, 'Monster', -1, 'sml', 'trd', 'lov', '0'), -(14, 'Monkey', -1, 'sml,ook', 'trd,yng', 'lov,swg', '0'), -(15, 'Horse', -1, 'sml,nei', 'trd,yng', 'lov', '0'), -(16, 'Monsterplant', -1, 'sml', 'trd', 'lov', '0'), -(17, 'Bunny', -1, 'sml,hop', 'trd,yng', 'lov', '0'), -(18, 'Evil Bunny', -1, 'sml', 'trd', 'lov', '0'), -(19, 'Bored Bunny', -1, 'sml', 'trd', 'lov', '0'), -(20, 'Cute Bunny', -1, 'sml,hop', 'trd', 'lov', '0'), -(21, 'Wise Pigeon', -1, 'sml,coo', 'trd', 'lov,pck', '0'), -(22, 'Evil Pigeon', -1, 'sml', 'trd', 'lov,pck', '0'), -(23, 'Evil Monkey', -1, 'sml', 'trd', 'lov,swg', '0'), -(24, 'Baby Bear', -1, 'sml', 'trd,yng', 'lov', '0'), -(25, 'Baby Terrier', -1, 'sml', 'trd,yng', 'lov', '0'), -(26, 'Gnome', -1, 'sml,grn', 'trd', 'lov', '0'), -(27, 'Leprechaun', -1, 'sml,grn', 'trd', 'lov,jig', '0'), -(28, 'Baby Cat', -1, 'sml', 'trd,yng', 'lov', '0'), -(29, 'Baby Dog', -1, 'sml', 'trd,yng', 'lov', '0'), -(30, 'Baby Pig', -1, 'sml', 'trd,yng', 'lov', '0'), -(31, 'Haloompa', -1, 'sml', 'trd', 'lov', '0'), -(32, 'Fools Pet', -1, 'sml', 'trd', 'lov', '0'), -(33, 'Pterodactyl', -1, 'sml,sqk', 'trd', 'lov,fly', '0'), -(34, 'Velociraptor', -1, 'sml,hss', 'trd', 'lov,clw', '0'), -(35, 'Cow', -1, 'sml,moo', 'trd,yng', 'lov,chw', '0'); - --- ===================================================== --- SECTION 3: Pet Commands Data (English Command Names) --- ===================================================== --- Command IDs mapped to PetManager.petActions: --- 0=Free, 1=Sit, 2=Down, 3=Here, 4=Beg, 5=PlayDead, 6=Stay, 7=Follow --- 8=Stand, 9=Jump, 10=Speak, 11=Play, 12=Silent, 13=Nest, 14=Drink --- 15=FollowLeft, 16=FollowRight, 17=PlayFootball, 18=Teleport, 19=Bounce --- 20=Flatten, 21=Dance, 22=Spin, 23=Switch, 24=MoveForward --- 25=TurnLeft, 26=TurnRight, 27=Relax, 28=Croak, 29=Dip, 30=Wave --- 31=Mambo, 32=HighJump, 33=ChickenDance, 34=TripleJump --- 35=Wings, 36=BreatheFire, 37=Hang, 38=Torch, 40=Swing, 41=Roll --- 42=RingOfFire, 43=Eat, 44=WagTail, 45=Count, 46=Breed - -DROP TABLE IF EXISTS `pet_commands_data`; -CREATE TABLE `pet_commands_data` ( - `command_id` int(11) NOT NULL, - `text` varchar(25) NOT NULL, - `required_level` int(11) NOT NULL DEFAULT 1, - `reward_xp` int(11) NOT NULL DEFAULT 5, - `cost_happiness` int(11) NOT NULL DEFAULT 0, - `cost_energy` int(11) NOT NULL DEFAULT 0, - PRIMARY KEY (`command_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -INSERT INTO `pet_commands_data` (`command_id`, `text`, `required_level`, `reward_xp`, `cost_happiness`, `cost_energy`) VALUES -(0, 'free', 1, 5, 0, 0), -(1, 'sit', 1, 5, 2, 2), -(2, 'down', 2, 10, 3, 3), -(3, 'come here', 2, 10, 2, 5), -(4, 'beg', 2, 10, 3, 4), -(5, 'play dead', 3, 15, 4, 5), -(6, 'stay', 4, 10, 2, 3), -(7, 'follow', 5, 15, 3, 8), -(8, 'stand', 6, 15, 2, 3), -(9, 'jump', 6, 15, 4, 8), -(10, 'speak', 7, 10, 3, 3), -(11, 'play', 8, 5, 5, 10), -(12, 'silent', 8, 5, 2, 1), -(13, 'nest', 5, 5, 0, 0), -(14, 'drink', 1, 5, 0, 0), -(15, 'follow left', 15, 15, 4, 10), -(16, 'follow right', 15, 15, 4, 10), -(17, 'play football', 10, 5, 5, 12), -(18, 'teleport', 9, 5, 3, 5), -(19, 'bounce', 9, 5, 5, 10), -(20, 'flatten', 11, 5, 3, 4), -(21, 'dance', 12, 10, 6, 12), -(22, 'spin', 10, 5, 4, 8), -(23, 'switch', 12, 5, 3, 3), -(24, 'move forward', 17, 5, 2, 2), -(25, 'turn left', 18, 5, 2, 2), -(26, 'turn right', 18, 5, 2, 2), -(27, 'relax', 13, 5, 0, 0), -(28, 'croak', 14, 5, 3, 3), -(29, 'dip', 14, 5, 5, 10), -(30, 'wave', 5, 5, 2, 3), -(31, 'mambo', 18, 5, 6, 12), -(32, 'high jump', 18, 5, 5, 12), -(33, 'chicken dance', 7, 5, 5, 10), -(34, 'triple jump', 9, 5, 6, 15), -(35, 'spread wings', 8, 5, 4, 6), -(36, 'breathe fire', 10, 5, 5, 8), -(37, 'hang', 12, 5, 4, 6), -(38, 'torch', 6, 5, 3, 5), -(40, 'swing', 13, 5, 4, 8), -(41, 'roll', 10, 5, 5, 10), -(42, 'ring of fire', 20, 10, 8, 15), -(43, 'eat', 1, 5, 0, 0), -(44, 'wag tail', 4, 5, 3, 4), -(45, 'count', 6, 5, 4, 5), -(46, 'breed', 1, 5, 10, 20); - --- ===================================================== --- SECTION 4: Pet Commands (Pet Type -> Command Mapping) --- ===================================================== - -DROP TABLE IF EXISTS `pet_commands`; -CREATE TABLE `pet_commands` ( - `pet_id` int(11) NOT NULL, - `command_id` int(11) NOT NULL, - PRIMARY KEY (`pet_id`, `command_id`), - KEY `pet_id` (`pet_id`), - KEY `command_id` (`command_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - --- Dog (0) - Full standard pet commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9), -(0, 10), (0, 11), (0, 12), (0, 13), (0, 14), (0, 15), (0, 16), (0, 17), (0, 24), -(0, 25), (0, 26), (0, 43), (0, 44), (0, 46); - --- Cat (1) - Full standard pet commands + breed -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), -(1, 10), (1, 11), (1, 12), (1, 13), (1, 14), (1, 15), (1, 16), (1, 17), (1, 24), -(1, 25), (1, 26), (1, 43), (1, 46); - --- Crocodile (2) - Standard commands (can swim) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), -(2, 10), (2, 11), (2, 12), (2, 13), (2, 14), (2, 15), (2, 16), (2, 17), (2, 24), -(2, 25), (2, 26), (2, 29), (2, 43); - --- Terrier (3) - Standard commands + breed -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), -(3, 10), (3, 11), (3, 12), (3, 13), (3, 14), (3, 15), (3, 16), (3, 17), (3, 24), -(3, 25), (3, 26), (3, 43), (3, 46); - --- Bear (4) - Standard commands + breed -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (4, 7), (4, 8), (4, 9), -(4, 10), (4, 11), (4, 12), (4, 13), (4, 14), (4, 15), (4, 16), (4, 17), (4, 24), -(4, 25), (4, 26), (4, 43), (4, 46); - --- Pig (5) - Standard commands + breed -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(5, 0), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7), (5, 8), (5, 9), -(5, 10), (5, 11), (5, 12), (5, 13), (5, 14), (5, 15), (5, 16), (5, 17), (5, 24), -(5, 25), (5, 26), (5, 43), (5, 46); - --- Lion (6) - Standard commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(6, 0), (6, 1), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6), (6, 7), (6, 8), (6, 9), -(6, 10), (6, 11), (6, 12), (6, 13), (6, 14), (6, 15), (6, 16), (6, 17), (6, 24), -(6, 25), (6, 26), (6, 43); - --- Rhino (7) - Standard commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(7, 0), (7, 1), (7, 2), (7, 3), (7, 4), (7, 5), (7, 6), (7, 7), (7, 8), (7, 9), -(7, 10), (7, 11), (7, 12), (7, 13), (7, 14), (7, 15), (7, 16), (7, 17), (7, 24), -(7, 25), (7, 26), (7, 43); - --- Tarantula (8) - Spider commands (bounce, flatten, spin, etc.) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(8, 0), (8, 2), (8, 3), (8, 5), (8, 6), (8, 7), (8, 9), (8, 10), (8, 11), (8, 13), -(8, 14), (8, 15), (8, 16), (8, 17), (8, 19), (8, 20), (8, 21), (8, 22), (8, 23), -(8, 24), (8, 25), (8, 26), (8, 43); - --- Turtle (9) - Aquatic commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(9, 0), (9, 1), (9, 2), (9, 3), (9, 6), (9, 7), (9, 8), (9, 10), (9, 11), (9, 13), -(9, 14), (9, 15), (9, 16), (9, 24), (9, 25), (9, 26), (9, 29), (9, 41), (9, 43); - --- Chick (10) - Bird commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(10, 0), (10, 2), (10, 3), (10, 6), (10, 7), (10, 11), (10, 13), (10, 15), (10, 16), -(10, 17), (10, 33); - --- Frog (11) - Amphibian commands (croak, dip, wave, mambo) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(11, 0), (11, 1), (11, 2), (11, 3), (11, 4), (11, 5), (11, 6), (11, 7), (11, 9), -(11, 13), (11, 14), (11, 15), (11, 16), (11, 17), (11, 27), (11, 28), (11, 29), -(11, 30), (11, 31), (11, 43); - --- Dragon (12) - Dragon special commands (fire, hang, swing, ring of fire) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(12, 0), (12, 2), (12, 3), (12, 5), (12, 6), (12, 7), (12, 8), (12, 9), (12, 10), -(12, 11), (12, 12), (12, 13), (12, 14), (12, 15), (12, 16), (12, 22), (12, 35), -(12, 36), (12, 37), (12, 38), (12, 40), (12, 41), (12, 42), (12, 43); - --- Monster (13) - Basic commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(13, 0), (13, 2), (13, 3), (13, 6), (13, 7), (13, 13); - --- Monkey (14) - Monkey commands (wave, hang, swing) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(14, 0), (14, 1), (14, 2), (14, 3), (14, 4), (14, 5), (14, 6), (14, 7), (14, 9), -(14, 13), (14, 14), (14, 15), (14, 16), (14, 17), (14, 27), (14, 29), (14, 30), -(14, 31), (14, 37), (14, 40), (14, 43); - --- Horse (15) - Rideable pet commands + wag tail, count -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(15, 0), (15, 2), (15, 3), (15, 6), (15, 7), (15, 10), (15, 11), (15, 12), (15, 13), -(15, 14), (15, 15), (15, 16), (15, 24), (15, 25), (15, 26), (15, 43), (15, 44), (15, 45); - --- Monsterplant (16) - Minimal commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(16, 0), (16, 14), (16, 43); - --- Bunnies (17-20) - Bunny commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(17, 0), (17, 2), (17, 3), (17, 6), (17, 7), (17, 11), (17, 13), (17, 15), (17, 16), (17, 17), -(18, 0), (18, 2), (18, 3), (18, 6), (18, 7), (18, 11), (18, 13), (18, 15), (18, 16), (18, 17), -(19, 0), (19, 2), (19, 3), (19, 6), (19, 7), (19, 11), (19, 13), (19, 15), (19, 16), (19, 17), -(20, 0), (20, 2), (20, 3), (20, 6), (20, 7), (20, 11), (20, 13), (20, 15), (20, 16), (20, 17); - --- Pigeons (21-22) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(21, 0), (21, 2), (21, 3), (21, 6), (21, 7), (21, 11), (21, 13), (21, 15), (21, 16), (21, 17), -(22, 0), (22, 2), (22, 3), (22, 6), (22, 7), (22, 11), (22, 13), (22, 15), (22, 16), (22, 17); - --- Evil Monkey (23) - Monkey commands -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(23, 0), (23, 1), (23, 2), (23, 3), (23, 4), (23, 5), (23, 6), (23, 7), (23, 9), -(23, 13), (23, 14), (23, 15), (23, 16), (23, 17), (23, 25), (23, 26), (23, 27), -(23, 29), (23, 30), (23, 31), (23, 37), (23, 40), (23, 43); - --- Baby Bear (24) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(24, 0), (24, 1), (24, 2), (24, 3), (24, 4), (24, 6), (24, 7), (24, 8), (24, 10), -(24, 11), (24, 12), (24, 13), (24, 14), (24, 15), (24, 16), (24, 17), (24, 24), -(24, 25), (24, 26), (24, 43); - --- Baby Terrier (25) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(25, 0), (25, 1), (25, 2), (25, 3), (25, 4), (25, 6), (25, 7), (25, 8), (25, 10), -(25, 11), (25, 12), (25, 13), (25, 14), (25, 15), (25, 16), (25, 17), (25, 24), -(25, 25), (25, 26), (25, 43); - --- Gnome (26) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(26, 0), (26, 1), (26, 2), (26, 3), (26, 4), (26, 6), (26, 7), (26, 8), (26, 13), -(26, 14), (26, 15), (26, 16), (26, 17), (26, 25), (26, 26), (26, 43); - --- Leprechaun (27) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(27, 0), (27, 1), (27, 2), (27, 3), (27, 4), (27, 6), (27, 7), (27, 8), (27, 13), -(27, 14), (27, 15), (27, 16), (27, 17), (27, 25), (27, 26), (27, 43); - --- Baby Cat (28) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(28, 0), (28, 1), (28, 2), (28, 3), (28, 4), (28, 6), (28, 7), (28, 8), (28, 10), -(28, 11), (28, 12), (28, 13), (28, 14), (28, 15), (28, 16), (28, 17), (28, 24), -(28, 25), (28, 26), (28, 43); - --- Baby Dog (29) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(29, 0), (29, 1), (29, 2), (29, 3), (29, 4), (29, 6), (29, 7), (29, 8), (29, 10), -(29, 11), (29, 12), (29, 13), (29, 14), (29, 15), (29, 16), (29, 17), (29, 24), -(29, 25), (29, 26), (29, 43); - --- Baby Pig (30) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(30, 0), (30, 1), (30, 2), (30, 3), (30, 4), (30, 6), (30, 7), (30, 8), (30, 10), -(30, 11), (30, 12), (30, 13), (30, 14), (30, 15), (30, 16), (30, 17), (30, 24), -(30, 25), (30, 26), (30, 43); - --- Haloompa (31) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(31, 0), (31, 1), (31, 2), (31, 3), (31, 4), (31, 6), (31, 7), (31, 8), (31, 13), -(31, 14), (31, 15), (31, 16), (31, 17), (31, 25), (31, 26), (31, 43); - --- Fools Pet (32) - Full dance/trick set -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(32, 0), (32, 1), (32, 2), (32, 3), (32, 4), (32, 5), (32, 6), (32, 7), (32, 8), -(32, 9), (32, 13), (32, 14), (32, 15), (32, 16), (32, 17), (32, 21), (32, 25), -(32, 26), (32, 43); - --- Pterodactyl (33) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(33, 0), (33, 2), (33, 3), (33, 4), (33, 6), (33, 7), (33, 11), (33, 13), (33, 14), -(33, 15), (33, 16), (33, 25), (33, 26), (33, 43); - --- Velociraptor (34) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(34, 0), (34, 1), (34, 2), (34, 3), (34, 6), (34, 7), (34, 8), (34, 10), (34, 12), -(34, 13), (34, 14), (34, 15), (34, 16), (34, 17), (34, 21), (34, 26), (34, 43); - --- Cow (35) -INSERT INTO `pet_commands` (`pet_id`, `command_id`) VALUES -(35, 0), (35, 2), (35, 3), (35, 4), (35, 6), (35, 7), (35, 13), (35, 14), (35, 15), -(35, 16), (35, 17), (35, 25), (35, 26), (35, 30), (35, 43); - --- ===================================================== --- SECTION 5: Pet Vocals (Pet Speech Messages) --- ===================================================== --- pet_id = -1 means general vocals for all pets --- pet_id >= 0 means specific to that pet type - -CREATE TABLE IF NOT EXISTS `pet_vocals` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `pet_id` int(11) NOT NULL DEFAULT -1, - `type` varchar(20) NOT NULL, - `message` varchar(255) NOT NULL, - PRIMARY KEY (`id`), - KEY `pet_id` (`pet_id`), - KEY `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - --- Clear existing vocals -DELETE FROM `pet_vocals`; - --- ===================================================== --- GENERAL VOCALS (pet_id = -1, used by all pets) --- ===================================================== - --- GREET_OWNER - When owner enters room -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'GREET_OWNER', '*perks up excitedly*'), -(-1, 'GREET_OWNER', 'You''re back!'), -(-1, 'GREET_OWNER', '*bounces with joy*'), -(-1, 'GREET_OWNER', 'I missed you!'), -(-1, 'GREET_OWNER', '*runs in circles happily*'), -(-1, 'GREET_OWNER', 'Yay! My favorite person!'), -(-1, 'GREET_OWNER', '*jumps up and down*'), -(-1, 'GREET_OWNER', 'Finally! You''re here!'), -(-1, 'GREET_OWNER', '*tail wagging intensifies*'); - --- LEVEL_UP - When pet gains a level -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'LEVEL_UP', '*jumps with joy!*'), -(-1, 'LEVEL_UP', 'I leveled up!'), -(-1, 'LEVEL_UP', 'I feel stronger!'), -(-1, 'LEVEL_UP', 'Woohoo! New level!'), -(-1, 'LEVEL_UP', '*celebrates*'), -(-1, 'LEVEL_UP', 'I''m getting better!'), -(-1, 'LEVEL_UP', 'Level up! Yeah!'); - --- MUTED - When told to be silent -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'MUTED', '*stays quiet*'), -(-1, 'MUTED', '...'), -(-1, 'MUTED', '*zips lips*'), -(-1, 'MUTED', '*nods silently*'); - --- UNKNOWN_COMMAND - When pet doesn't understand -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'UNKNOWN_COMMAND', '*tilts head confused*'), -(-1, 'UNKNOWN_COMMAND', 'Huh?'), -(-1, 'UNKNOWN_COMMAND', 'I don''t understand...'), -(-1, 'UNKNOWN_COMMAND', '*looks puzzled*'), -(-1, 'UNKNOWN_COMMAND', 'What do you mean?'), -(-1, 'UNKNOWN_COMMAND', '*scratches head*'); - --- DISOBEY - When pet refuses command -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'DISOBEY', '*ignores command*'), -(-1, 'DISOBEY', 'Maybe later...'), -(-1, 'DISOBEY', 'I don''t feel like it'), -(-1, 'DISOBEY', '*pretends not to hear*'), -(-1, 'DISOBEY', 'Nah...'), -(-1, 'DISOBEY', '*turns away*'), -(-1, 'DISOBEY', 'Not right now'), -(-1, 'DISOBEY', '*yawns dismissively*'), -(-1, 'DISOBEY', 'Too tired for that'), -(-1, 'DISOBEY', 'Ask me again later'), -(-1, 'DISOBEY', '*looks the other way*'), -(-1, 'DISOBEY', 'I''d rather not'), -(-1, 'DISOBEY', '*shakes head*'), -(-1, 'DISOBEY', 'No thanks'), -(-1, 'DISOBEY', '*walks away slowly*'), -(-1, 'DISOBEY', 'Can''t be bothered'), -(-1, 'DISOBEY', '*pretends to be asleep*'), -(-1, 'DISOBEY', 'You can''t make me!'), -(-1, 'DISOBEY', '*stubbornly sits down*'), -(-1, 'DISOBEY', 'I refuse!'); - --- DRINKING - When drinking water -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'DRINKING', '*laps up water*'), -(-1, 'DRINKING', 'Refreshing!'), -(-1, 'DRINKING', '*gulp gulp*'), -(-1, 'DRINKING', 'Ah, that''s good!'), -(-1, 'DRINKING', '*slurp*'); - --- EATING - When eating food -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'EATING', '*munches happily*'), -(-1, 'EATING', 'Yum!'), -(-1, 'EATING', 'Delicious!'), -(-1, 'EATING', '*nom nom nom*'), -(-1, 'EATING', 'This is tasty!'), -(-1, 'EATING', '*chomps*'), -(-1, 'EATING', 'More please!'); - --- PLAYFUL - When in playful mood -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'PLAYFUL', '*bounces excitedly*'), -(-1, 'PLAYFUL', 'Let''s play!'), -(-1, 'PLAYFUL', '*runs around happily*'), -(-1, 'PLAYFUL', 'Play with me!'), -(-1, 'PLAYFUL', '*jumps around*'), -(-1, 'PLAYFUL', 'I wanna play!'), -(-1, 'PLAYFUL', '*brings a toy*'), -(-1, 'PLAYFUL', 'Wheee!'), -(-1, 'PLAYFUL', '*zooms around the room*'), -(-1, 'PLAYFUL', 'Catch me if you can!'); - --- SLEEPING - When sleeping -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'SLEEPING', '*snores softly*'), -(-1, 'SLEEPING', 'Zzz...'), -(-1, 'SLEEPING', '*mumbles in sleep*'), -(-1, 'SLEEPING', 'ZzZzZz...'), -(-1, 'SLEEPING', '*dreams peacefully*'), -(-1, 'SLEEPING', '*twitches while dreaming*'), -(-1, 'SLEEPING', '*snoozes*'), -(-1, 'SLEEPING', '*breathes slowly*'); - --- TIRED - When tired/low energy -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'TIRED', '*yawns*'), -(-1, 'TIRED', 'So sleepy...'), -(-1, 'TIRED', '*eyes drooping*'), -(-1, 'TIRED', 'I need rest...'); - --- THIRSTY - When thirsty -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'THIRSTY', '*pants*'), -(-1, 'THIRSTY', 'Water please!'), -(-1, 'THIRSTY', '*looks at water bowl*'), -(-1, 'THIRSTY', 'So thirsty...'), -(-1, 'THIRSTY', '*dry tongue*'), -(-1, 'THIRSTY', 'Need a drink!'), -(-1, 'THIRSTY', '*licks lips*'), -(-1, 'THIRSTY', 'I''m parched!'); - --- HUNGRY - When hungry -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'HUNGRY', '*stomach growls*'), -(-1, 'HUNGRY', 'I need food!'), -(-1, 'HUNGRY', '*looks at food bowl*'), -(-1, 'HUNGRY', 'Feed me!'), -(-1, 'HUNGRY', 'So hungry...'), -(-1, 'HUNGRY', '*tummy rumbles*'), -(-1, 'HUNGRY', 'Food please!'), -(-1, 'HUNGRY', '*drools at thought of food*'), -(-1, 'HUNGRY', 'Is it dinner time?'), -(-1, 'HUNGRY', '*sniffs around for food*'), -(-1, 'HUNGRY', 'I could eat a horse!'), -(-1, 'HUNGRY', '*begs for food*'), -(-1, 'HUNGRY', 'Starving over here!'); - --- GENERIC_NEUTRAL - Random idle chat -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'GENERIC_NEUTRAL', '*looks around*'), -(-1, 'GENERIC_NEUTRAL', '*sniffs the air*'), -(-1, 'GENERIC_NEUTRAL', '*stretches*'), -(-1, 'GENERIC_NEUTRAL', '*scratches ear*'), -(-1, 'GENERIC_NEUTRAL', '*observes surroundings*'), -(-1, 'GENERIC_NEUTRAL', '*sits quietly*'), -(-1, 'GENERIC_NEUTRAL', '*watches curiously*'); - --- GENERIC_SAD - When sad/low happiness -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'GENERIC_SAD', '*whimpers*'), -(-1, 'GENERIC_SAD', '*looks sad*'), -(-1, 'GENERIC_SAD', '*sighs*'), -(-1, 'GENERIC_SAD', '*droops head*'), -(-1, 'GENERIC_SAD', 'I''m lonely...'), -(-1, 'GENERIC_SAD', '*mopes around*'), -(-1, 'GENERIC_SAD', '*looks dejected*'), -(-1, 'GENERIC_SAD', 'Nobody loves me...'), -(-1, 'GENERIC_SAD', '*sulks in corner*'); - --- GENERIC_HAPPY - When happy -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(-1, 'GENERIC_HAPPY', '*wags tail happily*'), -(-1, 'GENERIC_HAPPY', '*jumps with joy*'), -(-1, 'GENERIC_HAPPY', ':)'), -(-1, 'GENERIC_HAPPY', 'Life is good!'), -(-1, 'GENERIC_HAPPY', '*prances around*'), -(-1, 'GENERIC_HAPPY', '*does a happy dance*'), -(-1, 'GENERIC_HAPPY', 'I''m so happy!'), -(-1, 'GENERIC_HAPPY', '*beams with joy*'), -(-1, 'GENERIC_HAPPY', 'What a great day!'), -(-1, 'GENERIC_HAPPY', '*grins*'), -(-1, 'GENERIC_HAPPY', '*radiates happiness*'), -(-1, 'GENERIC_HAPPY', 'Yippee!'), -(-1, 'GENERIC_HAPPY', '*spins around happily*'), -(-1, 'GENERIC_HAPPY', 'This is the best!'); - --- ===================================================== --- PET-SPECIFIC VOCALS --- ===================================================== - --- Dog (0) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(0, 'GENERIC_HAPPY', 'Woof woof!'), -(0, 'GENERIC_HAPPY', '*wags tail furiously*'), -(0, 'GREET_OWNER', '*barks excitedly*'), -(0, 'GREET_OWNER', 'Woof! You''re home!'), -(0, 'PLAYFUL', '*drops ball at your feet*'), -(0, 'PLAYFUL', 'Throw the ball!'), -(0, 'HUNGRY', '*stares at food bowl*'), -(0, 'DISOBEY', '*chases own tail instead*'); - --- Cat (1) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(1, 'GENERIC_HAPPY', '*purrs*'), -(1, 'GENERIC_HAPPY', 'Meow!'), -(1, 'GENERIC_NEUTRAL', '*grooms self*'), -(1, 'GREET_OWNER', '*rubs against leg*'), -(1, 'DISOBEY', '*looks away disdainfully*'), -(1, 'DISOBEY', '*yawns dismissively*'), -(1, 'PLAYFUL', '*pounces on shadow*'), -(1, 'SLEEPING', '*purrs while sleeping*'); - --- Dragon (12) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(12, 'GENERIC_HAPPY', '*breathes small flames happily*'), -(12, 'GENERIC_HAPPY', '*roars softly*'), -(12, 'GENERIC_SAD', '*smoke puffs from nostrils*'), -(12, 'DISOBEY', '*snorts flames*'), -(12, 'DISOBEY', 'I am a DRAGON, not a servant!'), -(12, 'HUNGRY', '*eyes the nearest villager*'), -(12, 'HUNGRY', 'I require sustenance!'), -(12, 'THIRSTY', '*smoke rises as throat dries*'), -(12, 'PLAYFUL', '*chases own tail, breathing fire*'), -(12, 'GREET_OWNER', '*bows majestic head*'), -(12, 'SLEEPING', '*snores, causing small fires*'), -(12, 'LEVEL_UP', '*ROARS triumphantly!*'); - --- Horse (15) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(15, 'GENERIC_HAPPY', '*neighs happily*'), -(15, 'GENERIC_NEUTRAL', '*swishes tail*'), -(15, 'GREET_OWNER', '*whinnies in greeting*'), -(15, 'DISOBEY', 'Nay. (Geddit?)'), -(15, 'HUNGRY', '*looks at hay expectantly*'), -(15, 'PLAYFUL', 'Let''s go for a ride!'), -(15, 'TIRED', '*stamps hoof wearily*'); - --- Tarantula (8) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(8, 'GREET_OWNER', 'You look more edible every time!'), -(8, 'DISOBEY', '*hisses*'), -(8, 'DISOBEY', 'I do not obey mammals'), -(8, 'HUNGRY', 'Bring me fresh meat!'), -(8, 'PLAYFUL', '*dances on eight legs*'), -(8, 'GENERIC_HAPPY', '*clicks mandibles happily*'); - --- Frog (11) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(11, 'GENERIC_HAPPY', 'Ribbit!'), -(11, 'GENERIC_NEUTRAL', '*croaks*'), -(11, 'GREET_OWNER', '*hops excitedly*'), -(11, 'PLAYFUL', '*catches fly with tongue*'), -(11, 'THIRSTY', '*seeks water*'); - --- Cow (35) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(35, 'GENERIC_HAPPY', 'Moooo!'), -(35, 'GREET_OWNER', 'Greetings. Did you bring kale?'), -(35, 'EATING', '*chews grass thoughtfully*'), -(35, 'DISOBEY', 'I''d rather meditate'), -(35, 'LEVEL_UP', '*DING* I''m on the up!'); - --- Lion (6) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(6, 'GENERIC_HAPPY', '*roars majestically*'), -(6, 'GREET_OWNER', '*nods regally*'), -(6, 'DISOBEY', 'I am the king!'), -(6, 'HUNGRY', '*eyes prey*'), -(6, 'PLAYFUL', '*pounces playfully*'); - --- Bear (4) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(4, 'GENERIC_HAPPY', '*growls contentedly*'), -(4, 'HUNGRY', '*sniffs for honey*'), -(4, 'SLEEPING', '*hibernates*'), -(4, 'GREET_OWNER', '*bear hug incoming*'); - --- Monkey (14) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(14, 'GENERIC_HAPPY', 'Ook ook!'), -(14, 'PLAYFUL', '*swings from furniture*'), -(14, 'GREET_OWNER', '*does a backflip*'), -(14, 'DISOBEY', '*throws something*'), -(14, 'HUNGRY', '*looks for bananas*'); - --- Bunny (17) specific vocals -INSERT INTO `pet_vocals` (`pet_id`, `type`, `message`) VALUES -(17, 'GENERIC_HAPPY', '*hops happily*'), -(17, 'GREET_OWNER', '*twitches nose excitedly*'), -(17, 'PLAYFUL', '*binkies around*'), -(17, 'HUNGRY', '*nibbles on carrot*'); - -SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file diff --git a/Database Updates/12012026_Battle Banzai.sql b/Database Updates/12012026_Battle Banzai.sql deleted file mode 100644 index 1e9c9461..00000000 --- a/Database Updates/12012026_Battle Banzai.sql +++ /dev/null @@ -1,18 +0,0 @@ -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ===================================================== --- Update 4.0.2-beta to 4.0.3-beta --- ===================================================== - -INSERT INTO `emulator_settings` (`key`, `value`) VALUES --- Maximum pending flood-fill tasks in the executor queue --- Prevents memory leaks from rapid tile locking -('hotel.banzai.fill.max_queue', '50'), - --- Minimum interval (ms) between flood-fill calculations per game --- Prevents errors via rapid wired triggering -('hotel.banzai.fill.cooldown_ms', '100') -ON DUPLICATE KEY UPDATE `value` = VALUES(`value`); - -SET FOREIGN_KEY_CHECKS = 1; diff --git a/Database Updates/12012026_Breeding Fixes.sql b/Database Updates/12012026_Breeding Fixes.sql deleted file mode 100644 index 52825f2b..00000000 --- a/Database Updates/12012026_Breeding Fixes.sql +++ /dev/null @@ -1,216 +0,0 @@ --- ===================================================== --- Pet Breeding Complete Setup --- ===================================================== --- This file sets up all breeding-related data: --- 1. pet_breeding - Maps parent pet types to offspring types --- 2. pet_breeding_races - Defines possible breeds/colors for offspring by rarity --- ===================================================== - --- ===================================================== --- SECTION 1: Pet Breeding (Parent -> Offspring Mapping) --- ===================================================== --- This table maps which pet type produces which baby type - -CREATE TABLE IF NOT EXISTS `pet_breeding` ( - `pet_id` int(11) NOT NULL COMMENT 'Parent pet type', - `offspring_id` int(11) NOT NULL COMMENT 'Baby pet type', - PRIMARY KEY (`pet_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - --- Clear existing data -TRUNCATE TABLE `pet_breeding`; - --- Insert breeding mappings -INSERT INTO `pet_breeding` (`pet_id`, `offspring_id`) VALUES -(0, 29), -- Dog -> Baby Dog -(1, 28), -- Cat -> Baby Cat -(3, 25), -- Terrier -> Baby Terrier -(4, 24), -- Bear -> Baby Bear -(5, 30); -- Pig -> Baby Pig - --- ===================================================== --- SECTION 2: Pet Breeding Races (Offspring Breeds by Rarity) --- ===================================================== --- rarity_level: 1=Common, 2=Uncommon, 3=Rare, 4=Epic --- breed: The visual breed/color variant of the baby pet --- --- Higher rarity = harder to get, more special colors --- Each baby pet type should have breeds at all 4 rarity levels - -CREATE TABLE IF NOT EXISTS `pet_breeding_races` ( - `pet_id` int(11) NOT NULL COMMENT 'Baby pet type (offspring)', - `rarity_level` int(11) NOT NULL COMMENT '1=Common, 2=Uncommon, 3=Rare, 4=Epic', - `breed` int(11) NOT NULL COMMENT 'Visual breed/color variant', - PRIMARY KEY (`pet_id`, `rarity_level`, `breed`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - --- Clear existing data -TRUNCATE TABLE `pet_breeding_races`; - --- ===================================================== --- Baby Dog (29) - Offspring of Dog (0) - 20 breeds --- ===================================================== -INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES --- Common breeds (rarity 1) - Most likely to get -(29, 1, 0), -(29, 1, 1), -(29, 1, 2), -(29, 1, 3), -(29, 1, 4), -(29, 1, 5), -(29, 1, 6), -(29, 1, 7), --- Uncommon breeds (rarity 2) -(29, 2, 8), -(29, 2, 9), -(29, 2, 10), -(29, 2, 11), -(29, 2, 12), --- Rare breeds (rarity 3) -(29, 3, 13), -(29, 3, 14), -(29, 3, 15), -(29, 3, 16), --- Epic breeds (rarity 4) - Hardest to get -(29, 4, 17), -(29, 4, 18), -(29, 4, 19); - --- ===================================================== --- Baby Cat (28) - Offspring of Cat (1) - 20 breeds --- ===================================================== -INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES --- Common breeds (rarity 1) -(28, 1, 0), -(28, 1, 1), -(28, 1, 2), -(28, 1, 3), -(28, 1, 4), -(28, 1, 5), -(28, 1, 6), -(28, 1, 7), --- Uncommon breeds (rarity 2) -(28, 2, 8), -(28, 2, 9), -(28, 2, 10), -(28, 2, 11), -(28, 2, 12), --- Rare breeds (rarity 3) -(28, 3, 13), -(28, 3, 14), -(28, 3, 15), -(28, 3, 16), --- Epic breeds (rarity 4) -(28, 4, 17), -(28, 4, 18), -(28, 4, 19); - --- ===================================================== --- Baby Terrier (25) - Offspring of Terrier (3) - 20 breeds --- ===================================================== -INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES --- Common breeds (rarity 1) -(25, 1, 0), -(25, 1, 1), -(25, 1, 2), -(25, 1, 3), -(25, 1, 4), -(25, 1, 5), -(25, 1, 6), -(25, 1, 7), --- Uncommon breeds (rarity 2) -(25, 2, 8), -(25, 2, 9), -(25, 2, 10), -(25, 2, 11), -(25, 2, 12), --- Rare breeds (rarity 3) -(25, 3, 13), -(25, 3, 14), -(25, 3, 15), -(25, 3, 16), --- Epic breeds (rarity 4) -(25, 4, 17), -(25, 4, 18), -(25, 4, 19); - --- ===================================================== --- Baby Bear (24) - Offspring of Bear (4) - 20 breeds --- ===================================================== -INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES --- Common breeds (rarity 1) -(24, 1, 0), -(24, 1, 1), -(24, 1, 2), -(24, 1, 3), -(24, 1, 4), -(24, 1, 5), -(24, 1, 6), -(24, 1, 7), --- Uncommon breeds (rarity 2) -(24, 2, 8), -(24, 2, 9), -(24, 2, 10), -(24, 2, 11), -(24, 2, 12), --- Rare breeds (rarity 3) -(24, 3, 13), -(24, 3, 14), -(24, 3, 15), -(24, 3, 16), --- Epic breeds (rarity 4) -(24, 4, 17), -(24, 4, 18), -(24, 4, 19); - --- ===================================================== --- Baby Pig (30) - Offspring of Pig (5) - 20 breeds --- ===================================================== -INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES --- Common breeds (rarity 1) -(30, 1, 0), -(30, 1, 1), -(30, 1, 2), -(30, 1, 3), -(30, 1, 4), -(30, 1, 5), -(30, 1, 6), -(30, 1, 7), --- Uncommon breeds (rarity 2) -(30, 2, 8), -(30, 2, 9), -(30, 2, 10), -(30, 2, 11), -(30, 2, 12), --- Rare breeds (rarity 3) -(30, 3, 13), -(30, 3, 14), -(30, 3, 15), -(30, 3, 16), --- Epic breeds (rarity 4) -(30, 4, 17), -(30, 4, 18), -(30, 4, 19); - --- ===================================================== --- Also ensure pet_actions has correct offspring_type values --- ===================================================== -UPDATE `pet_actions` SET `offspring_type` = 29 WHERE `pet_type` = 0; -- Dog -> Baby Dog -UPDATE `pet_actions` SET `offspring_type` = 28 WHERE `pet_type` = 1; -- Cat -> Baby Cat -UPDATE `pet_actions` SET `offspring_type` = 25 WHERE `pet_type` = 3; -- Terrier -> Baby Terrier -UPDATE `pet_actions` SET `offspring_type` = 24 WHERE `pet_type` = 4; -- Bear -> Baby Bear -UPDATE `pet_actions` SET `offspring_type` = 30 WHERE `pet_type` = 5; -- Pig -> Baby Pig - --- Set non-breedable pets to -1 -UPDATE `pet_actions` SET `offspring_type` = -1 WHERE `pet_type` NOT IN (0, 1, 3, 4, 5); - --- ===================================================== --- Fix any items_base with leading/trailing spaces in interaction_type --- ===================================================== -UPDATE `items_base` SET `interaction_type` = TRIM(`interaction_type`); - --- ===================================================== --- Ensure breeding nest items have correct interaction_type --- ===================================================== -UPDATE `items_base` SET `interaction_type` = 'breeding_nest' -WHERE `item_name` LIKE 'pet_breeding_%' AND `interaction_type` != 'breeding_nest'; diff --git a/Database Updates/12012026_ChatBubbles.sql b/Database Updates/12012026_ChatBubbles.sql deleted file mode 100644 index e955ad1a..00000000 --- a/Database Updates/12012026_ChatBubbles.sql +++ /dev/null @@ -1,15 +0,0 @@ -ALTER TABLE `permissions` ADD COLUMN `cmd_update_chat_bubbles` ENUM('0','1') NOT NULL DEFAULT '0'; - ---New table for custom chat bubbles -CREATE TABLE chat_bubbles ( - type INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT "Only 46 and higher will work", - name VARCHAR(255) NOT NULL DEFAULT '', - permission VARCHAR(255) NOT NULL DEFAULT '', - overridable BOOLEAN NOT NULL DEFAULT TRUE, - triggers_talking_furniture BOOLEAN NOT NULL DEFAULT FALSE -); - ---New texts for update chat bubbles command -INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.keys.cmd_update_chat_bubbles', 'update_chat_bubbles'); -INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.success.cmd_update_chat_bubbles', 'Successfully updated chat bubbles'); -INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.description.cmd_update_chat_bubbles', ':update_chat_bubbles'); \ No newline at end of file diff --git a/Database Updates/16032026_updateall_command.sql b/Database Updates/16032026_updateall_command.sql deleted file mode 100644 index 8162bc0a..00000000 --- a/Database Updates/16032026_updateall_command.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE `permissions` ADD `cmd_update_all` ENUM('0','1') NOT NULL DEFAULT '0' AFTER `cmd_update_achievements`; - -INSERT INTO `emulator_texts` (`key`, `value`) VALUES - ('commands.keys.cmd_update_all', 'update_all'), - ('commands.description.cmd_update_all', ':update_all'), - ('commands.succes.cmd_update_all', 'Successfully updated everything!'); diff --git a/Database Updates/17032026_allow_underpass.sql b/Database Updates/17032026_allow_underpass.sql deleted file mode 100644 index c5c7dcac..00000000 --- a/Database Updates/17032026_allow_underpass.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE `rooms` ADD COLUMN `allow_underpass` ENUM('0','1') NOT NULL DEFAULT '0' AFTER `move_diagonally`; diff --git a/Database Updates/19032026_hotel_timezone.sql b/Database Updates/19032026_hotel_timezone.sql deleted file mode 100644 index 8c08a899..00000000 --- a/Database Updates/19032026_hotel_timezone.sql +++ /dev/null @@ -1,22 +0,0 @@ -SET NAMES utf8mb4; - --- Create the hotel timezone setting if it does not exist yet. -INSERT INTO `emulator_settings` (`key`, `value`) -SELECT 'hotel.timezone', 'Europe/Rome' -WHERE NOT EXISTS ( - SELECT 1 - FROM `emulator_settings` - WHERE `key` = 'hotel.timezone' -); - --- Keep the default/example value aligned for existing installs too. -UPDATE `emulator_settings` -SET `value` = 'Europe/Rome' -WHERE `key` = 'hotel.timezone'; - --- Helper query for a timezone selector. --- If MySQL/MariaDB timezone tables are populated, this returns the available timezone ids. -SELECT `Name` AS `timezone_id` -FROM `mysql`.`time_zone_name` -WHERE `Name` IS NOT NULL -ORDER BY `Name`; diff --git a/Database Updates/21022026_user_prefixes.sql b/Database Updates/21022026_user_prefixes.sql deleted file mode 100644 index e0ee239f..00000000 --- a/Database Updates/21022026_user_prefixes.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE IF NOT EXISTS `user_prefixes` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `user_id` INT(11) NOT NULL, - `text` VARCHAR(50) NOT NULL, - `color` VARCHAR(255) NOT NULL DEFAULT '#FFFFFF', - `icon` VARCHAR(50) NOT NULL DEFAULT '', - `effect` VARCHAR(50) NOT NULL DEFAULT '', - `active` TINYINT(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - INDEX `idx_user_id` (`user_id`), - INDEX `idx_user_active` (`user_id`, `active`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/Database Updates/Default_Camera.sql b/Database Updates/Default_Camera.sql deleted file mode 100644 index 63240398..00000000 --- a/Database Updates/Default_Camera.sql +++ /dev/null @@ -1,71 +0,0 @@ --- ============================================================ --- Camera - Database Setup --- Run this SQL manually before using the camera feature. --- ============================================================ - --- ----------------------------------------- --- Table: camera_web (stores published photos) --- ----------------------------------------- -CREATE TABLE IF NOT EXISTS `camera_web` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `user_id` INT(11) NOT NULL, - `room_id` INT(11) NOT NULL DEFAULT 0, - `timestamp` INT(11) NOT NULL DEFAULT 0, - `url` VARCHAR(255) NOT NULL DEFAULT '', - PRIMARY KEY (`id`), - INDEX `idx_camera_web_user_id` (`user_id`), - INDEX `idx_camera_web_timestamp` (`timestamp`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- ----------------------------------------- --- Emulator Settings for Camera --- ----------------------------------------- --- Uses INSERT IGNORE so existing values are not overwritten. - --- Base URL where camera photos are served (include trailing slash) -INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES -('camera.url', 'http://localhost/camera/'); - --- Filesystem path where full-size camera photos are saved (include trailing slash) -INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES -('imager.location.output.camera', '/path/to/www/camera/'); - --- Filesystem path where room thumbnail images are saved (include trailing slash) -INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES -('imager.location.output.thumbnail', '/path/to/www/thumbnails/'); - --- Item ID for the wall photo item (must exist in items_base with interaction type "external_image") -INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES -('camera.item_id', '0'); - --- Price in credits to purchase a photo as a wall item -INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES -('camera.price.credits', '2'); - --- Price in seasonal points to purchase a photo as a wall item -INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES -('camera.price.points', '0'); - --- Price in seasonal points to publish a photo to the web -INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES -('camera.price.points.publish', '1'); - --- JSON template for photo item extradata --- Available placeholders: %timestamp%, %room_id%, %url%, %id% -INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES -('camera.extradata', '{"t":"%timestamp%","u":"%id%","m":"","s":"%room_id%","w":"%url%"}'); - --- ----------------------------------------- --- Emulator Texts for Camera --- ----------------------------------------- -INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES -('camera.permission', 'You do not have permission to use the camera.'); - -INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES -('camera.wait', 'Please wait %seconds% more seconds before taking another photo.'); - -INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES -('camera.error.creation', 'An error occurred while processing your photo. Please try again.'); - -INSERT IGNORE INTO `emulator_texts` (`key`, `value`) VALUES -('camera.daily.limit', 'You have reached the daily photo limit. Try again tomorrow.'); diff --git a/Database Updates/UpdateDatabase_Allow_diagonale.sql b/Database Updates/UpdateDatabase_Allow_diagonale.sql deleted file mode 100644 index 7683c489..00000000 --- a/Database Updates/UpdateDatabase_Allow_diagonale.sql +++ /dev/null @@ -1 +0,0 @@ -INSERT INTO emulator_settings (`key`, `value`) VALUES ('pathfinder.diagonal.enabled', '1'); diff --git a/Database Updates/UpdateDatabase_BOT.sql b/Database Updates/UpdateDatabase_BOT.sql deleted file mode 100644 index 67382dde..00000000 --- a/Database Updates/UpdateDatabase_BOT.sql +++ /dev/null @@ -1,4 +0,0 @@ -### New bot walking settings - -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('hotel.bot.limit.walking.distance', '1'); -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('hotel.bot.limit.walking.distance.radius', '5'); \ No newline at end of file diff --git a/Database Updates/UpdateDatabase_Banners.sql b/Database Updates/UpdateDatabase_Banners.sql deleted file mode 100644 index 0792f3d6..00000000 --- a/Database Updates/UpdateDatabase_Banners.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE `users` -ADD COLUMN `background_id` INT(11) NOT NULL DEFAULT 0 AFTER `machine_id`, -ADD COLUMN `background_stand_id` INT(11) NOT NULL DEFAULT 0 AFTER `background_id`, -ADD COLUMN `background_overlay_id` INT(11) NOT NULL DEFAULT 0 AFTER `background_stand_id`; diff --git a/Database Updates/UpdateDatabase_DanceCMD.sql b/Database Updates/UpdateDatabase_DanceCMD.sql deleted file mode 100644 index 8ed80044..00000000 --- a/Database Updates/UpdateDatabase_DanceCMD.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE `camwijs`.`permissions` -ADD COLUMN `cms_dance` ENUM('0', '1') NULL DEFAULT '0' AFTER `cmd_credits`; - -INSERT INTO emulator_texts (`key`, `value`) VALUES ('commands.description.cmd_dance', 'dance around the world ! use 1 t/m 4 and 0 to stop'); -INSERT INTO emulator_texts (`key`, `value`) VALUES ('commands.keys.cmd_dance', 'dance'); diff --git a/Database Updates/UpdateDatabase_Happiness.sql b/Database Updates/UpdateDatabase_Happiness.sql deleted file mode 100644 index 68c3246e..00000000 --- a/Database Updates/UpdateDatabase_Happiness.sql +++ /dev/null @@ -1,4 +0,0 @@ -UPDATE `emulator_texts` SET `key` = 'generic.pet.happiness', `value` = 'Happiness' WHERE `key` = 'generic.pet.happyness'; - -ALTER TABLE `pet_commands_data` CHANGE `cost_happyness` `cost_happiness` int(11) NOT NULL DEFAULT '0'; -ALTER TABLE `users_pets` CHANGE `happyness` `happiness` int(11) NOT NULL DEFAULT '100'; diff --git a/Database Updates/UpdateDatabase_Websocket.sql b/Database Updates/UpdateDatabase_Websocket.sql deleted file mode 100644 index e629c94f..00000000 --- a/Database Updates/UpdateDatabase_Websocket.sql +++ /dev/null @@ -1,6 +0,0 @@ -### WEBSOCKET SETTINGS - -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('websockets.whitelist', 'localhost'); # Change this to the url of the websocket Expl. ws.mydomain.com -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('ws.nitro.host', '0.0.0.0'); # Best is this to leave it at 0.0.0.0 -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('ws.nitro.ip.header', ''); # When useing a proxy change the header : X-Forwarded-For when using a proxy server or CF-Connecting-IP if behind Cloudflare. -INSERT INTO `emulator_settings` (`key`, `value`) VALUES ('ws.nitro.port', '2096'); # set the port of the websocket, cloudflare ports : 443 / 2053 / 2083 / 2087 / 2096 / 8443 \ No newline at end of file diff --git a/Database Updates/UpdateDatabase_unignorable.sql b/Database Updates/UpdateDatabase_unignorable.sql deleted file mode 100644 index bcca3ce4..00000000 --- a/Database Updates/UpdateDatabase_unignorable.sql +++ /dev/null @@ -1,2 +0,0 @@ ---New permission -ALTER TABLE `permissions` ADD COLUMN `acc_unignorable` ENUM('0','1') NOT NULL DEFAULT '0'; From 5319e5e5c34137329d7c661f9e16147c725c97c9 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 26 Mar 2026 09:41:31 +0100 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=86=99=20Database=20performance=20fix?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. HabboManager - O(1) username lookup Before: getHabbo(String) held a synchronized lock and iterated ALL online users every call After: Secondary ConcurrentHashMap keyed by lowercase username → instant get() lookup, no lock contention 2. ItemsComponent - Batch DB saves Before: dispose() spawned a separate thread per dirty item, each opening its own DB connection After: Single connection, JDBC addBatch()/executeBatch() for both UPDATE and DELETE, flushed every 100 items. A user with 500 dirty items now does 5 batch executions instead of 500 thread spawns + 500 connections. 3. AcceptFriendRequestEvent - N+1 elimination Before: For each offline user: query 1 (getOfflineHabboInfo by ID) → query 2 (load full Habbo by username) = 2 queries × up to 100 users = 200 queries After: Single query by user ID directly = 1 query × up to 100 users = 100 queries (50% reduction) 4. RoomManager - Direct HashMap lookup Before: getCategory(int id) iterated all categories checking getId() == id even though the map is already keyed by ID After: Direct roomCategories.get(id) → O(1) instead of O(n) --- .../habbo/habbohotel/rooms/RoomManager.java | 18 +----- .../habbo/habbohotel/users/HabboManager.java | 21 +++---- .../users/inventory/ItemsComponent.java | 63 ++++++++++++++++--- .../friends/AcceptFriendRequestEvent.java | 16 +---- 4 files changed, 70 insertions(+), 48 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java index 6348ca40..b44f0723 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java @@ -179,12 +179,7 @@ public class RoomManager { } public RoomCategory getCategory(int id) { - for (RoomCategory category : this.roomCategories.values()) { - if (category.getId() == id) - return category; - } - - return null; + return this.roomCategories.get(id); } public RoomCategory getCategory(String name) { @@ -220,15 +215,8 @@ public class RoomManager { } public boolean hasCategory(int categoryId, Habbo habbo) { - for (RoomCategory category : this.roomCategories.values()) { - if (category.getId() == categoryId) { - if (category.getMinRank() <= habbo.getHabboInfo().getRank().getId()) { - return true; - } - } - } - - return false; + RoomCategory category = this.roomCategories.get(categoryId); + return category != null && category.getMinRank() <= habbo.getHabboInfo().getRank().getId(); } public THashMap getRoomCategories() { diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboManager.java index af9f7368..1ed19667 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboManager.java @@ -35,11 +35,13 @@ public class HabboManager { public static boolean NAMECHANGE_ENABLED = false; private final ConcurrentHashMap onlineHabbos; + private final ConcurrentHashMap onlineHabbosByName; public HabboManager() { long millis = System.currentTimeMillis(); this.onlineHabbos = new ConcurrentHashMap<>(); + this.onlineHabbosByName = new ConcurrentHashMap<>(); LOGGER.info("Habbo Manager -> Loaded! ({} MS)", System.currentTimeMillis() - millis); } @@ -80,10 +82,12 @@ public class HabboManager { public void addHabbo(Habbo habbo) { this.onlineHabbos.put(habbo.getHabboInfo().getId(), habbo); + this.onlineHabbosByName.put(habbo.getHabboInfo().getUsername().toLowerCase(), habbo); } public void removeHabbo(Habbo habbo) { this.onlineHabbos.remove(habbo.getHabboInfo().getId()); + this.onlineHabbosByName.remove(habbo.getHabboInfo().getUsername().toLowerCase()); } public Habbo getHabbo(int id) { @@ -91,14 +95,7 @@ public class HabboManager { } public Habbo getHabbo(String username) { - synchronized (this.onlineHabbos) { - for (Map.Entry map : this.onlineHabbos.entrySet()) { - if (map.getValue().getHabboInfo().getUsername().equalsIgnoreCase(username)) - return map.getValue(); - } - } - - return null; + return this.onlineHabbosByName.get(username.toLowerCase()); } public Habbo loadHabbo(String sso) { @@ -178,11 +175,9 @@ public class HabboManager { } public void sendPacketToHabbosWithPermission(ServerMessage message, String perm) { - synchronized (this.onlineHabbos) { - for (Habbo habbo : this.onlineHabbos.values()) { - if (habbo.hasPermission(perm)) { - habbo.getClient().sendResponse(message); - } + for (Habbo habbo : this.onlineHabbos.values()) { + if (habbo.hasPermission(perm)) { + habbo.getClient().sendResponse(message); } } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/inventory/ItemsComponent.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/inventory/ItemsComponent.java index 7a49a166..9589a892 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/inventory/ItemsComponent.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/inventory/ItemsComponent.java @@ -159,14 +159,63 @@ public class ItemsComponent { } if (!this.items.isEmpty()) { - for (int i = this.items.size(); i-- > 0; ) { - try { - items.advance(); - } catch (NoSuchElementException e) { - break; + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) { + try (PreparedStatement updateStmt = connection.prepareStatement( + "UPDATE items SET user_id = ?, room_id = ?, wall_pos = ?, x = ?, y = ?, z = ?, rot = ?, extra_data = ?, limited_data = ? WHERE id = ?")) { + try (PreparedStatement deleteStmt = connection.prepareStatement( + "DELETE FROM items WHERE id = ?")) { + + int updateCount = 0; + int deleteCount = 0; + + for (int i = this.items.size(); i-- > 0; ) { + try { + items.advance(); + } catch (NoSuchElementException e) { + break; + } + + HabboItem item = items.value(); + if (item.needsDelete()) { + deleteStmt.setInt(1, item.getId()); + deleteStmt.addBatch(); + deleteCount++; + item.needsUpdate(false); + item.needsDelete(false); + } else if (item.needsUpdate()) { + updateStmt.setInt(1, item.getUserId()); + updateStmt.setInt(2, item.getRoomId()); + updateStmt.setString(3, item.getWallPosition()); + updateStmt.setInt(4, item.getX()); + updateStmt.setInt(5, item.getY()); + updateStmt.setDouble(6, item.getZ()); + updateStmt.setInt(7, item.getRotation()); + updateStmt.setString(8, item.getExtradata()); + updateStmt.setString(9, item.getLimitedStack() + ":" + item.getLimitedSells()); + updateStmt.setInt(10, item.getId()); + updateStmt.addBatch(); + updateCount++; + item.needsUpdate(false); + } + + if (updateCount > 0 && updateCount % 100 == 0) { + updateStmt.executeBatch(); + } + if (deleteCount > 0 && deleteCount % 100 == 0) { + deleteStmt.executeBatch(); + } + } + + if (deleteCount % 100 != 0) { + deleteStmt.executeBatch(); + } + if (updateCount % 100 != 0) { + updateStmt.executeBatch(); + } + } } - if (items.value().needsUpdate()) - Emulator.getThreading().run(items.value()); + } catch (SQLException e) { + LOGGER.error("Caught SQL exception during batch item save", e); } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/friends/AcceptFriendRequestEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/friends/AcceptFriendRequestEvent.java index 869fe532..6017b08d 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/friends/AcceptFriendRequestEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/friends/AcceptFriendRequestEvent.java @@ -3,7 +3,6 @@ package com.eu.habbo.messages.incoming.friends; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.messenger.Messenger; import com.eu.habbo.habbohotel.users.Habbo; -import com.eu.habbo.habbohotel.users.HabboInfo; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.friends.FriendRequestErrorComposer; import org.slf4j.Logger; @@ -14,7 +13,6 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import static com.eu.habbo.habbohotel.users.HabboManager.getOfflineHabboInfo; public class AcceptFriendRequestEvent extends MessageHandler { private static final Logger LOGGER = LoggerFactory.getLogger(AcceptFriendRequestEvent.class); @@ -44,18 +42,10 @@ public class AcceptFriendRequestEvent extends MessageHandler { Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId); if(target == null) { - HabboInfo habboInfo = getOfflineHabboInfo(userId); - - if(habboInfo == null) { - this.client.sendResponse(new FriendRequestErrorComposer(FriendRequestErrorComposer.TARGET_NOT_FOUND)); - this.client.getHabbo().getMessenger().deleteFriendRequests(userId, this.client.getHabbo().getHabboInfo().getId()); - continue; - } - - try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT users.*, users_settings.block_friendrequests FROM users INNER JOIN users_settings ON users.id = users_settings.user_id WHERE username = ? LIMIT 1")) { - statement.setString(1, habboInfo.getUsername()); + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT users.*, users_settings.block_friendrequests FROM users INNER JOIN users_settings ON users.id = users_settings.user_id WHERE users.id = ? LIMIT 1")) { + statement.setInt(1, userId); try (ResultSet set = statement.executeQuery()) { - while (set.next()) { + if (set.next()) { target = new Habbo(set); } } From f2d8f109ff77ce521e92466a027f1f6b3eb71255 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 26 Mar 2026 09:59:02 +0100 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=86=99=20Optimization=20for=20the=20g?= =?UTF-8?q?ameserver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Room Cleanup Optimization (RoomManager.java) Added roomsByOwner ConcurrentHashMap that tracks which rooms belong to which owner clearInactiveRooms() now iterates unique owners instead of ALL rooms Went from O(rooms × clients) to O(unique_owners × clients) every 120s - Volatile Fields (Room.java) Removed volatile from 27 room config fields (score, category, chatMode, allowPets, etc.) Kept volatile only on 8 fields that genuinely need cross-thread visibility (loaded, preLoaded, needsUpdate, muted, etc.) Reduces CPU cache line invalidation on every room cycle tick - Search Cache TTL (SearchUserEvent.java + CleanerThread.java) SearchUserEvent now has 30-second TTL per entry instead of full wipe every 10s SearchRoomsEvent already had LRU eviction (max 200) — removed redundant .clear() call Frequently searched users stay cached, only stale entries get cleaned - scheduledComposers/scheduledTasks — After reading the code, these are actually already handled correctly: processScheduledTasks() swaps the set with a fresh one before processing, and processScheduledComposers() calls .clear() after sending. No leak risk. --- .../java/com/eu/habbo/core/CleanerThread.java | 4 +- .../com/eu/habbo/habbohotel/rooms/Room.java | 170 +++++++++--------- .../habbo/habbohotel/rooms/RoomManager.java | 48 ++++- .../incoming/friends/SearchUserEvent.java | 16 +- 4 files changed, 143 insertions(+), 95 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/core/CleanerThread.java b/Emulator/src/main/java/com/eu/habbo/core/CleanerThread.java index e9e2f122..ed49246c 100644 --- a/Emulator/src/main/java/com/eu/habbo/core/CleanerThread.java +++ b/Emulator/src/main/java/com/eu/habbo/core/CleanerThread.java @@ -4,7 +4,6 @@ import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.guilds.forums.ForumThread; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.messages.incoming.friends.SearchUserEvent; -import com.eu.habbo.messages.incoming.navigator.SearchRoomsEvent; import com.eu.habbo.messages.outgoing.users.UserDataComposer; import com.eu.habbo.threading.runnables.AchievementUpdater; import org.slf4j.Logger; @@ -101,8 +100,7 @@ public class CleanerThread implements Runnable { LAST_HABBO_CACHE_CLEARED = time; } - SearchRoomsEvent.cachedResults.clear(); - SearchUserEvent.cachedResults.clear(); + SearchUserEvent.cleanExpiredCache(); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java index f8f61b86..2b2c24ee 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java @@ -130,8 +130,8 @@ public class Room implements Comparable, ISerialize, Runnable { private String password; private RoomState state; private int usersMax; - private volatile int score; - private volatile int category; + private int score; + private int category; private String floorPaint; private String wallPaint; private String backgroundPaint; @@ -140,41 +140,41 @@ public class Room implements Comparable, ISerialize, Runnable { private int floorSize; private int guild; private String tags; - private volatile boolean publicRoom; - private volatile boolean staffPromotedRoom; - private volatile boolean allowPets; - private volatile boolean allowPetsEat; - private volatile boolean allowWalkthrough; - private volatile boolean allowBotsWalk; - private volatile boolean allowEffects; - private volatile boolean hideWall; - private volatile int chatMode; - private volatile int chatWeight; - private volatile int chatSpeed; - private volatile int chatDistance; - private volatile int chatProtection; - private volatile int muteOption; - private volatile int kickOption; - private volatile int banOption; - private volatile int pollId; - private volatile boolean promoted; - private volatile int tradeMode; - private volatile boolean moveDiagonally; - private volatile boolean allowUnderpass; - private volatile boolean jukeboxActive; - private volatile boolean hideWired; + private boolean publicRoom; + private boolean staffPromotedRoom; + private boolean allowPets; + private boolean allowPetsEat; + private boolean allowWalkthrough; + private boolean allowBotsWalk; + private boolean allowEffects; + private boolean hideWall; + private int chatMode; + private int chatWeight; + private int chatSpeed; + private int chatDistance; + private int chatProtection; + private int muteOption; + private int kickOption; + private int banOption; + private int pollId; + private boolean promoted; + private int tradeMode; + private boolean moveDiagonally; + private boolean allowUnderpass; + private boolean jukeboxActive; + private boolean hideWired; private RoomPromotion promotion; private volatile boolean needsUpdate; private volatile boolean loaded; private volatile boolean preLoaded; private volatile boolean loadingInProgress; private volatile CompletableFuture loadingFuture; - private volatile int rollerSpeed; - private volatile int lastTimerReset = Emulator.getIntUnixTimestamp(); + private int rollerSpeed; + private int lastTimerReset = Emulator.getIntUnixTimestamp(); private volatile boolean muted; private RoomSpecialTypes roomSpecialTypes; private TraxManager traxManager; - + public final THashMap cache; public Room(ResultSet set) throws SQLException { @@ -222,8 +222,8 @@ public class Room implements Comparable, ISerialize, Runnable { this.bannedHabbos = new TIntObjectHashMap<>(); try (Connection connection = Emulator.getDatabase().getDataSource() - .getConnection(); PreparedStatement statement = connection.prepareStatement( - "SELECT * FROM room_promotions WHERE room_id = ? AND end_timestamp > ? LIMIT 1")) { + .getConnection(); PreparedStatement statement = connection.prepareStatement( + "SELECT * FROM room_promotions WHERE room_id = ? AND end_timestamp > ? LIMIT 1")) { if (this.promoted) { statement.setInt(1, this.id); statement.setInt(2, Emulator.getIntUnixTimestamp()); @@ -398,7 +398,7 @@ public class Room implements Comparable, ISerialize, Runnable { if (this.loaded || this.loadingInProgress || !this.preLoaded) { return; } - + this.loadingInProgress = true; this.loadingFuture = CompletableFuture.runAsync(() -> { this.loadDataInternal(); @@ -418,7 +418,7 @@ public class Room implements Comparable, ISerialize, Runnable { } future = this.loadingFuture; } - + if (future != null) { try { future.join(); @@ -433,7 +433,7 @@ public class Room implements Comparable, ISerialize, Runnable { public void loadData() { CompletableFuture futureToWait = null; boolean shouldLoad = false; - + synchronized (this.loadLock) { if (this.loadingInProgress) { // Get the future to wait on outside the lock @@ -443,7 +443,7 @@ public class Room implements Comparable, ISerialize, Runnable { shouldLoad = true; } } - + // Wait for existing load outside the lock if (futureToWait != null) { try { @@ -453,7 +453,7 @@ public class Room implements Comparable, ISerialize, Runnable { } return; } - + // Load if needed if (shouldLoad) { this.loadDataInternal(); @@ -567,7 +567,7 @@ public class Room implements Comparable, ISerialize, Runnable { } this.roomCycleTask = Emulator.getThreading().getService() - .scheduleAtFixedRate(this, 500, 500, TimeUnit.MILLISECONDS); + .scheduleAtFixedRate(this, 500, 500, TimeUnit.MILLISECONDS); } catch (Exception e) { LOGGER.error("Caught exception during room load", e); } @@ -586,7 +586,7 @@ public class Room implements Comparable, ISerialize, Runnable { item.setExtradata("1"); this.updateItem(item); } - + // Set loaded flag with lock synchronized (this.loadLock) { this.loaded = true; @@ -603,7 +603,7 @@ public class Room implements Comparable, ISerialize, Runnable { this.layout = Emulator.getGameEnvironment().getRoomManager().loadCustomLayout(this); } else { this.layout = Emulator.getGameEnvironment().getRoomManager() - .loadLayout(this.layoutName, this); + .loadLayout(this.layoutName, this); } } } @@ -635,7 +635,7 @@ public class Room implements Comparable, ISerialize, Runnable { this.unitManager.clearBots(); try (PreparedStatement statement = connection.prepareStatement( - "SELECT users.username AS owner_name, bots.* FROM bots INNER JOIN users ON bots.user_id = users.id WHERE room_id = ?")) { + "SELECT users.username AS owner_name, bots.* FROM bots INNER JOIN users ON bots.user_id = users.id WHERE room_id = ?")) { statement.setInt(1, this.id); try (ResultSet set = statement.executeQuery()) { while (set.next()) { @@ -646,11 +646,11 @@ public class Room implements Comparable, ISerialize, Runnable { b.setRoomUnit(new RoomUnit()); b.getRoomUnit().setPathFinderRoom(this); b.getRoomUnit() - .setLocation(this.layout.getTile((short) set.getInt("x"), (short) set.getInt("y"))); + .setLocation(this.layout.getTile((short) set.getInt("x"), (short) set.getInt("y"))); if (b.getRoomUnit().getCurrentLocation() == null) { b.getRoomUnit().setLocation(this.getLayout().getDoorTile()); b.getRoomUnit() - .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); + .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); } else { b.getRoomUnit().setZ(set.getDouble("z")); b.getRoomUnit().setPreviousLocationZ(set.getDouble("z")); @@ -674,7 +674,7 @@ public class Room implements Comparable, ISerialize, Runnable { this.unitManager.clearPets(); try (PreparedStatement statement = connection.prepareStatement( - "SELECT users.username as pet_owner_name, users_pets.* FROM users_pets INNER JOIN users ON users_pets.user_id = users.id WHERE room_id = ?")) { + "SELECT users.username as pet_owner_name, users_pets.* FROM users_pets INNER JOIN users ON users_pets.user_id = users.id WHERE room_id = ?")) { statement.setInt(1, this.id); try (ResultSet set = statement.executeQuery()) { while (set.next()) { @@ -684,11 +684,11 @@ public class Room implements Comparable, ISerialize, Runnable { pet.setRoomUnit(new RoomUnit()); pet.getRoomUnit().setPathFinderRoom(this); pet.getRoomUnit() - .setLocation(this.layout.getTile((short) set.getInt("x"), (short) set.getInt("y"))); + .setLocation(this.layout.getTile((short) set.getInt("x"), (short) set.getInt("y"))); if (pet.getRoomUnit().getCurrentLocation() == null) { pet.getRoomUnit().setLocation(this.getLayout().getDoorTile()); pet.getRoomUnit() - .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); + .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); } else { pet.getRoomUnit().setZ(set.getDouble("z")); pet.getRoomUnit().setRotation(RoomUserRotation.values()[set.getInt("rot")]); @@ -760,7 +760,7 @@ public class Room implements Comparable, ISerialize, Runnable { THashSet updatedTiles = new THashSet<>(); Rectangle rectangle = RoomLayout.getRectangle(item.getX(), item.getY(), - item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); + item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); for (short x = (short) rectangle.x; x < rectangle.x + rectangle.getWidth(); x++) { for (short y = (short) rectangle.y; y < rectangle.y + rectangle.getHeight(); y++) { @@ -784,7 +784,7 @@ public class Room implements Comparable, ISerialize, Runnable { } Habbo habbo = (picker != null && picker.getHabboInfo().getId() == item.getId() ? picker - : Emulator.getGameServer().getGameClientManager().getHabbo(item.getUserId())); + : Emulator.getGameServer().getGameClientManager().getHabbo(item.getUserId())); if (habbo != null) { habbo.getInventory().getItemsComponent().addItem(item); habbo.getClient().sendResponse(new AddHabboItemComposer(item)); @@ -1022,7 +1022,7 @@ public class Room implements Comparable, ISerialize, Runnable { message.appendInt(this.category); String[] tags = Arrays.stream(this.tags.split(";")).filter(t -> !t.isEmpty()) - .toArray(String[]::new); + .toArray(String[]::new); message.appendInt(tags.length); for (String s : tags) { message.appendString(s); @@ -1086,8 +1086,8 @@ public class Room implements Comparable, ISerialize, Runnable { public void save() { if (this.needsUpdate) { try (Connection connection = Emulator.getDatabase().getDataSource() - .getConnection(); PreparedStatement statement = connection.prepareStatement( - "UPDATE rooms SET name = ?, description = ?, password = ?, state = ?, users_max = ?, category = ?, score = ?, paper_floor = ?, paper_wall = ?, paper_landscape = ?, thickness_wall = ?, wall_height = ?, thickness_floor = ?, moodlight_data = ?, tags = ?, allow_other_pets = ?, allow_other_pets_eat = ?, allow_walkthrough = ?, allow_hidewall = ?, chat_mode = ?, chat_weight = ?, chat_speed = ?, chat_hearing_distance = ?, chat_protection =?, who_can_mute = ?, who_can_kick = ?, who_can_ban = ?, poll_id = ?, guild_id = ?, roller_speed = ?, override_model = ?, is_staff_picked = ?, promoted = ?, trade_mode = ?, move_diagonally = ?, owner_id = ?, owner_name = ?, jukebox_active = ?, hidewired = ?, allow_underpass = ? WHERE id = ?")) { + .getConnection(); PreparedStatement statement = connection.prepareStatement( + "UPDATE rooms SET name = ?, description = ?, password = ?, state = ?, users_max = ?, category = ?, score = ?, paper_floor = ?, paper_wall = ?, paper_landscape = ?, thickness_wall = ?, wall_height = ?, thickness_floor = ?, moodlight_data = ?, tags = ?, allow_other_pets = ?, allow_other_pets_eat = ?, allow_walkthrough = ?, allow_hidewall = ?, chat_mode = ?, chat_weight = ?, chat_speed = ?, chat_hearing_distance = ?, chat_protection =?, who_can_mute = ?, who_can_kick = ?, who_can_ban = ?, poll_id = ?, guild_id = ?, roller_speed = ?, override_model = ?, is_staff_picked = ?, promoted = ?, trade_mode = ?, move_diagonally = ?, owner_id = ?, owner_name = ?, jukebox_active = ?, hidewired = ?, allow_underpass = ? WHERE id = ?")) { statement.setString(1, this.name); statement.setString(2, this.description); statement.setString(3, this.password); @@ -1152,8 +1152,8 @@ public class Room implements Comparable, ISerialize, Runnable { */ public void updateDatabaseUserCount() { try (Connection connection = Emulator.getDatabase().getDataSource() - .getConnection(); PreparedStatement statement = connection.prepareStatement( - "UPDATE rooms SET users = ? WHERE id = ? LIMIT 1")) { + .getConnection(); PreparedStatement statement = connection.prepareStatement( + "UPDATE rooms SET users = ? WHERE id = ? LIMIT 1")) { statement.setInt(1, this.getUserCount()); statement.setInt(2, this.id); statement.executeUpdate(); @@ -1467,7 +1467,7 @@ public class Room implements Comparable, ISerialize, Runnable { if (extraData.length == 4) { if (extraData[0].equalsIgnoreCase("1")) { return Color.getHSBColor(Integer.parseInt(extraData[1]), - Integer.parseInt(extraData[2]), Integer.parseInt(extraData[3])); + Integer.parseInt(extraData[2]), Integer.parseInt(extraData[3])); } } } @@ -1574,7 +1574,7 @@ public class Room implements Comparable, ISerialize, Runnable { public String[] filterAnything() { return new String[]{this.getOwnerName(), this.getGuildName(), this.getDescription(), - this.getPromotionDesc()}; + this.getPromotionDesc()}; } public long getCycleTimestamp() { @@ -1906,7 +1906,7 @@ public class Room implements Comparable, ISerialize, Runnable { } public void talk(final Habbo habbo, final RoomChatMessage roomChatMessage, RoomChatType chatType, - boolean ignoreWired) { + boolean ignoreWired) { this.chatManager.talk(habbo, roomChatMessage, chatType, ignoreWired); } @@ -2051,7 +2051,7 @@ public class Room implements Comparable, ISerialize, Runnable { private void loadRights(Connection connection) { this.rights.clear(); try (PreparedStatement statement = connection.prepareStatement( - "SELECT user_id FROM room_rights WHERE room_id = ?")) { + "SELECT user_id FROM room_rights WHERE room_id = ?")) { statement.setInt(1, this.id); try (ResultSet set = statement.executeQuery()) { while (set.next()) { @@ -2067,7 +2067,7 @@ public class Room implements Comparable, ISerialize, Runnable { this.bannedHabbos.clear(); try (PreparedStatement statement = connection.prepareStatement( - "SELECT users.username, users.id, room_bans.* FROM room_bans INNER JOIN users ON room_bans.user_id = users.id WHERE ends > ? AND room_bans.room_id = ?")) { + "SELECT users.username, users.id, room_bans.* FROM room_bans INNER JOIN users ON room_bans.user_id = users.id WHERE ends > ? AND room_bans.room_id = ?")) { statement.setInt(1, Emulator.getIntUnixTimestamp()); statement.setInt(2, this.id); try (ResultSet set = statement.executeQuery()) { @@ -2089,7 +2089,7 @@ public class Room implements Comparable, ISerialize, Runnable { Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(this.guild); if (Emulator.getGameEnvironment().getGuildManager().getOnlyAdmins(guild) - .get(habbo.getHabboInfo().getId()) != null) { + .get(habbo.getHabboInfo().getId()) != null) { return RoomRightLevels.GUILD_ADMIN; } @@ -2167,15 +2167,15 @@ public class Room implements Comparable, ISerialize, Runnable { } if (habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) || !habbo.getRoomUnit() - .canForcePosture()) { + .canForcePosture()) { return; } this.dance(habbo, DanceType.NONE); habbo.getRoomUnit().cmdSit = true; habbo.getRoomUnit().setBodyRotation( - RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() - - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); + RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() + - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT, 0.5 + ""); this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose()); WiredManager.triggerUserPerformsAction(this, habbo.getRoomUnit(), WiredUserActionType.SIT, -1); @@ -2189,11 +2189,11 @@ public class Room implements Comparable, ISerialize, Runnable { HabboItem item = this.getTopItemAt(habbo.getRoomUnit().getX(), habbo.getRoomUnit().getY()); if (item == null || !item.getBaseItem().allowSit() || !item.getBaseItem().allowLay()) { boolean wasSittingOrLaying = habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) - || habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); + || habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); habbo.getRoomUnit().cmdStand = true; habbo.getRoomUnit().setBodyRotation( - RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() - - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); + RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() + - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); habbo.getRoomUnit().removeStatus(RoomUnitStatus.SIT); habbo.getRoomUnit().removeStatus(RoomUnitStatus.LAY); this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose()); @@ -2227,9 +2227,9 @@ public class Room implements Comparable, ISerialize, Runnable { if (item.getBaseItem().getType() == FurnitureType.FLOOR) { this.sendComposer(new FloorItemUpdateComposer(item).compose()); this.updateTiles(this.getLayout() - .getTilesAt(this.layout.getTile(item.getX(), item.getY()), - item.getBaseItem().getWidth(), item.getBaseItem().getLength(), - item.getRotation())); + .getTilesAt(this.layout.getTile(item.getX(), item.getY()), + item.getBaseItem().getWidth(), item.getBaseItem().getLength(), + item.getRotation())); } else if (item.getBaseItem().getType() == FurnitureType.WALL) { this.sendComposer(new WallItemUpdateComposer(item).compose()); } @@ -2251,8 +2251,8 @@ public class Room implements Comparable, ISerialize, Runnable { } this.updateTiles(this.getLayout() - .getTilesAt(this.layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), - item.getBaseItem().getLength(), item.getRotation())); + .getTilesAt(this.layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), item.getRotation())); if (item instanceof InteractionMultiHeight) { ((InteractionMultiHeight) item).updateUnitsOnItem(this); @@ -2289,18 +2289,18 @@ public class Room implements Comparable, ISerialize, Runnable { public void refreshGuild(Guild guild) { if (guild.getRoomId() == this.id) { THashSet members = Emulator.getGameEnvironment().getGuildManager() - .getGuildMembers(guild.getId()); + .getGuildMembers(guild.getId()); for (Habbo habbo : this.getHabbos()) { Optional member = members.stream() - .filter(m -> m.getUserId() == habbo.getHabboInfo().getId()).findAny(); + .filter(m -> m.getUserId() == habbo.getHabboInfo().getId()).findAny(); if (!member.isPresent()) { continue; } habbo.getClient() - .sendResponse(new GuildInfoComposer(guild, habbo.getClient(), false, member.get())); + .sendResponse(new GuildInfoComposer(guild, habbo.getClient(), false, member.get())); } } @@ -2335,7 +2335,7 @@ public class Room implements Comparable, ISerialize, Runnable { if (habbo.getHabboInfo().getCurrentRoom() == this) { if (habbo.getHabboInfo().getId() != this.ownerId) { if (!(habbo.hasPermission(Permission.ACC_ANYROOMOWNER) || habbo.hasPermission( - Permission.ACC_MOVEROTATE))) { + Permission.ACC_MOVEROTATE))) { this.refreshRightsForHabbo(habbo); } } @@ -2421,18 +2421,18 @@ public class Room implements Comparable, ISerialize, Runnable { } } else { this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), - this.roomSpecialTypes.getTriggers()).compose()); + this.roomSpecialTypes.getTriggers()).compose()); this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), - this.roomSpecialTypes.getEffects()).compose()); + this.roomSpecialTypes.getEffects()).compose()); this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), - this.roomSpecialTypes.getConditions()).compose()); + this.roomSpecialTypes.getConditions()).compose()); this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), - this.roomSpecialTypes.getExtras()).compose()); + this.roomSpecialTypes.getExtras()).compose()); } } public FurnitureMovementError canPlaceFurnitureAt(HabboItem item, Habbo habbo, RoomTile tile, - int rotation) { + int rotation) { return this.itemManager.canPlaceFurnitureAt(item, habbo, tile, rotation); } @@ -2441,17 +2441,17 @@ public class Room implements Comparable, ISerialize, Runnable { } public FurnitureMovementError furnitureFitsAt(RoomTile tile, HabboItem item, int rotation, - boolean checkForUnits) { + boolean checkForUnits) { return this.itemManager.furnitureFitsAt(tile, item, rotation, checkForUnits); } public FurnitureMovementError furnitureFitsAtWithPhysics(RoomTile tile, HabboItem item, int rotation, - boolean checkForUnits, WiredMovementPhysics physics) { + boolean checkForUnits, WiredMovementPhysics physics) { return this.itemManager.furnitureFitsAtWithPhysics(tile, item, rotation, checkForUnits, physics); } public FurnitureMovementError placeFloorFurniAt(HabboItem item, RoomTile tile, int rotation, - Habbo owner) { + Habbo owner) { return this.itemManager.placeFloorFurniAt(item, tile, rotation, owner); } @@ -2460,17 +2460,17 @@ public class Room implements Comparable, ISerialize, Runnable { } public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, - Habbo actor) { + Habbo actor) { return this.itemManager.moveFurniTo(item, tile, rotation, actor); } public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, - Habbo actor, boolean sendUpdates) { + Habbo actor, boolean sendUpdates) { return this.itemManager.moveFurniTo(item, tile, rotation, actor, sendUpdates); } public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, - Habbo actor, boolean sendUpdates, boolean checkForUnits) { + Habbo actor, boolean sendUpdates, boolean checkForUnits) { return this.itemManager.moveFurniTo(item, tile, rotation, actor, sendUpdates, checkForUnits); } @@ -2487,12 +2487,12 @@ public class Room implements Comparable, ISerialize, Runnable { } public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, - Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, actor, sendUpdates, checkForUnits, physics); } public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, double z, - Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, z, actor, sendUpdates, checkForUnits, physics); } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java index b44f0723..c51000ca 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java @@ -72,6 +72,7 @@ public class RoomManager { private final THashMap roomCategories; private final List mapNames; private final ConcurrentHashMap activeRooms; + private final ConcurrentHashMap> roomsByOwner; private final ArrayList> gameTypes; public RoomManager() { @@ -79,6 +80,7 @@ public class RoomManager { this.roomCategories = new THashMap<>(); this.mapNames = new ArrayList<>(); this.activeRooms = new ConcurrentHashMap<>(); + this.roomsByOwner = new ConcurrentHashMap<>(); this.loadRoomCategories(); this.loadRoomModels(); @@ -95,6 +97,20 @@ public class RoomManager { LOGGER.info("Room Manager -> Loaded! ({} MS)", System.currentTimeMillis() - millis); } + private void trackRoomOwner(Room room) { + this.roomsByOwner.computeIfAbsent(room.getOwnerId(), k -> ConcurrentHashMap.newKeySet()).add(room.getId()); + } + + private void untrackRoomOwner(Room room) { + Set rooms = this.roomsByOwner.get(room.getOwnerId()); + if (rooms != null) { + rooms.remove(room.getId()); + if (rooms.isEmpty()) { + this.roomsByOwner.remove(room.getOwnerId()); + } + } + } + public void loadRoomModels() { this.mapNames.clear(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM room_models")) { @@ -143,6 +159,7 @@ public class RoomManager { Room room = new Room(set); room.preventUncaching = true; this.activeRooms.put(set.getInt("id"), room); + this.trackRoomOwner(room); } } } catch (SQLException e) { @@ -162,6 +179,7 @@ public class RoomManager { if (room == null) { room = new Room(set); this.activeRooms.put(set.getInt("id"), room); + this.trackRoomOwner(room); } if (!rooms.containsKey(set.getInt("category"))) { @@ -321,6 +339,7 @@ public class RoomManager { if (room != null) { this.activeRooms.put(room.getId(), room); + this.trackRoomOwner(room); } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); @@ -368,8 +387,11 @@ public class RoomManager { statement.setInt(1, habbo.getHabboInfo().getId()); try (ResultSet set = statement.executeQuery()) { while (set.next()) { - if (!this.activeRooms.containsKey(set.getInt("id"))) - this.activeRooms.put(set.getInt("id"), new Room(set)); + if (!this.activeRooms.containsKey(set.getInt("id"))) { + Room room = new Room(set); + this.activeRooms.put(room.getId(), room); + this.trackRoomOwner(room); + } } } } catch (SQLException e) { @@ -390,22 +412,31 @@ public class RoomManager { continue; room.dispose(); + this.untrackRoomOwner(room); this.activeRooms.remove(room.getId()); } } public void clearInactiveRooms() { THashSet roomsToDispose = new THashSet<>(); - for (Room room : this.activeRooms.values()) { - if (!room.isPublicRoom() && !room.isStaffPromotedRoom() && !Emulator.getGameServer().getGameClientManager().containsHabbo(room.getOwnerId()) && room.isPreLoaded()) { - roomsToDispose.add(room); + for (Map.Entry> entry : this.roomsByOwner.entrySet()) { + int ownerId = entry.getKey(); + if (!Emulator.getGameServer().getGameClientManager().containsHabbo(ownerId)) { + for (int roomId : entry.getValue()) { + Room room = this.activeRooms.get(roomId); + if (room != null && !room.isPublicRoom() && !room.isStaffPromotedRoom() && room.isPreLoaded()) { + roomsToDispose.add(room); + } + } } } for (Room room : roomsToDispose) { room.dispose(); - if (room.getUserCount() == 0) + if (room.getUserCount() == 0) { + this.untrackRoomOwner(room); this.activeRooms.remove(room.getId()); + } } } @@ -434,6 +465,7 @@ public class RoomManager { } public void uncacheRoom(Room room) { + this.untrackRoomOwner(room); this.activeRooms.remove(room.getId()); } @@ -1125,6 +1157,7 @@ public class RoomManager { Room r = new Room(set); rooms.add(r); this.activeRooms.put(r.getId(), r); + this.trackRoomOwner(r); } } } catch (SQLException e) { @@ -1185,6 +1218,7 @@ public class RoomManager { rooms.add(r); this.activeRooms.put(r.getId(), r); + this.trackRoomOwner(r); } } } catch (SQLException e) { @@ -1248,6 +1282,7 @@ public class RoomManager { room = new Room(set); this.activeRooms.put(room.getId(), room); + this.trackRoomOwner(room); } rooms.add(room); @@ -1489,6 +1524,7 @@ public class RoomManager { room.dispose(); } + this.roomsByOwner.clear(); this.activeRooms.clear(); LOGGER.info("Room Manager -> Disposed!"); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/friends/SearchUserEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/friends/SearchUserEvent.java index d4b01757..9578fa69 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/friends/SearchUserEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/friends/SearchUserEvent.java @@ -9,7 +9,20 @@ import gnu.trove.set.hash.THashSet; import java.util.concurrent.ConcurrentHashMap; public class SearchUserEvent extends MessageHandler { - public static ConcurrentHashMap> cachedResults = new ConcurrentHashMap<>(); + private static final long CACHE_TTL_MS = 30_000; // 30 second TTL + private static final ConcurrentHashMap cacheTimestamps = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap> cachedResults = new ConcurrentHashMap<>(); + + public static void cleanExpiredCache() { + long now = System.currentTimeMillis(); + cacheTimestamps.entrySet().removeIf(entry -> { + if (now - entry.getValue() > CACHE_TTL_MS) { + cachedResults.remove(entry.getKey()); + return true; + } + return false; + }); + } @Override public void handle() throws Exception { @@ -31,6 +44,7 @@ public class SearchUserEvent extends MessageHandler { if (buddies == null) { buddies = Messenger.searchUsers(username); cachedResults.put(username, buddies); + cacheTimestamps.put(username, System.currentTimeMillis()); } this.client.sendResponse(new UserSearchResultComposer(buddies, this.client.getHabbo().getMessenger().getFriends(username), this.client.getHabbo())); From 31ebb96a9c8b0032530eb9925ba3d18fb6af4442 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 26 Mar 2026 10:15:40 +0100 Subject: [PATCH 6/7] =?UTF-8?q?=F0=9F=86=99=20Speeds=20up=20the=20badge=20?= =?UTF-8?q?loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java index c51000ca..b7564a29 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java @@ -40,6 +40,7 @@ import com.eu.habbo.messages.outgoing.rooms.pets.RoomPetComposer; import com.eu.habbo.messages.outgoing.rooms.promotions.RoomPromotionMessageComposer; import com.eu.habbo.messages.outgoing.rooms.users.*; import com.eu.habbo.messages.outgoing.users.MutedWhisperComposer; +import com.eu.habbo.messages.outgoing.users.UserBadgesComposer; import com.eu.habbo.plugin.events.navigator.NavigatorRoomCreatedEvent; import com.eu.habbo.plugin.events.rooms.RoomFloorItemsLoadEvent; import com.eu.habbo.plugin.events.rooms.RoomUncachedEvent; @@ -765,6 +766,9 @@ public class RoomManager { habbo.getRoomUnit().setInvisible(false); room.addHabbo(habbo); + // Pre-send own wearing badges so the client cache is populated before the user clicks themselves + habbo.getClient().sendResponse(new UserBadgesComposer(habbo.getInventory().getBadgesComponent().getWearingBadges(), habbo.getHabboInfo().getId())); + List habbos = new ArrayList<>(); if (!room.getCurrentHabbos().isEmpty()) { From b755828d85110539ce2b61c1dcc231949409e858 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 26 Mar 2026 10:43:26 +0100 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=86=99=20Small=20fix=20UserCreditsCom?= =?UTF-8?q?poser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit se this instead of this.client.getHabbo() since giveCredits() is already called on the Habbo instance itself. No need for the round-trip through the client. --- Emulator/src/main/java/com/eu/habbo/habbohotel/users/Habbo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/Habbo.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/Habbo.java index 0bc6a604..399fc1c0 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/Habbo.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/Habbo.java @@ -259,7 +259,7 @@ public class Habbo implements Runnable { this.getHabboInfo().addCredits(event.credits); - if (this.client != null) this.client.sendResponse(new UserCreditsComposer(this.client.getHabbo())); + if (this.client != null) this.client.sendResponse(new UserCreditsComposer(this)); }