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
-
-
- 1710516016645
-
-
-
-
\ No newline at end of file
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';
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
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 6348ca40..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;
@@ -72,6 +73,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 +81,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 +98,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 +160,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 +180,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"))) {
@@ -179,12 +198,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 +234,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() {
@@ -333,6 +340,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);
@@ -380,8 +388,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) {
@@ -402,22 +413,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());
+ }
}
}
@@ -446,6 +466,7 @@ public class RoomManager {
}
public void uncacheRoom(Room room) {
+ this.untrackRoomOwner(room);
this.activeRooms.remove(room.getId());
}
@@ -745,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()) {
@@ -1137,6 +1161,7 @@ public class RoomManager {
Room r = new Room(set);
rooms.add(r);
this.activeRooms.put(r.getId(), r);
+ this.trackRoomOwner(r);
}
}
} catch (SQLException e) {
@@ -1197,6 +1222,7 @@ public class RoomManager {
rooms.add(r);
this.activeRooms.put(r.getId(), r);
+ this.trackRoomOwner(r);
}
}
} catch (SQLException e) {
@@ -1260,6 +1286,7 @@ public class RoomManager {
room = new Room(set);
this.activeRooms.put(room.getId(), room);
+ this.trackRoomOwner(room);
}
rooms.add(room);
@@ -1501,6 +1528,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/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));
}
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);
}
}
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()));