You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
Merge branch 'feature/checkpoint-20260403'
This commit is contained in:
@@ -213,8 +213,8 @@ 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.enabled', '1'),
|
||||
('wired.engine.exclusive', '1'),
|
||||
('wired.engine.maxStepsPerStack', '100'),
|
||||
('wired.engine.debug', '0')
|
||||
ON DUPLICATE KEY UPDATE `key` = `key`;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `guild_forum_views` ADD UNIQUE KEY `user_guild` (`user_id`, `guild_id`);
|
||||
@@ -0,0 +1,98 @@
|
||||
CREATE TABLE IF NOT EXISTS `wired_emulator_settings` (
|
||||
`key` varchar(191) NOT NULL,
|
||||
`value` text NOT NULL,
|
||||
`comment` text NOT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
|
||||
|
||||
INSERT INTO `wired_emulator_settings` (`key`, `value`, `comment`)
|
||||
SELECT 'wired.engine.enabled', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.engine.enabled' LIMIT 1), '1'), 'Compatibility flag kept for older configs. The runtime now always uses the new wired engine.'
|
||||
UNION ALL
|
||||
SELECT 'wired.engine.exclusive', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.engine.exclusive' LIMIT 1), '1'), 'Compatibility flag kept for older configs. The runtime now always uses the new wired engine.'
|
||||
UNION ALL
|
||||
SELECT 'wired.engine.maxStepsPerStack', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.engine.maxStepsPerStack' LIMIT 1), '100'), 'Maximum amount of internal processing steps allowed for a single wired stack execution.'
|
||||
UNION ALL
|
||||
SELECT 'wired.engine.debug', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.engine.debug' LIMIT 1), '0'), 'Enable verbose debug logging for the new wired engine.'
|
||||
UNION ALL
|
||||
SELECT 'wired.custom.enabled', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.custom.enabled' LIMIT 1), '0'), 'Enable custom legacy wired behaviour such as user-based cooldown exceptions and compatibility logic.'
|
||||
UNION ALL
|
||||
SELECT 'hotel.wired.furni.selection.count', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'hotel.wired.furni.selection.count' LIMIT 1), '5'), 'Maximum number of furni that a wired box can store or select.'
|
||||
UNION ALL
|
||||
SELECT 'hotel.wired.max_delay', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'hotel.wired.max_delay' LIMIT 1), '20'), 'Maximum delay value accepted by wired effects that support delayed execution.'
|
||||
UNION ALL
|
||||
SELECT 'hotel.wired.message.max_length', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'hotel.wired.message.max_length' LIMIT 1), '100'), 'Maximum length of text fields used by wired messages and bot text effects.'
|
||||
UNION ALL
|
||||
SELECT 'wired.effect.teleport.delay', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.effect.teleport.delay' LIMIT 1), '500'), 'Delay in milliseconds used by wired teleport movement.'
|
||||
UNION ALL
|
||||
SELECT 'wired.place.under', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.place.under' LIMIT 1), '0'), 'Allow placing wired furniture underneath other items when room rules permit it.'
|
||||
UNION ALL
|
||||
SELECT 'wired.tick.interval.ms', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.tick.interval.ms' LIMIT 1), '50'), 'Global wired tick interval in milliseconds used by repeaters and other tick-driven wired items.'
|
||||
UNION ALL
|
||||
SELECT 'wired.tick.resolution', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.tick.resolution' LIMIT 1), '100'), 'Legacy wired tick resolution value kept for compatibility with older wired timing setups.'
|
||||
UNION ALL
|
||||
SELECT 'wired.tick.debug', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.tick.debug' LIMIT 1), '0'), 'Enable verbose logging for the wired tick service.'
|
||||
UNION ALL
|
||||
SELECT 'wired.tick.thread.priority', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.tick.thread.priority' LIMIT 1), '6'), 'Java thread priority used by the wired tick service.'
|
||||
UNION ALL
|
||||
SELECT 'wired.highscores.displaycount', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.highscores.displaycount' LIMIT 1), '25'), 'Maximum number of wired highscore entries shown to users when a highscore is displayed.'
|
||||
UNION ALL
|
||||
SELECT 'wired.abuse.max.recursion.depth', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.abuse.max.recursion.depth' LIMIT 1), '10'), 'Maximum recursive wired depth allowed before execution is stopped.'
|
||||
UNION ALL
|
||||
SELECT 'wired.abuse.max.events.per.window', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.abuse.max.events.per.window' LIMIT 1), '100'), 'Maximum amount of identical wired events allowed inside the abuse rate-limit window before a room ban is applied.'
|
||||
UNION ALL
|
||||
SELECT 'wired.abuse.rate.limit.window.ms', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.abuse.rate.limit.window.ms' LIMIT 1), '10000'), 'Time window in milliseconds used by the wired abuse rate limiter.'
|
||||
UNION ALL
|
||||
SELECT 'wired.abuse.ban.duration.ms', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.abuse.ban.duration.ms' LIMIT 1), '600000'), 'Duration in milliseconds of the temporary wired ban after abuse detection.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.usage.window.ms', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.usage.window.ms' LIMIT 1), '1000'), 'Rolling window size in milliseconds used to calculate wired usage in the :wired monitor.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.usage.limit', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.usage.limit' LIMIT 1), '1000'), 'Maximum wired usage budget allowed in one monitor window before EXECUTION_CAP is raised.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.delayed.events.limit', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.delayed.events.limit' LIMIT 1), '100'), 'Maximum number of delayed wired events that can be queued in one room at the same time.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.overload.average.ms', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.overload.average.ms' LIMIT 1), '50'), 'Average execution time threshold in milliseconds that starts overload tracking.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.overload.peak.ms', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.overload.peak.ms' LIMIT 1), '150'), 'Peak single execution time threshold in milliseconds that starts overload tracking.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.overload.consecutive.windows', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.overload.consecutive.windows' LIMIT 1), '2'), 'Number of consecutive overloaded monitor windows required before logging EXECUTOR_OVERLOAD.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.heavy.usage.percent', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.heavy.usage.percent' LIMIT 1), '70'), 'Usage percentage threshold that contributes to marking a room as heavy in the :wired monitor.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.heavy.consecutive.windows', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.heavy.consecutive.windows' LIMIT 1), '5'), 'Number of consecutive windows above the heavy usage threshold required before the room is marked as heavy.'
|
||||
UNION ALL
|
||||
SELECT 'wired.monitor.heavy.delayed.percent', COALESCE((SELECT `value` FROM `emulator_settings` WHERE `key` = 'wired.monitor.heavy.delayed.percent' LIMIT 1), '60'), 'Delayed queue percentage threshold that also contributes to the heavy-room calculation.'
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`value` = VALUES(`value`),
|
||||
`comment` = VALUES(`comment`);
|
||||
|
||||
DELETE FROM `emulator_settings`
|
||||
WHERE `key` IN (
|
||||
'wired.engine.enabled',
|
||||
'wired.engine.exclusive',
|
||||
'wired.engine.maxStepsPerStack',
|
||||
'wired.engine.debug',
|
||||
'wired.custom.enabled',
|
||||
'hotel.wired.furni.selection.count',
|
||||
'hotel.wired.max_delay',
|
||||
'hotel.wired.message.max_length',
|
||||
'wired.effect.teleport.delay',
|
||||
'wired.place.under',
|
||||
'wired.tick.interval.ms',
|
||||
'wired.tick.resolution',
|
||||
'wired.tick.debug',
|
||||
'wired.tick.thread.priority',
|
||||
'wired.highscores.displaycount',
|
||||
'wired.abuse.max.recursion.depth',
|
||||
'wired.abuse.max.events.per.window',
|
||||
'wired.abuse.rate.limit.window.ms',
|
||||
'wired.abuse.ban.duration.ms',
|
||||
'wired.monitor.usage.window.ms',
|
||||
'wired.monitor.usage.limit',
|
||||
'wired.monitor.delayed.events.limit',
|
||||
'wired.monitor.overload.average.ms',
|
||||
'wired.monitor.overload.peak.ms',
|
||||
'wired.monitor.overload.consecutive.windows',
|
||||
'wired.monitor.heavy.usage.percent',
|
||||
'wired.monitor.heavy.consecutive.windows',
|
||||
'wired.monitor.heavy.delayed.percent'
|
||||
);
|
||||
@@ -0,0 +1,332 @@
|
||||
ALTER TABLE `emulator_settings`
|
||||
ADD COLUMN IF NOT EXISTS `comment` text NOT NULL AFTER `value`;
|
||||
|
||||
UPDATE `emulator_settings` SET `comment` = 'Characters allowed when users choose or change a username.' WHERE `key` = 'allowed.username.characters';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Cooldown in milliseconds used by the Apollyon-specific behaviour or command flow.' WHERE `key` = 'apollyon.cooldown.amount';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Asset URL used by the BaseJump or FastFood game client.' WHERE `key` = 'basejump.assets.url';
|
||||
UPDATE `emulator_settings` SET `comment` = 'SWF URL used to launch the BaseJump or FastFood game client.' WHERE `key` = 'basejump.url';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Date format used by visitor bots when they print timestamps.' WHERE `key` = 'bots.visitor.dateformat';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Master switch for bubble alert notifications.' WHERE `key` = 'bubblealerts.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable bubble alerts when friends come online.' WHERE `key` = 'bubblealerts.notif_friendonline.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Image template used when showing friend-online bubble alerts.' WHERE `key` = 'bubblealerts.notif_friendonline.image';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Use the configured figure image inside friend-online bubble alerts.' WHERE `key` = 'bubblealerts.notif_friendonline.useimage';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Show bubble alerts for marketplace notifications.' WHERE `key` = 'bubblealerts.notif_marketplace.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Show bubble alerts for limited-item purchases.' WHERE `key` = 'bubblealerts.notif_purchase.limited';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow bots to be included in room bundles or package rewards.' WHERE `key` = 'bundle.bots.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow pets to be included in room bundles or package rewards.' WHERE `key` = 'bundle.pets.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the GET callback used to report version to external services.' WHERE `key` = 'callback.get.version';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the POST callback used to report errors to external services.' WHERE `key` = 'callback.post.errors';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the POST callback used to report statistics to external services.' WHERE `key` = 'callback.post.statistics';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the in-room camera feature.' WHERE `key` = 'camera.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Extradata template written into camera photo items when they are created.' WHERE `key` = 'camera.extradata';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Base item ID used by the generated camera photo furniture.' WHERE `key` = 'camera.item_id';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Credit price charged when taking a camera photo.' WHERE `key` = 'camera.price.credits';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Amount of activity points charged when taking a camera photo.' WHERE `key` = 'camera.price.points';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Amount of activity points charged when publishing a camera photo.' WHERE `key` = 'camera.price.points.publish';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Activity point type used for the camera publish cost.' WHERE `key` = 'camera.price.points.publish.type';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Activity point type used for the camera capture cost.' WHERE `key` = 'camera.price.points.type';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Delay in seconds before a published camera photo becomes available.' WHERE `key` = 'camera.publish.delay';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Base URL where camera images are published.' WHERE `key` = 'camera.url';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Force HTTPS when generating camera image URLs.' WHERE `key` = 'camera.use.https';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Require HC or VIP status before users can create a guild.' WHERE `key` = 'catalog.guild.hc_required';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Credit cost required to create a guild.' WHERE `key` = 'catalog.guild.price';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Layout or image ID used when a limited page is sold out.' WHERE `key` = 'catalog.ltd.page.soldout';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Randomize the order or selection of limited catalog items.' WHERE `key` = 'catalog.ltd.random';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Catalog page ID used for VIP gift redemption.' WHERE `key` = 'catalog.page.vipgifts';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated list of chat color IDs blocked for the chatcolor command.' WHERE `key` = 'commands.cmd_chatcolor.banned_numbers';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Minimum permission rank required to use the staffonline command.' WHERE `key` = 'commands.cmd_staffonline.min_rank';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Use the legacy command plugin loading style.' WHERE `key` = 'commands.plugins.oldstyle';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Controls the emulator console mode or console output style.' WHERE `key` = 'console.mode';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable custom item stacking behaviour outside the default stacking rules.' WHERE `key` = 'custom.stacking.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum batch or partition size used by partitioned database operations.' WHERE `key` = 'db.max.partition.size';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Minimum batch or partition size used by partitioned database operations.' WHERE `key` = 'db.min.partition.size';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum size of the database connection pool.' WHERE `key` = 'db.pool.maxsize';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Minimum number of open connections kept in the database pool.' WHERE `key` = 'db.pool.minsize';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable general emulator debug mode.' WHERE `key` = 'debug.mode';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Show internal debug error messages.' WHERE `key` = 'debug.show.errors';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Show packet headers in debug logs.' WHERE `key` = 'debug.show.headers';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Print packet-level debug output.' WHERE `key` = 'debug.show.packets';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Print debug output for undefined incoming or outgoing packets.' WHERE `key` = 'debug.show.packets.undefined';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Log SQL exceptions to the console.' WHERE `key` = 'debug.show.sql.exception';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Show user-related debug messages.' WHERE `key` = 'debug.show.users';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated discount thresholds used for extra batch bonuses.' WHERE `key` = 'discount.additional.thresholds';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of free items granted inside one discount batch.' WHERE `key` = 'discount.batch.free.items';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of items required for one discount batch.' WHERE `key` = 'discount.batch.size';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Minimum number of discount batches required before the bonus logic applies.' WHERE `key` = 'discount.bonus.min.discounts';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of catalog items that can participate in one discount batch.' WHERE `key` = 'discount.max.allowed.items';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable or disable the feature controlled by `easter_eggs.enabled`.' WHERE `key` = 'easter_eggs.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'RSA private exponent used by the encryption layer.' WHERE `key` = 'enc.d';
|
||||
UPDATE `emulator_settings` SET `comment` = 'RSA public exponent used by the encryption layer.' WHERE `key` = 'enc.e';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable RSA encryption support for the socket handshake.' WHERE `key` = 'enc.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'RSA modulus used by the encryption layer.' WHERE `key` = 'enc.n';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated effect IDs used by the kill command for the killer.' WHERE `key` = 'essentials.cmd_kill.effect.killer';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated effect IDs used by the kill command for the victim.' WHERE `key` = 'essentials.cmd_kill.effect.victim';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow users with room rights to bypass the normal flood protection.' WHERE `key` = 'flood.with.rights';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable FTP uploads for generated assets.' WHERE `key` = 'ftp.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'FTP host used for asset uploads.' WHERE `key` = 'ftp.host';
|
||||
UPDATE `emulator_settings` SET `comment` = 'FTP password used for asset uploads.' WHERE `key` = 'ftp.password';
|
||||
UPDATE `emulator_settings` SET `comment` = 'FTP username used for asset uploads.' WHERE `key` = 'ftp.user';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum tile distance at which talking furniture can react to nearby speech.' WHERE `key` = 'furniture.talking.range';
|
||||
UPDATE `emulator_settings` SET `comment` = 'API key used by the FastFood or BaseJump integration.' WHERE `key` = 'gamecenter.fastfood.apiKey';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Asset base URL used by the FastFood or BaseJump game client.' WHERE `key` = 'gamecenter.fastfood.assets';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Background color used by the FastFood launcher UI.' WHERE `key` = 'gamecenter.fastfood.background.color';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the FastFood or BaseJump gamecenter integration.' WHERE `key` = 'gamecenter.fastfood.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Text color used by the FastFood launcher UI.' WHERE `key` = 'gamecenter.fastfood.text.color';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Theme name used by the FastFood launcher.' WHERE `key` = 'gamecenter.fastfood.theme';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Background image used for the SnowWar Arctic map.' WHERE `key` = 'gamecenter.snowwar.artic.bg';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Asset base URL used by the SnowWar game client.' WHERE `key` = 'gamecenter.snowwar.assets';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Background image used for the SnowWar Dragon Cave map.' WHERE `key` = 'gamecenter.snowwar.dragoncave.bg';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the SnowWar gamecenter integration.' WHERE `key` = 'gamecenter.snowwar.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Background image used for the SnowWar Fight Night map.' WHERE `key` = 'gamecenter.snowwar.fightnight.bg';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Background color used by the SnowWar launcher UI.' WHERE `key` = 'gamecenter.snowwar.game.background.color';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Countdown in seconds before a SnowWar round starts.' WHERE `key` = 'gamecenter.snowwar.game.start.time';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Text color used by the SnowWar launcher UI.' WHERE `key` = 'gamecenter.snowwar.game.text.color';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Minimum number of players required to start SnowWar.' WHERE `key` = 'gamecenter.snowwar.players.min';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Room ID used as the SnowWar lobby or host room.' WHERE `key` = 'gamecenter.snowwar.room.id';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Remote figuredata URL used when the hotel loads avatar figure definitions.' WHERE `key` = 'gamedata.figuredata.url';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Time in seconds that guardians have to accept a case.' WHERE `key` = 'guardians.accept.timer';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of guardians that can be assigned to one case.' WHERE `key` = 'guardians.maximum.guardians.total';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of times an unanswered guardian case can be resent.' WHERE `key` = 'guardians.maximum.resends';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Minimum number of guardian votes required to resolve a case.' WHERE `key` = 'guardians.minimum.votes';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Cooldown in seconds before the same user can open a new guardian report.' WHERE `key` = 'guardians.reporting.cooldown';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Use the legacy generic alert window style.' WHERE `key` = 'hotel.alert.oldstyle';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow users to ignore staff accounts.' WHERE `key` = 'hotel.allow.ignore.staffs';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Amount of credits granted on each automatic payout.' WHERE `key` = 'hotel.auto.credits.amount';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable automatic credits payouts.' WHERE `key` = 'hotel.auto.credits.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Multiplier applied to automatic credits payouts for HC users.' WHERE `key` = 'hotel.auto.credits.hc_modifier';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Skip users staying in hotel view when giving automatic credits payouts.' WHERE `key` = 'hotel.auto.credits.ignore.hotelview';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Skip idle users when giving automatic credits payouts.' WHERE `key` = 'hotel.auto.credits.ignore.idled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Interval in seconds between automatic credits payouts.' WHERE `key` = 'hotel.auto.credits.interval';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable automatic gotwpoints payouts.' WHERE `key` = 'hotel.auto.gotwpoints.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Multiplier applied to automatic gotwpoints payouts for HC users.' WHERE `key` = 'hotel.auto.gotwpoints.hc_modifier';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Skip users staying in hotel view when giving automatic gotwpoints payouts.' WHERE `key` = 'hotel.auto.gotwpoints.ignore.hotelview';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Skip idle users when giving automatic gotwpoints payouts.' WHERE `key` = 'hotel.auto.gotwpoints.ignore.idled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Interval in seconds between automatic gotwpoints payouts.' WHERE `key` = 'hotel.auto.gotwpoints.interval';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Internal currency name used by the automatic gotwpoints payout.' WHERE `key` = 'hotel.auto.gotwpoints.name';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Currency type ID used by the automatic gotwpoints payout.' WHERE `key` = 'hotel.auto.gotwpoints.type';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Amount of pixels granted on each automatic payout.' WHERE `key` = 'hotel.auto.pixels.amount';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable automatic pixels payouts.' WHERE `key` = 'hotel.auto.pixels.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Multiplier applied to automatic pixels payouts for HC users.' WHERE `key` = 'hotel.auto.pixels.hc_modifier';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Skip users staying in hotel view when giving automatic pixels payouts.' WHERE `key` = 'hotel.auto.pixels.ignore.hotelview';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Skip idle users when giving automatic pixels payouts.' WHERE `key` = 'hotel.auto.pixels.ignore.idled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Interval in seconds between automatic pixels payouts.' WHERE `key` = 'hotel.auto.pixels.interval';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Amount of points granted on each automatic payout.' WHERE `key` = 'hotel.auto.points.amount';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable automatic points payouts.' WHERE `key` = 'hotel.auto.points.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Multiplier applied to automatic points payouts for HC users.' WHERE `key` = 'hotel.auto.points.hc_modifier';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Skip users staying in hotel view when giving automatic points payouts.' WHERE `key` = 'hotel.auto.points.ignore.hotelview';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Skip idle users when giving automatic points payouts.' WHERE `key` = 'hotel.auto.points.ignore.idled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Interval in seconds between automatic points payouts.' WHERE `key` = 'hotel.auto.points.interval';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.banzai.points.tile.fill`.' WHERE `key` = 'hotel.banzai.points.tile.fill';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.banzai.points.tile.lock`.' WHERE `key` = 'hotel.banzai.points.tile.lock';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.banzai.points.tile.steal`.' WHERE `key` = 'hotel.banzai.points.tile.steal';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum tile distance from which a butler bot accepts commands.' WHERE `key` = 'hotel.bot.butler.commanddistance';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum tile distance from which a butler bot can serve requests.' WHERE `key` = 'hotel.bot.butler.servedistance';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Minimum number of seconds between bot chat lines.' WHERE `key` = 'hotel.bot.chat.minimum.interval';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum bot chat delay allowed when configuring scripted speech.' WHERE `key` = 'hotel.bot.max.chatdelay';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of characters allowed in bot chat lines.' WHERE `key` = 'hotel.bot.max.chatlength';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of characters allowed in bot names.' WHERE `key` = 'hotel.bot.max.namelength';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of bots allowed in one inventory.' WHERE `key` = 'hotel.bots.max.inventory';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of bots allowed in one room.' WHERE `key` = 'hotel.bots.max.room';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Default calendar campaign name or identifier.' WHERE `key` = 'hotel.calendar.default';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the hotel calendar feature.' WHERE `key` = 'hotel.calendar.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Multiplier applied to calendar pixel rewards for HC users.' WHERE `key` = 'hotel.calendar.pixels.hc_modifier';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Unix timestamp used as the calendar start date.' WHERE `key` = 'hotel.calendar.starttimestamp';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of discount slots or discount batches shown by the catalog.' WHERE `key` = 'hotel.catalog.discounts.amount';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Respect catalog item order numbers when rendering pages.' WHERE `key` = 'hotel.catalog.items.display.ordernum';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable daily purchase limits for limited catalog items.' WHERE `key` = 'hotel.catalog.ltd.limit.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Cooldown in seconds between catalog purchases.' WHERE `key` = 'hotel.catalog.purchase.cooldown';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the catalog recycler feature.' WHERE `key` = 'hotel.catalog.recycler.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of characters allowed in one public chat message.' WHERE `key` = 'hotel.chat.max.length';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Daily amount of respect points available for users.' WHERE `key` = 'hotel.daily.respect';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Daily amount of pet respect points available for users.' WHERE `key` = 'hotel.daily.respect.pets';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable or disable the feature controlled by `hotel.ecotron.enabled`.' WHERE `key` = 'hotel.ecotron.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.ecotron.rarity.chance.1`.' WHERE `key` = 'hotel.ecotron.rarity.chance.1';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.ecotron.rarity.chance.2`.' WHERE `key` = 'hotel.ecotron.rarity.chance.2';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.ecotron.rarity.chance.3`.' WHERE `key` = 'hotel.ecotron.rarity.chance.3';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.ecotron.rarity.chance.4`.' WHERE `key` = 'hotel.ecotron.rarity.chance.4';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.ecotron.rarity.chance.5`.' WHERE `key` = 'hotel.ecotron.rarity.chance.5';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Mute duration in seconds applied by the hotel flood protection.' WHERE `key` = 'hotel.flood.mute.time';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum total floorplan area allowed for custom rooms.' WHERE `key` = 'hotel.floorplan.max.totalarea';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum floorplan width or length allowed for custom rooms.' WHERE `key` = 'hotel.floorplan.max.widthlength';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of explosion boosts lost when a player gets frozen.' WHERE `key` = 'hotel.freeze.onfreeze.loose.explosionboost';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of snowballs lost when a player gets frozen.' WHERE `key` = 'hotel.freeze.onfreeze.loose.snowballs';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Time in seconds a player remains frozen.' WHERE `key` = 'hotel.freeze.onfreeze.time.frozen';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Score awarded for blocking tiles in Freeze.' WHERE `key` = 'hotel.freeze.points.block';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Score awarded for using Freeze effects or power-up actions.' WHERE `key` = 'hotel.freeze.points.effect';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Score awarded for freezing another player in Freeze.' WHERE `key` = 'hotel.freeze.points.freeze';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Chance for Freeze power-ups to spawn.' WHERE `key` = 'hotel.freeze.powerup.chance';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of extra lives granted by a Freeze power-up.' WHERE `key` = 'hotel.freeze.powerup.max.lives';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of extra snowballs granted by a Freeze power-up.' WHERE `key` = 'hotel.freeze.powerup.max.snowballs';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow Freeze protection power-ups to stack.' WHERE `key` = 'hotel.freeze.powerup.protection.stack';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Protection time in seconds after receiving a Freeze protection power-up.' WHERE `key` = 'hotel.freeze.powerup.protection.time';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Default friend category ID assigned to new friends.' WHERE `key` = 'hotel.friendcategory';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.furni.gym.achievement.olympics_c16_crosstrainer`.' WHERE `key` = 'hotel.furni.gym.achievement.olympics_c16_crosstrainer';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.furni.gym.achievement.olympics_c16_trampoline`.' WHERE `key` = 'hotel.furni.gym.achievement.olympics_c16_trampoline';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.furni.gym.achievement.olympics_c16_treadmill`.' WHERE `key` = 'hotel.furni.gym.achievement.olympics_c16_treadmill';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.furni.gym.forcerot.olympics_c16_crosstrainer`.' WHERE `key` = 'hotel.furni.gym.forcerot.olympics_c16_crosstrainer';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.furni.gym.forcerot.olympics_c16_trampoline`.' WHERE `key` = 'hotel.furni.gym.forcerot.olympics_c16_trampoline';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.furni.gym.forcerot.olympics_c16_treadmill`.' WHERE `key` = 'hotel.furni.gym.forcerot.olympics_c16_treadmill';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Comma-separated list of gift box type IDs allowed in the catalog.' WHERE `key` = 'hotel.gifts.box_types';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum message length allowed on gift notes.' WHERE `key` = 'hotel.gifts.length.max';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Comma-separated list of ribbon type IDs allowed in the catalog.' WHERE `key` = 'hotel.gifts.ribbon_types';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Credit price used by special gift boxes.' WHERE `key` = 'hotel.gifts.special.price';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Room ID used as the default home room for new users.' WHERE `key` = 'hotel.home.room';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of items allowed in one inventory.' WHERE `key` = 'hotel.inventory.max.items';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.item.trap.hween14_rare2`.' WHERE `key` = 'hotel.item.trap.hween14_rare2';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.item.trap.hween_c17_handstrap`.' WHERE `key` = 'hotel.item.trap.hween_c17_handstrap';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.item.trap.hween_c17_spiketrap`.' WHERE `key` = 'hotel.item.trap.hween_c17_spiketrap';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `hotel.item.trap.pirate_sandtrap`.' WHERE `key` = 'hotel.item.trap.pirate_sandtrap';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Track limit used by large jukebox furniture.' WHERE `key` = 'hotel.jukebox.limit.large';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Track limit used by normal jukebox furniture.' WHERE `key` = 'hotel.jukebox.limit.normal';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable logging for chat.' WHERE `key` = 'hotel.log.chat';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable logging for chat private.' WHERE `key` = 'hotel.log.chat.private';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable logging for room enter.' WHERE `key` = 'hotel.log.room.enter';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable logging for trades.' WHERE `key` = 'hotel.log.trades';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Currency type used for marketplace prices and taxes.' WHERE `key` = 'hotel.marketplace.currency';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable or disable the feature controlled by `hotel.marketplace.enabled`.' WHERE `key` = 'hotel.marketplace.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of bots allowed in one room.' WHERE `key` = 'hotel.max.bots.room';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum amount of duckets a user can hold.' WHERE `key` = 'hotel.max.duckets';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable or disable the feature controlled by `hotel.messenger.offline.messaging.enabled`.' WHERE `key` = 'hotel.messenger.offline.messaging.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of results returned by messenger user searches.' WHERE `key` = 'hotel.messenger.search.maxresults';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Public hotel name shown across the client and outgoing messages.' WHERE `key` = 'hotel.name';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable navigator room previews or camera mode.' WHERE `key` = 'hotel.navigator.camera';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Default owner name displayed by the navigator.' WHERE `key` = 'hotel.navigator.owner';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of rooms shown in the popular rooms list.' WHERE `key` = 'hotel.navigator.popular.amount';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of rooms shown per popular category.' WHERE `key` = 'hotel.navigator.popular.category.maxresults';
|
||||
UPDATE `emulator_settings` SET `comment` = 'List type used for the popular rooms tab.' WHERE `key` = 'hotel.navigator.popular.listtype';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Include public rooms inside the popular rooms tab.' WHERE `key` = 'hotel.navigator.populartab.publics';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of results returned by navigator searches.' WHERE `key` = 'hotel.navigator.search.maxresults';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Respect order numbers when sorting navigator results.' WHERE `key` = 'hotel.navigator.sort.ordernum';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Category ID used for the staff picks tab.' WHERE `key` = 'hotel.navigator.staffpicks.categoryid';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the NUX gift flow for new users.' WHERE `key` = 'hotel.nux.gifts.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of pets allowed in one inventory.' WHERE `key` = 'hotel.pets.max.inventory';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of pets allowed in one room.' WHERE `key` = 'hotel.pets.max.room';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum pet name length.' WHERE `key` = 'hotel.pets.name.length.max';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Minimum pet name length.' WHERE `key` = 'hotel.pets.name.length.min';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Generic player label used by text templates and client messages.' WHERE `key` = 'hotel.player.name';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of the same limited item a user can buy per day.' WHERE `key` = 'hotel.purchase.ltd.limit.daily.item';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of limited items a user can buy per day across all limited sales.' WHERE `key` = 'hotel.purchase.ltd.limit.daily.total';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Cooldown in seconds before daily counters such as respect are refilled.' WHERE `key` = 'hotel.refill.daily';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum roller delay or speed value accepted by roller furniture.' WHERE `key` = 'hotel.rollers.speed.maximum';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable room-entry logs.' WHERE `key` = 'hotel.room.enter.logs';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Validate custom floorplans before rooms are saved.' WHERE `key` = 'hotel.room.floorplan.check.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum amount of furniture allowed in one room.' WHERE `key` = 'hotel.room.furni.max';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Room ID used as the newbie lobby.' WHERE `key` = 'hotel.room.nooblobby';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Kick users who stand on public room door tiles.' WHERE `key` = 'hotel.room.public.doortile.kick';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow rollers to ignore normal placement rules.' WHERE `key` = 'hotel.room.rollers.norules';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of avatars that rollers can move at once.' WHERE `key` = 'hotel.room.rollers.roll_avatars.max';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of sticky notes allowed in one room.' WHERE `key` = 'hotel.room.stickies.max';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Prefix template written by sticky pole furniture.' WHERE `key` = 'hotel.room.stickypole.prefix';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated staff room tags.' WHERE `key` = 'hotel.room.tags.staff';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow empty rooms to switch into the idle state automatically.' WHERE `key` = 'hotel.rooms.auto.idle';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable decoration-hosting features for rooms.' WHERE `key` = 'hotel.rooms.deco_hosting';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Time in seconds before temporary hand items are cleared.' WHERE `key` = 'hotel.rooms.handitem.time';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of favorite rooms allowed per user.' WHERE `key` = 'hotel.rooms.max.favorite';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Idle cycle count before a room user is marked idle.' WHERE `key` = 'hotel.roomuser.idle.cycles';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Idle cycle count before a room user is kicked for idling.' WHERE `key` = 'hotel.roomuser.idle.cycles.kick';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Ignore the wired idle status when checking the room idle rule.' WHERE `key` = 'hotel.roomuser.idle.not_dancing.ignore.wired_idle';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the sanctions system.' WHERE `key` = 'hotel.sanctions.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Modifier used by the shop discount calculation.' WHERE `key` = 'hotel.shop.discount.modifier';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the talent track feature.' WHERE `key` = 'hotel.talenttrack.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Offer ID requested when the client asks for a targeted offer.' WHERE `key` = 'hotel.targetoffer.id';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow users to use teleports inside locked rooms when they otherwise qualify.' WHERE `key` = 'hotel.teleport.locked.allowed';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable room trading.' WHERE `key` = 'hotel.trading.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Require the trading perk before users may trade.' WHERE `key` = 'hotel.trading.requires.perk';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum value used by `hotel.trophies.length.max`.' WHERE `key` = 'hotel.trophies.length.max';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Run clothing validation when the related action occurs: onchangelooks.' WHERE `key` = 'hotel.users.clothingvalidation.onchangelooks';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Run clothing validation when the related action occurs: onfballgate.' WHERE `key` = 'hotel.users.clothingvalidation.onfballgate';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Run clothing validation when the related action occurs: onhcexpired.' WHERE `key` = 'hotel.users.clothingvalidation.onhcexpired';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Run clothing validation when the related action occurs: onlogin.' WHERE `key` = 'hotel.users.clothingvalidation.onlogin';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Run clothing validation when the related action occurs: onmannequin.' WHERE `key` = 'hotel.users.clothingvalidation.onmannequin';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Run clothing validation when the related action occurs: onmimic.' WHERE `key` = 'hotel.users.clothingvalidation.onmimic';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of friends allowed for normal users.' WHERE `key` = 'hotel.users.max.friends';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of friends allowed for HC users.' WHERE `key` = 'hotel.users.max.friends.hc';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of rooms allowed for normal users.' WHERE `key` = 'hotel.users.max.rooms';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of rooms allowed for HC users.' WHERE `key` = 'hotel.users.max.rooms.hc';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the limited-countdown hotel-view widget.' WHERE `key` = 'hotel.view.ltdcountdown.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Item ID shown by the limited-countdown widget.' WHERE `key` = 'hotel.view.ltdcountdown.itemid';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Item name shown by the limited-countdown widget.' WHERE `key` = 'hotel.view.ltdcountdown.itemname';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Catalog page ID linked by the limited-countdown widget.' WHERE `key` = 'hotel.view.ltdcountdown.pageid';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Unix timestamp used by the limited-countdown widget.' WHERE `key` = 'hotel.view.ltdcountdown.timestamp';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Delay in milliseconds before the welcome alert is shown.' WHERE `key` = 'hotel.welcome.alert.delay';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the welcome alert shown after login.' WHERE `key` = 'hotel.welcome.alert.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Message template used by the welcome alert.' WHERE `key` = 'hotel.welcome.alert.message';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Use the legacy welcome alert window style.' WHERE `key` = 'hotel.welcome.alert.oldstyle';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Mute duration in minutes applied when word-filter automute is triggered.' WHERE `key` = 'hotel.wordfilter.automute';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the word filter system.' WHERE `key` = 'hotel.wordfilter.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Apply the word filter to messenger messages.' WHERE `key` = 'hotel.wordfilter.messenger';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Normalise text before checking it against the word filter.' WHERE `key` = 'hotel.wordfilter.normalise';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Replacement word used when text is censored.' WHERE `key` = 'hotel.wordfilter.replacement';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Apply the word filter to room chat.' WHERE `key` = 'hotel.wordfilter.rooms';
|
||||
UPDATE `emulator_settings` SET `comment` = 'SQL query used to populate the hotel-view hall of fame panel.' WHERE `key` = 'hotelview.halloffame.query';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Amount of activity points awarded by the hotel-view promotion.' WHERE `key` = 'hotelview.promotional.points';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Activity point type used by the hotel-view promotional reward.' WHERE `key` = 'hotelview.promotional.points.type';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Base item ID used by the hotel-view promotional reward.' WHERE `key` = 'hotelview.promotional.reward.id';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Public item name used by the hotel-view promotional reward.' WHERE `key` = 'hotelview.promotional.reward.name';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Generate images locally instead of relying on an external imager service.' WHERE `key` = 'imager.internal.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Filesystem path where badge part assets are stored.' WHERE `key` = 'imager.location.badgeparts';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Filesystem output path for generated badges.' WHERE `key` = 'imager.location.output.badges';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Filesystem output path for saved camera photos.' WHERE `key` = 'imager.location.output.camera';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Filesystem output path for generated camera thumbnails.' WHERE `key` = 'imager.location.output.thumbnail';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Template URL used to fetch YouTube thumbnails.' WHERE `key` = 'imager.url.youtube';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Client asset path used for the basejump gamecenter images.' WHERE `key` = 'images.gamecenter.basejump';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Client asset path used for the snowwar gamecenter images.' WHERE `key` = 'images.gamecenter.snowwar';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Show the hotel information panel or startup information message.' WHERE `key` = 'info.shown';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Prevent invisible users from speaking in rooms.' WHERE `key` = 'invisible.prevent.chat';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of Netty boss-group threads used by the socket server.' WHERE `key` = 'io.bossgroup.threads';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Handle incoming client packets with a multi-threaded pipeline.' WHERE `key` = 'io.client.multithreaded.handler';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of Netty worker-group threads used by the socket server.' WHERE `key` = 'io.workergroup.threads';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable extra debug logging in the emulator logger.' WHERE `key` = 'logging.debug';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Log packet parsing errors.' WHERE `key` = 'logging.errors.packets';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Log runtime exceptions.' WHERE `key` = 'logging.errors.runtime';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Log SQL errors.' WHERE `key` = 'logging.errors.sql';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Log packet traffic in the standard logger.' WHERE `key` = 'logging.packets';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Log undefined packets in the standard logger.' WHERE `key` = 'logging.packets.undefined';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Global switch for the marketplace subsystem.' WHERE `key` = 'marketplace.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `monsterplant.seed.item_id`.' WHERE `key` = 'monsterplant.seed.item_id';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `monsterplant.seed_rare.item_id`.' WHERE `key` = 'monsterplant.seed_rare.item_id';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Validate moodlight color values before applying them.' WHERE `key` = 'moodlight.color_check.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated navigator event category definitions shown in the events tab.' WHERE `key` = 'navigator.eventcategories';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable TCP proxy-aware networking behaviour.' WHERE `key` = 'networking.tcp.proxy';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Automatically notify staff when a chat report is created.' WHERE `key` = 'notify.staff.chat.auto.report';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Base path used by the client to load furniture icon assets.' WHERE `key` = 'path.furniture.icons';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum pathfinder execution time in milliseconds before aborting.' WHERE `key` = 'pathfinder.execution_time.milli';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enforce the pathfinder execution time limit.' WHERE `key` = 'pathfinder.max_execution_time.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow the pathfinder to walk down falling steps.' WHERE `key` = 'pathfinder.step.allow.falling';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum height difference the pathfinder may step onto.' WHERE `key` = 'pathfinder.step.maximum.height';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Chat bubble style ID used by the pirate parrot.' WHERE `key` = 'pirate_parrot.message.bubble';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of predefined messages available to the pirate parrot.' WHERE `key` = 'pirate_parrot.message.count';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum number of characters allowed on post-it notes.' WHERE `key` = 'postit.charlimit';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Maximum delay allowed in the Pyramids minigame or puzzle timing.' WHERE `key` = 'pyramids.max.delay';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Use retro-style home room behaviour in the navigator or onboarding flow.' WHERE `key` = 'retro.style.homeroom';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Extra room chat delay applied before users can speak again.' WHERE `key` = 'room.chat.delay';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Allow whispering while a user stands inside a mute area.' WHERE `key` = 'room.chat.mutearea.allow_whisper';
|
||||
UPDATE `emulator_settings` SET `comment` = 'HTML or text format used for room chat prefixes.' WHERE `key` = 'room.chat.prefix.format';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Badge code displayed on promoted rooms.' WHERE `key` = 'room.promotion.badge';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Image used by Rosie bubble notifications.' WHERE `key` = 'rosie.bubble.image.url';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Currency type used by Rosie when buying a room or room package.' WHERE `key` = 'rosie.buyroom.currency.type';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `runtime.threads`.' WHERE `key` = 'runtime.threads';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `save.private.chats`.' WHERE `key` = 'save.private.chats';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Configuration value used by `save.room.chats`.' WHERE `key` = 'save.room.chats';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Expose moderation tickets to the scripter or automation tooling.' WHERE `key` = 'scripter.modtool.tickets';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Currency type ID used for diamonds.' WHERE `key` = 'seasonal.currency.diamond';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Currency type ID used for duckets.' WHERE `key` = 'seasonal.currency.ducket';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated display names for seasonal currency types.' WHERE `key` = 'seasonal.currency.names';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Currency type ID used for pixels.' WHERE `key` = 'seasonal.currency.pixel';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Currency type ID used for shells.' WHERE `key` = 'seasonal.currency.shell';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Primary seasonal currency type ID.' WHERE `key` = 'seasonal.primary.type';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated list of currency type IDs treated as seasonal currencies.' WHERE `key` = 'seasonal.types';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Achievement code granted for the HC subscription tier.' WHERE `key` = 'subscriptions.hc.achievement';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Number of days before expiry when HC discount offers become available.' WHERE `key` = 'subscriptions.hc.discount.days_before_end';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable discounted HC renewal offers.' WHERE `key` = 'subscriptions.hc.discount.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Reset tracked credits spent when the HC subscription expires.' WHERE `key` = 'subscriptions.hc.payday.creditsspent_reset_on_expire';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Currency rewarded by the HC payday system.' WHERE `key` = 'subscriptions.hc.payday.currency';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the HC payday reward system.' WHERE `key` = 'subscriptions.hc.payday.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Date interval used between HC payday reward runs.' WHERE `key` = 'subscriptions.hc.payday.interval';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Next scheduled execution date for HC payday rewards.' WHERE `key` = 'subscriptions.hc.payday.next_date';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Percentage of eligible spending returned by HC payday.' WHERE `key` = 'subscriptions.hc.payday.percentage';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Semicolon-separated streak thresholds and rewards for HC payday.' WHERE `key` = 'subscriptions.hc.payday.streak';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Enable the subscription background scheduler.' WHERE `key` = 'subscriptions.scheduler.enabled';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Interval in minutes between subscription scheduler runs.' WHERE `key` = 'subscriptions.scheduler.interval';
|
||||
UPDATE `emulator_settings` SET `comment` = 'Compatibility marker used by the custom team wired implementation. Do not remove.' WHERE `key` = 'team.wired.update.rc-1';
|
||||
UPDATE `emulator_settings` SET `comment` = 'API key used by the YouTube integration.' WHERE `key` = 'youtube.apikey';
|
||||
@@ -0,0 +1,6 @@
|
||||
INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES
|
||||
('furni.editor.renderer.config.path', '/var/www/Gamedata/config/renderer-config.json'),
|
||||
('furni.editor.asset.base.path', '/var/www/Gamedata/furniture/nitro-assets/');
|
||||
|
||||
ALTER TABLE permissions
|
||||
ADD COLUMN `acc_catalogfurni` ENUM('0','1') NOT NULL DEFAULT '0' AFTER `acc_catalog_ids`;
|
||||
@@ -0,0 +1 @@
|
||||
DELETE FROM emulator_settings WHERE (`key` = 'youtube.apikey');
|
||||
@@ -0,0 +1,499 @@
|
||||
-- Normalizes the legacy `permissions` table into:
|
||||
-- 1. `permission_ranks` -> one row per rank with rank metadata.
|
||||
-- 2. `permission_definitions` -> one row per permission key with comments and one `rank_<id>` column per rank.
|
||||
--
|
||||
-- This migration keeps the old `permissions` table untouched so the emulator can safely fall back to it.
|
||||
-- It also cleans up the older experimental normalized objects if they were already created.
|
||||
|
||||
DROP VIEW IF EXISTS `permissions_matrix_view`;
|
||||
DROP PROCEDURE IF EXISTS `refresh_permissions_matrix_view`;
|
||||
DROP TABLE IF EXISTS `permission_rank_values`;
|
||||
DROP TABLE IF EXISTS `permission_nodes`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `permission_ranks` (
|
||||
`id` int(11) NOT NULL,
|
||||
`rank_name` varchar(25) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
||||
`hidden_rank` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`badge` varchar(12) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '',
|
||||
`job_description` varchar(255) NOT NULL DEFAULT 'Here to help',
|
||||
`staff_color` varchar(8) NOT NULL DEFAULT '#327fa8',
|
||||
`staff_background` varchar(255) NOT NULL DEFAULT 'staff-bg.png',
|
||||
`level` int(11) NOT NULL DEFAULT 1,
|
||||
`room_effect` int(11) NOT NULL DEFAULT 0,
|
||||
`log_commands` enum('0','1') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '0',
|
||||
`prefix` varchar(5) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '',
|
||||
`prefix_color` varchar(7) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '',
|
||||
`auto_credits_amount` int(11) DEFAULT 0,
|
||||
`auto_pixels_amount` int(11) DEFAULT 0,
|
||||
`auto_gotw_amount` int(11) DEFAULT 0,
|
||||
`auto_points_amount` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_uca1400_ai_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `permission_definitions` (
|
||||
`permission_key` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
||||
`max_value` tinyint(3) unsigned NOT NULL DEFAULT 1,
|
||||
`comment` text NOT NULL,
|
||||
PRIMARY KEY (`permission_key`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_uca1400_ai_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
ALTER TABLE `permission_definitions`
|
||||
DROP COLUMN IF EXISTS `category`,
|
||||
DROP COLUMN IF EXISTS `value_type`,
|
||||
DROP COLUMN IF EXISTS `sort_order`;
|
||||
|
||||
INSERT INTO `permission_ranks` (
|
||||
`id`,
|
||||
`rank_name`,
|
||||
`hidden_rank`,
|
||||
`badge`,
|
||||
`job_description`,
|
||||
`staff_color`,
|
||||
`staff_background`,
|
||||
`level`,
|
||||
`room_effect`,
|
||||
`log_commands`,
|
||||
`prefix`,
|
||||
`prefix_color`,
|
||||
`auto_credits_amount`,
|
||||
`auto_pixels_amount`,
|
||||
`auto_gotw_amount`,
|
||||
`auto_points_amount`
|
||||
)
|
||||
SELECT
|
||||
`id`,
|
||||
`rank_name`,
|
||||
`hidden_rank`,
|
||||
`badge`,
|
||||
`job_description`,
|
||||
`staff_color`,
|
||||
`staff_background`,
|
||||
`level`,
|
||||
`room_effect`,
|
||||
`log_commands`,
|
||||
`prefix`,
|
||||
`prefix_color`,
|
||||
`auto_credits_amount`,
|
||||
`auto_pixels_amount`,
|
||||
`auto_gotw_amount`,
|
||||
`auto_points_amount`
|
||||
FROM `permissions`
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`rank_name` = VALUES(`rank_name`),
|
||||
`hidden_rank` = VALUES(`hidden_rank`),
|
||||
`badge` = VALUES(`badge`),
|
||||
`job_description` = VALUES(`job_description`),
|
||||
`staff_color` = VALUES(`staff_color`),
|
||||
`staff_background` = VALUES(`staff_background`),
|
||||
`level` = VALUES(`level`),
|
||||
`room_effect` = VALUES(`room_effect`),
|
||||
`log_commands` = VALUES(`log_commands`),
|
||||
`prefix` = VALUES(`prefix`),
|
||||
`prefix_color` = VALUES(`prefix_color`),
|
||||
`auto_credits_amount` = VALUES(`auto_credits_amount`),
|
||||
`auto_pixels_amount` = VALUES(`auto_pixels_amount`),
|
||||
`auto_gotw_amount` = VALUES(`auto_gotw_amount`),
|
||||
`auto_points_amount` = VALUES(`auto_points_amount`);
|
||||
|
||||
DROP PROCEDURE IF EXISTS `refresh_permission_definition_rank_columns`;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE `refresh_permission_definition_rank_columns`()
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT 0;
|
||||
DECLARE current_rank_id INT;
|
||||
DECLARE current_column_name VARCHAR(32);
|
||||
DECLARE column_exists INT DEFAULT 0;
|
||||
DECLARE rank_cursor CURSOR FOR SELECT `id` FROM `permission_ranks` ORDER BY `id` ASC;
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
|
||||
|
||||
OPEN rank_cursor;
|
||||
|
||||
rank_loop: LOOP
|
||||
FETCH rank_cursor INTO current_rank_id;
|
||||
|
||||
IF done = 1 THEN
|
||||
LEAVE rank_loop;
|
||||
END IF;
|
||||
|
||||
SET current_column_name = CONCAT('rank_', current_rank_id);
|
||||
|
||||
SELECT COUNT(*)
|
||||
INTO column_exists
|
||||
FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = DATABASE()
|
||||
AND `table_name` = 'permission_definitions'
|
||||
AND `column_name` = current_column_name;
|
||||
|
||||
IF column_exists = 0 THEN
|
||||
SET @alter_permissions_column_sql = CONCAT(
|
||||
'ALTER TABLE `permission_definitions` ADD COLUMN `',
|
||||
current_column_name,
|
||||
'` tinyint(3) unsigned NOT NULL DEFAULT 0'
|
||||
);
|
||||
|
||||
PREPARE alter_permissions_column_stmt FROM @alter_permissions_column_sql;
|
||||
EXECUTE alter_permissions_column_stmt;
|
||||
DEALLOCATE PREPARE alter_permissions_column_stmt;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
CLOSE rank_cursor;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CALL `refresh_permission_definition_rank_columns`();
|
||||
|
||||
INSERT INTO `permission_definitions` (
|
||||
`permission_key`,
|
||||
`max_value`,
|
||||
`comment`
|
||||
)
|
||||
SELECT
|
||||
`column_name` AS `permission_key`,
|
||||
CASE
|
||||
WHEN `column_type` LIKE '%''2''%' THEN 2
|
||||
ELSE 1
|
||||
END AS `max_value`,
|
||||
CASE
|
||||
WHEN COALESCE(`column_comment`, '') <> '' THEN `column_comment`
|
||||
WHEN `column_name` LIKE 'cmd\_%' AND `column_type` LIKE '%''2''%' THEN CONCAT(
|
||||
'Controls access to the :',
|
||||
REPLACE(SUBSTRING(`column_name`, 5), '_', ' '),
|
||||
' command. Values: 0 = disabled, 1 = allowed, 2 = allowed only when room-owner rights may be used.'
|
||||
)
|
||||
WHEN `column_name` LIKE 'cmd\_%' THEN CONCAT(
|
||||
'Controls access to the :',
|
||||
REPLACE(SUBSTRING(`column_name`, 5), '_', ' '),
|
||||
' command. Values: 0 = disabled, 1 = allowed.'
|
||||
)
|
||||
WHEN `column_name` LIKE 'acc\_%' AND `column_type` LIKE '%''2''%' THEN CONCAT(
|
||||
'Controls the ',
|
||||
REPLACE(SUBSTRING(`column_name`, 5), '_', ' '),
|
||||
' capability for this rank. Values: 0 = disabled, 1 = enabled, 2 = enabled only when room-owner rights may be used.'
|
||||
)
|
||||
WHEN `column_name` LIKE 'acc\_%' THEN CONCAT(
|
||||
'Controls the ',
|
||||
REPLACE(SUBSTRING(`column_name`, 5), '_', ' '),
|
||||
' capability for this rank. Values: 0 = disabled, 1 = enabled.'
|
||||
)
|
||||
ELSE CONCAT(
|
||||
'Legacy permission-related value migrated from the old permissions table for ',
|
||||
`column_name`,
|
||||
'.'
|
||||
)
|
||||
END AS `comment`
|
||||
FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = DATABASE()
|
||||
AND `table_name` = 'permissions'
|
||||
AND `column_name` NOT IN (
|
||||
'id',
|
||||
'rank_name',
|
||||
'hidden_rank',
|
||||
'badge',
|
||||
'job_description',
|
||||
'staff_color',
|
||||
'staff_background',
|
||||
'level',
|
||||
'room_effect',
|
||||
'log_commands',
|
||||
'prefix',
|
||||
'prefix_color',
|
||||
'auto_credits_amount',
|
||||
'auto_pixels_amount',
|
||||
'auto_gotw_amount',
|
||||
'auto_points_amount'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`max_value` = VALUES(`max_value`),
|
||||
`comment` = VALUES(`comment`);
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS `tmp_permission_comments`;
|
||||
|
||||
CREATE TEMPORARY TABLE `tmp_permission_comments` (
|
||||
`permission_key` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
||||
`comment` text NOT NULL,
|
||||
PRIMARY KEY (`permission_key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_uca1400_ai_ci;
|
||||
|
||||
INSERT INTO `tmp_permission_comments` (`permission_key`, `comment`) VALUES
|
||||
('cmd_about', 'Allows using :about to display emulator, revision, or hotel information exposed by the command.'),
|
||||
('cmd_alert', 'Allows using :alert to send a hotel alert popup to a specific user.'),
|
||||
('cmd_allow_trading', 'Allows using the trading-toggle command to enable or disable trading for a target user.'),
|
||||
('cmd_badge', 'Allows granting a badge code to a target user through a command.'),
|
||||
('cmd_ban', 'Allows banning users from the hotel.'),
|
||||
('cmd_blockalert', 'Allows sending the block-alert style moderation message.'),
|
||||
('cmd_bots', 'Allows using :bots to list the bots currently placed in the room.'),
|
||||
('cmd_bundle', 'Allows using :bundle / :roombundle to create a catalog room-bundle offer for the current room.'),
|
||||
('cmd_calendar', 'Allows using the hotel calendar command and any calendar actions wired to that command entry.'),
|
||||
('cmd_changename', 'Allows forcing a user-name change through the change-name command flow.'),
|
||||
('cmd_chatcolor', 'Allows changing the active chat bubble color through the chat-color command.'),
|
||||
('cmd_commands', 'Allows using :commands to list the command keys available to the current user.'),
|
||||
('cmd_connect_camera', 'Allows using the command that links the in-room camera feature to the current room session.'),
|
||||
('cmd_control', 'Allows using :control to take over another in-room user and stop controlling them later.'),
|
||||
('cmd_coords', 'Allows using :coords to inspect room coordinates for tiles, users, or furniture.'),
|
||||
('cmd_credits', 'Allows giving or removing credits from a user through the staff currency command.'),
|
||||
('cmd_subscription', 'Allows granting or editing subscription time through the subscription command.'),
|
||||
('cmd_danceall', 'Allows forcing every Habbo currently in the room to dance.'),
|
||||
('cmd_diagonal', 'Allows toggling diagonal walking for the current room.'),
|
||||
('cmd_disconnect', 'Allows disconnecting a user from the hotel immediately.'),
|
||||
('cmd_duckets', 'Allows giving or removing duckets from a user through the staff currency command.'),
|
||||
('cmd_ejectall', 'Allows ejecting all users from the current room.'),
|
||||
('cmd_empty', 'Allows clearing the current user furniture inventory through the empty-inventory command.'),
|
||||
('cmd_empty_bots', 'Allows clearing the current user bot inventory through the empty-bots command.'),
|
||||
('cmd_empty_pets', 'Allows clearing the current user pet inventory through the empty-pets command.'),
|
||||
('cmd_enable', 'Allows applying an avatar effect to yourself, or to another user when acc_enable_others is also granted.'),
|
||||
('cmd_event', 'Allows marking the current room as an event room through the event command.'),
|
||||
('cmd_faceless', 'Allows toggling the faceless avatar visual state on the executing room unit.'),
|
||||
('cmd_fastwalk', 'Allows toggling fast-walk mode for yourself or another in-room user.'),
|
||||
('cmd_filterword', 'Allows adding or removing entries from the configured word filter through command usage.'),
|
||||
('cmd_freeze', 'Allows freezing a target user in place.'),
|
||||
('cmd_freeze_bots', 'Allows freezing bots that are placed in the room.'),
|
||||
('cmd_gift', 'Allows sending a gift to a target user through the gift command.'),
|
||||
('cmd_give_rank', 'Allows setting another user rank through the give-rank command.'),
|
||||
('cmd_ha', 'Allows sending a hotel-wide alert.'),
|
||||
('acc_can_stalk', 'Allows following users even when they have disabled stalking.'),
|
||||
('cmd_hal', 'Allows sending a hotel-wide alert with a clickable link or extended content.'),
|
||||
('cmd_invisible', 'Allows toggling invisible staff mode.'),
|
||||
('cmd_ip_ban', 'Allows banning a user by IP address.'),
|
||||
('cmd_machine_ban', 'Allows banning a user by machine identifier.'),
|
||||
('cmd_hand_item', 'Allows spawning or changing the hand item currently held by a user.'),
|
||||
('cmd_happyhour', 'Allows starting or stopping the happy-hour event flow exposed by the happyhour command.'),
|
||||
('cmd_hidewired', 'Allows toggling whether wired furniture is visually hidden in the current room.'),
|
||||
('cmd_kickall', 'Allows kicking every user from the current room.'),
|
||||
('cmd_softkick', 'Allows soft-kicking a user back to the hotel view without a full sanction.'),
|
||||
('cmd_massbadge', 'Allows giving the same badge to many users at once.'),
|
||||
('cmd_roombadge', 'Allows setting or overriding the room badge shown to users.'),
|
||||
('cmd_masscredits', 'Allows giving credits to many users at once through the mass-credits command.'),
|
||||
('cmd_massduckets', 'Allows giving duckets to many users at once through the mass-duckets command.'),
|
||||
('cmd_massgift', 'Allows sending the same gift to many users at once.'),
|
||||
('cmd_masspoints', 'Allows giving activity points to many users at once through the mass-points command.'),
|
||||
('cmd_moonwalk', 'Allows toggling the moonwalk avatar effect for yourself while you are inside a room.'),
|
||||
('cmd_mimic', 'Allows copying another user appearance or presence state through the mimic command.'),
|
||||
('cmd_multi', 'Allows executing multiple chat commands from the special sticky/post-it scripting payload.'),
|
||||
('cmd_mute', 'Allows muting a target user.'),
|
||||
('cmd_pet_info', 'Allows opening the detailed pet-information view for a pet.'),
|
||||
('cmd_pickall', 'Allows picking up every furniture item from the current room.'),
|
||||
('cmd_plugins', 'Legacy key for the :plugins command, which currently lists loaded plugins without enforcing this dedicated permission node in code.'),
|
||||
('cmd_points', 'Allows giving or removing activity points from a user through the points command.'),
|
||||
('cmd_promote_offer', 'Allows using :promoteoffer to list active target offers or switch the globally promoted target offer.'),
|
||||
('cmd_pull', 'Allows pulling a nearby user onto the tile directly in front of you.'),
|
||||
('cmd_push', 'Allows pushing the user standing in front of you one tile farther in the direction you are facing.'),
|
||||
('cmd_redeem', 'Allows redeeming redeemable inventory items through the redeem command flow.'),
|
||||
('cmd_reload_room', 'Allows unloading and reloading the current room, then forwarding the occupants back into the fresh room instance.'),
|
||||
('cmd_roomalert', 'Allows sending the same alert message to everyone in the current room.'),
|
||||
('cmd_roomcredits', 'Allows giving credits to every Habbo currently in the room.'),
|
||||
('cmd_roomeffect', 'Allows applying the same avatar effect id to every Habbo currently in the room.'),
|
||||
('cmd_roomgift', 'Allows sending the same gift to every Habbo currently in the room.'),
|
||||
('cmd_roomitem', 'Allows setting the same hand-item id for every Habbo in the room; using 0 clears the hand item.'),
|
||||
('cmd_roommute', 'Allows muting every Habbo currently in the room.'),
|
||||
('cmd_roompixels', 'Allows giving duckets or pixels to every Habbo currently in the room.'),
|
||||
('cmd_roompoints', 'Allows giving activity points to every Habbo currently in the room.'),
|
||||
('cmd_say', 'Allows forcing another online user to say a custom message in their current room.'),
|
||||
('cmd_say_all', 'Allows making everyone in the room say a message.'),
|
||||
('cmd_setmax', 'Allows using :setmax to change the maximum user capacity of the current room.'),
|
||||
('cmd_set_poll', 'Allows using :setpoll to attach or remove a poll on the current room.'),
|
||||
('cmd_setpublic', 'Allows using :setpublic to change the room public/private visibility state.'),
|
||||
('cmd_setspeed', 'Allows using :setspeed to change the room walking speed setting.'),
|
||||
('cmd_shout', 'Allows forcing another online user to shout a custom message in their current room.'),
|
||||
('cmd_shout_all', 'Allows making everyone in the room shout a message.'),
|
||||
('cmd_shutdown', 'Allows using the shutdown command to stop the emulator process.'),
|
||||
('cmd_sitdown', 'Allows forcing users to sit down through the sitdown command.'),
|
||||
('cmd_staffalert', 'Allows sending an alert that is visible only to staff members.'),
|
||||
('cmd_staffonline', 'Allows viewing the current list of online staff members.'),
|
||||
('cmd_summon', 'Allows summoning a target user into the room where the staff member currently is.'),
|
||||
('cmd_summonrank', 'Allows summoning all online users of a given rank into the current room.'),
|
||||
('cmd_super_ban', 'Allows issuing the strongest ban command variant exposed by the super-ban command.'),
|
||||
('cmd_stalk', 'Allows following another user to their room.'),
|
||||
('cmd_superpull', 'Allows pulling a user to the tile in front of you without the short-range reach check used by :pull.'),
|
||||
('cmd_take_badge', 'Allows removing a badge code from a target user.'),
|
||||
('cmd_talk', 'Allows using the legacy :talk command to make another user speak a command-provided message.'),
|
||||
('cmd_teleport', 'Allows toggling the room-unit teleport mode used by the :teleport command.'),
|
||||
('cmd_trash', 'Allows deleting or trashing furniture/items through the trash command flow.'),
|
||||
('cmd_transform', 'Allows transforming your room unit into a chosen pet type, race, and color.'),
|
||||
('cmd_unban', 'Allows removing active bans.'),
|
||||
('cmd_unload', 'Allows disposing the current room instance immediately through :unload / :crash.'),
|
||||
('cmd_unmute', 'Allows removing an active mute from a target user.'),
|
||||
('cmd_update_achievements', 'Allows using :update_achievements to reload achievements configuration.'),
|
||||
('cmd_update_bots', 'Allows using :update_bots to reload bot data and bot configuration.'),
|
||||
('cmd_update_catalogue', 'Allows using :update_catalogue to reload catalogue pages and offers.'),
|
||||
('cmd_update_config', 'Allows using :update_config to reload emulator configuration settings.'),
|
||||
('cmd_update_guildparts', 'Allows using :update_guildparts to reload guild badge parts and guild configuration.'),
|
||||
('cmd_update_hotel_view', 'Allows using :update_hotel_view to reload hotel-view assets or settings.'),
|
||||
('cmd_update_items', 'Allows using :update_items to reload item data and furniture definitions.'),
|
||||
('cmd_update_navigator', 'Allows using :update_navigator to reload navigator configuration and listings.'),
|
||||
('cmd_update_permissions', 'Allows using :update_permissions to reload ranks and permissions from the database.'),
|
||||
('cmd_update_pet_data', 'Allows using :update_pet_data to reload pet types and pet races.'),
|
||||
('cmd_update_plugins', 'Allows using :update_plugins to reload plugin data or plugin metadata.'),
|
||||
('cmd_update_polls', 'Allows using :update_polls to reload poll and questionnaire data.'),
|
||||
('cmd_update_texts', 'Allows using :update_texts to reload external texts and localizations.'),
|
||||
('cmd_update_wordfilter', 'Allows using :update_wordfilter to reload the word-filter list.'),
|
||||
('cmd_userinfo', 'Allows opening the detailed user-information view used by staff tools.'),
|
||||
('cmd_word_quiz', 'Allows starting a room word-quiz event with a custom question and optional duration.'),
|
||||
('cmd_warp', 'Allows instantly warping your room unit to a target tile.'),
|
||||
('acc_anychatcolor', 'Allows selecting any chat bubble color, including normally restricted colors.'),
|
||||
('acc_anyroomowner', 'Treats the rank as room owner for owner-only checks such as room settings, wired saving, rights management, floorplan editing, and similar room-owner gates.'),
|
||||
('acc_empty_others', 'Allows :empty, :empty_bots, and :empty_pets to target another user inventory instead of only your own.'),
|
||||
('acc_enable_others', 'Allows :enable to apply avatar effects to another user instead of only to yourself.'),
|
||||
('acc_see_whispers', 'Allows seeing whispers sent between other users in the room.'),
|
||||
('acc_see_tentchat', 'Allows seeing tent chat or similar hidden chat channels that are normally not visible to everyone.'),
|
||||
('acc_superwired', 'Allows saving advanced wired data without the normal wordfilter and reward payload restrictions applied to regular users.'),
|
||||
('acc_supporttool', 'Allows opening and using the support/moderation tool interface.'),
|
||||
('acc_unkickable', 'Prevents the user from being kicked by normal moderation or room commands.'),
|
||||
('acc_guildgate', 'Allows bypassing guild gate access restrictions.'),
|
||||
('acc_moverotate', 'Allows moving, rotating, and saving wired furniture without the usual room-owner restriction checks.'),
|
||||
('acc_placefurni', 'Allows placing furniture, opening :wired, and passing room-right checks that normally require owner or controller rights.'),
|
||||
('acc_unlimited_bots', 'Removes both the bot inventory cap and the per-room bot placement cap for this rank.'),
|
||||
('acc_unlimited_pets', 'Removes both the pet inventory cap and the per-room pet placement cap for this rank.'),
|
||||
('acc_hide_ip', 'Hides the user IP address in staff tools and other staff-facing views.'),
|
||||
('acc_hide_mail', 'Hides the user email address in moderation tools and staff views.'),
|
||||
('acc_not_mimiced', 'Prevents other users from mimicking this account.'),
|
||||
('acc_chat_no_flood', 'Exempts the user from flood protection limits.'),
|
||||
('acc_staff_chat', 'Allows accessing staff-only chat channels and staff broadcasts.'),
|
||||
('acc_staff_pick', 'Allows using staff item pick-up actions that bypass normal room ownership restrictions.'),
|
||||
('acc_enteranyroom', 'Allows entering rooms regardless of door mode, bans, or normal access restrictions.'),
|
||||
('acc_fullrooms', 'Allows entering rooms even when they are at maximum user capacity.'),
|
||||
('acc_infinite_credits', 'Prevents credits from being consumed when a command or purchase checks credit balance.'),
|
||||
('acc_infinite_pixels', 'Prevents duckets or pixels from being consumed when the balance is checked.'),
|
||||
('acc_infinite_points', 'Prevents activity points from being consumed when the balance is checked.'),
|
||||
('acc_ambassador', 'Marks the rank as an ambassador for ambassador-only tools and visuals.'),
|
||||
('acc_debug', 'Allows using debug-only features, commands, or internal tooling.'),
|
||||
('acc_chat_no_limit', 'Lets the user hear and be heard regardless of room hearing distance limits.'),
|
||||
('acc_chat_no_filter', 'Bypasses the word filter for chat and staff-generated messages.'),
|
||||
('acc_nomute', 'Prevents the user from being muted by normal mute checks.'),
|
||||
('acc_guild_admin', 'Allows bypassing guild admin restrictions when managing guilds.'),
|
||||
('acc_catalog_ids', 'Allows seeing internal catalogue page ids, offer ids, or related technical catalogue identifiers.'),
|
||||
('acc_modtool_ticket_q', 'Allows seeing and handling the moderation ticket queue.'),
|
||||
('acc_modtool_user_logs', 'Allows reading user chat logs in the moderation tool.'),
|
||||
('acc_modtool_user_alert', 'Allows sending moderation alerts or cautions to users.'),
|
||||
('acc_modtool_user_kick', 'Allows kicking users from the moderation tool.'),
|
||||
('acc_modtool_user_ban', 'Allows banning users from the moderation tool.'),
|
||||
('acc_modtool_room_info', 'Allows viewing room information in the moderation tool.'),
|
||||
('acc_modtool_room_logs', 'Allows viewing room chat logs in the moderation tool.'),
|
||||
('acc_trade_anywhere', 'Allows starting trades outside the normal trade-enabled areas.'),
|
||||
('acc_update_notifications', 'Allows receiving update notifications emitted by the emulator.'),
|
||||
('acc_helper_use_guide_tool', 'Allows opening the helper guide tool.'),
|
||||
('acc_helper_give_guide_tours', 'Allows accepting and handling guide tour requests.'),
|
||||
('acc_helper_judge_chat_reviews', 'Allows reviewing helper or chat review tickets.'),
|
||||
('acc_floorplan_editor', 'Allows opening and saving the floorplan editor.'),
|
||||
('acc_camera', 'Allows using the in-room camera feature and related camera UI actions.'),
|
||||
('acc_ads_background', 'Allows editing room advertisement backgrounds.'),
|
||||
('cmd_wordquiz', 'Legacy alias of cmd_word_quiz for starting a room word-quiz event.'),
|
||||
('acc_room_staff_tags', 'Shows staff tags or markers above the user while inside rooms.'),
|
||||
('acc_infinite_friends', 'Removes the normal friend-list size limit.'),
|
||||
('acc_mimic_unredeemed', 'Allows mimicking looks even when they contain unreleased or restricted clothing.'),
|
||||
('cmd_update_youtube_playlists', 'Allows reloading YouTube playlist configuration for furniture integrations.'),
|
||||
('cmd_add_youtube_playlist', 'Allows adding a new YouTube playlist entry.'),
|
||||
('acc_mention', 'Allows using mention-related chat features beyond the normal rank restriction.'),
|
||||
('cmd_setstate', 'Legacy room-editor permission for :setstate / :ss, used to change the selected furni state or extradata value.'),
|
||||
('cmd_buildheight', 'Legacy room-editor permission for :buildheight / :bh, used to change the room build-height override.'),
|
||||
('cmd_setrotation', 'Legacy room-editor permission for :setrotation / :rot, used to change the rotation of the selected furni.'),
|
||||
('cmd_sellroom', 'Allows putting the current room up for sale through the sell-room command.'),
|
||||
('cmd_buyroom', 'Allows purchasing a room that is marked as for sale through the buy-room command.'),
|
||||
('cmd_pay', 'Allows transferring currency to another user through the pay command.'),
|
||||
('cmd_kill', 'Allows using the kill command effect exposed by the current command set.'),
|
||||
('cmd_hoverboard', 'Allows toggling the hoverboard effect or hoverboard movement mode.'),
|
||||
('cmd_kiss', 'Allows using the kiss interaction command on another user.'),
|
||||
('cmd_hug', 'Allows using the hug interaction command on another user.'),
|
||||
('cmd_welcome', 'Allows triggering the welcome command behavior defined by the current command set.'),
|
||||
('cmd_disable_effects', 'Allows disabling active avatar effects through the disable-effects command.'),
|
||||
('cmd_brb', 'Allows toggling the be-right-back status command.'),
|
||||
('cmd_nuke', 'Allows using the nuke command exposed by the current command set.'),
|
||||
('cmd_slime', 'Allows applying the slime command/effect exposed by the current command set.'),
|
||||
('cmd_explain', 'Allows using the explain command to send the predefined explanation/help flow to users.'),
|
||||
('cmd_closedice', 'Legacy essentials permission for :closedice, used to close dice items in the room or all dice at once.'),
|
||||
('acc_closedice_room', 'Legacy companion permission used by older closed-dice room checks.'),
|
||||
('cmd_set', 'Legacy essentials permission for :set / :changefurni, the generic furni editing command documented by :set info.'),
|
||||
('cmd_furnidata', 'Allows viewing technical furnidata information in-game for selected furniture.'),
|
||||
('kiss_cmd', 'Legacy alias used for the kiss command permission.'),
|
||||
('acc_calendar_force', 'Allows claiming calendar rewards even when the normal day-difference timing check would block the claim.'),
|
||||
('cmd_update_calendar', 'Allows using :update_calendar to reload calendar definitions and rewards.'),
|
||||
('cmd_update_all', 'Allows using :update_all to reload all supported runtime data sets in one command.'),
|
||||
('cms_dance', 'Legacy CMS-side permission kept for website integrations; no direct in-emulator command handler was found in the current tree.'),
|
||||
('acc_catalogfurni', 'Allows using catalogue administration features related to furniture pages and offers.'),
|
||||
('acc_unignorable', 'Prevents the account from being ignored by other users through the ignore system.'),
|
||||
('cmd_update_chat_bubbles', 'Allows using :update_chat_bubbles to reload chat-bubble definitions and assets.'),
|
||||
('cmd_calendar_staff', 'Allows the staff-only actions exposed by the calendar command flow.');
|
||||
|
||||
UPDATE `permission_definitions` pd
|
||||
INNER JOIN `tmp_permission_comments` tc ON tc.`permission_key` = pd.`permission_key`
|
||||
SET pd.`comment` = tc.`comment`;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS `tmp_permission_comments`;
|
||||
|
||||
DROP PROCEDURE IF EXISTS `refresh_permission_definition_values`;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE `refresh_permission_definition_values`()
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT 0;
|
||||
DECLARE current_rank_id INT;
|
||||
DECLARE current_column_name VARCHAR(32);
|
||||
DECLARE rank_cursor CURSOR FOR SELECT `id` FROM `permission_ranks` ORDER BY `id` ASC;
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
|
||||
|
||||
OPEN rank_cursor;
|
||||
|
||||
rank_loop: LOOP
|
||||
FETCH rank_cursor INTO current_rank_id;
|
||||
|
||||
IF done = 1 THEN
|
||||
LEAVE rank_loop;
|
||||
END IF;
|
||||
|
||||
SET current_column_name = CONCAT('rank_', current_rank_id);
|
||||
|
||||
SELECT GROUP_CONCAT(
|
||||
CONCAT(
|
||||
'SELECT ''',
|
||||
REPLACE(`column_name`, '''', ''''''),
|
||||
''' AS permission_key, CAST(COALESCE(`',
|
||||
REPLACE(`column_name`, '`', '``'),
|
||||
'`, ''0'') AS UNSIGNED) AS permission_value FROM `permissions` WHERE `id` = ',
|
||||
current_rank_id
|
||||
)
|
||||
ORDER BY `ordinal_position`
|
||||
SEPARATOR ' UNION ALL '
|
||||
) INTO @permission_rank_source_sql
|
||||
FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = DATABASE()
|
||||
AND `table_name` = 'permissions'
|
||||
AND `column_name` NOT IN (
|
||||
'id',
|
||||
'rank_name',
|
||||
'hidden_rank',
|
||||
'badge',
|
||||
'job_description',
|
||||
'staff_color',
|
||||
'staff_background',
|
||||
'level',
|
||||
'room_effect',
|
||||
'log_commands',
|
||||
'prefix',
|
||||
'prefix_color',
|
||||
'auto_credits_amount',
|
||||
'auto_pixels_amount',
|
||||
'auto_gotw_amount',
|
||||
'auto_points_amount'
|
||||
);
|
||||
|
||||
SET @permission_rank_update_sql = CONCAT(
|
||||
'UPDATE `permission_definitions` pd ',
|
||||
'INNER JOIN (',
|
||||
@permission_rank_source_sql,
|
||||
') src ON src.permission_key = pd.permission_key ',
|
||||
'SET pd.`',
|
||||
current_column_name,
|
||||
'` = src.permission_value'
|
||||
);
|
||||
|
||||
PREPARE permission_rank_update_stmt FROM @permission_rank_update_sql;
|
||||
EXECUTE permission_rank_update_stmt;
|
||||
DEALLOCATE PREPARE permission_rank_update_stmt;
|
||||
END LOOP;
|
||||
|
||||
CLOSE rank_cursor;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CALL `refresh_permission_definition_values`();
|
||||
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS `room_wired_settings` (
|
||||
`room_id` int(11) NOT NULL,
|
||||
`inspect_mask` int(11) NOT NULL DEFAULT 0 COMMENT 'Bitmask for who can open and inspect Wired in the room. 1=everyone, 2=users with rights, 4=group members, 8=group admins.',
|
||||
`modify_mask` int(11) NOT NULL DEFAULT 0 COMMENT 'Bitmask for who can modify Wired in the room. 2=users with rights, 4=group members, 8=group admins.',
|
||||
PRIMARY KEY (`room_id`),
|
||||
CONSTRAINT `fk_room_wired_settings_room_id` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,33 @@
|
||||
CREATE TABLE IF NOT EXISTS `room_user_wired_variables` (
|
||||
`room_id` int(11) NOT NULL,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`variable_item_id` int(11) NOT NULL,
|
||||
`value` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL DEFAULT 0,
|
||||
`updated_at` int(11) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`room_id`, `user_id`, `variable_item_id`),
|
||||
KEY `idx_room_user_wired_variables_room_item` (`room_id`, `variable_item_id`),
|
||||
KEY `idx_room_user_wired_variables_user` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `room_furni_wired_variables` (
|
||||
`room_id` int(11) NOT NULL,
|
||||
`furni_id` int(11) NOT NULL,
|
||||
`variable_item_id` int(11) NOT NULL,
|
||||
`value` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL DEFAULT 0,
|
||||
`updated_at` int(11) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`room_id`, `furni_id`, `variable_item_id`),
|
||||
KEY `idx_room_furni_wired_variables_room_item` (`room_id`, `variable_item_id`),
|
||||
KEY `idx_room_furni_wired_variables_furni` (`furni_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `room_wired_variables` (
|
||||
`room_id` int(11) NOT NULL,
|
||||
`variable_item_id` int(11) NOT NULL,
|
||||
`value` int(11) NOT NULL DEFAULT 0,
|
||||
`created_at` int(11) NOT NULL DEFAULT 0,
|
||||
`updated_at` int(11) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`room_id`, `variable_item_id`),
|
||||
KEY `idx_room_wired_variables_room_item` (`room_id`, `variable_item_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,32 @@
|
||||
ALTER TABLE `room_user_wired_variables`
|
||||
ADD COLUMN IF NOT EXISTS `created_at` int(11) NOT NULL DEFAULT 0 AFTER `value`;
|
||||
|
||||
ALTER TABLE `room_user_wired_variables`
|
||||
ADD COLUMN IF NOT EXISTS `updated_at` int(11) NOT NULL DEFAULT 0 AFTER `created_at`;
|
||||
|
||||
UPDATE `room_user_wired_variables`
|
||||
SET
|
||||
`created_at` = IF(`created_at` > 0, `created_at`, UNIX_TIMESTAMP()),
|
||||
`updated_at` = IF(`updated_at` > 0, `updated_at`, IF(`created_at` > 0, `created_at`, UNIX_TIMESTAMP()));
|
||||
|
||||
ALTER TABLE `room_furni_wired_variables`
|
||||
ADD COLUMN IF NOT EXISTS `created_at` int(11) NOT NULL DEFAULT 0 AFTER `value`;
|
||||
|
||||
ALTER TABLE `room_furni_wired_variables`
|
||||
ADD COLUMN IF NOT EXISTS `updated_at` int(11) NOT NULL DEFAULT 0 AFTER `created_at`;
|
||||
|
||||
UPDATE `room_furni_wired_variables`
|
||||
SET
|
||||
`created_at` = IF(`created_at` > 0, `created_at`, UNIX_TIMESTAMP()),
|
||||
`updated_at` = IF(`updated_at` > 0, `updated_at`, IF(`created_at` > 0, `created_at`, UNIX_TIMESTAMP()));
|
||||
|
||||
ALTER TABLE `room_wired_variables`
|
||||
ADD COLUMN IF NOT EXISTS `created_at` int(11) NOT NULL DEFAULT 0 AFTER `value`;
|
||||
|
||||
ALTER TABLE `room_wired_variables`
|
||||
ADD COLUMN IF NOT EXISTS `updated_at` int(11) NOT NULL DEFAULT 0 AFTER `created_at`;
|
||||
|
||||
UPDATE `room_wired_variables`
|
||||
SET
|
||||
`created_at` = 0,
|
||||
`updated_at` = IF(`updated_at` > 0, `updated_at`, UNIX_TIMESTAMP());
|
||||
@@ -17,14 +17,18 @@ import java.util.Properties;
|
||||
public class ConfigurationManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationManager.class);
|
||||
private static final String EMULATOR_SETTINGS_TABLE = "emulator_settings";
|
||||
private static final String WIRED_SETTINGS_TABLE = "wired_emulator_settings";
|
||||
|
||||
private final Properties properties;
|
||||
private final Properties wiredProperties;
|
||||
private final String configurationPath;
|
||||
public boolean loaded = false;
|
||||
public boolean isLoading = false;
|
||||
|
||||
public ConfigurationManager(String configurationPath) {
|
||||
this.properties = new Properties();
|
||||
this.wiredProperties = new Properties();
|
||||
this.configurationPath = configurationPath;
|
||||
this.reload();
|
||||
}
|
||||
@@ -32,6 +36,7 @@ public class ConfigurationManager {
|
||||
public void reload() {
|
||||
this.isLoading = true;
|
||||
this.properties.clear();
|
||||
this.wiredProperties.clear();
|
||||
|
||||
InputStream input = null;
|
||||
|
||||
@@ -116,31 +121,15 @@ public class ConfigurationManager {
|
||||
LOGGER.info("Loading configuration from database...");
|
||||
|
||||
long millis = System.currentTimeMillis();
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement()) {
|
||||
if (statement.execute("SELECT * FROM emulator_settings")) {
|
||||
try (ResultSet set = statement.getResultSet()) {
|
||||
while (set.next()) {
|
||||
this.properties.put(set.getString("key"), set.getString("value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
this.loadSettingsTable(EMULATOR_SETTINGS_TABLE, this.properties, false);
|
||||
this.loadSettingsTable(WIRED_SETTINGS_TABLE, this.wiredProperties, true);
|
||||
|
||||
LOGGER.info("Configuration -> loaded! ({} MS)", System.currentTimeMillis() - millis);
|
||||
}
|
||||
|
||||
public void saveToDatabase() {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("UPDATE emulator_settings SET `value` = ? WHERE `key` = ? LIMIT 1")) {
|
||||
for (Map.Entry<Object, Object> entry : this.properties.entrySet()) {
|
||||
statement.setString(1, entry.getValue().toString());
|
||||
statement.setString(2, entry.getKey().toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
this.saveSettingsTable(EMULATOR_SETTINGS_TABLE, this.properties);
|
||||
this.saveSettingsTable(WIRED_SETTINGS_TABLE, this.wiredProperties);
|
||||
}
|
||||
|
||||
|
||||
@@ -153,10 +142,21 @@ public class ConfigurationManager {
|
||||
if (this.isLoading)
|
||||
return defaultValue;
|
||||
|
||||
if (!this.properties.containsKey(key)) {
|
||||
Properties targetProperties = this.resolveProperties(key);
|
||||
|
||||
if (targetProperties.containsKey(key)) {
|
||||
return targetProperties.getProperty(key, defaultValue);
|
||||
}
|
||||
|
||||
if (this.isWiredSettingKey(key) && this.properties.containsKey(key)) {
|
||||
return this.properties.getProperty(key, defaultValue);
|
||||
}
|
||||
|
||||
if (!targetProperties.containsKey(key)) {
|
||||
LOGGER.error("Config key not found {}", key);
|
||||
}
|
||||
return this.properties.getProperty(key, defaultValue);
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key) {
|
||||
@@ -209,21 +209,91 @@ public class ConfigurationManager {
|
||||
}
|
||||
|
||||
public void update(String key, String value) {
|
||||
this.properties.setProperty(key, value);
|
||||
this.resolveProperties(key).setProperty(key, value);
|
||||
}
|
||||
|
||||
public void register(String key, String value) {
|
||||
if (this.properties.getProperty(key, null) != null)
|
||||
this.register(key, value, "");
|
||||
}
|
||||
|
||||
public void register(String key, String value, String comment) {
|
||||
Properties targetProperties = this.resolveProperties(key);
|
||||
|
||||
if (targetProperties.getProperty(key, null) != null)
|
||||
return;
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO emulator_settings VALUES (?, ?)")) {
|
||||
statement.setString(1, key);
|
||||
statement.setString(2, value);
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
|
||||
this.insertSetting(key, value, comment);
|
||||
this.update(key, value);
|
||||
}
|
||||
|
||||
private void loadSettingsTable(String tableName, Properties targetProperties, boolean optional) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
Statement statement = connection.createStatement()) {
|
||||
if (statement.execute("SELECT * FROM " + tableName)) {
|
||||
try (ResultSet set = statement.getResultSet()) {
|
||||
while (set.next()) {
|
||||
targetProperties.put(set.getString("key"), set.getString("value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
if (optional) {
|
||||
LOGGER.warn("Skipping optional config table {}: {}", tableName, e.getMessage());
|
||||
} else {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveSettingsTable(String tableName, Properties sourceProperties) {
|
||||
String sql = "UPDATE " + tableName + " SET `value` = ? WHERE `key` = ? LIMIT 1";
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
for (Map.Entry<Object, Object> entry : sourceProperties.entrySet()) {
|
||||
statement.setString(1, entry.getValue().toString());
|
||||
statement.setString(2, entry.getKey().toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
if (WIRED_SETTINGS_TABLE.equals(tableName)) {
|
||||
LOGGER.warn("Skipping wired config save for table {}: {}", tableName, e.getMessage());
|
||||
} else {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertSetting(String key, String value, String comment) {
|
||||
String tableName = this.isWiredSettingKey(key) ? WIRED_SETTINGS_TABLE : EMULATOR_SETTINGS_TABLE;
|
||||
String sql = this.isWiredSettingKey(key)
|
||||
? "INSERT INTO " + tableName + " (`key`, `value`, `comment`) VALUES (?, ?, ?)"
|
||||
: "INSERT INTO " + tableName + " (`key`, `value`) VALUES (?, ?)";
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
statement.setString(1, key);
|
||||
statement.setString(2, value);
|
||||
|
||||
if (this.isWiredSettingKey(key)) {
|
||||
statement.setString(3, comment == null ? "" : comment);
|
||||
}
|
||||
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
if (this.isWiredSettingKey(key)) {
|
||||
LOGGER.warn("Unable to insert wired setting {} into {}: {}", key, tableName, e.getMessage());
|
||||
} else {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Properties resolveProperties(String key) {
|
||||
return this.isWiredSettingKey(key) ? this.wiredProperties : this.properties;
|
||||
}
|
||||
|
||||
private boolean isWiredSettingKey(String key) {
|
||||
return key != null && (key.startsWith("wired.") || key.startsWith("hotel.wired."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package com.eu.habbo.habbohotel.commands;
|
||||
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
|
||||
import com.eu.habbo.messages.outgoing.users.InClientLinkComposer;
|
||||
|
||||
public class WiredCommand extends Command {
|
||||
public WiredCommand() {
|
||||
super(Permission.ACC_PLACEFURNI, new String[]{"wired"});
|
||||
super(null, new String[]{"wired"});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -20,12 +19,8 @@ public class WiredCommand extends Command {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean hasRights = room.hasRights(gameClient.getHabbo())
|
||||
|| room.isOwner(gameClient.getHabbo())
|
||||
|| gameClient.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER);
|
||||
|
||||
if (!hasRights) {
|
||||
gameClient.getHabbo().whisper("You need room rights to open the Wired Creator Tools.", RoomChatMessageBubbles.ALERT);
|
||||
if (!room.canInspectWired(gameClient.getHabbo())) {
|
||||
gameClient.sendResponse(new InClientLinkComposer("wired-tools/invalid"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,16 +135,10 @@ public class ForumThread implements Runnable, ISerialize {
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT A.*, B.`id` AS `last_comment_id` " +
|
||||
"FROM guilds_forums_threads A " +
|
||||
"JOIN (" +
|
||||
"SELECT * " +
|
||||
"LEFT JOIN (" +
|
||||
"SELECT `thread_id`, MAX(`id`) AS `id`, MAX(`created_at`) AS `created_at` " +
|
||||
"FROM `guilds_forums_comments` " +
|
||||
"WHERE `id` IN (" +
|
||||
"SELECT MAX(id) " +
|
||||
"FROM `guilds_forums_comments` B " +
|
||||
"GROUP BY `thread_id` AND B.`id` " +
|
||||
"ORDER BY B.`id` " +
|
||||
") " +
|
||||
"ORDER BY `id` DESC " +
|
||||
"GROUP BY `thread_id`" +
|
||||
") B ON A.`id` = B.`thread_id` " +
|
||||
"WHERE A.`guild_id` = ? " +
|
||||
"ORDER BY A.`pinned` DESC, B.`created_at` DESC "
|
||||
@@ -176,16 +170,10 @@ public class ForumThread implements Runnable, ISerialize {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT A.*, B.`id` AS `last_comment_id` " +
|
||||
"FROM guilds_forums_threads A " +
|
||||
"JOIN (" +
|
||||
"SELECT * " +
|
||||
"LEFT JOIN (" +
|
||||
"SELECT `thread_id`, MAX(`id`) AS `id`, MAX(`created_at`) AS `created_at` " +
|
||||
"FROM `guilds_forums_comments` " +
|
||||
"WHERE `id` IN (" +
|
||||
"SELECT MAX(id) " +
|
||||
"FROM `guilds_forums_comments` B " +
|
||||
"GROUP BY `thread_id` AND b.`id`" +
|
||||
"ORDER BY B.`id` " +
|
||||
") " +
|
||||
"ORDER BY `id` DESC " +
|
||||
"GROUP BY `thread_id`" +
|
||||
") B ON A.`id` = B.`thread_id` " +
|
||||
"WHERE A.`id` = ? " +
|
||||
"ORDER BY A.`pinned` DESC, B.`created_at` DESC " +
|
||||
@@ -222,6 +210,19 @@ public class ForumThread implements Runnable, ISerialize {
|
||||
guildThreads.add(thread);
|
||||
}
|
||||
|
||||
public static void clearCacheForGuild(int guildId) {
|
||||
synchronized (guildThreadsCache) {
|
||||
THashSet<ForumThread> threads = guildThreadsCache.remove(guildId);
|
||||
if (threads != null) {
|
||||
synchronized (forumThreadsCache) {
|
||||
for (ForumThread thread : threads) {
|
||||
forumThreadsCache.remove(thread.threadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
for (THashSet<ForumThread> threads : guildThreadsCache.values()) {
|
||||
for (ForumThread thread : threads) {
|
||||
|
||||
@@ -52,7 +52,10 @@ import com.eu.habbo.habbohotel.items.interactions.wired.effects.*;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraAnimationTime;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurniByVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUsersByVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFurniVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveCarryUsers;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecuteInOrder;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecutionLimit;
|
||||
@@ -60,9 +63,18 @@ import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMovePhys
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveNoAnimation;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRoomVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputFurniName;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextInputVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputUsername;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraContextVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUserVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableEcho;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableReference;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableLevelUpSystem;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableTextConnector;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.selector.*;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.*;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
@@ -225,6 +237,7 @@ public class ItemManager {
|
||||
this.interactionsList.add(new ItemInteraction("wf_trg_leave_room", WiredTriggerHabboLeavesRoom.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_trg_says_something", WiredTriggerHabboSaysKeyword.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_trg_clock_counter", WiredTriggerClockCounter.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_trg_var_changed", WiredTriggerVariableChanged.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_trg_periodically", WiredTriggerRepeater.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_trg_period_short", WiredTriggerRepeaterShort.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_trg_period_long", WiredTriggerRepeaterLong.class));
|
||||
@@ -300,7 +313,12 @@ public class ItemManager {
|
||||
this.interactionsList.add(new ItemInteraction("wf_slc_users_handitem", WiredEffectUsersHandItem.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_slc_users_onfurni", WiredEffectUsersOnFurni.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_slc_users_group", WiredEffectUsersGroup.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_slc_furni_with_var", WiredEffectFurniWithVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_slc_users_with_var", WiredEffectUsersWithVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_act_send_signal", WiredEffectSendSignal.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_act_give_var", WiredEffectGiveVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_act_remove_var", WiredEffectRemoveVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_act_change_var_val", WiredEffectChangeVariableValue.class));
|
||||
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_has_furni_on", WiredConditionFurniHaveFurni.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_furnis_hv_avtrs", WiredConditionFurniHaveHabbo.class));
|
||||
@@ -340,6 +358,10 @@ public class ItemManager {
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_not_triggerer_match", WiredConditionNotTriggererMatch.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_team_has_score", WiredConditionTeamHasScore.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_team_has_rank", WiredConditionTeamHasRank.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_has_var", WiredConditionHasVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_neg_has_var", WiredConditionNotHasVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_var_val_match", WiredConditionVariableValueMatch.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_cnd_var_age_match", WiredConditionVariableAgeMatch.class));
|
||||
|
||||
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_random", WiredExtraRandom.class));
|
||||
@@ -349,6 +371,8 @@ public class ItemManager {
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_filter_furni", WiredExtraFilterFurni.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_filter_user", WiredExtraFilterUser.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_filter_users", WiredExtraFilterUser.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_filter_furni_by_var", WiredExtraFilterFurniByVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_filter_users_by_var", WiredExtraFilterUsersByVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_mov_carry_users", WiredExtraMoveCarryUsers.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_mov_no_animation", WiredExtraMoveNoAnimation.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_anim_time", WiredExtraAnimationTime.class));
|
||||
@@ -357,6 +381,16 @@ public class ItemManager {
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_execution_limit", WiredExtraExecutionLimit.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_username", WiredExtraTextOutputUsername.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_furni_name", WiredExtraTextOutputFurniName.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_variable", WiredExtraTextOutputVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_text_input_variable", WiredExtraTextInputVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_var_text_connector", WiredExtraVariableTextConnector.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_var_lvlup_system", WiredExtraVariableLevelUpSystem.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_var_user", WiredExtraUserVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_var_furni", WiredExtraFurniVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_var_room", WiredExtraRoomVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_var_context", WiredExtraContextVariable.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_var_reference", WiredExtraVariableReference.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_var_echo", WiredExtraVariableEcho.class));
|
||||
|
||||
|
||||
this.interactionsList.add(new ItemInteraction("wf_highscore", InteractionWiredHighscore.class));
|
||||
|
||||
@@ -76,7 +76,11 @@ public class YoutubeManager {
|
||||
|
||||
private final THashMap<Integer, ArrayList<YoutubePlaylist>> playlists = new THashMap<>();
|
||||
private final THashMap<String, YoutubePlaylist> playlistCache = new THashMap<>();
|
||||
private final String apiKey = Emulator.getConfig().getValue("youtube.apikey");
|
||||
|
||||
private String getApiKey() {
|
||||
String key = Emulator.getConfig().getValue("youtube.apikey");
|
||||
return key != null ? key : "";
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.playlists.clear();
|
||||
@@ -89,11 +93,19 @@ public class YoutubeManager {
|
||||
|
||||
LOGGER.info("YouTube Manager -> Loading...");
|
||||
|
||||
if (getApiKey().isEmpty()) {
|
||||
LOGGER.warn("YouTube Manager -> No API key configured (youtube.apikey). YouTube TVs will not work!");
|
||||
}
|
||||
|
||||
int dbEntryCount = 0;
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM youtube_playlists")) {
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
final int itemId = set.getInt("item_id");
|
||||
final String playlistId = set.getString("playlist_id");
|
||||
dbEntryCount++;
|
||||
|
||||
LOGGER.info("YouTube Manager -> Loading playlist {} for base item #{}", playlistId, itemId);
|
||||
|
||||
youtubeDataLoaderPool.submit(() -> {
|
||||
YoutubePlaylist playlist;
|
||||
@@ -101,6 +113,9 @@ public class YoutubeManager {
|
||||
playlist = this.getPlaylistDataById(playlistId);
|
||||
if (playlist != null) {
|
||||
this.addPlaylistToItem(itemId, playlist);
|
||||
LOGGER.info("YouTube Manager -> Successfully loaded playlist {} for base item #{}", playlistId, itemId);
|
||||
} else {
|
||||
LOGGER.error("YouTube Manager -> Failed to load playlist {} for base item #{} (returned null - check API key and playlist ID)", playlistId, itemId);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to load YouTube playlist {} ERROR: {}", playlistId, e);
|
||||
@@ -112,6 +127,10 @@ public class YoutubeManager {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
|
||||
if (dbEntryCount == 0) {
|
||||
LOGGER.warn("YouTube Manager -> No entries found in youtube_playlists table!");
|
||||
}
|
||||
|
||||
youtubeDataLoaderPool.shutdown();
|
||||
try {
|
||||
youtubeDataLoaderPool.awaitTermination(60, TimeUnit.SECONDS);
|
||||
@@ -125,7 +144,12 @@ public class YoutubeManager {
|
||||
|
||||
public YoutubePlaylist getPlaylistDataById(String playlistId) throws IOException {
|
||||
if (this.playlistCache.containsKey(playlistId)) return this.playlistCache.get(playlistId);
|
||||
if(apiKey.isEmpty()) return null;
|
||||
|
||||
String apiKey = getApiKey();
|
||||
if(apiKey.isEmpty()) {
|
||||
LOGGER.error("YouTube API key is not configured! Set 'youtube.apikey' in emulator_settings to enable YouTube TV.");
|
||||
return null;
|
||||
}
|
||||
|
||||
YoutubePlaylist playlist;
|
||||
URL playlistInfo = URI.create("https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&id=" + playlistId + "&maxResults=1&key=" + apiKey).toURL();
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ public abstract class InteractionWiredCondition extends InteractionWired impleme
|
||||
@Override
|
||||
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
|
||||
if (client != null) {
|
||||
if (room.hasRights(client.getHabbo())) {
|
||||
if (room.canInspectWired(client.getHabbo())) {
|
||||
client.sendResponse(new WiredConditionDataComposer(this, room));
|
||||
this.activateBox(room);
|
||||
}
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ public abstract class InteractionWiredEffect extends InteractionWired implements
|
||||
@Override
|
||||
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
|
||||
if (client != null) {
|
||||
if (room.hasRights(client.getHabbo())) {
|
||||
if (room.canInspectWired(client.getHabbo())) {
|
||||
client.sendResponse(new WiredEffectDataComposer(this, room));
|
||||
this.activateBox(room);
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ public abstract class InteractionWiredExtra extends InteractionWired {
|
||||
@Override
|
||||
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
|
||||
if (client != null) {
|
||||
if (room.hasRights(client.getHabbo())) {
|
||||
if (room.canInspectWired(client.getHabbo())) {
|
||||
if (this.hasConfiguration()) {
|
||||
client.sendResponse(new WiredExtraDataComposer(this, room));
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public abstract class InteractionWiredTrigger extends InteractionWired implement
|
||||
@Override
|
||||
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
|
||||
if (client != null) {
|
||||
if (room.hasRights(client.getHabbo())) {
|
||||
if (room.canInspectWired(client.getHabbo())) {
|
||||
client.sendResponse(new WiredTriggerDataComposer(this, room));
|
||||
this.activateBox(room);
|
||||
}
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ public class InteractionYoutubeTV extends HabboItem {
|
||||
if (this.currentVideo == null) {
|
||||
serverMessage.appendString("");
|
||||
} else {
|
||||
serverMessage.appendString(Emulator.getConfig().getValue("imager.url.youtube").replace("%video%", this.currentVideo.getId()));
|
||||
serverMessage.appendString("https://img.youtube.com/vi/" + this.currentVideo.getId() + "/hqdefault.jpg");
|
||||
}
|
||||
|
||||
super.serializeExtradata(serverMessage);
|
||||
|
||||
+506
@@ -0,0 +1,506 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.items.FurnitureType;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomRightLevels;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.DanceType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredConditionType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WiredConditionHasVariable extends InteractionWiredCondition {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WiredConditionHasVariable.class);
|
||||
|
||||
protected static final int TARGET_USER = 0;
|
||||
protected static final int TARGET_FURNI = 1;
|
||||
protected static final int TARGET_CONTEXT = 2;
|
||||
protected static final int TARGET_ROOM = 3;
|
||||
protected static final int QUANTIFIER_ALL = 0;
|
||||
protected static final int QUANTIFIER_ANY = 1;
|
||||
private static final String CUSTOM_TOKEN_PREFIX = "custom:";
|
||||
private static final String INTERNAL_TOKEN_PREFIX = "internal:";
|
||||
|
||||
public static final WiredConditionType type = WiredConditionType.HAS_VAR;
|
||||
|
||||
protected final THashSet<HabboItem> selectedItems = new THashSet<>();
|
||||
protected int targetType = TARGET_USER;
|
||||
protected int userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected int quantifier = QUANTIFIER_ALL;
|
||||
protected String variableToken = "";
|
||||
protected int variableItemId = 0;
|
||||
|
||||
public WiredConditionHasVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredConditionHasVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredConditionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
this.refresh();
|
||||
|
||||
List<HabboItem> serializedItems = new ArrayList<>();
|
||||
if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
serializedItems.addAll(this.selectedItems);
|
||||
}
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(serializedItems.size());
|
||||
|
||||
for (HabboItem item : serializedItems) {
|
||||
message.appendInt(item.getId());
|
||||
}
|
||||
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableToken == null ? "" : this.variableToken);
|
||||
message.appendInt(4);
|
||||
message.appendInt(this.targetType);
|
||||
message.appendInt(this.userSource);
|
||||
message.appendInt(this.furniSource);
|
||||
message.appendInt(this.quantifier);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getType().code);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings) {
|
||||
int[] params = settings.getIntParams();
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
this.targetType = (params.length > 0) ? normalizeTargetType(params[0]) : TARGET_USER;
|
||||
this.userSource = (params.length > 1) ? normalizeUserSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.furniSource = (params.length > 2) ? normalizeFurniSource(params[2]) : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.quantifier = (params.length > 3) ? normalizeQuantifier(params[3]) : QUANTIFIER_ALL;
|
||||
this.setVariableToken(normalizeVariableToken(settings.getStringParam()));
|
||||
|
||||
if (this.variableToken.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.selectedItems.clear();
|
||||
|
||||
if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED && room != null) {
|
||||
int[] furniIds = settings.getFurniIds();
|
||||
if (furniIds.length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int furniId : furniIds) {
|
||||
HabboItem item = room.getHabboItem(furniId);
|
||||
|
||||
if (item != null) {
|
||||
this.selectedItems.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(WiredContext ctx) {
|
||||
return this.evaluateWithNegation(ctx, false);
|
||||
}
|
||||
|
||||
protected boolean evaluateWithNegation(WiredContext ctx, boolean negative) {
|
||||
Room room = ctx.room();
|
||||
|
||||
if (room == null || this.variableToken == null || this.variableToken.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (this.targetType) {
|
||||
case TARGET_FURNI -> this.evaluateFurniTargets(ctx, room, negative);
|
||||
case TARGET_CONTEXT -> {
|
||||
boolean contextMatch = this.matchesContext(ctx, room);
|
||||
yield negative ? !contextMatch : contextMatch;
|
||||
}
|
||||
case TARGET_ROOM -> {
|
||||
boolean roomMatch = this.matchesRoom(room);
|
||||
yield negative ? !roomMatch : roomMatch;
|
||||
}
|
||||
default -> this.evaluateUserTargets(ctx, room, negative);
|
||||
};
|
||||
}
|
||||
|
||||
private boolean evaluateUserTargets(WiredContext ctx, Room room, boolean negative) {
|
||||
List<RoomUnit> targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
|
||||
if (targets.isEmpty()) return false;
|
||||
|
||||
boolean match = (this.quantifier == QUANTIFIER_ANY)
|
||||
? this.matchesAnyUser(room, targets)
|
||||
: this.matchesAllUsers(room, targets);
|
||||
|
||||
return negative ? !match : match;
|
||||
}
|
||||
|
||||
private boolean evaluateFurniTargets(WiredContext ctx, Room room, boolean negative) {
|
||||
this.refresh();
|
||||
|
||||
List<HabboItem> targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.selectedItems);
|
||||
if (targets.isEmpty()) return false;
|
||||
|
||||
boolean match = (this.quantifier == QUANTIFIER_ANY)
|
||||
? this.matchesAnyFurni(room, targets)
|
||||
: this.matchesAllFurni(room, targets);
|
||||
|
||||
return negative ? !match : match;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
this.refresh();
|
||||
|
||||
List<Integer> itemIds = new ArrayList<>();
|
||||
for (HabboItem item : this.selectedItems) {
|
||||
if (item != null) itemIds.add(item.getId());
|
||||
}
|
||||
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
itemIds,
|
||||
this.targetType,
|
||||
this.variableToken,
|
||||
this.variableItemId,
|
||||
this.userSource,
|
||||
this.furniSource,
|
||||
this.quantifier
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) return;
|
||||
|
||||
try {
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
this.targetType = normalizeTargetType(data.targetType);
|
||||
this.userSource = normalizeUserSource(data.userSource);
|
||||
this.furniSource = normalizeFurniSource(data.furniSource);
|
||||
this.quantifier = normalizeQuantifier(data.quantifier);
|
||||
this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
|
||||
|
||||
if (room != null && data.itemIds != null) {
|
||||
for (Integer itemId : data.itemIds) {
|
||||
if (itemId == null || itemId <= 0) continue;
|
||||
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
if (item != null) this.selectedItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setVariableToken(normalizeVariableToken(wiredData));
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to load wired variable condition data for item {}", this.getId(), e);
|
||||
this.onPickUp();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.targetType = TARGET_USER;
|
||||
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.quantifier = QUANTIFIER_ALL;
|
||||
this.selectedItems.clear();
|
||||
this.setVariableToken("");
|
||||
}
|
||||
|
||||
protected boolean matchesAnyUser(Room room, List<RoomUnit> targets) {
|
||||
for (RoomUnit roomUnit : targets) {
|
||||
if (this.matchesUser(room, roomUnit)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean matchesAllUsers(Room room, List<RoomUnit> targets) {
|
||||
for (RoomUnit roomUnit : targets) {
|
||||
if (!this.matchesUser(room, roomUnit)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean matchesAnyFurni(Room room, List<HabboItem> targets) {
|
||||
for (HabboItem item : targets) {
|
||||
if (this.matchesFurni(room, item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean matchesAllFurni(Room room, List<HabboItem> targets) {
|
||||
for (HabboItem item : targets) {
|
||||
if (!this.matchesFurni(room, item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean matchesUser(Room room, RoomUnit roomUnit) {
|
||||
if (room == null || roomUnit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(this.variableToken)) {
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
|
||||
return habbo != null && room.getUserVariableManager().hasVariable(habbo.getHabboInfo().getId(), this.variableItemId);
|
||||
}
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
return this.hasUserInternalVariable(room, roomUnit, getInternalVariableKey(this.variableToken));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean matchesFurni(Room room, HabboItem item) {
|
||||
if (room == null || item == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(this.variableToken)) {
|
||||
return room.getFurniVariableManager().hasVariable(item.getId(), this.variableItemId);
|
||||
}
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
return this.hasFurniInternalVariable(item, getInternalVariableKey(this.variableToken));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean matchesContext(WiredContext ctx, Room room) {
|
||||
if (ctx == null || room == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(this.variableToken)) {
|
||||
return WiredContextVariableSupport.hasVariable(ctx, this.variableItemId);
|
||||
}
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
return WiredInternalVariableSupport.readContextValue(ctx, getInternalVariableKey(this.variableToken)) != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean matchesRoom(Room room) {
|
||||
if (room == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(this.variableToken)) {
|
||||
return room.getRoomVariableManager().hasVariable(this.variableItemId);
|
||||
}
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
return this.hasRoomInternalVariable(getInternalVariableKey(this.variableToken));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean hasUserInternalVariable(Room room, RoomUnit roomUnit, String key) {
|
||||
return WiredInternalVariableSupport.hasUserValue(room, roomUnit, key);
|
||||
}
|
||||
|
||||
protected boolean hasFurniInternalVariable(HabboItem item, String key) {
|
||||
return WiredInternalVariableSupport.hasFurniValue(item, key);
|
||||
}
|
||||
|
||||
protected boolean hasRoomInternalVariable(String key) {
|
||||
return WiredInternalVariableSupport.hasRoomValue(Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()), key);
|
||||
}
|
||||
|
||||
protected void refresh() {
|
||||
THashSet<HabboItem> staleItems = new THashSet<>();
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
staleItems.addAll(this.selectedItems);
|
||||
} else {
|
||||
for (HabboItem item : this.selectedItems) {
|
||||
if (item == null || item.getRoomId() != room.getId()) {
|
||||
staleItems.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedItems.removeAll(staleItems);
|
||||
}
|
||||
|
||||
protected void setVariableToken(String token) {
|
||||
this.variableToken = normalizeVariableToken(token);
|
||||
this.variableItemId = getCustomItemId(this.variableToken);
|
||||
}
|
||||
|
||||
protected boolean hasRoomEntryMethod(Habbo habbo) {
|
||||
if (habbo == null) return false;
|
||||
|
||||
String roomEntryMethod = habbo.getHabboInfo().getRoomEntryMethod();
|
||||
|
||||
return roomEntryMethod != null && !roomEntryMethod.trim().isEmpty() && !"unknown".equalsIgnoreCase(roomEntryMethod);
|
||||
}
|
||||
|
||||
protected TeamEffectData getTeamEffectData(int effectValue) {
|
||||
if (effectValue <= 0) return null;
|
||||
|
||||
if (effectValue >= 223 && effectValue <= 226) return new TeamEffectData(effectValue - 222, 0);
|
||||
if (effectValue >= 33 && effectValue <= 36) return new TeamEffectData(effectValue - 32, 1);
|
||||
if (effectValue >= 40 && effectValue <= 43) return new TeamEffectData(effectValue - 39, 2);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static int normalizeTargetType(int value) {
|
||||
return switch (value) {
|
||||
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
|
||||
default -> TARGET_USER;
|
||||
};
|
||||
}
|
||||
|
||||
protected static int normalizeQuantifier(int value) {
|
||||
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
|
||||
}
|
||||
|
||||
protected static int normalizeUserSource(int value) {
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
protected static int normalizeFurniSource(int value) {
|
||||
return switch (value) {
|
||||
case WiredSourceUtil.SOURCE_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
|
||||
default -> WiredSourceUtil.SOURCE_TRIGGER;
|
||||
};
|
||||
}
|
||||
|
||||
protected static boolean isCustomVariableToken(String token) {
|
||||
return token != null && token.startsWith(CUSTOM_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
protected static boolean isInternalVariableToken(String token) {
|
||||
return token != null && token.startsWith(INTERNAL_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
protected static int getCustomItemId(String token) {
|
||||
if (!isCustomVariableToken(token)) return 0;
|
||||
|
||||
try {
|
||||
return Integer.parseInt(token.substring(CUSTOM_TOKEN_PREFIX.length()));
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected static String getInternalVariableKey(String token) {
|
||||
return isInternalVariableToken(token) ? WiredInternalVariableSupport.normalizeKey(token.substring(INTERNAL_TOKEN_PREFIX.length())) : "";
|
||||
}
|
||||
|
||||
protected static String normalizeVariableToken(String token) {
|
||||
if (token == null) return "";
|
||||
|
||||
String normalized = token.trim();
|
||||
if (normalized.isEmpty()) return "";
|
||||
if (isCustomVariableToken(normalized)) return normalized;
|
||||
if (isInternalVariableToken(normalized)) return INTERNAL_TOKEN_PREFIX + WiredInternalVariableSupport.normalizeKey(normalized.substring(INTERNAL_TOKEN_PREFIX.length()));
|
||||
|
||||
try {
|
||||
int parsed = Integer.parseInt(normalized);
|
||||
return (parsed > 0) ? (CUSTOM_TOKEN_PREFIX + parsed) : "";
|
||||
} catch (NumberFormatException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
protected static class JsonData {
|
||||
List<Integer> itemIds;
|
||||
int targetType;
|
||||
String variableToken;
|
||||
int variableItemId;
|
||||
int userSource;
|
||||
int furniSource;
|
||||
int quantifier;
|
||||
|
||||
public JsonData(List<Integer> itemIds, int targetType, String variableToken, int variableItemId, int userSource, int furniSource, int quantifier) {
|
||||
this.itemIds = itemIds;
|
||||
this.targetType = targetType;
|
||||
this.variableToken = variableToken;
|
||||
this.variableItemId = variableItemId;
|
||||
this.userSource = userSource;
|
||||
this.furniSource = furniSource;
|
||||
this.quantifier = quantifier;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class TeamEffectData {
|
||||
final int colorId;
|
||||
final int typeId;
|
||||
|
||||
protected TeamEffectData(int colorId, int typeId) {
|
||||
this.colorId = colorId;
|
||||
this.typeId = typeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.wired.WiredConditionType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredConditionNotHasVariable extends WiredConditionHasVariable {
|
||||
public static final WiredConditionType type = WiredConditionType.NOT_HAS_VAR;
|
||||
|
||||
public WiredConditionNotHasVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredConditionNotHasVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredConditionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(WiredContext ctx) {
|
||||
return this.evaluateWithNegation(ctx, true);
|
||||
}
|
||||
}
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredConditionType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WiredConditionVariableAgeMatch extends WiredConditionHasVariable {
|
||||
public static final WiredConditionType type = WiredConditionType.VAR_AGE_MATCH;
|
||||
|
||||
private static final int TARGET_CONTEXT = 2;
|
||||
private static final int COMPARE_VALUE_CREATED = 0;
|
||||
private static final int COMPARE_VALUE_UPDATED = 1;
|
||||
private static final int COMPARISON_LOWER_THAN = 0;
|
||||
private static final int COMPARISON_HIGHER_THAN = 2;
|
||||
private static final int DURATION_UNIT_MILLISECONDS = 0;
|
||||
private static final int DURATION_UNIT_SECONDS = 1;
|
||||
private static final int DURATION_UNIT_MINUTES = 2;
|
||||
private static final int DURATION_UNIT_HOURS = 3;
|
||||
private static final int DURATION_UNIT_DAYS = 4;
|
||||
private static final int DURATION_UNIT_WEEKS = 5;
|
||||
private static final int DURATION_UNIT_MONTHS = 6;
|
||||
private static final int DURATION_UNIT_YEARS = 7;
|
||||
|
||||
protected int compareValue = COMPARE_VALUE_CREATED;
|
||||
protected int comparison = COMPARISON_LOWER_THAN;
|
||||
protected int durationAmount = 0;
|
||||
protected int durationUnit = DURATION_UNIT_SECONDS;
|
||||
|
||||
public WiredConditionVariableAgeMatch(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredConditionVariableAgeMatch(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredConditionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
this.refresh();
|
||||
|
||||
List<HabboItem> serializedItems = new ArrayList<>();
|
||||
if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
serializedItems.addAll(this.selectedItems);
|
||||
}
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(serializedItems.size());
|
||||
|
||||
for (HabboItem item : serializedItems) {
|
||||
message.appendInt(item.getId());
|
||||
}
|
||||
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableToken == null ? "" : this.variableToken);
|
||||
message.appendInt(8);
|
||||
message.appendInt(this.targetType);
|
||||
message.appendInt(this.compareValue);
|
||||
message.appendInt(this.comparison);
|
||||
message.appendInt(this.durationAmount);
|
||||
message.appendInt(this.durationUnit);
|
||||
message.appendInt(this.userSource);
|
||||
message.appendInt(this.furniSource);
|
||||
message.appendInt(this.quantifier);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getType().code);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings) {
|
||||
int[] params = settings.getIntParams();
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
this.targetType = (params.length > 0) ? normalizeTargetTypeExtended(params[0]) : TARGET_USER;
|
||||
this.compareValue = (params.length > 1) ? normalizeCompareValue(params[1]) : COMPARE_VALUE_CREATED;
|
||||
this.comparison = (params.length > 2) ? normalizeComparison(params[2]) : COMPARISON_LOWER_THAN;
|
||||
this.durationAmount = Math.max(0, (params.length > 3) ? params[3] : 0);
|
||||
this.durationUnit = (params.length > 4) ? normalizeDurationUnit(params[4]) : DURATION_UNIT_SECONDS;
|
||||
this.userSource = (params.length > 5) ? normalizeUserSource(params[5]) : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.furniSource = (params.length > 6) ? normalizeFurniSource(params[6]) : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.quantifier = (params.length > 7) ? normalizeQuantifier(params[7]) : QUANTIFIER_ALL;
|
||||
this.setVariableToken(normalizeVariableToken(settings.getStringParam()));
|
||||
|
||||
if (!this.isValidSource(room)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.selectedItems.clear();
|
||||
|
||||
if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED && room != null) {
|
||||
int[] furniIds = settings.getFurniIds();
|
||||
if (furniIds.length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int furniId : furniIds) {
|
||||
HabboItem item = room.getHabboItem(furniId);
|
||||
|
||||
if (item != null) {
|
||||
this.selectedItems.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
|
||||
if (room == null || this.variableToken == null || this.variableToken.isEmpty() || !isCustomVariableToken(this.variableToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long thresholdMs = durationToMillis(this.durationAmount, this.durationUnit);
|
||||
|
||||
return switch (this.targetType) {
|
||||
case TARGET_FURNI -> this.evaluateFurniTargets(ctx, room, thresholdMs);
|
||||
case TARGET_ROOM -> this.evaluateRoomTarget(room, thresholdMs);
|
||||
case TARGET_CONTEXT -> this.evaluateContextTarget(ctx, room, thresholdMs);
|
||||
default -> this.evaluateUserTargets(ctx, room, thresholdMs);
|
||||
};
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
this.refresh();
|
||||
|
||||
List<Integer> itemIds = new ArrayList<>();
|
||||
for (HabboItem item : this.selectedItems) {
|
||||
if (item != null) itemIds.add(item.getId());
|
||||
}
|
||||
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
itemIds,
|
||||
this.targetType,
|
||||
this.variableToken,
|
||||
this.variableItemId,
|
||||
this.compareValue,
|
||||
this.comparison,
|
||||
this.durationAmount,
|
||||
this.durationUnit,
|
||||
this.userSource,
|
||||
this.furniSource,
|
||||
this.quantifier
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) return;
|
||||
|
||||
try {
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
this.targetType = normalizeTargetTypeExtended(data.targetType);
|
||||
this.compareValue = normalizeCompareValue(data.compareValue);
|
||||
this.comparison = normalizeComparison(data.comparison);
|
||||
this.durationAmount = Math.max(0, data.durationAmount);
|
||||
this.durationUnit = normalizeDurationUnit(data.durationUnit);
|
||||
this.userSource = normalizeUserSource(data.userSource);
|
||||
this.furniSource = normalizeFurniSource(data.furniSource);
|
||||
this.quantifier = normalizeQuantifier(data.quantifier);
|
||||
this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
|
||||
|
||||
if (room != null && data.itemIds != null) {
|
||||
for (Integer itemId : data.itemIds) {
|
||||
if (itemId == null || itemId <= 0) continue;
|
||||
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
if (item != null) this.selectedItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setVariableToken(normalizeVariableToken(wiredData));
|
||||
} catch (Exception e) {
|
||||
this.onPickUp();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
super.onPickUp();
|
||||
this.compareValue = COMPARE_VALUE_CREATED;
|
||||
this.comparison = COMPARISON_LOWER_THAN;
|
||||
this.durationAmount = 0;
|
||||
this.durationUnit = DURATION_UNIT_SECONDS;
|
||||
}
|
||||
|
||||
private boolean evaluateUserTargets(WiredContext ctx, Room room, long thresholdMs) {
|
||||
List<RoomUnit> targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
|
||||
if (targets.isEmpty()) return false;
|
||||
|
||||
if (this.quantifier == QUANTIFIER_ANY) {
|
||||
for (RoomUnit roomUnit : targets) {
|
||||
if (this.matchesAge(this.readUserAgeMs(room, roomUnit), thresholdMs)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (RoomUnit roomUnit : targets) {
|
||||
if (!this.matchesAge(this.readUserAgeMs(room, roomUnit), thresholdMs)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean evaluateFurniTargets(WiredContext ctx, Room room, long thresholdMs) {
|
||||
this.refresh();
|
||||
|
||||
List<HabboItem> targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.selectedItems);
|
||||
if (targets.isEmpty()) return false;
|
||||
|
||||
if (this.quantifier == QUANTIFIER_ANY) {
|
||||
for (HabboItem item : targets) {
|
||||
if (this.matchesAge(this.readFurniAgeMs(room, item), thresholdMs)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (HabboItem item : targets) {
|
||||
if (!this.matchesAge(this.readFurniAgeMs(room, item), thresholdMs)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean evaluateRoomTarget(Room room, long thresholdMs) {
|
||||
return this.matchesAge(this.readRoomAgeMs(room), thresholdMs);
|
||||
}
|
||||
|
||||
private boolean evaluateContextTarget(WiredContext ctx, Room room, long thresholdMs) {
|
||||
return this.matchesAge(this.readContextAgeMs(ctx, room), thresholdMs);
|
||||
}
|
||||
|
||||
private Long readUserAgeMs(Room room, RoomUnit roomUnit) {
|
||||
if (room == null || roomUnit == null) return null;
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (habbo == null || !room.getUserVariableManager().hasVariable(habbo.getHabboInfo().getId(), this.variableItemId)) return null;
|
||||
|
||||
int timestamp = (this.compareValue == COMPARE_VALUE_UPDATED)
|
||||
? room.getUserVariableManager().getUpdatedAt(habbo.getHabboInfo().getId(), this.variableItemId)
|
||||
: room.getUserVariableManager().getCreatedAt(habbo.getHabboInfo().getId(), this.variableItemId);
|
||||
|
||||
return timestampToAgeMs(timestamp);
|
||||
}
|
||||
|
||||
private Long readFurniAgeMs(Room room, HabboItem item) {
|
||||
if (room == null || item == null || !room.getFurniVariableManager().hasVariable(item.getId(), this.variableItemId)) return null;
|
||||
|
||||
int timestamp = (this.compareValue == COMPARE_VALUE_UPDATED)
|
||||
? room.getFurniVariableManager().getUpdatedAt(item.getId(), this.variableItemId)
|
||||
: room.getFurniVariableManager().getCreatedAt(item.getId(), this.variableItemId);
|
||||
|
||||
return timestampToAgeMs(timestamp);
|
||||
}
|
||||
|
||||
private Long readRoomAgeMs(Room room) {
|
||||
if (room == null) return null;
|
||||
if (this.compareValue == COMPARE_VALUE_CREATED) return null;
|
||||
|
||||
int timestamp = room.getRoomVariableManager().getUpdatedAt(this.variableItemId);
|
||||
return timestampToAgeMs(timestamp);
|
||||
}
|
||||
|
||||
private Long readContextAgeMs(WiredContext ctx, Room room) {
|
||||
if (ctx == null || room == null || !WiredContextVariableSupport.hasVariable(ctx, this.variableItemId)) return null;
|
||||
|
||||
int timestamp = (this.compareValue == COMPARE_VALUE_UPDATED)
|
||||
? WiredContextVariableSupport.getUpdatedAt(ctx, this.variableItemId)
|
||||
: WiredContextVariableSupport.getCreatedAt(ctx, this.variableItemId);
|
||||
|
||||
return timestampToAgeMs(timestamp);
|
||||
}
|
||||
|
||||
private boolean matchesAge(Long ageMs, long thresholdMs) {
|
||||
if (ageMs == null) return false;
|
||||
|
||||
return switch (this.comparison) {
|
||||
case COMPARISON_HIGHER_THAN -> ageMs > thresholdMs;
|
||||
default -> ageMs < thresholdMs;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isValidSource(Room room) {
|
||||
if (room == null || !isCustomVariableToken(this.variableToken)) return false;
|
||||
|
||||
return switch (this.targetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().getDefinitionInfo(this.variableItemId) != null;
|
||||
case TARGET_CONTEXT -> WiredContextVariableSupport.getDefinitionInfo(room, this.variableItemId) != null;
|
||||
case TARGET_ROOM -> {
|
||||
WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
yield this.compareValue == COMPARE_VALUE_UPDATED && definition != null;
|
||||
}
|
||||
default -> room.getUserVariableManager().getDefinitionInfo(this.variableItemId) != null;
|
||||
};
|
||||
}
|
||||
|
||||
private static Long timestampToAgeMs(int timestampSeconds) {
|
||||
if (timestampSeconds <= 0) return null;
|
||||
|
||||
long timestampMs = (timestampSeconds * 1000L);
|
||||
return Math.max(0L, System.currentTimeMillis() - timestampMs);
|
||||
}
|
||||
|
||||
private static long durationToMillis(int amount, int unit) {
|
||||
long normalizedAmount = Math.max(0L, amount);
|
||||
|
||||
return switch (unit) {
|
||||
case DURATION_UNIT_MILLISECONDS -> normalizedAmount;
|
||||
case DURATION_UNIT_MINUTES -> safeMultiply(normalizedAmount, 60_000L);
|
||||
case DURATION_UNIT_HOURS -> safeMultiply(normalizedAmount, 3_600_000L);
|
||||
case DURATION_UNIT_DAYS -> safeMultiply(normalizedAmount, 86_400_000L);
|
||||
case DURATION_UNIT_WEEKS -> safeMultiply(normalizedAmount, 604_800_000L);
|
||||
case DURATION_UNIT_MONTHS -> safeMultiply(normalizedAmount, 2_592_000_000L);
|
||||
case DURATION_UNIT_YEARS -> safeMultiply(normalizedAmount, 31_536_000_000L);
|
||||
default -> safeMultiply(normalizedAmount, 1_000L);
|
||||
};
|
||||
}
|
||||
|
||||
private static long safeMultiply(long left, long right) {
|
||||
if (left <= 0 || right <= 0) return 0L;
|
||||
if (left > (Long.MAX_VALUE / right)) return Long.MAX_VALUE;
|
||||
|
||||
return left * right;
|
||||
}
|
||||
|
||||
private static int normalizeTargetTypeExtended(int value) {
|
||||
return switch (value) {
|
||||
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
|
||||
default -> TARGET_USER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeCompareValue(int value) {
|
||||
return (value == COMPARE_VALUE_UPDATED) ? COMPARE_VALUE_UPDATED : COMPARE_VALUE_CREATED;
|
||||
}
|
||||
|
||||
private static int normalizeComparison(int value) {
|
||||
return (value == COMPARISON_HIGHER_THAN) ? COMPARISON_HIGHER_THAN : COMPARISON_LOWER_THAN;
|
||||
}
|
||||
|
||||
private static int normalizeDurationUnit(int value) {
|
||||
return switch (value) {
|
||||
case DURATION_UNIT_MILLISECONDS, DURATION_UNIT_SECONDS, DURATION_UNIT_MINUTES, DURATION_UNIT_HOURS,
|
||||
DURATION_UNIT_DAYS, DURATION_UNIT_WEEKS, DURATION_UNIT_MONTHS, DURATION_UNIT_YEARS -> value;
|
||||
default -> DURATION_UNIT_SECONDS;
|
||||
};
|
||||
}
|
||||
|
||||
protected static class JsonData {
|
||||
List<Integer> itemIds;
|
||||
int targetType;
|
||||
String variableToken;
|
||||
int variableItemId;
|
||||
int compareValue;
|
||||
int comparison;
|
||||
int durationAmount;
|
||||
int durationUnit;
|
||||
int userSource;
|
||||
int furniSource;
|
||||
int quantifier;
|
||||
|
||||
JsonData(List<Integer> itemIds, int targetType, String variableToken, int variableItemId, int compareValue, int comparison, int durationAmount, int durationUnit, int userSource, int furniSource, int quantifier) {
|
||||
this.itemIds = itemIds;
|
||||
this.targetType = targetType;
|
||||
this.variableToken = variableToken;
|
||||
this.variableItemId = variableItemId;
|
||||
this.compareValue = compareValue;
|
||||
this.comparison = comparison;
|
||||
this.durationAmount = durationAmount;
|
||||
this.durationUnit = durationUnit;
|
||||
this.userSource = userSource;
|
||||
this.furniSource = furniSource;
|
||||
this.quantifier = quantifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
+814
@@ -0,0 +1,814 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.games.Game;
|
||||
import com.eu.habbo.habbohotel.games.GamePlayer;
|
||||
import com.eu.habbo.habbohotel.games.GameTeam;
|
||||
import com.eu.habbo.habbohotel.games.GameTeamColors;
|
||||
import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame;
|
||||
import com.eu.habbo.habbohotel.games.freeze.FreezeGame;
|
||||
import com.eu.habbo.habbohotel.games.wired.WiredGame;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredConditionType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.util.HotelDateTimeUtil;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class WiredConditionVariableValueMatch extends WiredConditionHasVariable {
|
||||
public static final WiredConditionType type = WiredConditionType.VAR_VAL_MATCH;
|
||||
|
||||
private static final int TARGET_CONTEXT = 2;
|
||||
private static final int SOURCE_SECONDARY_SELECTED = 101;
|
||||
private static final int REFERENCE_CONSTANT = 0;
|
||||
private static final int REFERENCE_VARIABLE = 1;
|
||||
private static final int COMPARISON_GREATER_THAN = 0;
|
||||
private static final int COMPARISON_GREATER_THAN_OR_EQUAL = 1;
|
||||
private static final int COMPARISON_EQUAL = 2;
|
||||
private static final int COMPARISON_LESS_THAN_OR_EQUAL = 3;
|
||||
private static final int COMPARISON_LESS_THAN = 4;
|
||||
private static final int COMPARISON_NOT_EQUAL = 5;
|
||||
private static final String DELIM = "\t";
|
||||
private static final String FURNI_DELIM = ";";
|
||||
|
||||
protected int comparison = COMPARISON_EQUAL;
|
||||
protected int referenceMode = REFERENCE_CONSTANT;
|
||||
protected int referenceConstantValue = 0;
|
||||
protected int referenceTargetType = TARGET_USER;
|
||||
protected int referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected int referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected String referenceVariableToken = "";
|
||||
protected int referenceVariableItemId = 0;
|
||||
protected final THashSet<HabboItem> referenceSelectedItems = new THashSet<>();
|
||||
|
||||
public WiredConditionVariableValueMatch(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredConditionVariableValueMatch(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredConditionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
this.refresh();
|
||||
this.refreshReferenceItems();
|
||||
|
||||
List<HabboItem> serializedItems = new ArrayList<>();
|
||||
if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
serializedItems.addAll(this.selectedItems);
|
||||
}
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(serializedItems.size());
|
||||
|
||||
for (HabboItem item : serializedItems) {
|
||||
message.appendInt(item.getId());
|
||||
}
|
||||
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.serializeStringData());
|
||||
message.appendInt(10);
|
||||
message.appendInt(this.targetType);
|
||||
message.appendInt(this.comparison);
|
||||
message.appendInt(this.referenceMode);
|
||||
message.appendInt(this.referenceConstantValue);
|
||||
message.appendInt(this.referenceTargetType);
|
||||
message.appendInt(this.userSource);
|
||||
message.appendInt(this.furniSource);
|
||||
message.appendInt(this.referenceUserSource);
|
||||
message.appendInt(this.referenceFurniSource);
|
||||
message.appendInt(this.quantifier);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getType().code);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings) {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
if (room == null) return false;
|
||||
|
||||
int[] params = settings.getIntParams();
|
||||
String[] stringParts = this.parseStringData(settings.getStringParam());
|
||||
int nextTargetType = normalizeTargetTypeExtended(param(params, 0, TARGET_USER));
|
||||
int nextComparison = normalizeComparison(param(params, 1, COMPARISON_EQUAL));
|
||||
int nextReferenceMode = normalizeReferenceMode(param(params, 2, REFERENCE_CONSTANT));
|
||||
int nextReferenceConstantValue = param(params, 3, 0);
|
||||
int nextReferenceTargetType = normalizeTargetTypeExtended(param(params, 4, TARGET_USER));
|
||||
int nextUserSource = normalizeUserSource(param(params, 5, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextFurniSource = normalizeFurniSource(param(params, 6, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextReferenceUserSource = normalizeUserSource(param(params, 7, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextReferenceFurniSource = normalizeReferenceFurniSource(param(params, 8, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextQuantifier = normalizeQuantifier(param(params, 9, QUANTIFIER_ALL));
|
||||
String nextVariableToken = normalizeVariableToken((stringParts.length > 0) ? stringParts[0] : settings.getStringParam());
|
||||
String nextReferenceVariableToken = normalizeVariableToken((stringParts.length > 1) ? stringParts[1] : "");
|
||||
|
||||
if (!this.isValidSource(room, nextTargetType, nextVariableToken)) return false;
|
||||
if (nextReferenceMode == REFERENCE_VARIABLE && !this.isValidReference(room, nextReferenceTargetType, nextReferenceVariableToken)) return false;
|
||||
|
||||
int selectionLimit = Emulator.getConfig().getInt("hotel.wired.furni.selection.count");
|
||||
List<HabboItem> nextSelectedItems = (nextTargetType == TARGET_FURNI && nextFurniSource == WiredSourceUtil.SOURCE_SELECTED)
|
||||
? this.parseItems(settings.getFurniIds(), room)
|
||||
: new ArrayList<>();
|
||||
List<HabboItem> nextReferenceItems = (nextReferenceMode == REFERENCE_VARIABLE && nextReferenceTargetType == TARGET_FURNI && nextReferenceFurniSource == SOURCE_SECONDARY_SELECTED)
|
||||
? this.parseItems((stringParts.length > 2) ? stringParts[2] : "", room)
|
||||
: new ArrayList<>();
|
||||
|
||||
if (nextSelectedItems.size() > selectionLimit || nextReferenceItems.size() > selectionLimit) return false;
|
||||
|
||||
this.selectedItems.clear();
|
||||
this.selectedItems.addAll(nextSelectedItems);
|
||||
this.referenceSelectedItems.clear();
|
||||
this.referenceSelectedItems.addAll(nextReferenceItems);
|
||||
this.targetType = nextTargetType;
|
||||
this.comparison = nextComparison;
|
||||
this.referenceMode = nextReferenceMode;
|
||||
this.referenceConstantValue = nextReferenceConstantValue;
|
||||
this.referenceTargetType = nextReferenceTargetType;
|
||||
this.userSource = nextUserSource;
|
||||
this.furniSource = nextFurniSource;
|
||||
this.referenceUserSource = nextReferenceUserSource;
|
||||
this.referenceFurniSource = nextReferenceFurniSource;
|
||||
this.quantifier = nextQuantifier;
|
||||
this.setVariableToken(nextVariableToken);
|
||||
this.setReferenceVariableToken(nextReferenceVariableToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
|
||||
if (room == null || this.variableToken == null || this.variableToken.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (this.targetType) {
|
||||
case TARGET_FURNI -> this.evaluateFurniTargets(ctx, room);
|
||||
case TARGET_ROOM -> this.evaluateRoomTarget(ctx, room);
|
||||
case TARGET_CONTEXT -> this.evaluateContextTarget(ctx, room);
|
||||
default -> this.evaluateUserTargets(ctx, room);
|
||||
};
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
this.refresh();
|
||||
this.refreshReferenceItems();
|
||||
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
this.targetType,
|
||||
this.variableToken,
|
||||
this.variableItemId,
|
||||
this.comparison,
|
||||
this.referenceMode,
|
||||
this.referenceConstantValue,
|
||||
this.referenceTargetType,
|
||||
this.referenceVariableToken,
|
||||
this.referenceVariableItemId,
|
||||
this.userSource,
|
||||
this.furniSource,
|
||||
this.referenceUserSource,
|
||||
this.referenceFurniSource,
|
||||
this.quantifier,
|
||||
this.toIds(this.selectedItems),
|
||||
this.toIds(this.referenceSelectedItems)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) return;
|
||||
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
if (data == null) return;
|
||||
|
||||
this.targetType = normalizeTargetTypeExtended(data.targetType);
|
||||
this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
|
||||
this.comparison = normalizeComparison(data.comparison);
|
||||
this.referenceMode = normalizeReferenceMode(data.referenceMode);
|
||||
this.referenceConstantValue = data.referenceConstantValue;
|
||||
this.referenceTargetType = normalizeTargetTypeExtended(data.referenceTargetType);
|
||||
this.setReferenceVariableToken(normalizeVariableToken((data.referenceVariableToken != null) ? data.referenceVariableToken : ((data.referenceVariableItemId > 0) ? String.valueOf(data.referenceVariableItemId) : "")));
|
||||
this.userSource = normalizeUserSource(data.userSource);
|
||||
this.furniSource = normalizeFurniSource(data.furniSource);
|
||||
this.referenceUserSource = normalizeUserSource(data.referenceUserSource);
|
||||
this.referenceFurniSource = normalizeReferenceFurniSource(data.referenceFurniSource);
|
||||
this.quantifier = normalizeQuantifier(data.quantifier);
|
||||
|
||||
if (room == null) return;
|
||||
|
||||
this.selectedItems.addAll(this.parseItems(data.selectedItemIds, room));
|
||||
this.referenceSelectedItems.addAll(this.parseItems(data.referenceSelectedItemIds, room));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
super.onPickUp();
|
||||
this.comparison = COMPARISON_EQUAL;
|
||||
this.referenceMode = REFERENCE_CONSTANT;
|
||||
this.referenceConstantValue = 0;
|
||||
this.referenceTargetType = TARGET_USER;
|
||||
this.referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.referenceSelectedItems.clear();
|
||||
this.setReferenceVariableToken("");
|
||||
}
|
||||
|
||||
public boolean requiresTriggeringUser() {
|
||||
return (this.targetType == TARGET_USER && this.userSource == WiredSourceUtil.SOURCE_TRIGGER)
|
||||
|| (this.referenceMode == REFERENCE_VARIABLE && this.referenceTargetType == TARGET_USER && this.referenceUserSource == WiredSourceUtil.SOURCE_TRIGGER);
|
||||
}
|
||||
|
||||
private boolean evaluateUserTargets(WiredContext ctx, Room room) {
|
||||
List<RoomUnit> targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
|
||||
if (targets.isEmpty()) return false;
|
||||
|
||||
ReferenceSnapshot references = this.resolveReferences(ctx, room);
|
||||
|
||||
if (this.quantifier == QUANTIFIER_ANY) {
|
||||
int index = 0;
|
||||
for (RoomUnit roomUnit : targets) {
|
||||
Integer currentValue = this.readUserValue(room, roomUnit);
|
||||
Integer referenceValue = this.referenceFor(references, roomUnit != null ? roomUnit.getId() : 0, TARGET_USER, index++);
|
||||
|
||||
if (this.matchesComparison(currentValue, referenceValue)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (RoomUnit roomUnit : targets) {
|
||||
Integer currentValue = this.readUserValue(room, roomUnit);
|
||||
Integer referenceValue = this.referenceFor(references, roomUnit != null ? roomUnit.getId() : 0, TARGET_USER, index++);
|
||||
|
||||
if (!this.matchesComparison(currentValue, referenceValue)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean evaluateFurniTargets(WiredContext ctx, Room room) {
|
||||
this.refresh();
|
||||
|
||||
List<HabboItem> targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.selectedItems);
|
||||
if (targets.isEmpty()) return false;
|
||||
|
||||
ReferenceSnapshot references = this.resolveReferences(ctx, room);
|
||||
|
||||
if (this.quantifier == QUANTIFIER_ANY) {
|
||||
int index = 0;
|
||||
for (HabboItem item : targets) {
|
||||
Integer currentValue = this.readFurniValue(room, item);
|
||||
Integer referenceValue = this.referenceFor(references, item != null ? item.getId() : 0, TARGET_FURNI, index++);
|
||||
|
||||
if (this.matchesComparison(currentValue, referenceValue)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (HabboItem item : targets) {
|
||||
Integer currentValue = this.readFurniValue(room, item);
|
||||
Integer referenceValue = this.referenceFor(references, item != null ? item.getId() : 0, TARGET_FURNI, index++);
|
||||
|
||||
if (!this.matchesComparison(currentValue, referenceValue)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean evaluateRoomTarget(WiredContext ctx, Room room) {
|
||||
Integer currentValue = this.readRoomValue(room);
|
||||
Integer referenceValue = this.referenceFor(this.resolveReferences(ctx, room), room.getId(), TARGET_ROOM, 0);
|
||||
|
||||
return this.matchesComparison(currentValue, referenceValue);
|
||||
}
|
||||
|
||||
private boolean evaluateContextTarget(WiredContext ctx, Room room) {
|
||||
Integer currentValue = this.readContextTargetValue(ctx, room);
|
||||
Integer referenceValue = this.referenceFor(this.resolveReferences(ctx, room), this.variableItemId, TARGET_CONTEXT, 0);
|
||||
|
||||
return this.matchesComparison(currentValue, referenceValue);
|
||||
}
|
||||
|
||||
private ReferenceSnapshot resolveReferences(WiredContext ctx, Room room) {
|
||||
if (this.referenceMode != REFERENCE_VARIABLE) return null;
|
||||
|
||||
return switch (this.referenceTargetType) {
|
||||
case TARGET_USER -> this.userReferences(ctx, room);
|
||||
case TARGET_FURNI -> this.furniReferences(ctx, room);
|
||||
case TARGET_CONTEXT -> this.contextReferences(ctx, room);
|
||||
case TARGET_ROOM -> this.roomReferences(room);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private ReferenceSnapshot userReferences(WiredContext ctx, Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_USER);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseUserInternalReference(key)) return null;
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
|
||||
Integer value = this.readUserInternalValue(room, roomUnit, key);
|
||||
if (value != null && roomUnit != null) snapshot.add(roomUnit.getId(), value);
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
|
||||
if (roomUnit == null) continue;
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (habbo != null) snapshot.add(roomUnit.getId(), room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.referenceVariableItemId));
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot furniReferences(WiredContext ctx, Room room) {
|
||||
int source = (this.referenceFurniSource == SOURCE_SECONDARY_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : this.referenceFurniSource;
|
||||
if (source == WiredSourceUtil.SOURCE_SELECTED) this.refreshReferenceItems();
|
||||
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_FURNI);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseFurniInternalReference(key)) return null;
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedItems)) {
|
||||
Integer value = this.readFurniInternalValue(room, item, key);
|
||||
if (value != null && item != null) snapshot.add(item.getId(), value);
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedItems)) {
|
||||
if (item != null) snapshot.add(item.getId(), room.getFurniVariableManager().getCurrentValue(item.getId(), this.referenceVariableItemId));
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot roomReferences(Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_ROOM);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseRoomInternalReference(key)) return null;
|
||||
|
||||
Integer value = this.readRoomInternalValue(room, key);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(room.getId(), value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
snapshot.add(room.getId(), room.getRoomVariableManager().getCurrentValue(this.referenceVariableItemId));
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot contextReferences(WiredContext ctx, Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_CONTEXT);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseContextInternalReference(key)) return null;
|
||||
|
||||
Integer value = WiredInternalVariableSupport.readContextValue(ctx, key);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(this.referenceVariableItemId > 0 ? this.referenceVariableItemId : (room != null ? room.getId() : 0), value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue() || !WiredContextVariableSupport.hasVariable(ctx, this.referenceVariableItemId)) return null;
|
||||
|
||||
Integer value = WiredContextVariableSupport.getCurrentValue(ctx, this.referenceVariableItemId);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(this.referenceVariableItemId, value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private Integer readUserValue(Room room, RoomUnit roomUnit) {
|
||||
if (room == null || roomUnit == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
String key = getInternalVariableKey(this.variableToken);
|
||||
return canUseUserInternalReference(key) ? this.readUserInternalValue(room, roomUnit, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
return (habbo != null) ? room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.variableItemId) : null;
|
||||
}
|
||||
|
||||
private Integer readFurniValue(Room room, HabboItem item) {
|
||||
if (room == null || item == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
String key = getInternalVariableKey(this.variableToken);
|
||||
return canUseFurniInternalReference(key) ? this.readFurniInternalValue(room, item, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
return (definition != null && definition.hasValue()) ? room.getFurniVariableManager().getCurrentValue(item.getId(), this.variableItemId) : null;
|
||||
}
|
||||
|
||||
private Integer readRoomValue(Room room) {
|
||||
if (room == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
String key = getInternalVariableKey(this.variableToken);
|
||||
return canUseRoomInternalReference(key) ? this.readRoomInternalValue(room, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
return (definition != null && definition.hasValue()) ? room.getRoomVariableManager().getCurrentValue(this.variableItemId) : null;
|
||||
}
|
||||
|
||||
private Integer readContextTargetValue(WiredContext ctx, Room room) {
|
||||
if (ctx == null || room == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
String key = getInternalVariableKey(this.variableToken);
|
||||
return canUseContextInternalReference(key) ? WiredInternalVariableSupport.readContextValue(ctx, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.variableItemId);
|
||||
if (definition == null || !definition.hasValue() || !WiredContextVariableSupport.hasVariable(ctx, this.variableItemId)) return null;
|
||||
|
||||
return WiredContextVariableSupport.getCurrentValue(ctx, this.variableItemId);
|
||||
}
|
||||
|
||||
private Integer referenceFor(ReferenceSnapshot snapshot, int destinationEntityId, int destinationTarget, int destinationIndex) {
|
||||
if (this.referenceMode != REFERENCE_VARIABLE) return this.referenceConstantValue;
|
||||
if (snapshot == null || snapshot.isEmpty()) return null;
|
||||
if (snapshot.targetType == destinationTarget && snapshot.values.containsKey(destinationEntityId)) return snapshot.values.get(destinationEntityId);
|
||||
if (destinationIndex >= 0 && destinationIndex < snapshot.values.size()) return new ArrayList<>(snapshot.values.values()).get(destinationIndex);
|
||||
return new ArrayList<>(snapshot.values.values()).get(0);
|
||||
}
|
||||
|
||||
private boolean matchesComparison(Integer currentValue, Integer referenceValue) {
|
||||
if (currentValue == null || referenceValue == null) return false;
|
||||
|
||||
return switch (this.comparison) {
|
||||
case COMPARISON_GREATER_THAN -> currentValue > referenceValue;
|
||||
case COMPARISON_GREATER_THAN_OR_EQUAL -> currentValue >= referenceValue;
|
||||
case COMPARISON_LESS_THAN_OR_EQUAL -> currentValue <= referenceValue;
|
||||
case COMPARISON_LESS_THAN -> currentValue < referenceValue;
|
||||
case COMPARISON_NOT_EQUAL -> !currentValue.equals(referenceValue);
|
||||
default -> currentValue.equals(referenceValue);
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isValidSource(Room room, int targetType, String variableToken) {
|
||||
if (variableToken == null || variableToken.isEmpty()) return false;
|
||||
|
||||
return switch (targetType) {
|
||||
case TARGET_USER -> isInternalVariableToken(variableToken)
|
||||
? canUseUserInternalReference(getInternalVariableKey(variableToken))
|
||||
: this.isValidUserCustomValue(room, getCustomItemId(variableToken));
|
||||
case TARGET_FURNI -> isInternalVariableToken(variableToken)
|
||||
? canUseFurniInternalReference(getInternalVariableKey(variableToken))
|
||||
: this.isValidFurniCustomValue(room, getCustomItemId(variableToken));
|
||||
case TARGET_CONTEXT -> isInternalVariableToken(variableToken)
|
||||
? canUseContextInternalReference(getInternalVariableKey(variableToken))
|
||||
: this.isValidContextCustomValue(room, getCustomItemId(variableToken));
|
||||
case TARGET_ROOM -> isInternalVariableToken(variableToken)
|
||||
? canUseRoomInternalReference(getInternalVariableKey(variableToken))
|
||||
: this.isValidRoomCustomValue(room, getCustomItemId(variableToken));
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isValidReference(Room room, int targetType, String variableToken) {
|
||||
return this.isValidSource(room, targetType, variableToken);
|
||||
}
|
||||
|
||||
private boolean isValidUserCustomValue(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getUserVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private boolean isValidFurniCustomValue(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getFurniVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private boolean isValidRoomCustomValue(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getRoomVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private boolean isValidContextCustomValue(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, variableItemId);
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private Integer readUserInternalValue(Room room, RoomUnit roomUnit, String key) {
|
||||
return WiredInternalVariableSupport.readUserValue(room, roomUnit, key);
|
||||
}
|
||||
|
||||
private Integer readFurniInternalValue(Room room, HabboItem item, String key) {
|
||||
return WiredInternalVariableSupport.readFurniValue(room, item, key);
|
||||
}
|
||||
|
||||
private Integer readRoomInternalValue(Room room, String key) {
|
||||
return WiredInternalVariableSupport.readRoomValue(room, key);
|
||||
}
|
||||
|
||||
private Integer getUserTeamScore(Room room, Habbo habbo) {
|
||||
if (room == null || habbo == null || habbo.getHabboInfo().getGamePlayer() == null) return null;
|
||||
|
||||
Game game = this.resolveTeamGame(room, habbo);
|
||||
GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer();
|
||||
|
||||
if (game == null || gamePlayer.getTeamColor() == null) return gamePlayer.getScore();
|
||||
|
||||
GameTeam team = game.getTeam(gamePlayer.getTeamColor());
|
||||
return (team != null) ? team.getTotalScore() : gamePlayer.getScore();
|
||||
}
|
||||
|
||||
private Integer getTeamColorId(int effectId) {
|
||||
TeamEffectData data = this.getTeamEffectData(effectId);
|
||||
return data == null ? null : data.colorId;
|
||||
}
|
||||
|
||||
private Integer getTeamTypeId(int effectId) {
|
||||
TeamEffectData data = this.getTeamEffectData(effectId);
|
||||
return data == null ? null : data.typeId;
|
||||
}
|
||||
|
||||
private int getTeamMetric(Room room, GameTeamColors color, boolean score) {
|
||||
Game game = this.resolveTeamGame(room, null);
|
||||
if (game == null || color == null) return 0;
|
||||
|
||||
GameTeam team = game.getTeam(color);
|
||||
if (team == null) return 0;
|
||||
|
||||
return score ? team.getTotalScore() : team.getMembers().size();
|
||||
}
|
||||
|
||||
private Game resolveTeamGame(Room room, Habbo habbo) {
|
||||
if (room == null) return null;
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getCurrentGame() != null) {
|
||||
Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
|
||||
if (game != null) return game;
|
||||
}
|
||||
|
||||
Game wiredGame = room.getGame(WiredGame.class);
|
||||
if (wiredGame != null) return wiredGame;
|
||||
|
||||
Game freezeGame = room.getGame(FreezeGame.class);
|
||||
if (freezeGame != null) return freezeGame;
|
||||
|
||||
return room.getGame(BattleBanzaiGame.class);
|
||||
}
|
||||
|
||||
private List<HabboItem> parseItems(int[] ids, Room room) {
|
||||
List<HabboItem> items = new ArrayList<>();
|
||||
if (ids == null || room == null) return items;
|
||||
|
||||
for (int id : ids) {
|
||||
HabboItem item = room.getHabboItem(id);
|
||||
if (item != null) items.add(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<HabboItem> parseItems(List<Integer> ids, Room room) {
|
||||
List<HabboItem> items = new ArrayList<>();
|
||||
if (ids == null || room == null) return items;
|
||||
|
||||
for (Integer id : ids) {
|
||||
if (id == null || id <= 0) continue;
|
||||
|
||||
HabboItem item = room.getHabboItem(id);
|
||||
if (item != null) items.add(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<HabboItem> parseItems(String ids, Room room) {
|
||||
List<HabboItem> items = new ArrayList<>();
|
||||
if (ids == null || ids.trim().isEmpty() || room == null) return items;
|
||||
|
||||
for (String part : ids.split("[;,\\t]")) {
|
||||
int id = parseInteger(part);
|
||||
if (id <= 0) continue;
|
||||
|
||||
HabboItem item = room.getHabboItem(id);
|
||||
if (item != null) items.add(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private void refreshReferenceItems() {
|
||||
THashSet<HabboItem> staleItems = new THashSet<>();
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
staleItems.addAll(this.referenceSelectedItems);
|
||||
} else {
|
||||
for (HabboItem item : this.referenceSelectedItems) {
|
||||
if (item == null || item.getRoomId() != room.getId() || room.getHabboItem(item.getId()) == null) {
|
||||
staleItems.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.referenceSelectedItems.removeAll(staleItems);
|
||||
}
|
||||
|
||||
private String serializeStringData() {
|
||||
return (this.variableToken == null ? "" : this.variableToken) + DELIM + (this.referenceVariableToken == null ? "" : this.referenceVariableToken) + DELIM + this.serializeIds(this.referenceSelectedItems);
|
||||
}
|
||||
|
||||
private String[] parseStringData(String value) {
|
||||
return (value == null || value.isEmpty()) ? new String[0] : value.split("\\t", -1);
|
||||
}
|
||||
|
||||
private List<Integer> toIds(THashSet<HabboItem> items) {
|
||||
List<Integer> ids = new ArrayList<>();
|
||||
for (HabboItem item : items) {
|
||||
if (item != null) ids.add(item.getId());
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private String serializeIds(THashSet<HabboItem> items) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (HabboItem item : items) {
|
||||
if (item == null) continue;
|
||||
if (builder.length() > 0) builder.append(FURNI_DELIM);
|
||||
builder.append(item.getId());
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void setReferenceVariableToken(String token) {
|
||||
this.referenceVariableToken = normalizeVariableToken(token);
|
||||
this.referenceVariableItemId = getCustomItemId(this.referenceVariableToken);
|
||||
}
|
||||
|
||||
private static boolean canUseUserInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseFurniInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseRoomInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseRoomReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseContextInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseContextReference(key);
|
||||
}
|
||||
|
||||
private static int param(int[] params, int index, int fallback) {
|
||||
return (params.length > index) ? params[index] : fallback;
|
||||
}
|
||||
|
||||
private static int normalizeTargetTypeExtended(int value) {
|
||||
return switch (value) {
|
||||
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
|
||||
default -> TARGET_USER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeReferenceMode(int value) {
|
||||
return (value == REFERENCE_VARIABLE) ? REFERENCE_VARIABLE : REFERENCE_CONSTANT;
|
||||
}
|
||||
|
||||
private static int normalizeReferenceFurniSource(int value) {
|
||||
return switch (value) {
|
||||
case SOURCE_SECONDARY_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
|
||||
default -> WiredSourceUtil.SOURCE_TRIGGER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeComparison(int value) {
|
||||
return switch (value) {
|
||||
case COMPARISON_GREATER_THAN, COMPARISON_GREATER_THAN_OR_EQUAL, COMPARISON_LESS_THAN_OR_EQUAL, COMPARISON_LESS_THAN, COMPARISON_NOT_EQUAL -> value;
|
||||
default -> COMPARISON_EQUAL;
|
||||
};
|
||||
}
|
||||
|
||||
private static int parseInteger(String value) {
|
||||
try {
|
||||
return (value == null || value.trim().isEmpty()) ? 0 : Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int targetType, variableItemId, comparison, referenceMode, referenceConstantValue, referenceTargetType, referenceVariableItemId, userSource, furniSource, referenceUserSource, referenceFurniSource, quantifier;
|
||||
String variableToken, referenceVariableToken;
|
||||
List<Integer> selectedItemIds, referenceSelectedItemIds;
|
||||
|
||||
JsonData(int targetType, String variableToken, int variableItemId, int comparison, int referenceMode, int referenceConstantValue, int referenceTargetType, String referenceVariableToken, int referenceVariableItemId, int userSource, int furniSource, int referenceUserSource, int referenceFurniSource, int quantifier, List<Integer> selectedItemIds, List<Integer> referenceSelectedItemIds) {
|
||||
this.targetType = targetType;
|
||||
this.variableToken = variableToken;
|
||||
this.variableItemId = variableItemId;
|
||||
this.comparison = comparison;
|
||||
this.referenceMode = referenceMode;
|
||||
this.referenceConstantValue = referenceConstantValue;
|
||||
this.referenceTargetType = referenceTargetType;
|
||||
this.referenceVariableToken = referenceVariableToken;
|
||||
this.referenceVariableItemId = referenceVariableItemId;
|
||||
this.userSource = userSource;
|
||||
this.furniSource = furniSource;
|
||||
this.referenceUserSource = referenceUserSource;
|
||||
this.referenceFurniSource = referenceFurniSource;
|
||||
this.quantifier = quantifier;
|
||||
this.selectedItemIds = selectedItemIds;
|
||||
this.referenceSelectedItemIds = referenceSelectedItemIds;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReferenceSnapshot {
|
||||
final int targetType;
|
||||
final LinkedHashMap<Integer, Integer> values = new LinkedHashMap<>();
|
||||
|
||||
ReferenceSnapshot(int targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
void add(int entityId, int value) {
|
||||
this.values.put(entityId, value);
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return this.values.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
+932
@@ -0,0 +1,932 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.games.Game;
|
||||
import com.eu.habbo.habbohotel.games.GamePlayer;
|
||||
import com.eu.habbo.habbohotel.games.GameTeam;
|
||||
import com.eu.habbo.habbohotel.games.GameTeamColors;
|
||||
import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame;
|
||||
import com.eu.habbo.habbohotel.games.freeze.FreezeGame;
|
||||
import com.eu.habbo.habbohotel.games.wired.WiredGame;
|
||||
import com.eu.habbo.habbohotel.items.FurnitureType;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.FurnitureMovementError;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTileState;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||
import com.eu.habbo.util.HotelDateTimeUtil;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class WiredEffectChangeVariableValue extends InteractionWiredEffect {
|
||||
public static final WiredEffectType type = WiredEffectType.CHANGE_VAR_VAL;
|
||||
public static final int TARGET_USER = 0, TARGET_FURNI = 1, TARGET_CONTEXT = 2, TARGET_ROOM = 3;
|
||||
public static final int REF_CONSTANT = 0, REF_VARIABLE = 1;
|
||||
public static final int OP_ASSIGN = 0, OP_ADD = 1, OP_SUB = 2, OP_MUL = 3, OP_DIV = 4, OP_POW = 5, OP_MOD = 6, OP_MIN = 40, OP_MAX = 41, OP_RANDOM = 50, OP_ABS = 60, OP_AND = 100, OP_OR = 101, OP_XOR = 102, OP_NOT = 103, OP_LSHIFT = 104, OP_RSHIFT = 105;
|
||||
|
||||
private static final int SOURCE_SECONDARY_SELECTED = 101;
|
||||
private static final String DELIM = "\t", FURNI_DELIM = ";";
|
||||
private static final String CUSTOM_TOKEN_PREFIX = "custom:";
|
||||
private static final String INTERNAL_TOKEN_PREFIX = "internal:";
|
||||
|
||||
private int destinationTargetType = TARGET_USER, destinationVariableItemId = 0, operation = OP_ASSIGN, referenceMode = REF_CONSTANT, referenceConstantValue = 0, referenceTargetType = TARGET_USER, referenceVariableItemId = 0, destinationUserSource = WiredSourceUtil.SOURCE_TRIGGER, destinationFurniSource = WiredSourceUtil.SOURCE_TRIGGER, referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER, referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
private String destinationVariableToken = "", referenceVariableToken = "";
|
||||
private final List<HabboItem> destinationSelectedFurni = new ArrayList<>();
|
||||
private final List<HabboItem> referenceSelectedFurni = new ArrayList<>();
|
||||
|
||||
public WiredEffectChangeVariableValue(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredEffectChangeVariableValue(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
if (room == null) return;
|
||||
|
||||
switch (this.destinationTargetType) {
|
||||
case TARGET_USER -> this.executeUsers(ctx, room);
|
||||
case TARGET_FURNI -> this.executeFurni(ctx, room);
|
||||
case TARGET_CONTEXT -> this.executeContext(ctx, room);
|
||||
case TARGET_ROOM -> this.executeRoom(ctx, room);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeUsers(WiredContext ctx, Room room) {
|
||||
if (isInternalVariableToken(this.destinationVariableToken)) {
|
||||
this.executeUsersInternal(ctx, room, getInternalVariableKey(this.destinationVariableToken));
|
||||
return;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.destinationVariableItemId);
|
||||
if (definition == null || !definition.hasValue() || definition.isReadOnly()) return;
|
||||
|
||||
ReferenceSnapshot references = this.resolveReferences(ctx, room);
|
||||
int index = 0;
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.destinationUserSource)) {
|
||||
if (roomUnit == null) continue;
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (habbo == null) continue;
|
||||
|
||||
Integer referenceValue = this.referenceFor(references, roomUnit.getId(), TARGET_USER, index++);
|
||||
if (!this.isUnaryOperation() && referenceValue == null) continue;
|
||||
|
||||
int currentValue = room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.destinationVariableItemId);
|
||||
room.getUserVariableManager().updateVariableValue(habbo.getHabboInfo().getId(), this.destinationVariableItemId, this.applyOperation(currentValue, referenceValue));
|
||||
}
|
||||
}
|
||||
|
||||
private void executeUsersInternal(WiredContext ctx, Room room, String key) {
|
||||
if (!canUseUserInternalDestination(key)) return;
|
||||
|
||||
ReferenceSnapshot references = this.resolveReferences(ctx, room);
|
||||
int index = 0;
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.destinationUserSource)) {
|
||||
if (roomUnit == null) continue;
|
||||
|
||||
Integer currentValue = this.readUserInternalValue(room, roomUnit, key);
|
||||
if (currentValue == null) continue;
|
||||
|
||||
Integer referenceValue = this.referenceFor(references, roomUnit.getId(), TARGET_USER, index++);
|
||||
if (!this.isUnaryOperation() && referenceValue == null) continue;
|
||||
|
||||
this.writeUserInternalValue(room, roomUnit, key, this.applyOperation(currentValue, referenceValue));
|
||||
}
|
||||
}
|
||||
|
||||
private void executeFurni(WiredContext ctx, Room room) {
|
||||
if (isInternalVariableToken(this.destinationVariableToken)) {
|
||||
this.executeFurniInternal(ctx, room, getInternalVariableKey(this.destinationVariableToken));
|
||||
return;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.destinationVariableItemId);
|
||||
if (definition == null || !definition.hasValue() || definition.isReadOnly()) return;
|
||||
if (this.destinationFurniSource == WiredSourceUtil.SOURCE_SELECTED) this.validateItems(this.destinationSelectedFurni);
|
||||
|
||||
ReferenceSnapshot references = this.resolveReferences(ctx, room);
|
||||
int index = 0;
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, this.destinationFurniSource, this.destinationSelectedFurni)) {
|
||||
if (item == null) continue;
|
||||
|
||||
Integer referenceValue = this.referenceFor(references, item.getId(), TARGET_FURNI, index++);
|
||||
if (!this.isUnaryOperation() && referenceValue == null) continue;
|
||||
|
||||
int currentValue = room.getFurniVariableManager().getCurrentValue(item.getId(), this.destinationVariableItemId);
|
||||
room.getFurniVariableManager().updateVariableValue(item.getId(), this.destinationVariableItemId, this.applyOperation(currentValue, referenceValue));
|
||||
}
|
||||
}
|
||||
|
||||
private void executeFurniInternal(WiredContext ctx, Room room, String key) {
|
||||
if (!canUseFurniInternalDestination(key)) return;
|
||||
if (this.destinationFurniSource == WiredSourceUtil.SOURCE_SELECTED) this.validateItems(this.destinationSelectedFurni);
|
||||
|
||||
ReferenceSnapshot references = this.resolveReferences(ctx, room);
|
||||
int index = 0;
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, this.destinationFurniSource, this.destinationSelectedFurni)) {
|
||||
if (item == null) continue;
|
||||
|
||||
Integer currentValue = this.readFurniInternalValue(room, item, key);
|
||||
if (currentValue == null) continue;
|
||||
|
||||
Integer referenceValue = this.referenceFor(references, item.getId(), TARGET_FURNI, index++);
|
||||
if (!this.isUnaryOperation() && referenceValue == null) continue;
|
||||
|
||||
this.writeFurniInternalValue(room, item, key, this.applyOperation(currentValue, referenceValue));
|
||||
}
|
||||
}
|
||||
|
||||
private void executeRoom(WiredContext ctx, Room room) {
|
||||
if (isInternalVariableToken(this.destinationVariableToken)) return;
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.destinationVariableItemId);
|
||||
if (definition == null || !definition.hasValue() || definition.isReadOnly()) return;
|
||||
|
||||
ReferenceSnapshot references = this.resolveReferences(ctx, room);
|
||||
Integer referenceValue = this.referenceFor(references, room.getId(), TARGET_ROOM, 0);
|
||||
if (!this.isUnaryOperation() && referenceValue == null) return;
|
||||
|
||||
int currentValue = room.getRoomVariableManager().getCurrentValue(this.destinationVariableItemId);
|
||||
room.getRoomVariableManager().updateVariableValue(this.destinationVariableItemId, this.applyOperation(currentValue, referenceValue));
|
||||
}
|
||||
|
||||
private void executeContext(WiredContext ctx, Room room) {
|
||||
if (isInternalVariableToken(this.destinationVariableToken)) return;
|
||||
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.destinationVariableItemId);
|
||||
if (definition == null || !definition.hasValue() || definition.isReadOnly()) return;
|
||||
|
||||
ReferenceSnapshot references = this.resolveReferences(ctx, room);
|
||||
Integer referenceValue = this.referenceFor(references, this.destinationVariableItemId, TARGET_CONTEXT, 0);
|
||||
if (!this.isUnaryOperation() && referenceValue == null) return;
|
||||
if (!WiredContextVariableSupport.hasVariable(ctx, this.destinationVariableItemId)) return;
|
||||
|
||||
Integer currentValue = WiredContextVariableSupport.getCurrentValue(ctx, this.destinationVariableItemId);
|
||||
int nextValue = this.applyOperation(currentValue != null ? currentValue : 0, referenceValue);
|
||||
WiredContextVariableSupport.updateVariableValue(ctx, room, this.destinationVariableItemId, nextValue);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
this.validateItems(this.destinationSelectedFurni);
|
||||
this.validateItems(this.referenceSelectedFurni);
|
||||
|
||||
List<HabboItem> selectedItems = new ArrayList<>();
|
||||
if (this.destinationTargetType == TARGET_FURNI && this.destinationFurniSource == WiredSourceUtil.SOURCE_SELECTED) selectedItems.addAll(this.destinationSelectedFurni);
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(selectedItems.size());
|
||||
for (HabboItem item : selectedItems) message.appendInt(item.getId());
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.serializeStringData());
|
||||
message.appendInt(9);
|
||||
message.appendInt(this.destinationTargetType);
|
||||
message.appendInt(this.operation);
|
||||
message.appendInt(this.referenceMode);
|
||||
message.appendInt(this.referenceConstantValue);
|
||||
message.appendInt(this.referenceTargetType);
|
||||
message.appendInt(this.destinationUserSource);
|
||||
message.appendInt(this.destinationFurniSource);
|
||||
message.appendInt(this.referenceUserSource);
|
||||
message.appendInt(this.referenceFurniSource);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getType().code);
|
||||
message.appendInt(this.getDelay());
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = this.getRoom();
|
||||
if (room == null) throw new WiredSaveException("Room not found");
|
||||
|
||||
int[] params = settings.getIntParams();
|
||||
String[] stringParts = this.parseStringData(settings.getStringParam());
|
||||
int nextDestinationTargetType = normalizeTargetType(param(params, 0, TARGET_USER));
|
||||
int nextOperation = normalizeOperation(param(params, 1, OP_ASSIGN));
|
||||
int nextReferenceMode = normalizeReferenceMode(param(params, 2, REF_CONSTANT));
|
||||
int nextReferenceConstantValue = param(params, 3, 0);
|
||||
int nextReferenceTargetType = normalizeTargetType(param(params, 4, TARGET_USER));
|
||||
int nextDestinationUserSource = normalizeUserSource(param(params, 5, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextDestinationFurniSource = normalizeDestinationFurniSource(param(params, 6, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextReferenceUserSource = normalizeUserSource(param(params, 7, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextReferenceFurniSource = normalizeReferenceFurniSource(param(params, 8, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
String nextDestinationVariableToken = normalizeVariableToken((stringParts.length > 0) ? stringParts[0] : "");
|
||||
String nextReferenceVariableToken = normalizeVariableToken((stringParts.length > 1) ? stringParts[1] : "");
|
||||
|
||||
this.validateDestination(room, nextDestinationTargetType, nextDestinationVariableToken);
|
||||
if (nextReferenceMode == REF_VARIABLE) this.validateReference(room, nextReferenceTargetType, nextReferenceVariableToken);
|
||||
|
||||
int maxDelay = Emulator.getConfig().getInt("hotel.wired.max_delay", 20);
|
||||
if (settings.getDelay() > maxDelay) throw new WiredSaveException("Delay too long");
|
||||
|
||||
List<HabboItem> nextDestinationItems = (nextDestinationTargetType == TARGET_FURNI && nextDestinationFurniSource == WiredSourceUtil.SOURCE_SELECTED) ? this.parseItems(settings.getFurniIds(), room) : new ArrayList<>();
|
||||
List<HabboItem> nextReferenceItems = (nextReferenceMode == REF_VARIABLE && nextReferenceTargetType == TARGET_FURNI && nextReferenceFurniSource == SOURCE_SECONDARY_SELECTED) ? this.parseItems((stringParts.length > 2) ? stringParts[2] : "", room) : new ArrayList<>();
|
||||
int selectionLimit = Emulator.getConfig().getInt("hotel.wired.furni.selection.count");
|
||||
if (nextDestinationItems.size() > selectionLimit || nextReferenceItems.size() > selectionLimit) throw new WiredSaveException("Too many furni selected");
|
||||
|
||||
this.destinationSelectedFurni.clear();
|
||||
this.destinationSelectedFurni.addAll(nextDestinationItems);
|
||||
this.referenceSelectedFurni.clear();
|
||||
this.referenceSelectedFurni.addAll(nextReferenceItems);
|
||||
this.destinationTargetType = nextDestinationTargetType;
|
||||
this.setDestinationVariableToken(nextDestinationVariableToken);
|
||||
this.operation = nextOperation;
|
||||
this.referenceMode = nextReferenceMode;
|
||||
this.referenceConstantValue = nextReferenceConstantValue;
|
||||
this.referenceTargetType = nextReferenceTargetType;
|
||||
this.setReferenceVariableToken(nextReferenceVariableToken);
|
||||
this.destinationUserSource = nextDestinationUserSource;
|
||||
this.destinationFurniSource = nextDestinationFurniSource;
|
||||
this.referenceUserSource = nextReferenceUserSource;
|
||||
this.referenceFurniSource = nextReferenceFurniSource;
|
||||
this.setDelay(settings.getDelay());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.destinationTargetType, this.destinationVariableToken, this.destinationVariableItemId, this.operation, this.referenceMode, this.referenceConstantValue, this.referenceTargetType, this.referenceVariableToken, this.referenceVariableItemId, this.destinationUserSource, this.destinationFurniSource, this.referenceUserSource, this.referenceFurniSource, this.getDelay(), this.toIds(this.destinationSelectedFurni), this.toIds(this.referenceSelectedFurni)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) return;
|
||||
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
if (data == null) return;
|
||||
|
||||
this.destinationTargetType = normalizeTargetType(data.destinationTargetType);
|
||||
this.setDestinationVariableToken(normalizeVariableToken((data.destinationVariableToken != null) ? data.destinationVariableToken : ((data.destinationVariableItemId > 0) ? String.valueOf(data.destinationVariableItemId) : "")));
|
||||
this.operation = normalizeOperation(data.operation);
|
||||
this.referenceMode = normalizeReferenceMode(data.referenceMode);
|
||||
this.referenceConstantValue = data.referenceConstantValue;
|
||||
this.referenceTargetType = normalizeTargetType(data.referenceTargetType);
|
||||
this.setReferenceVariableToken(normalizeVariableToken((data.referenceVariableToken != null) ? data.referenceVariableToken : ((data.referenceVariableItemId > 0) ? String.valueOf(data.referenceVariableItemId) : "")));
|
||||
this.destinationUserSource = normalizeUserSource(data.destinationUserSource);
|
||||
this.destinationFurniSource = normalizeDestinationFurniSource(data.destinationFurniSource);
|
||||
this.referenceUserSource = normalizeUserSource(data.referenceUserSource);
|
||||
this.referenceFurniSource = normalizeReferenceFurniSource(data.referenceFurniSource);
|
||||
this.setDelay(Math.max(0, data.delay));
|
||||
|
||||
if (room != null) {
|
||||
try {
|
||||
this.destinationSelectedFurni.addAll(this.parseItems(data.destinationSelectedFurniIds, room));
|
||||
this.referenceSelectedFurni.addAll(this.parseItems(data.referenceSelectedFurniIds, room));
|
||||
} catch (WiredSaveException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.destinationTargetType = TARGET_USER;
|
||||
this.setDestinationVariableToken("");
|
||||
this.operation = OP_ASSIGN;
|
||||
this.referenceMode = REF_CONSTANT;
|
||||
this.referenceConstantValue = 0;
|
||||
this.referenceTargetType = TARGET_USER;
|
||||
this.setReferenceVariableToken("");
|
||||
this.destinationUserSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.destinationFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.destinationSelectedFurni.clear();
|
||||
this.referenceSelectedFurni.clear();
|
||||
this.setDelay(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredEffectType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresTriggeringUser() {
|
||||
return (this.destinationTargetType == TARGET_USER && this.destinationUserSource == WiredSourceUtil.SOURCE_TRIGGER)
|
||||
|| (this.referenceMode == REF_VARIABLE && this.referenceTargetType == TARGET_USER && this.referenceUserSource == WiredSourceUtil.SOURCE_TRIGGER);
|
||||
}
|
||||
|
||||
private ReferenceSnapshot resolveReferences(WiredContext ctx, Room room) {
|
||||
if (this.referenceMode != REF_VARIABLE) return null;
|
||||
|
||||
return switch (this.referenceTargetType) {
|
||||
case TARGET_USER -> this.userReferences(ctx, room);
|
||||
case TARGET_FURNI -> this.furniReferences(ctx, room);
|
||||
case TARGET_CONTEXT -> this.contextReferences(ctx, room);
|
||||
case TARGET_ROOM -> this.roomReferences(room);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private ReferenceSnapshot userReferences(WiredContext ctx, Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_USER);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseUserInternalReference(key)) return null;
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
|
||||
if (roomUnit == null) continue;
|
||||
|
||||
Integer value = this.readUserInternalValue(room, roomUnit, key);
|
||||
if (value != null) snapshot.add(roomUnit.getId(), value);
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
|
||||
if (roomUnit == null) continue;
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (habbo != null) snapshot.add(roomUnit.getId(), room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.referenceVariableItemId));
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot furniReferences(WiredContext ctx, Room room) {
|
||||
int source = (this.referenceFurniSource == SOURCE_SECONDARY_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : this.referenceFurniSource;
|
||||
if (source == WiredSourceUtil.SOURCE_SELECTED) this.validateItems(this.referenceSelectedFurni);
|
||||
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_FURNI);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseFurniInternalReference(key)) return null;
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedFurni)) {
|
||||
if (item == null) continue;
|
||||
|
||||
Integer value = this.readFurniInternalValue(room, item, key);
|
||||
if (value != null) snapshot.add(item.getId(), value);
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedFurni)) {
|
||||
if (item != null) snapshot.add(item.getId(), room.getFurniVariableManager().getCurrentValue(item.getId(), this.referenceVariableItemId));
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot roomReferences(Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_ROOM);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseRoomInternalReference(key)) return null;
|
||||
|
||||
Integer value = this.readRoomInternalValue(room, key);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(room.getId(), value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
snapshot.add(room.getId(), room.getRoomVariableManager().getCurrentValue(this.referenceVariableItemId));
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot contextReferences(WiredContext ctx, Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_CONTEXT);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseContextInternalReference(key)) return null;
|
||||
|
||||
Integer value = WiredInternalVariableSupport.readContextValue(ctx, key);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(this.referenceVariableItemId > 0 ? this.referenceVariableItemId : room.getId(), value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue() || !WiredContextVariableSupport.hasVariable(ctx, this.referenceVariableItemId)) return null;
|
||||
|
||||
Integer value = WiredContextVariableSupport.getCurrentValue(ctx, this.referenceVariableItemId);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(this.referenceVariableItemId, value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private Integer referenceFor(ReferenceSnapshot snapshot, int destinationEntityId, int destinationTarget, int destinationIndex) {
|
||||
if (this.referenceMode != REF_VARIABLE) return this.referenceConstantValue;
|
||||
if (this.isUnaryOperation()) return 0;
|
||||
if (snapshot == null || snapshot.isEmpty()) return null;
|
||||
if (snapshot.targetType == destinationTarget && snapshot.values.containsKey(destinationEntityId)) return snapshot.values.get(destinationEntityId);
|
||||
if (destinationIndex >= 0 && destinationIndex < snapshot.values.size()) return new ArrayList<>(snapshot.values.values()).get(destinationIndex);
|
||||
return new ArrayList<>(snapshot.values.values()).get(0);
|
||||
}
|
||||
|
||||
private int applyOperation(int currentValue, Integer referenceValue) {
|
||||
return switch (this.operation) {
|
||||
case OP_ASSIGN -> (referenceValue != null) ? referenceValue : currentValue;
|
||||
case OP_ADD -> clamp((long) currentValue + referenceValue);
|
||||
case OP_SUB -> clamp((long) currentValue - referenceValue);
|
||||
case OP_MUL -> clamp((long) currentValue * referenceValue);
|
||||
case OP_DIV -> (referenceValue == null || referenceValue == 0) ? currentValue : (currentValue / referenceValue);
|
||||
case OP_POW -> (referenceValue == null || referenceValue < 0) ? 0 : clamp(Math.round(Math.pow(currentValue, referenceValue)));
|
||||
case OP_MOD -> (referenceValue == null || referenceValue == 0) ? currentValue : (currentValue % referenceValue);
|
||||
case OP_MIN -> (referenceValue != null) ? Math.min(currentValue, referenceValue) : currentValue;
|
||||
case OP_MAX -> (referenceValue != null) ? Math.max(currentValue, referenceValue) : currentValue;
|
||||
case OP_RANDOM -> (referenceValue == null || referenceValue <= 0) ? 0 : Emulator.getRandom().nextInt(referenceValue + 1);
|
||||
case OP_ABS -> (currentValue == Integer.MIN_VALUE) ? Integer.MAX_VALUE : Math.abs(currentValue);
|
||||
case OP_AND -> (referenceValue != null) ? (currentValue & referenceValue) : currentValue;
|
||||
case OP_OR -> (referenceValue != null) ? (currentValue | referenceValue) : currentValue;
|
||||
case OP_XOR -> (referenceValue != null) ? (currentValue ^ referenceValue) : currentValue;
|
||||
case OP_NOT -> ~currentValue;
|
||||
case OP_LSHIFT -> currentValue << shift(referenceValue);
|
||||
case OP_RSHIFT -> currentValue >> shift(referenceValue);
|
||||
default -> currentValue;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isUnaryOperation() {
|
||||
return this.operation == OP_ABS || this.operation == OP_NOT;
|
||||
}
|
||||
|
||||
private void validateDestination(Room room, int targetType, String variableToken) throws WiredSaveException {
|
||||
if (variableToken == null || variableToken.isEmpty()) throw new WiredSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
|
||||
boolean valid = switch (targetType) {
|
||||
case TARGET_USER -> isInternalVariableToken(variableToken)
|
||||
? canUseUserInternalDestination(getInternalVariableKey(variableToken))
|
||||
: this.isValidUserCustomDestination(room, getCustomItemId(variableToken));
|
||||
case TARGET_FURNI -> isInternalVariableToken(variableToken)
|
||||
? canUseFurniInternalDestination(getInternalVariableKey(variableToken))
|
||||
: this.isValidFurniCustomDestination(room, getCustomItemId(variableToken));
|
||||
case TARGET_CONTEXT -> !isInternalVariableToken(variableToken)
|
||||
&& this.isValidContextCustomDestination(room, getCustomItemId(variableToken));
|
||||
case TARGET_ROOM -> !isInternalVariableToken(variableToken) && this.isValidRoomCustomDestination(room, getCustomItemId(variableToken));
|
||||
default -> false;
|
||||
};
|
||||
|
||||
if (!valid) throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
private void validateReference(Room room, int targetType, String variableToken) throws WiredSaveException {
|
||||
if (variableToken == null || variableToken.isEmpty()) throw new WiredSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
|
||||
boolean valid = switch (targetType) {
|
||||
case TARGET_USER -> isInternalVariableToken(variableToken)
|
||||
? canUseUserInternalReference(getInternalVariableKey(variableToken))
|
||||
: this.isValidUserCustomReference(room, getCustomItemId(variableToken));
|
||||
case TARGET_FURNI -> isInternalVariableToken(variableToken)
|
||||
? canUseFurniInternalReference(getInternalVariableKey(variableToken))
|
||||
: this.isValidFurniCustomDestination(room, getCustomItemId(variableToken));
|
||||
case TARGET_CONTEXT -> isInternalVariableToken(variableToken)
|
||||
? canUseContextInternalReference(getInternalVariableKey(variableToken))
|
||||
: this.isValidContextCustomReference(room, getCustomItemId(variableToken));
|
||||
case TARGET_ROOM -> isInternalVariableToken(variableToken)
|
||||
? canUseRoomInternalReference(getInternalVariableKey(variableToken))
|
||||
: this.isValidRoomCustomReference(room, getCustomItemId(variableToken));
|
||||
default -> false;
|
||||
};
|
||||
|
||||
if (!valid) throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
private boolean isValidUserCustomDestination(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getUserVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue() && !definition.isReadOnly();
|
||||
}
|
||||
|
||||
private boolean isValidFurniCustomDestination(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getFurniVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue() && !definition.isReadOnly();
|
||||
}
|
||||
|
||||
private boolean isValidRoomCustomDestination(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getRoomVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue() && !definition.isReadOnly();
|
||||
}
|
||||
|
||||
private boolean isValidContextCustomDestination(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, variableItemId);
|
||||
return definition != null && definition.hasValue() && !definition.isReadOnly();
|
||||
}
|
||||
|
||||
private boolean isValidUserCustomReference(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getUserVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private boolean isValidRoomCustomReference(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getRoomVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private boolean isValidContextCustomReference(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, variableItemId);
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private boolean isValidFurniCustomReference(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getFurniVariableManager().getDefinitionInfo(variableItemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private Integer readUserInternalValue(Room room, RoomUnit roomUnit, String key) {
|
||||
return WiredInternalVariableSupport.readUserValue(room, roomUnit, key);
|
||||
}
|
||||
|
||||
private boolean writeUserInternalValue(Room room, RoomUnit roomUnit, String key, int value) {
|
||||
return WiredInternalVariableSupport.writeUserValue(room, roomUnit, key, value);
|
||||
}
|
||||
|
||||
private Integer readFurniInternalValue(Room room, HabboItem item, String key) {
|
||||
return WiredInternalVariableSupport.readFurniValue(room, item, key);
|
||||
}
|
||||
|
||||
private boolean writeFurniInternalValue(Room room, HabboItem item, String key, int value) {
|
||||
return WiredInternalVariableSupport.writeFurniValue(room, item, key, value);
|
||||
}
|
||||
|
||||
private Integer readRoomInternalValue(Room room, String key) {
|
||||
return WiredInternalVariableSupport.readRoomValue(room, key);
|
||||
}
|
||||
|
||||
private Integer getUserTeamScore(Room room, Habbo habbo) {
|
||||
if (room == null || habbo == null || habbo.getHabboInfo().getGamePlayer() == null) return null;
|
||||
|
||||
Game game = this.resolveTeamGame(room, habbo);
|
||||
GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer();
|
||||
|
||||
if (game == null || gamePlayer.getTeamColor() == null) return gamePlayer.getScore();
|
||||
|
||||
GameTeam team = game.getTeam(gamePlayer.getTeamColor());
|
||||
return (team != null) ? team.getTotalScore() : gamePlayer.getScore();
|
||||
}
|
||||
|
||||
private Integer getTeamColorId(int effectId) {
|
||||
TeamEffectData data = this.getTeamEffectData(effectId);
|
||||
return data == null ? null : data.colorId;
|
||||
}
|
||||
|
||||
private Integer getTeamTypeId(int effectId) {
|
||||
TeamEffectData data = this.getTeamEffectData(effectId);
|
||||
return data == null ? null : data.typeId;
|
||||
}
|
||||
|
||||
private int getTeamMetric(Room room, GameTeamColors color, boolean score) {
|
||||
Game game = this.resolveTeamGame(room, null);
|
||||
if (game == null || color == null) return 0;
|
||||
|
||||
GameTeam team = game.getTeam(color);
|
||||
if (team == null) return 0;
|
||||
|
||||
return score ? team.getTotalScore() : team.getMembers().size();
|
||||
}
|
||||
|
||||
private Game resolveTeamGame(Room room, Habbo habbo) {
|
||||
if (room == null) return null;
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getCurrentGame() != null) {
|
||||
Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
|
||||
if (game != null) return game;
|
||||
}
|
||||
|
||||
Game wiredGame = room.getGame(WiredGame.class);
|
||||
if (wiredGame != null) return wiredGame;
|
||||
|
||||
Game freezeGame = room.getGame(FreezeGame.class);
|
||||
if (freezeGame != null) return freezeGame;
|
||||
|
||||
return room.getGame(BattleBanzaiGame.class);
|
||||
}
|
||||
|
||||
private TeamEffectData getTeamEffectData(int effectValue) {
|
||||
if (effectValue <= 0) return null;
|
||||
|
||||
if (effectValue >= 223 && effectValue <= 226) return new TeamEffectData(effectValue - 222, 0);
|
||||
if (effectValue >= 33 && effectValue <= 36) return new TeamEffectData(effectValue - 32, 1);
|
||||
if (effectValue >= 40 && effectValue <= 43) return new TeamEffectData(effectValue - 39, 2);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean moveUserTo(Room room, RoomUnit roomUnit, int x, int y) {
|
||||
if (room == null || roomUnit == null || room.getLayout() == null) return false;
|
||||
|
||||
RoomTile targetTile = room.getLayout().getTile((short) x, (short) y);
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) return false;
|
||||
|
||||
double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0);
|
||||
return WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), 0, true);
|
||||
}
|
||||
|
||||
private boolean moveFurniTo(Room room, HabboItem item, int x, int y, int rotation, double z) {
|
||||
if (room == null || item == null || room.getLayout() == null) return false;
|
||||
|
||||
RoomTile targetTile = room.getLayout().getTile((short) x, (short) y);
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) return false;
|
||||
|
||||
FurnitureMovementError error = room.moveFurniTo(item, targetTile, rotation, z, null, true, true);
|
||||
return error == FurnitureMovementError.NONE;
|
||||
}
|
||||
|
||||
private List<HabboItem> parseItems(int[] ids, Room room) throws WiredSaveException {
|
||||
List<HabboItem> items = new ArrayList<>();
|
||||
if (ids == null || room == null) return items;
|
||||
|
||||
for (int id : ids) {
|
||||
HabboItem item = room.getHabboItem(id);
|
||||
if (item == null) throw new WiredSaveException(String.format("Item %s not found", id));
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<HabboItem> parseItems(List<Integer> ids, Room room) throws WiredSaveException {
|
||||
List<HabboItem> items = new ArrayList<>();
|
||||
if (ids == null || room == null) return items;
|
||||
|
||||
for (Integer id : ids) {
|
||||
if (id == null || id <= 0) continue;
|
||||
|
||||
HabboItem item = room.getHabboItem(id);
|
||||
if (item == null) throw new WiredSaveException(String.format("Item %s not found", id));
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<HabboItem> parseItems(String ids, Room room) throws WiredSaveException {
|
||||
List<HabboItem> items = new ArrayList<>();
|
||||
if (ids == null || ids.trim().isEmpty() || room == null) return items;
|
||||
|
||||
for (String part : ids.split("[;,\\t]")) {
|
||||
int id = parseInteger(part);
|
||||
if (id <= 0) continue;
|
||||
|
||||
HabboItem item = room.getHabboItem(id);
|
||||
if (item == null) throw new WiredSaveException(String.format("Item %s not found", id));
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private String serializeStringData() {
|
||||
return (this.destinationVariableToken == null ? "" : this.destinationVariableToken) + DELIM + (this.referenceVariableToken == null ? "" : this.referenceVariableToken) + DELIM + this.serializeIds(this.referenceSelectedFurni);
|
||||
}
|
||||
|
||||
private String[] parseStringData(String value) {
|
||||
return (value == null || value.isEmpty()) ? new String[0] : value.split("\\t", -1);
|
||||
}
|
||||
|
||||
private List<Integer> toIds(List<HabboItem> items) {
|
||||
List<Integer> ids = new ArrayList<>();
|
||||
for (HabboItem item : items) if (item != null) ids.add(item.getId());
|
||||
return ids;
|
||||
}
|
||||
|
||||
private String serializeIds(List<HabboItem> items) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (HabboItem item : items) {
|
||||
if (item == null) continue;
|
||||
if (builder.length() > 0) builder.append(FURNI_DELIM);
|
||||
builder.append(item.getId());
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void setDestinationVariableToken(String token) {
|
||||
this.destinationVariableToken = normalizeVariableToken(token);
|
||||
this.destinationVariableItemId = getCustomItemId(this.destinationVariableToken);
|
||||
}
|
||||
|
||||
private void setReferenceVariableToken(String token) {
|
||||
this.referenceVariableToken = normalizeVariableToken(token);
|
||||
this.referenceVariableItemId = getCustomItemId(this.referenceVariableToken);
|
||||
}
|
||||
|
||||
private static boolean isCustomVariableToken(String token) {
|
||||
return token != null && token.startsWith(CUSTOM_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
private static boolean isInternalVariableToken(String token) {
|
||||
return token != null && token.startsWith(INTERNAL_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
private static int getCustomItemId(String token) {
|
||||
if (!isCustomVariableToken(token)) return 0;
|
||||
return parseInteger(token.substring(CUSTOM_TOKEN_PREFIX.length()));
|
||||
}
|
||||
|
||||
private static String getInternalVariableKey(String token) {
|
||||
return isInternalVariableToken(token) ? WiredInternalVariableSupport.normalizeKey(token.substring(INTERNAL_TOKEN_PREFIX.length())) : "";
|
||||
}
|
||||
|
||||
private static String normalizeVariableToken(String token) {
|
||||
if (token == null) return "";
|
||||
|
||||
String normalized = token.trim();
|
||||
if (normalized.isEmpty()) return "";
|
||||
if (isCustomVariableToken(normalized)) return normalized;
|
||||
if (isInternalVariableToken(normalized)) return INTERNAL_TOKEN_PREFIX + WiredInternalVariableSupport.normalizeKey(normalized.substring(INTERNAL_TOKEN_PREFIX.length()));
|
||||
|
||||
int parsedValue = parseInteger(normalized);
|
||||
return parsedValue > 0 ? CUSTOM_TOKEN_PREFIX + parsedValue : "";
|
||||
}
|
||||
|
||||
private static boolean canUseUserInternalDestination(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserDestination(key);
|
||||
}
|
||||
|
||||
private static boolean canUseFurniInternalDestination(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniDestination(key);
|
||||
}
|
||||
|
||||
private static boolean canUseUserInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseFurniInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseRoomInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseRoomReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseContextInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseContextReference(key);
|
||||
}
|
||||
|
||||
private static int param(int[] params, int index, int fallback) {
|
||||
return (params.length > index) ? params[index] : fallback;
|
||||
}
|
||||
|
||||
private static int normalizeTargetType(int value) {
|
||||
return switch (value) {
|
||||
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
|
||||
default -> TARGET_USER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeReferenceMode(int value) {
|
||||
return (value == REF_VARIABLE) ? REF_VARIABLE : REF_CONSTANT;
|
||||
}
|
||||
|
||||
private static int normalizeUserSource(int value) {
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private static int normalizeDestinationFurniSource(int value) {
|
||||
return switch (value) {
|
||||
case WiredSourceUtil.SOURCE_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
|
||||
default -> WiredSourceUtil.SOURCE_TRIGGER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeReferenceFurniSource(int value) {
|
||||
return switch (value) {
|
||||
case SOURCE_SECONDARY_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
|
||||
default -> WiredSourceUtil.SOURCE_TRIGGER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeOperation(int value) {
|
||||
return switch (value) {
|
||||
case OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_POW, OP_MOD, OP_MIN, OP_MAX, OP_RANDOM, OP_ABS, OP_AND, OP_OR, OP_XOR, OP_NOT, OP_LSHIFT, OP_RSHIFT -> value;
|
||||
default -> OP_ASSIGN;
|
||||
};
|
||||
}
|
||||
|
||||
private static int parseInteger(String value) {
|
||||
try {
|
||||
return (value == null || value.trim().isEmpty()) ? 0 : Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int shift(Integer value) {
|
||||
return (value == null) ? 0 : Math.max(0, Math.min(31, value));
|
||||
}
|
||||
|
||||
private static int clamp(long value) {
|
||||
return (value > Integer.MAX_VALUE) ? Integer.MAX_VALUE : ((value < Integer.MIN_VALUE) ? Integer.MIN_VALUE : (int) value);
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int destinationTargetType, destinationVariableItemId, operation, referenceMode, referenceConstantValue, referenceTargetType, referenceVariableItemId, destinationUserSource, destinationFurniSource, referenceUserSource, referenceFurniSource, delay;
|
||||
String destinationVariableToken, referenceVariableToken;
|
||||
List<Integer> destinationSelectedFurniIds, referenceSelectedFurniIds;
|
||||
|
||||
JsonData(int destinationTargetType, String destinationVariableToken, int destinationVariableItemId, int operation, int referenceMode, int referenceConstantValue, int referenceTargetType, String referenceVariableToken, int referenceVariableItemId, int destinationUserSource, int destinationFurniSource, int referenceUserSource, int referenceFurniSource, int delay, List<Integer> destinationSelectedFurniIds, List<Integer> referenceSelectedFurniIds) {
|
||||
this.destinationTargetType = destinationTargetType;
|
||||
this.destinationVariableToken = destinationVariableToken;
|
||||
this.destinationVariableItemId = destinationVariableItemId;
|
||||
this.operation = operation;
|
||||
this.referenceMode = referenceMode;
|
||||
this.referenceConstantValue = referenceConstantValue;
|
||||
this.referenceTargetType = referenceTargetType;
|
||||
this.referenceVariableToken = referenceVariableToken;
|
||||
this.referenceVariableItemId = referenceVariableItemId;
|
||||
this.destinationUserSource = destinationUserSource;
|
||||
this.destinationFurniSource = destinationFurniSource;
|
||||
this.referenceUserSource = referenceUserSource;
|
||||
this.referenceFurniSource = referenceFurniSource;
|
||||
this.delay = delay;
|
||||
this.destinationSelectedFurniIds = destinationSelectedFurniIds;
|
||||
this.referenceSelectedFurniIds = referenceSelectedFurniIds;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReferenceSnapshot {
|
||||
final int targetType;
|
||||
final LinkedHashMap<Integer, Integer> values = new LinkedHashMap<>();
|
||||
|
||||
ReferenceSnapshot(int targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
void add(int entityId, int value) {
|
||||
this.values.put(entityId, value);
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return this.values.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TeamEffectData {
|
||||
final int colorId;
|
||||
final int typeId;
|
||||
|
||||
TeamEffectData(int colorId, int typeId) {
|
||||
this.colorId = colorId;
|
||||
this.typeId = typeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WiredEffectGiveVariable extends InteractionWiredEffect {
|
||||
public static final WiredEffectType type = WiredEffectType.GIVE_VAR;
|
||||
|
||||
public static final int TARGET_USER = 0;
|
||||
public static final int TARGET_FURNI = 1;
|
||||
public static final int TARGET_CONTEXT = 2;
|
||||
|
||||
private int variableItemId = 0;
|
||||
private int targetType = TARGET_USER;
|
||||
private boolean overrideExisting = false;
|
||||
private int initialValue = 0;
|
||||
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
private final THashSet<HabboItem> selectedFurni;
|
||||
|
||||
public WiredEffectGiveVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
this.selectedFurni = new THashSet<>();
|
||||
}
|
||||
|
||||
public WiredEffectGiveVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
this.selectedFurni = new THashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
|
||||
if (room == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.targetType) {
|
||||
case TARGET_USER:
|
||||
this.executeUserVariables(ctx, room);
|
||||
return;
|
||||
case TARGET_FURNI:
|
||||
this.executeFurniVariables(ctx, room);
|
||||
return;
|
||||
case TARGET_CONTEXT:
|
||||
this.executeContextVariables(ctx, room);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void executeContextVariables(WiredContext ctx, Room room) {
|
||||
WiredVariableDefinitionInfo definitionInfo = WiredContextVariableSupport.getDefinitionInfo(room, this.variableItemId);
|
||||
if (definitionInfo == null || definitionInfo.isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer value = definitionInfo.hasValue() ? this.initialValue : null;
|
||||
WiredContextVariableSupport.assignVariable(ctx, room, this.variableItemId, value, this.overrideExisting);
|
||||
}
|
||||
|
||||
private void executeUserVariables(WiredContext ctx, Room room) {
|
||||
WiredVariableDefinitionInfo definitionInfo = room.getUserVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
|
||||
if (definitionInfo == null || definitionInfo.isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<RoomUnit> users = WiredSourceUtil.resolveUsers(ctx, this.userSource);
|
||||
|
||||
if (users.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer value = definitionInfo.hasValue() ? this.initialValue : null;
|
||||
|
||||
for (RoomUnit roomUnit : users) {
|
||||
if (roomUnit == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
|
||||
if (habbo == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
room.getUserVariableManager().assignVariable(habbo, this.variableItemId, value, this.overrideExisting);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeFurniVariables(WiredContext ctx, Room room) {
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
|
||||
if (definition == null || definition.isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
this.validateItems(this.selectedFurni);
|
||||
}
|
||||
|
||||
List<HabboItem> furni = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.selectedFurni);
|
||||
|
||||
if (furni.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer value = definition.hasValue() ? this.initialValue : null;
|
||||
|
||||
for (HabboItem item : furni) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
room.getFurniVariableManager().assignVariable(item, this.variableItemId, value, this.overrideExisting);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
List<HabboItem> selectedItems = new ArrayList<>();
|
||||
|
||||
if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
for (HabboItem item : this.selectedFurni) {
|
||||
if (item != null && room != null && room.getHabboItem(item.getId()) != null) {
|
||||
selectedItems.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(selectedItems.size());
|
||||
|
||||
for (HabboItem item : selectedItems) {
|
||||
message.appendInt(item.getId());
|
||||
}
|
||||
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(String.valueOf(this.variableItemId));
|
||||
message.appendInt(5);
|
||||
message.appendInt(this.targetType);
|
||||
message.appendInt(this.overrideExisting ? 1 : 0);
|
||||
message.appendInt(this.initialValue);
|
||||
message.appendInt(this.userSource);
|
||||
message.appendInt(this.furniSource);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getType().code);
|
||||
message.appendInt(this.getDelay());
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
int[] intParams = settings.getIntParams();
|
||||
int nextTargetType = normalizeTargetType((intParams.length > 0) ? intParams[0] : TARGET_USER);
|
||||
boolean nextOverrideExisting = (intParams.length > 1) && (intParams[1] == 1);
|
||||
int nextInitialValue = (intParams.length > 2) ? intParams[2] : 0;
|
||||
int nextUserSource = normalizeUserSource((intParams.length > 3) ? intParams[3] : WiredSourceUtil.SOURCE_TRIGGER);
|
||||
int nextFurniSource = normalizeFurniSource((intParams.length > 4) ? intParams[4] : WiredSourceUtil.SOURCE_TRIGGER);
|
||||
int nextVariableItemId = parseVariableItemId(settings.getStringParam());
|
||||
|
||||
if (nextVariableItemId <= 0 && settings.getFurniIds() != null && settings.getFurniIds().length > 0) {
|
||||
int legacyItemId = settings.getFurniIds()[0];
|
||||
|
||||
if (room.getUserVariableManager().hasDefinition(legacyItemId)) {
|
||||
nextVariableItemId = legacyItemId;
|
||||
nextTargetType = TARGET_USER;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextVariableItemId <= 0) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo userDefinitionInfo = (nextTargetType == TARGET_USER) ? room.getUserVariableManager().getDefinitionInfo(nextVariableItemId) : null;
|
||||
|
||||
if (nextTargetType == TARGET_USER && (userDefinitionInfo == null || userDefinitionInfo.isReadOnly())) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
if (nextTargetType == TARGET_FURNI) {
|
||||
WiredVariableDefinitionInfo furniDefinitionInfo = room.getFurniVariableManager().getDefinitionInfo(nextVariableItemId);
|
||||
|
||||
if (furniDefinitionInfo == null || furniDefinitionInfo.isReadOnly()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
}
|
||||
|
||||
if (nextTargetType == TARGET_CONTEXT) {
|
||||
WiredVariableDefinitionInfo contextDefinitionInfo = WiredContextVariableSupport.getDefinitionInfo(room, nextVariableItemId);
|
||||
|
||||
if (contextDefinitionInfo == null || contextDefinitionInfo.isReadOnly()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedFurni.clear();
|
||||
|
||||
if (nextTargetType == TARGET_FURNI && nextFurniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
int[] furniIds = settings.getFurniIds();
|
||||
int itemsCount = (furniIds != null) ? furniIds.length : 0;
|
||||
|
||||
if (itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
|
||||
throw new WiredSaveException("Too many furni selected");
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemsCount; i++) {
|
||||
int itemId = furniIds[i];
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
|
||||
if (item == null) {
|
||||
throw new WiredSaveException(String.format("Item %s not found", itemId));
|
||||
}
|
||||
|
||||
this.selectedFurni.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
this.variableItemId = nextVariableItemId;
|
||||
this.targetType = nextTargetType;
|
||||
this.overrideExisting = nextOverrideExisting;
|
||||
this.initialValue = nextInitialValue;
|
||||
this.userSource = nextUserSource;
|
||||
this.furniSource = nextFurniSource;
|
||||
this.setDelay(settings.getDelay());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
List<Integer> selectedItemIds = new ArrayList<>();
|
||||
|
||||
for (HabboItem item : this.selectedFurni) {
|
||||
if (item != null) {
|
||||
selectedItemIds.add(item.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return WiredManager.getGson().toJson(new JsonData(this.variableItemId, this.targetType, this.overrideExisting, this.initialValue, this.userSource, this.furniSource, this.getDelay(), selectedItemIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.variableItemId = Math.max(0, data.variableItemId);
|
||||
this.targetType = normalizeTargetType(data.targetType);
|
||||
this.overrideExisting = data.overrideExisting;
|
||||
this.initialValue = data.initialValue;
|
||||
this.userSource = normalizeUserSource(data.userSource);
|
||||
this.furniSource = normalizeFurniSource(data.furniSource);
|
||||
this.setDelay(Math.max(0, data.delay));
|
||||
|
||||
if (room != null && data.selectedFurniIds != null) {
|
||||
for (Integer itemId : data.selectedFurniIds) {
|
||||
if (itemId == null || itemId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
|
||||
if (item != null) {
|
||||
this.selectedFurni.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.variableItemId = Math.max(0, Integer.parseInt(wiredData.trim()));
|
||||
this.targetType = TARGET_USER;
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableItemId = 0;
|
||||
this.targetType = TARGET_USER;
|
||||
this.overrideExisting = false;
|
||||
this.initialValue = 0;
|
||||
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.selectedFurni.clear();
|
||||
this.setDelay(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredEffectType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getVariableItemId() {
|
||||
return this.variableItemId;
|
||||
}
|
||||
|
||||
public int getTargetType() {
|
||||
return this.targetType;
|
||||
}
|
||||
|
||||
public boolean isOverrideExisting() {
|
||||
return this.overrideExisting;
|
||||
}
|
||||
|
||||
public int getInitialValue() {
|
||||
return this.initialValue;
|
||||
}
|
||||
|
||||
public int getUserSource() {
|
||||
return this.userSource;
|
||||
}
|
||||
|
||||
public int getFurniSource() {
|
||||
return this.furniSource;
|
||||
}
|
||||
|
||||
public THashSet<HabboItem> getSelectedFurni() {
|
||||
return this.selectedFurni;
|
||||
}
|
||||
|
||||
private static int normalizeTargetType(int value) {
|
||||
switch (value) {
|
||||
case TARGET_FURNI:
|
||||
case TARGET_CONTEXT:
|
||||
return value;
|
||||
default:
|
||||
return TARGET_USER;
|
||||
}
|
||||
}
|
||||
|
||||
private static int normalizeUserSource(int value) {
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private static int normalizeFurniSource(int value) {
|
||||
switch (value) {
|
||||
case WiredSourceUtil.SOURCE_SELECTED:
|
||||
case WiredSourceUtil.SOURCE_SELECTOR:
|
||||
case WiredSourceUtil.SOURCE_SIGNAL:
|
||||
return value;
|
||||
default:
|
||||
return WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseVariableItemId(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Math.max(0, Integer.parseInt(value.trim()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int variableItemId;
|
||||
int targetType;
|
||||
boolean overrideExisting;
|
||||
int initialValue;
|
||||
int userSource;
|
||||
int furniSource;
|
||||
int delay;
|
||||
List<Integer> selectedFurniIds;
|
||||
|
||||
JsonData(int variableItemId, int targetType, boolean overrideExisting, int initialValue, int userSource, int furniSource, int delay, List<Integer> selectedFurniIds) {
|
||||
this.variableItemId = variableItemId;
|
||||
this.targetType = targetType;
|
||||
this.overrideExisting = overrideExisting;
|
||||
this.initialValue = initialValue;
|
||||
this.userSource = userSource;
|
||||
this.furniSource = furniSource;
|
||||
this.delay = delay;
|
||||
this.selectedFurniIds = selectedFurniIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-18
@@ -15,6 +15,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredMovementPhysics;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper;
|
||||
@@ -53,6 +54,7 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
@Override
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
WiredMovementPhysics movementPhysics = WiredMoveCarryHelper.getUserMovementPhysics(room, this, ctx);
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
|
||||
if (roomUnit == null || roomUnit.getRoom() != room) {
|
||||
@@ -63,7 +65,7 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
RoomUserRotation targetBodyRotation = hasRotation ? this.getTargetRotation(roomUnit) : roomUnit.getBodyRotation();
|
||||
RoomUserRotation targetHeadRotation = hasRotation ? targetBodyRotation : roomUnit.getHeadRotation();
|
||||
RoomTile targetTile = (this.movementDirection >= 0) ? this.getTargetTile(room, roomUnit, this.movementDirection) : null;
|
||||
boolean canMove = this.canMoveTo(room, roomUnit, targetTile);
|
||||
boolean canMove = this.canMoveTo(room, roomUnit, targetTile, movementPhysics);
|
||||
boolean noAnimation = WiredMoveCarryHelper.hasNoAnimationExtra(room, this);
|
||||
int animationDuration = noAnimation ? 0 : WiredMoveCarryHelper.getAnimationDuration(room, this, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION);
|
||||
int activeWindowMs = this.resolveActiveWindow(canMove, hasRotation, noAnimation, animationDuration);
|
||||
@@ -72,7 +74,7 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0);
|
||||
this.markActive(roomUnit, activeWindowMs);
|
||||
if (!WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, targetBodyRotation, targetHeadRotation,
|
||||
animationDuration, noAnimation)) {
|
||||
animationDuration, noAnimation, movementPhysics)) {
|
||||
if (hasRotation) {
|
||||
WiredUserMovementHelper.updateUserDirection(room, roomUnit, targetBodyRotation, targetHeadRotation);
|
||||
}
|
||||
@@ -266,22 +268,8 @@ public class WiredEffectMoveRotateUser extends InteractionWiredEffect {
|
||||
return room.getLayout().getTile((short) (currentTile.x + deltaX), (short) (currentTile.y + deltaY));
|
||||
}
|
||||
|
||||
private boolean canMoveTo(Room room, RoomUnit roomUnit, RoomTile targetTile) {
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID || targetTile.state == RoomTileState.BLOCKED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!room.tileWalkable(targetTile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (RoomUnit unit : room.getRoomUnitsAt(targetTile)) {
|
||||
if (unit != null && unit != roomUnit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
private boolean canMoveTo(Room room, RoomUnit roomUnit, RoomTile targetTile, WiredMovementPhysics movementPhysics) {
|
||||
return WiredUserMovementHelper.canMoveTo(room, roomUnit, targetTile, movementPhysics);
|
||||
}
|
||||
|
||||
private void markActive(RoomUnit roomUnit, int durationMs) {
|
||||
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WiredEffectRemoveVariable extends InteractionWiredEffect {
|
||||
public static final WiredEffectType type = WiredEffectType.REMOVE_VAR;
|
||||
|
||||
public static final int TARGET_USER = 0;
|
||||
public static final int TARGET_FURNI = 1;
|
||||
public static final int TARGET_CONTEXT = 2;
|
||||
|
||||
private int variableItemId = 0;
|
||||
private int targetType = TARGET_USER;
|
||||
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
private final THashSet<HabboItem> selectedFurni;
|
||||
|
||||
public WiredEffectRemoveVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
this.selectedFurni = new THashSet<>();
|
||||
}
|
||||
|
||||
public WiredEffectRemoveVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
this.selectedFurni = new THashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
|
||||
if (room == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.targetType) {
|
||||
case TARGET_USER:
|
||||
this.executeUserVariables(ctx, room);
|
||||
return;
|
||||
case TARGET_FURNI:
|
||||
this.executeFurniVariables(ctx, room);
|
||||
return;
|
||||
case TARGET_CONTEXT:
|
||||
this.executeContextVariables(ctx, room);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void executeUserVariables(WiredContext ctx, Room room) {
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
|
||||
if (definition == null || definition.isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<RoomUnit> users = WiredSourceUtil.resolveUsers(ctx, this.userSource);
|
||||
|
||||
for (RoomUnit roomUnit : users) {
|
||||
if (roomUnit == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
|
||||
if (habbo == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
room.getUserVariableManager().removeVariable(habbo.getHabboInfo().getId(), this.variableItemId);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeFurniVariables(WiredContext ctx, Room room) {
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
|
||||
if (definition == null || definition.isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
this.validateItems(this.selectedFurni);
|
||||
}
|
||||
|
||||
List<HabboItem> furni = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.selectedFurni);
|
||||
|
||||
for (HabboItem item : furni) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
room.getFurniVariableManager().removeVariable(item.getId(), this.variableItemId);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeContextVariables(WiredContext ctx, Room room) {
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.variableItemId);
|
||||
|
||||
if (definition == null || definition.isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
WiredContextVariableSupport.removeVariable(ctx, room, this.variableItemId);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
List<HabboItem> selectedItems = new ArrayList<>();
|
||||
|
||||
if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
for (HabboItem item : this.selectedFurni) {
|
||||
if (item != null && room != null && room.getHabboItem(item.getId()) != null) {
|
||||
selectedItems.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(selectedItems.size());
|
||||
|
||||
for (HabboItem item : selectedItems) {
|
||||
message.appendInt(item.getId());
|
||||
}
|
||||
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(String.valueOf(this.variableItemId));
|
||||
message.appendInt(3);
|
||||
message.appendInt(this.targetType);
|
||||
message.appendInt(this.userSource);
|
||||
message.appendInt(this.furniSource);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getType().code);
|
||||
message.appendInt(this.getDelay());
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
int[] intParams = settings.getIntParams();
|
||||
int nextTargetType = normalizeTargetType((intParams.length > 0) ? intParams[0] : TARGET_USER);
|
||||
int nextUserSource = normalizeUserSource((intParams.length > 1) ? intParams[1] : WiredSourceUtil.SOURCE_TRIGGER);
|
||||
int nextFurniSource = normalizeFurniSource((intParams.length > 2) ? intParams[2] : WiredSourceUtil.SOURCE_TRIGGER);
|
||||
int nextVariableItemId = parseVariableItemId(settings.getStringParam());
|
||||
|
||||
if (nextVariableItemId <= 0) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
}
|
||||
|
||||
switch (nextTargetType) {
|
||||
case TARGET_USER:
|
||||
WiredVariableDefinitionInfo userDefinition = room.getUserVariableManager().getDefinitionInfo(nextVariableItemId);
|
||||
if (userDefinition == null || userDefinition.isReadOnly()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
break;
|
||||
case TARGET_FURNI:
|
||||
WiredVariableDefinitionInfo furniDefinition = room.getFurniVariableManager().getDefinitionInfo(nextVariableItemId);
|
||||
if (furniDefinition == null || furniDefinition.isReadOnly()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
break;
|
||||
case TARGET_CONTEXT:
|
||||
WiredVariableDefinitionInfo contextDefinition = WiredContextVariableSupport.getDefinitionInfo(room, nextVariableItemId);
|
||||
if (contextDefinition == null || contextDefinition.isReadOnly()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
this.selectedFurni.clear();
|
||||
|
||||
if (nextTargetType == TARGET_FURNI && nextFurniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
int[] furniIds = settings.getFurniIds();
|
||||
int itemsCount = (furniIds != null) ? furniIds.length : 0;
|
||||
|
||||
if (itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
|
||||
throw new WiredSaveException("Too many furni selected");
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemsCount; i++) {
|
||||
int itemId = furniIds[i];
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
|
||||
if (item == null) {
|
||||
throw new WiredSaveException(String.format("Item %s not found", itemId));
|
||||
}
|
||||
|
||||
this.selectedFurni.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
this.variableItemId = nextVariableItemId;
|
||||
this.targetType = nextTargetType;
|
||||
this.userSource = nextUserSource;
|
||||
this.furniSource = nextFurniSource;
|
||||
this.setDelay(settings.getDelay());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
List<Integer> selectedItemIds = new ArrayList<>();
|
||||
|
||||
for (HabboItem item : this.selectedFurni) {
|
||||
if (item != null) {
|
||||
selectedItemIds.add(item.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return WiredManager.getGson().toJson(new JsonData(this.variableItemId, this.targetType, this.userSource, this.furniSource, this.getDelay(), selectedItemIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.variableItemId = Math.max(0, data.variableItemId);
|
||||
this.targetType = normalizeTargetType(data.targetType);
|
||||
this.userSource = normalizeUserSource(data.userSource);
|
||||
this.furniSource = normalizeFurniSource(data.furniSource);
|
||||
this.setDelay(Math.max(0, data.delay));
|
||||
|
||||
if (room != null && data.selectedFurniIds != null) {
|
||||
for (Integer itemId : data.selectedFurniIds) {
|
||||
if (itemId == null || itemId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
|
||||
if (item != null) {
|
||||
this.selectedFurni.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.variableItemId = Math.max(0, Integer.parseInt(wiredData.trim()));
|
||||
this.targetType = TARGET_USER;
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableItemId = 0;
|
||||
this.targetType = TARGET_USER;
|
||||
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.selectedFurni.clear();
|
||||
this.setDelay(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredEffectType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
private static int normalizeTargetType(int value) {
|
||||
switch (value) {
|
||||
case TARGET_FURNI:
|
||||
case TARGET_CONTEXT:
|
||||
return value;
|
||||
default:
|
||||
return TARGET_USER;
|
||||
}
|
||||
}
|
||||
|
||||
private static int normalizeUserSource(int value) {
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private static int normalizeFurniSource(int value) {
|
||||
switch (value) {
|
||||
case WiredSourceUtil.SOURCE_SELECTED:
|
||||
case WiredSourceUtil.SOURCE_SELECTOR:
|
||||
case WiredSourceUtil.SOURCE_SIGNAL:
|
||||
return value;
|
||||
default:
|
||||
return WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseVariableItemId(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Math.max(0, Integer.parseInt(value.trim()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int variableItemId;
|
||||
int targetType;
|
||||
int userSource;
|
||||
int furniSource;
|
||||
int delay;
|
||||
List<Integer> selectedFurniIds;
|
||||
|
||||
JsonData(int variableItemId, int targetType, int userSource, int furniSource, int delay, List<Integer> selectedFurniIds) {
|
||||
this.variableItemId = variableItemId;
|
||||
this.targetType = targetType;
|
||||
this.userSource = userSource;
|
||||
this.furniSource = furniSource;
|
||||
this.delay = delay;
|
||||
this.selectedFurniIds = selectedFurniIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -122,13 +122,13 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
|
||||
for (RoomUnit user : usersToSend) {
|
||||
for (HabboItem sourceItem : furniToSend) {
|
||||
for (HabboItem antenna : resolvedAntennas) {
|
||||
fireSignalAtAntenna(room, antenna, user, sourceItem, nextDepth);
|
||||
fireSignalAtAntenna(ctx, room, antenna, user, sourceItem, nextDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fireSignalAtAntenna(Room room, HabboItem antenna, RoomUnit actor, HabboItem sourceItem, int depth) {
|
||||
private void fireSignalAtAntenna(WiredContext ctx, Room room, HabboItem antenna, RoomUnit actor, HabboItem sourceItem, int depth) {
|
||||
if (antenna == null) return;
|
||||
RoomTile tile = room.getLayout().getTile(antenna.getX(), antenna.getY());
|
||||
if (tile == null) return;
|
||||
@@ -144,6 +144,9 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
|
||||
.tile(tile)
|
||||
.callStackDepth(depth)
|
||||
.signalChannel(signalChannel)
|
||||
.signalUserCount(actor != null ? 1 : 0)
|
||||
.signalFurniCount(sourceItem != null ? 1 : 0)
|
||||
.contextVariableScope(ctx.contextVariables())
|
||||
.triggeredByEffect(true);
|
||||
|
||||
if (actor != null) builder.actor(actor);
|
||||
|
||||
+5
-3
@@ -14,6 +14,7 @@ import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredMovementPhysics;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper;
|
||||
@@ -47,6 +48,7 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase {
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
HabboItem item = this.resolveLastItem(ctx);
|
||||
WiredMovementPhysics movementPhysics = WiredMoveCarryHelper.getUserMovementPhysics(room, this, ctx);
|
||||
|
||||
if (room == null || item == null) {
|
||||
return;
|
||||
@@ -58,7 +60,7 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase {
|
||||
}
|
||||
|
||||
for (Habbo habbo : this.resolveHabbos(room, ctx)) {
|
||||
this.moveHabboSmooth(room, habbo, item, targetTile);
|
||||
this.moveHabboSmooth(room, habbo, item, targetTile, movementPhysics);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +229,7 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void moveHabboSmooth(Room room, Habbo habbo, HabboItem item, RoomTile targetTile) {
|
||||
private void moveHabboSmooth(Room room, Habbo habbo, HabboItem item, RoomTile targetTile, WiredMovementPhysics movementPhysics) {
|
||||
if (room == null || habbo == null || item == null || targetTile == null || habbo.getRoomUnit() == null) {
|
||||
return;
|
||||
}
|
||||
@@ -245,7 +247,7 @@ public class WiredEffectUserToFurni extends WiredEffectUserFurniBase {
|
||||
double newZ = item.getZ() + Item.getCurrentHeight(item);
|
||||
int animationDuration = noAnimation ? 0 : WiredMoveCarryHelper.getAnimationDuration(room, this, WiredUserMovementHelper.DEFAULT_ANIMATION_DURATION);
|
||||
if (!WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, newZ,
|
||||
roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), animationDuration, noAnimation)) {
|
||||
roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), animationDuration, noAnimation, movementPhysics)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredExtraContextVariable extends InteractionWiredExtra {
|
||||
public static final int CODE = 84;
|
||||
|
||||
private String variableName = "";
|
||||
private boolean hasValue = false;
|
||||
|
||||
public WiredExtraContextVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraContextVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
int[] intParams = settings.getIntParams();
|
||||
String normalizedName = WiredVariableNameValidator.normalizeForSave(settings.getStringParam());
|
||||
|
||||
WiredVariableNameValidator.validateDefinitionName(room, this.getId(), normalizedName);
|
||||
|
||||
this.variableName = normalizedName;
|
||||
this.hasValue = (intParams.length > 0) && (intParams[0] == 1);
|
||||
|
||||
WiredContextVariableSupport.broadcastDefinitions(room);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.variableName, this.hasValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableName);
|
||||
message.appendInt(1);
|
||||
message.appendInt(this.hasValue ? 1 : 0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(data.variableName);
|
||||
this.hasValue = data.hasValue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(wiredData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableName = "";
|
||||
this.hasValue = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getVariableName() {
|
||||
return this.variableName;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return this.hasValue;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String variableName;
|
||||
boolean hasValue;
|
||||
|
||||
JsonData(String variableName, boolean hasValue) {
|
||||
this.variableName = variableName;
|
||||
this.hasValue = hasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredExtraFilterFurniByVariable extends WiredExtraVariableFilterBase {
|
||||
public static final int CODE = 78;
|
||||
|
||||
public WiredExtraFilterFurniByVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraFilterFurniByVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVariableTargetType() {
|
||||
return TARGET_FURNI;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getCode() {
|
||||
return CODE;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredExtraFilterUsersByVariable extends WiredExtraVariableFilterBase {
|
||||
public static final int CODE = 77;
|
||||
|
||||
public WiredExtraFilterUsersByVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraFilterUsersByVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVariableTargetType() {
|
||||
return TARGET_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getCode() {
|
||||
return CODE;
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredExtraFurniVariable extends InteractionWiredExtra {
|
||||
public static final int CODE = 71;
|
||||
public static final int AVAILABILITY_ROOM_ACTIVE = 1;
|
||||
public static final int AVAILABILITY_PERMANENT = 10;
|
||||
|
||||
private String variableName = "";
|
||||
private boolean hasValue = false;
|
||||
private int availability = AVAILABILITY_ROOM_ACTIVE;
|
||||
|
||||
public WiredExtraFurniVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraFurniVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
int[] intParams = settings.getIntParams();
|
||||
String normalizedName = WiredVariableNameValidator.normalizeForSave(settings.getStringParam());
|
||||
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
WiredVariableNameValidator.validateDefinitionName(room, this.getId(), normalizedName);
|
||||
|
||||
this.variableName = normalizedName;
|
||||
this.hasValue = (intParams.length > 0) && (intParams[0] == 1);
|
||||
this.availability = normalizeAvailability((intParams.length > 1) ? intParams[1] : AVAILABILITY_ROOM_ACTIVE);
|
||||
|
||||
room.getFurniVariableManager().handleDefinitionUpdated(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.variableName, this.hasValue, this.availability));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableName);
|
||||
message.appendInt(2);
|
||||
message.appendInt(this.hasValue ? 1 : 0);
|
||||
message.appendInt(this.availability);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(data.variableName);
|
||||
this.hasValue = data.hasValue;
|
||||
this.availability = normalizeAvailability(data.availability);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(wiredData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableName = "";
|
||||
this.hasValue = false;
|
||||
this.availability = AVAILABILITY_ROOM_ACTIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getVariableName() {
|
||||
return this.variableName;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return this.hasValue;
|
||||
}
|
||||
|
||||
public int getAvailability() {
|
||||
return this.availability;
|
||||
}
|
||||
|
||||
public boolean isPermanentAvailability() {
|
||||
return this.availability == AVAILABILITY_PERMANENT;
|
||||
}
|
||||
|
||||
private static int normalizeAvailability(int value) {
|
||||
if (value == AVAILABILITY_PERMANENT) {
|
||||
return AVAILABILITY_PERMANENT;
|
||||
}
|
||||
|
||||
return AVAILABILITY_ROOM_ACTIVE;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String variableName;
|
||||
boolean hasValue;
|
||||
int availability;
|
||||
|
||||
JsonData(String variableName, boolean hasValue, int availability) {
|
||||
this.variableName = variableName;
|
||||
this.hasValue = hasValue;
|
||||
this.availability = availability;
|
||||
}
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredExtraRoomVariable extends InteractionWiredExtra {
|
||||
public static final int CODE = 72;
|
||||
public static final int AVAILABILITY_ROOM_ACTIVE = 1;
|
||||
public static final int AVAILABILITY_PERMANENT = 10;
|
||||
public static final int AVAILABILITY_SHARED = 11;
|
||||
|
||||
private String variableName = "";
|
||||
private int availability = AVAILABILITY_ROOM_ACTIVE;
|
||||
|
||||
public WiredExtraRoomVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraRoomVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
String normalizedName = WiredVariableNameValidator.normalizeForSave(settings.getStringParam());
|
||||
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
WiredVariableNameValidator.validateDefinitionName(room, this.getId(), normalizedName);
|
||||
|
||||
int[] intParams = settings.getIntParams();
|
||||
|
||||
this.variableName = normalizedName;
|
||||
this.availability = normalizeAvailability((intParams.length > 0) ? intParams[0] : AVAILABILITY_ROOM_ACTIVE);
|
||||
|
||||
room.getRoomVariableManager().handleDefinitionUpdated(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.variableName, this.availability));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
int currentValue = (room != null) ? room.getRoomVariableManager().getCurrentValue(this.getId()) : 0;
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableName);
|
||||
message.appendInt(2);
|
||||
message.appendInt(this.availability);
|
||||
message.appendInt(currentValue);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(data.variableName);
|
||||
this.availability = normalizeAvailability(data.availability);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(wiredData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableName = "";
|
||||
this.availability = AVAILABILITY_ROOM_ACTIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getVariableName() {
|
||||
return this.variableName;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getAvailability() {
|
||||
return this.availability;
|
||||
}
|
||||
|
||||
public boolean isPermanentAvailability() {
|
||||
return this.availability == AVAILABILITY_PERMANENT || this.availability == AVAILABILITY_SHARED;
|
||||
}
|
||||
|
||||
public boolean isSharedAvailability() {
|
||||
return this.availability == AVAILABILITY_SHARED;
|
||||
}
|
||||
|
||||
private static int normalizeAvailability(int value) {
|
||||
if (value == AVAILABILITY_PERMANENT || value == AVAILABILITY_SHARED) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return AVAILABILITY_ROOM_ACTIVE;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String variableName;
|
||||
int availability;
|
||||
|
||||
JsonData(String variableName, int availability) {
|
||||
this.variableName = variableName;
|
||||
this.availability = availability;
|
||||
}
|
||||
}
|
||||
}
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredVariableTextConnectorSupport;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class WiredExtraTextInputVariable extends InteractionWiredExtra {
|
||||
public static final int CODE = 85;
|
||||
public static final int DISPLAY_NUMERIC = 1;
|
||||
public static final int DISPLAY_TEXTUAL = 2;
|
||||
public static final String DEFAULT_CAPTURER_NAME = "";
|
||||
public static final int MAX_CAPTURER_NAME_LENGTH = 32;
|
||||
|
||||
private static final String CUSTOM_TOKEN_PREFIX = "custom:";
|
||||
private static final Pattern WRAPPED_PLACEHOLDER_PATTERN = Pattern.compile("^#(.*)#$");
|
||||
|
||||
private int variableItemId = 0;
|
||||
private String variableToken = "";
|
||||
private String capturerName = DEFAULT_CAPTURER_NAME;
|
||||
private int displayType = DISPLAY_NUMERIC;
|
||||
|
||||
public WiredExtraTextInputVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraTextInputVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
int[] intParams = settings.getIntParams();
|
||||
String[] stringData = splitStringData(settings.getStringParam());
|
||||
String nextVariableToken = normalizeVariableToken(stringData[0]);
|
||||
int nextVariableItemId = getCustomItemId(nextVariableToken);
|
||||
|
||||
if (nextVariableItemId <= 0) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definitionInfo = WiredContextVariableSupport.getDefinitionInfo(room, nextVariableItemId);
|
||||
if (definitionInfo == null || !definitionInfo.hasValue()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
this.variableItemId = nextVariableItemId;
|
||||
this.variableToken = nextVariableToken;
|
||||
this.capturerName = normalizeCapturerName(stringData[1]);
|
||||
this.displayType = normalizeDisplayType((intParams.length > 0) ? intParams[0] : DISPLAY_NUMERIC);
|
||||
|
||||
if (!canUseTextualDisplay(room, this.variableItemId)) {
|
||||
this.displayType = DISPLAY_NUMERIC;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.variableToken, this.variableItemId, this.capturerName, this.displayType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableToken + "\t" + this.capturerName);
|
||||
message.appendInt(1);
|
||||
message.appendInt(this.getDisplayType(room));
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.variableToken = normalizeVariableToken((data.variableToken != null)
|
||||
? data.variableToken
|
||||
: ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : ""));
|
||||
this.variableItemId = getCustomItemId(this.variableToken);
|
||||
this.capturerName = normalizeCapturerName(data.capturerName);
|
||||
this.displayType = normalizeDisplayType(data.displayType);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
String[] legacyData = splitStringData(wiredData);
|
||||
this.variableToken = normalizeVariableToken(legacyData[0]);
|
||||
this.variableItemId = getCustomItemId(this.variableToken);
|
||||
this.capturerName = normalizeCapturerName(legacyData[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableItemId = 0;
|
||||
this.variableToken = "";
|
||||
this.capturerName = DEFAULT_CAPTURER_NAME;
|
||||
this.displayType = DISPLAY_NUMERIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getVariableItemId() {
|
||||
return this.variableItemId;
|
||||
}
|
||||
|
||||
public String getVariableToken() {
|
||||
return this.variableToken;
|
||||
}
|
||||
|
||||
public String getCapturerName() {
|
||||
return this.capturerName;
|
||||
}
|
||||
|
||||
public String getPlaceholderToken() {
|
||||
return this.capturerName.isEmpty() ? "" : "#" + this.capturerName + "#";
|
||||
}
|
||||
|
||||
public int getDisplayType(Room room) {
|
||||
return (this.displayType == DISPLAY_TEXTUAL && canUseTextualDisplay(room, this.variableItemId))
|
||||
? DISPLAY_TEXTUAL
|
||||
: DISPLAY_NUMERIC;
|
||||
}
|
||||
|
||||
public Integer resolveCapturedValue(Room room, String rawValue) {
|
||||
String normalizedValue = rawValue != null ? rawValue.trim() : "";
|
||||
if (normalizedValue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.getDisplayType(room) == DISPLAY_TEXTUAL) {
|
||||
return WiredVariableTextConnectorSupport.toValue(room, this.variableItemId, normalizedValue);
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(normalizedValue);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canUseTextualDisplay(Room room, int definitionItemId) {
|
||||
WiredVariableDefinitionInfo definitionInfo = WiredContextVariableSupport.getDefinitionInfo(room, definitionItemId);
|
||||
return definitionInfo != null && definitionInfo.hasValue() && definitionInfo.isTextConnected();
|
||||
}
|
||||
|
||||
private static String[] splitStringData(String value) {
|
||||
if (value == null) {
|
||||
return new String[]{ "", DEFAULT_CAPTURER_NAME };
|
||||
}
|
||||
|
||||
String[] parts = value.split("\t", -1);
|
||||
if (parts.length == 1) {
|
||||
return new String[]{ parts[0], DEFAULT_CAPTURER_NAME };
|
||||
}
|
||||
|
||||
return new String[]{ parts[0], parts[1] };
|
||||
}
|
||||
|
||||
private static int normalizeDisplayType(int value) {
|
||||
return (value == DISPLAY_TEXTUAL) ? DISPLAY_TEXTUAL : DISPLAY_NUMERIC;
|
||||
}
|
||||
|
||||
private static String normalizeCapturerName(String value) {
|
||||
if (value == null) {
|
||||
return DEFAULT_CAPTURER_NAME;
|
||||
}
|
||||
|
||||
String normalized = value.trim().replace("\t", "").replace("\r", "").replace("\n", "");
|
||||
if (WRAPPED_PLACEHOLDER_PATTERN.matcher(normalized).matches()) {
|
||||
normalized = normalized.substring(1, normalized.length() - 1).trim();
|
||||
}
|
||||
|
||||
if (normalized.length() > MAX_CAPTURER_NAME_LENGTH) {
|
||||
normalized = normalized.substring(0, MAX_CAPTURER_NAME_LENGTH);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static String normalizeVariableToken(String value) {
|
||||
String normalized = value == null ? "" : value.trim();
|
||||
if (normalized.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (normalized.startsWith(CUSTOM_TOKEN_PREFIX)) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
try {
|
||||
int parsedValue = Integer.parseInt(normalized);
|
||||
return parsedValue > 0 ? (CUSTOM_TOKEN_PREFIX + parsedValue) : "";
|
||||
} catch (NumberFormatException ignored) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCustomItemId(String token) {
|
||||
if (token == null || !token.startsWith(CUSTOM_TOKEN_PREFIX)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(token.substring(CUSTOM_TOKEN_PREFIX.length()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String variableToken;
|
||||
int variableItemId;
|
||||
String capturerName;
|
||||
int displayType;
|
||||
|
||||
JsonData(String variableToken, int variableItemId, String capturerName, int displayType) {
|
||||
this.variableToken = variableToken;
|
||||
this.variableItemId = variableItemId;
|
||||
this.capturerName = capturerName;
|
||||
this.displayType = displayType;
|
||||
}
|
||||
}
|
||||
}
|
||||
+544
@@ -0,0 +1,544 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WiredExtraTextOutputVariable extends InteractionWiredExtra {
|
||||
public static final int CODE = 80;
|
||||
public static final int TARGET_USER = 0;
|
||||
public static final int TARGET_FURNI = 1;
|
||||
public static final int TARGET_CONTEXT = 2;
|
||||
public static final int TARGET_ROOM = 3;
|
||||
public static final int DISPLAY_NUMERIC = 1;
|
||||
public static final int DISPLAY_TEXTUAL = 2;
|
||||
public static final int TYPE_SINGLE = 1;
|
||||
public static final int TYPE_MULTIPLE = 2;
|
||||
public static final String DEFAULT_VARIABLE_TOKEN = "";
|
||||
public static final String DEFAULT_PLACEHOLDER_NAME = "";
|
||||
public static final String DEFAULT_DELIMITER = ", ";
|
||||
public static final int MAX_PLACEHOLDER_NAME_LENGTH = 32;
|
||||
public static final int MAX_DELIMITER_LENGTH = 16;
|
||||
|
||||
private static final String CUSTOM_TOKEN_PREFIX = "custom:";
|
||||
private static final String INTERNAL_TOKEN_PREFIX = "internal:";
|
||||
private static final Pattern WRAPPED_PLACEHOLDER_PATTERN = Pattern.compile("^\\$\\((.*)\\)$");
|
||||
|
||||
private final THashSet<HabboItem> items;
|
||||
private int targetType = TARGET_USER;
|
||||
private int displayType = DISPLAY_NUMERIC;
|
||||
private int placeholderType = TYPE_SINGLE;
|
||||
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
private int variableItemId = 0;
|
||||
private String variableToken = DEFAULT_VARIABLE_TOKEN;
|
||||
private String placeholderName = DEFAULT_PLACEHOLDER_NAME;
|
||||
private String delimiter = DEFAULT_DELIMITER;
|
||||
|
||||
public WiredExtraTextOutputVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
this.items = new THashSet<>();
|
||||
}
|
||||
|
||||
public WiredExtraTextOutputVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
this.items = new THashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
int[] intParams = settings.getIntParams();
|
||||
String[] stringData = splitStringData(settings.getStringParam());
|
||||
int nextTargetType = normalizeTargetType((intParams.length > 0) ? intParams[0] : TARGET_USER);
|
||||
String nextVariableToken = normalizeVariableToken(stringData[0]);
|
||||
|
||||
if (nextVariableToken.isEmpty()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
}
|
||||
|
||||
if (!isValidVariable(room, nextTargetType, nextVariableToken)) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
int nextFurniSource = normalizeFurniSource((intParams.length > 4) ? intParams[4] : WiredSourceUtil.SOURCE_TRIGGER);
|
||||
this.items.clear();
|
||||
|
||||
if (nextTargetType == TARGET_FURNI && nextFurniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
for (int itemId : settings.getFurniIds()) {
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
|
||||
if (item != null) {
|
||||
this.items.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.targetType = nextTargetType;
|
||||
this.setVariableToken(nextVariableToken);
|
||||
this.displayType = normalizeDisplayType((intParams.length > 1) ? intParams[1] : DISPLAY_NUMERIC);
|
||||
this.placeholderType = normalizePlaceholderType((intParams.length > 2) ? intParams[2] : TYPE_SINGLE);
|
||||
this.userSource = normalizeUserSource((intParams.length > 3) ? intParams[3] : WiredSourceUtil.SOURCE_TRIGGER);
|
||||
this.furniSource = nextFurniSource;
|
||||
this.placeholderName = normalizePlaceholderName(stringData[1]);
|
||||
this.delimiter = normalizeDelimiter(stringData[2]);
|
||||
|
||||
if (!canUseTextualDisplay(room, this.targetType, this.variableToken)) {
|
||||
this.displayType = DISPLAY_NUMERIC;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
this.targetType,
|
||||
this.variableToken,
|
||||
this.variableItemId,
|
||||
this.displayType,
|
||||
this.placeholderType,
|
||||
this.userSource,
|
||||
this.furniSource,
|
||||
this.placeholderName,
|
||||
this.delimiter,
|
||||
this.items.stream().map(HabboItem::getId).collect(Collectors.toList())
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
this.refresh(room);
|
||||
|
||||
List<HabboItem> selectedItems = new ArrayList<>();
|
||||
if (this.targetType == TARGET_FURNI && this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
|
||||
selectedItems.addAll(this.items);
|
||||
}
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(selectedItems.size());
|
||||
|
||||
for (HabboItem item : selectedItems) {
|
||||
message.appendInt(item.getId());
|
||||
}
|
||||
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableToken + "\t" + this.placeholderName + "\t" + this.delimiter);
|
||||
message.appendInt(5);
|
||||
message.appendInt(this.targetType);
|
||||
message.appendInt(this.displayType);
|
||||
message.appendInt(this.placeholderType);
|
||||
message.appendInt(this.userSource);
|
||||
message.appendInt(this.furniSource);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.targetType = normalizeTargetType(data.targetType);
|
||||
this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
|
||||
this.displayType = normalizeDisplayType(data.displayType);
|
||||
this.placeholderType = normalizePlaceholderType(data.placeholderType);
|
||||
this.userSource = normalizeUserSource(data.userSource);
|
||||
this.furniSource = normalizeFurniSource(data.furniSource);
|
||||
this.placeholderName = normalizePlaceholderName(data.placeholderName);
|
||||
this.delimiter = normalizeDelimiter(data.delimiter);
|
||||
|
||||
if (room != null && data.itemIds != null) {
|
||||
for (Integer itemId : data.itemIds) {
|
||||
if (itemId == null || itemId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
if (item != null) {
|
||||
this.items.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (room == null || !canUseTextualDisplay(room, this.targetType, this.variableToken)) {
|
||||
this.displayType = DISPLAY_NUMERIC;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
String[] legacyData = splitStringData(wiredData);
|
||||
this.setVariableToken(normalizeVariableToken(legacyData[0]));
|
||||
this.placeholderName = normalizePlaceholderName(legacyData[1]);
|
||||
this.delimiter = normalizeDelimiter(legacyData[2]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.items.clear();
|
||||
this.targetType = TARGET_USER;
|
||||
this.setVariableToken(DEFAULT_VARIABLE_TOKEN);
|
||||
this.displayType = DISPLAY_NUMERIC;
|
||||
this.placeholderType = TYPE_SINGLE;
|
||||
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.placeholderName = DEFAULT_PLACEHOLDER_NAME;
|
||||
this.delimiter = DEFAULT_DELIMITER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getTargetType() {
|
||||
return this.targetType;
|
||||
}
|
||||
|
||||
public String getVariableToken() {
|
||||
return this.variableToken;
|
||||
}
|
||||
|
||||
public int getVariableItemId() {
|
||||
return this.variableItemId;
|
||||
}
|
||||
|
||||
public int getDisplayType(Room room) {
|
||||
return (this.displayType == DISPLAY_TEXTUAL && canUseTextualDisplay(room, this.targetType, this.variableToken))
|
||||
? DISPLAY_TEXTUAL
|
||||
: DISPLAY_NUMERIC;
|
||||
}
|
||||
|
||||
public int getPlaceholderType() {
|
||||
return this.placeholderType;
|
||||
}
|
||||
|
||||
public String getPlaceholderName() {
|
||||
return this.placeholderName;
|
||||
}
|
||||
|
||||
public String getPlaceholderToken() {
|
||||
return this.placeholderName.isEmpty() ? "" : "$(" + this.placeholderName + ")";
|
||||
}
|
||||
|
||||
public String getDelimiter() {
|
||||
return this.delimiter;
|
||||
}
|
||||
|
||||
public int getUserSource() {
|
||||
return this.userSource;
|
||||
}
|
||||
|
||||
public int getFurniSource() {
|
||||
return this.furniSource;
|
||||
}
|
||||
|
||||
public THashSet<HabboItem> getItems() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public boolean requiresActor() {
|
||||
return this.targetType == TARGET_USER
|
||||
&& (this.userSource == WiredSourceUtil.SOURCE_TRIGGER || this.userSource == WiredSourceUtil.SOURCE_CLICKED_USER);
|
||||
}
|
||||
|
||||
public void refresh(Room room) {
|
||||
THashSet<HabboItem> remove = new THashSet<>();
|
||||
|
||||
for (HabboItem item : this.items) {
|
||||
if (room == null || room.getHabboItem(item.getId()) == null) {
|
||||
remove.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
for (HabboItem item : remove) {
|
||||
this.items.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isCustomVariableToken(String token) {
|
||||
return token != null && token.startsWith(CUSTOM_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
public static boolean isInternalVariableToken(String token) {
|
||||
return token != null && token.startsWith(INTERNAL_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
public static String getInternalVariableKey(String token) {
|
||||
return isInternalVariableToken(token) ? WiredInternalVariableSupport.normalizeKey(token.substring(INTERNAL_TOKEN_PREFIX.length())) : "";
|
||||
}
|
||||
|
||||
public static int getCustomItemId(String token) {
|
||||
if (!isCustomVariableToken(token)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(token.substring(CUSTOM_TOKEN_PREFIX.length()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] splitStringData(String value) {
|
||||
if (value == null) {
|
||||
return new String[]{ DEFAULT_VARIABLE_TOKEN, DEFAULT_PLACEHOLDER_NAME, DEFAULT_DELIMITER };
|
||||
}
|
||||
|
||||
String[] parts = value.split("\t", -1);
|
||||
if (parts.length == 1) {
|
||||
return new String[]{ value, DEFAULT_PLACEHOLDER_NAME, DEFAULT_DELIMITER };
|
||||
}
|
||||
|
||||
if (parts.length == 2) {
|
||||
return new String[]{ parts[0], parts[1], DEFAULT_DELIMITER };
|
||||
}
|
||||
|
||||
return new String[]{ parts[0], parts[1], parts[2] };
|
||||
}
|
||||
|
||||
private static int normalizeTargetType(int value) {
|
||||
return switch (value) {
|
||||
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
|
||||
default -> TARGET_USER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeDisplayType(int value) {
|
||||
return (value == DISPLAY_TEXTUAL) ? DISPLAY_TEXTUAL : DISPLAY_NUMERIC;
|
||||
}
|
||||
|
||||
private static int normalizePlaceholderType(int value) {
|
||||
return (value == TYPE_MULTIPLE) ? TYPE_MULTIPLE : TYPE_SINGLE;
|
||||
}
|
||||
|
||||
private static int normalizeUserSource(int value) {
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private static int normalizeFurniSource(int value) {
|
||||
return switch (value) {
|
||||
case WiredSourceUtil.SOURCE_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
|
||||
default -> WiredSourceUtil.SOURCE_TRIGGER;
|
||||
};
|
||||
}
|
||||
|
||||
private static String normalizePlaceholderName(String value) {
|
||||
if (value == null) {
|
||||
return DEFAULT_PLACEHOLDER_NAME;
|
||||
}
|
||||
|
||||
String normalized = value.trim().replace("\t", "").replace("\r", "").replace("\n", "");
|
||||
if (WRAPPED_PLACEHOLDER_PATTERN.matcher(normalized).matches()) {
|
||||
normalized = normalized.substring(2, normalized.length() - 1).trim();
|
||||
}
|
||||
|
||||
if (normalized.length() > MAX_PLACEHOLDER_NAME_LENGTH) {
|
||||
normalized = normalized.substring(0, MAX_PLACEHOLDER_NAME_LENGTH);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static String normalizeDelimiter(String value) {
|
||||
if (value == null) {
|
||||
return DEFAULT_DELIMITER;
|
||||
}
|
||||
|
||||
String normalized = value.replace("\t", "").replace("\r", "").replace("\n", "");
|
||||
if (normalized.length() > MAX_DELIMITER_LENGTH) {
|
||||
normalized = normalized.substring(0, MAX_DELIMITER_LENGTH);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static String normalizeVariableToken(String value) {
|
||||
String normalized = (value == null) ? "" : value.trim();
|
||||
if (normalized.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
if (isInternalVariableToken(normalized)) {
|
||||
return INTERNAL_TOKEN_PREFIX + WiredInternalVariableSupport.normalizeKey(normalized.substring(INTERNAL_TOKEN_PREFIX.length()));
|
||||
}
|
||||
|
||||
try {
|
||||
int parsedValue = Integer.parseInt(normalized);
|
||||
return parsedValue > 0 ? (CUSTOM_TOKEN_PREFIX + parsedValue) : "";
|
||||
} catch (NumberFormatException ignored) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private void setVariableToken(String token) {
|
||||
this.variableToken = normalizeVariableToken(token);
|
||||
this.variableItemId = getCustomItemId(this.variableToken);
|
||||
}
|
||||
|
||||
private static boolean canUseTextualDisplay(Room room, int targetType, String variableToken) {
|
||||
if (room == null || !isCustomVariableToken(variableToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int itemId = getCustomItemId(variableToken);
|
||||
if (itemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (targetType) {
|
||||
case TARGET_USER -> {
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(itemId);
|
||||
yield definition != null && definition.hasValue() && definition.isTextConnected();
|
||||
}
|
||||
case TARGET_FURNI -> {
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(itemId);
|
||||
yield definition != null && definition.hasValue() && definition.isTextConnected();
|
||||
}
|
||||
case TARGET_CONTEXT -> {
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, itemId);
|
||||
yield definition != null && definition.hasValue() && definition.isTextConnected();
|
||||
}
|
||||
case TARGET_ROOM -> {
|
||||
WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(itemId);
|
||||
yield definition != null && definition.hasValue() && definition.isTextConnected();
|
||||
}
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isValidVariable(Room room, int targetType, String variableToken) {
|
||||
if (room == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (targetType) {
|
||||
case TARGET_USER -> isInternalVariableToken(variableToken)
|
||||
? canUseUserInternalReference(getInternalVariableKey(variableToken))
|
||||
: isUserCustomValue(room, getCustomItemId(variableToken));
|
||||
case TARGET_FURNI -> isInternalVariableToken(variableToken)
|
||||
? canUseFurniInternalReference(getInternalVariableKey(variableToken))
|
||||
: isFurniCustomValue(room, getCustomItemId(variableToken));
|
||||
case TARGET_CONTEXT -> isInternalVariableToken(variableToken)
|
||||
? WiredInternalVariableSupport.canUseContextReference(getInternalVariableKey(variableToken))
|
||||
: isContextCustomValue(room, getCustomItemId(variableToken));
|
||||
case TARGET_ROOM -> isInternalVariableToken(variableToken)
|
||||
? canUseRoomInternalReference(getInternalVariableKey(variableToken))
|
||||
: isRoomCustomValue(room, getCustomItemId(variableToken));
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isUserCustomValue(Room room, int itemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getUserVariableManager().getDefinitionInfo(itemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private static boolean isFurniCustomValue(Room room, int itemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getFurniVariableManager().getDefinitionInfo(itemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private static boolean isRoomCustomValue(Room room, int itemId) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getRoomVariableManager().getDefinitionInfo(itemId) : null;
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private static boolean isContextCustomValue(Room room, int itemId) {
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, itemId);
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private static boolean canUseUserInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseFurniInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseRoomInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseRoomReference(key);
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int targetType;
|
||||
String variableToken;
|
||||
int variableItemId;
|
||||
int displayType;
|
||||
int placeholderType;
|
||||
int userSource;
|
||||
int furniSource;
|
||||
String placeholderName;
|
||||
String delimiter;
|
||||
List<Integer> itemIds;
|
||||
|
||||
JsonData(int targetType, String variableToken, int variableItemId, int displayType, int placeholderType, int userSource, int furniSource, String placeholderName, String delimiter, List<Integer> itemIds) {
|
||||
this.targetType = targetType;
|
||||
this.variableToken = variableToken;
|
||||
this.variableItemId = variableItemId;
|
||||
this.displayType = displayType;
|
||||
this.placeholderType = placeholderType;
|
||||
this.userSource = userSource;
|
||||
this.furniSource = furniSource;
|
||||
this.placeholderName = placeholderName;
|
||||
this.delimiter = delimiter;
|
||||
this.itemIds = itemIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredExtraUserVariable extends InteractionWiredExtra {
|
||||
public static final int CODE = 70;
|
||||
public static final int AVAILABILITY_ROOM = 0;
|
||||
public static final int AVAILABILITY_PERMANENT = 10;
|
||||
public static final int AVAILABILITY_SHARED = 11;
|
||||
|
||||
private String variableName = "";
|
||||
private boolean hasValue = false;
|
||||
private int availability = AVAILABILITY_ROOM;
|
||||
|
||||
public WiredExtraUserVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraUserVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
int[] intParams = settings.getIntParams();
|
||||
String normalizedName = WiredVariableNameValidator.normalizeForSave(settings.getStringParam());
|
||||
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
WiredVariableNameValidator.validateDefinitionName(room, this.getId(), normalizedName);
|
||||
|
||||
this.variableName = normalizedName;
|
||||
this.hasValue = (intParams.length > 0) && (intParams[0] == 1);
|
||||
this.availability = normalizeAvailability((intParams.length > 1) ? intParams[1] : AVAILABILITY_ROOM);
|
||||
|
||||
room.getUserVariableManager().handleDefinitionUpdated(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.variableName, this.hasValue, this.availability));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableName);
|
||||
message.appendInt(2);
|
||||
message.appendInt(this.hasValue ? 1 : 0);
|
||||
message.appendInt(this.availability);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(data.variableName);
|
||||
this.hasValue = data.hasValue;
|
||||
this.availability = normalizeAvailability(data.availability);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(wiredData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableName = "";
|
||||
this.hasValue = false;
|
||||
this.availability = AVAILABILITY_ROOM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getVariableName() {
|
||||
return variableName;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return hasValue;
|
||||
}
|
||||
|
||||
public int getAvailability() {
|
||||
return availability;
|
||||
}
|
||||
|
||||
public boolean isPermanentAvailability() {
|
||||
return this.availability == AVAILABILITY_PERMANENT || this.availability == AVAILABILITY_SHARED;
|
||||
}
|
||||
|
||||
public boolean isSharedAvailability() {
|
||||
return this.availability == AVAILABILITY_SHARED;
|
||||
}
|
||||
|
||||
private static int normalizeAvailability(int value) {
|
||||
if (value == AVAILABILITY_PERMANENT || value == AVAILABILITY_SHARED) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return AVAILABILITY_ROOM;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String variableName;
|
||||
boolean hasValue;
|
||||
int availability;
|
||||
|
||||
JsonData(String variableName, boolean hasValue, int availability) {
|
||||
this.variableName = variableName;
|
||||
this.hasValue = hasValue;
|
||||
this.availability = availability;
|
||||
}
|
||||
}
|
||||
}
|
||||
+800
@@ -0,0 +1,800 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.games.Game;
|
||||
import com.eu.habbo.habbohotel.games.GamePlayer;
|
||||
import com.eu.habbo.habbohotel.games.GameTeam;
|
||||
import com.eu.habbo.habbohotel.games.GameTeamColors;
|
||||
import com.eu.habbo.habbohotel.items.FurnitureType;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.FurnitureMovementError;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTileState;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomRightLevels;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.DanceType;
|
||||
import com.eu.habbo.habbohotel.users.HabboGender;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredUserMovementHelper;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredVariableLevelSystemSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredVariableTextConnectorSupport;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import com.eu.habbo.util.HotelDateTimeUtil;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.Locale;
|
||||
|
||||
public class WiredExtraVariableEcho extends InteractionWiredExtra {
|
||||
public static final int CODE = 83;
|
||||
public static final int TARGET_USER = 0;
|
||||
public static final int TARGET_FURNI = 1;
|
||||
public static final int TARGET_ROOM = 3;
|
||||
|
||||
private static final String CUSTOM_TOKEN_PREFIX = "custom:";
|
||||
private static final String INTERNAL_TOKEN_PREFIX = "internal:";
|
||||
private static final int DEFAULT_USER_AVAILABILITY = WiredExtraUserVariable.AVAILABILITY_ROOM;
|
||||
private static final int DEFAULT_FURNI_AVAILABILITY = WiredExtraFurniVariable.AVAILABILITY_ROOM_ACTIVE;
|
||||
private static final int DEFAULT_ROOM_AVAILABILITY = WiredExtraRoomVariable.AVAILABILITY_ROOM_ACTIVE;
|
||||
|
||||
private String variableName = "";
|
||||
private int sourceTargetType = TARGET_USER;
|
||||
private String sourceVariableToken = "";
|
||||
private int sourceVariableItemId = 0;
|
||||
private String sourceVariableName = "";
|
||||
|
||||
public WiredExtraVariableEcho(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraVariableEcho(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
ConfigData config = parseConfigData(settings.getStringParam());
|
||||
int normalizedTargetType = normalizeTargetType(config.sourceTargetType);
|
||||
String normalizedToken = normalizeVariableToken(config.sourceVariableToken, config.sourceVariableItemId);
|
||||
int normalizedItemId = getCustomVariableItemId(normalizedToken);
|
||||
SourceState sourceState = this.resolveSourceState(room, normalizedTargetType, normalizedToken, normalizedItemId);
|
||||
|
||||
if (normalizedToken.isEmpty()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
}
|
||||
|
||||
if (sourceState == null || !sourceState.hasValue()) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
if (!isAllowedEchoSource(sourceState, normalizedToken)) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
if (!WiredVariableTextConnectorSupport.isTextConnected(room, this)) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
if (createsCycle(room, this.getId(), normalizedTargetType, normalizedToken, normalizedItemId)) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
String normalizedName = deriveVariableName(config.variableName, sourceState.getName());
|
||||
WiredVariableNameValidator.validateDefinitionName(room, this.getId(), normalizedName);
|
||||
|
||||
this.variableName = normalizedName;
|
||||
this.sourceTargetType = normalizedTargetType;
|
||||
this.sourceVariableToken = normalizedToken;
|
||||
this.sourceVariableItemId = normalizedItemId;
|
||||
this.sourceVariableName = sourceState.getName();
|
||||
|
||||
room.getUserVariableManager().broadcastSnapshot();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.variableName, this.sourceTargetType, this.sourceVariableToken, this.sourceVariableItemId, this.sourceVariableName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(WiredManager.getGson().toJson(new EditorPayload(this.variableName, this.sourceTargetType, this.sourceVariableToken, this.sourceVariableItemId, this.getResolvedSourceName(room))));
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(data.variableName);
|
||||
this.sourceTargetType = normalizeTargetType(data.sourceTargetType);
|
||||
this.sourceVariableToken = normalizeVariableToken(data.sourceVariableToken, data.sourceVariableItemId);
|
||||
this.sourceVariableItemId = getCustomVariableItemId(this.sourceVariableToken);
|
||||
this.sourceVariableName = normalizeSourceName(data.sourceVariableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableName = "";
|
||||
this.sourceTargetType = TARGET_USER;
|
||||
this.sourceVariableToken = "";
|
||||
this.sourceVariableItemId = 0;
|
||||
this.sourceVariableName = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getVariableName() {
|
||||
return this.variableName;
|
||||
}
|
||||
|
||||
public int getSourceTargetType() {
|
||||
return this.sourceTargetType;
|
||||
}
|
||||
|
||||
public String getSourceVariableToken() {
|
||||
return this.sourceVariableToken;
|
||||
}
|
||||
|
||||
public int getSourceVariableItemId() {
|
||||
return this.sourceVariableItemId;
|
||||
}
|
||||
|
||||
public String getSourceVariableName() {
|
||||
return this.sourceVariableName;
|
||||
}
|
||||
|
||||
public boolean isUserEcho() {
|
||||
return this.sourceTargetType == TARGET_USER;
|
||||
}
|
||||
|
||||
public boolean isFurniEcho() {
|
||||
return this.sourceTargetType == TARGET_FURNI;
|
||||
}
|
||||
|
||||
public boolean isRoomEcho() {
|
||||
return this.sourceTargetType == TARGET_ROOM;
|
||||
}
|
||||
|
||||
public WiredVariableDefinitionInfo createDefinitionInfo(Room room) {
|
||||
SourceState sourceState = this.resolveSourceState(room, this.sourceTargetType, this.sourceVariableToken, this.sourceVariableItemId);
|
||||
int availability = (sourceState != null) ? sourceState.getAvailability() : defaultAvailability(this.sourceTargetType);
|
||||
boolean hasValue = (sourceState == null) || sourceState.hasValue();
|
||||
boolean readOnly = sourceState == null || sourceState.isReadOnly();
|
||||
|
||||
return new WiredVariableDefinitionInfo(
|
||||
this.getId(),
|
||||
this.variableName,
|
||||
hasValue,
|
||||
availability,
|
||||
WiredVariableTextConnectorSupport.isTextConnected(room, this),
|
||||
readOnly
|
||||
);
|
||||
}
|
||||
|
||||
public boolean hasVariable(Room room, int entityId) {
|
||||
if (room == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(this.sourceVariableToken)) {
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().hasVariable(entityId, this.sourceVariableItemId);
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().hasVariable(this.sourceVariableItemId);
|
||||
default -> room.getUserVariableManager().hasVariable(entityId, this.sourceVariableItemId);
|
||||
};
|
||||
}
|
||||
|
||||
return this.readCurrentValue(room, entityId) != null;
|
||||
}
|
||||
|
||||
public int getCurrentValue(Room room, int entityId) {
|
||||
Integer value = this.readCurrentValue(room, entityId);
|
||||
return (value != null) ? value : 0;
|
||||
}
|
||||
|
||||
public int getCreatedAt(Room room, int entityId) {
|
||||
if (room == null || !isCustomVariableToken(this.sourceVariableToken)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().getCreatedAt(entityId, this.sourceVariableItemId);
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().getCreatedAt(this.sourceVariableItemId);
|
||||
default -> room.getUserVariableManager().getCreatedAt(entityId, this.sourceVariableItemId);
|
||||
};
|
||||
}
|
||||
|
||||
public int getUpdatedAt(Room room, int entityId) {
|
||||
if (room == null || !isCustomVariableToken(this.sourceVariableToken)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().getUpdatedAt(entityId, this.sourceVariableItemId);
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().getUpdatedAt(this.sourceVariableItemId);
|
||||
default -> room.getUserVariableManager().getUpdatedAt(entityId, this.sourceVariableItemId);
|
||||
};
|
||||
}
|
||||
|
||||
public boolean assignValue(Room room, int entityId, Integer value, boolean overrideExisting) {
|
||||
if (room == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SourceState sourceState = this.resolveSourceState(room, this.sourceTargetType, this.sourceVariableToken, this.sourceVariableItemId);
|
||||
if (sourceState == null || sourceState.isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(this.sourceVariableToken)) {
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().assignVariable(room.getHabboItem(entityId), this.sourceVariableItemId, value, overrideExisting);
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().updateVariableValue(this.sourceVariableItemId, (value != null) ? value : 0);
|
||||
default -> room.getUserVariableManager().assignVariable(room.getHabbo(entityId), this.sourceVariableItemId, value, overrideExisting);
|
||||
};
|
||||
}
|
||||
|
||||
return value != null && this.writeCurrentValue(room, entityId, value);
|
||||
}
|
||||
|
||||
public boolean updateValue(Room room, int entityId, Integer value) {
|
||||
if (room == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SourceState sourceState = this.resolveSourceState(room, this.sourceTargetType, this.sourceVariableToken, this.sourceVariableItemId);
|
||||
if (sourceState == null || sourceState.isReadOnly() || !sourceState.hasValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(this.sourceVariableToken)) {
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().updateVariableValue(entityId, this.sourceVariableItemId, value);
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().updateVariableValue(this.sourceVariableItemId, (value != null) ? value : 0);
|
||||
default -> room.getUserVariableManager().updateVariableValue(entityId, this.sourceVariableItemId, value);
|
||||
};
|
||||
}
|
||||
|
||||
return value != null && this.writeCurrentValue(room, entityId, value);
|
||||
}
|
||||
|
||||
public boolean removeValue(Room room, int entityId) {
|
||||
if (room == null || !isCustomVariableToken(this.sourceVariableToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SourceState sourceState = this.resolveSourceState(room, this.sourceTargetType, this.sourceVariableToken, this.sourceVariableItemId);
|
||||
if (sourceState == null || sourceState.isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().removeVariable(entityId, this.sourceVariableItemId);
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().removeVariable(this.sourceVariableItemId);
|
||||
default -> room.getUserVariableManager().removeVariable(entityId, this.sourceVariableItemId);
|
||||
};
|
||||
}
|
||||
|
||||
private Integer readCurrentValue(Room room, int entityId) {
|
||||
if (room == null || this.sourceVariableToken == null || this.sourceVariableToken.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(this.sourceVariableToken)) {
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().hasVariable(entityId, this.sourceVariableItemId)
|
||||
? room.getFurniVariableManager().getCurrentValue(entityId, this.sourceVariableItemId)
|
||||
: null;
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().getCurrentValue(this.sourceVariableItemId);
|
||||
default -> room.getUserVariableManager().hasVariable(entityId, this.sourceVariableItemId)
|
||||
? room.getUserVariableManager().getCurrentValue(entityId, this.sourceVariableItemId)
|
||||
: null;
|
||||
};
|
||||
}
|
||||
|
||||
String key = getInternalVariableKey(this.sourceVariableToken);
|
||||
if (key.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> this.readFurniInternalValue(room, room.getHabboItem(entityId), key);
|
||||
case TARGET_ROOM -> this.readRoomInternalValue(room, key);
|
||||
default -> {
|
||||
Habbo habbo = room.getHabbo(entityId);
|
||||
yield this.readUserInternalValue(room, (habbo != null) ? habbo.getRoomUnit() : null, key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean writeCurrentValue(Room room, int entityId, int value) {
|
||||
if (room == null || !isInternalVariableToken(this.sourceVariableToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String key = getInternalVariableKey(this.sourceVariableToken);
|
||||
if (key.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (this.sourceTargetType) {
|
||||
case TARGET_FURNI -> this.writeFurniInternalValue(room, room.getHabboItem(entityId), key, value);
|
||||
case TARGET_ROOM -> false;
|
||||
default -> {
|
||||
Habbo habbo = room.getHabbo(entityId);
|
||||
yield this.writeUserInternalValue(room, (habbo != null) ? habbo.getRoomUnit() : null, key, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private SourceState resolveSourceState(Room room, int targetType, String token, int variableItemId) {
|
||||
if (room == null || token == null || token.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isCustomVariableToken(token)) {
|
||||
WiredVariableDefinitionInfo definitionInfo = switch (targetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().getDefinitionInfo(variableItemId);
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().getDefinitionInfo(variableItemId);
|
||||
default -> room.getUserVariableManager().getDefinitionInfo(variableItemId);
|
||||
};
|
||||
|
||||
if (definitionInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SourceState(definitionInfo.getName(), definitionInfo.hasValue(), definitionInfo.getAvailability(), definitionInfo.isReadOnly());
|
||||
}
|
||||
|
||||
String key = getInternalVariableKey(token);
|
||||
if (key.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return switch (targetType) {
|
||||
case TARGET_FURNI -> canUseFurniInternalReference(key)
|
||||
? new SourceState(key, true, DEFAULT_FURNI_AVAILABILITY, !canUseFurniInternalDestination(key))
|
||||
: null;
|
||||
case TARGET_ROOM -> canUseRoomInternalReference(key)
|
||||
? new SourceState(key, true, DEFAULT_ROOM_AVAILABILITY, true)
|
||||
: null;
|
||||
default -> canUseUserInternalReference(key)
|
||||
? new SourceState(key, true, DEFAULT_USER_AVAILABILITY, !canUseUserInternalDestination(key))
|
||||
: null;
|
||||
};
|
||||
}
|
||||
|
||||
private String getResolvedSourceName(Room room) {
|
||||
SourceState sourceState = this.resolveSourceState(room, this.sourceTargetType, this.sourceVariableToken, this.sourceVariableItemId);
|
||||
return (sourceState != null) ? sourceState.getName() : this.sourceVariableName;
|
||||
}
|
||||
|
||||
private static boolean createsCycle(Room room, int currentItemId, int targetType, String token, int variableItemId) {
|
||||
if (room == null || currentItemId <= 0 || !isCustomVariableToken(token) || variableItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (variableItemId == currentItemId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
WiredVariableLevelSystemSupport.DerivedDefinition derivedDefinition = WiredVariableLevelSystemSupport.resolveDerivedDefinition(room, targetType, variableItemId);
|
||||
if (derivedDefinition != null) {
|
||||
return createsCycle(room, currentItemId, targetType, createCustomVariableToken(derivedDefinition.getBaseDefinitionItemId()), derivedDefinition.getBaseDefinitionItemId());
|
||||
}
|
||||
|
||||
if (room.getRoomSpecialTypes() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InteractionWiredExtra extra = room.getRoomSpecialTypes().getExtra(variableItemId);
|
||||
if (!(extra instanceof WiredExtraVariableEcho)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WiredExtraVariableEcho echo = (WiredExtraVariableEcho) extra;
|
||||
return createsCycle(room, currentItemId, echo.getSourceTargetType(), echo.getSourceVariableToken(), echo.getSourceVariableItemId());
|
||||
}
|
||||
|
||||
private static String deriveVariableName(String requestedName, String sourceName) {
|
||||
String normalizedRequestedName = WiredVariableNameValidator.normalizeForSave(requestedName);
|
||||
if (!normalizedRequestedName.isEmpty()) {
|
||||
return normalizedRequestedName;
|
||||
}
|
||||
|
||||
String fallbackValue = normalizeSourceName(sourceName)
|
||||
.replaceAll("^[~@]+", "")
|
||||
.replaceAll("[^A-Za-z0-9_]+", "_")
|
||||
.replaceAll("_+", "_")
|
||||
.replaceAll("^_+", "")
|
||||
.replaceAll("_+$", "");
|
||||
|
||||
if (fallbackValue.length() > WiredVariableNameValidator.MAX_NAME_LENGTH) {
|
||||
fallbackValue = fallbackValue.substring(0, WiredVariableNameValidator.MAX_NAME_LENGTH);
|
||||
}
|
||||
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
private static boolean isAllowedEchoSource(SourceState sourceState, String token) {
|
||||
if (sourceState == null || token == null || token.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isInternalVariableToken(token)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isCustomVariableToken(token) && sourceState.getName() != null && sourceState.getName().contains(".");
|
||||
}
|
||||
|
||||
private static ConfigData parseConfigData(String value) {
|
||||
if (value == null || value.isEmpty() || !value.startsWith("{")) {
|
||||
return new ConfigData();
|
||||
}
|
||||
|
||||
ConfigData config = WiredManager.getGson().fromJson(value, ConfigData.class);
|
||||
return (config != null) ? config : new ConfigData();
|
||||
}
|
||||
|
||||
private static String normalizeSourceName(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return value.trim().replace("\t", "").replace("\r", "").replace("\n", "");
|
||||
}
|
||||
|
||||
private static int normalizeTargetType(int value) {
|
||||
if (value == TARGET_FURNI || value == TARGET_ROOM) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return TARGET_USER;
|
||||
}
|
||||
|
||||
private static int defaultAvailability(int targetType) {
|
||||
return switch (targetType) {
|
||||
case TARGET_FURNI -> DEFAULT_FURNI_AVAILABILITY;
|
||||
case TARGET_ROOM -> DEFAULT_ROOM_AVAILABILITY;
|
||||
default -> DEFAULT_USER_AVAILABILITY;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isCustomVariableToken(String token) {
|
||||
return token != null && token.startsWith(CUSTOM_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
private static boolean isInternalVariableToken(String token) {
|
||||
return token != null && token.startsWith(INTERNAL_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
private static String getInternalVariableKey(String token) {
|
||||
return isInternalVariableToken(token) ? WiredInternalVariableSupport.normalizeKey(token.substring(INTERNAL_TOKEN_PREFIX.length())) : "";
|
||||
}
|
||||
|
||||
private static int getCustomVariableItemId(String token) {
|
||||
if (!isCustomVariableToken(token)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(token.substring(CUSTOM_TOKEN_PREFIX.length()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static String createCustomVariableToken(int itemId) {
|
||||
return itemId > 0 ? CUSTOM_TOKEN_PREFIX + itemId : "";
|
||||
}
|
||||
|
||||
private static String normalizeVariableToken(String token, int fallbackItemId) {
|
||||
String normalizedToken = (token != null) ? token.trim() : "";
|
||||
|
||||
if (isCustomVariableToken(normalizedToken)) {
|
||||
return normalizedToken;
|
||||
}
|
||||
|
||||
if (isInternalVariableToken(normalizedToken)) {
|
||||
return INTERNAL_TOKEN_PREFIX + WiredInternalVariableSupport.normalizeKey(normalizedToken.substring(INTERNAL_TOKEN_PREFIX.length()));
|
||||
}
|
||||
|
||||
if (fallbackItemId > 0) {
|
||||
return createCustomVariableToken(fallbackItemId);
|
||||
}
|
||||
|
||||
if (normalizedToken.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return createCustomVariableToken(Integer.parseInt(normalizedToken));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private Integer readUserInternalValue(Room room, RoomUnit roomUnit, String key) {
|
||||
return WiredInternalVariableSupport.readUserValue(room, roomUnit, key);
|
||||
}
|
||||
|
||||
private Integer getUserTypeValue(Habbo habbo, Bot bot, Pet pet) {
|
||||
if (habbo != null) return 1;
|
||||
if (bot != null) return 2;
|
||||
if (pet != null) return 3;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Integer getGenderValue(Habbo habbo, Bot bot) {
|
||||
HabboGender gender = null;
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null) {
|
||||
gender = habbo.getHabboInfo().getGender();
|
||||
} else if (bot != null) {
|
||||
gender = bot.getGender();
|
||||
}
|
||||
|
||||
if (gender == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return gender == HabboGender.F ? 1 : 2;
|
||||
}
|
||||
|
||||
private boolean writeUserInternalValue(Room room, RoomUnit roomUnit, String key, int value) {
|
||||
return WiredInternalVariableSupport.writeUserValue(room, roomUnit, key, value);
|
||||
}
|
||||
|
||||
private Integer readFurniInternalValue(Room room, HabboItem item, String key) {
|
||||
return WiredInternalVariableSupport.readFurniValue(room, item, key);
|
||||
}
|
||||
|
||||
private boolean writeFurniInternalValue(Room room, HabboItem item, String key, int value) {
|
||||
return WiredInternalVariableSupport.writeFurniValue(room, item, key, value);
|
||||
}
|
||||
|
||||
private Integer readRoomInternalValue(Room room, String key) {
|
||||
return WiredInternalVariableSupport.readRoomValue(room, key);
|
||||
}
|
||||
|
||||
private Integer getUserTeamScore(Room room, Habbo habbo) {
|
||||
if (room == null || habbo == null || habbo.getHabboInfo() == null || habbo.getHabboInfo().getGamePlayer() == null) return null;
|
||||
|
||||
Game game = this.resolveTeamGame(room, habbo);
|
||||
GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer();
|
||||
|
||||
if (game == null || gamePlayer.getTeamColor() == null) return gamePlayer.getScore();
|
||||
|
||||
GameTeam team = game.getTeam(gamePlayer.getTeamColor());
|
||||
return (team != null) ? team.getTotalScore() : gamePlayer.getScore();
|
||||
}
|
||||
|
||||
private int getTeamMetric(Room room, GameTeamColors color, boolean score) {
|
||||
Game game = this.resolveTeamGame(room, null);
|
||||
if (game == null || color == null) return 0;
|
||||
|
||||
GameTeam team = game.getTeam(color);
|
||||
if (team == null) return 0;
|
||||
|
||||
return score ? team.getTotalScore() : team.getMembers().size();
|
||||
}
|
||||
|
||||
private int getTeamColorId(int effectId) {
|
||||
if (effectId >= 33 && effectId <= 36) return effectId - 32;
|
||||
if (effectId >= 40 && effectId <= 43) return effectId - 39;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int getTeamTypeId(int effectId) {
|
||||
if (effectId >= 33 && effectId <= 36) return 1;
|
||||
if (effectId >= 40 && effectId <= 43) return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Game resolveTeamGame(Room room, Habbo habbo) {
|
||||
if (room == null) return null;
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getCurrentGame() != null) {
|
||||
Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
|
||||
if (game != null) return game;
|
||||
}
|
||||
|
||||
Game game = room.getGame(com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame.class);
|
||||
if (game != null) return game;
|
||||
|
||||
game = room.getGame(com.eu.habbo.habbohotel.games.freeze.FreezeGame.class);
|
||||
if (game != null) return game;
|
||||
|
||||
return room.getGame(com.eu.habbo.habbohotel.games.wired.WiredGame.class);
|
||||
}
|
||||
|
||||
private boolean moveUserTo(Room room, RoomUnit roomUnit, int x, int y) {
|
||||
if (room == null || roomUnit == null || room.getLayout() == null) return false;
|
||||
|
||||
RoomTile targetTile = room.getLayout().getTile((short) x, (short) y);
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) return false;
|
||||
|
||||
double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0);
|
||||
return WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), 0, true);
|
||||
}
|
||||
|
||||
private boolean moveFurniTo(Room room, HabboItem item, int x, int y, int rotation, double z) {
|
||||
if (room == null || item == null || room.getLayout() == null) return false;
|
||||
|
||||
RoomTile targetTile = room.getLayout().getTile((short) x, (short) y);
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) return false;
|
||||
|
||||
FurnitureMovementError error = room.moveFurniTo(item, targetTile, rotation, z, null, true, true);
|
||||
return error == FurnitureMovementError.NONE;
|
||||
}
|
||||
|
||||
private static Integer parseInteger(String value) {
|
||||
if (value == null || value.isEmpty()) return 0;
|
||||
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canUseUserInternalDestination(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserDestination(key);
|
||||
}
|
||||
|
||||
private static boolean canUseFurniInternalDestination(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniDestination(key);
|
||||
}
|
||||
|
||||
private static boolean canUseUserInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseFurniInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseRoomInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseRoomReference(key);
|
||||
}
|
||||
|
||||
private Integer getRoomEntryMethodValue(Habbo habbo) {
|
||||
if (habbo == null || habbo.getHabboInfo() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String roomEntryMethod = habbo.getHabboInfo().getRoomEntryMethod();
|
||||
|
||||
if (roomEntryMethod == null || roomEntryMethod.trim().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return switch (roomEntryMethod.trim().toLowerCase(Locale.ROOT)) {
|
||||
case "door" -> 1;
|
||||
case "teleport" -> 2;
|
||||
default -> 3;
|
||||
};
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String variableName;
|
||||
int sourceTargetType;
|
||||
String sourceVariableToken;
|
||||
int sourceVariableItemId;
|
||||
String sourceVariableName;
|
||||
|
||||
JsonData(String variableName, int sourceTargetType, String sourceVariableToken, int sourceVariableItemId, String sourceVariableName) {
|
||||
this.variableName = variableName;
|
||||
this.sourceTargetType = sourceTargetType;
|
||||
this.sourceVariableToken = sourceVariableToken;
|
||||
this.sourceVariableItemId = sourceVariableItemId;
|
||||
this.sourceVariableName = sourceVariableName;
|
||||
}
|
||||
}
|
||||
|
||||
static class ConfigData {
|
||||
String variableName = "";
|
||||
int sourceTargetType = TARGET_USER;
|
||||
String sourceVariableToken = "";
|
||||
int sourceVariableItemId = 0;
|
||||
}
|
||||
|
||||
static class EditorPayload extends ConfigData {
|
||||
String sourceVariableName;
|
||||
|
||||
EditorPayload(String variableName, int sourceTargetType, String sourceVariableToken, int sourceVariableItemId, String sourceVariableName) {
|
||||
this.variableName = variableName;
|
||||
this.sourceTargetType = sourceTargetType;
|
||||
this.sourceVariableToken = sourceVariableToken;
|
||||
this.sourceVariableItemId = sourceVariableItemId;
|
||||
this.sourceVariableName = sourceVariableName;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SourceState {
|
||||
private final String name;
|
||||
private final boolean hasValue;
|
||||
private final int availability;
|
||||
private final boolean readOnly;
|
||||
|
||||
private SourceState(String name, boolean hasValue, int availability, boolean readOnly) {
|
||||
this.name = name;
|
||||
this.hasValue = hasValue;
|
||||
this.availability = availability;
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return this.hasValue;
|
||||
}
|
||||
|
||||
public int getAvailability() {
|
||||
return this.availability;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return this.readOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
+757
@@ -0,0 +1,757 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.games.Game;
|
||||
import com.eu.habbo.habbohotel.games.GamePlayer;
|
||||
import com.eu.habbo.habbohotel.games.GameTeam;
|
||||
import com.eu.habbo.habbohotel.games.GameTeamColors;
|
||||
import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame;
|
||||
import com.eu.habbo.habbohotel.games.freeze.FreezeGame;
|
||||
import com.eu.habbo.habbohotel.games.wired.WiredGame;
|
||||
import com.eu.habbo.habbohotel.items.FurnitureType;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.users.DanceType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import com.eu.habbo.util.HotelDateTimeUtil;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public abstract class WiredExtraVariableFilterBase extends InteractionWiredExtra {
|
||||
protected static final int TARGET_USER = 0;
|
||||
protected static final int TARGET_FURNI = 1;
|
||||
protected static final int TARGET_CONTEXT = 2;
|
||||
protected static final int TARGET_ROOM = 3;
|
||||
|
||||
protected static final int AMOUNT_CONSTANT = 0;
|
||||
protected static final int AMOUNT_VARIABLE = 1;
|
||||
protected static final int SOURCE_SECONDARY_SELECTED = 101;
|
||||
|
||||
protected static final int SORT_VALUE_HIGHEST = 0;
|
||||
protected static final int SORT_VALUE_LOWEST = 1;
|
||||
protected static final int SORT_CREATION_OLDEST = 2;
|
||||
protected static final int SORT_CREATION_LATEST = 3;
|
||||
protected static final int SORT_UPDATE_OLDEST = 4;
|
||||
protected static final int SORT_UPDATE_LATEST = 5;
|
||||
|
||||
private static final int MAX_FILTER_AMOUNT = 10000;
|
||||
private static final String CUSTOM_TOKEN_PREFIX = "custom:";
|
||||
private static final String INTERNAL_TOKEN_PREFIX = "internal:";
|
||||
private static final String DELIM = "\t";
|
||||
|
||||
protected int sortBy = SORT_VALUE_HIGHEST;
|
||||
protected int amountMode = AMOUNT_CONSTANT;
|
||||
protected int amountConstantValue = 1;
|
||||
protected int referenceTargetType = TARGET_USER;
|
||||
protected int referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected int referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected String variableToken = "";
|
||||
protected int variableItemId = 0;
|
||||
protected String referenceVariableToken = "";
|
||||
protected int referenceVariableItemId = 0;
|
||||
protected final List<HabboItem> referenceSelectedItems = new ArrayList<>();
|
||||
|
||||
protected WiredExtraVariableFilterBase(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
protected WiredExtraVariableFilterBase(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
protected abstract int getVariableTargetType();
|
||||
|
||||
protected abstract int getCode();
|
||||
|
||||
public List<RoomUnit> filterUsers(Room room, WiredContext ctx, Iterable<RoomUnit> values) {
|
||||
if (room == null || ctx == null || this.getVariableTargetType() != TARGET_USER || this.variableToken.isEmpty()) {
|
||||
return toUserList(values);
|
||||
}
|
||||
|
||||
int amount = this.resolveAmount(ctx, room);
|
||||
if (amount <= 0) return new ArrayList<>();
|
||||
|
||||
List<SortableEntry<RoomUnit>> matches = new ArrayList<>();
|
||||
|
||||
for (RoomUnit roomUnit : values) {
|
||||
if (roomUnit == null) continue;
|
||||
|
||||
MetricSnapshot metric = this.resolveUserMetric(room, roomUnit);
|
||||
if (metric == null) continue;
|
||||
|
||||
matches.add(new SortableEntry<>(roomUnit, metric));
|
||||
}
|
||||
|
||||
matches.sort(this.metricComparator());
|
||||
return trimUsers(matches, amount);
|
||||
}
|
||||
|
||||
public List<HabboItem> filterItems(Room room, WiredContext ctx, Iterable<HabboItem> values) {
|
||||
if (room == null || ctx == null || this.getVariableTargetType() != TARGET_FURNI || this.variableToken.isEmpty()) {
|
||||
return toItemList(values);
|
||||
}
|
||||
|
||||
int amount = this.resolveAmount(ctx, room);
|
||||
if (amount <= 0) return new ArrayList<>();
|
||||
|
||||
List<SortableEntry<HabboItem>> matches = new ArrayList<>();
|
||||
|
||||
for (HabboItem item : values) {
|
||||
if (item == null) continue;
|
||||
|
||||
MetricSnapshot metric = this.resolveFurniMetric(room, item);
|
||||
if (metric == null) continue;
|
||||
|
||||
matches.add(new SortableEntry<>(item, metric));
|
||||
}
|
||||
|
||||
matches.sort(this.metricComparator());
|
||||
return trimItems(matches, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
if (room == null) throw new WiredSaveException("Room not found");
|
||||
|
||||
int[] params = settings.getIntParams();
|
||||
String[] stringParts = parseStringData(settings.getStringParam());
|
||||
String nextVariableToken = normalizeVariableToken((stringParts.length > 0) ? stringParts[0] : "");
|
||||
String nextReferenceVariableToken = normalizeVariableToken((stringParts.length > 1) ? stringParts[1] : "");
|
||||
int nextSortBy = normalizeSortBy(param(params, 0, SORT_VALUE_HIGHEST));
|
||||
int nextAmountMode = normalizeAmountMode(param(params, 1, AMOUNT_CONSTANT));
|
||||
int nextAmountConstantValue = normalizeAmount(param(params, 2, 1));
|
||||
int nextReferenceTargetType = normalizeReferenceTargetType(param(params, 3, TARGET_USER));
|
||||
int nextReferenceUserSource = normalizeUserSource(param(params, 4, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextReferenceFurniSource = normalizeReferenceFurniSource(param(params, 5, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
|
||||
if (!this.isValidMainVariable(room, nextVariableToken)) throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
if (nextAmountMode == AMOUNT_VARIABLE && !this.isValidReference(room, nextReferenceTargetType, nextReferenceVariableToken)) throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
|
||||
List<HabboItem> nextReferenceItems = new ArrayList<>();
|
||||
if (nextAmountMode == AMOUNT_VARIABLE && nextReferenceTargetType == TARGET_FURNI && nextReferenceFurniSource == SOURCE_SECONDARY_SELECTED) {
|
||||
int selectionLimit = Emulator.getConfig().getInt("hotel.wired.furni.selection.count");
|
||||
if (settings.getFurniIds().length > selectionLimit) throw new WiredSaveException("Too many furni selected");
|
||||
|
||||
for (int furniId : settings.getFurniIds()) {
|
||||
HabboItem item = room.getHabboItem(furniId);
|
||||
if (item != null) nextReferenceItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
this.sortBy = nextSortBy;
|
||||
this.amountMode = nextAmountMode;
|
||||
this.amountConstantValue = nextAmountConstantValue;
|
||||
this.referenceTargetType = nextReferenceTargetType;
|
||||
this.referenceUserSource = nextReferenceUserSource;
|
||||
this.referenceFurniSource = nextReferenceFurniSource;
|
||||
this.setVariableToken(nextVariableToken);
|
||||
this.setReferenceVariableToken(nextReferenceVariableToken);
|
||||
this.referenceSelectedItems.clear();
|
||||
this.referenceSelectedItems.addAll(nextReferenceItems);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
this.refreshReferenceItems();
|
||||
return WiredManager.getGson().toJson(new JsonData(this.sortBy, this.amountMode, this.amountConstantValue, this.referenceTargetType, this.referenceUserSource, this.referenceFurniSource, this.variableToken, this.variableItemId, this.referenceVariableToken, this.referenceVariableItemId, this.toIds(this.referenceSelectedItems)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
this.refreshReferenceItems();
|
||||
List<HabboItem> selectedItems = new ArrayList<>();
|
||||
if (this.amountMode == AMOUNT_VARIABLE && this.referenceTargetType == TARGET_FURNI && this.referenceFurniSource == SOURCE_SECONDARY_SELECTED) {
|
||||
selectedItems.addAll(this.referenceSelectedItems);
|
||||
}
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(selectedItems.size());
|
||||
for (HabboItem item : selectedItems) message.appendInt(item.getId());
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.serializeStringData());
|
||||
message.appendInt(6);
|
||||
message.appendInt(this.sortBy);
|
||||
message.appendInt(this.amountMode);
|
||||
message.appendInt(this.amountConstantValue);
|
||||
message.appendInt(this.referenceTargetType);
|
||||
message.appendInt(this.referenceUserSource);
|
||||
message.appendInt(this.referenceFurniSource);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getCode());
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) return;
|
||||
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
if (data == null) return;
|
||||
|
||||
this.sortBy = normalizeSortBy(data.sortBy);
|
||||
this.amountMode = normalizeAmountMode(data.amountMode);
|
||||
this.amountConstantValue = normalizeAmount(data.amountConstantValue);
|
||||
this.referenceTargetType = normalizeReferenceTargetType(data.referenceTargetType);
|
||||
this.referenceUserSource = normalizeUserSource(data.referenceUserSource);
|
||||
this.referenceFurniSource = normalizeReferenceFurniSource(data.referenceFurniSource);
|
||||
this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
|
||||
this.setReferenceVariableToken(normalizeVariableToken((data.referenceVariableToken != null) ? data.referenceVariableToken : ((data.referenceVariableItemId > 0) ? String.valueOf(data.referenceVariableItemId) : "")));
|
||||
|
||||
if (room == null || data.selectedItemIds == null) return;
|
||||
|
||||
for (Integer itemId : data.selectedItemIds) {
|
||||
if (itemId == null || itemId <= 0) continue;
|
||||
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
if (item != null) this.referenceSelectedItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.sortBy = SORT_VALUE_HIGHEST;
|
||||
this.amountMode = AMOUNT_CONSTANT;
|
||||
this.amountConstantValue = 1;
|
||||
this.referenceTargetType = TARGET_USER;
|
||||
this.referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.referenceSelectedItems.clear();
|
||||
this.setVariableToken("");
|
||||
this.setReferenceVariableToken("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private int resolveAmount(WiredContext ctx, Room room) {
|
||||
if (this.amountMode != AMOUNT_VARIABLE) return normalizeAmount(this.amountConstantValue);
|
||||
|
||||
Integer value = this.resolveReferenceValue(ctx, room);
|
||||
return value == null ? 0 : normalizeAmount(value);
|
||||
}
|
||||
|
||||
private Integer resolveReferenceValue(WiredContext ctx, Room room) {
|
||||
if (room == null) return null;
|
||||
|
||||
if (this.referenceTargetType == TARGET_FURNI) {
|
||||
int source = (this.referenceFurniSource == SOURCE_SECONDARY_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : this.referenceFurniSource;
|
||||
if (source == WiredSourceUtil.SOURCE_SELECTED) this.refreshReferenceItems();
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedItems)) {
|
||||
Integer value = this.readFurniReferenceValue(room, item);
|
||||
if (value != null) return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.referenceTargetType == TARGET_CONTEXT) {
|
||||
return this.readContextReferenceValue(ctx, room);
|
||||
}
|
||||
|
||||
if (this.referenceTargetType == TARGET_ROOM) {
|
||||
return this.readRoomReferenceValue(room);
|
||||
}
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
|
||||
Integer value = this.readUserReferenceValue(room, roomUnit);
|
||||
if (value != null) return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Integer readUserReferenceValue(Room room, RoomUnit roomUnit) {
|
||||
if (room == null || roomUnit == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
return canUseUserInternalReference(key) ? this.readUserInternalValue(room, roomUnit, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
return (habbo != null) ? room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.referenceVariableItemId) : null;
|
||||
}
|
||||
|
||||
private Integer readFurniReferenceValue(Room room, HabboItem item) {
|
||||
if (room == null || item == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
return canUseFurniInternalReference(key) ? this.readFurniInternalValue(room, item, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
return (definition != null && definition.hasValue()) ? room.getFurniVariableManager().getCurrentValue(item.getId(), this.referenceVariableItemId) : null;
|
||||
}
|
||||
|
||||
private Integer readRoomReferenceValue(Room room) {
|
||||
if (room == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
return canUseRoomInternalReference(key) ? this.readRoomInternalValue(room, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
return (definition != null && definition.hasValue()) ? room.getRoomVariableManager().getCurrentValue(this.referenceVariableItemId) : null;
|
||||
}
|
||||
|
||||
private Integer readContextReferenceValue(WiredContext ctx, Room room) {
|
||||
if (ctx == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
return canUseContextInternalReference(key) ? WiredInternalVariableSupport.readContextValue(ctx, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue() || !WiredContextVariableSupport.hasVariable(ctx, this.referenceVariableItemId)) return null;
|
||||
|
||||
return WiredContextVariableSupport.getCurrentValue(ctx, this.referenceVariableItemId);
|
||||
}
|
||||
|
||||
private MetricSnapshot resolveUserMetric(Room room, RoomUnit roomUnit) {
|
||||
if (room == null || roomUnit == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
String key = getInternalVariableKey(this.variableToken);
|
||||
Integer value = canUseUserInternalReference(key) ? this.readUserInternalValue(room, roomUnit, key) : null;
|
||||
return (value != null) ? new MetricSnapshot(roomUnit.getId(), value, 0, 0) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (definition == null || habbo == null) return null;
|
||||
if (!room.getUserVariableManager().hasVariable(habbo.getHabboInfo().getId(), this.variableItemId)) return null;
|
||||
|
||||
return new MetricSnapshot(
|
||||
roomUnit.getId(),
|
||||
definition.hasValue() ? room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.variableItemId) : 0,
|
||||
room.getUserVariableManager().getCreatedAt(habbo.getHabboInfo().getId(), this.variableItemId),
|
||||
room.getUserVariableManager().getUpdatedAt(habbo.getHabboInfo().getId(), this.variableItemId));
|
||||
}
|
||||
|
||||
private MetricSnapshot resolveFurniMetric(Room room, HabboItem item) {
|
||||
if (room == null || item == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
String key = getInternalVariableKey(this.variableToken);
|
||||
Integer value = canUseFurniInternalReference(key) ? this.readFurniInternalValue(room, item, key) : null;
|
||||
return (value != null) ? new MetricSnapshot(item.getId(), value, 0, 0) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
if (definition == null) return null;
|
||||
if (!room.getFurniVariableManager().hasVariable(item.getId(), this.variableItemId)) return null;
|
||||
|
||||
return new MetricSnapshot(
|
||||
item.getId(),
|
||||
definition.hasValue() ? room.getFurniVariableManager().getCurrentValue(item.getId(), this.variableItemId) : 0,
|
||||
room.getFurniVariableManager().getCreatedAt(item.getId(), this.variableItemId),
|
||||
room.getFurniVariableManager().getUpdatedAt(item.getId(), this.variableItemId));
|
||||
}
|
||||
|
||||
private Comparator<SortableEntry<?>> metricComparator() {
|
||||
return switch (this.sortBy) {
|
||||
case SORT_VALUE_LOWEST -> Comparator.comparingInt((SortableEntry<?> entry) -> entry.metric.value).thenComparingInt(entry -> entry.metric.entityId);
|
||||
case SORT_CREATION_OLDEST -> Comparator.comparingInt((SortableEntry<?> entry) -> entry.metric.createdAt).thenComparingInt(entry -> entry.metric.entityId);
|
||||
case SORT_CREATION_LATEST -> Comparator.<SortableEntry<?>, Integer>comparing(entry -> entry.metric.createdAt).reversed().thenComparingInt(entry -> entry.metric.entityId);
|
||||
case SORT_UPDATE_OLDEST -> Comparator.comparingInt((SortableEntry<?> entry) -> entry.metric.updatedAt).thenComparingInt(entry -> entry.metric.entityId);
|
||||
case SORT_UPDATE_LATEST -> Comparator.<SortableEntry<?>, Integer>comparing(entry -> entry.metric.updatedAt).reversed().thenComparingInt(entry -> entry.metric.entityId);
|
||||
default -> Comparator.<SortableEntry<?>, Integer>comparing(entry -> entry.metric.value).reversed().thenComparingInt(entry -> entry.metric.entityId);
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isValidMainVariable(Room room, String token) {
|
||||
if (token == null || token.isEmpty()) return false;
|
||||
|
||||
if (isInternalVariableToken(token)) {
|
||||
String key = getInternalVariableKey(token);
|
||||
return this.getVariableTargetType() == TARGET_FURNI ? canUseFurniInternalReference(key) : canUseUserInternalReference(key);
|
||||
}
|
||||
|
||||
if (this.getVariableTargetType() == TARGET_FURNI) {
|
||||
return room != null && room.getFurniVariableManager().getDefinitionInfo(getCustomItemId(token)) != null;
|
||||
}
|
||||
|
||||
return room != null && room.getUserVariableManager().getDefinitionInfo(getCustomItemId(token)) != null;
|
||||
}
|
||||
|
||||
private boolean isValidReference(Room room, int targetType, String token) {
|
||||
if (token == null || token.isEmpty()) return false;
|
||||
|
||||
if (isInternalVariableToken(token)) {
|
||||
String key = getInternalVariableKey(token);
|
||||
return switch (targetType) {
|
||||
case TARGET_FURNI -> canUseFurniInternalReference(key);
|
||||
case TARGET_CONTEXT -> canUseContextInternalReference(key);
|
||||
case TARGET_ROOM -> canUseRoomInternalReference(key);
|
||||
default -> canUseUserInternalReference(key);
|
||||
};
|
||||
}
|
||||
|
||||
return switch (targetType) {
|
||||
case TARGET_FURNI -> {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getFurniVariableManager().getDefinitionInfo(getCustomItemId(token)) : null;
|
||||
yield definition != null && definition.hasValue();
|
||||
}
|
||||
case TARGET_CONTEXT -> this.isValidContextCustomReference(room, getCustomItemId(token));
|
||||
case TARGET_ROOM -> {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getRoomVariableManager().getDefinitionInfo(getCustomItemId(token)) : null;
|
||||
yield definition != null && definition.hasValue();
|
||||
}
|
||||
default -> {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getUserVariableManager().getDefinitionInfo(getCustomItemId(token)) : null;
|
||||
yield definition != null && definition.hasValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isValidContextCustomReference(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, variableItemId);
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
private static List<RoomUnit> trimUsers(List<SortableEntry<RoomUnit>> matches, int amount) {
|
||||
List<RoomUnit> result = new ArrayList<>();
|
||||
for (SortableEntry<RoomUnit> match : matches) {
|
||||
if (result.size() >= amount) break;
|
||||
result.add(match.entity);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<HabboItem> trimItems(List<SortableEntry<HabboItem>> matches, int amount) {
|
||||
List<HabboItem> result = new ArrayList<>();
|
||||
for (SortableEntry<HabboItem> match : matches) {
|
||||
if (result.size() >= amount) break;
|
||||
result.add(match.entity);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<RoomUnit> toUserList(Iterable<RoomUnit> values) {
|
||||
List<RoomUnit> result = new ArrayList<>();
|
||||
if (values == null) return result;
|
||||
for (RoomUnit value : values) if (value != null) result.add(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<HabboItem> toItemList(Iterable<HabboItem> values) {
|
||||
List<HabboItem> result = new ArrayList<>();
|
||||
if (values == null) return result;
|
||||
for (HabboItem value : values) if (value != null) result.add(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
private String serializeStringData() {
|
||||
return (this.variableToken == null ? "" : this.variableToken) + DELIM + (this.referenceVariableToken == null ? "" : this.referenceVariableToken);
|
||||
}
|
||||
|
||||
private void refreshReferenceItems() {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
if (room == null) {
|
||||
this.referenceSelectedItems.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this.referenceSelectedItems.removeIf(item -> item == null || item.getRoomId() != room.getId() || room.getHabboItem(item.getId()) == null);
|
||||
}
|
||||
|
||||
private void setVariableToken(String token) {
|
||||
this.variableToken = normalizeVariableToken(token);
|
||||
this.variableItemId = getCustomItemId(this.variableToken);
|
||||
}
|
||||
|
||||
private void setReferenceVariableToken(String token) {
|
||||
this.referenceVariableToken = normalizeVariableToken(token);
|
||||
this.referenceVariableItemId = getCustomItemId(this.referenceVariableToken);
|
||||
}
|
||||
|
||||
private List<Integer> toIds(List<HabboItem> items) {
|
||||
List<Integer> ids = new ArrayList<>();
|
||||
for (HabboItem item : items) if (item != null) ids.add(item.getId());
|
||||
return ids;
|
||||
}
|
||||
|
||||
private Integer readUserInternalValue(Room room, RoomUnit roomUnit, String key) {
|
||||
return WiredInternalVariableSupport.readUserValue(room, roomUnit, key);
|
||||
}
|
||||
|
||||
private Integer readFurniInternalValue(Room room, HabboItem item, String key) {
|
||||
return WiredInternalVariableSupport.readFurniValue(room, item, key);
|
||||
}
|
||||
|
||||
private Integer readRoomInternalValue(Room room, String key) {
|
||||
return WiredInternalVariableSupport.readRoomValue(room, key);
|
||||
}
|
||||
private int getUserTeamScore(Room room, Habbo habbo) {
|
||||
if (room == null || habbo == null || habbo.getHabboInfo() == null || habbo.getHabboInfo().getGamePlayer() == null) return 0;
|
||||
|
||||
Game game = this.resolveTeamGame(room, habbo);
|
||||
if (game == null) return 0;
|
||||
|
||||
GamePlayer player = habbo.getHabboInfo().getGamePlayer();
|
||||
return player.getScore();
|
||||
}
|
||||
|
||||
private int getTeamColorId(int effectValue) {
|
||||
TeamEffectData effectData = this.getTeamEffectData(effectValue);
|
||||
return (effectData != null) ? effectData.colorId : 0;
|
||||
}
|
||||
|
||||
private int getTeamTypeId(int effectValue) {
|
||||
TeamEffectData effectData = this.getTeamEffectData(effectValue);
|
||||
return (effectData != null) ? effectData.typeId : 0;
|
||||
}
|
||||
|
||||
private int getTeamMetric(Room room, GameTeamColors color, boolean score) {
|
||||
Game game = room.getGame(WiredGame.class);
|
||||
if (game == null) game = room.getGame(FreezeGame.class);
|
||||
if (game == null) game = room.getGame(BattleBanzaiGame.class);
|
||||
if (game == null) return 0;
|
||||
|
||||
GameTeam team = game.getTeam(color);
|
||||
if (team == null) return 0;
|
||||
|
||||
return score ? team.getTotalScore() : team.getMembers().size();
|
||||
}
|
||||
|
||||
private Game resolveTeamGame(Room room, Habbo habbo) {
|
||||
if (room == null) return null;
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getCurrentGame() != null) {
|
||||
Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
|
||||
if (game != null) return game;
|
||||
}
|
||||
|
||||
Game wiredGame = room.getGame(WiredGame.class);
|
||||
if (wiredGame != null) return wiredGame;
|
||||
|
||||
Game freezeGame = room.getGame(FreezeGame.class);
|
||||
if (freezeGame != null) return freezeGame;
|
||||
|
||||
return room.getGame(BattleBanzaiGame.class);
|
||||
}
|
||||
|
||||
private TeamEffectData getTeamEffectData(int effectValue) {
|
||||
if (effectValue <= 0) return null;
|
||||
|
||||
if (effectValue >= 223 && effectValue <= 226) return new TeamEffectData(effectValue - 222, 0);
|
||||
if (effectValue >= 33 && effectValue <= 36) return new TeamEffectData(effectValue - 32, 1);
|
||||
if (effectValue >= 40 && effectValue <= 43) return new TeamEffectData(effectValue - 39, 2);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int normalizeSortBy(int value) {
|
||||
return switch (value) {
|
||||
case SORT_VALUE_LOWEST, SORT_CREATION_OLDEST, SORT_CREATION_LATEST, SORT_UPDATE_OLDEST, SORT_UPDATE_LATEST -> value;
|
||||
default -> SORT_VALUE_HIGHEST;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeAmountMode(int value) {
|
||||
return (value == AMOUNT_VARIABLE) ? AMOUNT_VARIABLE : AMOUNT_CONSTANT;
|
||||
}
|
||||
|
||||
private static int normalizeReferenceTargetType(int value) {
|
||||
return switch (value) {
|
||||
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
|
||||
default -> TARGET_USER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeUserSource(int value) {
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private static int normalizeReferenceFurniSource(int value) {
|
||||
return switch (value) {
|
||||
case SOURCE_SECONDARY_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
|
||||
default -> WiredSourceUtil.SOURCE_TRIGGER;
|
||||
};
|
||||
}
|
||||
|
||||
protected static String normalizeVariableToken(String token) {
|
||||
if (token == null) return "";
|
||||
|
||||
String normalized = token.trim();
|
||||
if (normalized.isEmpty()) return "";
|
||||
if (normalized.startsWith(INTERNAL_TOKEN_PREFIX)) {
|
||||
return INTERNAL_TOKEN_PREFIX + WiredInternalVariableSupport.normalizeKey(normalized.substring(INTERNAL_TOKEN_PREFIX.length()));
|
||||
}
|
||||
if (isCustomVariableToken(normalized) || isInternalVariableToken(normalized)) return normalized;
|
||||
|
||||
try {
|
||||
int parsed = Integer.parseInt(normalized);
|
||||
return (parsed > 0) ? (CUSTOM_TOKEN_PREFIX + parsed) : "";
|
||||
} catch (NumberFormatException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean isCustomVariableToken(String token) {
|
||||
return token != null && token.startsWith(CUSTOM_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
protected static boolean isInternalVariableToken(String token) {
|
||||
return token != null && token.startsWith(INTERNAL_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
protected static int getCustomItemId(String token) {
|
||||
if (!isCustomVariableToken(token)) return 0;
|
||||
|
||||
try {
|
||||
return Integer.parseInt(token.substring(CUSTOM_TOKEN_PREFIX.length()));
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected static String getInternalVariableKey(String token) {
|
||||
return isInternalVariableToken(token) ? WiredInternalVariableSupport.normalizeKey(token.substring(INTERNAL_TOKEN_PREFIX.length())) : "";
|
||||
}
|
||||
|
||||
private static boolean canUseUserInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseFurniInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseRoomInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseRoomReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseContextInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseContextReference(key);
|
||||
}
|
||||
|
||||
private static int param(int[] params, int index, int fallback) {
|
||||
return (params != null && params.length > index) ? params[index] : fallback;
|
||||
}
|
||||
|
||||
private static String[] parseStringData(String value) {
|
||||
return (value == null || value.isEmpty()) ? new String[0] : value.split("\\t", -1);
|
||||
}
|
||||
|
||||
private static int parseInteger(String value) {
|
||||
try {
|
||||
return (value == null || value.trim().isEmpty()) ? 0 : Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int normalizeAmount(int value) {
|
||||
return Math.max(0, Math.min(MAX_FILTER_AMOUNT, value));
|
||||
}
|
||||
|
||||
protected static class JsonData {
|
||||
int sortBy;
|
||||
int amountMode;
|
||||
int amountConstantValue;
|
||||
int referenceTargetType;
|
||||
int referenceUserSource;
|
||||
int referenceFurniSource;
|
||||
String variableToken;
|
||||
int variableItemId;
|
||||
String referenceVariableToken;
|
||||
int referenceVariableItemId;
|
||||
List<Integer> selectedItemIds;
|
||||
|
||||
JsonData(int sortBy, int amountMode, int amountConstantValue, int referenceTargetType, int referenceUserSource, int referenceFurniSource, String variableToken, int variableItemId, String referenceVariableToken, int referenceVariableItemId, List<Integer> selectedItemIds) {
|
||||
this.sortBy = sortBy;
|
||||
this.amountMode = amountMode;
|
||||
this.amountConstantValue = amountConstantValue;
|
||||
this.referenceTargetType = referenceTargetType;
|
||||
this.referenceUserSource = referenceUserSource;
|
||||
this.referenceFurniSource = referenceFurniSource;
|
||||
this.variableToken = variableToken;
|
||||
this.variableItemId = variableItemId;
|
||||
this.referenceVariableToken = referenceVariableToken;
|
||||
this.referenceVariableItemId = referenceVariableItemId;
|
||||
this.selectedItemIds = selectedItemIds;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SortableEntry<T> {
|
||||
final T entity;
|
||||
final MetricSnapshot metric;
|
||||
|
||||
SortableEntry(T entity, MetricSnapshot metric) {
|
||||
this.entity = entity;
|
||||
this.metric = metric;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MetricSnapshot {
|
||||
final int entityId;
|
||||
final int value;
|
||||
final int createdAt;
|
||||
final int updatedAt;
|
||||
|
||||
MetricSnapshot(int entityId, int value, int createdAt, int updatedAt) {
|
||||
this.entityId = entityId;
|
||||
this.value = value;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TeamEffectData {
|
||||
final int colorId;
|
||||
final int typeId;
|
||||
|
||||
TeamEffectData(int colorId, int typeId) {
|
||||
this.colorId = colorId;
|
||||
this.typeId = typeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WiredExtraVariableLevelUpSystem extends InteractionWiredExtra {
|
||||
public static final int CODE = 82;
|
||||
|
||||
public static final int MODE_LINEAR = 1;
|
||||
public static final int MODE_EXPONENTIAL = 2;
|
||||
public static final int MODE_MANUAL = 3;
|
||||
|
||||
public static final int SUB_CURRENT_LEVEL = 0;
|
||||
public static final int SUB_CURRENT_XP = 1;
|
||||
public static final int SUB_LEVEL_PROGRESS = 2;
|
||||
public static final int SUB_LEVEL_PROGRESS_PERCENT = 3;
|
||||
public static final int SUB_TOTAL_XP_REQUIRED = 4;
|
||||
public static final int SUB_XP_REMAINING = 5;
|
||||
public static final int SUB_IS_AT_MAX = 6;
|
||||
public static final int SUB_MAX_LEVEL = 7;
|
||||
public static final int SUBVARIABLE_COUNT = 8;
|
||||
|
||||
private static final int DEFAULT_STEP_SIZE = 100;
|
||||
private static final int DEFAULT_MAX_LEVEL = 10;
|
||||
private static final int DEFAULT_FIRST_LEVEL_XP = 100;
|
||||
private static final int DEFAULT_INCREASE_FACTOR = 100;
|
||||
private static final int MAX_MANUAL_TEXT_LENGTH = 4096;
|
||||
|
||||
private int mode = MODE_LINEAR;
|
||||
private int stepSize = DEFAULT_STEP_SIZE;
|
||||
private int maxLevel = DEFAULT_MAX_LEVEL;
|
||||
private int firstLevelXp = DEFAULT_FIRST_LEVEL_XP;
|
||||
private int increaseFactor = DEFAULT_INCREASE_FACTOR;
|
||||
private String interpolationText = "";
|
||||
private int subvariableMask = (1 << SUB_CURRENT_LEVEL) | (1 << SUB_CURRENT_XP);
|
||||
|
||||
public WiredExtraVariableLevelUpSystem(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraVariableLevelUpSystem(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) {
|
||||
this.applyConfig(parseJsonData(settings.getStringParam()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
this.mode,
|
||||
this.stepSize,
|
||||
this.maxLevel,
|
||||
this.firstLevelXp,
|
||||
this.increaseFactor,
|
||||
this.interpolationText,
|
||||
this.getSelectedSubvariables()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.getWiredData());
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.applyConfig(parseJsonData(wiredData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.mode = MODE_LINEAR;
|
||||
this.stepSize = DEFAULT_STEP_SIZE;
|
||||
this.maxLevel = DEFAULT_MAX_LEVEL;
|
||||
this.firstLevelXp = DEFAULT_FIRST_LEVEL_XP;
|
||||
this.increaseFactor = DEFAULT_INCREASE_FACTOR;
|
||||
this.interpolationText = "";
|
||||
this.subvariableMask = (1 << SUB_CURRENT_LEVEL) | (1 << SUB_CURRENT_XP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public int getStepSize() {
|
||||
return this.stepSize;
|
||||
}
|
||||
|
||||
public int getMaxLevel() {
|
||||
return this.maxLevel;
|
||||
}
|
||||
|
||||
public int getFirstLevelXp() {
|
||||
return this.firstLevelXp;
|
||||
}
|
||||
|
||||
public int getIncreaseFactor() {
|
||||
return this.increaseFactor;
|
||||
}
|
||||
|
||||
public String getInterpolationText() {
|
||||
return this.interpolationText;
|
||||
}
|
||||
|
||||
public boolean hasSubvariable(int subvariableType) {
|
||||
return subvariableType >= 0
|
||||
&& subvariableType < SUBVARIABLE_COUNT
|
||||
&& ((this.subvariableMask & (1 << subvariableType)) != 0);
|
||||
}
|
||||
|
||||
public List<Integer> getSelectedSubvariables() {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
|
||||
for (int index = 0; index < SUBVARIABLE_COUNT; index++) {
|
||||
if (this.hasSubvariable(index)) {
|
||||
result.add(index);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void applyConfig(JsonData data) {
|
||||
if (data == null) {
|
||||
this.onPickUp();
|
||||
return;
|
||||
}
|
||||
|
||||
this.mode = normalizeMode(data.mode);
|
||||
this.stepSize = normalizeNonNegative(data.stepSize, DEFAULT_STEP_SIZE);
|
||||
this.maxLevel = normalizeMaxLevel(data.maxLevel);
|
||||
this.firstLevelXp = normalizeNonNegative(data.firstLevelXp, DEFAULT_FIRST_LEVEL_XP);
|
||||
this.increaseFactor = normalizeNonNegative(data.increaseFactor, DEFAULT_INCREASE_FACTOR);
|
||||
this.interpolationText = normalizeInterpolationText(data.interpolationText);
|
||||
this.subvariableMask = normalizeSubvariableMask(data.subvariables);
|
||||
}
|
||||
|
||||
private static JsonData parseJsonData(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return new JsonData();
|
||||
}
|
||||
|
||||
try {
|
||||
if (value.trim().startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(value, JsonData.class);
|
||||
return (data != null) ? data : new JsonData();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
JsonData fallback = new JsonData();
|
||||
fallback.interpolationText = normalizeInterpolationText(value);
|
||||
fallback.mode = MODE_MANUAL;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private static int normalizeMode(int value) {
|
||||
return switch (value) {
|
||||
case MODE_EXPONENTIAL, MODE_MANUAL -> value;
|
||||
default -> MODE_LINEAR;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeNonNegative(int value, int fallback) {
|
||||
return Math.max(0, (value > 0) ? value : fallback);
|
||||
}
|
||||
|
||||
private static int normalizeMaxLevel(int value) {
|
||||
return Math.max(1, (value > 0) ? value : DEFAULT_MAX_LEVEL);
|
||||
}
|
||||
|
||||
private static String normalizeInterpolationText(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String normalized = value.replace("\r", "");
|
||||
if (normalized.length() > MAX_MANUAL_TEXT_LENGTH) {
|
||||
normalized = normalized.substring(0, MAX_MANUAL_TEXT_LENGTH);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static int normalizeSubvariableMask(List<Integer> subvariables) {
|
||||
if (subvariables == null) {
|
||||
return (1 << SUB_CURRENT_LEVEL) | (1 << SUB_CURRENT_XP);
|
||||
}
|
||||
|
||||
int mask = 0;
|
||||
for (Integer subvariable : subvariables) {
|
||||
if (subvariable == null || subvariable < 0 || subvariable >= SUBVARIABLE_COUNT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mask |= (1 << subvariable);
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int mode = MODE_LINEAR;
|
||||
int stepSize = DEFAULT_STEP_SIZE;
|
||||
int maxLevel = DEFAULT_MAX_LEVEL;
|
||||
int firstLevelXp = DEFAULT_FIRST_LEVEL_XP;
|
||||
int increaseFactor = DEFAULT_INCREASE_FACTOR;
|
||||
String interpolationText = "";
|
||||
List<Integer> subvariables = null;
|
||||
|
||||
JsonData() {
|
||||
}
|
||||
|
||||
JsonData(int mode, int stepSize, int maxLevel, int firstLevelXp, int increaseFactor, String interpolationText, List<Integer> subvariables) {
|
||||
this.mode = mode;
|
||||
this.stepSize = stepSize;
|
||||
this.maxLevel = maxLevel;
|
||||
this.firstLevelXp = firstLevelXp;
|
||||
this.increaseFactor = increaseFactor;
|
||||
this.interpolationText = interpolationText;
|
||||
this.subvariables = subvariables;
|
||||
}
|
||||
}
|
||||
}
|
||||
+321
@@ -0,0 +1,321 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WiredExtraVariableReference extends InteractionWiredExtra {
|
||||
public static final int CODE = 81;
|
||||
|
||||
private String variableName = "";
|
||||
private int sourceRoomId = 0;
|
||||
private String sourceRoomName = "";
|
||||
private int sourceVariableItemId = 0;
|
||||
private String sourceVariableName = "";
|
||||
private int sourceTargetType = WiredVariableReferenceSupport.TARGET_USER;
|
||||
private boolean hasValue = false;
|
||||
private boolean readOnly = true;
|
||||
|
||||
public WiredExtraVariableReference(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraVariableReference(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
|
||||
if (room == null) {
|
||||
throw new WiredSaveException("Room not found");
|
||||
}
|
||||
|
||||
ConfigData config = parseConfigData(settings.getStringParam());
|
||||
String normalizedName = WiredVariableNameValidator.normalizeForSave(config.variableName);
|
||||
|
||||
WiredVariableNameValidator.validateDefinitionName(room, this.getId(), normalizedName);
|
||||
|
||||
if (config.sourceRoomId <= 0 || config.sourceVariableItemId <= 0) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
}
|
||||
|
||||
WiredVariableReferenceSupport.SharedDefinitionOption definition = WiredVariableReferenceSupport.findSharedDefinition(
|
||||
room,
|
||||
config.sourceRoomId,
|
||||
config.sourceVariableItemId,
|
||||
config.sourceTargetType
|
||||
);
|
||||
|
||||
if (definition == null) {
|
||||
throw new WiredSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
this.variableName = normalizedName;
|
||||
this.sourceRoomId = definition.getRoomId();
|
||||
this.sourceRoomName = sanitizeLabel(definition.getRoomName());
|
||||
this.sourceVariableItemId = definition.getItemId();
|
||||
this.sourceVariableName = definition.getName();
|
||||
this.sourceTargetType = definition.getTargetType();
|
||||
this.hasValue = definition.hasValue();
|
||||
this.readOnly = config.readOnly;
|
||||
|
||||
room.getUserVariableManager().broadcastSnapshot();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
this.variableName,
|
||||
this.sourceRoomId,
|
||||
this.sourceRoomName,
|
||||
this.sourceVariableItemId,
|
||||
this.sourceVariableName,
|
||||
this.sourceTargetType,
|
||||
this.hasValue,
|
||||
this.readOnly
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(buildEditorPayload(room));
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.variableName = WiredVariableNameValidator.normalizeLegacy(data.variableName);
|
||||
this.sourceRoomId = Math.max(0, data.sourceRoomId);
|
||||
this.sourceRoomName = sanitizeLabel(data.sourceRoomName);
|
||||
this.sourceVariableItemId = Math.max(0, data.sourceVariableItemId);
|
||||
this.sourceVariableName = WiredVariableNameValidator.normalizeLegacy(data.sourceVariableName);
|
||||
this.sourceTargetType = normalizeTargetType(data.sourceTargetType);
|
||||
this.hasValue = data.hasValue;
|
||||
this.readOnly = data.readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableName = "";
|
||||
this.sourceRoomId = 0;
|
||||
this.sourceRoomName = "";
|
||||
this.sourceVariableItemId = 0;
|
||||
this.sourceVariableName = "";
|
||||
this.sourceTargetType = WiredVariableReferenceSupport.TARGET_USER;
|
||||
this.hasValue = false;
|
||||
this.readOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getVariableName() {
|
||||
return this.variableName;
|
||||
}
|
||||
|
||||
public int getSourceRoomId() {
|
||||
return this.sourceRoomId;
|
||||
}
|
||||
|
||||
public String getSourceRoomName() {
|
||||
return this.sourceRoomName;
|
||||
}
|
||||
|
||||
public int getSourceVariableItemId() {
|
||||
return this.sourceVariableItemId;
|
||||
}
|
||||
|
||||
public String getSourceVariableName() {
|
||||
return this.sourceVariableName;
|
||||
}
|
||||
|
||||
public int getSourceTargetType() {
|
||||
return this.sourceTargetType;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return this.hasValue;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return this.readOnly;
|
||||
}
|
||||
|
||||
public int getAvailability() {
|
||||
return WiredVariableReferenceSupport.SHARED_AVAILABILITY;
|
||||
}
|
||||
|
||||
public boolean isUserReference() {
|
||||
return this.sourceTargetType == WiredVariableReferenceSupport.TARGET_USER;
|
||||
}
|
||||
|
||||
public boolean isRoomReference() {
|
||||
return this.sourceTargetType == WiredVariableReferenceSupport.TARGET_ROOM;
|
||||
}
|
||||
|
||||
private String buildEditorPayload(Room room) {
|
||||
List<RoomEditorData> roomOptions = new ArrayList<>();
|
||||
|
||||
for (WiredVariableReferenceSupport.RoomOption option : WiredVariableReferenceSupport.loadRoomOptions(room)) {
|
||||
List<VariableEditorData> variables = new ArrayList<>();
|
||||
|
||||
for (WiredVariableReferenceSupport.SharedDefinitionOption definition : option.getVariables()) {
|
||||
variables.add(new VariableEditorData(definition.getItemId(), definition.getName(), definition.getTargetType(), definition.hasValue()));
|
||||
}
|
||||
|
||||
roomOptions.add(new RoomEditorData(option.getRoomId(), option.getRoomName(), variables));
|
||||
}
|
||||
|
||||
return WiredManager.getGson().toJson(new EditorPayload(
|
||||
this.variableName,
|
||||
this.sourceRoomId,
|
||||
this.sourceRoomName,
|
||||
this.sourceVariableItemId,
|
||||
this.sourceVariableName,
|
||||
this.sourceTargetType,
|
||||
this.readOnly,
|
||||
roomOptions
|
||||
));
|
||||
}
|
||||
|
||||
private static ConfigData parseConfigData(String value) {
|
||||
if (value == null || value.isEmpty() || !value.startsWith("{")) {
|
||||
return new ConfigData();
|
||||
}
|
||||
|
||||
ConfigData config = WiredManager.getGson().fromJson(value, ConfigData.class);
|
||||
return (config != null) ? config : new ConfigData();
|
||||
}
|
||||
|
||||
private static int normalizeTargetType(int value) {
|
||||
return (value == WiredVariableReferenceSupport.TARGET_ROOM) ? WiredVariableReferenceSupport.TARGET_ROOM : WiredVariableReferenceSupport.TARGET_USER;
|
||||
}
|
||||
|
||||
private static String sanitizeLabel(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return value.trim().replace("\t", "").replace("\r", "").replace("\n", "");
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String variableName;
|
||||
int sourceRoomId;
|
||||
String sourceRoomName;
|
||||
int sourceVariableItemId;
|
||||
String sourceVariableName;
|
||||
int sourceTargetType;
|
||||
boolean hasValue;
|
||||
boolean readOnly;
|
||||
|
||||
JsonData(String variableName, int sourceRoomId, String sourceRoomName, int sourceVariableItemId, String sourceVariableName, int sourceTargetType, boolean hasValue, boolean readOnly) {
|
||||
this.variableName = variableName;
|
||||
this.sourceRoomId = sourceRoomId;
|
||||
this.sourceRoomName = sourceRoomName;
|
||||
this.sourceVariableItemId = sourceVariableItemId;
|
||||
this.sourceVariableName = sourceVariableName;
|
||||
this.sourceTargetType = sourceTargetType;
|
||||
this.hasValue = hasValue;
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
}
|
||||
|
||||
static class ConfigData {
|
||||
String variableName = "";
|
||||
int sourceRoomId = 0;
|
||||
int sourceVariableItemId = 0;
|
||||
int sourceTargetType = WiredVariableReferenceSupport.TARGET_USER;
|
||||
boolean readOnly = true;
|
||||
}
|
||||
|
||||
static class EditorPayload extends ConfigData {
|
||||
String sourceRoomName;
|
||||
String sourceVariableName;
|
||||
List<RoomEditorData> rooms;
|
||||
|
||||
EditorPayload(String variableName, int sourceRoomId, String sourceRoomName, int sourceVariableItemId, String sourceVariableName, int sourceTargetType, boolean readOnly, List<RoomEditorData> rooms) {
|
||||
this.variableName = variableName;
|
||||
this.sourceRoomId = sourceRoomId;
|
||||
this.sourceRoomName = sourceRoomName;
|
||||
this.sourceVariableItemId = sourceVariableItemId;
|
||||
this.sourceVariableName = sourceVariableName;
|
||||
this.sourceTargetType = sourceTargetType;
|
||||
this.readOnly = readOnly;
|
||||
this.rooms = rooms;
|
||||
}
|
||||
}
|
||||
|
||||
static class RoomEditorData {
|
||||
int roomId;
|
||||
String roomName;
|
||||
List<VariableEditorData> variables;
|
||||
|
||||
RoomEditorData(int roomId, String roomName, List<VariableEditorData> variables) {
|
||||
this.roomId = roomId;
|
||||
this.roomName = roomName;
|
||||
this.variables = variables;
|
||||
}
|
||||
}
|
||||
|
||||
static class VariableEditorData {
|
||||
int itemId;
|
||||
String name;
|
||||
int targetType;
|
||||
boolean hasValue;
|
||||
|
||||
VariableEditorData(int itemId, String name, int targetType, boolean hasValue) {
|
||||
this.itemId = itemId;
|
||||
this.name = name;
|
||||
this.targetType = targetType;
|
||||
this.hasValue = hasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class WiredExtraVariableTextConnector extends InteractionWiredExtra {
|
||||
public static final int CODE = 79;
|
||||
private static final int MAX_MAPPING_LENGTH = 4096;
|
||||
|
||||
private String mappingsText = "";
|
||||
private LinkedHashMap<Integer, String> mappings = new LinkedHashMap<>();
|
||||
|
||||
public WiredExtraVariableTextConnector(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraVariableTextConnector(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) {
|
||||
this.setMappingsText(settings.getStringParam());
|
||||
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
if (room != null) {
|
||||
WiredContextVariableSupport.broadcastDefinitions(room);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.mappingsText));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.mappingsText);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.setMappingsText(data.mappingsText);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setMappingsText(wiredData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.mappingsText = "";
|
||||
this.mappings = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getMappingsText() {
|
||||
return this.mappingsText;
|
||||
}
|
||||
|
||||
public Map<Integer, String> getMappings() {
|
||||
return Collections.unmodifiableMap(this.mappings);
|
||||
}
|
||||
|
||||
public String resolveText(Integer value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String mappedValue = this.mappings.get(value);
|
||||
return mappedValue != null ? mappedValue : String.valueOf(value);
|
||||
}
|
||||
|
||||
public Integer resolveValue(String text) {
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String normalizedText = text.trim();
|
||||
if (normalizedText.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, String> entry : this.mappings.entrySet()) {
|
||||
if (entry == null || entry.getKey() == null || entry.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.getValue().trim().equalsIgnoreCase(normalizedText)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setMappingsText(String value) {
|
||||
this.mappingsText = normalizeMappingsText(value);
|
||||
this.mappings = parseMappings(this.mappingsText);
|
||||
}
|
||||
|
||||
private static String normalizeMappingsText(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String normalized = value.replace("\r", "");
|
||||
|
||||
if (normalized.length() > MAX_MAPPING_LENGTH) {
|
||||
normalized = normalized.substring(0, MAX_MAPPING_LENGTH);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static LinkedHashMap<Integer, String> parseMappings(String value) {
|
||||
LinkedHashMap<Integer, String> result = new LinkedHashMap<>();
|
||||
if (value == null || value.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (String rawLine : value.split("\n")) {
|
||||
if (rawLine == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String line = rawLine.trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int separatorIndex = line.indexOf('=');
|
||||
if (separatorIndex < 0) {
|
||||
separatorIndex = line.indexOf(',');
|
||||
}
|
||||
|
||||
if (separatorIndex <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String keyPart = line.substring(0, separatorIndex).trim();
|
||||
String valuePart = line.substring(separatorIndex + 1).trim();
|
||||
|
||||
try {
|
||||
result.put(Integer.parseInt(keyPart), valuePart);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String mappingsText;
|
||||
|
||||
JsonData(String mappingsText) {
|
||||
this.mappingsText = mappingsText;
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
final class WiredVariableNameValidator {
|
||||
static final int MIN_NAME_LENGTH = 1;
|
||||
static final int MAX_NAME_LENGTH = 40;
|
||||
|
||||
private static final Pattern VALID_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9_]+$");
|
||||
|
||||
private WiredVariableNameValidator() {
|
||||
}
|
||||
|
||||
static String normalizeForSave(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return value.trim()
|
||||
.replace("\t", "")
|
||||
.replace("\r", "")
|
||||
.replace("\n", "");
|
||||
}
|
||||
|
||||
static String normalizeLegacy(String value) {
|
||||
String normalized = normalizeForSave(value);
|
||||
|
||||
if (normalized.contains("=")) {
|
||||
normalized = normalized.substring(0, normalized.indexOf('=')).trim();
|
||||
}
|
||||
|
||||
while (normalized.startsWith("@") || normalized.startsWith("~")) {
|
||||
normalized = normalized.substring(1).trim();
|
||||
}
|
||||
|
||||
if (normalized.length() > MAX_NAME_LENGTH) {
|
||||
normalized = normalized.substring(0, MAX_NAME_LENGTH);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
static void validateDefinitionName(Room room, int currentItemId, String variableName) throws WiredSaveException {
|
||||
String normalized = normalizeForSave(variableName);
|
||||
|
||||
if (normalized.length() < MIN_NAME_LENGTH || normalized.length() > MAX_NAME_LENGTH) {
|
||||
throw new WiredSaveException("wiredfurni.error.variables.name_length");
|
||||
}
|
||||
|
||||
if (!VALID_NAME_PATTERN.matcher(normalized).matches()) {
|
||||
throw new WiredSaveException("wiredfurni.error.variables.name_syntax");
|
||||
}
|
||||
|
||||
if (isNameInUse(room, currentItemId, normalized)) {
|
||||
throw new WiredSaveException("wiredfurni.error.variables.name_uniq");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNameInUse(Room room, int currentItemId, String variableName) {
|
||||
if (room == null || room.getRoomSpecialTypes() == null || variableName == null || variableName.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (InteractionWiredExtra extra : room.getRoomSpecialTypes().getExtras()) {
|
||||
if (extra == null || extra.getId() == currentItemId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String existingName = getDefinitionName(extra);
|
||||
|
||||
if (existingName != null && existingName.equalsIgnoreCase(variableName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String getDefinitionName(InteractionWiredExtra extra) {
|
||||
if (extra instanceof WiredExtraUserVariable) {
|
||||
return ((WiredExtraUserVariable) extra).getVariableName();
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraFurniVariable) {
|
||||
return ((WiredExtraFurniVariable) extra).getVariableName();
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraRoomVariable) {
|
||||
return ((WiredExtraRoomVariable) extra).getVariableName();
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraContextVariable) {
|
||||
return ((WiredExtraContextVariable) extra).getVariableName();
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference) {
|
||||
return ((WiredExtraVariableReference) extra).getVariableName();
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableEcho) {
|
||||
return ((WiredExtraVariableEcho) extra).getVariableName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+629
@@ -0,0 +1,629 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class WiredVariableReferenceSupport {
|
||||
public static final int TARGET_USER = 0;
|
||||
public static final int TARGET_ROOM = 3;
|
||||
public static final int SHARED_AVAILABILITY = 11;
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WiredVariableReferenceSupport.class);
|
||||
|
||||
private static final ConcurrentHashMap<String, CachedUserAssignment> USER_ASSIGNMENT_CACHE = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, CachedRoomAssignment> ROOM_ASSIGNMENT_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private WiredVariableReferenceSupport() {
|
||||
}
|
||||
|
||||
public static boolean isSharedAvailability(int availability) {
|
||||
return availability == SHARED_AVAILABILITY;
|
||||
}
|
||||
|
||||
public static SharedDefinitionOption findSharedDefinition(Room room, int sourceRoomId, int sourceVariableItemId, int sourceTargetType) {
|
||||
if (room == null || sourceRoomId <= 0 || sourceVariableItemId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (RoomOption roomOption : loadRoomOptions(room)) {
|
||||
if (roomOption.getRoomId() != sourceRoomId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (SharedDefinitionOption definition : roomOption.getVariables()) {
|
||||
if (definition.getItemId() == sourceVariableItemId && definition.getTargetType() == sourceTargetType) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<RoomOption> loadRoomOptions(Room room) {
|
||||
if (room == null || room.getOwnerId() <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Map<Integer, RoomOption> optionsByRoomId = new LinkedHashMap<>();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT rooms.id AS room_id, rooms.name AS room_name, items.id AS item_id, items.wired_data, items_base.interaction_type " +
|
||||
"FROM rooms " +
|
||||
"INNER JOIN items ON rooms.id = items.room_id " +
|
||||
"INNER JOIN items_base ON items.item_id = items_base.id " +
|
||||
"WHERE rooms.owner_id = ? AND rooms.id <> ? AND items_base.interaction_type IN ('wf_var_user', 'wf_var_room') " +
|
||||
"ORDER BY rooms.name ASC, items.id ASC")) {
|
||||
statement.setInt(1, room.getOwnerId());
|
||||
statement.setInt(2, room.getId());
|
||||
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
SharedDefinitionOption definition = parseSharedDefinition(
|
||||
set.getString("interaction_type"),
|
||||
set.getInt("item_id"),
|
||||
set.getString("wired_data"),
|
||||
set.getInt("room_id"),
|
||||
set.getString("room_name")
|
||||
);
|
||||
|
||||
if (definition == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RoomOption roomOption = optionsByRoomId.computeIfAbsent(
|
||||
definition.getRoomId(),
|
||||
key -> new RoomOption(definition.getRoomId(), definition.getRoomName(), new ArrayList<>())
|
||||
);
|
||||
|
||||
roomOption.getVariables().add(definition);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to load shared variable reference options for room {}", room.getId(), e);
|
||||
}
|
||||
|
||||
List<RoomOption> result = new ArrayList<>(optionsByRoomId.values());
|
||||
|
||||
for (RoomOption option : result) {
|
||||
option.getVariables().sort(Comparator.comparing(SharedDefinitionOption::getName, String.CASE_INSENSITIVE_ORDER).thenComparingInt(SharedDefinitionOption::getItemId));
|
||||
}
|
||||
|
||||
result.sort(Comparator.comparing(RoomOption::getRoomName, String.CASE_INSENSITIVE_ORDER).thenComparingInt(RoomOption::getRoomId));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SharedUserAssignment getSharedUserAssignment(WiredExtraVariableReference reference, int userId) {
|
||||
if (reference == null || !reference.isUserReference() || userId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String cacheKey = createUserCacheKey(reference.getSourceRoomId(), reference.getSourceVariableItemId(), userId);
|
||||
CachedUserAssignment cachedValue = USER_ASSIGNMENT_CACHE.get(cacheKey);
|
||||
|
||||
if (cachedValue != null) {
|
||||
return cachedValue.present ? cachedValue.toAssignment() : null;
|
||||
}
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT value, created_at, updated_at FROM room_user_wired_variables WHERE room_id = ? AND user_id = ? AND variable_item_id = ? LIMIT 1")) {
|
||||
statement.setInt(1, reference.getSourceRoomId());
|
||||
statement.setInt(2, userId);
|
||||
statement.setInt(3, reference.getSourceVariableItemId());
|
||||
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (!set.next()) {
|
||||
USER_ASSIGNMENT_CACHE.put(cacheKey, CachedUserAssignment.missing());
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer value = null;
|
||||
int rawValue = set.getInt("value");
|
||||
if (!set.wasNull()) {
|
||||
value = rawValue;
|
||||
}
|
||||
|
||||
int createdAt = normalizeTimestamp(set.getInt("created_at"), 0);
|
||||
SharedUserAssignment assignment = new SharedUserAssignment(
|
||||
value,
|
||||
createdAt,
|
||||
normalizeTimestamp(set.getInt("updated_at"), createdAt)
|
||||
);
|
||||
|
||||
USER_ASSIGNMENT_CACHE.put(cacheKey, CachedUserAssignment.present(assignment));
|
||||
return assignment;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to load shared wired user variable {} for room {} user {}", reference.getSourceVariableItemId(), reference.getSourceRoomId(), userId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean assignSharedUserVariable(WiredExtraVariableReference reference, int userId, Integer value, boolean overrideExisting) {
|
||||
if (reference == null || !reference.isUserReference() || reference.isReadOnly() || userId <= 0 || !isSharedSourceStillAvailable(reference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer normalizedValue = reference.hasValue() ? value : null;
|
||||
SharedUserAssignment existingAssignment = getSharedUserAssignment(reference, userId);
|
||||
|
||||
if (existingAssignment != null && !overrideExisting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int now = Emulator.getIntUnixTimestamp();
|
||||
SharedUserAssignment nextAssignment = (existingAssignment == null)
|
||||
? new SharedUserAssignment(normalizedValue, now, now)
|
||||
: new SharedUserAssignment(normalizedValue, existingAssignment.getCreatedAt(), Objects.equals(existingAssignment.getValue(), normalizedValue) ? existingAssignment.getUpdatedAt() : now);
|
||||
|
||||
if (existingAssignment != null && Objects.equals(existingAssignment.getValue(), normalizedValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
upsertSharedUserAssignment(reference.getSourceRoomId(), reference.getSourceVariableItemId(), userId, nextAssignment);
|
||||
USER_ASSIGNMENT_CACHE.put(createUserCacheKey(reference.getSourceRoomId(), reference.getSourceVariableItemId(), userId), CachedUserAssignment.present(nextAssignment));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean updateSharedUserVariable(WiredExtraVariableReference reference, int userId, Integer value) {
|
||||
if (reference == null || !reference.isUserReference() || reference.isReadOnly() || userId <= 0 || !reference.hasValue() || !isSharedSourceStillAvailable(reference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedUserAssignment existingAssignment = getSharedUserAssignment(reference, userId);
|
||||
if (existingAssignment == null || Objects.equals(existingAssignment.getValue(), value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedUserAssignment nextAssignment = new SharedUserAssignment(value, existingAssignment.getCreatedAt(), Emulator.getIntUnixTimestamp());
|
||||
upsertSharedUserAssignment(reference.getSourceRoomId(), reference.getSourceVariableItemId(), userId, nextAssignment);
|
||||
USER_ASSIGNMENT_CACHE.put(createUserCacheKey(reference.getSourceRoomId(), reference.getSourceVariableItemId(), userId), CachedUserAssignment.present(nextAssignment));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean removeSharedUserVariable(WiredExtraVariableReference reference, int userId) {
|
||||
if (reference == null || !reference.isUserReference() || reference.isReadOnly() || userId <= 0 || !isSharedSourceStillAvailable(reference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedUserAssignment existingAssignment = getSharedUserAssignment(reference, userId);
|
||||
if (existingAssignment == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
deleteSharedUserAssignment(reference.getSourceRoomId(), reference.getSourceVariableItemId(), userId);
|
||||
USER_ASSIGNMENT_CACHE.put(createUserCacheKey(reference.getSourceRoomId(), reference.getSourceVariableItemId(), userId), CachedUserAssignment.missing());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void cacheSharedUserAssignment(int sourceRoomId, int sourceVariableItemId, int userId, Integer value, int createdAt, int updatedAt) {
|
||||
USER_ASSIGNMENT_CACHE.put(createUserCacheKey(sourceRoomId, sourceVariableItemId, userId), CachedUserAssignment.present(new SharedUserAssignment(value, createdAt, updatedAt)));
|
||||
}
|
||||
|
||||
public static void clearSharedUserAssignment(int sourceRoomId, int sourceVariableItemId, int userId) {
|
||||
USER_ASSIGNMENT_CACHE.put(createUserCacheKey(sourceRoomId, sourceVariableItemId, userId), CachedUserAssignment.missing());
|
||||
}
|
||||
|
||||
public static void clearSharedUserDefinition(int sourceRoomId, int sourceVariableItemId) {
|
||||
String prefix = createDefinitionPrefix(sourceRoomId, sourceVariableItemId) + ":";
|
||||
USER_ASSIGNMENT_CACHE.entrySet().removeIf(entry -> entry.getKey().startsWith(prefix));
|
||||
}
|
||||
|
||||
public static SharedRoomAssignment getSharedRoomAssignment(WiredExtraVariableReference reference) {
|
||||
if (reference == null || !reference.isRoomReference()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String cacheKey = createRoomCacheKey(reference.getSourceRoomId(), reference.getSourceVariableItemId());
|
||||
CachedRoomAssignment cachedValue = ROOM_ASSIGNMENT_CACHE.get(cacheKey);
|
||||
|
||||
if (cachedValue != null) {
|
||||
return cachedValue.present ? cachedValue.toAssignment() : null;
|
||||
}
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT value, updated_at FROM room_wired_variables WHERE room_id = ? AND variable_item_id = ? LIMIT 1")) {
|
||||
statement.setInt(1, reference.getSourceRoomId());
|
||||
statement.setInt(2, reference.getSourceVariableItemId());
|
||||
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (!set.next()) {
|
||||
ROOM_ASSIGNMENT_CACHE.put(cacheKey, CachedRoomAssignment.missing());
|
||||
return null;
|
||||
}
|
||||
|
||||
SharedRoomAssignment assignment = new SharedRoomAssignment(set.getInt("value"), normalizeTimestamp(set.getInt("updated_at"), 0));
|
||||
ROOM_ASSIGNMENT_CACHE.put(cacheKey, CachedRoomAssignment.present(assignment));
|
||||
return assignment;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to load shared wired room variable {} for room {}", reference.getSourceVariableItemId(), reference.getSourceRoomId(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean updateSharedRoomVariable(WiredExtraVariableReference reference, int value) {
|
||||
if (reference == null || !reference.isRoomReference() || reference.isReadOnly() || !isSharedSourceStillAvailable(reference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedRoomAssignment existingAssignment = getSharedRoomAssignment(reference);
|
||||
if (existingAssignment != null && existingAssignment.getValue() == value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedRoomAssignment nextAssignment = new SharedRoomAssignment(value, Emulator.getIntUnixTimestamp());
|
||||
upsertSharedRoomAssignment(reference.getSourceRoomId(), reference.getSourceVariableItemId(), nextAssignment);
|
||||
ROOM_ASSIGNMENT_CACHE.put(createRoomCacheKey(reference.getSourceRoomId(), reference.getSourceVariableItemId()), CachedRoomAssignment.present(nextAssignment));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean removeSharedRoomVariable(WiredExtraVariableReference reference) {
|
||||
if (reference == null || !reference.isRoomReference() || reference.isReadOnly() || !isSharedSourceStillAvailable(reference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedRoomAssignment existingAssignment = getSharedRoomAssignment(reference);
|
||||
if (existingAssignment == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
deleteSharedRoomAssignment(reference.getSourceRoomId(), reference.getSourceVariableItemId());
|
||||
ROOM_ASSIGNMENT_CACHE.put(createRoomCacheKey(reference.getSourceRoomId(), reference.getSourceVariableItemId()), CachedRoomAssignment.missing());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void cacheSharedRoomAssignment(int sourceRoomId, int sourceVariableItemId, int value, int updatedAt) {
|
||||
ROOM_ASSIGNMENT_CACHE.put(createRoomCacheKey(sourceRoomId, sourceVariableItemId), CachedRoomAssignment.present(new SharedRoomAssignment(value, updatedAt)));
|
||||
}
|
||||
|
||||
public static void clearSharedRoomDefinition(int sourceRoomId, int sourceVariableItemId) {
|
||||
ROOM_ASSIGNMENT_CACHE.put(createRoomCacheKey(sourceRoomId, sourceVariableItemId), CachedRoomAssignment.missing());
|
||||
}
|
||||
|
||||
private static SharedDefinitionOption parseSharedDefinition(String interactionType, int itemId, String wiredData, int roomId, String roomName) {
|
||||
if ("wf_var_user".equals(interactionType)) {
|
||||
UserDefinitionData data = parseUserDefinitionData(wiredData);
|
||||
if (data == null || !isSharedAvailability(data.availability) || data.variableName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SharedDefinitionOption(roomId, roomName, itemId, data.variableName, TARGET_USER, data.hasValue);
|
||||
}
|
||||
|
||||
if ("wf_var_room".equals(interactionType)) {
|
||||
RoomDefinitionData data = parseRoomDefinitionData(wiredData);
|
||||
if (data == null || !isSharedAvailability(data.availability) || data.variableName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SharedDefinitionOption(roomId, roomName, itemId, data.variableName, TARGET_ROOM, true);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static UserDefinitionData parseUserDefinitionData(String wiredData) {
|
||||
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserDefinitionData data = WiredManager.getGson().fromJson(wiredData, UserDefinitionData.class);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
data.variableName = WiredVariableNameValidator.normalizeLegacy(data.variableName);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static RoomDefinitionData parseRoomDefinitionData(String wiredData) {
|
||||
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RoomDefinitionData data = WiredManager.getGson().fromJson(wiredData, RoomDefinitionData.class);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
data.variableName = WiredVariableNameValidator.normalizeLegacy(data.variableName);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static boolean isSharedSourceStillAvailable(WiredExtraVariableReference reference) {
|
||||
if (reference == null || reference.getSourceRoomId() <= 0 || reference.getSourceVariableItemId() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT items.wired_data, items_base.interaction_type " +
|
||||
"FROM items INNER JOIN items_base ON items.item_id = items_base.id " +
|
||||
"WHERE items.id = ? AND items.room_id = ? LIMIT 1")) {
|
||||
statement.setInt(1, reference.getSourceVariableItemId());
|
||||
statement.setInt(2, reference.getSourceRoomId());
|
||||
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (!set.next()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedDefinitionOption definition = parseSharedDefinition(
|
||||
set.getString("interaction_type"),
|
||||
reference.getSourceVariableItemId(),
|
||||
set.getString("wired_data"),
|
||||
reference.getSourceRoomId(),
|
||||
""
|
||||
);
|
||||
|
||||
return definition != null && definition.getTargetType() == reference.getSourceTargetType();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to validate shared wired variable source {} in room {}", reference.getSourceVariableItemId(), reference.getSourceRoomId(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void upsertSharedUserAssignment(int sourceRoomId, int sourceVariableItemId, int userId, SharedUserAssignment assignment) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("INSERT INTO room_user_wired_variables (room_id, user_id, variable_item_id, value, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value), updated_at = VALUES(updated_at)")) {
|
||||
statement.setInt(1, sourceRoomId);
|
||||
statement.setInt(2, userId);
|
||||
statement.setInt(3, sourceVariableItemId);
|
||||
|
||||
if (assignment.getValue() == null) {
|
||||
statement.setNull(4, java.sql.Types.INTEGER);
|
||||
} else {
|
||||
statement.setInt(4, assignment.getValue());
|
||||
}
|
||||
|
||||
statement.setInt(5, assignment.getCreatedAt());
|
||||
statement.setInt(6, assignment.getUpdatedAt());
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to store shared wired user variable {} for room {} user {}", sourceVariableItemId, sourceRoomId, userId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteSharedUserAssignment(int sourceRoomId, int sourceVariableItemId, int userId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("DELETE FROM room_user_wired_variables WHERE room_id = ? AND user_id = ? AND variable_item_id = ?")) {
|
||||
statement.setInt(1, sourceRoomId);
|
||||
statement.setInt(2, userId);
|
||||
statement.setInt(3, sourceVariableItemId);
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to delete shared wired user variable {} for room {} user {}", sourceVariableItemId, sourceRoomId, userId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void upsertSharedRoomAssignment(int sourceRoomId, int sourceVariableItemId, SharedRoomAssignment assignment) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("INSERT INTO room_wired_variables (room_id, variable_item_id, value, created_at, updated_at) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value), updated_at = VALUES(updated_at)")) {
|
||||
statement.setInt(1, sourceRoomId);
|
||||
statement.setInt(2, sourceVariableItemId);
|
||||
statement.setInt(3, assignment.getValue());
|
||||
statement.setInt(4, 0);
|
||||
statement.setInt(5, assignment.getUpdatedAt());
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to store shared wired room variable {} for room {}", sourceVariableItemId, sourceRoomId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteSharedRoomAssignment(int sourceRoomId, int sourceVariableItemId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("DELETE FROM room_wired_variables WHERE room_id = ? AND variable_item_id = ?")) {
|
||||
statement.setInt(1, sourceRoomId);
|
||||
statement.setInt(2, sourceVariableItemId);
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to delete shared wired room variable {} for room {}", sourceVariableItemId, sourceRoomId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String createDefinitionPrefix(int sourceRoomId, int sourceVariableItemId) {
|
||||
return sourceRoomId + ":" + sourceVariableItemId;
|
||||
}
|
||||
|
||||
private static String createUserCacheKey(int sourceRoomId, int sourceVariableItemId, int userId) {
|
||||
return createDefinitionPrefix(sourceRoomId, sourceVariableItemId) + ":" + userId;
|
||||
}
|
||||
|
||||
private static String createRoomCacheKey(int sourceRoomId, int sourceVariableItemId) {
|
||||
return createDefinitionPrefix(sourceRoomId, sourceVariableItemId);
|
||||
}
|
||||
|
||||
private static int normalizeTimestamp(int value, int fallback) {
|
||||
if (value > 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (fallback > 0) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return Emulator.getIntUnixTimestamp();
|
||||
}
|
||||
|
||||
public static class RoomOption {
|
||||
private final int roomId;
|
||||
private final String roomName;
|
||||
private final List<SharedDefinitionOption> variables;
|
||||
|
||||
public RoomOption(int roomId, String roomName, List<SharedDefinitionOption> variables) {
|
||||
this.roomId = roomId;
|
||||
this.roomName = roomName;
|
||||
this.variables = variables;
|
||||
}
|
||||
|
||||
public int getRoomId() {
|
||||
return this.roomId;
|
||||
}
|
||||
|
||||
public String getRoomName() {
|
||||
return this.roomName;
|
||||
}
|
||||
|
||||
public List<SharedDefinitionOption> getVariables() {
|
||||
return this.variables;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SharedDefinitionOption {
|
||||
private final int roomId;
|
||||
private final String roomName;
|
||||
private final int itemId;
|
||||
private final String name;
|
||||
private final int targetType;
|
||||
private final boolean hasValue;
|
||||
|
||||
public SharedDefinitionOption(int roomId, String roomName, int itemId, String name, int targetType, boolean hasValue) {
|
||||
this.roomId = roomId;
|
||||
this.roomName = roomName;
|
||||
this.itemId = itemId;
|
||||
this.name = name;
|
||||
this.targetType = targetType;
|
||||
this.hasValue = hasValue;
|
||||
}
|
||||
|
||||
public int getRoomId() {
|
||||
return this.roomId;
|
||||
}
|
||||
|
||||
public String getRoomName() {
|
||||
return this.roomName;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return this.itemId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int getTargetType() {
|
||||
return this.targetType;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return this.hasValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SharedUserAssignment {
|
||||
private final Integer value;
|
||||
private final int createdAt;
|
||||
private final int updatedAt;
|
||||
|
||||
public SharedUserAssignment(Integer value, int createdAt, int updatedAt) {
|
||||
this.value = value;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public int getCreatedAt() {
|
||||
return this.createdAt;
|
||||
}
|
||||
|
||||
public int getUpdatedAt() {
|
||||
return this.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SharedRoomAssignment {
|
||||
private final int value;
|
||||
private final int updatedAt;
|
||||
|
||||
public SharedRoomAssignment(int value, int updatedAt) {
|
||||
this.value = value;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public int getUpdatedAt() {
|
||||
return this.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CachedUserAssignment {
|
||||
private final boolean present;
|
||||
private final SharedUserAssignment assignment;
|
||||
|
||||
private CachedUserAssignment(boolean present, SharedUserAssignment assignment) {
|
||||
this.present = present;
|
||||
this.assignment = assignment;
|
||||
}
|
||||
|
||||
private static CachedUserAssignment present(SharedUserAssignment assignment) {
|
||||
return new CachedUserAssignment(true, assignment);
|
||||
}
|
||||
|
||||
private static CachedUserAssignment missing() {
|
||||
return new CachedUserAssignment(false, null);
|
||||
}
|
||||
|
||||
private SharedUserAssignment toAssignment() {
|
||||
return this.assignment;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CachedRoomAssignment {
|
||||
private final boolean present;
|
||||
private final SharedRoomAssignment assignment;
|
||||
|
||||
private CachedRoomAssignment(boolean present, SharedRoomAssignment assignment) {
|
||||
this.present = present;
|
||||
this.assignment = assignment;
|
||||
}
|
||||
|
||||
private static CachedRoomAssignment present(SharedRoomAssignment assignment) {
|
||||
return new CachedRoomAssignment(true, assignment);
|
||||
}
|
||||
|
||||
private static CachedRoomAssignment missing() {
|
||||
return new CachedRoomAssignment(false, null);
|
||||
}
|
||||
|
||||
private SharedRoomAssignment toAssignment() {
|
||||
return this.assignment;
|
||||
}
|
||||
}
|
||||
|
||||
private static class UserDefinitionData {
|
||||
String variableName;
|
||||
boolean hasValue;
|
||||
int availability;
|
||||
}
|
||||
|
||||
private static class RoomDefinitionData {
|
||||
String variableName;
|
||||
int availability;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.selector;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredEffectFurniWithVariable extends WiredEffectVariableSelectorBase {
|
||||
public static final WiredEffectType type = WiredEffectType.FURNI_WITH_VAR_SELECTOR;
|
||||
|
||||
public WiredEffectFurniWithVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredEffectFurniWithVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVariableTargetType() {
|
||||
return TARGET_FURNI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredEffectType getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.selector;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredEffectUsersWithVariable extends WiredEffectVariableSelectorBase {
|
||||
public static final WiredEffectType type = WiredEffectType.USERS_WITH_VAR_SELECTOR;
|
||||
|
||||
public WiredEffectUsersWithVariable(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredEffectUsersWithVariable(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVariableTargetType() {
|
||||
return TARGET_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredEffectType getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
+862
@@ -0,0 +1,862 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.selector;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.games.Game;
|
||||
import com.eu.habbo.habbohotel.games.GamePlayer;
|
||||
import com.eu.habbo.habbohotel.games.GameTeam;
|
||||
import com.eu.habbo.habbohotel.games.GameTeamColors;
|
||||
import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame;
|
||||
import com.eu.habbo.habbohotel.games.freeze.FreezeGame;
|
||||
import com.eu.habbo.habbohotel.games.wired.WiredGame;
|
||||
import com.eu.habbo.habbohotel.items.FurnitureType;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWired;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomRightLevels;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.DanceType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContext;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredInternalVariableSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import com.eu.habbo.util.HotelDateTimeUtil;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public abstract class WiredEffectVariableSelectorBase extends InteractionWiredEffect {
|
||||
protected static final int TARGET_USER = 0;
|
||||
protected static final int TARGET_FURNI = 1;
|
||||
protected static final int TARGET_CONTEXT = 2;
|
||||
protected static final int TARGET_ROOM = 3;
|
||||
|
||||
protected static final int REFERENCE_CONSTANT = 0;
|
||||
protected static final int REFERENCE_VARIABLE = 1;
|
||||
|
||||
protected static final int SOURCE_SECONDARY_SELECTED = 101;
|
||||
|
||||
protected static final int COMPARISON_GREATER_THAN = 0;
|
||||
protected static final int COMPARISON_GREATER_THAN_OR_EQUAL = 1;
|
||||
protected static final int COMPARISON_EQUAL = 2;
|
||||
protected static final int COMPARISON_LESS_THAN_OR_EQUAL = 3;
|
||||
protected static final int COMPARISON_LESS_THAN = 4;
|
||||
protected static final int COMPARISON_NOT_EQUAL = 5;
|
||||
|
||||
private static final String CUSTOM_TOKEN_PREFIX = "custom:";
|
||||
private static final String INTERNAL_TOKEN_PREFIX = "internal:";
|
||||
private static final String DELIM = "\t";
|
||||
|
||||
protected boolean selectByValue = false;
|
||||
protected int comparison = COMPARISON_EQUAL;
|
||||
protected int referenceMode = REFERENCE_CONSTANT;
|
||||
protected int referenceConstantValue = 0;
|
||||
protected int referenceTargetType = TARGET_USER;
|
||||
protected int referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected int referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
protected boolean filterExisting = false;
|
||||
protected boolean invert = false;
|
||||
protected String variableToken = "";
|
||||
protected int variableItemId = 0;
|
||||
protected String referenceVariableToken = "";
|
||||
protected int referenceVariableItemId = 0;
|
||||
protected final THashSet<HabboItem> referenceSelectedItems = new THashSet<>();
|
||||
|
||||
protected WiredEffectVariableSelectorBase(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
protected WiredEffectVariableSelectorBase(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
protected abstract int getVariableTargetType();
|
||||
|
||||
@Override
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
|
||||
if (room == null || this.variableToken == null || this.variableToken.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getVariableTargetType() == TARGET_FURNI) {
|
||||
LinkedHashSet<HabboItem> matchedItems = new LinkedHashSet<>();
|
||||
|
||||
for (HabboItem item : this.getSelectableFloorItems(room, ctx)) {
|
||||
if (item == null) continue;
|
||||
if (!this.matchesFurni(room, item, ctx)) continue;
|
||||
|
||||
matchedItems.add(item);
|
||||
}
|
||||
|
||||
LinkedHashSet<HabboItem> result = this.applySelectorModifiers(matchedItems, this.getSelectableFloorItems(room, ctx), ctx.targets().items(), this.filterExisting, this.invert);
|
||||
ctx.targets().setItems(result);
|
||||
return;
|
||||
}
|
||||
|
||||
LinkedHashSet<RoomUnit> matchedUsers = new LinkedHashSet<>();
|
||||
|
||||
for (RoomUnit roomUnit : room.getRoomUnits()) {
|
||||
if (roomUnit == null) continue;
|
||||
if (!this.matchesUser(room, roomUnit, ctx)) continue;
|
||||
|
||||
matchedUsers.add(roomUnit);
|
||||
}
|
||||
|
||||
LinkedHashSet<RoomUnit> result = this.applySelectorModifiers(matchedUsers, room.getRoomUnits(), ctx.targets().users(), this.filterExisting, this.invert);
|
||||
ctx.targets().setUsers(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
|
||||
Room room = this.getRoom();
|
||||
if (room == null) return false;
|
||||
|
||||
int[] params = settings.getIntParams();
|
||||
String[] stringParts = parseStringData(settings.getStringParam());
|
||||
|
||||
boolean nextSelectByValue = param(params, 0, 0) == 1;
|
||||
int nextComparison = normalizeComparison(param(params, 1, COMPARISON_EQUAL));
|
||||
int nextReferenceMode = normalizeReferenceMode(param(params, 2, REFERENCE_CONSTANT));
|
||||
int nextReferenceConstantValue = param(params, 3, 0);
|
||||
int nextReferenceTargetType = normalizeReferenceTargetType(param(params, 4, TARGET_USER));
|
||||
int nextReferenceUserSource = normalizeUserSource(param(params, 5, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
int nextReferenceFurniSource = normalizeReferenceFurniSource(param(params, 6, WiredSourceUtil.SOURCE_TRIGGER));
|
||||
boolean nextFilterExisting = param(params, 7, 0) == 1;
|
||||
boolean nextInvert = param(params, 8, 0) == 1;
|
||||
String nextVariableToken = normalizeVariableToken((stringParts.length > 0) ? stringParts[0] : settings.getStringParam());
|
||||
String nextReferenceVariableToken = normalizeVariableToken((stringParts.length > 1) ? stringParts[1] : "");
|
||||
|
||||
if (!this.isValidMainVariable(room, nextVariableToken, nextSelectByValue)) return false;
|
||||
if (nextSelectByValue && nextReferenceMode == REFERENCE_VARIABLE && !this.isValidReference(room, nextReferenceTargetType, nextReferenceVariableToken)) return false;
|
||||
|
||||
List<HabboItem> nextReferenceItems = new ArrayList<>();
|
||||
|
||||
if (nextSelectByValue && nextReferenceMode == REFERENCE_VARIABLE && nextReferenceTargetType == TARGET_FURNI && nextReferenceFurniSource == SOURCE_SECONDARY_SELECTED) {
|
||||
int[] furniIds = settings.getFurniIds();
|
||||
if (furniIds.length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false;
|
||||
|
||||
for (int furniId : furniIds) {
|
||||
HabboItem item = room.getHabboItem(furniId);
|
||||
if (item != null) nextReferenceItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
this.referenceSelectedItems.clear();
|
||||
this.referenceSelectedItems.addAll(nextReferenceItems);
|
||||
this.selectByValue = nextSelectByValue;
|
||||
this.comparison = nextComparison;
|
||||
this.referenceMode = nextReferenceMode;
|
||||
this.referenceConstantValue = nextReferenceConstantValue;
|
||||
this.referenceTargetType = nextReferenceTargetType;
|
||||
this.referenceUserSource = nextReferenceUserSource;
|
||||
this.referenceFurniSource = nextReferenceFurniSource;
|
||||
this.filterExisting = nextFilterExisting;
|
||||
this.invert = nextInvert;
|
||||
this.setVariableToken(nextVariableToken);
|
||||
this.setReferenceVariableToken(nextReferenceVariableToken);
|
||||
this.setDelay(settings.getDelay());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelector() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
this.refreshReferenceItems();
|
||||
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
this.selectByValue,
|
||||
this.comparison,
|
||||
this.referenceMode,
|
||||
this.referenceConstantValue,
|
||||
this.referenceTargetType,
|
||||
this.referenceUserSource,
|
||||
this.referenceFurniSource,
|
||||
this.filterExisting,
|
||||
this.invert,
|
||||
this.variableToken,
|
||||
this.variableItemId,
|
||||
this.referenceVariableToken,
|
||||
this.referenceVariableItemId,
|
||||
this.toIds(this.referenceSelectedItems),
|
||||
this.getDelay()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) return;
|
||||
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
if (data == null) return;
|
||||
|
||||
this.selectByValue = data.selectByValue;
|
||||
this.comparison = normalizeComparison(data.comparison);
|
||||
this.referenceMode = normalizeReferenceMode(data.referenceMode);
|
||||
this.referenceConstantValue = data.referenceConstantValue;
|
||||
this.referenceTargetType = normalizeReferenceTargetType(data.referenceTargetType);
|
||||
this.referenceUserSource = normalizeUserSource(data.referenceUserSource);
|
||||
this.referenceFurniSource = normalizeReferenceFurniSource(data.referenceFurniSource);
|
||||
this.filterExisting = data.filterExisting;
|
||||
this.invert = data.invert;
|
||||
this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
|
||||
this.setReferenceVariableToken(normalizeVariableToken((data.referenceVariableToken != null) ? data.referenceVariableToken : ((data.referenceVariableItemId > 0) ? String.valueOf(data.referenceVariableItemId) : "")));
|
||||
this.setDelay(data.delay);
|
||||
|
||||
if (room == null || data.selectedItemIds == null) return;
|
||||
|
||||
for (Integer itemId : data.selectedItemIds) {
|
||||
if (itemId == null || itemId <= 0) continue;
|
||||
|
||||
HabboItem item = room.getHabboItem(itemId);
|
||||
if (item != null) this.referenceSelectedItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.selectByValue = false;
|
||||
this.comparison = COMPARISON_EQUAL;
|
||||
this.referenceMode = REFERENCE_CONSTANT;
|
||||
this.referenceConstantValue = 0;
|
||||
this.referenceTargetType = TARGET_USER;
|
||||
this.referenceUserSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.referenceFurniSource = WiredSourceUtil.SOURCE_TRIGGER;
|
||||
this.filterExisting = false;
|
||||
this.invert = false;
|
||||
this.referenceSelectedItems.clear();
|
||||
this.setVariableToken("");
|
||||
this.setReferenceVariableToken("");
|
||||
this.setDelay(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
this.refreshReferenceItems();
|
||||
|
||||
List<HabboItem> serializedItems = new ArrayList<>();
|
||||
if (this.selectByValue && this.referenceMode == REFERENCE_VARIABLE && this.referenceTargetType == TARGET_FURNI && this.referenceFurniSource == SOURCE_SECONDARY_SELECTED) {
|
||||
serializedItems.addAll(this.referenceSelectedItems);
|
||||
}
|
||||
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
|
||||
message.appendInt(serializedItems.size());
|
||||
|
||||
for (HabboItem item : serializedItems) {
|
||||
message.appendInt(item.getId());
|
||||
}
|
||||
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.serializeStringData());
|
||||
message.appendInt(9);
|
||||
message.appendInt(this.selectByValue ? 1 : 0);
|
||||
message.appendInt(this.comparison);
|
||||
message.appendInt(this.referenceMode);
|
||||
message.appendInt(this.referenceConstantValue);
|
||||
message.appendInt(this.referenceTargetType);
|
||||
message.appendInt(this.referenceUserSource);
|
||||
message.appendInt(this.referenceFurniSource);
|
||||
message.appendInt(this.filterExisting ? 1 : 0);
|
||||
message.appendInt(this.invert ? 1 : 0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getType().code);
|
||||
message.appendInt(this.getDelay());
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresTriggeringUser() {
|
||||
return this.selectByValue && this.referenceMode == REFERENCE_VARIABLE && this.referenceTargetType == TARGET_USER && this.referenceUserSource == WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private boolean matchesUser(Room room, RoomUnit roomUnit, WiredContext ctx) {
|
||||
if (!this.selectByValue) return this.hasUserVariable(room, roomUnit);
|
||||
|
||||
Integer currentValue = this.readUserValue(room, roomUnit);
|
||||
Integer referenceValue = this.resolveReferenceValue(ctx, room, roomUnit != null ? roomUnit.getId() : 0, TARGET_USER, -1);
|
||||
|
||||
return this.matchesComparison(currentValue, referenceValue);
|
||||
}
|
||||
|
||||
private boolean matchesFurni(Room room, HabboItem item, WiredContext ctx) {
|
||||
if (!this.selectByValue) return this.hasFurniVariable(room, item);
|
||||
|
||||
Integer currentValue = this.readFurniValue(room, item);
|
||||
Integer referenceValue = this.resolveReferenceValue(ctx, room, item != null ? item.getId() : 0, TARGET_FURNI, -1);
|
||||
|
||||
return this.matchesComparison(currentValue, referenceValue);
|
||||
}
|
||||
|
||||
private boolean hasUserVariable(Room room, RoomUnit roomUnit) {
|
||||
if (room == null || roomUnit == null) return false;
|
||||
|
||||
if (isCustomVariableToken(this.variableToken)) {
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
return habbo != null && room.getUserVariableManager().hasVariable(habbo.getHabboInfo().getId(), this.variableItemId);
|
||||
}
|
||||
|
||||
return isInternalVariableToken(this.variableToken) && this.hasUserInternalVariable(room, roomUnit, getInternalVariableKey(this.variableToken));
|
||||
}
|
||||
|
||||
private boolean hasFurniVariable(Room room, HabboItem item) {
|
||||
if (room == null || item == null) return false;
|
||||
|
||||
if (isCustomVariableToken(this.variableToken)) {
|
||||
return room.getFurniVariableManager().hasVariable(item.getId(), this.variableItemId);
|
||||
}
|
||||
|
||||
return isInternalVariableToken(this.variableToken) && this.hasFurniInternalVariable(item, getInternalVariableKey(this.variableToken));
|
||||
}
|
||||
|
||||
private Integer resolveReferenceValue(WiredContext ctx, Room room, int destinationEntityId, int destinationTargetType, int destinationIndex) {
|
||||
if (!this.selectByValue || this.referenceMode != REFERENCE_VARIABLE) return this.referenceConstantValue;
|
||||
|
||||
ReferenceSnapshot snapshot = this.resolveReferences(ctx, room);
|
||||
if (snapshot == null || snapshot.isEmpty()) return null;
|
||||
if (snapshot.targetType == destinationTargetType && snapshot.values.containsKey(destinationEntityId)) return snapshot.values.get(destinationEntityId);
|
||||
if (destinationIndex >= 0 && destinationIndex < snapshot.values.size()) return new ArrayList<>(snapshot.values.values()).get(destinationIndex);
|
||||
|
||||
return new ArrayList<>(snapshot.values.values()).get(0);
|
||||
}
|
||||
|
||||
private ReferenceSnapshot resolveReferences(WiredContext ctx, Room room) {
|
||||
return switch (this.referenceTargetType) {
|
||||
case TARGET_FURNI -> this.furniReferences(ctx, room);
|
||||
case TARGET_CONTEXT -> this.contextReferences(ctx, room);
|
||||
case TARGET_ROOM -> this.roomReferences(room);
|
||||
default -> this.userReferences(ctx, room);
|
||||
};
|
||||
}
|
||||
|
||||
private ReferenceSnapshot userReferences(WiredContext ctx, Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_USER);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseUserInternalReference(key)) return null;
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
|
||||
Integer value = this.readUserInternalValue(room, roomUnit, key);
|
||||
if (value != null && roomUnit != null) snapshot.add(roomUnit.getId(), value);
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.referenceUserSource)) {
|
||||
if (roomUnit == null) continue;
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (habbo != null) snapshot.add(roomUnit.getId(), room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.referenceVariableItemId));
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot furniReferences(WiredContext ctx, Room room) {
|
||||
int source = (this.referenceFurniSource == SOURCE_SECONDARY_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : this.referenceFurniSource;
|
||||
if (source == WiredSourceUtil.SOURCE_SELECTED) this.refreshReferenceItems();
|
||||
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_FURNI);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseFurniInternalReference(key)) return null;
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedItems)) {
|
||||
Integer value = this.readFurniInternalValue(room, item, key);
|
||||
if (value != null && item != null) snapshot.add(item.getId(), value);
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
for (HabboItem item : WiredSourceUtil.resolveItems(ctx, source, this.referenceSelectedItems)) {
|
||||
if (item != null) snapshot.add(item.getId(), room.getFurniVariableManager().getCurrentValue(item.getId(), this.referenceVariableItemId));
|
||||
}
|
||||
|
||||
return snapshot.isEmpty() ? null : snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot roomReferences(Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_ROOM);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseRoomInternalReference(key)) return null;
|
||||
|
||||
Integer value = this.readRoomInternalValue(room, key);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(room.getId(), value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getRoomVariableManager().getDefinitionInfo(this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
snapshot.add(room.getId(), room.getRoomVariableManager().getCurrentValue(this.referenceVariableItemId));
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private ReferenceSnapshot contextReferences(WiredContext ctx, Room room) {
|
||||
ReferenceSnapshot snapshot = new ReferenceSnapshot(TARGET_CONTEXT);
|
||||
|
||||
if (isInternalVariableToken(this.referenceVariableToken)) {
|
||||
String key = getInternalVariableKey(this.referenceVariableToken);
|
||||
if (!canUseContextInternalReference(key)) return null;
|
||||
|
||||
Integer value = WiredInternalVariableSupport.readContextValue(ctx, key);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(this.referenceVariableItemId > 0 ? this.referenceVariableItemId : (room != null ? room.getId() : 0), value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, this.referenceVariableItemId);
|
||||
if (definition == null || !definition.hasValue() || !WiredContextVariableSupport.hasVariable(ctx, this.referenceVariableItemId)) return null;
|
||||
|
||||
Integer value = WiredContextVariableSupport.getCurrentValue(ctx, this.referenceVariableItemId);
|
||||
if (value == null) return null;
|
||||
|
||||
snapshot.add(this.referenceVariableItemId, value);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private boolean isValidMainVariable(Room room, String token, boolean requireValue) {
|
||||
if (token == null || token.isEmpty()) return false;
|
||||
|
||||
int targetType = this.getVariableTargetType();
|
||||
|
||||
if (isInternalVariableToken(token)) {
|
||||
String key = getInternalVariableKey(token);
|
||||
return targetType == TARGET_FURNI
|
||||
? (requireValue ? canUseFurniInternalReference(key) : this.hasFurniInternalKey(key))
|
||||
: (requireValue ? canUseUserInternalReference(key) : this.hasUserInternalKey(key));
|
||||
}
|
||||
|
||||
if (targetType == TARGET_FURNI) {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getFurniVariableManager().getDefinitionInfo(getCustomItemId(token)) : null;
|
||||
return definition != null && (!requireValue || definition.hasValue());
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getUserVariableManager().getDefinitionInfo(getCustomItemId(token)) : null;
|
||||
return definition != null && (!requireValue || definition.hasValue());
|
||||
}
|
||||
|
||||
private boolean isValidReference(Room room, int targetType, String token) {
|
||||
if (token == null || token.isEmpty()) return false;
|
||||
|
||||
if (isInternalVariableToken(token)) {
|
||||
String key = getInternalVariableKey(token);
|
||||
return switch (targetType) {
|
||||
case TARGET_FURNI -> canUseFurniInternalReference(key);
|
||||
case TARGET_CONTEXT -> canUseContextInternalReference(key);
|
||||
case TARGET_ROOM -> canUseRoomInternalReference(key);
|
||||
default -> canUseUserInternalReference(key);
|
||||
};
|
||||
}
|
||||
|
||||
return switch (targetType) {
|
||||
case TARGET_FURNI -> {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getFurniVariableManager().getDefinitionInfo(getCustomItemId(token)) : null;
|
||||
yield definition != null && definition.hasValue();
|
||||
}
|
||||
case TARGET_CONTEXT -> this.isValidContextCustomReference(room, getCustomItemId(token));
|
||||
case TARGET_ROOM -> {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getRoomVariableManager().getDefinitionInfo(getCustomItemId(token)) : null;
|
||||
yield definition != null && definition.hasValue();
|
||||
}
|
||||
default -> {
|
||||
WiredVariableDefinitionInfo definition = (room != null) ? room.getUserVariableManager().getDefinitionInfo(getCustomItemId(token)) : null;
|
||||
yield definition != null && definition.hasValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isValidContextCustomReference(Room room, int variableItemId) {
|
||||
WiredVariableDefinitionInfo definition = WiredContextVariableSupport.getDefinitionInfo(room, variableItemId);
|
||||
return definition != null && definition.hasValue();
|
||||
}
|
||||
|
||||
private boolean matchesComparison(Integer currentValue, Integer referenceValue) {
|
||||
if (currentValue == null || referenceValue == null) return false;
|
||||
|
||||
return switch (this.comparison) {
|
||||
case COMPARISON_GREATER_THAN -> currentValue > referenceValue;
|
||||
case COMPARISON_GREATER_THAN_OR_EQUAL -> currentValue >= referenceValue;
|
||||
case COMPARISON_LESS_THAN_OR_EQUAL -> currentValue <= referenceValue;
|
||||
case COMPARISON_LESS_THAN -> currentValue < referenceValue;
|
||||
case COMPARISON_NOT_EQUAL -> !currentValue.equals(referenceValue);
|
||||
default -> currentValue.equals(referenceValue);
|
||||
};
|
||||
}
|
||||
|
||||
private Integer readUserValue(Room room, RoomUnit roomUnit) {
|
||||
if (room == null || roomUnit == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
String key = getInternalVariableKey(this.variableToken);
|
||||
return canUseUserInternalReference(key) ? this.readUserInternalValue(room, roomUnit, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getUserVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
if (definition == null || !definition.hasValue()) return null;
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
return (habbo != null) ? room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), this.variableItemId) : null;
|
||||
}
|
||||
|
||||
private Integer readFurniValue(Room room, HabboItem item) {
|
||||
if (room == null || item == null) return null;
|
||||
|
||||
if (isInternalVariableToken(this.variableToken)) {
|
||||
String key = getInternalVariableKey(this.variableToken);
|
||||
return canUseFurniInternalReference(key) ? this.readFurniInternalValue(room, item, key) : null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definition = room.getFurniVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
return (definition != null && definition.hasValue()) ? room.getFurniVariableManager().getCurrentValue(item.getId(), this.variableItemId) : null;
|
||||
}
|
||||
|
||||
private boolean hasUserInternalVariable(Room room, RoomUnit roomUnit, String key) {
|
||||
return WiredInternalVariableSupport.hasUserValue(room, roomUnit, key);
|
||||
}
|
||||
|
||||
private boolean hasFurniInternalVariable(HabboItem item, String key) {
|
||||
return WiredInternalVariableSupport.hasFurniValue(item, key);
|
||||
}
|
||||
|
||||
private boolean hasUserInternalKey(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserReference(key);
|
||||
}
|
||||
|
||||
private boolean hasFurniInternalKey(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniReference(key) || "@wallitem_offset".equals(WiredInternalVariableSupport.normalizeKey(key));
|
||||
}
|
||||
|
||||
private boolean hasRoomEntryMethod(Habbo habbo) {
|
||||
if (habbo == null) return false;
|
||||
|
||||
String roomEntryMethod = habbo.getHabboInfo().getRoomEntryMethod();
|
||||
return roomEntryMethod != null && !roomEntryMethod.trim().isEmpty() && !"unknown".equalsIgnoreCase(roomEntryMethod);
|
||||
}
|
||||
|
||||
private Integer readUserInternalValue(Room room, RoomUnit roomUnit, String key) {
|
||||
return WiredInternalVariableSupport.readUserValue(room, roomUnit, key);
|
||||
}
|
||||
|
||||
private Integer readFurniInternalValue(Room room, HabboItem item, String key) {
|
||||
return WiredInternalVariableSupport.readFurniValue(room, item, key);
|
||||
}
|
||||
|
||||
private Integer readRoomInternalValue(Room room, String key) {
|
||||
return WiredInternalVariableSupport.readRoomValue(room, key);
|
||||
}
|
||||
|
||||
private Integer getUserTeamScore(Room room, Habbo habbo) {
|
||||
if (room == null || habbo == null || habbo.getHabboInfo().getGamePlayer() == null) return null;
|
||||
|
||||
Game game = this.resolveTeamGame(room, habbo);
|
||||
GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer();
|
||||
|
||||
if (game == null || gamePlayer.getTeamColor() == null) return gamePlayer.getScore();
|
||||
|
||||
GameTeam team = game.getTeam(gamePlayer.getTeamColor());
|
||||
return (team != null) ? team.getTotalScore() : gamePlayer.getScore();
|
||||
}
|
||||
|
||||
private Integer getTeamColorId(int effectId) {
|
||||
TeamEffectData data = this.getTeamEffectData(effectId);
|
||||
return data == null ? null : data.colorId;
|
||||
}
|
||||
|
||||
private Integer getTeamTypeId(int effectId) {
|
||||
TeamEffectData data = this.getTeamEffectData(effectId);
|
||||
return data == null ? null : data.typeId;
|
||||
}
|
||||
|
||||
private int getTeamMetric(Room room, GameTeamColors color, boolean score) {
|
||||
Game game = this.resolveTeamGame(room, null);
|
||||
if (game == null || color == null) return 0;
|
||||
|
||||
GameTeam team = game.getTeam(color);
|
||||
if (team == null) return 0;
|
||||
|
||||
return score ? team.getTotalScore() : team.getMembers().size();
|
||||
}
|
||||
|
||||
private Game resolveTeamGame(Room room, Habbo habbo) {
|
||||
if (room == null) return null;
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getCurrentGame() != null) {
|
||||
Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
|
||||
if (game != null) return game;
|
||||
}
|
||||
|
||||
Game wiredGame = room.getGame(WiredGame.class);
|
||||
if (wiredGame != null) return wiredGame;
|
||||
|
||||
Game freezeGame = room.getGame(FreezeGame.class);
|
||||
if (freezeGame != null) return freezeGame;
|
||||
|
||||
return room.getGame(BattleBanzaiGame.class);
|
||||
}
|
||||
|
||||
private TeamEffectData getTeamEffectData(int effectValue) {
|
||||
if (effectValue <= 0) return null;
|
||||
|
||||
if (effectValue >= 223 && effectValue <= 226) return new TeamEffectData(effectValue - 222, 0);
|
||||
if (effectValue >= 33 && effectValue <= 36) return new TeamEffectData(effectValue - 32, 1);
|
||||
if (effectValue >= 40 && effectValue <= 43) return new TeamEffectData(effectValue - 39, 2);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void refreshReferenceItems() {
|
||||
THashSet<HabboItem> staleItems = new THashSet<>();
|
||||
Room room = this.getRoom();
|
||||
|
||||
if (room == null) {
|
||||
staleItems.addAll(this.referenceSelectedItems);
|
||||
} else {
|
||||
for (HabboItem item : this.referenceSelectedItems) {
|
||||
if (item == null || item.getRoomId() != room.getId() || room.getHabboItem(item.getId()) == null) {
|
||||
staleItems.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.referenceSelectedItems.removeAll(staleItems);
|
||||
}
|
||||
|
||||
private String serializeStringData() {
|
||||
return (this.variableToken == null ? "" : this.variableToken) + DELIM + (this.referenceVariableToken == null ? "" : this.referenceVariableToken);
|
||||
}
|
||||
|
||||
private void setVariableToken(String token) {
|
||||
this.variableToken = normalizeVariableToken(token);
|
||||
this.variableItemId = getCustomItemId(this.variableToken);
|
||||
}
|
||||
|
||||
private void setReferenceVariableToken(String token) {
|
||||
this.referenceVariableToken = normalizeVariableToken(token);
|
||||
this.referenceVariableItemId = getCustomItemId(this.referenceVariableToken);
|
||||
}
|
||||
|
||||
private List<Integer> toIds(THashSet<HabboItem> items) {
|
||||
List<Integer> ids = new ArrayList<>();
|
||||
|
||||
for (HabboItem item : items) {
|
||||
if (item != null) ids.add(item.getId());
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
private static int normalizeReferenceMode(int value) {
|
||||
return (value == REFERENCE_VARIABLE) ? REFERENCE_VARIABLE : REFERENCE_CONSTANT;
|
||||
}
|
||||
|
||||
private static int normalizeReferenceTargetType(int value) {
|
||||
return switch (value) {
|
||||
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
|
||||
default -> TARGET_USER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeReferenceFurniSource(int value) {
|
||||
return switch (value) {
|
||||
case SOURCE_SECONDARY_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
|
||||
default -> WiredSourceUtil.SOURCE_TRIGGER;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeComparison(int value) {
|
||||
return switch (value) {
|
||||
case COMPARISON_GREATER_THAN, COMPARISON_GREATER_THAN_OR_EQUAL, COMPARISON_LESS_THAN_OR_EQUAL, COMPARISON_LESS_THAN, COMPARISON_NOT_EQUAL -> value;
|
||||
default -> COMPARISON_EQUAL;
|
||||
};
|
||||
}
|
||||
|
||||
private static int normalizeUserSource(int value) {
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
protected static boolean isCustomVariableToken(String token) {
|
||||
return token != null && token.startsWith(CUSTOM_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
protected static boolean isInternalVariableToken(String token) {
|
||||
return token != null && token.startsWith(INTERNAL_TOKEN_PREFIX);
|
||||
}
|
||||
|
||||
private static int getCustomItemId(String token) {
|
||||
if (!isCustomVariableToken(token)) return 0;
|
||||
|
||||
try {
|
||||
return Integer.parseInt(token.substring(CUSTOM_TOKEN_PREFIX.length()));
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getInternalVariableKey(String token) {
|
||||
return isInternalVariableToken(token) ? WiredInternalVariableSupport.normalizeKey(token.substring(INTERNAL_TOKEN_PREFIX.length())) : "";
|
||||
}
|
||||
|
||||
protected static String normalizeVariableToken(String token) {
|
||||
if (token == null) return "";
|
||||
|
||||
String normalized = token.trim();
|
||||
if (normalized.isEmpty()) return "";
|
||||
if (isCustomVariableToken(normalized)) return normalized;
|
||||
if (isInternalVariableToken(normalized)) return INTERNAL_TOKEN_PREFIX + WiredInternalVariableSupport.normalizeKey(normalized.substring(INTERNAL_TOKEN_PREFIX.length()));
|
||||
|
||||
try {
|
||||
int parsed = Integer.parseInt(normalized);
|
||||
return (parsed > 0) ? (CUSTOM_TOKEN_PREFIX + parsed) : "";
|
||||
} catch (NumberFormatException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canUseUserInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseUserReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseFurniInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseFurniReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseRoomInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseRoomReference(key);
|
||||
}
|
||||
|
||||
private static boolean canUseContextInternalReference(String key) {
|
||||
return WiredInternalVariableSupport.canUseContextReference(key);
|
||||
}
|
||||
|
||||
private static int param(int[] params, int index, int fallback) {
|
||||
return (params != null && params.length > index) ? params[index] : fallback;
|
||||
}
|
||||
|
||||
private static String[] parseStringData(String value) {
|
||||
return (value == null || value.isEmpty()) ? new String[0] : value.split("\\t", -1);
|
||||
}
|
||||
|
||||
private static int parseInteger(String value) {
|
||||
try {
|
||||
return (value == null || value.trim().isEmpty()) ? 0 : Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class JsonData {
|
||||
boolean selectByValue;
|
||||
int comparison;
|
||||
int referenceMode;
|
||||
int referenceConstantValue;
|
||||
int referenceTargetType;
|
||||
int referenceUserSource;
|
||||
int referenceFurniSource;
|
||||
boolean filterExisting;
|
||||
boolean invert;
|
||||
String variableToken;
|
||||
int variableItemId;
|
||||
String referenceVariableToken;
|
||||
int referenceVariableItemId;
|
||||
List<Integer> selectedItemIds;
|
||||
int delay;
|
||||
|
||||
JsonData(boolean selectByValue, int comparison, int referenceMode, int referenceConstantValue, int referenceTargetType, int referenceUserSource, int referenceFurniSource, boolean filterExisting, boolean invert, String variableToken, int variableItemId, String referenceVariableToken, int referenceVariableItemId, List<Integer> selectedItemIds, int delay) {
|
||||
this.selectByValue = selectByValue;
|
||||
this.comparison = comparison;
|
||||
this.referenceMode = referenceMode;
|
||||
this.referenceConstantValue = referenceConstantValue;
|
||||
this.referenceTargetType = referenceTargetType;
|
||||
this.referenceUserSource = referenceUserSource;
|
||||
this.referenceFurniSource = referenceFurniSource;
|
||||
this.filterExisting = filterExisting;
|
||||
this.invert = invert;
|
||||
this.variableToken = variableToken;
|
||||
this.variableItemId = variableItemId;
|
||||
this.referenceVariableToken = referenceVariableToken;
|
||||
this.referenceVariableItemId = referenceVariableItemId;
|
||||
this.selectedItemIds = selectedItemIds;
|
||||
this.delay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReferenceSnapshot {
|
||||
final int targetType;
|
||||
final LinkedHashMap<Integer, Integer> values = new LinkedHashMap<>();
|
||||
|
||||
ReferenceSnapshot(int targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
void add(int entityId, int value) {
|
||||
this.values.put(entityId, value);
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return this.values.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TeamEffectData {
|
||||
final int colorId;
|
||||
final int typeId;
|
||||
|
||||
TeamEffectData(int colorId, int typeId) {
|
||||
this.colorId = colorId;
|
||||
this.typeId = typeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -141,6 +141,18 @@ public class WiredTriggerHabboSaysKeyword extends InteractionWiredTrigger {
|
||||
return this.hideMessage;
|
||||
}
|
||||
|
||||
public boolean isOwnerOnly() {
|
||||
return this.ownerOnly;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public int getMatchMode() {
|
||||
return this.matchMode;
|
||||
}
|
||||
|
||||
private boolean matchesText(String text) {
|
||||
String normalizedText = text.toLowerCase().trim();
|
||||
String normalizedKey = this.key.toLowerCase().trim();
|
||||
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredTriggerVariableChanged extends InteractionWiredTrigger {
|
||||
public static final WiredTriggerType type = WiredTriggerType.VARIABLE_CHANGED;
|
||||
|
||||
public static final int TARGET_USER = 0;
|
||||
public static final int TARGET_FURNI = 1;
|
||||
public static final int TARGET_ROOM = 3;
|
||||
|
||||
private static final String CUSTOM_TOKEN_PREFIX = "custom:";
|
||||
|
||||
private String variableToken = "";
|
||||
private int variableItemId = 0;
|
||||
private int targetType = TARGET_USER;
|
||||
private boolean createdEnabled = true;
|
||||
private boolean valueChangedEnabled = true;
|
||||
private boolean increasedEnabled = true;
|
||||
private boolean decreasedEnabled = true;
|
||||
private boolean unchangedEnabled = true;
|
||||
private boolean deletedEnabled = true;
|
||||
|
||||
public WiredTriggerVariableChanged(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredTriggerVariableChanged(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HabboItem triggerItem, WiredEvent event) {
|
||||
if (event == null || event.getType() != WiredEvent.Type.VARIABLE_CHANGED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.getVariableTargetType() != this.targetType || event.getVariableDefinitionItemId() != this.variableItemId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.createdEnabled && event.isVariableCreated()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.deletedEnabled && event.isVariableDeleted()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.valueChangedEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (event.getVariableChangeKind()) {
|
||||
case INCREASED -> this.increasedEnabled;
|
||||
case DECREASED -> this.decreasedEnabled;
|
||||
case UNCHANGED -> this.unchangedEnabled;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WiredTriggerType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString(this.variableToken == null ? "" : this.variableToken);
|
||||
message.appendInt(7);
|
||||
message.appendInt(this.targetType);
|
||||
message.appendInt(this.createdEnabled ? 1 : 0);
|
||||
message.appendInt(this.valueChangedEnabled ? 1 : 0);
|
||||
message.appendInt(this.increasedEnabled ? 1 : 0);
|
||||
message.appendInt(this.decreasedEnabled ? 1 : 0);
|
||||
message.appendInt(this.unchangedEnabled ? 1 : 0);
|
||||
message.appendInt(this.deletedEnabled ? 1 : 0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getType().code);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings) {
|
||||
return this.saveData(settings, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) {
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
|
||||
int[] params = settings.getIntParams();
|
||||
|
||||
this.targetType = normalizeTargetType((params.length > 0) ? params[0] : TARGET_USER);
|
||||
this.createdEnabled = (params.length <= 1) || (params[1] == 1);
|
||||
this.valueChangedEnabled = (params.length <= 2) || (params[2] == 1);
|
||||
this.increasedEnabled = (params.length <= 3) || (params[3] == 1);
|
||||
this.decreasedEnabled = (params.length <= 4) || (params[4] == 1);
|
||||
this.unchangedEnabled = (params.length <= 5) || (params[5] == 1);
|
||||
this.deletedEnabled = (params.length <= 6) || (params[6] == 1);
|
||||
this.setVariableToken(normalizeVariableToken(settings.getStringParam()));
|
||||
this.normalizeOptions();
|
||||
|
||||
if (this.variableItemId <= 0) {
|
||||
throw new WiredTriggerSaveException("wiredfurni.params.variables.validation.missing_variable");
|
||||
}
|
||||
|
||||
if (!this.hasAnyEnabledOption()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (room == null || !this.isValidDefinition(room)) {
|
||||
throw new WiredTriggerSaveException("wiredfurni.params.variables.validation.invalid_variable");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
this.variableToken,
|
||||
this.variableItemId,
|
||||
this.targetType,
|
||||
this.createdEnabled,
|
||||
this.valueChangedEnabled,
|
||||
this.increasedEnabled,
|
||||
this.decreasedEnabled,
|
||||
this.unchangedEnabled,
|
||||
this.deletedEnabled
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wiredData.startsWith("{")) {
|
||||
this.setVariableToken(normalizeVariableToken(wiredData));
|
||||
return;
|
||||
}
|
||||
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.targetType = normalizeTargetType(data.targetType);
|
||||
this.createdEnabled = data.createdEnabled;
|
||||
this.valueChangedEnabled = data.valueChangedEnabled;
|
||||
this.increasedEnabled = data.increasedEnabled;
|
||||
this.decreasedEnabled = data.decreasedEnabled;
|
||||
this.unchangedEnabled = data.unchangedEnabled;
|
||||
this.deletedEnabled = data.deletedEnabled;
|
||||
this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
|
||||
this.normalizeOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.variableToken = "";
|
||||
this.variableItemId = 0;
|
||||
this.targetType = TARGET_USER;
|
||||
this.createdEnabled = true;
|
||||
this.valueChangedEnabled = true;
|
||||
this.increasedEnabled = true;
|
||||
this.decreasedEnabled = true;
|
||||
this.unchangedEnabled = true;
|
||||
this.deletedEnabled = true;
|
||||
}
|
||||
|
||||
private void setVariableToken(String token) {
|
||||
this.variableToken = normalizeVariableToken(token);
|
||||
this.variableItemId = getCustomItemId(this.variableToken);
|
||||
}
|
||||
|
||||
private void normalizeOptions() {
|
||||
if (!this.valueChangedEnabled) {
|
||||
this.increasedEnabled = false;
|
||||
this.decreasedEnabled = false;
|
||||
this.unchangedEnabled = false;
|
||||
}
|
||||
|
||||
if (this.targetType == TARGET_ROOM) {
|
||||
this.createdEnabled = false;
|
||||
this.deletedEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAnyEnabledOption() {
|
||||
return this.createdEnabled
|
||||
|| this.deletedEnabled
|
||||
|| (this.valueChangedEnabled && (this.increasedEnabled || this.decreasedEnabled || this.unchangedEnabled));
|
||||
}
|
||||
|
||||
private boolean isValidDefinition(Room room) {
|
||||
WiredVariableDefinitionInfo definitionInfo = switch (this.targetType) {
|
||||
case TARGET_FURNI -> room.getFurniVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
case TARGET_ROOM -> room.getRoomVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
default -> room.getUserVariableManager().getDefinitionInfo(this.variableItemId);
|
||||
};
|
||||
|
||||
return definitionInfo != null;
|
||||
}
|
||||
|
||||
private static int normalizeTargetType(int value) {
|
||||
return switch (value) {
|
||||
case TARGET_FURNI, TARGET_ROOM -> value;
|
||||
default -> TARGET_USER;
|
||||
};
|
||||
}
|
||||
|
||||
private static String normalizeVariableToken(String token) {
|
||||
if (token == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String normalized = token.trim();
|
||||
if (normalized.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (normalized.startsWith(CUSTOM_TOKEN_PREFIX)) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
try {
|
||||
int itemId = Integer.parseInt(normalized);
|
||||
return (itemId > 0) ? (CUSTOM_TOKEN_PREFIX + itemId) : "";
|
||||
} catch (NumberFormatException ignored) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCustomItemId(String token) {
|
||||
if (token == null || !token.startsWith(CUSTOM_TOKEN_PREFIX)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(token.substring(CUSTOM_TOKEN_PREFIX.length()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
String variableToken;
|
||||
int variableItemId;
|
||||
int targetType;
|
||||
boolean createdEnabled;
|
||||
boolean valueChangedEnabled;
|
||||
boolean increasedEnabled;
|
||||
boolean decreasedEnabled;
|
||||
boolean unchangedEnabled;
|
||||
boolean deletedEnabled;
|
||||
|
||||
JsonData(String variableToken, int variableItemId, int targetType, boolean createdEnabled, boolean valueChangedEnabled, boolean increasedEnabled, boolean decreasedEnabled, boolean unchangedEnabled, boolean deletedEnabled) {
|
||||
this.variableToken = variableToken;
|
||||
this.variableItemId = variableItemId;
|
||||
this.targetType = targetType;
|
||||
this.createdEnabled = createdEnabled;
|
||||
this.valueChangedEnabled = valueChangedEnabled;
|
||||
this.increasedEnabled = increasedEnabled;
|
||||
this.decreasedEnabled = decreasedEnabled;
|
||||
this.unchangedEnabled = unchangedEnabled;
|
||||
this.deletedEnabled = deletedEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,11 +54,23 @@ public class ModToolManager {
|
||||
if (userId <= 0)
|
||||
return;
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT users.*, users_settings.*, permissions.rank_name, permissions.acc_hide_mail AS hide_mail, permissions.id AS rank_id FROM users INNER JOIN users_settings ON users.id = users_settings.user_id INNER JOIN permissions ON permissions.id = users.rank WHERE users.id = ? LIMIT 1")) {
|
||||
String query = Emulator.getGameEnvironment().getPermissionsManager().isNormalizedSchemaEnabled()
|
||||
? "SELECT users.*, users_settings.*, permission_ranks.rank_name, permission_ranks.id AS rank_id " +
|
||||
"FROM users " +
|
||||
"INNER JOIN users_settings ON users.id = users_settings.user_id " +
|
||||
"INNER JOIN permission_ranks ON permission_ranks.id = users.rank " +
|
||||
"WHERE users.id = ? LIMIT 1"
|
||||
: "SELECT users.*, users_settings.*, permissions.rank_name, permissions.acc_hide_mail AS hide_mail, permissions.id AS rank_id FROM users INNER JOIN users_settings ON users.id = users_settings.user_id INNER JOIN permissions ON permissions.id = users.rank WHERE users.id = ? LIMIT 1";
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(query)) {
|
||||
statement.setInt(1, userId);
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
client.sendResponse(new ModToolUserInfoComposer(set));
|
||||
boolean hideMail = Emulator.getGameEnvironment().getPermissionsManager().isNormalizedSchemaEnabled()
|
||||
? Emulator.getGameEnvironment().getPermissionsManager().getRank(set.getInt("rank_id")).hasPermission("acc_hide_mail", false)
|
||||
: set.getBoolean("hide_mail");
|
||||
|
||||
client.sendResponse(new ModToolUserInfoComposer(set, hideMail));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
|
||||
+156
-8
@@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -20,6 +21,7 @@ public class PermissionsManager {
|
||||
private final TIntObjectHashMap<Rank> ranks;
|
||||
private final TIntIntHashMap enables;
|
||||
private final THashMap<String, List<Rank>> badges;
|
||||
private volatile boolean normalizedSchemaEnabled;
|
||||
|
||||
public PermissionsManager() {
|
||||
long millis = System.currentTimeMillis();
|
||||
@@ -40,7 +42,30 @@ public class PermissionsManager {
|
||||
private void loadPermissions() {
|
||||
this.badges.clear();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM permissions ORDER BY id ASC")) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
if (this.hasNormalizedPermissionsSchema(connection)) {
|
||||
try {
|
||||
if (this.loadPermissionsNormalized(connection)) {
|
||||
this.normalizedSchemaEnabled = true;
|
||||
LOGGER.info("Permissions Manager -> Using normalized permissions schema.");
|
||||
return;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.warn("Permissions Manager -> Failed to load normalized permissions schema, falling back to legacy permissions table.", e);
|
||||
}
|
||||
}
|
||||
|
||||
this.normalizedSchemaEnabled = false;
|
||||
this.badges.clear();
|
||||
LOGGER.info("Permissions Manager -> Using legacy permissions schema.");
|
||||
this.loadPermissionsLegacy(connection);
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadPermissionsLegacy(Connection connection) throws SQLException {
|
||||
try (Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM permissions ORDER BY id ASC")) {
|
||||
while (set.next()) {
|
||||
Rank rank = null;
|
||||
if (!this.ranks.containsKey(set.getInt("id"))) {
|
||||
@@ -51,16 +76,135 @@ public class PermissionsManager {
|
||||
rank.load(set);
|
||||
}
|
||||
|
||||
if (rank != null && !rank.getBadge().isEmpty()) {
|
||||
if (!this.badges.containsKey(rank.getBadge())) {
|
||||
this.badges.put(rank.getBadge(), new ArrayList<Rank>());
|
||||
}
|
||||
this.addBadgeMapping(rank);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.badges.get(rank.getBadge()).add(rank);
|
||||
private boolean loadPermissionsNormalized(Connection connection) throws SQLException {
|
||||
boolean hasRanks = false;
|
||||
List<Rank> loadedRanks = new ArrayList<>();
|
||||
|
||||
try (Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM permission_ranks ORDER BY id ASC")) {
|
||||
while (set.next()) {
|
||||
hasRanks = true;
|
||||
|
||||
Rank rank = this.ranks.get(set.getInt("id"));
|
||||
|
||||
if (rank == null) {
|
||||
rank = new Rank(set.getInt("id"));
|
||||
this.ranks.put(set.getInt("id"), rank);
|
||||
}
|
||||
|
||||
rank.loadNormalizedMetadata(set);
|
||||
this.addBadgeMapping(rank);
|
||||
loadedRanks.add(rank);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasRanks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.ensureNormalizedRankColumns(connection, loadedRanks);
|
||||
|
||||
boolean hasDefinitions = false;
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM permission_definitions ORDER BY permission_key ASC");
|
||||
ResultSet set = statement.executeQuery()) {
|
||||
ResultSetMetaData meta = set.getMetaData();
|
||||
Set<String> availableColumns = new HashSet<>();
|
||||
|
||||
for (int i = 1; i <= meta.getColumnCount(); i++) {
|
||||
availableColumns.add(meta.getColumnName(i).toLowerCase());
|
||||
}
|
||||
|
||||
for (Rank rank : loadedRanks) {
|
||||
if (!availableColumns.contains(("rank_" + rank.getId()).toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
|
||||
while (set.next()) {
|
||||
hasDefinitions = true;
|
||||
String permissionKey = set.getString("permission_key");
|
||||
|
||||
for (Rank rank : loadedRanks) {
|
||||
String rankColumn = "rank_" + rank.getId();
|
||||
|
||||
if (!availableColumns.contains(rankColumn.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rank.setPermission(permissionKey, PermissionSetting.fromString(Integer.toString(set.getInt(rankColumn))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasDefinitions;
|
||||
}
|
||||
|
||||
private void ensureNormalizedRankColumns(Connection connection, List<Rank> loadedRanks) throws SQLException {
|
||||
Set<String> availableColumns = new HashSet<>();
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement("SELECT column_name FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'permission_definitions'");
|
||||
ResultSet set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
availableColumns.add(set.getString("column_name").toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
for (Rank rank : loadedRanks) {
|
||||
String rankColumn = "rank_" + rank.getId();
|
||||
|
||||
if (availableColumns.contains(rankColumn.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute("ALTER TABLE permission_definitions ADD COLUMN `" + rankColumn + "` tinyint(3) unsigned NOT NULL DEFAULT 0");
|
||||
}
|
||||
|
||||
availableColumns.add(rankColumn.toLowerCase());
|
||||
LOGGER.info("Permissions Manager -> Added missing normalized permission column {}.", rankColumn);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasNormalizedPermissionsSchema(Connection connection) throws SQLException {
|
||||
if (!this.tableExists(connection, "permission_ranks") || !this.tableExists(connection, "permission_definitions")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.tableHasRows(connection, "permission_ranks")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.tableHasRows(connection, "permission_definitions");
|
||||
}
|
||||
|
||||
private boolean tableExists(Connection connection, String tableName) throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?")) {
|
||||
statement.setString(1, tableName);
|
||||
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
return set.next() && set.getInt(1) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tableHasRows(Connection connection, String tableName) throws SQLException {
|
||||
try (Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT COUNT(*) FROM " + tableName)) {
|
||||
return set.next() && set.getInt(1) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void addBadgeMapping(Rank rank) {
|
||||
if (rank != null && !rank.getBadge().isEmpty()) {
|
||||
if (!this.badges.containsKey(rank.getBadge())) {
|
||||
this.badges.put(rank.getBadge(), new ArrayList<Rank>());
|
||||
}
|
||||
|
||||
this.badges.get(rank.getBadge()).add(rank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,4 +283,8 @@ public class PermissionsManager {
|
||||
public List<Rank> getAllRanks() {
|
||||
return new ArrayList<>(this.ranks.valueCollection());
|
||||
}
|
||||
|
||||
public boolean isNormalizedSchemaEnabled() {
|
||||
return this.normalizedSchemaEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,32 +35,29 @@ public class Rank {
|
||||
private int gotwTimerAmount;
|
||||
|
||||
public Rank(ResultSet set) throws SQLException {
|
||||
this(set.getInt("id"));
|
||||
this.load(set);
|
||||
}
|
||||
|
||||
public Rank(int id) {
|
||||
this.permissions = new THashMap<>();
|
||||
this.variables = new THashMap<>();
|
||||
this.id = set.getInt("id");
|
||||
this.level = set.getInt("level");
|
||||
this.id = id;
|
||||
this.level = 1;
|
||||
this.diamondsTimerAmount = 1;
|
||||
this.creditsTimerAmount = 1;
|
||||
this.pixelsTimerAmount = 1;
|
||||
this.gotwTimerAmount = 1;
|
||||
|
||||
this.load(set);
|
||||
}
|
||||
|
||||
public void load(ResultSet set) throws SQLException {
|
||||
this.permissions.clear();
|
||||
this.variables.clear();
|
||||
|
||||
this.loadMetadata(set);
|
||||
|
||||
ResultSetMetaData meta = set.getMetaData();
|
||||
this.name = set.getString("rank_name");
|
||||
this.badge = set.getString("badge");
|
||||
this.roomEffect = set.getInt("room_effect");
|
||||
this.logCommands = set.getString("log_commands").equals("1");
|
||||
this.prefix = set.getString("prefix");
|
||||
this.prefixColor = set.getString("prefix_color");
|
||||
this.level = set.getInt("level");
|
||||
this.diamondsTimerAmount = set.getInt("auto_points_amount");
|
||||
this.creditsTimerAmount = set.getInt("auto_credits_amount");
|
||||
this.pixelsTimerAmount = set.getInt("auto_pixels_amount");
|
||||
this.gotwTimerAmount = set.getInt("auto_gotw_amount");
|
||||
this.hasPrefix = !this.prefix.isEmpty();
|
||||
|
||||
for (int i = 1; i < meta.getColumnCount() + 1; i++) {
|
||||
String columnName = meta.getColumnName(i);
|
||||
if (columnName.startsWith("cmd_") || columnName.startsWith("acc_")) {
|
||||
@@ -71,6 +68,51 @@ public class Rank {
|
||||
}
|
||||
}
|
||||
|
||||
public void loadNormalizedMetadata(ResultSet set) throws SQLException {
|
||||
this.permissions.clear();
|
||||
this.variables.clear();
|
||||
this.loadMetadata(set);
|
||||
this.storeMetadataVariables();
|
||||
}
|
||||
|
||||
public void setPermission(String key, PermissionSetting setting) {
|
||||
this.permissions.put(key, new Permission(key, setting));
|
||||
}
|
||||
|
||||
private void loadMetadata(ResultSet set) throws SQLException {
|
||||
this.name = this.safeString(set.getString("rank_name"));
|
||||
this.badge = this.safeString(set.getString("badge"));
|
||||
this.roomEffect = set.getInt("room_effect");
|
||||
this.logCommands = "1".equals(this.safeString(set.getString("log_commands")));
|
||||
this.prefix = this.safeString(set.getString("prefix"));
|
||||
this.prefixColor = this.safeString(set.getString("prefix_color"));
|
||||
this.level = set.getInt("level");
|
||||
this.diamondsTimerAmount = set.getInt("auto_points_amount");
|
||||
this.creditsTimerAmount = set.getInt("auto_credits_amount");
|
||||
this.pixelsTimerAmount = set.getInt("auto_pixels_amount");
|
||||
this.gotwTimerAmount = set.getInt("auto_gotw_amount");
|
||||
this.hasPrefix = !this.prefix.isEmpty();
|
||||
}
|
||||
|
||||
private void storeMetadataVariables() {
|
||||
this.variables.put("id", Integer.toString(this.id));
|
||||
this.variables.put("rank_name", this.name);
|
||||
this.variables.put("badge", this.badge);
|
||||
this.variables.put("room_effect", Integer.toString(this.roomEffect));
|
||||
this.variables.put("log_commands", this.logCommands ? "1" : "0");
|
||||
this.variables.put("prefix", this.prefix);
|
||||
this.variables.put("prefix_color", this.prefixColor);
|
||||
this.variables.put("level", Integer.toString(this.level));
|
||||
this.variables.put("auto_points_amount", Integer.toString(this.diamondsTimerAmount));
|
||||
this.variables.put("auto_credits_amount", Integer.toString(this.creditsTimerAmount));
|
||||
this.variables.put("auto_pixels_amount", Integer.toString(this.pixelsTimerAmount));
|
||||
this.variables.put("auto_gotw_amount", Integer.toString(this.gotwTimerAmount));
|
||||
}
|
||||
|
||||
private String safeString(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
public boolean hasPermission(String key, boolean isRoomOwner) {
|
||||
if (this.permissions.containsKey(key)) {
|
||||
Permission permission = this.permissions.get(key);
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.games.Game;
|
||||
import com.eu.habbo.habbohotel.guilds.Guild;
|
||||
import com.eu.habbo.habbohotel.guilds.GuildMember;
|
||||
import com.eu.habbo.habbohotel.guilds.GuildRank;
|
||||
import com.eu.habbo.habbohotel.items.FurnitureType;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.*;
|
||||
@@ -30,6 +31,7 @@ import com.eu.habbo.messages.outgoing.rooms.UpdateStackHeightComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.*;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserIgnoredComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||
import com.eu.habbo.messages.outgoing.wired.WiredRoomSettingsDataComposer;
|
||||
import com.eu.habbo.plugin.Event;
|
||||
import com.eu.habbo.plugin.events.furniture.FurniturePickedUpEvent;
|
||||
import com.eu.habbo.plugin.events.rooms.RoomLoadedEvent;
|
||||
@@ -75,6 +77,9 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
private RoomRollerManager rollerManager;
|
||||
private RoomMessagingManager messagingManager;
|
||||
private RoomCycleManager cycleManager;
|
||||
private RoomUserVariableManager userVariableManager;
|
||||
private RoomFurniVariableManager furniVariableManager;
|
||||
private RoomVariableManager roomVariableManager;
|
||||
|
||||
public static final Comparator<Room> SORT_SCORE = (o1, o2) -> o2.getScore() - o1.getScore();
|
||||
public static final Comparator<Room> SORT_ID = (o1, o2) -> o2.getId() - o1.getId();
|
||||
@@ -92,6 +97,14 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
public static int ROLLERS_MAXIMUM_ROLL_AVATARS = 1;
|
||||
public static boolean MUTEAREA_CAN_WHISPER = false;
|
||||
public static double MAXIMUM_FURNI_HEIGHT = 40d;
|
||||
public static final int WIRED_ACCESS_EVERYONE = 1;
|
||||
public static final int WIRED_ACCESS_USERS_WITH_RIGHTS = 2;
|
||||
public static final int WIRED_ACCESS_GROUP_MEMBERS = 4;
|
||||
public static final int WIRED_ACCESS_GROUP_ADMINS = 8;
|
||||
public static final int WIRED_ACCESS_ALLOWED_INSPECT_MASK = WIRED_ACCESS_EVERYONE | WIRED_ACCESS_USERS_WITH_RIGHTS | WIRED_ACCESS_GROUP_MEMBERS | WIRED_ACCESS_GROUP_ADMINS;
|
||||
public static final int WIRED_ACCESS_ALLOWED_MODIFY_MASK = WIRED_ACCESS_USERS_WITH_RIGHTS | WIRED_ACCESS_GROUP_MEMBERS | WIRED_ACCESS_GROUP_ADMINS;
|
||||
public static final int WIRED_ACCESS_DEFAULT_INSPECT_MASK = 0;
|
||||
public static final int WIRED_ACCESS_DEFAULT_MODIFY_MASK = 0;
|
||||
|
||||
static {
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
@@ -175,6 +188,10 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
private volatile boolean muted;
|
||||
private RoomSpecialTypes roomSpecialTypes;
|
||||
private TraxManager traxManager;
|
||||
private final Object wiredSettingsLock = new Object();
|
||||
private volatile boolean wiredSettingsLoaded;
|
||||
private int wiredInspectMask = WIRED_ACCESS_DEFAULT_INSPECT_MASK;
|
||||
private int wiredModifyMask = WIRED_ACCESS_DEFAULT_MODIFY_MASK;
|
||||
|
||||
public final THashMap<String, Object> cache;
|
||||
|
||||
@@ -269,6 +286,9 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
this.rollerManager = new RoomRollerManager(this);
|
||||
this.messagingManager = new RoomMessagingManager(this);
|
||||
this.cycleManager = new RoomCycleManager(this);
|
||||
this.userVariableManager = new RoomUserVariableManager(this);
|
||||
this.furniVariableManager = new RoomFurniVariableManager(this);
|
||||
this.roomVariableManager = new RoomVariableManager(this);
|
||||
}
|
||||
|
||||
// ==================== MANAGER GETTERS ====================
|
||||
@@ -350,6 +370,18 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
return this.cycleManager;
|
||||
}
|
||||
|
||||
public RoomUserVariableManager getUserVariableManager() {
|
||||
return this.userVariableManager;
|
||||
}
|
||||
|
||||
public RoomFurniVariableManager getFurniVariableManager() {
|
||||
return this.furniVariableManager;
|
||||
}
|
||||
|
||||
public RoomVariableManager getRoomVariableManager() {
|
||||
return this.roomVariableManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the roller manager for this room.
|
||||
*/
|
||||
@@ -924,13 +956,13 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
|
||||
this.itemManager.saveAllPendingItems();
|
||||
|
||||
// Unregister all wired tickables for this room from the tick service
|
||||
com.eu.habbo.habbohotel.wired.core.WiredManager.unregisterRoomTickables(this);
|
||||
|
||||
if (this.roomSpecialTypes != null) {
|
||||
this.roomSpecialTypes.dispose();
|
||||
}
|
||||
|
||||
// Unregister all wired tickables for this room from the tick service
|
||||
com.eu.habbo.habbohotel.wired.core.WiredManager.unregisterRoomTickables(this);
|
||||
|
||||
// Clear wired engine caches for this room
|
||||
if (com.eu.habbo.habbohotel.wired.core.WiredManager.getStackIndex() != null) {
|
||||
com.eu.habbo.habbohotel.wired.core.WiredManager.getStackIndex().invalidateAll(this);
|
||||
@@ -938,6 +970,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
if (com.eu.habbo.habbohotel.wired.core.WiredManager.getEngine() != null) {
|
||||
com.eu.habbo.habbohotel.wired.core.WiredManager.getEngine().clearRoomRecursionDepth(this.id);
|
||||
com.eu.habbo.habbohotel.wired.core.WiredManager.getEngine().clearRoomRateLimiters(this.id);
|
||||
com.eu.habbo.habbohotel.wired.core.WiredManager.getEngine().clearRoomBan(this.id);
|
||||
com.eu.habbo.habbohotel.wired.core.WiredManager.getEngine().clearRoomDiagnostics(this.id);
|
||||
}
|
||||
|
||||
this.itemManager.clear();
|
||||
@@ -2127,20 +2161,108 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
return this.rightsManager.hasRights(habbo);
|
||||
}
|
||||
|
||||
public boolean hasExplicitRights(Habbo habbo) {
|
||||
return habbo != null && this.rights.contains(habbo.getHabboInfo().getId());
|
||||
}
|
||||
|
||||
public int getWiredInspectMask() {
|
||||
this.ensureWiredSettingsLoaded();
|
||||
return this.wiredInspectMask;
|
||||
}
|
||||
|
||||
public int getWiredModifyMask() {
|
||||
this.ensureWiredSettingsLoaded();
|
||||
return this.wiredModifyMask;
|
||||
}
|
||||
|
||||
public boolean canInspectWired(Habbo habbo) {
|
||||
if (habbo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.canManageWiredSettings(habbo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.ensureWiredSettingsLoaded();
|
||||
return this.matchesWiredAccessMask(habbo, this.wiredInspectMask, true);
|
||||
}
|
||||
|
||||
public boolean canModifyWired(Habbo habbo) {
|
||||
if (habbo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.canManageWiredSettings(habbo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.ensureWiredSettingsLoaded();
|
||||
return this.matchesWiredAccessMask(habbo, this.wiredModifyMask, false);
|
||||
}
|
||||
|
||||
public boolean canManageWiredSettings(Habbo habbo) {
|
||||
return habbo != null && this.isOwner(habbo);
|
||||
}
|
||||
|
||||
public boolean saveWiredSettings(int inspectMask, int modifyMask) {
|
||||
int sanitizedInspectMask = sanitizeWiredInspectMask(inspectMask);
|
||||
int sanitizedModifyMask = sanitizeWiredModifyMask(modifyMask);
|
||||
sanitizedInspectMask |= sanitizedModifyMask;
|
||||
|
||||
synchronized (this.wiredSettingsLock) {
|
||||
int previousInspectMask = this.wiredInspectMask;
|
||||
int previousModifyMask = this.wiredModifyMask;
|
||||
this.wiredInspectMask = sanitizedInspectMask;
|
||||
this.wiredModifyMask = sanitizedModifyMask;
|
||||
this.wiredSettingsLoaded = true;
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"INSERT INTO room_wired_settings (room_id, inspect_mask, modify_mask) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE inspect_mask = VALUES(inspect_mask), modify_mask = VALUES(modify_mask)")) {
|
||||
statement.setInt(1, this.id);
|
||||
statement.setInt(2, sanitizedInspectMask);
|
||||
statement.setInt(3, sanitizedModifyMask);
|
||||
statement.executeUpdate();
|
||||
this.pushWiredSettingsToCurrentHabbos();
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
this.wiredInspectMask = previousInspectMask;
|
||||
this.wiredModifyMask = previousModifyMask;
|
||||
LOGGER.error("Caught SQL exception while saving wired room settings", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void giveRights(Habbo habbo) {
|
||||
this.rightsManager.giveRights(habbo);
|
||||
if (habbo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.giveRights(habbo.getHabboInfo().getId());
|
||||
}
|
||||
|
||||
public void giveRights(int userId) {
|
||||
this.rightsManager.giveRights(userId);
|
||||
|
||||
if (!this.rights.contains(userId)) {
|
||||
this.rights.add(userId);
|
||||
}
|
||||
|
||||
this.pushWiredSettingsToCurrentHabbos();
|
||||
}
|
||||
|
||||
public void removeRights(int userId) {
|
||||
this.rightsManager.removeRights(userId);
|
||||
this.rights.remove(userId);
|
||||
this.pushWiredSettingsToCurrentHabbos();
|
||||
}
|
||||
|
||||
public void removeAllRights() {
|
||||
this.rightsManager.removeAllRights();
|
||||
this.rights.clear();
|
||||
this.pushWiredSettingsToCurrentHabbos();
|
||||
}
|
||||
|
||||
void refreshRightsInRoom() {
|
||||
@@ -2167,6 +2289,111 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
return this.bannedHabbos;
|
||||
}
|
||||
|
||||
private void ensureWiredSettingsLoaded() {
|
||||
if (this.wiredSettingsLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this.wiredSettingsLock) {
|
||||
if (this.wiredSettingsLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.wiredInspectMask = WIRED_ACCESS_DEFAULT_INSPECT_MASK;
|
||||
this.wiredModifyMask = WIRED_ACCESS_DEFAULT_MODIFY_MASK;
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT inspect_mask, modify_mask FROM room_wired_settings WHERE room_id = ? LIMIT 1")) {
|
||||
statement.setInt(1, this.id);
|
||||
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
if (set.next()) {
|
||||
this.wiredInspectMask = sanitizeWiredInspectMask(set.getInt("inspect_mask"));
|
||||
this.wiredModifyMask = sanitizeWiredModifyMask(set.getInt("modify_mask"));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception while loading wired room settings", e);
|
||||
}
|
||||
|
||||
this.wiredSettingsLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean matchesWiredAccessMask(Habbo habbo, int mask, boolean allowEveryone) {
|
||||
if (habbo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allowEveryone && hasWiredAccess(mask, WIRED_ACCESS_EVERYONE)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasWiredAccess(mask, WIRED_ACCESS_USERS_WITH_RIGHTS) && this.hasExplicitRights(habbo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasWiredAccess(mask, WIRED_ACCESS_GROUP_ADMINS) && this.isRoomGroupAdmin(habbo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return hasWiredAccess(mask, WIRED_ACCESS_GROUP_MEMBERS) && this.isRoomGroupMember(habbo);
|
||||
}
|
||||
|
||||
private boolean isRoomGroupMember(Habbo habbo) {
|
||||
return habbo != null && this.guild > 0 && habbo.getHabboStats().hasGuild(this.guild);
|
||||
}
|
||||
|
||||
private boolean isRoomGroupAdmin(Habbo habbo) {
|
||||
if (!this.isRoomGroupMember(habbo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(this.guild, habbo.getHabboInfo().getId());
|
||||
|
||||
if (member == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GuildRank rank = member.getRank();
|
||||
return rank == GuildRank.OWNER || rank == GuildRank.ADMIN;
|
||||
}
|
||||
|
||||
private static boolean hasWiredAccess(int mask, int permissionMask) {
|
||||
return (mask & permissionMask) != 0;
|
||||
}
|
||||
|
||||
private static int sanitizeWiredInspectMask(int mask) {
|
||||
int sanitizedMask = mask & WIRED_ACCESS_ALLOWED_INSPECT_MASK;
|
||||
|
||||
if (hasWiredAccess(sanitizedMask, WIRED_ACCESS_GROUP_MEMBERS)) {
|
||||
sanitizedMask |= WIRED_ACCESS_GROUP_ADMINS;
|
||||
}
|
||||
|
||||
return sanitizedMask;
|
||||
}
|
||||
|
||||
private static int sanitizeWiredModifyMask(int mask) {
|
||||
int sanitizedMask = mask & WIRED_ACCESS_ALLOWED_MODIFY_MASK;
|
||||
|
||||
if (hasWiredAccess(sanitizedMask, WIRED_ACCESS_GROUP_MEMBERS)) {
|
||||
sanitizedMask |= WIRED_ACCESS_GROUP_ADMINS;
|
||||
}
|
||||
|
||||
return sanitizedMask;
|
||||
}
|
||||
|
||||
private void pushWiredSettingsToCurrentHabbos() {
|
||||
for (Habbo currentHabbo : this.getCurrentHabbos().values()) {
|
||||
if (currentHabbo == null || currentHabbo.getClient() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
currentHabbo.getClient().sendResponse(new WiredRoomSettingsDataComposer(this, currentHabbo));
|
||||
}
|
||||
}
|
||||
|
||||
public void addRoomBan(RoomBan roomBan) {
|
||||
this.rightsManager.addRoomBan(roomBan);
|
||||
}
|
||||
|
||||
@@ -328,7 +328,9 @@ public class RoomChatManager {
|
||||
suppressSaysOutput = WiredManager.shouldSuppressUserSaysOutput(
|
||||
habbo.getHabboInfo().getCurrentRoom(),
|
||||
habbo.getRoomUnit(),
|
||||
wiredSayMessage);
|
||||
wiredSayMessage,
|
||||
chatType.ordinal(),
|
||||
roomChatMessage.getBubble() != null ? roomChatMessage.getBubble().getType() : -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,7 +409,12 @@ public class RoomChatManager {
|
||||
}
|
||||
|
||||
if (chatType != RoomChatType.WHISPER && !ignoreWired && !roomChatMessage.isCommand) {
|
||||
WiredManager.triggerUserSays(habbo.getHabboInfo().getCurrentRoom(), habbo.getRoomUnit(), wiredSayMessage);
|
||||
WiredManager.triggerUserSays(
|
||||
habbo.getHabboInfo().getCurrentRoom(),
|
||||
habbo.getRoomUnit(),
|
||||
wiredSayMessage,
|
||||
chatType.ordinal(),
|
||||
roomChatMessage.getBubble() != null ? roomChatMessage.getBubble().getType() : -1);
|
||||
}
|
||||
|
||||
// Notify bots and talking furniture
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,14 @@ import com.eu.habbo.habbohotel.items.interactions.games.tag.InteractionTagPole;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.*;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectSendSignal;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFurniVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRoomVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUserVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableEcho;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableReference;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableTextConnector;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraContextVariable;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredContextVariableSupport;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerReceiveSignal;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
@@ -773,6 +781,8 @@ public class RoomItemManager {
|
||||
if (specialTypes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.room.getFurniVariableManager().removeAssignmentsForFurni(item.getId());
|
||||
|
||||
boolean isWiredItem = false;
|
||||
|
||||
@@ -785,21 +795,53 @@ public class RoomItemManager {
|
||||
specialTypes.removeCycleTask((ICycleable) item);
|
||||
}
|
||||
|
||||
if (item instanceof InteractionBattleBanzaiTeleporter) {
|
||||
specialTypes.removeBanzaiTeleporter((InteractionBattleBanzaiTeleporter) item);
|
||||
} else if (item instanceof InteractionWiredTrigger) {
|
||||
specialTypes.removeTrigger((InteractionWiredTrigger) item);
|
||||
isWiredItem = true;
|
||||
} else if (item instanceof InteractionWiredEffect) {
|
||||
specialTypes.removeEffect((InteractionWiredEffect) item);
|
||||
isWiredItem = true;
|
||||
} else if (item instanceof InteractionWiredCondition) {
|
||||
specialTypes.removeCondition((InteractionWiredCondition) item);
|
||||
isWiredItem = true;
|
||||
} else if (item instanceof InteractionWiredExtra) {
|
||||
specialTypes.removeExtra((InteractionWiredExtra) item);
|
||||
isWiredItem = true;
|
||||
} else if (item instanceof InteractionRoller) {
|
||||
if (item instanceof InteractionBattleBanzaiTeleporter) {
|
||||
specialTypes.removeBanzaiTeleporter((InteractionBattleBanzaiTeleporter) item);
|
||||
} else if (item instanceof InteractionWiredTrigger) {
|
||||
specialTypes.removeTrigger((InteractionWiredTrigger) item);
|
||||
isWiredItem = true;
|
||||
} else if (item instanceof InteractionWiredEffect) {
|
||||
specialTypes.removeEffect((InteractionWiredEffect) item);
|
||||
isWiredItem = true;
|
||||
} else if (item instanceof InteractionWiredCondition) {
|
||||
specialTypes.removeCondition((InteractionWiredCondition) item);
|
||||
isWiredItem = true;
|
||||
} else if (item instanceof InteractionWiredExtra) {
|
||||
boolean removedContextDefinition = false;
|
||||
boolean removedVariableTextConnector = false;
|
||||
if (item instanceof WiredExtraUserVariable) {
|
||||
this.room.getUserVariableManager().removeDefinition(item.getId());
|
||||
} else if (item instanceof WiredExtraFurniVariable) {
|
||||
this.room.getFurniVariableManager().removeDefinition(item.getId());
|
||||
} else if (item instanceof WiredExtraRoomVariable) {
|
||||
this.room.getRoomVariableManager().removeDefinition(item.getId());
|
||||
} else if (item instanceof WiredExtraContextVariable) {
|
||||
removedContextDefinition = true;
|
||||
} else if (item instanceof WiredExtraVariableTextConnector) {
|
||||
removedVariableTextConnector = true;
|
||||
} else if (item instanceof WiredExtraVariableReference) {
|
||||
if (((WiredExtraVariableReference) item).isRoomReference()) {
|
||||
this.room.getRoomVariableManager().removeDefinition(item.getId());
|
||||
} else {
|
||||
this.room.getUserVariableManager().removeDefinition(item.getId());
|
||||
}
|
||||
} else if (item instanceof WiredExtraVariableEcho) {
|
||||
WiredExtraVariableEcho echo = (WiredExtraVariableEcho) item;
|
||||
|
||||
if (echo.isRoomEcho()) {
|
||||
this.room.getRoomVariableManager().removeDefinition(item.getId());
|
||||
} else if (echo.isFurniEcho()) {
|
||||
this.room.getFurniVariableManager().removeDefinition(item.getId());
|
||||
} else {
|
||||
this.room.getUserVariableManager().removeDefinition(item.getId());
|
||||
}
|
||||
}
|
||||
specialTypes.removeExtra((InteractionWiredExtra) item);
|
||||
if (removedContextDefinition || removedVariableTextConnector) {
|
||||
WiredContextVariableSupport.broadcastDefinitions(this.room);
|
||||
}
|
||||
isWiredItem = true;
|
||||
} else if (item instanceof InteractionRoller) {
|
||||
specialTypes.removeRoller((InteractionRoller) item);
|
||||
} else if (item instanceof InteractionGameScoreboard) {
|
||||
specialTypes.removeScoreboard((InteractionGameScoreboard) item);
|
||||
|
||||
@@ -780,6 +780,7 @@ public class RoomManager {
|
||||
|
||||
habbo.getRoomUnit().setInvisible(false);
|
||||
room.addHabbo(habbo);
|
||||
room.getUserVariableManager().restorePermanentAssignments(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()));
|
||||
|
||||
@@ -232,12 +232,14 @@ public class RoomUnitManager {
|
||||
habbo.getRoomUnit().getCurrentLocation().removeUnit(habbo.getRoomUnit());
|
||||
}
|
||||
|
||||
synchronized (this.room.roomUnitLock) {
|
||||
this.currentHabbos.remove(habbo.getHabboInfo().getId());
|
||||
}
|
||||
synchronized (this.room.roomUnitLock) {
|
||||
this.currentHabbos.remove(habbo.getHabboInfo().getId());
|
||||
}
|
||||
|
||||
if (sendRemovePacket && habbo.getRoomUnit() != null && !habbo.getRoomUnit().isTeleporting) {
|
||||
this.room.sendComposer(new RoomUserRemoveComposer(habbo.getRoomUnit()).compose());
|
||||
this.room.getUserVariableManager().clearAssignmentsForUser(habbo.getHabboInfo().getId());
|
||||
|
||||
if (sendRemovePacket && habbo.getRoomUnit() != null && !habbo.getRoomUnit().isTeleporting) {
|
||||
this.room.sendComposer(new RoomUserRemoveComposer(habbo.getRoomUnit()).compose());
|
||||
}
|
||||
|
||||
if (habbo.getRoomUnit().getCurrentLocation() != null) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,827 @@
|
||||
package com.eu.habbo.habbohotel.rooms;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRoomVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableEcho;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableReference;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredVariableReferenceSupport;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredVariableLevelSystemSupport;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredVariableTextConnectorSupport;
|
||||
import com.eu.habbo.messages.outgoing.wired.WiredUserVariablesDataComposer;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class RoomVariableManager {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RoomVariableManager.class);
|
||||
|
||||
private final Room room;
|
||||
private final ConcurrentHashMap<Integer, VariableAssignment> activeAssignmentsByDefinitionId;
|
||||
private volatile boolean persistentValuesLoaded;
|
||||
|
||||
public RoomVariableManager(Room room) {
|
||||
this.room = room;
|
||||
this.activeAssignmentsByDefinitionId = new ConcurrentHashMap<>();
|
||||
this.persistentValuesLoaded = false;
|
||||
}
|
||||
|
||||
public void ensurePersistentValuesLoaded() {
|
||||
if (this.persistentValuesLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (this.persistentValuesLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Integer> staleDefinitionIds = new ArrayList<>();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT variable_item_id, value, created_at, updated_at FROM room_wired_variables WHERE room_id = ?")) {
|
||||
statement.setInt(1, this.room.getId());
|
||||
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
int definitionItemId = set.getInt("variable_item_id");
|
||||
WiredExtraRoomVariable definition = this.getDefinition(definitionItemId);
|
||||
|
||||
if (definition == null || !definition.isPermanentAvailability()) {
|
||||
staleDefinitionIds.add(definitionItemId);
|
||||
continue;
|
||||
}
|
||||
|
||||
int updatedAt = normalizeTimestamp(set.getInt("updated_at"), 0);
|
||||
|
||||
this.activeAssignmentsByDefinitionId.put(definitionItemId, new VariableAssignment(set.getInt("value"), 0, updatedAt));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to restore wired room variables for room {}", this.room.getId(), e);
|
||||
}
|
||||
|
||||
for (Integer definitionItemId : staleDefinitionIds) {
|
||||
this.deletePersistentAssignment(definitionItemId);
|
||||
}
|
||||
|
||||
this.persistentValuesLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCurrentValue(int definitionItemId) {
|
||||
this.ensurePersistentValuesLoaded();
|
||||
|
||||
WiredVariableLevelSystemSupport.DerivedDefinition derivedDefinition = WiredVariableLevelSystemSupport.resolveDerivedDefinition(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, definitionItemId);
|
||||
if (derivedDefinition != null) {
|
||||
Integer baseValue = this.getRawValue(derivedDefinition.getBaseDefinitionItemId());
|
||||
Integer derivedValue = WiredVariableLevelSystemSupport.getDerivedValue(derivedDefinition.getLevelSystem(), derivedDefinition.getSubvariableType(), baseValue);
|
||||
return (derivedValue != null) ? derivedValue : 0;
|
||||
}
|
||||
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
if (extra instanceof WiredExtraVariableEcho) {
|
||||
return ((WiredExtraVariableEcho) extra).getCurrentValue(this.room, this.room.getId());
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference) {
|
||||
WiredVariableReferenceSupport.SharedRoomAssignment assignment = WiredVariableReferenceSupport.getSharedRoomAssignment((WiredExtraVariableReference) extra);
|
||||
return assignment != null ? assignment.getValue() : 0;
|
||||
}
|
||||
|
||||
VariableAssignment assignment = this.activeAssignmentsByDefinitionId.get(definitionItemId);
|
||||
|
||||
return (assignment != null) ? assignment.getValue() : 0;
|
||||
}
|
||||
|
||||
public int getCreatedAt(int definitionItemId) {
|
||||
this.ensurePersistentValuesLoaded();
|
||||
|
||||
WiredVariableLevelSystemSupport.DerivedDefinition derivedDefinition = WiredVariableLevelSystemSupport.resolveDerivedDefinition(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, definitionItemId);
|
||||
if (derivedDefinition != null) {
|
||||
VariableAssignment assignment = this.getRawAssignment(derivedDefinition.getBaseDefinitionItemId());
|
||||
return (assignment != null) ? assignment.getCreatedAt() : 0;
|
||||
}
|
||||
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
if (extra instanceof WiredExtraVariableEcho) {
|
||||
return ((WiredExtraVariableEcho) extra).getCreatedAt(this.room, this.room.getId());
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VariableAssignment assignment = this.activeAssignmentsByDefinitionId.get(definitionItemId);
|
||||
return (assignment != null) ? assignment.getCreatedAt() : 0;
|
||||
}
|
||||
|
||||
public int getUpdatedAt(int definitionItemId) {
|
||||
this.ensurePersistentValuesLoaded();
|
||||
|
||||
WiredVariableLevelSystemSupport.DerivedDefinition derivedDefinition = WiredVariableLevelSystemSupport.resolveDerivedDefinition(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, definitionItemId);
|
||||
if (derivedDefinition != null) {
|
||||
VariableAssignment assignment = this.getRawAssignment(derivedDefinition.getBaseDefinitionItemId());
|
||||
return (assignment != null) ? assignment.getUpdatedAt() : 0;
|
||||
}
|
||||
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
if (extra instanceof WiredExtraVariableEcho) {
|
||||
return ((WiredExtraVariableEcho) extra).getUpdatedAt(this.room, this.room.getId());
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference) {
|
||||
WiredVariableReferenceSupport.SharedRoomAssignment assignment = WiredVariableReferenceSupport.getSharedRoomAssignment((WiredExtraVariableReference) extra);
|
||||
return assignment != null ? assignment.getUpdatedAt() : 0;
|
||||
}
|
||||
|
||||
VariableAssignment assignment = this.activeAssignmentsByDefinitionId.get(definitionItemId);
|
||||
return (assignment != null) ? assignment.getUpdatedAt() : 0;
|
||||
}
|
||||
|
||||
public boolean hasVariable(int definitionItemId) {
|
||||
if (definitionItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WiredVariableLevelSystemSupport.DerivedDefinition derivedDefinition = WiredVariableLevelSystemSupport.resolveDerivedDefinition(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, definitionItemId);
|
||||
if (derivedDefinition != null) {
|
||||
return this.getRawAssignment(derivedDefinition.getBaseDefinitionItemId()) != null;
|
||||
}
|
||||
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
if (extra instanceof WiredExtraVariableEcho) {
|
||||
return ((WiredExtraVariableEcho) extra).hasVariable(this.room, this.room.getId());
|
||||
}
|
||||
|
||||
return this.getDefinitionInfo(definitionItemId) != null;
|
||||
}
|
||||
|
||||
public boolean updateVariableValue(int definitionItemId, int value) {
|
||||
this.ensurePersistentValuesLoaded();
|
||||
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
WiredVariableDefinitionInfo definitionInfo = this.getDefinitionInfo(definitionItemId);
|
||||
|
||||
if (definitionInfo == null || definitionInfo.isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer previousValue = definitionInfo.hasValue() ? this.getCurrentValue(definitionItemId) : null;
|
||||
|
||||
if (extra instanceof WiredExtraVariableEcho) {
|
||||
boolean changed = ((WiredExtraVariableEcho) extra).updateValue(this.room, this.room.getId(), value);
|
||||
boolean shouldEmit = changed || (definitionInfo.hasValue() && previousValue != null && previousValue == value);
|
||||
|
||||
if (shouldEmit) {
|
||||
Integer currentValue = definitionInfo.hasValue() ? this.getCurrentValue(definitionItemId) : null;
|
||||
this.emitVariableChangedEvents(extra, definitionInfo, previousValue, currentValue);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.broadcastSnapshot();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference) {
|
||||
boolean changed = WiredVariableReferenceSupport.updateSharedRoomVariable((WiredExtraVariableReference) extra, value);
|
||||
boolean shouldEmit = changed || (definitionInfo.hasValue() && previousValue != null && previousValue == value);
|
||||
|
||||
if (shouldEmit) {
|
||||
Integer currentValue = definitionInfo.hasValue() ? this.getCurrentValue(definitionItemId) : null;
|
||||
this.emitVariableChangedEvents(extra, definitionInfo, previousValue, currentValue);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.broadcastSnapshot();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
VariableAssignment assignment = this.activeAssignmentsByDefinitionId.get(definitionItemId);
|
||||
|
||||
if (assignment == null) {
|
||||
assignment = new VariableAssignment(value, 0, Emulator.getIntUnixTimestamp());
|
||||
this.activeAssignmentsByDefinitionId.put(definitionItemId, assignment);
|
||||
} else if (assignment.getValue() == value) {
|
||||
this.emitVariableChangedEvents(extra, definitionInfo, previousValue, assignment.getValue());
|
||||
return false;
|
||||
} else {
|
||||
assignment.setValue(value, Emulator.getIntUnixTimestamp());
|
||||
}
|
||||
|
||||
WiredExtraRoomVariable definition = (WiredExtraRoomVariable) extra;
|
||||
|
||||
if (definition.isPermanentAvailability()) {
|
||||
this.upsertPersistentAssignment(definitionItemId, assignment);
|
||||
}
|
||||
|
||||
if (definition.isSharedAvailability()) {
|
||||
WiredVariableReferenceSupport.cacheSharedRoomAssignment(this.room.getId(), definitionItemId, assignment.getValue(), assignment.getUpdatedAt());
|
||||
} else {
|
||||
WiredVariableReferenceSupport.clearSharedRoomDefinition(this.room.getId(), definitionItemId);
|
||||
}
|
||||
|
||||
this.emitVariableChangedEvents(extra, definitionInfo, previousValue, assignment.getValue());
|
||||
this.broadcastSnapshot();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeVariable(int definitionItemId) {
|
||||
this.ensurePersistentValuesLoaded();
|
||||
|
||||
if (definitionItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo definitionInfo = this.getDefinitionInfo(definitionItemId);
|
||||
if (definitionInfo == null || definitionInfo.isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer previousValue = definitionInfo.hasValue() ? this.getCurrentValue(definitionItemId) : null;
|
||||
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
if (extra instanceof WiredExtraVariableEcho) {
|
||||
boolean changed = ((WiredExtraVariableEcho) extra).removeValue(this.room, this.room.getId());
|
||||
|
||||
if (changed) {
|
||||
Integer currentValue = definitionInfo.hasValue() ? this.getCurrentValue(definitionItemId) : null;
|
||||
this.emitVariableChangedEvents(extra, definitionInfo, previousValue, currentValue);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.broadcastSnapshot();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference) {
|
||||
boolean changed = WiredVariableReferenceSupport.removeSharedRoomVariable((WiredExtraVariableReference) extra);
|
||||
|
||||
if (changed) {
|
||||
Integer currentValue = definitionInfo.hasValue() ? this.getCurrentValue(definitionItemId) : null;
|
||||
this.emitVariableChangedEvents(extra, definitionInfo, previousValue, currentValue);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.broadcastSnapshot();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
VariableAssignment removed = this.activeAssignmentsByDefinitionId.remove(definitionItemId);
|
||||
this.deletePersistentAssignment(definitionItemId);
|
||||
|
||||
WiredExtraRoomVariable definition = this.getDefinition(definitionItemId);
|
||||
if (definition != null && definition.isSharedAvailability()) {
|
||||
WiredVariableReferenceSupport.clearSharedRoomDefinition(this.room.getId(), definitionItemId);
|
||||
}
|
||||
|
||||
if (removed != null) {
|
||||
Integer currentValue = definitionInfo.hasValue() ? this.getCurrentValue(definitionItemId) : null;
|
||||
this.emitVariableChangedEvents(extra, definitionInfo, previousValue, currentValue);
|
||||
this.broadcastSnapshot();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void clearTransientAssignments() {
|
||||
this.ensurePersistentValuesLoaded();
|
||||
|
||||
boolean changed = false;
|
||||
|
||||
for (Integer definitionItemId : new ArrayList<>(this.activeAssignmentsByDefinitionId.keySet())) {
|
||||
WiredExtraRoomVariable definition = this.getDefinition(definitionItemId);
|
||||
|
||||
if (definition != null && definition.isPermanentAvailability()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.activeAssignmentsByDefinitionId.remove(definitionItemId) != null) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.broadcastSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeDefinition(int definitionItemId) {
|
||||
this.ensurePersistentValuesLoaded();
|
||||
this.activeAssignmentsByDefinitionId.remove(definitionItemId);
|
||||
this.deletePersistentAssignment(definitionItemId);
|
||||
WiredExtraRoomVariable definition = this.getDefinition(definitionItemId);
|
||||
if (definition != null && definition.isSharedAvailability()) {
|
||||
WiredVariableReferenceSupport.clearSharedRoomDefinition(this.room.getId(), definitionItemId);
|
||||
}
|
||||
this.broadcastSnapshot();
|
||||
}
|
||||
|
||||
public void handleDefinitionUpdated(WiredExtraRoomVariable definition) {
|
||||
if (definition == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ensurePersistentValuesLoaded();
|
||||
|
||||
if (!definition.isPermanentAvailability()) {
|
||||
this.deletePersistentAssignment(definition.getId());
|
||||
} else {
|
||||
VariableAssignment assignment = this.activeAssignmentsByDefinitionId.get(definition.getId());
|
||||
|
||||
if (assignment == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.upsertPersistentAssignment(definition.getId(), assignment);
|
||||
}
|
||||
|
||||
if (definition.isSharedAvailability()) {
|
||||
VariableAssignment assignment = this.activeAssignmentsByDefinitionId.get(definition.getId());
|
||||
|
||||
if (assignment != null) {
|
||||
WiredVariableReferenceSupport.cacheSharedRoomAssignment(this.room.getId(), definition.getId(), assignment.getValue(), assignment.getUpdatedAt());
|
||||
}
|
||||
} else {
|
||||
WiredVariableReferenceSupport.clearSharedRoomDefinition(this.room.getId(), definition.getId());
|
||||
}
|
||||
|
||||
this.broadcastSnapshot();
|
||||
}
|
||||
|
||||
public Snapshot createSnapshot() {
|
||||
this.ensurePersistentValuesLoaded();
|
||||
|
||||
List<DefinitionEntry> definitions = new ArrayList<>();
|
||||
List<AssignmentEntry> assignments = new ArrayList<>();
|
||||
List<Integer> derivedDefinitionIds = new ArrayList<>();
|
||||
List<WiredExtraVariableEcho> roomEchoes = this.getRoomEchoes();
|
||||
|
||||
for (WiredVariableDefinitionInfo definition : this.getAllDefinitionInfos()) {
|
||||
definitions.add(new DefinitionEntry(definition.getItemId(), definition.getName(), definition.hasValue(), definition.getAvailability(), definition.isTextConnected(), definition.isReadOnly()));
|
||||
|
||||
if (WiredVariableLevelSystemSupport.resolveDerivedDefinition(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, definition.getItemId()) != null) {
|
||||
derivedDefinitionIds.add(definition.getItemId());
|
||||
}
|
||||
|
||||
if (this.isReferenceDefinition(definition.getItemId())) {
|
||||
WiredExtraVariableReference reference = (WiredExtraVariableReference) this.getDefinitionExtra(definition.getItemId());
|
||||
WiredVariableReferenceSupport.SharedRoomAssignment assignment = WiredVariableReferenceSupport.getSharedRoomAssignment(reference);
|
||||
assignments.add(new AssignmentEntry(definition.getItemId(), (assignment != null) ? assignment.getValue() : 0, 0, (assignment != null) ? assignment.getUpdatedAt() : 0));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (derivedDefinitionIds.contains(definition.getItemId())) {
|
||||
assignments.add(new AssignmentEntry(
|
||||
definition.getItemId(),
|
||||
this.getCurrentValue(definition.getItemId()),
|
||||
this.getCreatedAt(definition.getItemId()),
|
||||
this.getUpdatedAt(definition.getItemId())
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (roomEchoes.stream().anyMatch(echo -> echo.getId() == definition.getItemId())) {
|
||||
assignments.add(new AssignmentEntry(
|
||||
definition.getItemId(),
|
||||
this.getCurrentValue(definition.getItemId()),
|
||||
this.getCreatedAt(definition.getItemId()),
|
||||
this.getUpdatedAt(definition.getItemId())
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
VariableAssignment assignment = this.activeAssignmentsByDefinitionId.get(definition.getItemId());
|
||||
assignments.add(new AssignmentEntry(definition.getItemId(), (assignment != null) ? assignment.getValue() : 0, 0, (assignment != null) ? assignment.getUpdatedAt() : 0));
|
||||
}
|
||||
|
||||
assignments.sort(Comparator.comparingInt(AssignmentEntry::getVariableItemId));
|
||||
|
||||
return new Snapshot(this.room.getId(), definitions, assignments);
|
||||
}
|
||||
|
||||
public void sendSnapshot(Habbo habbo) {
|
||||
if (habbo == null || habbo.getClient() == null || !this.room.canInspectWired(habbo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
habbo.getClient().sendResponse(new WiredUserVariablesDataComposer(this.room.getUserVariableManager().createSnapshot(), this.room.getFurniVariableManager().createSnapshot(), this.createSnapshot()));
|
||||
}
|
||||
|
||||
public void broadcastSnapshot() {
|
||||
RoomUserVariableManager.Snapshot userSnapshot = this.room.getUserVariableManager().createSnapshot();
|
||||
RoomFurniVariableManager.Snapshot furniSnapshot = this.room.getFurniVariableManager().createSnapshot();
|
||||
Snapshot roomSnapshot = this.createSnapshot();
|
||||
|
||||
for (Habbo habbo : this.room.getCurrentHabbos().values()) {
|
||||
if (habbo == null || habbo.getClient() == null || !this.room.canInspectWired(habbo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
habbo.getClient().sendResponse(new WiredUserVariablesDataComposer(userSnapshot, furniSnapshot, roomSnapshot));
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<WiredExtraRoomVariable> getDefinitions() {
|
||||
if (this.room.getRoomSpecialTypes() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = this.room.getRoomSpecialTypes().getExtras();
|
||||
List<WiredExtraRoomVariable> result = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extra instanceof WiredExtraRoomVariable) {
|
||||
result.add((WiredExtraRoomVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
result.sort(Comparator.comparing(WiredExtraRoomVariable::getVariableName, String.CASE_INSENSITIVE_ORDER).thenComparingInt(WiredExtraRoomVariable::getId));
|
||||
return result;
|
||||
}
|
||||
|
||||
public Collection<WiredVariableDefinitionInfo> getAllDefinitionInfos() {
|
||||
List<WiredVariableDefinitionInfo> result = new ArrayList<>();
|
||||
List<WiredVariableDefinitionInfo> baseDefinitions = new ArrayList<>();
|
||||
|
||||
for (WiredExtraRoomVariable definition : this.getDefinitions()) {
|
||||
baseDefinitions.add(new WiredVariableDefinitionInfo(
|
||||
definition.getId(),
|
||||
definition.getVariableName(),
|
||||
definition.hasValue(),
|
||||
definition.getAvailability(),
|
||||
WiredVariableTextConnectorSupport.isTextConnected(this.room, definition),
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
for (WiredExtraVariableReference reference : this.getRoomReferences()) {
|
||||
baseDefinitions.add(new WiredVariableDefinitionInfo(reference.getId(), reference.getVariableName(), reference.hasValue(), reference.getAvailability(), false, reference.isReadOnly()));
|
||||
}
|
||||
|
||||
for (WiredExtraVariableEcho echo : this.getRoomEchoes()) {
|
||||
baseDefinitions.add(echo.createDefinitionInfo(this.room));
|
||||
}
|
||||
|
||||
result.addAll(baseDefinitions);
|
||||
|
||||
for (WiredVariableDefinitionInfo definition : baseDefinitions) {
|
||||
result.addAll(WiredVariableLevelSystemSupport.getDerivedDefinitions(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, this.getDefinitionExtra(definition.getItemId()), definition));
|
||||
}
|
||||
|
||||
result.sort(Comparator.comparing(WiredVariableDefinitionInfo::getName, String.CASE_INSENSITIVE_ORDER).thenComparingInt(WiredVariableDefinitionInfo::getItemId));
|
||||
return result;
|
||||
}
|
||||
|
||||
public WiredVariableDefinitionInfo getDefinitionInfo(int definitionItemId) {
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
|
||||
if (extra instanceof WiredExtraRoomVariable) {
|
||||
WiredExtraRoomVariable definition = (WiredExtraRoomVariable) extra;
|
||||
return new WiredVariableDefinitionInfo(
|
||||
definition.getId(),
|
||||
definition.getVariableName(),
|
||||
definition.hasValue(),
|
||||
definition.getAvailability(),
|
||||
WiredVariableTextConnectorSupport.isTextConnected(this.room, definition),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference && ((WiredExtraVariableReference) extra).isRoomReference()) {
|
||||
WiredExtraVariableReference reference = (WiredExtraVariableReference) extra;
|
||||
return new WiredVariableDefinitionInfo(reference.getId(), reference.getVariableName(), reference.hasValue(), reference.getAvailability(), false, reference.isReadOnly());
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableEcho && ((WiredExtraVariableEcho) extra).isRoomEcho()) {
|
||||
return ((WiredExtraVariableEcho) extra).createDefinitionInfo(this.room);
|
||||
}
|
||||
|
||||
return WiredVariableLevelSystemSupport.getDerivedDefinitionInfo(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, definitionItemId);
|
||||
}
|
||||
|
||||
private WiredExtraRoomVariable getDefinition(int definitionItemId) {
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
|
||||
if (!(extra instanceof WiredExtraRoomVariable)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (WiredExtraRoomVariable) extra;
|
||||
}
|
||||
|
||||
private InteractionWiredExtra getDefinitionExtra(int definitionItemId) {
|
||||
if (this.room.getRoomSpecialTypes() == null || definitionItemId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.room.getRoomSpecialTypes().getExtra(definitionItemId);
|
||||
}
|
||||
|
||||
private boolean isReferenceDefinition(int definitionItemId) {
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
return extra instanceof WiredExtraVariableReference && ((WiredExtraVariableReference) extra).isRoomReference();
|
||||
}
|
||||
|
||||
private List<WiredExtraVariableReference> getRoomReferences() {
|
||||
if (this.room.getRoomSpecialTypes() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<WiredExtraVariableReference> result = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredExtra extra : this.room.getRoomSpecialTypes().getExtras()) {
|
||||
if (extra instanceof WiredExtraVariableReference && ((WiredExtraVariableReference) extra).isRoomReference()) {
|
||||
result.add((WiredExtraVariableReference) extra);
|
||||
}
|
||||
}
|
||||
|
||||
result.sort(Comparator.comparing(WiredExtraVariableReference::getVariableName, String.CASE_INSENSITIVE_ORDER).thenComparingInt(WiredExtraVariableReference::getId));
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<WiredExtraVariableEcho> getRoomEchoes() {
|
||||
if (this.room.getRoomSpecialTypes() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<WiredExtraVariableEcho> result = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredExtra extra : this.room.getRoomSpecialTypes().getExtras()) {
|
||||
if (extra instanceof WiredExtraVariableEcho && ((WiredExtraVariableEcho) extra).isRoomEcho()) {
|
||||
result.add((WiredExtraVariableEcho) extra);
|
||||
}
|
||||
}
|
||||
|
||||
result.sort(Comparator.comparing(WiredExtraVariableEcho::getVariableName, String.CASE_INSENSITIVE_ORDER).thenComparingInt(WiredExtraVariableEcho::getId));
|
||||
return result;
|
||||
}
|
||||
|
||||
private VariableAssignment getRawAssignment(int definitionItemId) {
|
||||
if (definitionItemId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InteractionWiredExtra extra = this.getDefinitionExtra(definitionItemId);
|
||||
if (extra instanceof WiredExtraVariableReference) {
|
||||
WiredVariableReferenceSupport.SharedRoomAssignment assignment = WiredVariableReferenceSupport.getSharedRoomAssignment((WiredExtraVariableReference) extra);
|
||||
return (assignment != null) ? new VariableAssignment(assignment.getValue(), 0, assignment.getUpdatedAt()) : null;
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableEcho) {
|
||||
WiredExtraVariableEcho echo = (WiredExtraVariableEcho) extra;
|
||||
if (!echo.hasVariable(this.room, this.room.getId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VariableAssignment(echo.getCurrentValue(this.room, this.room.getId()), echo.getCreatedAt(this.room, this.room.getId()), echo.getUpdatedAt(this.room, this.room.getId()));
|
||||
}
|
||||
|
||||
return this.activeAssignmentsByDefinitionId.get(definitionItemId);
|
||||
}
|
||||
|
||||
private Integer getRawValue(int definitionItemId) {
|
||||
VariableAssignment assignment = this.getRawAssignment(definitionItemId);
|
||||
return (assignment != null) ? assignment.getValue() : null;
|
||||
}
|
||||
|
||||
private void emitVariableChangedEvents(InteractionWiredExtra definitionExtra, WiredVariableDefinitionInfo definitionInfo, Integer previousValue, Integer currentValue) {
|
||||
if (definitionInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emitVariableChangedEvent(definitionInfo.getItemId(), definitionInfo.hasValue(), previousValue, currentValue);
|
||||
|
||||
for (WiredVariableDefinitionInfo derivedDefinition : WiredVariableLevelSystemSupport.getDerivedDefinitions(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, definitionExtra, definitionInfo)) {
|
||||
WiredVariableLevelSystemSupport.DerivedDefinition resolvedDefinition = WiredVariableLevelSystemSupport.resolveDerivedDefinition(this.room, WiredVariableLevelSystemSupport.TARGET_ROOM, derivedDefinition.getItemId());
|
||||
|
||||
if (resolvedDefinition == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer derivedPreviousValue = WiredVariableLevelSystemSupport.getDerivedValue(resolvedDefinition.getLevelSystem(), resolvedDefinition.getSubvariableType(), previousValue);
|
||||
Integer derivedCurrentValue = WiredVariableLevelSystemSupport.getDerivedValue(resolvedDefinition.getLevelSystem(), resolvedDefinition.getSubvariableType(), currentValue);
|
||||
|
||||
this.emitVariableChangedEvent(derivedDefinition.getItemId(), true, derivedPreviousValue, derivedCurrentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void emitVariableChangedEvent(int definitionItemId, boolean hasValue, Integer previousValue, Integer currentValue) {
|
||||
WiredEvent.VariableChangeKind changeKind = resolveVariableChangeKind(hasValue, previousValue, currentValue);
|
||||
|
||||
if (changeKind == WiredEvent.VariableChangeKind.NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
WiredManager.triggerRoomVariableChanged(this.room, definitionItemId, changeKind);
|
||||
}
|
||||
|
||||
private static WiredEvent.VariableChangeKind resolveVariableChangeKind(boolean hasValue, Integer previousValue, Integer currentValue) {
|
||||
if (!hasValue) {
|
||||
return WiredEvent.VariableChangeKind.NONE;
|
||||
}
|
||||
|
||||
if (Objects.equals(previousValue, currentValue)) {
|
||||
return WiredEvent.VariableChangeKind.UNCHANGED;
|
||||
}
|
||||
|
||||
int previousNumericValue = (previousValue != null) ? previousValue : 0;
|
||||
int currentNumericValue = (currentValue != null) ? currentValue : 0;
|
||||
|
||||
return (currentNumericValue > previousNumericValue)
|
||||
? WiredEvent.VariableChangeKind.INCREASED
|
||||
: WiredEvent.VariableChangeKind.DECREASED;
|
||||
}
|
||||
|
||||
private void upsertPersistentAssignment(int definitionItemId, VariableAssignment assignment) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("INSERT INTO room_wired_variables (room_id, variable_item_id, value, created_at, updated_at) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value), updated_at = VALUES(updated_at)")) {
|
||||
statement.setInt(1, this.room.getId());
|
||||
statement.setInt(2, definitionItemId);
|
||||
statement.setInt(3, (assignment != null) ? assignment.getValue() : 0);
|
||||
|
||||
int now = Emulator.getIntUnixTimestamp();
|
||||
statement.setInt(4, 0);
|
||||
statement.setInt(5, (assignment != null) ? assignment.getUpdatedAt() : now);
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to store permanent wired room variable for room {} and item {}", this.room.getId(), definitionItemId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deletePersistentAssignment(int definitionItemId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("DELETE FROM room_wired_variables WHERE room_id = ? AND variable_item_id = ?")) {
|
||||
statement.setInt(1, this.room.getId());
|
||||
statement.setInt(2, definitionItemId);
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to delete permanent wired room variable for room {} and item {}", this.room.getId(), definitionItemId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static int normalizeTimestamp(int value, int fallback) {
|
||||
if (value > 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (fallback > 0) {
|
||||
return fallback;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static class Snapshot {
|
||||
private final int roomId;
|
||||
private final List<DefinitionEntry> definitions;
|
||||
private final List<AssignmentEntry> assignments;
|
||||
|
||||
public Snapshot(int roomId, List<DefinitionEntry> definitions, List<AssignmentEntry> assignments) {
|
||||
this.roomId = roomId;
|
||||
this.definitions = definitions;
|
||||
this.assignments = assignments;
|
||||
}
|
||||
|
||||
public int getRoomId() {
|
||||
return this.roomId;
|
||||
}
|
||||
|
||||
public List<DefinitionEntry> getDefinitions() {
|
||||
return this.definitions;
|
||||
}
|
||||
|
||||
public List<AssignmentEntry> getAssignments() {
|
||||
return this.assignments;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DefinitionEntry {
|
||||
private final int itemId;
|
||||
private final String name;
|
||||
private final boolean hasValue;
|
||||
private final int availability;
|
||||
private final boolean textConnected;
|
||||
private final boolean readOnly;
|
||||
|
||||
public DefinitionEntry(int itemId, String name, boolean hasValue, int availability, boolean textConnected, boolean readOnly) {
|
||||
this.itemId = itemId;
|
||||
this.name = name;
|
||||
this.hasValue = hasValue;
|
||||
this.availability = availability;
|
||||
this.textConnected = textConnected;
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return this.itemId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return this.hasValue;
|
||||
}
|
||||
|
||||
public int getAvailability() {
|
||||
return this.availability;
|
||||
}
|
||||
|
||||
public boolean isTextConnected() {
|
||||
return this.textConnected;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return this.readOnly;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AssignmentEntry {
|
||||
private final int variableItemId;
|
||||
private final Integer value;
|
||||
private final int createdAt;
|
||||
private final int updatedAt;
|
||||
|
||||
public AssignmentEntry(int variableItemId, Integer value, int createdAt, int updatedAt) {
|
||||
this.variableItemId = variableItemId;
|
||||
this.value = value;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public int getVariableItemId() {
|
||||
return this.variableItemId;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return this.value != null;
|
||||
}
|
||||
|
||||
public int getCreatedAt() {
|
||||
return this.createdAt;
|
||||
}
|
||||
|
||||
public int getUpdatedAt() {
|
||||
return this.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
private static class VariableAssignment {
|
||||
private int value;
|
||||
private final int createdAt;
|
||||
private int updatedAt;
|
||||
|
||||
public VariableAssignment(int value, int createdAt, int updatedAt) {
|
||||
this.value = value;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void setValue(int value, int updatedAt) {
|
||||
this.value = value;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public int getCreatedAt() {
|
||||
return this.createdAt;
|
||||
}
|
||||
|
||||
public int getUpdatedAt() {
|
||||
return this.updatedAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.eu.habbo.habbohotel.rooms;
|
||||
|
||||
public class WiredVariableDefinitionInfo {
|
||||
private final int itemId;
|
||||
private final String name;
|
||||
private final boolean hasValue;
|
||||
private final int availability;
|
||||
private final boolean textConnected;
|
||||
private final boolean readOnly;
|
||||
|
||||
public WiredVariableDefinitionInfo(int itemId, String name, boolean hasValue, int availability, boolean textConnected, boolean readOnly) {
|
||||
this.itemId = itemId;
|
||||
this.name = name;
|
||||
this.hasValue = hasValue;
|
||||
this.availability = availability;
|
||||
this.textConnected = textConnected;
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return this.itemId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return this.hasValue;
|
||||
}
|
||||
|
||||
public int getAvailability() {
|
||||
return this.availability;
|
||||
}
|
||||
|
||||
public boolean isTextConnected() {
|
||||
return this.textConnected;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return this.readOnly;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,11 @@ public enum WiredConditionType {
|
||||
MATCH_TIME(36),
|
||||
MATCH_DATE(37),
|
||||
ACTOR_DIR(38),
|
||||
SLC_QUANTITY(39);
|
||||
SLC_QUANTITY(39),
|
||||
HAS_VAR(40),
|
||||
NOT_HAS_VAR(41),
|
||||
VAR_VAL_MATCH(42),
|
||||
VAR_AGE_MATCH(43);
|
||||
|
||||
public final int code;
|
||||
|
||||
|
||||
@@ -54,7 +54,12 @@ public enum WiredEffectType {
|
||||
USERS_BY_NAME_SELECTOR(52),
|
||||
USERS_ON_FURNI_SELECTOR(53),
|
||||
USERS_GROUP_SELECTOR(54),
|
||||
USERS_HANDITEM_SELECTOR(55);
|
||||
USERS_HANDITEM_SELECTOR(55),
|
||||
GIVE_VAR(69),
|
||||
REMOVE_VAR(73),
|
||||
CHANGE_VAR_VAL(74),
|
||||
FURNI_WITH_VAR_SELECTOR(75),
|
||||
USERS_WITH_VAR_SELECTOR(76);
|
||||
|
||||
public final int code;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ public enum WiredTriggerType {
|
||||
CLICKS_USER(20),
|
||||
USER_PERFORMS_ACTION(21),
|
||||
CLOCK_COUNTER(22),
|
||||
VARIABLE_CHANGED(23),
|
||||
SAY_COMMAND(0),
|
||||
IDLES(11),
|
||||
UNIDLES(11),
|
||||
|
||||
@@ -59,6 +59,9 @@ public final class WiredContext {
|
||||
/** Extra settings from the trigger item (for legacy compatibility) */
|
||||
private final Object[] legacySettings;
|
||||
|
||||
/** Runtime-local context variables shared through the current execution chain. */
|
||||
private WiredContextVariableScope contextVariables;
|
||||
|
||||
/** Whether selector item resolution should include wired furniture too. */
|
||||
private boolean includeWiredSelectorItems = false;
|
||||
|
||||
@@ -108,6 +111,9 @@ public final class WiredContext {
|
||||
this.services = services;
|
||||
this.state = state;
|
||||
this.legacySettings = legacySettings;
|
||||
this.contextVariables = (event.getContextVariableScope() != null)
|
||||
? event.getContextVariableScope()
|
||||
: new WiredContextVariableScope();
|
||||
this.targets = new WiredTargets();
|
||||
|
||||
// Default targets: include actor and trigger item for backwards compatibility
|
||||
@@ -259,6 +265,14 @@ public final class WiredContext {
|
||||
return legacySettings != null ? legacySettings : new Object[0];
|
||||
}
|
||||
|
||||
public WiredContextVariableScope contextVariables() {
|
||||
return this.contextVariables;
|
||||
}
|
||||
|
||||
public void forkContextVariables() {
|
||||
this.contextVariables = this.contextVariables.copy();
|
||||
}
|
||||
|
||||
public boolean includeWiredSelectorItems() {
|
||||
return this.includeWiredSelectorItems;
|
||||
}
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class WiredContextVariableScope {
|
||||
private final LinkedHashMap<Integer, VariableAssignment> assignments;
|
||||
|
||||
public WiredContextVariableScope() {
|
||||
this.assignments = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
private WiredContextVariableScope(Map<Integer, VariableAssignment> source) {
|
||||
this.assignments = new LinkedHashMap<>();
|
||||
|
||||
if (source == null || source.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, VariableAssignment> entry : source.entrySet()) {
|
||||
if (entry == null || entry.getKey() == null || entry.getKey() <= 0 || entry.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.assignments.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
}
|
||||
|
||||
public WiredContextVariableScope copy() {
|
||||
return new WiredContextVariableScope(this.assignments);
|
||||
}
|
||||
|
||||
public boolean hasVariable(int definitionItemId) {
|
||||
return definitionItemId > 0 && this.assignments.containsKey(definitionItemId);
|
||||
}
|
||||
|
||||
public Integer getValue(int definitionItemId) {
|
||||
VariableAssignment assignment = this.assignments.get(definitionItemId);
|
||||
return assignment != null ? assignment.getValue() : null;
|
||||
}
|
||||
|
||||
public int getCreatedAt(int definitionItemId) {
|
||||
VariableAssignment assignment = this.assignments.get(definitionItemId);
|
||||
return assignment != null ? assignment.getCreatedAt() : 0;
|
||||
}
|
||||
|
||||
public int getUpdatedAt(int definitionItemId) {
|
||||
VariableAssignment assignment = this.assignments.get(definitionItemId);
|
||||
return assignment != null ? assignment.getUpdatedAt() : 0;
|
||||
}
|
||||
|
||||
public boolean assignValue(int definitionItemId, Integer value, boolean overrideExisting) {
|
||||
if (definitionItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VariableAssignment existingAssignment = this.assignments.get(definitionItemId);
|
||||
|
||||
if (existingAssignment != null && !overrideExisting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int now = Emulator.getIntUnixTimestamp();
|
||||
|
||||
if (existingAssignment == null || overrideExisting) {
|
||||
this.assignments.put(definitionItemId, new VariableAssignment(value, now, now));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean updateValue(int definitionItemId, Integer value) {
|
||||
if (definitionItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VariableAssignment assignment = this.assignments.get(definitionItemId);
|
||||
if (assignment == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((assignment.getValue() == null && value == null)
|
||||
|| (assignment.getValue() != null && assignment.getValue().equals(value))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assignment.setValue(value, Emulator.getIntUnixTimestamp());
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeValue(int definitionItemId) {
|
||||
if (definitionItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.assignments.remove(definitionItemId) != null;
|
||||
}
|
||||
|
||||
public static final class VariableAssignment {
|
||||
private Integer value;
|
||||
private final int createdAt;
|
||||
private int updatedAt;
|
||||
|
||||
public VariableAssignment(Integer value, int createdAt, int updatedAt) {
|
||||
this.value = value;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public int getCreatedAt() {
|
||||
return this.createdAt;
|
||||
}
|
||||
|
||||
public int getUpdatedAt() {
|
||||
return this.updatedAt;
|
||||
}
|
||||
|
||||
public void setValue(Integer value, int updatedAt) {
|
||||
this.value = value;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
private VariableAssignment copy() {
|
||||
return new VariableAssignment(this.value, this.createdAt, this.updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraContextVariable;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import com.eu.habbo.messages.outgoing.wired.WiredUserVariablesDataComposer;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public final class WiredContextVariableSupport {
|
||||
private WiredContextVariableSupport() {
|
||||
}
|
||||
|
||||
public static List<WiredExtraContextVariable> getDefinitions(Room room) {
|
||||
List<WiredExtraContextVariable> definitions = new ArrayList<>();
|
||||
|
||||
if (room == null || room.getRoomSpecialTypes() == null) {
|
||||
return definitions;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras();
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return definitions;
|
||||
}
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extra instanceof WiredExtraContextVariable) {
|
||||
definitions.add((WiredExtraContextVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
definitions.sort(Comparator
|
||||
.comparing(WiredExtraContextVariable::getVariableName, String.CASE_INSENSITIVE_ORDER)
|
||||
.thenComparingInt(WiredExtraContextVariable::getId));
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
public static List<WiredVariableDefinitionInfo> createDefinitionInfos(Room room) {
|
||||
List<WiredVariableDefinitionInfo> definitions = new ArrayList<>();
|
||||
|
||||
for (WiredExtraContextVariable definition : getDefinitions(room)) {
|
||||
if (definition == null || definition.getVariableName() == null || definition.getVariableName().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
definitions.add(new WiredVariableDefinitionInfo(
|
||||
definition.getId(),
|
||||
definition.getVariableName(),
|
||||
definition.hasValue(),
|
||||
0,
|
||||
WiredVariableTextConnectorSupport.isTextConnected(room, definition.getId()),
|
||||
false));
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
public static WiredExtraContextVariable getDefinition(Room room, int definitionItemId) {
|
||||
if (room == null || room.getRoomSpecialTypes() == null || definitionItemId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InteractionWiredExtra extra = room.getRoomSpecialTypes().getExtra(definitionItemId);
|
||||
return (extra instanceof WiredExtraContextVariable) ? (WiredExtraContextVariable) extra : null;
|
||||
}
|
||||
|
||||
public static WiredVariableDefinitionInfo getDefinitionInfo(Room room, int definitionItemId) {
|
||||
WiredExtraContextVariable definition = getDefinition(room, definitionItemId);
|
||||
if (definition == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new WiredVariableDefinitionInfo(
|
||||
definition.getId(),
|
||||
definition.getVariableName(),
|
||||
definition.hasValue(),
|
||||
0,
|
||||
WiredVariableTextConnectorSupport.isTextConnected(room, definition.getId()),
|
||||
false);
|
||||
}
|
||||
|
||||
public static boolean hasDefinition(Room room, int definitionItemId) {
|
||||
return getDefinition(room, definitionItemId) != null;
|
||||
}
|
||||
|
||||
public static boolean assignVariable(WiredContext ctx, Room room, int definitionItemId, Integer value, boolean overrideExisting) {
|
||||
WiredExtraContextVariable definition = getDefinition(room, definitionItemId);
|
||||
if (ctx == null || definition == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (overrideExisting && ctx.contextVariables().hasVariable(definitionItemId)) {
|
||||
ctx.forkContextVariables();
|
||||
}
|
||||
|
||||
return ctx.contextVariables().assignValue(definitionItemId, definition.hasValue() ? value : null, overrideExisting);
|
||||
}
|
||||
|
||||
public static boolean updateVariableValue(WiredContext ctx, Room room, int definitionItemId, Integer value) {
|
||||
WiredExtraContextVariable definition = getDefinition(room, definitionItemId);
|
||||
if (ctx == null || definition == null || !definition.hasValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ctx.contextVariables().updateValue(definitionItemId, value);
|
||||
}
|
||||
|
||||
public static boolean removeVariable(WiredContext ctx, Room room, int definitionItemId) {
|
||||
return ctx != null && getDefinition(room, definitionItemId) != null && ctx.contextVariables().removeValue(definitionItemId);
|
||||
}
|
||||
|
||||
public static boolean hasVariable(WiredContext ctx, int definitionItemId) {
|
||||
return ctx != null && ctx.contextVariables().hasVariable(definitionItemId);
|
||||
}
|
||||
|
||||
public static Integer getCurrentValue(WiredContext ctx, int definitionItemId) {
|
||||
return ctx != null ? ctx.contextVariables().getValue(definitionItemId) : null;
|
||||
}
|
||||
|
||||
public static int getCreatedAt(WiredContext ctx, int definitionItemId) {
|
||||
return ctx != null ? ctx.contextVariables().getCreatedAt(definitionItemId) : 0;
|
||||
}
|
||||
|
||||
public static int getUpdatedAt(WiredContext ctx, int definitionItemId) {
|
||||
return ctx != null ? ctx.contextVariables().getUpdatedAt(definitionItemId) : 0;
|
||||
}
|
||||
|
||||
public static void broadcastDefinitions(Room room) {
|
||||
if (room == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
WiredUserVariablesDataComposer composer = new WiredUserVariablesDataComposer(
|
||||
room.getUserVariableManager().createSnapshot(),
|
||||
room.getFurniVariableManager().createSnapshot(),
|
||||
room.getRoomVariableManager().createSnapshot());
|
||||
|
||||
room.getHabbos().forEach(habbo ->
|
||||
{
|
||||
if (habbo == null || habbo.getClient() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
habbo.getClient().sendResponse(composer);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurniByVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUsersByVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecutionLimit;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom;
|
||||
@@ -20,6 +22,7 @@ import com.eu.habbo.habbohotel.wired.WiredConditionOperator;
|
||||
import com.eu.habbo.habbohotel.wired.api.IWiredCondition;
|
||||
import com.eu.habbo.habbohotel.wired.api.IWiredEffect;
|
||||
import com.eu.habbo.habbohotel.wired.api.WiredStack;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.GenericAlertComposer;
|
||||
import com.eu.habbo.plugin.events.furniture.wired.WiredStackExecutedEvent;
|
||||
@@ -77,6 +80,33 @@ public final class WiredEngine {
|
||||
/** Duration to ban wired execution in a room after abuse detected (milliseconds) */
|
||||
public static long WIRED_BAN_DURATION_MS = 600000;
|
||||
|
||||
/** Monitor usage window in milliseconds */
|
||||
public static int MONITOR_USAGE_WINDOW_MS = 1000;
|
||||
|
||||
/** Monitor execution cap per room window */
|
||||
public static int MONITOR_USAGE_LIMIT = 1000;
|
||||
|
||||
/** Maximum delayed events allowed per room at the same time */
|
||||
public static int MONITOR_DELAYED_EVENTS_LIMIT = 100;
|
||||
|
||||
/** Average execution threshold that marks overload */
|
||||
public static int MONITOR_OVERLOAD_AVERAGE_MS = 50;
|
||||
|
||||
/** Peak execution threshold that marks overload */
|
||||
public static int MONITOR_OVERLOAD_PEAK_MS = 150;
|
||||
|
||||
/** Consecutive overloaded windows required before recording overload */
|
||||
public static int MONITOR_OVERLOAD_CONSECUTIVE_WINDOWS = 2;
|
||||
|
||||
/** Usage percentage threshold that marks a room as heavy */
|
||||
public static int MONITOR_HEAVY_USAGE_PERCENT = 70;
|
||||
|
||||
/** Consecutive windows above threshold before marking heavy */
|
||||
public static int MONITOR_HEAVY_CONSECUTIVE_WINDOWS = 5;
|
||||
|
||||
/** Delayed queue percentage threshold that contributes to heavy state */
|
||||
public static int MONITOR_HEAVY_DELAYED_PERCENT = 60;
|
||||
|
||||
private final WiredServices services;
|
||||
private final WiredStackIndex index;
|
||||
private final int maxStepsPerStack;
|
||||
@@ -93,6 +123,9 @@ public final class WiredEngine {
|
||||
/** Track rooms that are banned from wired execution: roomId -> ban expiry timestamp */
|
||||
private final ConcurrentHashMap<Integer, Long> bannedRooms;
|
||||
|
||||
/** Track monitor diagnostics per room */
|
||||
private final ConcurrentHashMap<Integer, WiredRoomDiagnostics> roomDiagnostics;
|
||||
|
||||
/**
|
||||
* Create a new wired engine.
|
||||
*
|
||||
@@ -112,6 +145,7 @@ public final class WiredEngine {
|
||||
this.roomRecursionDepth = new ConcurrentHashMap<>();
|
||||
this.eventRateLimiters = new ConcurrentHashMap<>();
|
||||
this.bannedRooms = new ConcurrentHashMap<>();
|
||||
this.roomDiagnostics = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,6 +180,12 @@ public final class WiredEngine {
|
||||
// Check and increment recursion depth to prevent infinite loops
|
||||
int currentDepth = roomRecursionDepth.getOrDefault(roomId, 0);
|
||||
if (currentDepth >= MAX_RECURSION_DEPTH) {
|
||||
getDiagnostics(roomId).recordRecursionTimeout(
|
||||
System.currentTimeMillis(),
|
||||
String.format("Recursion depth %d/%d while handling %s", currentDepth, MAX_RECURSION_DEPTH, event.getType().name()),
|
||||
event.getType().name(),
|
||||
0
|
||||
);
|
||||
LOGGER.warn("Wired recursion limit reached in room {} (depth: {}). " +
|
||||
"Possible infinite loop detected (e.g., collision + chase). Aborting.", roomId, currentDepth);
|
||||
debug(room, "RECURSION LIMIT REACHED - aborting to prevent crash");
|
||||
@@ -215,9 +255,10 @@ public final class WiredEngine {
|
||||
*/
|
||||
private boolean processStack(WiredStack stack, WiredEvent event, long currentTime) {
|
||||
Room room = event.getRoom();
|
||||
WiredTextInputCaptureSupport.CaptureResult captureResult = resolveTextInputCapture(stack, event);
|
||||
|
||||
// Check if trigger matches
|
||||
if (!stack.trigger().matches(stack.triggerItem(), event)) {
|
||||
if (!captureResult.matches()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -226,13 +267,23 @@ public final class WiredEngine {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stackHasExecutableOutcome(stack, event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create execution context with stack reference
|
||||
WiredState state = new WiredState(maxStepsPerStack);
|
||||
WiredContext ctx = new WiredContext(event, stack.triggerItem(), stack, services, state, null);
|
||||
WiredTextInputCaptureSupport.applyToContext(ctx, room, captureResult);
|
||||
WiredRoomDiagnostics diagnostics = getDiagnostics(room.getId());
|
||||
|
||||
// Initial step for trigger
|
||||
state.step();
|
||||
|
||||
|
||||
int stackCost = estimateStackCost(stack, roomRecursionDepth.getOrDefault(room.getId(), 0));
|
||||
String monitorSourceLabel = getMonitorSourceLabel(stack.triggerItem(), event);
|
||||
int monitorSourceId = getMonitorSourceId(stack.triggerItem());
|
||||
|
||||
debug(room, "Trigger matched: {} at item {} (conditions: {}, effects: {})",
|
||||
event.getType(),
|
||||
stack.triggerItem() != null ? stack.triggerItem().getId() : "null",
|
||||
@@ -274,6 +325,16 @@ public final class WiredEngine {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!diagnostics.tryConsumeExecutionBudget(
|
||||
stackCost,
|
||||
currentTime,
|
||||
monitorSourceLabel,
|
||||
monitorSourceId,
|
||||
buildStackMonitorReason(stack, event, stackCost))) {
|
||||
debug(room, "Execution cap blocked stack {}", stack.triggerItem() != null ? stack.triggerItem().getId() : "null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((event.getType() == WiredEvent.Type.USER_CLICKS_USER)
|
||||
&& (stack.triggerItem() instanceof WiredTriggerHabboClicksUser)
|
||||
&& event.getActor().isPresent()) {
|
||||
@@ -302,14 +363,22 @@ public final class WiredEngine {
|
||||
|
||||
// Fire executed event
|
||||
fireExecutedEvent(stack, event);
|
||||
diagnostics.recordExecution(
|
||||
state.elapsedMs(),
|
||||
System.currentTimeMillis(),
|
||||
monitorSourceLabel,
|
||||
monitorSourceId,
|
||||
buildExecutionMonitorReason(stack, state.elapsedMs())
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean wouldTriggerStack(WiredStack stack, WiredEvent event, long currentTime) {
|
||||
Room room = event.getRoom();
|
||||
WiredTextInputCaptureSupport.CaptureResult captureResult = resolveTextInputCapture(stack, event);
|
||||
|
||||
if (!stack.trigger().matches(stack.triggerItem(), event)) {
|
||||
if (!captureResult.matches()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -317,8 +386,13 @@ public final class WiredEngine {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stackHasExecutableOutcome(stack, event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WiredState state = new WiredState(maxStepsPerStack);
|
||||
WiredContext ctx = new WiredContext(event, stack.triggerItem(), stack, services, state, null);
|
||||
WiredTextInputCaptureSupport.applyToContext(ctx, room, captureResult);
|
||||
|
||||
state.step();
|
||||
|
||||
@@ -336,6 +410,43 @@ public final class WiredEngine {
|
||||
return executionLimitExtra == null || executionLimitExtra.canExecuteAt(currentTime);
|
||||
}
|
||||
|
||||
private boolean stackHasExecutableOutcome(WiredStack stack, WiredEvent event) {
|
||||
if (stack == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stack.hasEffects()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stack.triggerItem() instanceof WiredTriggerHabboSaysKeyword) {
|
||||
return ((WiredTriggerHabboSaysKeyword) stack.triggerItem()).isHideMessage();
|
||||
}
|
||||
|
||||
if ((event != null)
|
||||
&& (event.getType() == WiredEvent.Type.USER_CLICKS_USER)
|
||||
&& (stack.triggerItem() instanceof WiredTriggerHabboClicksUser)) {
|
||||
WiredTriggerHabboClicksUser trigger = (WiredTriggerHabboClicksUser) stack.triggerItem();
|
||||
return trigger.isBlockMenuOpen() || trigger.isDoNotRotate();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private WiredTextInputCaptureSupport.CaptureResult resolveTextInputCapture(WiredStack stack, WiredEvent event) {
|
||||
if (stack == null || event == null) {
|
||||
return WiredTextInputCaptureSupport.CaptureResult.noMatch();
|
||||
}
|
||||
|
||||
if (event.getType() != WiredEvent.Type.USER_SAYS || !(stack.triggerItem() instanceof WiredTriggerHabboSaysKeyword)) {
|
||||
return stack.trigger().matches(stack.triggerItem(), event)
|
||||
? WiredTextInputCaptureSupport.CaptureResult.matched(new LinkedHashMap<>())
|
||||
: WiredTextInputCaptureSupport.CaptureResult.noMatch();
|
||||
}
|
||||
|
||||
return WiredTextInputCaptureSupport.resolve(stack, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate all conditions in a stack.
|
||||
*/
|
||||
@@ -462,38 +573,48 @@ public final class WiredEngine {
|
||||
executeOrderedEffects(regulars, ctx, currentTime);
|
||||
return;
|
||||
} else {
|
||||
// Normal mode: regular effects in random order
|
||||
// Normal mode: preserve the physical stack order.
|
||||
// This matches the legacy handler behavior and avoids visual/state races
|
||||
// for combinations such as Move/Rotate + Match To Snapshot in the same stack.
|
||||
toExecute = new ArrayList<>(regulars);
|
||||
Collections.shuffle(toExecute);
|
||||
}
|
||||
|
||||
// Execute selected effects
|
||||
for (IWiredEffect effect : toExecute) {
|
||||
// Check if effect requires actor
|
||||
if (effect.requiresActor() && !ctx.hasActor()) {
|
||||
continue;
|
||||
}
|
||||
WiredMoveCarryHelper.beginMovementCollection();
|
||||
|
||||
// Handle delay
|
||||
int delay = effect.getDelay();
|
||||
if (delay > 0) {
|
||||
// Schedule delayed execution
|
||||
scheduleDelayedEffect(effect, ctx, delay, currentTime);
|
||||
} else {
|
||||
// Execute immediately
|
||||
ctx.state().step();
|
||||
try {
|
||||
effect.execute(ctx);
|
||||
|
||||
// Activate box animation after execution
|
||||
if (effect instanceof InteractionWiredEffect) {
|
||||
InteractionWiredEffect wiredEffect = (InteractionWiredEffect) effect;
|
||||
wiredEffect.setCooldown(currentTime);
|
||||
wiredEffect.activateBox(ctx.room(), ctx.actor().orElse(null), currentTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Error executing effect: {}", e.getMessage());
|
||||
try {
|
||||
// Execute selected effects
|
||||
for (IWiredEffect effect : toExecute) {
|
||||
// Check if effect requires actor
|
||||
if (effect.requiresActor() && !ctx.hasActor()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle delay
|
||||
int delay = effect.getDelay();
|
||||
if (delay > 0) {
|
||||
// Schedule delayed execution
|
||||
scheduleDelayedEffect(effect, ctx, delay, currentTime);
|
||||
} else {
|
||||
// Execute immediately
|
||||
ctx.state().step();
|
||||
try {
|
||||
effect.execute(ctx);
|
||||
|
||||
// Activate box animation after execution
|
||||
if (effect instanceof InteractionWiredEffect) {
|
||||
InteractionWiredEffect wiredEffect = (InteractionWiredEffect) effect;
|
||||
wiredEffect.setCooldown(currentTime);
|
||||
wiredEffect.activateBox(ctx.room(), ctx.actor().orElse(null), currentTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Error executing effect: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ServerMessage movementComposer = WiredMoveCarryHelper.finishMovementCollection();
|
||||
if (movementComposer != null) {
|
||||
ctx.room().sendComposer(movementComposer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -561,21 +682,50 @@ public final class WiredEngine {
|
||||
|
||||
int furniLimit = Integer.MAX_VALUE;
|
||||
int userLimit = Integer.MAX_VALUE;
|
||||
List<WiredExtraFilterFurniByVariable> furniVariableFilters = new ArrayList<>();
|
||||
List<WiredExtraFilterUsersByVariable> userVariableFilters = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extra instanceof WiredExtraFilterFurni) {
|
||||
furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount());
|
||||
} else if (extra instanceof WiredExtraFilterUser) {
|
||||
userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount());
|
||||
} else if (extra instanceof WiredExtraFilterFurniByVariable) {
|
||||
furniVariableFilters.add((WiredExtraFilterFurniByVariable) extra);
|
||||
} else if (extra instanceof WiredExtraFilterUsersByVariable) {
|
||||
userVariableFilters.add((WiredExtraFilterUsersByVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) {
|
||||
ctx.targets().setItems(limitIterable(ctx.targets().items(), furniLimit));
|
||||
furniVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
userVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
|
||||
if (ctx.targets().isItemsModifiedBySelector()) {
|
||||
Iterable<HabboItem> filteredItems = ctx.targets().items();
|
||||
|
||||
for (WiredExtraFilterFurniByVariable extra : furniVariableFilters) {
|
||||
filteredItems = extra.filterItems(room, ctx, filteredItems);
|
||||
}
|
||||
|
||||
if (furniLimit != Integer.MAX_VALUE) {
|
||||
filteredItems = limitIterable(filteredItems, furniLimit);
|
||||
}
|
||||
|
||||
ctx.targets().setItems(filteredItems);
|
||||
}
|
||||
|
||||
if (ctx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) {
|
||||
ctx.targets().setUsers(limitIterable(ctx.targets().users(), userLimit));
|
||||
if (ctx.targets().isUsersModifiedBySelector()) {
|
||||
Iterable<RoomUnit> filteredUsers = ctx.targets().users();
|
||||
|
||||
for (WiredExtraFilterUsersByVariable extra : userVariableFilters) {
|
||||
filteredUsers = extra.filterUsers(room, ctx, filteredUsers);
|
||||
}
|
||||
|
||||
if (userLimit != Integer.MAX_VALUE) {
|
||||
filteredUsers = limitIterable(filteredUsers, userLimit);
|
||||
}
|
||||
|
||||
ctx.targets().setUsers(filteredUsers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,6 +754,19 @@ public final class WiredEngine {
|
||||
* Schedule a delayed effect execution.
|
||||
*/
|
||||
private void scheduleDelayedEffect(IWiredEffect effect, WiredContext ctx, int delay, long triggerTime) {
|
||||
WiredRoomDiagnostics diagnostics = getDiagnostics(ctx.room().getId());
|
||||
String sourceLabel = getMonitorSourceLabel(ctx.triggerItem(), ctx.event());
|
||||
int sourceId = getMonitorSourceId(ctx.triggerItem());
|
||||
|
||||
if (!diagnostics.tryScheduleDelayedEvent(
|
||||
System.currentTimeMillis(),
|
||||
sourceLabel,
|
||||
sourceId,
|
||||
String.format("Scheduling delayed effect %s with delay %d tick(s)", effect.getClass().getSimpleName(), delay))) {
|
||||
debug(ctx.room(), "Delayed events cap blocked effect {}", effect.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Delay is in 500ms ticks
|
||||
long delayMs = delay * 500L;
|
||||
long elapsedSinceTrigger = Math.max(0L, System.currentTimeMillis() - triggerTime);
|
||||
@@ -613,6 +776,7 @@ public final class WiredEngine {
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
if (!room.isLoaded() || room.getHabbos().isEmpty()) {
|
||||
diagnostics.completeDelayedEvent();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -627,6 +791,8 @@ public final class WiredEngine {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Error executing delayed effect: {}", e.getMessage());
|
||||
} finally {
|
||||
diagnostics.completeDelayedEvent();
|
||||
}
|
||||
}, remainingDelayMs);
|
||||
}
|
||||
@@ -712,6 +878,19 @@ public final class WiredEngine {
|
||||
}
|
||||
|
||||
private void scheduleOrderedEffectBatch(List<IWiredEffect> batch, WiredContext ctx, int delay, long triggerTime) {
|
||||
WiredRoomDiagnostics diagnostics = getDiagnostics(ctx.room().getId());
|
||||
String sourceLabel = getMonitorSourceLabel(ctx.triggerItem(), ctx.event());
|
||||
int sourceId = getMonitorSourceId(ctx.triggerItem());
|
||||
|
||||
if (!diagnostics.tryScheduleDelayedEvent(
|
||||
System.currentTimeMillis(),
|
||||
sourceLabel,
|
||||
sourceId,
|
||||
String.format("Scheduling ordered batch with %d effect(s) and delay %d tick(s)", batch.size(), delay))) {
|
||||
debug(ctx.room(), "Delayed events cap blocked ordered batch with {} effect(s)", batch.size());
|
||||
return;
|
||||
}
|
||||
|
||||
long delayMs = delay * 500L;
|
||||
long elapsedSinceTrigger = Math.max(0L, System.currentTimeMillis() - triggerTime);
|
||||
long remainingDelayMs = Math.max(0L, delayMs - elapsedSinceTrigger);
|
||||
@@ -719,10 +898,15 @@ public final class WiredEngine {
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
if (!room.isLoaded() || room.getHabbos().isEmpty()) {
|
||||
diagnostics.completeDelayedEvent();
|
||||
return;
|
||||
}
|
||||
|
||||
executeOrderedEffectBatch(batch, ctx, System.currentTimeMillis(), true);
|
||||
try {
|
||||
executeOrderedEffectBatch(batch, ctx, System.currentTimeMillis(), true);
|
||||
} finally {
|
||||
diagnostics.completeDelayedEvent();
|
||||
}
|
||||
}, remainingDelayMs);
|
||||
}
|
||||
|
||||
@@ -730,21 +914,30 @@ public final class WiredEngine {
|
||||
Room room = ctx.room();
|
||||
RoomUnit actor = ctx.actor().orElse(null);
|
||||
|
||||
for (IWiredEffect effect : batch) {
|
||||
try {
|
||||
if (!useExecutionTimeForCooldown) {
|
||||
ctx.state().step();
|
||||
}
|
||||
WiredMoveCarryHelper.beginMovementCollection();
|
||||
|
||||
effect.execute(ctx);
|
||||
try {
|
||||
for (IWiredEffect effect : batch) {
|
||||
try {
|
||||
if (!useExecutionTimeForCooldown) {
|
||||
ctx.state().step();
|
||||
}
|
||||
|
||||
if (effect instanceof InteractionWiredEffect) {
|
||||
InteractionWiredEffect wiredEffect = (InteractionWiredEffect) effect;
|
||||
wiredEffect.setCooldown(executionTime);
|
||||
wiredEffect.activateBox(room, actor, executionTime);
|
||||
effect.execute(ctx);
|
||||
|
||||
if (effect instanceof InteractionWiredEffect) {
|
||||
InteractionWiredEffect wiredEffect = (InteractionWiredEffect) effect;
|
||||
wiredEffect.setCooldown(executionTime);
|
||||
wiredEffect.activateBox(room, actor, executionTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Error executing ordered effect batch item: {}", e.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Error executing ordered effect batch item: {}", e.getMessage());
|
||||
}
|
||||
} finally {
|
||||
ServerMessage movementComposer = WiredMoveCarryHelper.finishMovementCollection();
|
||||
if (movementComposer != null) {
|
||||
room.sendComposer(movementComposer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -961,6 +1154,29 @@ public final class WiredEngine {
|
||||
String prefix = roomId + ":";
|
||||
eventRateLimiters.keySet().removeIf(key -> key.startsWith(prefix));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear monitor diagnostics for a specific room.
|
||||
* @param roomId the room ID
|
||||
*/
|
||||
public void clearRoomDiagnostics(int roomId) {
|
||||
roomDiagnostics.remove(roomId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all monitor diagnostics.
|
||||
*/
|
||||
public void clearAllDiagnostics() {
|
||||
roomDiagnostics.clear();
|
||||
}
|
||||
|
||||
public void clearRoomDiagnosticsLogs(int roomId) {
|
||||
WiredRoomDiagnostics diagnostics = roomDiagnostics.get(roomId);
|
||||
|
||||
if (diagnostics != null) {
|
||||
diagnostics.clearLogs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear room ban for a specific room.
|
||||
@@ -970,6 +1186,23 @@ public final class WiredEngine {
|
||||
public void clearRoomBan(int roomId) {
|
||||
bannedRooms.remove(roomId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a monitor snapshot for a room.
|
||||
* @param roomId the room ID
|
||||
* @return the diagnostics snapshot
|
||||
*/
|
||||
public WiredRoomDiagnostics.Snapshot getDiagnosticsSnapshot(int roomId) {
|
||||
long now = System.currentTimeMillis();
|
||||
long killedUntil = bannedRooms.getOrDefault(roomId, 0L);
|
||||
|
||||
return getDiagnostics(roomId).snapshot(
|
||||
getRecursionDepth(roomId),
|
||||
MAX_RECURSION_DEPTH,
|
||||
killedUntil,
|
||||
now
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a room is currently banned from wired execution.
|
||||
@@ -997,9 +1230,15 @@ public final class WiredEngine {
|
||||
* @param roomId the room ID
|
||||
* @param room the room object (for sending alerts)
|
||||
*/
|
||||
private void banRoom(int roomId, Room room) {
|
||||
private void banRoom(int roomId, Room room, WiredEvent.Type eventType, int eventCount) {
|
||||
long banExpiry = System.currentTimeMillis() + WIRED_BAN_DURATION_MS;
|
||||
bannedRooms.put(roomId, banExpiry);
|
||||
getDiagnostics(roomId).recordKilled(
|
||||
System.currentTimeMillis(),
|
||||
String.format("Rate limit exceeded for %s with %d event(s) in %dms", eventType.name(), eventCount, RATE_LIMIT_WINDOW_MS),
|
||||
eventType.name(),
|
||||
0
|
||||
);
|
||||
|
||||
long banMinutes = WIRED_BAN_DURATION_MS / 60000;
|
||||
|
||||
@@ -1049,10 +1288,109 @@ public final class WiredEngine {
|
||||
boolean limited = tracker.isRateLimited(now);
|
||||
if (limited && tracker.shouldBan(now)) {
|
||||
// First time hitting limit in this suppression window - ban the room
|
||||
banRoom(roomId, room);
|
||||
banRoom(roomId, room, eventType, tracker.getEventCount());
|
||||
}
|
||||
return limited;
|
||||
}
|
||||
|
||||
private WiredRoomDiagnostics getDiagnostics(int roomId) {
|
||||
return roomDiagnostics.computeIfAbsent(roomId, ignored -> new WiredRoomDiagnostics(
|
||||
MONITOR_USAGE_WINDOW_MS,
|
||||
MONITOR_USAGE_LIMIT,
|
||||
MONITOR_DELAYED_EVENTS_LIMIT,
|
||||
MONITOR_OVERLOAD_AVERAGE_MS,
|
||||
MONITOR_OVERLOAD_PEAK_MS,
|
||||
MONITOR_HEAVY_USAGE_PERCENT,
|
||||
MONITOR_HEAVY_CONSECUTIVE_WINDOWS,
|
||||
MONITOR_OVERLOAD_CONSECUTIVE_WINDOWS,
|
||||
MONITOR_HEAVY_DELAYED_PERCENT,
|
||||
200
|
||||
));
|
||||
}
|
||||
|
||||
private int estimateStackCost(WiredStack stack, int recursionDepth) {
|
||||
int cost = 1;
|
||||
|
||||
if (stack == null) {
|
||||
return cost;
|
||||
}
|
||||
|
||||
cost += Math.max(0, stack.conditions().size());
|
||||
|
||||
for (IWiredEffect effect : stack.effects()) {
|
||||
if (effect == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cost += effect.isSelector() ? 2 : 3;
|
||||
|
||||
if (effect.getDelay() > 0) {
|
||||
cost += 4;
|
||||
}
|
||||
}
|
||||
|
||||
cost += Math.max(0, recursionDepth) * 2;
|
||||
|
||||
return Math.max(1, cost);
|
||||
}
|
||||
|
||||
private String getMonitorSourceLabel(HabboItem triggerItem, WiredEvent event) {
|
||||
if (triggerItem != null && triggerItem.getBaseItem() != null && triggerItem.getBaseItem().getInteractionType() != null) {
|
||||
return triggerItem.getBaseItem().getInteractionType().getName();
|
||||
}
|
||||
|
||||
return (event != null && event.getType() != null) ? event.getType().name() : "room";
|
||||
}
|
||||
|
||||
private int getMonitorSourceId(HabboItem triggerItem) {
|
||||
return triggerItem != null ? triggerItem.getId() : 0;
|
||||
}
|
||||
|
||||
private String buildStackMonitorReason(WiredStack stack, WiredEvent event, int stackCost) {
|
||||
if (stack == null) {
|
||||
return String.format("Processing %s with estimated cost %d", event.getType().name(), stackCost);
|
||||
}
|
||||
|
||||
int selectors = 0;
|
||||
int delayedEffects = 0;
|
||||
|
||||
for (IWiredEffect effect : stack.effects()) {
|
||||
if (effect == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (effect.isSelector()) {
|
||||
selectors++;
|
||||
}
|
||||
|
||||
if (effect.getDelay() > 0) {
|
||||
delayedEffects++;
|
||||
}
|
||||
}
|
||||
|
||||
return String.format(
|
||||
"Trigger %s with %d condition(s), %d effect(s), %d selector(s), %d delayed effect(s) and estimated cost %d",
|
||||
event.getType().name(),
|
||||
stack.conditions().size(),
|
||||
stack.effects().size(),
|
||||
selectors,
|
||||
delayedEffects,
|
||||
stackCost
|
||||
);
|
||||
}
|
||||
|
||||
private String buildExecutionMonitorReason(WiredStack stack, long elapsedMs) {
|
||||
if (stack == null) {
|
||||
return String.format("Execution completed in %dms", elapsedMs);
|
||||
}
|
||||
|
||||
return String.format(
|
||||
"Stack with %d condition(s) and %d effect(s) completed in %dms",
|
||||
stack.conditions().size(),
|
||||
stack.effects().size(),
|
||||
elapsedMs
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks event rate for a specific room + event type combination.
|
||||
@@ -1094,5 +1432,9 @@ public final class WiredEngine {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized int getEventCount() {
|
||||
return eventCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,9 @@ public final class WiredEvent {
|
||||
|
||||
/** Up-counter reaches a configured elapsed time */
|
||||
CLOCK_COUNTER_REACHED(WiredTriggerType.CLOCK_COUNTER),
|
||||
|
||||
/** A user, furni or global variable changed */
|
||||
VARIABLE_CHANGED(WiredTriggerType.VARIABLE_CHANGED),
|
||||
|
||||
/** Long timer repeat */
|
||||
TIMER_REPEAT_LONG(WiredTriggerType.PERIODICALLY_LONG),
|
||||
@@ -150,6 +153,13 @@ public final class WiredEvent {
|
||||
}
|
||||
}
|
||||
|
||||
public enum VariableChangeKind {
|
||||
NONE,
|
||||
INCREASED,
|
||||
DECREASED,
|
||||
UNCHANGED
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final Room room;
|
||||
private final RoomUnit actor; // nullable - the user/bot that caused the event
|
||||
@@ -164,6 +174,16 @@ public final class WiredEvent {
|
||||
private final int signalChannel; // channel for signal routing (0-based)
|
||||
private final int actionId; // user action id for USER_PERFORMS_ACTION
|
||||
private final int actionParameter; // sign/dance parameter when relevant
|
||||
private final int chatType; // RoomChatType metadata for USER_SAYS
|
||||
private final int chatStyle; // bubble style for USER_SAYS
|
||||
private final int signalUserCount; // forwarded users in SIGNAL_RECEIVED
|
||||
private final int signalFurniCount; // forwarded furni in SIGNAL_RECEIVED
|
||||
private final int variableTargetType;
|
||||
private final int variableDefinitionItemId;
|
||||
private final boolean variableCreated;
|
||||
private final boolean variableDeleted;
|
||||
private final VariableChangeKind variableChangeKind;
|
||||
private final WiredContextVariableScope contextVariableScope;
|
||||
private final long createdAtMs;
|
||||
|
||||
private WiredEvent(Builder builder) {
|
||||
@@ -181,6 +201,16 @@ public final class WiredEvent {
|
||||
this.signalChannel = builder.signalChannel;
|
||||
this.actionId = builder.actionId;
|
||||
this.actionParameter = builder.actionParameter;
|
||||
this.chatType = builder.chatType;
|
||||
this.chatStyle = builder.chatStyle;
|
||||
this.signalUserCount = builder.signalUserCount;
|
||||
this.signalFurniCount = builder.signalFurniCount;
|
||||
this.variableTargetType = builder.variableTargetType;
|
||||
this.variableDefinitionItemId = builder.variableDefinitionItemId;
|
||||
this.variableCreated = builder.variableCreated;
|
||||
this.variableDeleted = builder.variableDeleted;
|
||||
this.variableChangeKind = builder.variableChangeKind;
|
||||
this.contextVariableScope = builder.contextVariableScope;
|
||||
this.createdAtMs = builder.createdAtMs;
|
||||
}
|
||||
|
||||
@@ -291,6 +321,46 @@ public final class WiredEvent {
|
||||
return actionParameter;
|
||||
}
|
||||
|
||||
public int getChatType() {
|
||||
return chatType;
|
||||
}
|
||||
|
||||
public int getChatStyle() {
|
||||
return chatStyle;
|
||||
}
|
||||
|
||||
public int getSignalUserCount() {
|
||||
return signalUserCount;
|
||||
}
|
||||
|
||||
public int getSignalFurniCount() {
|
||||
return signalFurniCount;
|
||||
}
|
||||
|
||||
public int getVariableTargetType() {
|
||||
return variableTargetType;
|
||||
}
|
||||
|
||||
public int getVariableDefinitionItemId() {
|
||||
return variableDefinitionItemId;
|
||||
}
|
||||
|
||||
public boolean isVariableCreated() {
|
||||
return variableCreated;
|
||||
}
|
||||
|
||||
public boolean isVariableDeleted() {
|
||||
return variableDeleted;
|
||||
}
|
||||
|
||||
public VariableChangeKind getVariableChangeKind() {
|
||||
return variableChangeKind;
|
||||
}
|
||||
|
||||
public WiredContextVariableScope getContextVariableScope() {
|
||||
return contextVariableScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp when this event was created.
|
||||
* @return milliseconds since epoch
|
||||
@@ -348,6 +418,16 @@ public final class WiredEvent {
|
||||
private int signalChannel;
|
||||
private int actionId;
|
||||
private int actionParameter = -1;
|
||||
private int chatType = -1;
|
||||
private int chatStyle = -1;
|
||||
private int signalUserCount;
|
||||
private int signalFurniCount;
|
||||
private int variableTargetType = -1;
|
||||
private int variableDefinitionItemId;
|
||||
private boolean variableCreated;
|
||||
private boolean variableDeleted;
|
||||
private VariableChangeKind variableChangeKind = VariableChangeKind.NONE;
|
||||
private WiredContextVariableScope contextVariableScope;
|
||||
private long createdAtMs = System.currentTimeMillis();
|
||||
|
||||
private Builder(Type type, Room room) {
|
||||
@@ -462,6 +542,56 @@ public final class WiredEvent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder chatType(int chatType) {
|
||||
this.chatType = chatType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder chatStyle(int chatStyle) {
|
||||
this.chatStyle = chatStyle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder signalUserCount(int signalUserCount) {
|
||||
this.signalUserCount = Math.max(0, signalUserCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder signalFurniCount(int signalFurniCount) {
|
||||
this.signalFurniCount = Math.max(0, signalFurniCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder variableTargetType(int variableTargetType) {
|
||||
this.variableTargetType = variableTargetType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder variableDefinitionItemId(int variableDefinitionItemId) {
|
||||
this.variableDefinitionItemId = Math.max(0, variableDefinitionItemId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder variableCreated(boolean variableCreated) {
|
||||
this.variableCreated = variableCreated;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder variableDeleted(boolean variableDeleted) {
|
||||
this.variableDeleted = variableDeleted;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder variableChangeKind(VariableChangeKind variableChangeKind) {
|
||||
this.variableChangeKind = (variableChangeKind != null) ? variableChangeKind : VariableChangeKind.NONE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder contextVariableScope(WiredContextVariableScope contextVariableScope) {
|
||||
this.contextVariableScope = contextVariableScope;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom creation timestamp.
|
||||
* @param createdAtMs milliseconds since epoch
|
||||
|
||||
+554
@@ -0,0 +1,554 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.games.Game;
|
||||
import com.eu.habbo.habbohotel.games.GamePlayer;
|
||||
import com.eu.habbo.habbohotel.games.GameTeam;
|
||||
import com.eu.habbo.habbohotel.games.GameTeamColors;
|
||||
import com.eu.habbo.habbohotel.items.FurnitureType;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.FurnitureMovementError;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomRightLevels;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTileState;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||
import com.eu.habbo.habbohotel.users.DanceType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboGender;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.util.HotelDateTimeUtil;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class WiredInternalVariableSupport {
|
||||
private WiredInternalVariableSupport() {
|
||||
}
|
||||
|
||||
public static String normalizeKey(String key) {
|
||||
if (key == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String normalized = key.trim();
|
||||
|
||||
return switch (normalized) {
|
||||
case "@position.x" -> "@position_x";
|
||||
case "@position.y" -> "@position_y";
|
||||
case "@effect" -> "@effect_id";
|
||||
case "@handitems" -> "@handitem_id";
|
||||
case "@is_mute" -> "@is_muted";
|
||||
case "@teams.red.score" -> "@team_red_score";
|
||||
case "@teams.green.score" -> "@team_green_score";
|
||||
case "@teams.blue.score" -> "@team_blue_score";
|
||||
case "@teams.yellow.score" -> "@team_yellow_score";
|
||||
case "@teams.red.size" -> "@team_red_size";
|
||||
case "@teams.green.size" -> "@team_green_size";
|
||||
case "@teams.blue.size" -> "@team_blue_size";
|
||||
case "@teams.yellow.size" -> "@team_yellow_size";
|
||||
default -> normalized;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean canUseUserDestination(String key) {
|
||||
String normalized = normalizeKey(key);
|
||||
return "@position_x".equals(normalized) || "@position_y".equals(normalized) || "@direction".equals(normalized);
|
||||
}
|
||||
|
||||
public static boolean canUseFurniDestination(String key) {
|
||||
String normalized = normalizeKey(key);
|
||||
return "@state".equals(normalized) || "@position_x".equals(normalized) || "@position_y".equals(normalized)
|
||||
|| "@rotation".equals(normalized) || "@altitude".equals(normalized);
|
||||
}
|
||||
|
||||
public static boolean canUseUserReference(String key) {
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return "@index".equals(normalized) || "@type".equals(normalized) || "@gender".equals(normalized)
|
||||
|| "@level".equals(normalized) || "@achievement_score".equals(normalized) || "@is_hc".equals(normalized)
|
||||
|| "@has_rights".equals(normalized) || "@is_group_admin".equals(normalized) || "@is_owner".equals(normalized)
|
||||
|| "@is_muted".equals(normalized) || "@is_trading".equals(normalized) || "@is_frozen".equals(normalized)
|
||||
|| "@effect_id".equals(normalized) || "@team_score".equals(normalized) || "@team_color".equals(normalized)
|
||||
|| "@team_type".equals(normalized) || "@sign".equals(normalized) || "@dance".equals(normalized)
|
||||
|| "@is_idle".equals(normalized) || "@handitem_id".equals(normalized) || "@position_x".equals(normalized)
|
||||
|| "@position_y".equals(normalized) || "@direction".equals(normalized) || "@altitude".equals(normalized)
|
||||
|| "@favourite_group_id".equals(normalized) || "@room_entry.method".equals(normalized)
|
||||
|| "@room_entry.teleport_id".equals(normalized) || "@user_id".equals(normalized)
|
||||
|| "@bot_id".equals(normalized) || "@pet_id".equals(normalized) || "@pet_owner_id".equals(normalized);
|
||||
}
|
||||
|
||||
public static boolean canUseFurniReference(String key) {
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return "~teleport.target_id".equals(normalized) || "@id".equals(normalized) || "@class_id".equals(normalized)
|
||||
|| "@height".equals(normalized) || "@state".equals(normalized) || "@position_x".equals(normalized)
|
||||
|| "@position_y".equals(normalized) || "@rotation".equals(normalized) || "@altitude".equals(normalized)
|
||||
|| "@is_invisible".equals(normalized) || "@type".equals(normalized) || "@is_stackable".equals(normalized)
|
||||
|| "@can_stand_on".equals(normalized) || "@can_sit_on".equals(normalized) || "@can_lay_on".equals(normalized)
|
||||
|| "@owner_id".equals(normalized) || "@wallitem_offset".equals(normalized)
|
||||
|| "@dimensions.x".equals(normalized) || "@dimensions.y".equals(normalized);
|
||||
}
|
||||
|
||||
public static boolean canUseRoomReference(String key) {
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return "@furni_count".equals(normalized) || "@user_count".equals(normalized) || "@wired_timer".equals(normalized)
|
||||
|| "@team_red_score".equals(normalized) || "@team_green_score".equals(normalized) || "@team_blue_score".equals(normalized)
|
||||
|| "@team_yellow_score".equals(normalized) || "@team_red_size".equals(normalized) || "@team_green_size".equals(normalized)
|
||||
|| "@team_blue_size".equals(normalized) || "@team_yellow_size".equals(normalized) || "@room_id".equals(normalized)
|
||||
|| "@group_id".equals(normalized) || "@timezone_server".equals(normalized) || "@timezone_client".equals(normalized)
|
||||
|| "@current_time".equals(normalized) || "@current_time.millisecond_of_second".equals(normalized)
|
||||
|| "@current_time.seconds_of_minute".equals(normalized) || "@current_time.minute_of_hour".equals(normalized)
|
||||
|| "@current_time.hour_of_day".equals(normalized) || "@current_time.day_of_week".equals(normalized)
|
||||
|| "@current_time.day_of_month".equals(normalized) || "@current_time.day_of_year".equals(normalized)
|
||||
|| "@current_time.week_of_year".equals(normalized) || "@current_time.month_of_year".equals(normalized)
|
||||
|| "@current_time.year".equals(normalized);
|
||||
}
|
||||
|
||||
public static boolean canUseContextReference(String key) {
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return "@selector_furni_count".equals(normalized) || "@selector_user_count".equals(normalized)
|
||||
|| "@signal_furni_count".equals(normalized) || "@signal_user_count".equals(normalized)
|
||||
|| "@antenna_id".equals(normalized) || "@chat_type".equals(normalized) || "@chat_style".equals(normalized);
|
||||
}
|
||||
|
||||
public static boolean hasUserValue(Room room, RoomUnit roomUnit, String key) {
|
||||
if (room == null || roomUnit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
Bot bot = room.getBot(roomUnit);
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return switch (normalized) {
|
||||
case "@index", "@type", "@level", "@achievement_score", "@position_x", "@position_y", "@direction", "@altitude" -> true;
|
||||
case "@gender" -> habbo != null || bot != null;
|
||||
case "@is_hc" -> habbo != null && habbo.getHabboStats().hasActiveClub();
|
||||
case "@has_rights" -> habbo != null && (room.hasRights(habbo) || room.getGuildRightLevel(habbo).isEqualOrGreaterThan(RoomRightLevels.GUILD_RIGHTS));
|
||||
case "@is_group_admin" -> habbo != null && room.getGuildRightLevel(habbo).isEqualOrGreaterThan(RoomRightLevels.GUILD_ADMIN);
|
||||
case "@is_owner" -> habbo != null && room.isOwner(habbo);
|
||||
case "@is_muted" -> (habbo != null && room.isMuted(habbo)) || (pet != null && pet.isMuted());
|
||||
case "@is_trading" -> habbo != null && room.getActiveTradeForHabbo(habbo) != null;
|
||||
case "@is_frozen" -> WiredFreezeUtil.isFrozen(roomUnit);
|
||||
case "@effect_id" -> roomUnit.getEffectId() > 0;
|
||||
case "@team_score", "@team_color", "@team_type" -> getTeamEffectData(roomUnit.getEffectId()) != null;
|
||||
case "@sign" -> roomUnit.hasStatus(RoomUnitStatus.SIGN);
|
||||
case "@dance" -> roomUnit.getDanceType() != null && roomUnit.getDanceType() != DanceType.NONE;
|
||||
case "@is_idle" -> roomUnit.isIdle();
|
||||
case "@handitem_id" -> roomUnit.getHandItem() > 0;
|
||||
case "@favourite_group_id" -> habbo != null && habbo.getHabboStats().guild > 0;
|
||||
case "@room_entry.method" -> habbo != null && hasRoomEntryMethod(habbo);
|
||||
case "@room_entry.teleport_id" -> habbo != null && habbo.getHabboInfo().getRoomEntryTeleportId() > 0;
|
||||
case "@user_id" -> habbo != null;
|
||||
case "@bot_id" -> bot != null;
|
||||
case "@pet_id" -> pet != null;
|
||||
case "@pet_owner_id" -> pet != null && pet.getUserId() > 0;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean hasFurniValue(HabboItem item, String key) {
|
||||
if (item == null || item.getBaseItem() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return switch (normalized) {
|
||||
case "@id", "@class_id", "@height", "@state", "@position_x", "@position_y", "@rotation", "@altitude",
|
||||
"@is_invisible", "@type", "@owner_id", "@dimensions.x", "@dimensions.y" -> true;
|
||||
case "~teleport.target_id" -> item.getTeleportTargetId() > 0;
|
||||
case "@wallitem_offset" -> item.getBaseItem().getType() == FurnitureType.WALL;
|
||||
case "@is_stackable" -> item.getBaseItem().allowStack();
|
||||
case "@can_stand_on" -> item.getBaseItem().allowWalk();
|
||||
case "@can_sit_on" -> item.getBaseItem().allowSit();
|
||||
case "@can_lay_on" -> item.getBaseItem().allowLay();
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean hasRoomValue(Room room, String key) {
|
||||
return room != null && canUseRoomReference(key);
|
||||
}
|
||||
|
||||
public static Integer readUserValue(Room room, RoomUnit roomUnit, String key) {
|
||||
if (room == null || roomUnit == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
Bot bot = room.getBot(roomUnit);
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return switch (normalized) {
|
||||
case "@index" -> roomUnit.getId();
|
||||
case "@type" -> getUserTypeValue(habbo, bot, pet);
|
||||
case "@gender" -> getGenderValue(habbo, bot);
|
||||
case "@level" -> (roomUnit.getRightsLevel() != null) ? roomUnit.getRightsLevel().level : 0;
|
||||
case "@achievement_score" -> (habbo != null) ? habbo.getHabboStats().getAchievementScore() : null;
|
||||
case "@is_hc" -> (habbo != null && habbo.getHabboStats().hasActiveClub()) ? 1 : 0;
|
||||
case "@has_rights" -> (habbo != null && (room.hasRights(habbo) || room.getGuildRightLevel(habbo).isEqualOrGreaterThan(RoomRightLevels.GUILD_RIGHTS))) ? 1 : 0;
|
||||
case "@is_group_admin" -> (habbo != null && room.getGuildRightLevel(habbo).isEqualOrGreaterThan(RoomRightLevels.GUILD_ADMIN)) ? 1 : 0;
|
||||
case "@is_owner" -> (habbo != null && room.isOwner(habbo)) ? 1 : 0;
|
||||
case "@is_muted" -> ((habbo != null && room.isMuted(habbo)) || (pet != null && pet.isMuted())) ? 1 : 0;
|
||||
case "@is_trading" -> (habbo != null && room.getActiveTradeForHabbo(habbo) != null) ? 1 : 0;
|
||||
case "@is_frozen" -> WiredFreezeUtil.isFrozen(roomUnit) ? 1 : 0;
|
||||
case "@effect_id" -> roomUnit.getEffectId();
|
||||
case "@team_score" -> getUserTeamScore(room, habbo);
|
||||
case "@team_color" -> getTeamColorId(roomUnit.getEffectId());
|
||||
case "@team_type" -> getTeamTypeId(roomUnit.getEffectId());
|
||||
case "@sign" -> parseStatusInteger(roomUnit, RoomUnitStatus.SIGN);
|
||||
case "@dance" -> (roomUnit.getDanceType() != null) ? roomUnit.getDanceType().getType() : 0;
|
||||
case "@is_idle" -> roomUnit.isIdle() ? 1 : 0;
|
||||
case "@handitem_id" -> roomUnit.getHandItem();
|
||||
case "@position_x" -> (int) roomUnit.getX();
|
||||
case "@position_y" -> (int) roomUnit.getY();
|
||||
case "@direction" -> (roomUnit.getBodyRotation() != null) ? (int) roomUnit.getBodyRotation().getValue() : 0;
|
||||
case "@altitude" -> (int) Math.round(roomUnit.getZ() * 100);
|
||||
case "@favourite_group_id" -> (habbo != null) ? habbo.getHabboStats().guild : null;
|
||||
case "@room_entry.method" -> getRoomEntryMethodValue(habbo);
|
||||
case "@room_entry.teleport_id" -> (habbo != null) ? habbo.getHabboInfo().getRoomEntryTeleportId() : null;
|
||||
case "@user_id" -> (habbo != null) ? habbo.getHabboInfo().getId() : null;
|
||||
case "@bot_id" -> (bot != null) ? bot.getId() : null;
|
||||
case "@pet_id" -> (pet != null) ? pet.getId() : null;
|
||||
case "@pet_owner_id" -> (pet != null) ? pet.getUserId() : null;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean writeUserValue(Room room, RoomUnit roomUnit, String key, int value) {
|
||||
if (room == null || roomUnit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return switch (normalized) {
|
||||
case "@position_x" -> moveUserTo(room, roomUnit, value, roomUnit.getY());
|
||||
case "@position_y" -> moveUserTo(room, roomUnit, roomUnit.getX(), value);
|
||||
case "@direction" -> {
|
||||
RoomUserRotation rotation = RoomUserRotation.fromValue(value);
|
||||
yield WiredUserMovementHelper.updateUserDirection(room, roomUnit, rotation, rotation);
|
||||
}
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public static Integer readFurniValue(Room room, HabboItem item, String key) {
|
||||
if (room == null || item == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return switch (normalized) {
|
||||
case "~teleport.target_id" -> item.getTeleportTargetId();
|
||||
case "@id" -> item.getId();
|
||||
case "@class_id" -> (item.getBaseItem() != null) ? item.getBaseItem().getId() : null;
|
||||
case "@height" -> (item.getBaseItem() != null) ? (int) Math.round(item.getBaseItem().getHeight() * 100) : null;
|
||||
case "@state" -> parseInteger(item.getExtradata());
|
||||
case "@position_x" -> (int) item.getX();
|
||||
case "@position_y" -> (int) item.getY();
|
||||
case "@rotation" -> item.getRotation();
|
||||
case "@altitude" -> (int) Math.round(item.getZ() * 100);
|
||||
case "@is_invisible" -> 0;
|
||||
case "@type" -> 0;
|
||||
case "@is_stackable" -> (item.getBaseItem() != null && item.getBaseItem().allowStack()) ? 1 : 0;
|
||||
case "@can_stand_on" -> (item.getBaseItem() != null && item.getBaseItem().allowWalk()) ? 1 : 0;
|
||||
case "@can_sit_on" -> (item.getBaseItem() != null && item.getBaseItem().allowSit()) ? 1 : 0;
|
||||
case "@can_lay_on" -> (item.getBaseItem() != null && item.getBaseItem().allowLay()) ? 1 : 0;
|
||||
case "@wallitem_offset" -> ((item.getBaseItem() != null) && item.getBaseItem().getType() == FurnitureType.WALL && item.getWallPosition() != null && !item.getWallPosition().trim().isEmpty()) ? 1 : 0;
|
||||
case "@dimensions.x" -> (item.getBaseItem() != null) ? (int) item.getBaseItem().getWidth() : null;
|
||||
case "@dimensions.y" -> (item.getBaseItem() != null) ? (int) item.getBaseItem().getLength() : null;
|
||||
case "@owner_id" -> item.getUserId();
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean writeFurniValue(Room room, HabboItem item, String key, int value) {
|
||||
if (room == null || item == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
if ("@state".equals(normalized)) {
|
||||
item.setExtradata(String.valueOf(value));
|
||||
room.updateItemState(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getBaseItem() == null || item.getBaseItem().getType() != FurnitureType.FLOOR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (normalized) {
|
||||
case "@position_x" -> moveFurniTo(room, item, value, item.getY(), item.getRotation(), item.getZ());
|
||||
case "@position_y" -> moveFurniTo(room, item, item.getX(), value, item.getRotation(), item.getZ());
|
||||
case "@rotation" -> moveFurniTo(room, item, item.getX(), item.getY(), value, item.getZ());
|
||||
case "@altitude" -> moveFurniTo(room, item, item.getX(), item.getY(), item.getRotation(), value / 100.0);
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public static Integer readRoomValue(Room room, String key) {
|
||||
if (room == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ZonedDateTime now = HotelDateTimeUtil.now();
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return switch (normalized) {
|
||||
case "@furni_count" -> room.getFloorItems().size() + room.getWallItems().size();
|
||||
case "@user_count" -> room.getUserCount();
|
||||
case "@wired_timer" -> (int) (WiredManager.getTickService().getTickCount() / 10L);
|
||||
case "@team_red_score" -> getTeamMetric(room, GameTeamColors.RED, true);
|
||||
case "@team_green_score" -> getTeamMetric(room, GameTeamColors.GREEN, true);
|
||||
case "@team_blue_score" -> getTeamMetric(room, GameTeamColors.BLUE, true);
|
||||
case "@team_yellow_score" -> getTeamMetric(room, GameTeamColors.YELLOW, true);
|
||||
case "@team_red_size" -> getTeamMetric(room, GameTeamColors.RED, false);
|
||||
case "@team_green_size" -> getTeamMetric(room, GameTeamColors.GREEN, false);
|
||||
case "@team_blue_size" -> getTeamMetric(room, GameTeamColors.BLUE, false);
|
||||
case "@team_yellow_size" -> getTeamMetric(room, GameTeamColors.YELLOW, false);
|
||||
case "@room_id" -> room.getId();
|
||||
case "@group_id" -> room.getGuildId();
|
||||
case "@timezone_server" -> now.getOffset().getTotalSeconds() / 60;
|
||||
case "@timezone_client" -> 0;
|
||||
case "@current_time" -> (int) now.toEpochSecond();
|
||||
case "@current_time.millisecond_of_second" -> now.getNano() / 1_000_000;
|
||||
case "@current_time.seconds_of_minute" -> now.getSecond();
|
||||
case "@current_time.minute_of_hour" -> now.getMinute();
|
||||
case "@current_time.hour_of_day" -> now.getHour();
|
||||
case "@current_time.day_of_week" -> now.getDayOfWeek().getValue();
|
||||
case "@current_time.day_of_month" -> now.getDayOfMonth();
|
||||
case "@current_time.day_of_year" -> now.getDayOfYear();
|
||||
case "@current_time.week_of_year" -> now.get(WeekFields.of(Locale.ITALY).weekOfWeekBasedYear());
|
||||
case "@current_time.month_of_year" -> now.getMonthValue();
|
||||
case "@current_time.year" -> now.getYear();
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public static Integer readContextValue(WiredContext ctx, String key) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String normalized = normalizeKey(key);
|
||||
|
||||
return switch (normalized) {
|
||||
case "@selector_furni_count" -> countIterable(ctx.targets() != null ? ctx.targets().items() : null);
|
||||
case "@selector_user_count" -> countIterable(ctx.targets() != null ? ctx.targets().users() : null);
|
||||
case "@signal_furni_count" -> ctx.event().getSignalFurniCount();
|
||||
case "@signal_user_count" -> ctx.event().getSignalUserCount();
|
||||
case "@antenna_id" -> ctx.event().getSignalChannel();
|
||||
case "@chat_type" -> ctx.event().getChatType();
|
||||
case "@chat_style" -> ctx.event().getChatStyle();
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static Integer getUserTypeValue(Habbo habbo, Bot bot, Pet pet) {
|
||||
if (habbo != null) return 1;
|
||||
if (pet != null) return 2;
|
||||
if (bot != null) return 4;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Integer getGenderValue(Habbo habbo, Bot bot) {
|
||||
HabboGender gender = null;
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null) {
|
||||
gender = habbo.getHabboInfo().getGender();
|
||||
} else if (bot != null) {
|
||||
gender = bot.getGender();
|
||||
}
|
||||
|
||||
if (gender == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (gender == HabboGender.F) ? 1 : 0;
|
||||
}
|
||||
|
||||
private static Integer getUserTeamScore(Room room, Habbo habbo) {
|
||||
if (room == null || habbo == null || habbo.getHabboInfo() == null || habbo.getHabboInfo().getGamePlayer() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Game game = resolveTeamGame(room, habbo);
|
||||
GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer();
|
||||
|
||||
if (game == null || gamePlayer.getTeamColor() == null) {
|
||||
return gamePlayer.getScore();
|
||||
}
|
||||
|
||||
GameTeam team = game.getTeam(gamePlayer.getTeamColor());
|
||||
return (team != null) ? team.getTotalScore() : gamePlayer.getScore();
|
||||
}
|
||||
|
||||
private static Integer getTeamColorId(int effectId) {
|
||||
TeamEffectData data = getTeamEffectData(effectId);
|
||||
return (data != null) ? data.colorId : null;
|
||||
}
|
||||
|
||||
private static Integer getTeamTypeId(int effectId) {
|
||||
TeamEffectData data = getTeamEffectData(effectId);
|
||||
return (data != null) ? data.typeId : null;
|
||||
}
|
||||
|
||||
private static TeamEffectData getTeamEffectData(int effectId) {
|
||||
if (effectId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (effectId >= 223 && effectId <= 226) return new TeamEffectData(effectId - 222, 0);
|
||||
if (effectId >= 33 && effectId <= 36) return new TeamEffectData(effectId - 32, 1);
|
||||
if (effectId >= 40 && effectId <= 43) return new TeamEffectData(effectId - 39, 2);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getTeamMetric(Room room, GameTeamColors color, boolean score) {
|
||||
Game game = resolveTeamGame(room, null);
|
||||
if (game == null || color == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GameTeam team = game.getTeam(color);
|
||||
if (team == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return score ? team.getTotalScore() : team.getMembers().size();
|
||||
}
|
||||
|
||||
private static Game resolveTeamGame(Room room, Habbo habbo) {
|
||||
if (room == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getCurrentGame() != null) {
|
||||
Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
|
||||
if (game != null) {
|
||||
return game;
|
||||
}
|
||||
}
|
||||
|
||||
Game game = room.getGame(com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame.class);
|
||||
if (game != null) {
|
||||
return game;
|
||||
}
|
||||
|
||||
game = room.getGame(com.eu.habbo.habbohotel.games.freeze.FreezeGame.class);
|
||||
if (game != null) {
|
||||
return game;
|
||||
}
|
||||
|
||||
return room.getGame(com.eu.habbo.habbohotel.games.wired.WiredGame.class);
|
||||
}
|
||||
|
||||
private static boolean hasRoomEntryMethod(Habbo habbo) {
|
||||
if (habbo == null || habbo.getHabboInfo() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String roomEntryMethod = habbo.getHabboInfo().getRoomEntryMethod();
|
||||
return roomEntryMethod != null && !roomEntryMethod.trim().isEmpty() && !"unknown".equalsIgnoreCase(roomEntryMethod);
|
||||
}
|
||||
|
||||
private static Integer getRoomEntryMethodValue(Habbo habbo) {
|
||||
if (habbo == null || habbo.getHabboInfo() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String roomEntryMethod = habbo.getHabboInfo().getRoomEntryMethod();
|
||||
|
||||
if (roomEntryMethod == null || roomEntryMethod.trim().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return switch (roomEntryMethod.trim().toLowerCase(Locale.ROOT)) {
|
||||
case "door" -> 1;
|
||||
case "teleport" -> 2;
|
||||
default -> 3;
|
||||
};
|
||||
}
|
||||
|
||||
private static int parseStatusInteger(RoomUnit roomUnit, RoomUnitStatus status) {
|
||||
if (roomUnit == null || status == null || !roomUnit.hasStatus(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parseInteger(roomUnit.getStatus(status));
|
||||
}
|
||||
|
||||
private static boolean moveUserTo(Room room, RoomUnit roomUnit, int x, int y) {
|
||||
if (room == null || roomUnit == null || room.getLayout() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RoomTile targetTile = room.getLayout().getTile((short) x, (short) y);
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double targetZ = targetTile.getStackHeight() + ((targetTile.state == RoomTileState.SIT) ? -0.5 : 0);
|
||||
return WiredUserMovementHelper.moveUser(room, roomUnit, targetTile, targetZ, roomUnit.getBodyRotation(), roomUnit.getHeadRotation(), 0, true);
|
||||
}
|
||||
|
||||
private static boolean moveFurniTo(Room room, HabboItem item, int x, int y, int rotation, double z) {
|
||||
if (room == null || item == null || room.getLayout() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RoomTile targetTile = room.getLayout().getTile((short) x, (short) y);
|
||||
if (targetTile == null || targetTile.state == RoomTileState.INVALID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FurnitureMovementError error = room.moveFurniTo(item, targetTile, rotation, z, null, true, true);
|
||||
return error == FurnitureMovementError.NONE;
|
||||
}
|
||||
|
||||
private static int parseInteger(String value) {
|
||||
try {
|
||||
return (value == null || value.trim().isEmpty()) ? 0 : Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException exception) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int countIterable(Iterable<?> values) {
|
||||
if (values == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (Object ignored : values) {
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static class TeamEffectData {
|
||||
final int colorId;
|
||||
final int typeId;
|
||||
|
||||
TeamEffectData(int colorId, int typeId) {
|
||||
this.colorId = colorId;
|
||||
this.typeId = typeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,29 +40,21 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Manager class for the new wired engine system.
|
||||
* Manager class for the wired runtime.
|
||||
* <p>
|
||||
* This class serves as the integration point between the emulator and the new
|
||||
* wired engine. It provides static methods for triggering events and manages
|
||||
* the lifecycle of the engine.
|
||||
* WiredManager is now the sole runtime entrypoint for wired execution. Legacy
|
||||
* configuration keys are still read for backwards compatibility with existing
|
||||
* databases, but they no longer switch execution back to {@code WiredHandler}.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <h3>Configuration Options:</h3>
|
||||
* <ul>
|
||||
* <li>{@code wired.engine.enabled} - Enable new engine (parallel mode)</li>
|
||||
* <li>{@code wired.engine.exclusive} - Disable legacy handler when true</li>
|
||||
* <li>{@code wired.engine.enabled} - Compatibility flag kept for old configs</li>
|
||||
* <li>{@code wired.engine.exclusive} - Compatibility flag kept for old configs</li>
|
||||
* <li>{@code wired.engine.maxStepsPerStack} - Loop protection limit</li>
|
||||
* <li>{@code wired.engine.debug} - Verbose logging</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Migration Strategy:</h3>
|
||||
* <ol>
|
||||
* <li>Set {@code wired.engine.enabled=true} to run both engines in parallel</li>
|
||||
* <li>Test thoroughly to ensure identical behavior</li>
|
||||
* <li>Set {@code wired.engine.exclusive=true} to disable legacy engine</li>
|
||||
* <li>Full migration complete - WiredManager is now the only wired engine</li>
|
||||
* </ol>
|
||||
*
|
||||
*
|
||||
* @see WiredEngine
|
||||
* @see WiredEvents
|
||||
*/
|
||||
@@ -80,8 +72,8 @@ public final class WiredManager {
|
||||
public static final String CONFIG_DEBUG = "wired.engine.debug";
|
||||
|
||||
// Defaults
|
||||
private static final boolean DEFAULT_ENABLED = false;
|
||||
private static final boolean DEFAULT_EXCLUSIVE = false;
|
||||
private static final boolean DEFAULT_ENABLED = true;
|
||||
private static final boolean DEFAULT_EXCLUSIVE = true;
|
||||
private static final int DEFAULT_MAX_STEPS = 100;
|
||||
|
||||
/** The singleton engine instance */
|
||||
@@ -117,6 +109,7 @@ public final class WiredManager {
|
||||
|
||||
// Load configuration
|
||||
boolean enabled = Emulator.getConfig().getBoolean(CONFIG_ENABLED, DEFAULT_ENABLED);
|
||||
boolean exclusive = Emulator.getConfig().getBoolean(CONFIG_EXCLUSIVE, DEFAULT_EXCLUSIVE);
|
||||
int maxSteps = Emulator.getConfig().getInt(CONFIG_MAX_STEPS, DEFAULT_MAX_STEPS);
|
||||
boolean debug = Emulator.getConfig().getBoolean(CONFIG_DEBUG, false);
|
||||
|
||||
@@ -138,9 +131,13 @@ public final class WiredManager {
|
||||
WiredTickService.getInstance().start();
|
||||
|
||||
initialized = true;
|
||||
|
||||
LOGGER.info("Wired Manager initialized - enabled: {}, maxSteps: {}, debug: {}",
|
||||
enabled, maxSteps, debug);
|
||||
|
||||
if (!enabled || !exclusive) {
|
||||
LOGGER.warn("wired.engine.enabled / wired.engine.exclusive are now compatibility-only flags. WiredManager runs as the exclusive engine runtime.");
|
||||
}
|
||||
|
||||
LOGGER.info("Wired Manager initialized - exclusive runtime active, maxSteps: {}, debug: {}",
|
||||
maxSteps, debug);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,6 +160,7 @@ public final class WiredManager {
|
||||
|
||||
if (engine != null) {
|
||||
engine.clearUnseenCache();
|
||||
engine.clearAllDiagnostics();
|
||||
}
|
||||
|
||||
initialized = false;
|
||||
@@ -174,7 +172,7 @@ public final class WiredManager {
|
||||
* @return true if enabled
|
||||
*/
|
||||
public static boolean isEnabled() {
|
||||
return Emulator.getConfig().getBoolean(CONFIG_ENABLED, DEFAULT_ENABLED);
|
||||
return initialized && engine != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,7 +180,7 @@ public final class WiredManager {
|
||||
* @return true if exclusive mode
|
||||
*/
|
||||
public static boolean isExclusive() {
|
||||
return Emulator.getConfig().getBoolean(CONFIG_EXCLUSIVE, DEFAULT_EXCLUSIVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,6 +199,27 @@ public final class WiredManager {
|
||||
return stackIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current monitor snapshot for a room.
|
||||
* @param roomId the room ID
|
||||
* @return the diagnostics snapshot, or null if the engine is unavailable
|
||||
*/
|
||||
public static WiredRoomDiagnostics.Snapshot getDiagnosticsSnapshot(int roomId) {
|
||||
if (engine == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return engine.getDiagnosticsSnapshot(roomId);
|
||||
}
|
||||
|
||||
public static void clearDiagnosticsLogs(int roomId) {
|
||||
if (engine == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
engine.clearRoomDiagnosticsLogs(roomId);
|
||||
}
|
||||
|
||||
// ========== Event Triggering Methods ==========
|
||||
|
||||
/**
|
||||
@@ -308,20 +327,28 @@ public final class WiredManager {
|
||||
* Trigger when a user says something.
|
||||
*/
|
||||
public static boolean triggerUserSays(Room room, RoomUnit user, String message) {
|
||||
return triggerUserSays(room, user, message, -1, -1);
|
||||
}
|
||||
|
||||
public static boolean triggerUserSays(Room room, RoomUnit user, String message, int chatType, int chatStyle) {
|
||||
if (!isEnabled() || room == null || user == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WiredEvent event = WiredEvents.userSays(room, user, message);
|
||||
|
||||
WiredEvent event = WiredEvents.userSays(room, user, message, chatType, chatStyle);
|
||||
return handleEvent(event);
|
||||
}
|
||||
|
||||
public static boolean shouldSuppressUserSaysOutput(Room room, RoomUnit user, String message) {
|
||||
return shouldSuppressUserSaysOutput(room, user, message, -1, -1);
|
||||
}
|
||||
|
||||
public static boolean shouldSuppressUserSaysOutput(Room room, RoomUnit user, String message, int chatType, int chatStyle) {
|
||||
if (!isEnabled() || engine == null || room == null || user == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WiredEvent event = WiredEvents.userSays(room, user, message);
|
||||
WiredEvent event = WiredEvents.userSays(room, user, message, chatType, chatStyle);
|
||||
return engine.shouldSuppressUserSaysOutput(event);
|
||||
}
|
||||
|
||||
@@ -361,6 +388,36 @@ public final class WiredManager {
|
||||
return handleEvent(event);
|
||||
}
|
||||
|
||||
public static boolean triggerUserVariableChanged(Room room, int userId, int definitionItemId, boolean created, boolean deleted, WiredEvent.VariableChangeKind changeKind) {
|
||||
if (!isEnabled() || room == null || definitionItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Habbo habbo = room.getHabbo(userId);
|
||||
RoomUnit roomUnit = (habbo != null) ? habbo.getRoomUnit() : null;
|
||||
WiredEvent event = WiredEvents.userVariableChanged(room, roomUnit, definitionItemId, created, deleted, changeKind);
|
||||
return handleEvent(event);
|
||||
}
|
||||
|
||||
public static boolean triggerFurniVariableChanged(Room room, int furniId, int definitionItemId, boolean created, boolean deleted, WiredEvent.VariableChangeKind changeKind) {
|
||||
if (!isEnabled() || room == null || furniId <= 0 || definitionItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HabboItem item = room.getHabboItem(furniId);
|
||||
WiredEvent event = WiredEvents.furniVariableChanged(room, item, definitionItemId, created, deleted, changeKind);
|
||||
return handleEvent(event);
|
||||
}
|
||||
|
||||
public static boolean triggerRoomVariableChanged(Room room, int definitionItemId, WiredEvent.VariableChangeKind changeKind) {
|
||||
if (!isEnabled() || room == null || definitionItemId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WiredEvent event = WiredEvents.roomVariableChanged(room, definitionItemId, changeKind);
|
||||
return handleEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a timer tick.
|
||||
*/
|
||||
@@ -567,8 +624,8 @@ public final class WiredManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger from legacy system for parallel running.
|
||||
* This allows the new engine to run alongside the old one during migration.
|
||||
* Compatibility bridge for code paths that still describe themselves as
|
||||
* legacy-triggered. Execution still goes through the new engine only.
|
||||
*/
|
||||
public static boolean triggerFromLegacy(WiredTriggerType triggerType, RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
if (!isEnabled() || room == null) {
|
||||
@@ -725,6 +782,11 @@ public final class WiredManager {
|
||||
*/
|
||||
public static void unregisterRoomTickables(Room room) {
|
||||
WiredTickService.getInstance().unregisterRoom(room);
|
||||
|
||||
if (room != null) {
|
||||
room.getFurniVariableManager().clearTransientAssignments();
|
||||
room.getRoomVariableManager().clearTransientAssignments();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-1
@@ -176,7 +176,25 @@ public final class WiredMoveCarryHelper {
|
||||
&& !sendUpdates
|
||||
&& oldLocation != null
|
||||
&& (oldLocation.x != targetTile.x || oldLocation.y != targetTile.y || Double.compare(oldZ, movingItem.getZ()) != 0)) {
|
||||
room.sendComposer(new FloorItemOnRollerComposer(movingItem, null, oldLocation, oldZ, targetTile, movingItem.getZ(), 0, room).compose());
|
||||
List<WiredMovementsComposer.MovementData> collectedMovements = COLLECTED_MOVEMENTS.get();
|
||||
|
||||
if (collectedMovements != null) {
|
||||
collectedMovements.add(WiredMovementsComposer.furniMovement(
|
||||
movingItem.getId(),
|
||||
oldLocation.x,
|
||||
oldLocation.y,
|
||||
targetTile.x,
|
||||
targetTile.y,
|
||||
oldZ,
|
||||
movingItem.getZ(),
|
||||
movingItem.getRotation(),
|
||||
WiredMovementsComposer.DEFAULT_DURATION,
|
||||
0,
|
||||
WiredMovementsComposer.FURNI_ANCHOR_NONE,
|
||||
0));
|
||||
} else {
|
||||
room.sendComposer(new FloorItemOnRollerComposer(movingItem, null, oldLocation, oldZ, targetTile, movingItem.getZ(), 0, room).compose());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -386,6 +404,14 @@ public final class WiredMoveCarryHelper {
|
||||
return (extra != null) ? extra.getDurationMs() : fallbackDuration;
|
||||
}
|
||||
|
||||
public static WiredMovementPhysics getUserMovementPhysics(Room room, HabboItem stackItem, WiredContext ctx) {
|
||||
if (room == null || stackItem == null) {
|
||||
return WiredMovementPhysics.NONE;
|
||||
}
|
||||
|
||||
return getMovementPhysics(room, stackItem, null, ctx);
|
||||
}
|
||||
|
||||
public static int resolveMoveStepElapsed(RoomUnit roomUnit) {
|
||||
if (roomUnit == null) {
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,586 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tracks wired monitor data for a single room.
|
||||
*/
|
||||
public final class WiredRoomDiagnostics {
|
||||
|
||||
public enum Type {
|
||||
EXECUTION_CAP,
|
||||
DELAYED_EVENTS_CAP,
|
||||
EXECUTOR_OVERLOAD,
|
||||
MARKED_AS_HEAVY,
|
||||
KILLED,
|
||||
RECURSION_TIMEOUT
|
||||
}
|
||||
|
||||
public enum Severity {
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
|
||||
public static final class LogEntry {
|
||||
private final Type type;
|
||||
private final Severity severity;
|
||||
private int count;
|
||||
private long firstOccurredAtMs;
|
||||
private long lastOccurredAtMs;
|
||||
private String latestReason;
|
||||
private String latestSourceLabel;
|
||||
private int latestSourceId;
|
||||
|
||||
private LogEntry(Type type, Severity severity) {
|
||||
this.type = type;
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
private void record(long now, String reason, String sourceLabel, int sourceId) {
|
||||
if (this.count <= 0) {
|
||||
this.firstOccurredAtMs = now;
|
||||
}
|
||||
|
||||
this.count++;
|
||||
this.lastOccurredAtMs = now;
|
||||
this.latestReason = sanitizeReason(reason);
|
||||
this.latestSourceLabel = sanitizeSourceLabel(sourceLabel);
|
||||
this.latestSourceId = Math.max(0, sourceId);
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Severity getSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public long getFirstOccurredAtMs() {
|
||||
return firstOccurredAtMs;
|
||||
}
|
||||
|
||||
public long getLastOccurredAtMs() {
|
||||
return lastOccurredAtMs;
|
||||
}
|
||||
|
||||
public String getLatestReason() {
|
||||
return latestReason;
|
||||
}
|
||||
|
||||
public String getLatestSourceLabel() {
|
||||
return latestSourceLabel;
|
||||
}
|
||||
|
||||
public int getLatestSourceId() {
|
||||
return latestSourceId;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Snapshot {
|
||||
private final int usageCurrentWindow;
|
||||
private final int usageLimitPerWindow;
|
||||
private final boolean heavy;
|
||||
private final int delayedEventsPending;
|
||||
private final int delayedEventsLimit;
|
||||
private final int averageExecutionMs;
|
||||
private final int peakExecutionMs;
|
||||
private final int recursionDepthCurrent;
|
||||
private final int recursionDepthLimit;
|
||||
private final int killedRemainingSeconds;
|
||||
private final int usageWindowMs;
|
||||
private final int overloadAverageThresholdMs;
|
||||
private final int overloadPeakThresholdMs;
|
||||
private final int heavyUsageThresholdPercent;
|
||||
private final int heavyConsecutiveWindowsThreshold;
|
||||
private final int overloadConsecutiveWindowsThreshold;
|
||||
private final int heavyDelayedThresholdPercent;
|
||||
private final List<LogEntry> logs;
|
||||
private final List<HistoryEntry> history;
|
||||
|
||||
public Snapshot(int usageCurrentWindow, int usageLimitPerWindow, boolean heavy, int delayedEventsPending,
|
||||
int delayedEventsLimit, int averageExecutionMs, int peakExecutionMs,
|
||||
int recursionDepthCurrent, int recursionDepthLimit, int killedRemainingSeconds,
|
||||
int usageWindowMs, int overloadAverageThresholdMs, int overloadPeakThresholdMs,
|
||||
int heavyUsageThresholdPercent, int heavyConsecutiveWindowsThreshold,
|
||||
int overloadConsecutiveWindowsThreshold, int heavyDelayedThresholdPercent,
|
||||
List<LogEntry> logs, List<HistoryEntry> history) {
|
||||
this.usageCurrentWindow = usageCurrentWindow;
|
||||
this.usageLimitPerWindow = usageLimitPerWindow;
|
||||
this.heavy = heavy;
|
||||
this.delayedEventsPending = delayedEventsPending;
|
||||
this.delayedEventsLimit = delayedEventsLimit;
|
||||
this.averageExecutionMs = averageExecutionMs;
|
||||
this.peakExecutionMs = peakExecutionMs;
|
||||
this.recursionDepthCurrent = recursionDepthCurrent;
|
||||
this.recursionDepthLimit = recursionDepthLimit;
|
||||
this.killedRemainingSeconds = killedRemainingSeconds;
|
||||
this.usageWindowMs = usageWindowMs;
|
||||
this.overloadAverageThresholdMs = overloadAverageThresholdMs;
|
||||
this.overloadPeakThresholdMs = overloadPeakThresholdMs;
|
||||
this.heavyUsageThresholdPercent = heavyUsageThresholdPercent;
|
||||
this.heavyConsecutiveWindowsThreshold = heavyConsecutiveWindowsThreshold;
|
||||
this.overloadConsecutiveWindowsThreshold = overloadConsecutiveWindowsThreshold;
|
||||
this.heavyDelayedThresholdPercent = heavyDelayedThresholdPercent;
|
||||
this.logs = Collections.unmodifiableList(logs);
|
||||
this.history = Collections.unmodifiableList(history);
|
||||
}
|
||||
|
||||
public int getUsageCurrentWindow() {
|
||||
return usageCurrentWindow;
|
||||
}
|
||||
|
||||
public int getUsageLimitPerWindow() {
|
||||
return usageLimitPerWindow;
|
||||
}
|
||||
|
||||
public boolean isHeavy() {
|
||||
return heavy;
|
||||
}
|
||||
|
||||
public int getDelayedEventsPending() {
|
||||
return delayedEventsPending;
|
||||
}
|
||||
|
||||
public int getDelayedEventsLimit() {
|
||||
return delayedEventsLimit;
|
||||
}
|
||||
|
||||
public int getAverageExecutionMs() {
|
||||
return averageExecutionMs;
|
||||
}
|
||||
|
||||
public int getPeakExecutionMs() {
|
||||
return peakExecutionMs;
|
||||
}
|
||||
|
||||
public int getRecursionDepthCurrent() {
|
||||
return recursionDepthCurrent;
|
||||
}
|
||||
|
||||
public int getRecursionDepthLimit() {
|
||||
return recursionDepthLimit;
|
||||
}
|
||||
|
||||
public int getKilledRemainingSeconds() {
|
||||
return killedRemainingSeconds;
|
||||
}
|
||||
|
||||
public int getUsageWindowMs() {
|
||||
return usageWindowMs;
|
||||
}
|
||||
|
||||
public int getOverloadAverageThresholdMs() {
|
||||
return overloadAverageThresholdMs;
|
||||
}
|
||||
|
||||
public int getOverloadPeakThresholdMs() {
|
||||
return overloadPeakThresholdMs;
|
||||
}
|
||||
|
||||
public int getHeavyUsageThresholdPercent() {
|
||||
return heavyUsageThresholdPercent;
|
||||
}
|
||||
|
||||
public int getHeavyConsecutiveWindowsThreshold() {
|
||||
return heavyConsecutiveWindowsThreshold;
|
||||
}
|
||||
|
||||
public int getOverloadConsecutiveWindowsThreshold() {
|
||||
return overloadConsecutiveWindowsThreshold;
|
||||
}
|
||||
|
||||
public int getHeavyDelayedThresholdPercent() {
|
||||
return heavyDelayedThresholdPercent;
|
||||
}
|
||||
|
||||
public List<LogEntry> getLogs() {
|
||||
return logs;
|
||||
}
|
||||
|
||||
public List<HistoryEntry> getHistory() {
|
||||
return history;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class HistoryEntry {
|
||||
private final Type type;
|
||||
private final Severity severity;
|
||||
private final long occurredAtMs;
|
||||
private final String reason;
|
||||
private final String sourceLabel;
|
||||
private final int sourceId;
|
||||
|
||||
public HistoryEntry(Type type, Severity severity, long occurredAtMs, String reason, String sourceLabel, int sourceId) {
|
||||
this.type = type;
|
||||
this.severity = severity;
|
||||
this.occurredAtMs = occurredAtMs;
|
||||
this.reason = sanitizeReason(reason);
|
||||
this.sourceLabel = sanitizeSourceLabel(sourceLabel);
|
||||
this.sourceId = Math.max(0, sourceId);
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Severity getSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public long getOccurredAtMs() {
|
||||
return occurredAtMs;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public String getSourceLabel() {
|
||||
return sourceLabel;
|
||||
}
|
||||
|
||||
public int getSourceId() {
|
||||
return sourceId;
|
||||
}
|
||||
}
|
||||
|
||||
private final int usageWindowMs;
|
||||
private final int usageLimitPerWindow;
|
||||
private final int delayedEventsLimit;
|
||||
private final int overloadAverageThresholdMs;
|
||||
private final int overloadPeakThresholdMs;
|
||||
private final int heavyUsageThresholdPercent;
|
||||
private final int heavyConsecutiveWindowsThreshold;
|
||||
private final int overloadConsecutiveWindowsThreshold;
|
||||
private final int heavyDelayedThresholdPercent;
|
||||
private final EnumMap<Type, LogEntry> logs;
|
||||
private final ArrayDeque<HistoryEntry> history;
|
||||
private final int maxHistoryEntries;
|
||||
|
||||
private long windowStartedAt;
|
||||
private int usageCurrentWindow;
|
||||
private int delayedEventsPending;
|
||||
private long totalExecutionMsCurrentWindow;
|
||||
private int executionSamplesCurrentWindow;
|
||||
private int averageExecutionMs;
|
||||
private int peakExecutionMs;
|
||||
private int consecutiveHeavyWindows;
|
||||
private int consecutiveOverloadWindows;
|
||||
private boolean heavy;
|
||||
private String peakExecutionSourceLabel;
|
||||
private int peakExecutionSourceId;
|
||||
private String peakExecutionReason;
|
||||
|
||||
public WiredRoomDiagnostics(int usageWindowMs, int usageLimitPerWindow, int delayedEventsLimit,
|
||||
int overloadAverageThresholdMs, int overloadPeakThresholdMs,
|
||||
int heavyUsageThresholdPercent, int heavyConsecutiveWindowsThreshold) {
|
||||
this(usageWindowMs, usageLimitPerWindow, delayedEventsLimit, overloadAverageThresholdMs, overloadPeakThresholdMs,
|
||||
heavyUsageThresholdPercent, heavyConsecutiveWindowsThreshold, 2, 60, 200);
|
||||
}
|
||||
|
||||
public WiredRoomDiagnostics(int usageWindowMs, int usageLimitPerWindow, int delayedEventsLimit,
|
||||
int overloadAverageThresholdMs, int overloadPeakThresholdMs,
|
||||
int heavyUsageThresholdPercent, int heavyConsecutiveWindowsThreshold,
|
||||
int overloadConsecutiveWindowsThreshold, int heavyDelayedThresholdPercent,
|
||||
int maxHistoryEntries) {
|
||||
this.usageWindowMs = Math.max(250, usageWindowMs);
|
||||
this.usageLimitPerWindow = Math.max(1, usageLimitPerWindow);
|
||||
this.delayedEventsLimit = Math.max(1, delayedEventsLimit);
|
||||
this.overloadAverageThresholdMs = Math.max(1, overloadAverageThresholdMs);
|
||||
this.overloadPeakThresholdMs = Math.max(this.overloadAverageThresholdMs, overloadPeakThresholdMs);
|
||||
this.heavyUsageThresholdPercent = Math.max(1, Math.min(100, heavyUsageThresholdPercent));
|
||||
this.heavyConsecutiveWindowsThreshold = Math.max(1, heavyConsecutiveWindowsThreshold);
|
||||
this.overloadConsecutiveWindowsThreshold = Math.max(1, overloadConsecutiveWindowsThreshold);
|
||||
this.heavyDelayedThresholdPercent = Math.max(1, Math.min(100, heavyDelayedThresholdPercent));
|
||||
this.maxHistoryEntries = Math.max(10, maxHistoryEntries);
|
||||
this.logs = new EnumMap<>(Type.class);
|
||||
this.history = new ArrayDeque<>(this.maxHistoryEntries);
|
||||
|
||||
for (Type type : Type.values()) {
|
||||
this.logs.put(type, new LogEntry(type, defaultSeverity(type)));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean tryConsumeExecutionBudget(int estimatedCost, long now, String sourceLabel, int sourceId, String reason) {
|
||||
rollWindow(now);
|
||||
|
||||
int normalizedCost = Math.max(0, estimatedCost);
|
||||
if ((this.usageCurrentWindow + normalizedCost) > this.usageLimitPerWindow) {
|
||||
record(Type.EXECUTION_CAP, now,
|
||||
buildExecutionCapReason(normalizedCost, reason),
|
||||
sourceLabel,
|
||||
sourceId);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.usageCurrentWindow += normalizedCost;
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean tryScheduleDelayedEvent(long now, String sourceLabel, int sourceId, String reason) {
|
||||
rollWindow(now);
|
||||
|
||||
if ((this.delayedEventsPending + 1) > this.delayedEventsLimit) {
|
||||
record(Type.DELAYED_EVENTS_CAP, now,
|
||||
buildDelayedCapReason(reason),
|
||||
sourceLabel,
|
||||
sourceId);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.delayedEventsPending++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized void completeDelayedEvent() {
|
||||
if (this.delayedEventsPending > 0) {
|
||||
this.delayedEventsPending--;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void recordExecution(long elapsedMs, long now, String sourceLabel, int sourceId, String reason) {
|
||||
rollWindow(now);
|
||||
|
||||
int normalizedElapsed = (int) Math.max(0L, elapsedMs);
|
||||
|
||||
this.totalExecutionMsCurrentWindow += normalizedElapsed;
|
||||
this.executionSamplesCurrentWindow++;
|
||||
this.averageExecutionMs = (int) Math.round(this.totalExecutionMsCurrentWindow / (double) this.executionSamplesCurrentWindow);
|
||||
|
||||
if (normalizedElapsed >= this.peakExecutionMs) {
|
||||
this.peakExecutionMs = normalizedElapsed;
|
||||
this.peakExecutionSourceLabel = sanitizeSourceLabel(sourceLabel);
|
||||
this.peakExecutionSourceId = Math.max(0, sourceId);
|
||||
this.peakExecutionReason = sanitizeReason(reason);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void recordKilled(long now, String reason, String sourceLabel, int sourceId) {
|
||||
rollWindow(now);
|
||||
record(Type.KILLED, now, reason, sourceLabel, sourceId);
|
||||
}
|
||||
|
||||
public synchronized void recordRecursionTimeout(long now, String reason, String sourceLabel, int sourceId) {
|
||||
rollWindow(now);
|
||||
record(Type.RECURSION_TIMEOUT, now, reason, sourceLabel, sourceId);
|
||||
}
|
||||
|
||||
public synchronized void clearLogs() {
|
||||
for (Type type : Type.values()) {
|
||||
LogEntry entry = this.logs.get(type);
|
||||
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.count = 0;
|
||||
entry.firstOccurredAtMs = 0L;
|
||||
entry.lastOccurredAtMs = 0L;
|
||||
entry.latestReason = "";
|
||||
entry.latestSourceLabel = "";
|
||||
entry.latestSourceId = 0;
|
||||
}
|
||||
|
||||
this.history.clear();
|
||||
}
|
||||
|
||||
public synchronized Snapshot snapshot(int recursionDepthCurrent, int recursionDepthLimit, long killedUntilMs, long now) {
|
||||
rollWindow(now);
|
||||
|
||||
List<LogEntry> logEntries = new ArrayList<>(Type.values().length);
|
||||
List<HistoryEntry> historyEntries = new ArrayList<>(this.history.size());
|
||||
|
||||
for (Type type : Type.values()) {
|
||||
LogEntry source = this.logs.get(type);
|
||||
LogEntry copy = new LogEntry(source.getType(), source.getSeverity());
|
||||
|
||||
copy.count = source.getCount();
|
||||
copy.firstOccurredAtMs = source.getFirstOccurredAtMs();
|
||||
copy.lastOccurredAtMs = source.getLastOccurredAtMs();
|
||||
copy.latestReason = source.getLatestReason();
|
||||
copy.latestSourceLabel = source.getLatestSourceLabel();
|
||||
copy.latestSourceId = source.getLatestSourceId();
|
||||
|
||||
logEntries.add(copy);
|
||||
}
|
||||
|
||||
historyEntries.addAll(this.history);
|
||||
|
||||
int killedRemainingSeconds = 0;
|
||||
|
||||
if (killedUntilMs > now) {
|
||||
killedRemainingSeconds = (int) Math.max(0L, Math.ceil((killedUntilMs - now) / 1000D));
|
||||
}
|
||||
|
||||
return new Snapshot(
|
||||
this.usageCurrentWindow,
|
||||
this.usageLimitPerWindow,
|
||||
this.heavy,
|
||||
this.delayedEventsPending,
|
||||
this.delayedEventsLimit,
|
||||
this.averageExecutionMs,
|
||||
this.peakExecutionMs,
|
||||
recursionDepthCurrent,
|
||||
recursionDepthLimit,
|
||||
killedRemainingSeconds,
|
||||
this.usageWindowMs,
|
||||
this.overloadAverageThresholdMs,
|
||||
this.overloadPeakThresholdMs,
|
||||
this.heavyUsageThresholdPercent,
|
||||
this.heavyConsecutiveWindowsThreshold,
|
||||
this.overloadConsecutiveWindowsThreshold,
|
||||
this.heavyDelayedThresholdPercent,
|
||||
logEntries,
|
||||
historyEntries
|
||||
);
|
||||
}
|
||||
|
||||
private void rollWindow(long now) {
|
||||
if (this.windowStartedAt <= 0L) {
|
||||
this.windowStartedAt = now;
|
||||
return;
|
||||
}
|
||||
|
||||
while ((now - this.windowStartedAt) >= this.usageWindowMs) {
|
||||
evaluateWindow(this.windowStartedAt + this.usageWindowMs);
|
||||
this.windowStartedAt += this.usageWindowMs;
|
||||
this.usageCurrentWindow = 0;
|
||||
this.totalExecutionMsCurrentWindow = 0L;
|
||||
this.executionSamplesCurrentWindow = 0;
|
||||
this.averageExecutionMs = 0;
|
||||
this.peakExecutionMs = 0;
|
||||
this.peakExecutionSourceLabel = null;
|
||||
this.peakExecutionSourceId = 0;
|
||||
this.peakExecutionReason = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateWindow(long now) {
|
||||
int usagePercent = (int) Math.round((this.usageCurrentWindow * 100D) / this.usageLimitPerWindow);
|
||||
int delayedPercent = (int) Math.round((this.delayedEventsPending * 100D) / this.delayedEventsLimit);
|
||||
boolean overloadWindow = (this.executionSamplesCurrentWindow > 0)
|
||||
&& ((this.averageExecutionMs >= this.overloadAverageThresholdMs) || (this.peakExecutionMs >= this.overloadPeakThresholdMs));
|
||||
boolean heavyWindow = (usagePercent >= this.heavyUsageThresholdPercent)
|
||||
|| (delayedPercent >= this.heavyDelayedThresholdPercent)
|
||||
|| overloadWindow;
|
||||
|
||||
if (overloadWindow) {
|
||||
this.consecutiveOverloadWindows++;
|
||||
|
||||
if (this.consecutiveOverloadWindows >= this.overloadConsecutiveWindowsThreshold) {
|
||||
record(Type.EXECUTOR_OVERLOAD, now,
|
||||
buildExecutorOverloadReason(),
|
||||
this.peakExecutionSourceLabel,
|
||||
this.peakExecutionSourceId);
|
||||
}
|
||||
} else {
|
||||
this.consecutiveOverloadWindows = 0;
|
||||
}
|
||||
|
||||
if (heavyWindow) {
|
||||
this.consecutiveHeavyWindows++;
|
||||
|
||||
if (!this.heavy && (this.consecutiveHeavyWindows >= this.heavyConsecutiveWindowsThreshold)) {
|
||||
this.heavy = true;
|
||||
record(Type.MARKED_AS_HEAVY, now,
|
||||
buildHeavyReason(usagePercent, delayedPercent, overloadWindow),
|
||||
overloadWindow ? this.peakExecutionSourceLabel : null,
|
||||
overloadWindow ? this.peakExecutionSourceId : 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.consecutiveHeavyWindows = 0;
|
||||
this.heavy = false;
|
||||
}
|
||||
|
||||
private void record(Type type, long now, String reason, String sourceLabel, int sourceId) {
|
||||
LogEntry entry = this.logs.get(type);
|
||||
if (entry != null) {
|
||||
entry.record(now, reason, sourceLabel, sourceId);
|
||||
this.history.addFirst(new HistoryEntry(type, entry.getSeverity(), now, reason, sourceLabel, sourceId));
|
||||
|
||||
while (this.history.size() > this.maxHistoryEntries) {
|
||||
this.history.removeLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildExecutionCapReason(int normalizedCost, String reason) {
|
||||
return joinReason(
|
||||
reason,
|
||||
String.format("Estimated stack cost %d would exceed usage budget %d/%d in %dms window",
|
||||
normalizedCost,
|
||||
this.usageCurrentWindow,
|
||||
this.usageLimitPerWindow,
|
||||
this.usageWindowMs)
|
||||
);
|
||||
}
|
||||
|
||||
private String buildDelayedCapReason(String reason) {
|
||||
return joinReason(
|
||||
reason,
|
||||
String.format("Pending delayed events would exceed queue %d/%d",
|
||||
this.delayedEventsPending,
|
||||
this.delayedEventsLimit)
|
||||
);
|
||||
}
|
||||
|
||||
private String buildExecutorOverloadReason() {
|
||||
return joinReason(
|
||||
this.peakExecutionReason,
|
||||
String.format("Average execution %dms (limit %dms), peak %dms (limit %dms) across %d execution(s) in %dms window",
|
||||
this.averageExecutionMs,
|
||||
this.overloadAverageThresholdMs,
|
||||
this.peakExecutionMs,
|
||||
this.overloadPeakThresholdMs,
|
||||
this.executionSamplesCurrentWindow,
|
||||
this.usageWindowMs)
|
||||
);
|
||||
}
|
||||
|
||||
private String buildHeavyReason(int usagePercent, int delayedPercent, boolean overloadWindow) {
|
||||
return String.format(
|
||||
"Room stayed above heavy thresholds for %d consecutive window(s): usage %d%%/%d%%, delayed %d%%/%d%%, overload %s",
|
||||
this.consecutiveHeavyWindows,
|
||||
usagePercent,
|
||||
this.heavyUsageThresholdPercent,
|
||||
delayedPercent,
|
||||
this.heavyDelayedThresholdPercent,
|
||||
overloadWindow ? "yes" : "no"
|
||||
);
|
||||
}
|
||||
|
||||
private static String joinReason(String primary, String fallback) {
|
||||
String cleanPrimary = sanitizeReason(primary);
|
||||
String cleanFallback = sanitizeReason(fallback);
|
||||
|
||||
if (cleanPrimary.isEmpty()) return cleanFallback;
|
||||
if (cleanFallback.isEmpty()) return cleanPrimary;
|
||||
if (cleanPrimary.equals(cleanFallback)) return cleanPrimary;
|
||||
|
||||
return cleanPrimary + ". " + cleanFallback;
|
||||
}
|
||||
|
||||
private static String sanitizeReason(String reason) {
|
||||
return (reason == null) ? "" : reason.trim();
|
||||
}
|
||||
|
||||
private static String sanitizeSourceLabel(String sourceLabel) {
|
||||
return (sourceLabel == null) ? "" : sourceLabel.trim();
|
||||
}
|
||||
|
||||
private Severity defaultSeverity(Type type) {
|
||||
return (type == Type.MARKED_AS_HEAVY) ? Severity.WARNING : Severity.ERROR;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurniByVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUsersByVariable;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
@@ -233,21 +235,50 @@ public final class WiredSourceUtil {
|
||||
|
||||
int furniLimit = Integer.MAX_VALUE;
|
||||
int userLimit = Integer.MAX_VALUE;
|
||||
List<WiredExtraFilterFurniByVariable> furniVariableFilters = new ArrayList<>();
|
||||
List<WiredExtraFilterUsersByVariable> userVariableFilters = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extra instanceof WiredExtraFilterFurni) {
|
||||
furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount());
|
||||
} else if (extra instanceof WiredExtraFilterUser) {
|
||||
userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount());
|
||||
} else if (extra instanceof WiredExtraFilterFurniByVariable) {
|
||||
furniVariableFilters.add((WiredExtraFilterFurniByVariable) extra);
|
||||
} else if (extra instanceof WiredExtraFilterUsersByVariable) {
|
||||
userVariableFilters.add((WiredExtraFilterUsersByVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectorCtx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) {
|
||||
selectorCtx.targets().setItems(limitIterable(selectorCtx.targets().items(), furniLimit));
|
||||
furniVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
userVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
|
||||
if (selectorCtx.targets().isItemsModifiedBySelector()) {
|
||||
Iterable<HabboItem> filteredItems = selectorCtx.targets().items();
|
||||
|
||||
for (WiredExtraFilterFurniByVariable extra : furniVariableFilters) {
|
||||
filteredItems = extra.filterItems(room, selectorCtx, filteredItems);
|
||||
}
|
||||
|
||||
if (furniLimit != Integer.MAX_VALUE) {
|
||||
filteredItems = limitIterable(filteredItems, furniLimit);
|
||||
}
|
||||
|
||||
selectorCtx.targets().setItems(filteredItems);
|
||||
}
|
||||
|
||||
if (selectorCtx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) {
|
||||
selectorCtx.targets().setUsers(limitIterable(selectorCtx.targets().users(), userLimit));
|
||||
if (selectorCtx.targets().isUsersModifiedBySelector()) {
|
||||
Iterable<RoomUnit> filteredUsers = selectorCtx.targets().users();
|
||||
|
||||
for (WiredExtraFilterUsersByVariable extra : userVariableFilters) {
|
||||
filteredUsers = extra.filterUsers(room, selectorCtx, filteredUsers);
|
||||
}
|
||||
|
||||
if (userLimit != Integer.MAX_VALUE) {
|
||||
filteredUsers = limitIterable(filteredUsers, userLimit);
|
||||
}
|
||||
|
||||
selectorCtx.targets().setUsers(filteredUsers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextInputVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboSaysKeyword;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.wired.api.WiredStack;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class WiredTextInputCaptureSupport {
|
||||
private static final int MATCH_CONTAINS = 0;
|
||||
private static final int MATCH_EXACT = 1;
|
||||
private static final int MATCH_ALL_WORDS = 2;
|
||||
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("#([^#]+)#");
|
||||
|
||||
private WiredTextInputCaptureSupport() {
|
||||
}
|
||||
|
||||
public static CaptureResult resolve(WiredStack stack, WiredEvent event) {
|
||||
if (stack == null || event == null || !(stack.triggerItem() instanceof WiredTriggerHabboSaysKeyword)) {
|
||||
return CaptureResult.noMatch();
|
||||
}
|
||||
|
||||
WiredTriggerHabboSaysKeyword trigger = (WiredTriggerHabboSaysKeyword) stack.triggerItem();
|
||||
Room room = event.getRoom();
|
||||
RoomUnit actor = event.getActor().orElse(null);
|
||||
String text = event.getText().orElse(null);
|
||||
|
||||
if (room == null || actor == null || text == null) {
|
||||
return CaptureResult.noMatch();
|
||||
}
|
||||
|
||||
List<WiredExtraTextInputVariable> capturers = getCapturers(room, trigger);
|
||||
if (capturers.isEmpty()) {
|
||||
return trigger.matches(stack.triggerItem(), event) ? CaptureResult.matched(new LinkedHashMap<>()) : CaptureResult.noMatch();
|
||||
}
|
||||
|
||||
if (trigger.isOwnerOnly()) {
|
||||
Habbo habbo = room.getHabbo(actor);
|
||||
if (habbo == null || room.getOwnerId() != habbo.getHabboInfo().getId()) {
|
||||
return CaptureResult.noMatch();
|
||||
}
|
||||
}
|
||||
|
||||
LinkedHashMap<String, WiredExtraTextInputVariable> capturersByName = new LinkedHashMap<>();
|
||||
for (WiredExtraTextInputVariable capturer : capturers) {
|
||||
if (capturer == null || capturer.getCapturerName() == null || capturer.getCapturerName().trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
capturersByName.put(capturer.getCapturerName().trim().toLowerCase(), capturer);
|
||||
}
|
||||
|
||||
if (capturersByName.isEmpty()) {
|
||||
return trigger.matches(stack.triggerItem(), event) ? CaptureResult.matched(new LinkedHashMap<>()) : CaptureResult.noMatch();
|
||||
}
|
||||
|
||||
MatchResult matchResult = matchTemplate(trigger, text, capturersByName);
|
||||
if (!matchResult.matches) {
|
||||
return CaptureResult.noMatch();
|
||||
}
|
||||
|
||||
LinkedHashMap<Integer, Integer> capturedValues = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, String> capture : matchResult.captures.entrySet()) {
|
||||
WiredExtraTextInputVariable capturer = capturersByName.get(capture.getKey());
|
||||
if (capturer == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer resolvedValue = capturer.resolveCapturedValue(room, capture.getValue());
|
||||
if (resolvedValue == null) {
|
||||
return CaptureResult.noMatch();
|
||||
}
|
||||
|
||||
capturedValues.put(capturer.getVariableItemId(), resolvedValue);
|
||||
}
|
||||
|
||||
return CaptureResult.matched(capturedValues);
|
||||
}
|
||||
|
||||
private static List<WiredExtraTextInputVariable> getCapturers(Room room, WiredTriggerHabboSaysKeyword trigger) {
|
||||
List<WiredExtraTextInputVariable> capturers = new ArrayList<>();
|
||||
|
||||
if (room == null || trigger == null || room.getRoomSpecialTypes() == null) {
|
||||
return capturers;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(trigger.getX(), trigger.getY());
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return capturers;
|
||||
}
|
||||
|
||||
for (InteractionWiredExtra extra : WiredExecutionOrderUtil.sort(extras)) {
|
||||
if (extra instanceof WiredExtraTextInputVariable) {
|
||||
capturers.add((WiredExtraTextInputVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
return capturers;
|
||||
}
|
||||
|
||||
private static MatchResult matchTemplate(WiredTriggerHabboSaysKeyword trigger, String rawText, Map<String, WiredExtraTextInputVariable> capturersByName) {
|
||||
String text = rawText != null ? rawText.trim() : "";
|
||||
String template = trigger.getKey() != null ? trigger.getKey().trim() : "";
|
||||
|
||||
if (trigger.getMatchMode() == MATCH_ALL_WORDS && template.isEmpty()) {
|
||||
if (capturersByName.size() != 1 || text.isEmpty()) {
|
||||
return MatchResult.noMatch();
|
||||
}
|
||||
|
||||
String placeholderName = capturersByName.keySet().iterator().next();
|
||||
LinkedHashMap<String, String> captures = new LinkedHashMap<>();
|
||||
captures.put(placeholderName, text);
|
||||
return MatchResult.matched(captures);
|
||||
}
|
||||
|
||||
TemplatePattern pattern = buildPattern(template);
|
||||
if (pattern == null) {
|
||||
return MatchResult.noMatch();
|
||||
}
|
||||
|
||||
Matcher matcher = pattern.pattern.matcher(text);
|
||||
boolean matches = (trigger.getMatchMode() == MATCH_CONTAINS) ? matcher.find() : matcher.matches();
|
||||
if (!matches) {
|
||||
return MatchResult.noMatch();
|
||||
}
|
||||
|
||||
LinkedHashMap<String, String> captures = new LinkedHashMap<>();
|
||||
for (int index = 0; index < pattern.placeholderNames.size(); index++) {
|
||||
String placeholderName = pattern.placeholderNames.get(index);
|
||||
if (!capturersByName.containsKey(placeholderName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String capturedValue = matcher.group(index + 1);
|
||||
captures.put(placeholderName, capturedValue != null ? capturedValue.trim() : "");
|
||||
}
|
||||
|
||||
return MatchResult.matched(captures);
|
||||
}
|
||||
|
||||
private static TemplatePattern buildPattern(String template) {
|
||||
if (template == null || template.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Matcher matcher = PLACEHOLDER_PATTERN.matcher(template);
|
||||
StringBuilder regex = new StringBuilder();
|
||||
List<String> placeholderNames = new ArrayList<>();
|
||||
int cursor = 0;
|
||||
|
||||
while (matcher.find()) {
|
||||
regex.append(Pattern.quote(template.substring(cursor, matcher.start())));
|
||||
regex.append("(.+?)");
|
||||
|
||||
String placeholderName = matcher.group(1) != null ? matcher.group(1).trim().toLowerCase() : "";
|
||||
placeholderNames.add(placeholderName);
|
||||
cursor = matcher.end();
|
||||
}
|
||||
|
||||
regex.append(Pattern.quote(template.substring(cursor)));
|
||||
|
||||
if (placeholderNames.isEmpty()) {
|
||||
regex = new StringBuilder(Pattern.quote(template));
|
||||
}
|
||||
|
||||
return new TemplatePattern(Pattern.compile(regex.toString(), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE), placeholderNames);
|
||||
}
|
||||
|
||||
public static void applyToContext(WiredContext ctx, Room room, CaptureResult captureResult) {
|
||||
if (ctx == null || room == null || captureResult == null || !captureResult.matches || captureResult.capturedValues.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.forkContextVariables();
|
||||
|
||||
for (Map.Entry<Integer, Integer> entry : captureResult.capturedValues.entrySet()) {
|
||||
if (entry == null || entry.getKey() == null || entry.getKey() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!WiredContextVariableSupport.updateVariableValue(ctx, room, entry.getKey(), entry.getValue())) {
|
||||
WiredContextVariableSupport.assignVariable(ctx, room, entry.getKey(), entry.getValue(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CaptureResult {
|
||||
private final boolean matches;
|
||||
private final LinkedHashMap<Integer, Integer> capturedValues;
|
||||
|
||||
private CaptureResult(boolean matches, LinkedHashMap<Integer, Integer> capturedValues) {
|
||||
this.matches = matches;
|
||||
this.capturedValues = capturedValues;
|
||||
}
|
||||
|
||||
public boolean matches() {
|
||||
return this.matches;
|
||||
}
|
||||
|
||||
public static CaptureResult matched(LinkedHashMap<Integer, Integer> capturedValues) {
|
||||
return new CaptureResult(true, capturedValues);
|
||||
}
|
||||
|
||||
public static CaptureResult noMatch() {
|
||||
return new CaptureResult(false, new LinkedHashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class MatchResult {
|
||||
private final boolean matches;
|
||||
private final LinkedHashMap<String, String> captures;
|
||||
|
||||
private MatchResult(boolean matches, LinkedHashMap<String, String> captures) {
|
||||
this.matches = matches;
|
||||
this.captures = captures;
|
||||
}
|
||||
|
||||
private static MatchResult matched(LinkedHashMap<String, String> captures) {
|
||||
return new MatchResult(true, captures);
|
||||
}
|
||||
|
||||
private static MatchResult noMatch() {
|
||||
return new MatchResult(false, new LinkedHashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TemplatePattern {
|
||||
private final Pattern pattern;
|
||||
private final List<String> placeholderNames;
|
||||
|
||||
private TemplatePattern(Pattern pattern, List<String> placeholderNames) {
|
||||
this.pattern = pattern;
|
||||
this.placeholderNames = placeholderNames;
|
||||
}
|
||||
}
|
||||
}
|
||||
+331
-4
@@ -1,20 +1,35 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.habbohotel.bots.Bot;
|
||||
import com.eu.habbo.habbohotel.games.Game;
|
||||
import com.eu.habbo.habbohotel.games.GamePlayer;
|
||||
import com.eu.habbo.habbohotel.games.GameTeam;
|
||||
import com.eu.habbo.habbohotel.games.GameTeamColors;
|
||||
import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame;
|
||||
import com.eu.habbo.habbohotel.games.freeze.FreezeGame;
|
||||
import com.eu.habbo.habbohotel.games.wired.WiredGame;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputFurniName;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputUsername;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUserVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFurniVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRoomVariable;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.util.HotelDateTimeUtil;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class WiredTextPlaceholderUtil {
|
||||
private WiredTextPlaceholderUtil() {
|
||||
@@ -58,6 +73,17 @@ public final class WiredTextPlaceholderUtil {
|
||||
if (!placeholderToken.isEmpty() && resolvedText.contains(placeholderToken)) {
|
||||
resolvedText = resolvedText.replace(placeholderToken, buildFurniNameReplacement(ctx, furniExtra));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraTextOutputVariable) {
|
||||
WiredExtraTextOutputVariable variableExtra = (WiredExtraTextOutputVariable) extra;
|
||||
String placeholderToken = variableExtra.getPlaceholderToken();
|
||||
|
||||
if (!placeholderToken.isEmpty() && resolvedText.contains(placeholderToken)) {
|
||||
resolvedText = resolvedText.replace(placeholderToken, buildVariableReplacement(ctx, variableExtra));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,12 +101,14 @@ public final class WiredTextPlaceholderUtil {
|
||||
}
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (!(extra instanceof WiredExtraTextOutputUsername)) {
|
||||
continue;
|
||||
if (extra instanceof WiredExtraTextOutputUsername) {
|
||||
int userSource = ((WiredExtraTextOutputUsername) extra).getUserSource();
|
||||
if (userSource == WiredSourceUtil.SOURCE_TRIGGER || userSource == WiredSourceUtil.SOURCE_CLICKED_USER) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int userSource = ((WiredExtraTextOutputUsername) extra).getUserSource();
|
||||
if ((userSource == WiredSourceUtil.SOURCE_TRIGGER) || (userSource == WiredSourceUtil.SOURCE_CLICKED_USER)) {
|
||||
if (extra instanceof WiredExtraTextOutputVariable && ((WiredExtraTextOutputVariable) extra).requiresActor()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -164,6 +192,186 @@ public final class WiredTextPlaceholderUtil {
|
||||
return furniNames.get(0);
|
||||
}
|
||||
|
||||
private static String buildVariableReplacement(WiredContext ctx, WiredExtraTextOutputVariable extra) {
|
||||
List<String> values = switch (extra.getTargetType()) {
|
||||
case WiredExtraTextOutputVariable.TARGET_FURNI -> collectFurniVariableValues(ctx, extra);
|
||||
case WiredExtraTextOutputVariable.TARGET_CONTEXT -> collectContextVariableValues(ctx, extra);
|
||||
case WiredExtraTextOutputVariable.TARGET_ROOM -> collectRoomVariableValues(ctx, extra);
|
||||
default -> collectUserVariableValues(ctx, extra);
|
||||
};
|
||||
|
||||
if (values.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (extra.getPlaceholderType() == WiredExtraTextOutputVariable.TYPE_MULTIPLE) {
|
||||
return String.join(extra.getDelimiter(), values);
|
||||
}
|
||||
|
||||
return values.get(0);
|
||||
}
|
||||
|
||||
private static List<String> collectUserVariableValues(WiredContext ctx, WiredExtraTextOutputVariable extra) {
|
||||
Room room = ctx.room();
|
||||
List<RoomUnit> users = WiredSourceUtil.resolveUsers(ctx, extra.getUserSource());
|
||||
if (room == null || users.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
LinkedHashSet<Integer> seenUserIds = new LinkedHashSet<>();
|
||||
List<String> values = new ArrayList<>();
|
||||
|
||||
for (RoomUnit roomUnit : users) {
|
||||
if (roomUnit == null || !seenUserIds.add(roomUnit.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String value = resolveUserVariableValue(room, roomUnit, extra);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static List<String> collectFurniVariableValues(WiredContext ctx, WiredExtraTextOutputVariable extra) {
|
||||
Room room = ctx.room();
|
||||
if (room == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
extra.refresh(room);
|
||||
|
||||
List<HabboItem> items = (extra.getFurniSource() == WiredSourceUtil.SOURCE_SELECTOR)
|
||||
? WiredSourceUtil.resolveSelectorItems(ctx, true)
|
||||
: WiredSourceUtil.resolveItems(ctx, extra.getFurniSource(), extra.getItems());
|
||||
|
||||
if (items.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
LinkedHashSet<Integer> seenItemIds = new LinkedHashSet<>();
|
||||
List<String> values = new ArrayList<>();
|
||||
|
||||
for (HabboItem item : items) {
|
||||
if (item == null || !seenItemIds.add(item.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String value = resolveFurniVariableValue(room, item, extra);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static List<String> collectRoomVariableValues(WiredContext ctx, WiredExtraTextOutputVariable extra) {
|
||||
Room room = ctx.room();
|
||||
if (room == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
String value = resolveRoomVariableValue(room, extra);
|
||||
return (value == null || value.isEmpty()) ? List.of() : List.of(value);
|
||||
}
|
||||
|
||||
private static List<String> collectContextVariableValues(WiredContext ctx, WiredExtraTextOutputVariable extra) {
|
||||
if (ctx == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
String value = resolveContextVariableValue(ctx, extra);
|
||||
return (value == null || value.isEmpty()) ? List.of() : List.of(value);
|
||||
}
|
||||
|
||||
private static String resolveUserVariableValue(Room room, RoomUnit roomUnit, WiredExtraTextOutputVariable extra) {
|
||||
if (room == null || roomUnit == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (WiredExtraTextOutputVariable.isInternalVariableToken(extra.getVariableToken())) {
|
||||
Integer value = readUserInternalValue(room, roomUnit, WiredExtraTextOutputVariable.getInternalVariableKey(extra.getVariableToken()));
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
Habbo habbo = room.getHabbo(roomUnit);
|
||||
if (habbo == null || !room.getUserVariableManager().hasVariable(habbo.getHabboInfo().getId(), extra.getVariableItemId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer value = room.getUserVariableManager().getCurrentValue(habbo.getHabboInfo().getId(), extra.getVariableItemId());
|
||||
if (extra.getDisplayType(room) == WiredExtraTextOutputVariable.DISPLAY_TEXTUAL) {
|
||||
return WiredVariableTextConnectorSupport.toText(room, extra.getVariableItemId(), value);
|
||||
}
|
||||
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
private static String resolveFurniVariableValue(Room room, HabboItem item, WiredExtraTextOutputVariable extra) {
|
||||
if (room == null || item == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (WiredExtraTextOutputVariable.isInternalVariableToken(extra.getVariableToken())) {
|
||||
Integer value = readFurniInternalValue(room, item, WiredExtraTextOutputVariable.getInternalVariableKey(extra.getVariableToken()));
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
if (!room.getFurniVariableManager().hasVariable(item.getId(), extra.getVariableItemId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer value = room.getFurniVariableManager().getCurrentValue(item.getId(), extra.getVariableItemId());
|
||||
if (extra.getDisplayType(room) == WiredExtraTextOutputVariable.DISPLAY_TEXTUAL) {
|
||||
return WiredVariableTextConnectorSupport.toText(room, extra.getVariableItemId(), value);
|
||||
}
|
||||
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
private static String resolveRoomVariableValue(Room room, WiredExtraTextOutputVariable extra) {
|
||||
if (room == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (WiredExtraTextOutputVariable.isInternalVariableToken(extra.getVariableToken())) {
|
||||
Integer value = readRoomInternalValue(room, WiredExtraTextOutputVariable.getInternalVariableKey(extra.getVariableToken()));
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
Integer value = room.getRoomVariableManager().getCurrentValue(extra.getVariableItemId());
|
||||
if (extra.getDisplayType(room) == WiredExtraTextOutputVariable.DISPLAY_TEXTUAL) {
|
||||
return WiredVariableTextConnectorSupport.toText(room, extra.getVariableItemId(), value);
|
||||
}
|
||||
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
private static String resolveContextVariableValue(WiredContext ctx, WiredExtraTextOutputVariable extra) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (WiredExtraTextOutputVariable.isInternalVariableToken(extra.getVariableToken())) {
|
||||
Integer value = WiredInternalVariableSupport.readContextValue(ctx, WiredExtraTextOutputVariable.getInternalVariableKey(extra.getVariableToken()));
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
if (!WiredContextVariableSupport.hasVariable(ctx, extra.getVariableItemId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer value = WiredContextVariableSupport.getCurrentValue(ctx, extra.getVariableItemId());
|
||||
if (extra.getDisplayType(ctx.room()) == WiredExtraTextOutputVariable.DISPLAY_TEXTUAL) {
|
||||
return WiredVariableTextConnectorSupport.toText(ctx.room(), extra.getVariableItemId(), value);
|
||||
}
|
||||
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
private static String getRoomUnitName(Room room, RoomUnit roomUnit) {
|
||||
if (room == null || roomUnit == null) {
|
||||
return "";
|
||||
@@ -186,4 +394,123 @@ public final class WiredTextPlaceholderUtil {
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private static Integer readUserInternalValue(Room room, RoomUnit roomUnit, String key) {
|
||||
return WiredInternalVariableSupport.readUserValue(room, roomUnit, key);
|
||||
}
|
||||
|
||||
private static Integer readFurniInternalValue(Room room, HabboItem item, String key) {
|
||||
return WiredInternalVariableSupport.readFurniValue(room, item, key);
|
||||
}
|
||||
|
||||
private static Integer readRoomInternalValue(Room room, String key) {
|
||||
return WiredInternalVariableSupport.readRoomValue(room, key);
|
||||
}
|
||||
|
||||
private static Integer getUserTeamScore(Room room, Habbo habbo) {
|
||||
if (room == null || habbo == null || habbo.getHabboInfo().getGamePlayer() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Game game = resolveTeamGame(room, habbo);
|
||||
GamePlayer gamePlayer = habbo.getHabboInfo().getGamePlayer();
|
||||
|
||||
if (game == null || gamePlayer.getTeamColor() == null) {
|
||||
return gamePlayer.getScore();
|
||||
}
|
||||
|
||||
GameTeam team = game.getTeam(gamePlayer.getTeamColor());
|
||||
return (team != null) ? team.getTotalScore() : gamePlayer.getScore();
|
||||
}
|
||||
|
||||
private static Integer getTeamColorId(int effectId) {
|
||||
TeamEffectData data = getTeamEffectData(effectId);
|
||||
return data == null ? null : data.colorId;
|
||||
}
|
||||
|
||||
private static Integer getTeamTypeId(int effectId) {
|
||||
TeamEffectData data = getTeamEffectData(effectId);
|
||||
return data == null ? null : data.typeId;
|
||||
}
|
||||
|
||||
private static int getTeamMetric(Room room, GameTeamColors color, boolean score) {
|
||||
Game game = resolveTeamGame(room, null);
|
||||
if (game == null || color == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GameTeam team = game.getTeam(color);
|
||||
if (team == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return score ? team.getTotalScore() : team.getMembers().size();
|
||||
}
|
||||
|
||||
private static Game resolveTeamGame(Room room, Habbo habbo) {
|
||||
if (room == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getCurrentGame() != null) {
|
||||
Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
|
||||
if (game != null) {
|
||||
return game;
|
||||
}
|
||||
}
|
||||
|
||||
Game wiredGame = room.getGame(WiredGame.class);
|
||||
if (wiredGame != null) {
|
||||
return wiredGame;
|
||||
}
|
||||
|
||||
Game freezeGame = room.getGame(FreezeGame.class);
|
||||
if (freezeGame != null) {
|
||||
return freezeGame;
|
||||
}
|
||||
|
||||
return room.getGame(BattleBanzaiGame.class);
|
||||
}
|
||||
|
||||
private static TeamEffectData getTeamEffectData(int effectValue) {
|
||||
if (effectValue <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (effectValue >= 223 && effectValue <= 226) {
|
||||
return new TeamEffectData(effectValue - 222, 0);
|
||||
}
|
||||
|
||||
if (effectValue >= 33 && effectValue <= 36) {
|
||||
return new TeamEffectData(effectValue - 32, 1);
|
||||
}
|
||||
|
||||
if (effectValue >= 40 && effectValue <= 43) {
|
||||
return new TeamEffectData(effectValue - 39, 2);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Integer parseInteger(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TeamEffectData {
|
||||
final int colorId;
|
||||
final int typeId;
|
||||
|
||||
TeamEffectData(int colorId, int typeId) {
|
||||
this.colorId = colorId;
|
||||
this.typeId = typeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-4
@@ -4,7 +4,9 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurniByVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUsersByVariable;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
@@ -172,21 +174,50 @@ public final class WiredTriggerSourceUtil {
|
||||
|
||||
int furniLimit = Integer.MAX_VALUE;
|
||||
int userLimit = Integer.MAX_VALUE;
|
||||
List<WiredExtraFilterFurniByVariable> furniVariableFilters = new ArrayList<>();
|
||||
List<WiredExtraFilterUsersByVariable> userVariableFilters = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extra instanceof WiredExtraFilterFurni) {
|
||||
furniLimit = Math.min(furniLimit, ((WiredExtraFilterFurni) extra).getAmount());
|
||||
} else if (extra instanceof WiredExtraFilterUser) {
|
||||
userLimit = Math.min(userLimit, ((WiredExtraFilterUser) extra).getAmount());
|
||||
} else if (extra instanceof WiredExtraFilterFurniByVariable) {
|
||||
furniVariableFilters.add((WiredExtraFilterFurniByVariable) extra);
|
||||
} else if (extra instanceof WiredExtraFilterUsersByVariable) {
|
||||
userVariableFilters.add((WiredExtraFilterUsersByVariable) extra);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectorCtx.targets().isItemsModifiedBySelector() && furniLimit != Integer.MAX_VALUE) {
|
||||
selectorCtx.targets().setItems(limitIterable(selectorCtx.targets().items(), furniLimit));
|
||||
furniVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
userVariableFilters.sort((left, right) -> Integer.compare(left.getId(), right.getId()));
|
||||
|
||||
if (selectorCtx.targets().isItemsModifiedBySelector()) {
|
||||
Iterable<HabboItem> filteredItems = selectorCtx.targets().items();
|
||||
|
||||
for (WiredExtraFilterFurniByVariable extra : furniVariableFilters) {
|
||||
filteredItems = extra.filterItems(room, selectorCtx, filteredItems);
|
||||
}
|
||||
|
||||
if (furniLimit != Integer.MAX_VALUE) {
|
||||
filteredItems = limitIterable(filteredItems, furniLimit);
|
||||
}
|
||||
|
||||
selectorCtx.targets().setItems(filteredItems);
|
||||
}
|
||||
|
||||
if (selectorCtx.targets().isUsersModifiedBySelector() && userLimit != Integer.MAX_VALUE) {
|
||||
selectorCtx.targets().setUsers(limitIterable(selectorCtx.targets().users(), userLimit));
|
||||
if (selectorCtx.targets().isUsersModifiedBySelector()) {
|
||||
Iterable<RoomUnit> filteredUsers = selectorCtx.targets().users();
|
||||
|
||||
for (WiredExtraFilterUsersByVariable extra : userVariableFilters) {
|
||||
filteredUsers = extra.filterUsers(room, selectorCtx, filteredUsers);
|
||||
}
|
||||
|
||||
if (userLimit != Integer.MAX_VALUE) {
|
||||
filteredUsers = limitIterable(filteredUsers, userLimit);
|
||||
}
|
||||
|
||||
selectorCtx.targets().setUsers(filteredUsers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+105
-17
@@ -19,6 +19,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -36,20 +37,26 @@ public final class WiredUserMovementHelper {
|
||||
|
||||
public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, int duration) {
|
||||
return moveUser(room, roomUnit, targetTile, targetZ, roomUnit == null ? null : roomUnit.getBodyRotation(),
|
||||
roomUnit == null ? null : roomUnit.getHeadRotation(), duration, false);
|
||||
roomUnit == null ? null : roomUnit.getHeadRotation(), duration, false, WiredMovementPhysics.NONE);
|
||||
}
|
||||
|
||||
public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, int duration) {
|
||||
return moveUser(room, roomUnit, targetTile, targetZ, bodyRotation, headRotation, duration, false);
|
||||
return moveUser(room, roomUnit, targetTile, targetZ, bodyRotation, headRotation, duration, false, WiredMovementPhysics.NONE);
|
||||
}
|
||||
|
||||
public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, int duration, boolean noAnimation) {
|
||||
return moveUser(room, roomUnit, targetTile, targetZ, bodyRotation, headRotation, duration, noAnimation, WiredMovementPhysics.NONE);
|
||||
}
|
||||
|
||||
public static boolean moveUser(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, int duration, boolean noAnimation, WiredMovementPhysics movementPhysics) {
|
||||
if (room == null || roomUnit == null || targetTile == null || room.getLayout() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RoomTile oldLocation = roomUnit.getCurrentLocation();
|
||||
if (oldLocation == null || hasBlockingUnits(room, roomUnit, targetTile)) {
|
||||
WiredMovementPhysics resolvedMovementPhysics = movementPhysics == null ? WiredMovementPhysics.NONE : movementPhysics;
|
||||
|
||||
if (oldLocation == null || !canMoveTo(room, roomUnit, targetTile, resolvedMovementPhysics)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -66,13 +73,12 @@ public final class WiredUserMovementHelper {
|
||||
}
|
||||
|
||||
runWithSuppressedStatusUpdates(Collections.singletonList(roomUnit), () -> {
|
||||
roomUnit.setPreviousLocation(oldLocation);
|
||||
roomUnit.setCurrentLocation(targetTile);
|
||||
roomUnit.removeStatus(RoomUnitStatus.MOVE);
|
||||
roomUnit.setZ(targetZ);
|
||||
roomUnit.setLocation(targetTile);
|
||||
roomUnit.setPath(new LinkedList<>());
|
||||
roomUnit.setBodyRotation(resolvedBodyRotation);
|
||||
roomUnit.setHeadRotation(resolvedHeadRotation);
|
||||
roomUnit.stopWalking();
|
||||
roomUnit.resetIdleTimer();
|
||||
|
||||
if (habbo != null) {
|
||||
@@ -99,10 +105,8 @@ public final class WiredUserMovementHelper {
|
||||
suppressStatusComposer(roomUnit, animationDuration);
|
||||
room.sendComposer(new WiredMovementsComposer(movements).compose());
|
||||
|
||||
roomUnit.setPreviousLocation(targetTile);
|
||||
roomUnit.setPreviousLocationZ(roomUnit.getZ());
|
||||
|
||||
scheduleTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem, animationDuration);
|
||||
scheduleFinalStatusSync(room, roomUnit, targetTile, animationDuration);
|
||||
schedulePostureSync(room, roomUnit, targetTile, animationDuration);
|
||||
return true;
|
||||
}
|
||||
@@ -161,7 +165,32 @@ public final class WiredUserMovementHelper {
|
||||
SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId());
|
||||
}
|
||||
|
||||
private static boolean hasBlockingUnits(Room room, RoomUnit roomUnit, RoomTile targetTile) {
|
||||
public static boolean canMoveTo(Room room, RoomUnit roomUnit, RoomTile targetTile, WiredMovementPhysics movementPhysics) {
|
||||
if (room == null || roomUnit == null || targetTile == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WiredMovementPhysics resolvedMovementPhysics = movementPhysics == null ? WiredMovementPhysics.NONE : movementPhysics;
|
||||
|
||||
if (targetTile.state == null || targetTile.state == com.eu.habbo.habbohotel.rooms.RoomTileState.INVALID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetTile.state == com.eu.habbo.habbohotel.rooms.RoomTileState.BLOCKED
|
||||
&& !canBypassBlockedTile(room, targetTile, resolvedMovementPhysics)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!room.getLayout().tileWalkable(targetTile.x, targetTile.y)
|
||||
&& !room.canSitOrLayAt(targetTile.x, targetTile.y)
|
||||
&& !canBypassBlockedTile(room, targetTile, resolvedMovementPhysics)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !hasBlockingUnits(room, roomUnit, targetTile, resolvedMovementPhysics);
|
||||
}
|
||||
|
||||
private static boolean hasBlockingUnits(Room room, RoomUnit roomUnit, RoomTile targetTile, WiredMovementPhysics movementPhysics) {
|
||||
Collection<RoomUnit> units = room.getRoomUnitsAt(targetTile);
|
||||
|
||||
if (units == null || units.isEmpty()) {
|
||||
@@ -169,7 +198,9 @@ public final class WiredUserMovementHelper {
|
||||
}
|
||||
|
||||
for (RoomUnit targetUnit : units) {
|
||||
if (targetUnit != null && targetUnit != roomUnit) {
|
||||
if (targetUnit != null
|
||||
&& targetUnit != roomUnit
|
||||
&& !movementPhysics.shouldIgnoreUser(targetUnit)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -177,15 +208,50 @@ public final class WiredUserMovementHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean canBypassBlockedTile(Room room, RoomTile targetTile, WiredMovementPhysics movementPhysics) {
|
||||
if (room == null || targetTile == null || movementPhysics == null || !movementPhysics.isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Collection<HabboItem> items = room.getItemsAt(targetTile);
|
||||
if (items == null || items.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasIgnoredFurni = false;
|
||||
|
||||
for (HabboItem item : items) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (movementPhysics.isBlockingFurni(item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (movementPhysics.shouldIgnoreFurni(item)) {
|
||||
hasIgnoredFurni = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!item.isWalkable()
|
||||
&& !item.getBaseItem().allowSit()
|
||||
&& !item.getBaseItem().allowLay()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return hasIgnoredFurni;
|
||||
}
|
||||
|
||||
private static boolean moveUserInstant(Room room, RoomUnit roomUnit, RoomTile targetTile, double targetZ, RoomUserRotation bodyRotation, RoomUserRotation headRotation, RoomTile oldLocation, HabboItem oldTopItem, HabboItem newTopItem, Habbo habbo) {
|
||||
runWithSuppressedStatusUpdates(Collections.singletonList(roomUnit), () -> {
|
||||
roomUnit.setPreviousLocation(oldLocation);
|
||||
roomUnit.setCurrentLocation(targetTile);
|
||||
roomUnit.removeStatus(RoomUnitStatus.MOVE);
|
||||
roomUnit.setZ(targetZ);
|
||||
roomUnit.setLocation(targetTile);
|
||||
roomUnit.setPath(new LinkedList<>());
|
||||
roomUnit.setBodyRotation(bodyRotation);
|
||||
roomUnit.setHeadRotation(headRotation);
|
||||
roomUnit.stopWalking();
|
||||
roomUnit.resetIdleTimer();
|
||||
|
||||
if (habbo != null) {
|
||||
@@ -193,13 +259,12 @@ public final class WiredUserMovementHelper {
|
||||
movedHabbos.add(habbo);
|
||||
room.updateHabbosAt(targetTile.x, targetTile.y, movedHabbos);
|
||||
}
|
||||
|
||||
roomUnit.setPreviousLocation(targetTile);
|
||||
roomUnit.setPreviousLocationZ(roomUnit.getZ());
|
||||
roomUnit.statusUpdate(false);
|
||||
});
|
||||
|
||||
processTileCallbacks(room, roomUnit, oldLocation, targetTile, oldTopItem, newTopItem);
|
||||
roomUnit.setPreviousLocation(roomUnit.getCurrentLocation());
|
||||
roomUnit.setPreviousLocationZ(roomUnit.getZ());
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
return true;
|
||||
}
|
||||
@@ -258,6 +323,29 @@ public final class WiredUserMovementHelper {
|
||||
}, delay + STATUS_SUPPRESSION_GRACE_MS + 25);
|
||||
}
|
||||
|
||||
private static void scheduleFinalStatusSync(Room room, RoomUnit roomUnit, RoomTile targetTile, int delay) {
|
||||
if (room == null || roomUnit == null || targetTile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
if (room == null || !room.isLoaded() || roomUnit == null || roomUnit.getCurrentLocation() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomUnit.isWalking()
|
||||
|| roomUnit.getCurrentLocation().x != targetTile.x
|
||||
|| roomUnit.getCurrentLocation().y != targetTile.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearStatusComposerSuppression(roomUnit);
|
||||
roomUnit.setPreviousLocation(roomUnit.getCurrentLocation());
|
||||
roomUnit.setPreviousLocationZ(roomUnit.getZ());
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
}, Math.max(delay, 1) + 25);
|
||||
}
|
||||
|
||||
private static void suppressStatusComposer(RoomUnit roomUnit, int duration) {
|
||||
if (roomUnit == null) {
|
||||
return;
|
||||
|
||||
+564
@@ -0,0 +1,564 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFurniVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRoomVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUserVariable;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableLevelUpSystem;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableReference;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.WiredVariableDefinitionInfo;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class WiredVariableLevelSystemSupport {
|
||||
public static final int TARGET_USER = 0;
|
||||
public static final int TARGET_FURNI = 1;
|
||||
public static final int TARGET_ROOM = 3;
|
||||
|
||||
private static final int SYNTHETIC_USER_OFFSET = 700_000_000;
|
||||
private static final int SYNTHETIC_FURNI_OFFSET = 800_000_000;
|
||||
private static final int SYNTHETIC_ROOM_OFFSET = 900_000_000;
|
||||
private static final int SYNTHETIC_STRIDE = 16;
|
||||
|
||||
private WiredVariableLevelSystemSupport() {
|
||||
}
|
||||
|
||||
public static WiredExtraVariableLevelUpSystem getLevelSystem(Room room, InteractionWiredExtra definition) {
|
||||
if (room == null || definition == null || room.getRoomSpecialTypes() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(definition.getX(), definition.getY());
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (InteractionWiredExtra extra : WiredExecutionOrderUtil.sort(extras)) {
|
||||
if (extra instanceof WiredExtraVariableLevelUpSystem) {
|
||||
return (WiredExtraVariableLevelUpSystem) extra;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<WiredVariableDefinitionInfo> getDerivedDefinitions(Room room, int targetType, InteractionWiredExtra definitionExtra, WiredVariableDefinitionInfo baseDefinition) {
|
||||
if (room == null || definitionExtra == null || baseDefinition == null || !baseDefinition.hasValue()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
WiredExtraVariableLevelUpSystem levelSystem = getLevelSystem(room, definitionExtra);
|
||||
if (levelSystem == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<WiredVariableDefinitionInfo> result = new ArrayList<>();
|
||||
|
||||
for (int subvariableType : levelSystem.getSelectedSubvariables()) {
|
||||
result.add(new WiredVariableDefinitionInfo(
|
||||
createSyntheticItemId(targetType, baseDefinition.getItemId(), subvariableType),
|
||||
baseDefinition.getName() + "." + getSubvariableKey(subvariableType),
|
||||
true,
|
||||
baseDefinition.getAvailability(),
|
||||
false,
|
||||
true
|
||||
));
|
||||
}
|
||||
|
||||
result.sort(Comparator.comparing(WiredVariableDefinitionInfo::getName, String.CASE_INSENSITIVE_ORDER).thenComparingInt(WiredVariableDefinitionInfo::getItemId));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static WiredVariableDefinitionInfo getDerivedDefinitionInfo(Room room, int targetType, int syntheticItemId) {
|
||||
DerivedDefinition derived = resolveDerivedDefinition(room, targetType, syntheticItemId);
|
||||
|
||||
if (derived == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new WiredVariableDefinitionInfo(
|
||||
derived.syntheticItemId,
|
||||
derived.variableName,
|
||||
true,
|
||||
derived.baseDefinition.getAvailability(),
|
||||
false,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public static DerivedDefinition resolveDerivedDefinition(Room room, int targetType, int syntheticItemId) {
|
||||
DecodedSyntheticId decoded = decodeSyntheticId(syntheticItemId);
|
||||
if (decoded == null || decoded.targetType != targetType || room == null || room.getRoomSpecialTypes() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InteractionWiredExtra baseExtra = room.getRoomSpecialTypes().getExtra(decoded.baseDefinitionItemId);
|
||||
if (!matchesTarget(baseExtra, targetType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
WiredVariableDefinitionInfo baseDefinition = createBaseDefinitionInfo(room, baseExtra, targetType);
|
||||
if (baseDefinition == null || !baseDefinition.hasValue()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
WiredExtraVariableLevelUpSystem levelSystem = getLevelSystem(room, baseExtra);
|
||||
if (levelSystem == null || !levelSystem.hasSubvariable(decoded.subvariableType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DerivedDefinition(
|
||||
syntheticItemId,
|
||||
decoded.baseDefinitionItemId,
|
||||
decoded.subvariableType,
|
||||
baseDefinition.getName() + "." + getSubvariableKey(decoded.subvariableType),
|
||||
baseDefinition,
|
||||
levelSystem
|
||||
);
|
||||
}
|
||||
|
||||
public static Integer getDerivedValue(WiredExtraVariableLevelUpSystem levelSystem, int subvariableType, Integer baseValue) {
|
||||
if (levelSystem == null || baseValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LevelProgress progress = calculateProgress(levelSystem, baseValue);
|
||||
return switch (subvariableType) {
|
||||
case WiredExtraVariableLevelUpSystem.SUB_CURRENT_LEVEL -> progress.currentLevel;
|
||||
case WiredExtraVariableLevelUpSystem.SUB_CURRENT_XP -> progress.currentXp;
|
||||
case WiredExtraVariableLevelUpSystem.SUB_LEVEL_PROGRESS -> progress.progressXp;
|
||||
case WiredExtraVariableLevelUpSystem.SUB_LEVEL_PROGRESS_PERCENT -> progress.progressPercent;
|
||||
case WiredExtraVariableLevelUpSystem.SUB_TOTAL_XP_REQUIRED -> progress.totalXpRequired;
|
||||
case WiredExtraVariableLevelUpSystem.SUB_XP_REMAINING -> progress.xpRemaining;
|
||||
case WiredExtraVariableLevelUpSystem.SUB_IS_AT_MAX -> progress.isAtMax ? 1 : 0;
|
||||
case WiredExtraVariableLevelUpSystem.SUB_MAX_LEVEL -> progress.maxLevel;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public static List<LevelEntry> buildPreviewEntries(WiredExtraVariableLevelUpSystem levelSystem) {
|
||||
if (levelSystem == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return buildThresholdEntries(levelSystem);
|
||||
}
|
||||
|
||||
private static boolean matchesTarget(InteractionWiredExtra extra, int targetType) {
|
||||
if (extra == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (targetType) {
|
||||
case TARGET_FURNI -> extra instanceof WiredExtraFurniVariable;
|
||||
case TARGET_ROOM -> (extra instanceof WiredExtraRoomVariable)
|
||||
|| (extra instanceof WiredExtraVariableReference && ((WiredExtraVariableReference) extra).isRoomReference());
|
||||
default -> (extra instanceof WiredExtraUserVariable)
|
||||
|| (extra instanceof WiredExtraVariableReference && ((WiredExtraVariableReference) extra).isUserReference());
|
||||
};
|
||||
}
|
||||
|
||||
private static WiredVariableDefinitionInfo createBaseDefinitionInfo(Room room, InteractionWiredExtra extra, int targetType) {
|
||||
if (room == null || extra == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (targetType == TARGET_FURNI && extra instanceof WiredExtraFurniVariable) {
|
||||
WiredExtraFurniVariable definition = (WiredExtraFurniVariable) extra;
|
||||
return new WiredVariableDefinitionInfo(
|
||||
definition.getId(),
|
||||
definition.getVariableName(),
|
||||
definition.hasValue(),
|
||||
definition.getAvailability(),
|
||||
WiredVariableTextConnectorSupport.isTextConnected(room, definition),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (targetType == TARGET_USER) {
|
||||
if (extra instanceof WiredExtraUserVariable) {
|
||||
WiredExtraUserVariable definition = (WiredExtraUserVariable) extra;
|
||||
return new WiredVariableDefinitionInfo(
|
||||
definition.getId(),
|
||||
definition.getVariableName(),
|
||||
definition.hasValue(),
|
||||
definition.getAvailability(),
|
||||
WiredVariableTextConnectorSupport.isTextConnected(room, definition),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference && ((WiredExtraVariableReference) extra).isUserReference()) {
|
||||
WiredExtraVariableReference reference = (WiredExtraVariableReference) extra;
|
||||
return new WiredVariableDefinitionInfo(reference.getId(), reference.getVariableName(), reference.hasValue(), reference.getAvailability(), false, reference.isReadOnly());
|
||||
}
|
||||
}
|
||||
|
||||
if (targetType == TARGET_ROOM) {
|
||||
if (extra instanceof WiredExtraRoomVariable) {
|
||||
WiredExtraRoomVariable definition = (WiredExtraRoomVariable) extra;
|
||||
return new WiredVariableDefinitionInfo(
|
||||
definition.getId(),
|
||||
definition.getVariableName(),
|
||||
definition.hasValue(),
|
||||
definition.getAvailability(),
|
||||
WiredVariableTextConnectorSupport.isTextConnected(room, definition),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (extra instanceof WiredExtraVariableReference && ((WiredExtraVariableReference) extra).isRoomReference()) {
|
||||
WiredExtraVariableReference reference = (WiredExtraVariableReference) extra;
|
||||
return new WiredVariableDefinitionInfo(reference.getId(), reference.getVariableName(), reference.hasValue(), reference.getAvailability(), false, reference.isReadOnly());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int createSyntheticItemId(int targetType, int baseDefinitionItemId, int subvariableType) {
|
||||
int offset = switch (targetType) {
|
||||
case TARGET_FURNI -> SYNTHETIC_FURNI_OFFSET;
|
||||
case TARGET_ROOM -> SYNTHETIC_ROOM_OFFSET;
|
||||
default -> SYNTHETIC_USER_OFFSET;
|
||||
};
|
||||
|
||||
return offset + (baseDefinitionItemId * SYNTHETIC_STRIDE) + (subvariableType + 1);
|
||||
}
|
||||
|
||||
private static DecodedSyntheticId decodeSyntheticId(int syntheticItemId) {
|
||||
if (syntheticItemId >= SYNTHETIC_ROOM_OFFSET) {
|
||||
return decodeSyntheticId(syntheticItemId, TARGET_ROOM, SYNTHETIC_ROOM_OFFSET);
|
||||
}
|
||||
|
||||
if (syntheticItemId >= SYNTHETIC_FURNI_OFFSET) {
|
||||
return decodeSyntheticId(syntheticItemId, TARGET_FURNI, SYNTHETIC_FURNI_OFFSET);
|
||||
}
|
||||
|
||||
if (syntheticItemId >= SYNTHETIC_USER_OFFSET) {
|
||||
return decodeSyntheticId(syntheticItemId, TARGET_USER, SYNTHETIC_USER_OFFSET);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DecodedSyntheticId decodeSyntheticId(int syntheticItemId, int targetType, int offset) {
|
||||
int localValue = syntheticItemId - offset;
|
||||
if (localValue < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int encodedSubvariable = localValue % SYNTHETIC_STRIDE;
|
||||
int baseDefinitionItemId = localValue / SYNTHETIC_STRIDE;
|
||||
int subvariableType = encodedSubvariable - 1;
|
||||
|
||||
if (baseDefinitionItemId <= 0 || subvariableType < 0 || subvariableType >= WiredExtraVariableLevelUpSystem.SUBVARIABLE_COUNT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DecodedSyntheticId(targetType, baseDefinitionItemId, subvariableType);
|
||||
}
|
||||
|
||||
private static String getSubvariableKey(int subvariableType) {
|
||||
return switch (subvariableType) {
|
||||
case WiredExtraVariableLevelUpSystem.SUB_CURRENT_LEVEL -> "current_level";
|
||||
case WiredExtraVariableLevelUpSystem.SUB_CURRENT_XP -> "current_xp";
|
||||
case WiredExtraVariableLevelUpSystem.SUB_LEVEL_PROGRESS -> "level_progress";
|
||||
case WiredExtraVariableLevelUpSystem.SUB_LEVEL_PROGRESS_PERCENT -> "level_progress_percent";
|
||||
case WiredExtraVariableLevelUpSystem.SUB_TOTAL_XP_REQUIRED -> "total_xp_required";
|
||||
case WiredExtraVariableLevelUpSystem.SUB_XP_REMAINING -> "xp_remaining";
|
||||
case WiredExtraVariableLevelUpSystem.SUB_IS_AT_MAX -> "is_at_max";
|
||||
case WiredExtraVariableLevelUpSystem.SUB_MAX_LEVEL -> "max_level";
|
||||
default -> "value";
|
||||
};
|
||||
}
|
||||
|
||||
private static LevelProgress calculateProgress(WiredExtraVariableLevelUpSystem levelSystem, int rawBaseValue) {
|
||||
int currentXp = Math.max(0, rawBaseValue);
|
||||
List<LevelEntry> entries = buildThresholdEntries(levelSystem);
|
||||
|
||||
if (entries.isEmpty()) {
|
||||
entries = new ArrayList<>();
|
||||
entries.add(new LevelEntry(1, 0));
|
||||
}
|
||||
|
||||
int maxLevel = entries.get(entries.size() - 1).level;
|
||||
int currentLevel = 1;
|
||||
int currentThreshold = 0;
|
||||
int nextThreshold = 0;
|
||||
|
||||
for (int index = 0; index < entries.size(); index++) {
|
||||
LevelEntry entry = entries.get(index);
|
||||
|
||||
if (currentXp >= entry.requiredXp) {
|
||||
currentLevel = entry.level;
|
||||
currentThreshold = entry.requiredXp;
|
||||
nextThreshold = (index + 1 < entries.size()) ? entries.get(index + 1).requiredXp : entry.requiredXp;
|
||||
continue;
|
||||
}
|
||||
|
||||
nextThreshold = entry.requiredXp;
|
||||
break;
|
||||
}
|
||||
|
||||
boolean isAtMax = currentLevel >= maxLevel;
|
||||
|
||||
if (isAtMax) {
|
||||
nextThreshold = currentThreshold;
|
||||
}
|
||||
|
||||
int progressXp = Math.max(0, currentXp - currentThreshold);
|
||||
int progressPercent;
|
||||
|
||||
if (isAtMax) {
|
||||
progressPercent = 100;
|
||||
} else {
|
||||
int delta = Math.max(0, nextThreshold - currentThreshold);
|
||||
progressPercent = (delta <= 0) ? 100 : Math.max(0, Math.min(100, (int) Math.floor((progressXp * 100D) / delta)));
|
||||
}
|
||||
|
||||
int totalXpRequired = isAtMax ? currentThreshold : nextThreshold;
|
||||
int xpRemaining = Math.max(0, totalXpRequired - currentXp);
|
||||
|
||||
return new LevelProgress(currentLevel, currentXp, progressXp, progressPercent, totalXpRequired, xpRemaining, isAtMax, maxLevel);
|
||||
}
|
||||
|
||||
private static List<LevelEntry> buildThresholdEntries(WiredExtraVariableLevelUpSystem levelSystem) {
|
||||
return switch (levelSystem.getMode()) {
|
||||
case WiredExtraVariableLevelUpSystem.MODE_EXPONENTIAL -> buildExponentialEntries(levelSystem);
|
||||
case WiredExtraVariableLevelUpSystem.MODE_MANUAL -> buildManualEntries(levelSystem);
|
||||
default -> buildLinearEntries(levelSystem);
|
||||
};
|
||||
}
|
||||
|
||||
private static List<LevelEntry> buildLinearEntries(WiredExtraVariableLevelUpSystem levelSystem) {
|
||||
List<LevelEntry> entries = new ArrayList<>();
|
||||
int maxLevel = Math.max(1, levelSystem.getMaxLevel());
|
||||
int stepSize = Math.max(0, levelSystem.getStepSize());
|
||||
|
||||
for (int level = 1; level <= maxLevel; level++) {
|
||||
entries.add(new LevelEntry(level, clamp((long) (level - 1) * stepSize)));
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static List<LevelEntry> buildExponentialEntries(WiredExtraVariableLevelUpSystem levelSystem) {
|
||||
List<LevelEntry> entries = new ArrayList<>();
|
||||
int maxLevel = Math.max(1, levelSystem.getMaxLevel());
|
||||
int currentIncrement = Math.max(0, levelSystem.getFirstLevelXp());
|
||||
int factor = Math.max(0, levelSystem.getIncreaseFactor());
|
||||
long threshold = 0L;
|
||||
|
||||
entries.add(new LevelEntry(1, 0));
|
||||
|
||||
for (int level = 2; level <= maxLevel; level++) {
|
||||
threshold += currentIncrement;
|
||||
entries.add(new LevelEntry(level, clamp(threshold)));
|
||||
currentIncrement = clamp(Math.round(currentIncrement * (100D + factor) / 100D));
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static List<LevelEntry> buildManualEntries(WiredExtraVariableLevelUpSystem levelSystem) {
|
||||
LinkedHashMap<Integer, Integer> anchors = parseAnchors(levelSystem.getInterpolationText());
|
||||
if (!anchors.containsKey(1)) {
|
||||
anchors.put(1, 0);
|
||||
}
|
||||
|
||||
List<Map.Entry<Integer, Integer>> sortedAnchors = new ArrayList<>(anchors.entrySet());
|
||||
sortedAnchors.sort(Map.Entry.comparingByKey());
|
||||
|
||||
if (sortedAnchors.isEmpty()) {
|
||||
return Collections.singletonList(new LevelEntry(1, 0));
|
||||
}
|
||||
|
||||
LinkedHashMap<Integer, Integer> result = new LinkedHashMap<>();
|
||||
|
||||
for (int index = 0; index < sortedAnchors.size(); index++) {
|
||||
Map.Entry<Integer, Integer> current = sortedAnchors.get(index);
|
||||
int currentLevel = Math.max(1, current.getKey());
|
||||
int currentXp = Math.max(0, current.getValue());
|
||||
|
||||
result.put(currentLevel, currentXp);
|
||||
|
||||
if (index + 1 >= sortedAnchors.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<Integer, Integer> next = sortedAnchors.get(index + 1);
|
||||
int nextLevel = Math.max(currentLevel, next.getKey());
|
||||
int nextXp = Math.max(0, next.getValue());
|
||||
|
||||
if (nextLevel <= currentLevel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int level = currentLevel + 1; level < nextLevel; level++) {
|
||||
double ratio = (double) (level - currentLevel) / (double) (nextLevel - currentLevel);
|
||||
int interpolatedXp = clamp(Math.round(currentXp + ((nextXp - currentXp) * ratio)));
|
||||
result.put(level, interpolatedXp);
|
||||
}
|
||||
}
|
||||
|
||||
List<LevelEntry> entries = new ArrayList<>();
|
||||
for (Map.Entry<Integer, Integer> entry : result.entrySet()) {
|
||||
entries.add(new LevelEntry(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
|
||||
entries.sort(Comparator.comparingInt(levelEntry -> levelEntry.level));
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static LinkedHashMap<Integer, Integer> parseAnchors(String interpolationText) {
|
||||
LinkedHashMap<Integer, Integer> result = new LinkedHashMap<>();
|
||||
if (interpolationText == null || interpolationText.trim().isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (String rawLine : interpolationText.split("\n")) {
|
||||
if (rawLine == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String line = rawLine.trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int separatorIndex = line.indexOf('=');
|
||||
if (separatorIndex < 0) {
|
||||
separatorIndex = line.indexOf(',');
|
||||
}
|
||||
|
||||
if (separatorIndex <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer level = parseInteger(line.substring(0, separatorIndex));
|
||||
Integer xp = parseInteger(line.substring(separatorIndex + 1));
|
||||
|
||||
if (level == null || xp == null || level <= 0 || xp < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.put(level, xp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Integer parseInteger(String value) {
|
||||
try {
|
||||
return (value == null || value.trim().isEmpty()) ? null : Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clamp(long value) {
|
||||
if (value > Integer.MAX_VALUE) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
if (value < Integer.MIN_VALUE) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
public static class DerivedDefinition {
|
||||
private final int syntheticItemId;
|
||||
private final int baseDefinitionItemId;
|
||||
private final int subvariableType;
|
||||
private final String variableName;
|
||||
private final WiredVariableDefinitionInfo baseDefinition;
|
||||
private final WiredExtraVariableLevelUpSystem levelSystem;
|
||||
|
||||
public DerivedDefinition(int syntheticItemId, int baseDefinitionItemId, int subvariableType, String variableName, WiredVariableDefinitionInfo baseDefinition, WiredExtraVariableLevelUpSystem levelSystem) {
|
||||
this.syntheticItemId = syntheticItemId;
|
||||
this.baseDefinitionItemId = baseDefinitionItemId;
|
||||
this.subvariableType = subvariableType;
|
||||
this.variableName = variableName;
|
||||
this.baseDefinition = baseDefinition;
|
||||
this.levelSystem = levelSystem;
|
||||
}
|
||||
|
||||
public int getBaseDefinitionItemId() {
|
||||
return this.baseDefinitionItemId;
|
||||
}
|
||||
|
||||
public int getSubvariableType() {
|
||||
return this.subvariableType;
|
||||
}
|
||||
|
||||
public WiredVariableDefinitionInfo getBaseDefinition() {
|
||||
return this.baseDefinition;
|
||||
}
|
||||
|
||||
public WiredExtraVariableLevelUpSystem getLevelSystem() {
|
||||
return this.levelSystem;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LevelEntry {
|
||||
private final int level;
|
||||
private final int requiredXp;
|
||||
|
||||
public LevelEntry(int level, int requiredXp) {
|
||||
this.level = level;
|
||||
this.requiredXp = requiredXp;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
public int getRequiredXp() {
|
||||
return this.requiredXp;
|
||||
}
|
||||
}
|
||||
|
||||
private static class LevelProgress {
|
||||
private final int currentLevel;
|
||||
private final int currentXp;
|
||||
private final int progressXp;
|
||||
private final int progressPercent;
|
||||
private final int totalXpRequired;
|
||||
private final int xpRemaining;
|
||||
private final boolean isAtMax;
|
||||
private final int maxLevel;
|
||||
|
||||
private LevelProgress(int currentLevel, int currentXp, int progressXp, int progressPercent, int totalXpRequired, int xpRemaining, boolean isAtMax, int maxLevel) {
|
||||
this.currentLevel = currentLevel;
|
||||
this.currentXp = currentXp;
|
||||
this.progressXp = progressXp;
|
||||
this.progressPercent = progressPercent;
|
||||
this.totalXpRequired = totalXpRequired;
|
||||
this.xpRemaining = xpRemaining;
|
||||
this.isAtMax = isAtMax;
|
||||
this.maxLevel = maxLevel;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DecodedSyntheticId {
|
||||
private final int targetType;
|
||||
private final int baseDefinitionItemId;
|
||||
private final int subvariableType;
|
||||
|
||||
private DecodedSyntheticId(int targetType, int baseDefinitionItemId, int subvariableType) {
|
||||
this.targetType = targetType;
|
||||
this.baseDefinitionItemId = baseDefinitionItemId;
|
||||
this.subvariableType = subvariableType;
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.eu.habbo.habbohotel.wired.core;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraVariableTextConnector;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
|
||||
public final class WiredVariableTextConnectorSupport {
|
||||
private WiredVariableTextConnectorSupport() {
|
||||
}
|
||||
|
||||
public static boolean isTextConnected(Room room, InteractionWiredExtra definition) {
|
||||
return getConnector(room, definition) != null;
|
||||
}
|
||||
|
||||
public static boolean isTextConnected(Room room, int definitionItemId) {
|
||||
return getConnector(room, definitionItemId) != null;
|
||||
}
|
||||
|
||||
public static WiredExtraVariableTextConnector getConnector(Room room, int definitionItemId) {
|
||||
if (room == null || room.getRoomSpecialTypes() == null || definitionItemId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InteractionWiredExtra extra = room.getRoomSpecialTypes().getExtra(definitionItemId);
|
||||
return getConnector(room, extra);
|
||||
}
|
||||
|
||||
public static WiredExtraVariableTextConnector getConnector(Room room, InteractionWiredExtra definition) {
|
||||
if (room == null || definition == null || room.getRoomSpecialTypes() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(definition.getX(), definition.getY());
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (InteractionWiredExtra extra : WiredExecutionOrderUtil.sort(extras)) {
|
||||
if (extra instanceof WiredExtraVariableTextConnector) {
|
||||
return (WiredExtraVariableTextConnector) extra;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String toText(Room room, int definitionItemId, Integer value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
WiredExtraVariableTextConnector connector = getConnector(room, definitionItemId);
|
||||
return connector != null ? connector.resolveText(value) : String.valueOf(value);
|
||||
}
|
||||
|
||||
public static Integer toValue(Room room, int definitionItemId, String text) {
|
||||
WiredExtraVariableTextConnector connector = getConnector(room, definitionItemId);
|
||||
return connector != null ? connector.resolveValue(text) : null;
|
||||
}
|
||||
}
|
||||
@@ -167,9 +167,15 @@ public final class WiredEvents {
|
||||
* @return the event
|
||||
*/
|
||||
public static WiredEvent userSays(Room room, RoomUnit user, String message) {
|
||||
return userSays(room, user, message, -1, -1);
|
||||
}
|
||||
|
||||
public static WiredEvent userSays(Room room, RoomUnit user, String message, int chatType, int chatStyle) {
|
||||
return WiredEvent.builder(WiredEvent.Type.USER_SAYS, room)
|
||||
.actor(user)
|
||||
.text(message)
|
||||
.chatType(chatType)
|
||||
.chatStyle(chatStyle)
|
||||
.tile(user.getCurrentLocation())
|
||||
.build();
|
||||
}
|
||||
@@ -192,6 +198,42 @@ public final class WiredEvents {
|
||||
.build();
|
||||
}
|
||||
|
||||
public static WiredEvent userVariableChanged(Room room, RoomUnit user, int definitionItemId, boolean created, boolean deleted, WiredEvent.VariableChangeKind changeKind) {
|
||||
return WiredEvent.builder(WiredEvent.Type.VARIABLE_CHANGED, room)
|
||||
.actor(user)
|
||||
.tile((user != null) ? user.getCurrentLocation() : null)
|
||||
.variableTargetType(0)
|
||||
.variableDefinitionItemId(definitionItemId)
|
||||
.variableCreated(created)
|
||||
.variableDeleted(deleted)
|
||||
.variableChangeKind(changeKind)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static WiredEvent furniVariableChanged(Room room, HabboItem item, int definitionItemId, boolean created, boolean deleted, WiredEvent.VariableChangeKind changeKind) {
|
||||
RoomTile tile = (item != null) ? room.getLayout().getTile(item.getX(), item.getY()) : null;
|
||||
|
||||
return WiredEvent.builder(WiredEvent.Type.VARIABLE_CHANGED, room)
|
||||
.sourceItem(item)
|
||||
.tile(tile)
|
||||
.variableTargetType(1)
|
||||
.variableDefinitionItemId(definitionItemId)
|
||||
.variableCreated(created)
|
||||
.variableDeleted(deleted)
|
||||
.variableChangeKind(changeKind)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static WiredEvent roomVariableChanged(Room room, int definitionItemId, WiredEvent.VariableChangeKind changeKind) {
|
||||
return WiredEvent.builder(WiredEvent.Type.VARIABLE_CHANGED, room)
|
||||
.variableTargetType(3)
|
||||
.variableDefinitionItemId(definitionItemId)
|
||||
.variableCreated(false)
|
||||
.variableDeleted(false)
|
||||
.variableChangeKind(changeKind)
|
||||
.build();
|
||||
}
|
||||
|
||||
// ========== Timer Events ==========
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.eu.habbo.messages.incoming.ambassadors.AmbassadorVisitCommandEvent;
|
||||
import com.eu.habbo.messages.incoming.camera.*;
|
||||
import com.eu.habbo.messages.incoming.catalog.*;
|
||||
import com.eu.habbo.messages.incoming.catalog.catalogadmin.*;
|
||||
import com.eu.habbo.messages.incoming.furnieditor.*;
|
||||
import com.eu.habbo.messages.incoming.catalog.marketplace.*;
|
||||
import com.eu.habbo.messages.incoming.catalog.recycler.OpenRecycleBoxEvent;
|
||||
import com.eu.habbo.messages.incoming.catalog.recycler.RecycleEvent;
|
||||
@@ -66,6 +67,13 @@ import com.eu.habbo.messages.incoming.users.*;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredApplySetConditionsEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredConditionSaveDataEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredEffectSaveDataEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredMonitorRequestEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredRoomSettingsRequestEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredRoomSettingsSaveEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredUserInspectMoveEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredUserVariableManageEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredUserVariableUpdateEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredUserVariablesRequestEvent;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveDataEvent;
|
||||
import com.eu.habbo.plugin.EventHandler;
|
||||
import com.eu.habbo.plugin.events.emulator.EmulatorConfigUpdatedEvent;
|
||||
@@ -260,6 +268,14 @@ public class PacketManager {
|
||||
this.registerHandler(Incoming.CatalogRequestClubDiscountEvent, CatalogRequestClubDiscountEvent.class);
|
||||
this.registerHandler(Incoming.CatalogBuyClubDiscountEvent, CatalogBuyClubDiscountEvent.class);
|
||||
|
||||
// Furni Editor
|
||||
this.registerHandler(Incoming.FurniEditorSearchEvent, FurniEditorSearchEvent.class);
|
||||
this.registerHandler(Incoming.FurniEditorDetailEvent, FurniEditorDetailEvent.class);
|
||||
this.registerHandler(Incoming.FurniEditorBySpriteEvent, FurniEditorBySpriteEvent.class);
|
||||
this.registerHandler(Incoming.FurniEditorInteractionsEvent, FurniEditorInteractionsEvent.class);
|
||||
this.registerHandler(Incoming.FurniEditorUpdateEvent, FurniEditorUpdateEvent.class);
|
||||
this.registerHandler(Incoming.FurniEditorDeleteEvent, FurniEditorDeleteEvent.class);
|
||||
|
||||
// Catalog Admin
|
||||
this.registerHandler(Incoming.CatalogAdminSavePageEvent, CatalogAdminSavePageEvent.class);
|
||||
this.registerHandler(Incoming.CatalogAdminCreatePageEvent, CatalogAdminCreatePageEvent.class);
|
||||
@@ -572,6 +588,7 @@ public class PacketManager {
|
||||
this.registerHandler(Incoming.GuildForumModerateMessageEvent, GuildForumModerateMessageEvent.class);
|
||||
this.registerHandler(Incoming.GuildForumModerateThreadEvent, GuildForumModerateThreadEvent.class);
|
||||
this.registerHandler(Incoming.GuildForumThreadUpdateEvent, GuildForumThreadUpdateEvent.class);
|
||||
this.registerHandler(Incoming.GuildForumMarkAsReadEvent, GuildForumMarkAsReadEvent.class);
|
||||
this.registerHandler(Incoming.GetHabboGuildBadgesMessageEvent, GetHabboGuildBadgesMessageEvent.class);
|
||||
|
||||
// this.registerHandler(Incoming.GuildForumDataEvent, GuildForumModerateMessageEvent.class);
|
||||
@@ -605,6 +622,13 @@ public class PacketManager {
|
||||
this.registerHandler(Incoming.WiredEffectSaveDataEvent, WiredEffectSaveDataEvent.class);
|
||||
this.registerHandler(Incoming.WiredConditionSaveDataEvent, WiredConditionSaveDataEvent.class);
|
||||
this.registerHandler(Incoming.WiredApplySetConditionsEvent, WiredApplySetConditionsEvent.class);
|
||||
this.registerHandler(Incoming.WiredMonitorRequestEvent, WiredMonitorRequestEvent.class);
|
||||
this.registerHandler(Incoming.WiredRoomSettingsRequestEvent, WiredRoomSettingsRequestEvent.class);
|
||||
this.registerHandler(Incoming.WiredRoomSettingsSaveEvent, WiredRoomSettingsSaveEvent.class);
|
||||
this.registerHandler(Incoming.WiredUserVariablesRequestEvent, WiredUserVariablesRequestEvent.class);
|
||||
this.registerHandler(Incoming.WiredUserVariableUpdateEvent, WiredUserVariableUpdateEvent.class);
|
||||
this.registerHandler(Incoming.WiredUserVariableManageEvent, WiredUserVariableManageEvent.class);
|
||||
this.registerHandler(Incoming.WiredUserInspectMoveEvent, WiredUserInspectMoveEvent.class);
|
||||
}
|
||||
|
||||
void registerUnknown() throws Exception {
|
||||
|
||||
@@ -409,9 +409,24 @@ public class Incoming {
|
||||
// CUSTOM
|
||||
public static final int UpdateFurniturePositionEvent = 10019;
|
||||
public static final int ClickUserEvent = 10020;
|
||||
public static final int WiredMonitorRequestEvent = 10021;
|
||||
public static final int WiredRoomSettingsRequestEvent = 10022;
|
||||
public static final int WiredRoomSettingsSaveEvent = 10023;
|
||||
public static final int WiredUserVariablesRequestEvent = 10024;
|
||||
public static final int WiredUserVariableUpdateEvent = 10025;
|
||||
public static final int WiredUserVariableManageEvent = 10026;
|
||||
public static final int WiredUserInspectMoveEvent = 10027;
|
||||
public static final int RequestInventoryPetDelete = 10030;
|
||||
public static final int RequestInventoryBadgeDelete = 10031;
|
||||
|
||||
// Furni Editor
|
||||
public static final int FurniEditorSearchEvent = 10040;
|
||||
public static final int FurniEditorDetailEvent = 10041;
|
||||
public static final int FurniEditorBySpriteEvent = 10042;
|
||||
public static final int FurniEditorInteractionsEvent = 10043;
|
||||
public static final int FurniEditorUpdateEvent = 10044;
|
||||
public static final int FurniEditorDeleteEvent = 10045;
|
||||
|
||||
// Catalog Admin
|
||||
public static final int CatalogAdminSavePageEvent = 10050;
|
||||
public static final int CatalogAdminCreatePageEvent = 10051;
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package com.eu.habbo.messages.incoming.furnieditor;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Manages reading and writing of FurnitureData.json entries.
|
||||
* Resolves the file path from emulator config keys.
|
||||
*/
|
||||
public class FurniDataManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FurniDataManager.class);
|
||||
|
||||
/**
|
||||
* Get the JSON string for a specific item from FurnitureData.json.
|
||||
* Returns "{}" if not found or on error.
|
||||
*/
|
||||
public static String getItemJson(int itemId) {
|
||||
try {
|
||||
Path furniDataPath = resolveFurniDataPath();
|
||||
if (furniDataPath == null || !Files.exists(furniDataPath)) {
|
||||
return "{}";
|
||||
}
|
||||
|
||||
String content = Files.readString(furniDataPath, StandardCharsets.UTF_8);
|
||||
JsonObject root = JsonParser.parseString(content).getAsJsonObject();
|
||||
|
||||
// Search in both "roomitemtypes" and "wallitemtypes"
|
||||
for (String section : new String[]{"roomitemtypes", "wallitemtypes"}) {
|
||||
if (!root.has(section)) continue;
|
||||
JsonObject sectionObj = root.getAsJsonObject(section);
|
||||
if (!sectionObj.has("furnitype")) continue;
|
||||
JsonArray types = sectionObj.getAsJsonArray("furnitype");
|
||||
|
||||
for (JsonElement el : types) {
|
||||
JsonObject obj = el.getAsJsonObject();
|
||||
if (obj.has("id") && obj.get("id").getAsInt() == itemId) {
|
||||
return obj.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Failed to read FurnitureData.json for item " + itemId, e);
|
||||
}
|
||||
|
||||
return "{}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the path to FurnitureData.json from emulator config.
|
||||
*/
|
||||
private static Path resolveFurniDataPath() {
|
||||
try {
|
||||
String configPath = Emulator.getConfig().getValue("furni.editor.renderer.config.path", "");
|
||||
|
||||
if (configPath.isEmpty()) {
|
||||
// Fallback: try common locations
|
||||
String basePath = Emulator.getConfig().getValue("furni.editor.asset.base.path", "");
|
||||
if (!basePath.isEmpty()) {
|
||||
Path candidate = Paths.get(basePath, "FurnitureData.json");
|
||||
if (Files.exists(candidate)) return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Read the renderer config to find the furnidata URL/path
|
||||
Path rendererConfig = Paths.get(configPath);
|
||||
if (!Files.exists(rendererConfig)) return null;
|
||||
|
||||
String rendererContent = Files.readString(rendererConfig, StandardCharsets.UTF_8);
|
||||
JsonObject rendererObj = JsonParser.parseString(rendererContent).getAsJsonObject();
|
||||
|
||||
if (rendererObj.has("furnidata.url")) {
|
||||
String furniUrl = rendererObj.get("furnidata.url").getAsString();
|
||||
|
||||
// Skip unresolved placeholders like ${gamedata.url}
|
||||
if (furniUrl.contains("${")) {
|
||||
String basePath = Emulator.getConfig().getValue("furni.editor.asset.base.path", "");
|
||||
if (!basePath.isEmpty()) {
|
||||
Path candidate = Paths.get(basePath, "FurnitureData.json");
|
||||
if (Files.exists(candidate)) return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Strip query string (?v=1 etc.)
|
||||
String cleanUrl = furniUrl.contains("?") ? furniUrl.substring(0, furniUrl.indexOf('?')) : furniUrl;
|
||||
|
||||
// If it's a local file path (not http), use it directly
|
||||
if (!cleanUrl.startsWith("http")) {
|
||||
return Paths.get(cleanUrl);
|
||||
}
|
||||
|
||||
// For http URLs, try to derive local path from base path
|
||||
String basePath = Emulator.getConfig().getValue("furni.editor.asset.base.path", "");
|
||||
if (!basePath.isEmpty()) {
|
||||
// Extract filename from URL (without query string)
|
||||
String filename = cleanUrl.substring(cleanUrl.lastIndexOf('/') + 1);
|
||||
return Paths.get(basePath, filename);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Failed to resolve FurnitureData.json path", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.eu.habbo.messages.incoming.furnieditor;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorResultComposer;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
public class FurniEditorBySpriteEvent extends MessageHandler {
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
if (!this.client.getHabbo().hasPermission(Permission.ACC_CATALOGFURNI)) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
int spriteId = this.packet.readInt();
|
||||
|
||||
if (spriteId <= 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "Invalid sprite ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Look up the item ID by sprite_id
|
||||
int itemId = -1;
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement stmt = connection.prepareStatement("SELECT id FROM items_base WHERE sprite_id = ? LIMIT 1")) {
|
||||
stmt.setInt(1, spriteId);
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
itemId = rs.getInt("id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemId <= 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No item found with sprite_id: " + spriteId));
|
||||
return;
|
||||
}
|
||||
|
||||
// Delegate to the detail response builder
|
||||
FurniEditorDetailEvent.sendDetailResponse(this.client, itemId);
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.eu.habbo.messages.incoming.furnieditor;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorResultComposer;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
public class FurniEditorDeleteEvent extends MessageHandler {
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
if (!this.client.getHabbo().hasPermission(Permission.ACC_CATALOGFURNI)) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
int id = this.packet.readInt();
|
||||
|
||||
if (id <= 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "Invalid item ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
// Check if item exists
|
||||
try (PreparedStatement stmt = connection.prepareStatement("SELECT id FROM items_base WHERE id = ?")) {
|
||||
stmt.setInt(1, id);
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (!rs.next()) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "Item not found: " + id));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check usage count - items placed in rooms
|
||||
int usageCount = 0;
|
||||
try (PreparedStatement stmt = connection.prepareStatement("SELECT COUNT(*) FROM items WHERE item_id = ?")) {
|
||||
stmt.setInt(1, id);
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
usageCount = rs.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usageCount > 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false,
|
||||
"Cannot delete: " + usageCount + " instances exist in the game"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check catalog_items references
|
||||
int catalogCount = 0;
|
||||
try (PreparedStatement stmt = connection.prepareStatement(
|
||||
"SELECT COUNT(*) FROM catalog_items WHERE item_ids LIKE ?")) {
|
||||
stmt.setString(1, "%" + id + "%");
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
catalogCount = rs.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (catalogCount > 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false,
|
||||
"Cannot delete: item is referenced by " + catalogCount + " catalog entries"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Safe to delete
|
||||
try (PreparedStatement stmt = connection.prepareStatement("DELETE FROM items_base WHERE id = ?")) {
|
||||
stmt.setInt(1, id);
|
||||
stmt.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// Reload emulator item definitions
|
||||
Emulator.getGameEnvironment().getItemManager().loadItems();
|
||||
|
||||
this.client.sendResponse(new FurniEditorResultComposer(true, "Item deleted"));
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package com.eu.habbo.messages.incoming.furnieditor;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorDetailComposer;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorResultComposer;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FurniEditorDetailEvent extends MessageHandler {
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
if (!this.client.getHabbo().hasPermission(Permission.ACC_CATALOGFURNI)) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
int id = this.packet.readInt();
|
||||
|
||||
if (id <= 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "Invalid item ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
sendDetailResponse(this.client, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared method to build and send a detail response for a given item ID.
|
||||
* Used by both FurniEditorDetailEvent and FurniEditorBySpriteEvent.
|
||||
*/
|
||||
public static void sendDetailResponse(com.eu.habbo.habbohotel.gameclients.GameClient client, int itemId) throws Exception {
|
||||
Map<String, Object> item = null;
|
||||
int usageCount = 0;
|
||||
List<Map<String, Object>> catalogItems = new ArrayList<>();
|
||||
String furniDataJson = "{}";
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
// Load full item data
|
||||
try (PreparedStatement stmt = connection.prepareStatement("SELECT * FROM items_base WHERE id = ?")) {
|
||||
stmt.setInt(1, itemId);
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
item = FurniEditorHelper.readFullItem(rs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item == null) {
|
||||
client.sendResponse(new FurniEditorResultComposer(false, "Item not found: " + itemId));
|
||||
return;
|
||||
}
|
||||
|
||||
// Count placed instances
|
||||
try (PreparedStatement stmt = connection.prepareStatement("SELECT COUNT(*) FROM items WHERE item_id = ?")) {
|
||||
stmt.setInt(1, itemId);
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
usageCount = rs.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load catalog references (join catalog_items with catalog_pages)
|
||||
try (PreparedStatement stmt = connection.prepareStatement(
|
||||
"SELECT ci.id AS ci_id, ci.catalog_name, ci.cost_credits, ci.cost_points, ci.points_type, " +
|
||||
"ci.page_id AS ci_page_id, COALESCE(cp.caption, '') AS page_caption " +
|
||||
"FROM catalog_items ci " +
|
||||
"LEFT JOIN catalog_pages cp ON ci.page_id = cp.id " +
|
||||
"WHERE ci.item_ids LIKE ?")) {
|
||||
stmt.setString(1, "%" + itemId + "%");
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
catalogItems.add(FurniEditorHelper.readCatalogRef(rs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to read furnidata.json entry
|
||||
try {
|
||||
furniDataJson = FurniDataManager.getItemJson(itemId);
|
||||
} catch (Exception e) {
|
||||
furniDataJson = "{}";
|
||||
}
|
||||
|
||||
client.sendResponse(new FurniEditorDetailComposer(item, usageCount, catalogItems, furniDataJson));
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package com.eu.habbo.messages.incoming.furnieditor;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Shared utility for building item data maps from ResultSet rows.
|
||||
* Used by FurniEditorDetailEvent, FurniEditorBySpriteEvent, and
|
||||
* FurniEditorSearchEvent to ensure consistent field reading.
|
||||
*/
|
||||
public class FurniEditorHelper {
|
||||
|
||||
/**
|
||||
* Read the 14 base fields from items_base into a Map.
|
||||
*/
|
||||
public static Map<String, Object> readBaseItem(ResultSet set) throws SQLException {
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("id", set.getInt("id"));
|
||||
item.put("sprite_id", set.getInt("sprite_id"));
|
||||
item.put("item_name", set.getString("item_name"));
|
||||
item.put("public_name", set.getString("public_name"));
|
||||
item.put("type", set.getString("type"));
|
||||
item.put("width", set.getInt("width"));
|
||||
item.put("length", set.getInt("length"));
|
||||
item.put("stack_height", set.getDouble("stack_height"));
|
||||
item.put("allow_stack", set.getString("allow_stack"));
|
||||
item.put("allow_walk", set.getString("allow_walk"));
|
||||
item.put("allow_sit", set.getString("allow_sit"));
|
||||
item.put("allow_lay", set.getString("allow_lay"));
|
||||
item.put("interaction_type", set.getString("interaction_type"));
|
||||
item.put("interaction_modes_count", set.getInt("interaction_modes_count"));
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all fields (14 base + 13 extended) from items_base into a Map.
|
||||
*/
|
||||
public static Map<String, Object> readFullItem(ResultSet set) throws SQLException {
|
||||
Map<String, Object> item = readBaseItem(set);
|
||||
item.put("allow_gift", set.getString("allow_gift"));
|
||||
item.put("allow_trade", set.getString("allow_trade"));
|
||||
item.put("allow_recycle", set.getString("allow_recycle"));
|
||||
item.put("allow_marketplace_sell", set.getString("allow_marketplace_sell"));
|
||||
item.put("allow_inventory_stack", set.getString("allow_inventory_stack"));
|
||||
item.put("vending_ids", set.getString("vending_ids"));
|
||||
item.put("customparams", set.getString("customparams"));
|
||||
item.put("effect_id_male", set.getInt("effect_id_male"));
|
||||
item.put("effect_id_female", set.getInt("effect_id_female"));
|
||||
item.put("clothing_on_walk", set.getString("clothing_on_walk"));
|
||||
item.put("multiheight", set.getString("multiheight"));
|
||||
|
||||
// description may not exist in all schemas, handle gracefully
|
||||
try {
|
||||
item.put("description", set.getString("description"));
|
||||
} catch (SQLException e) {
|
||||
item.put("description", "");
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a catalog item reference from a result set that joined
|
||||
* catalog_items with catalog_pages.
|
||||
*/
|
||||
public static Map<String, Object> readCatalogRef(ResultSet set) throws SQLException {
|
||||
Map<String, Object> ref = new HashMap<>();
|
||||
ref.put("id", set.getInt("ci_id"));
|
||||
ref.put("catalog_name", set.getString("catalog_name"));
|
||||
ref.put("cost_credits", set.getInt("cost_credits"));
|
||||
ref.put("cost_points", set.getInt("cost_points"));
|
||||
ref.put("points_type", set.getInt("points_type"));
|
||||
ref.put("page_id", set.getInt("ci_page_id"));
|
||||
ref.put("page_caption", set.getString("page_caption"));
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whitelist of allowed field names for update operations.
|
||||
* Prevents SQL injection via arbitrary column names.
|
||||
*/
|
||||
public static final java.util.Set<String> ALLOWED_UPDATE_FIELDS = java.util.Set.of(
|
||||
"item_name", "public_name", "sprite_id", "type", "width", "length",
|
||||
"stack_height", "allow_stack", "allow_walk", "allow_sit", "allow_lay",
|
||||
"allow_gift", "allow_trade", "allow_recycle", "allow_marketplace_sell",
|
||||
"allow_inventory_stack", "interaction_type", "interaction_modes_count",
|
||||
"vending_ids", "customparams", "effect_id_male", "effect_id_female",
|
||||
"clothing_on_walk", "multiheight", "description"
|
||||
);
|
||||
|
||||
/**
|
||||
* Map camelCase JS field names to DB column names.
|
||||
*/
|
||||
public static final Map<String, String> FIELD_MAP = Map.ofEntries(
|
||||
Map.entry("itemName", "item_name"),
|
||||
Map.entry("publicName", "public_name"),
|
||||
Map.entry("spriteId", "sprite_id"),
|
||||
Map.entry("type", "type"),
|
||||
Map.entry("width", "width"),
|
||||
Map.entry("length", "length"),
|
||||
Map.entry("stackHeight", "stack_height"),
|
||||
Map.entry("allowStack", "allow_stack"),
|
||||
Map.entry("allowWalk", "allow_walk"),
|
||||
Map.entry("allowSit", "allow_sit"),
|
||||
Map.entry("allowLay", "allow_lay"),
|
||||
Map.entry("allowGift", "allow_gift"),
|
||||
Map.entry("allowTrade", "allow_trade"),
|
||||
Map.entry("allowRecycle", "allow_recycle"),
|
||||
Map.entry("allowMarketplaceSell", "allow_marketplace_sell"),
|
||||
Map.entry("allowInventoryStack", "allow_inventory_stack"),
|
||||
Map.entry("interactionType", "interaction_type"),
|
||||
Map.entry("interactionModesCount", "interaction_modes_count"),
|
||||
Map.entry("vendingIds", "vending_ids"),
|
||||
Map.entry("customparams", "customparams"),
|
||||
Map.entry("effectIdMale", "effect_id_male"),
|
||||
Map.entry("effectIdFemale", "effect_id_female"),
|
||||
Map.entry("clothingOnWalk", "clothing_on_walk"),
|
||||
Map.entry("multiheight", "multiheight"),
|
||||
Map.entry("description", "description")
|
||||
);
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.eu.habbo.messages.incoming.furnieditor;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorInteractionsComposer;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorResultComposer;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class FurniEditorInteractionsEvent extends MessageHandler {
|
||||
|
||||
private static List<String> cachedInteractions = null;
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
if (!this.client.getHabbo().hasPermission(Permission.ACC_CATALOGFURNI)) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cachedInteractions == null) {
|
||||
synchronized (FurniEditorInteractionsEvent.class) {
|
||||
if (cachedInteractions == null) {
|
||||
List<String> list = new ArrayList<>();
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet set = stmt.executeQuery("SELECT DISTINCT interaction_type FROM items_base WHERE interaction_type != '' ORDER BY interaction_type ASC")) {
|
||||
while (set.next()) {
|
||||
list.add(set.getString("interaction_type"));
|
||||
}
|
||||
}
|
||||
cachedInteractions = Collections.unmodifiableList(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.client.sendResponse(new FurniEditorInteractionsComposer(cachedInteractions));
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package com.eu.habbo.messages.incoming.furnieditor;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorResultComposer;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorSearchComposer;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FurniEditorSearchEvent extends MessageHandler {
|
||||
|
||||
private static final int PAGE_SIZE = 20;
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
if (!this.client.getHabbo().hasPermission(Permission.ACC_CATALOGFURNI)) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
String query = this.packet.readString();
|
||||
String type = this.packet.readString();
|
||||
int page = this.packet.readInt();
|
||||
|
||||
// Input validation
|
||||
if (query.length() > 100) {
|
||||
query = query.substring(0, 100);
|
||||
}
|
||||
|
||||
if (page < 1) page = 1;
|
||||
|
||||
int offset = (page - 1) * PAGE_SIZE;
|
||||
|
||||
// Build WHERE clause
|
||||
StringBuilder whereClause = new StringBuilder("WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (!query.isEmpty()) {
|
||||
// Try numeric match first (id or sprite_id)
|
||||
boolean isNumeric = false;
|
||||
try {
|
||||
int numericQuery = Integer.parseInt(query);
|
||||
isNumeric = true;
|
||||
whereClause.append(" AND (id = ? OR sprite_id = ? OR item_name LIKE ? OR public_name LIKE ?)");
|
||||
params.add(numericQuery);
|
||||
params.add(numericQuery);
|
||||
params.add("%" + query + "%");
|
||||
params.add("%" + query + "%");
|
||||
} catch (NumberFormatException e) {
|
||||
whereClause.append(" AND (item_name LIKE ? OR public_name LIKE ?)");
|
||||
params.add("%" + query + "%");
|
||||
params.add("%" + query + "%");
|
||||
}
|
||||
}
|
||||
|
||||
if (type != null && !type.isEmpty()) {
|
||||
whereClause.append(" AND type = ?");
|
||||
params.add(type);
|
||||
}
|
||||
|
||||
// Count total
|
||||
int total = 0;
|
||||
String countSql = "SELECT COUNT(*) FROM items_base " + whereClause;
|
||||
String dataSql = "SELECT * FROM items_base " + whereClause + " ORDER BY id ASC LIMIT ? OFFSET ?";
|
||||
|
||||
List<Map<String, Object>> items = new ArrayList<>();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
// Get total count
|
||||
try (PreparedStatement stmt = connection.prepareStatement(countSql)) {
|
||||
int idx = 1;
|
||||
for (Object param : params) {
|
||||
if (param instanceof Integer) {
|
||||
stmt.setInt(idx++, (Integer) param);
|
||||
} else {
|
||||
stmt.setString(idx++, (String) param);
|
||||
}
|
||||
}
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
total = rs.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get items page
|
||||
try (PreparedStatement stmt = connection.prepareStatement(dataSql)) {
|
||||
int idx = 1;
|
||||
for (Object param : params) {
|
||||
if (param instanceof Integer) {
|
||||
stmt.setInt(idx++, (Integer) param);
|
||||
} else {
|
||||
stmt.setString(idx++, (String) param);
|
||||
}
|
||||
}
|
||||
stmt.setInt(idx++, PAGE_SIZE);
|
||||
stmt.setInt(idx, offset);
|
||||
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
items.add(FurniEditorHelper.readBaseItem(rs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.client.sendResponse(new FurniEditorSearchComposer(items, total, page));
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package com.eu.habbo.messages.incoming.furnieditor;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorResultComposer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FurniEditorUpdateEvent extends MessageHandler {
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
if (!this.client.getHabbo().hasPermission(Permission.ACC_CATALOGFURNI)) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
int id = this.packet.readInt();
|
||||
String jsonFieldsStr = this.packet.readString();
|
||||
|
||||
if (id <= 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "Invalid item ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject json;
|
||||
try {
|
||||
json = JsonParser.parseString(jsonFieldsStr).getAsJsonObject();
|
||||
} catch (Exception e) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "Invalid JSON data"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (json.size() == 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No fields to update"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build dynamic UPDATE with whitelisted fields
|
||||
StringBuilder setClauses = new StringBuilder();
|
||||
List<Object> values = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : json.entrySet()) {
|
||||
String jsKey = entry.getKey();
|
||||
String dbColumn = FurniEditorHelper.FIELD_MAP.get(jsKey);
|
||||
|
||||
if (dbColumn == null || !FurniEditorHelper.ALLOWED_UPDATE_FIELDS.contains(dbColumn)) {
|
||||
continue; // Skip unknown or disallowed fields
|
||||
}
|
||||
|
||||
if (setClauses.length() > 0) setClauses.append(", ");
|
||||
setClauses.append("`").append(dbColumn).append("` = ?");
|
||||
|
||||
JsonElement val = entry.getValue();
|
||||
if (val.isJsonPrimitive()) {
|
||||
if (val.getAsJsonPrimitive().isBoolean()) {
|
||||
values.add(val.getAsBoolean() ? "1" : "0");
|
||||
} else if (val.getAsJsonPrimitive().isNumber()) {
|
||||
// Check if it's a decimal number
|
||||
String numStr = val.getAsString();
|
||||
if (numStr.contains(".")) {
|
||||
values.add(val.getAsDouble());
|
||||
} else {
|
||||
values.add(val.getAsInt());
|
||||
}
|
||||
} else {
|
||||
values.add(val.getAsString());
|
||||
}
|
||||
} else {
|
||||
values.add(val.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (setClauses.length() == 0) {
|
||||
this.client.sendResponse(new FurniEditorResultComposer(false, "No valid fields to update"));
|
||||
return;
|
||||
}
|
||||
|
||||
String sql = "UPDATE items_base SET " + setClauses + " WHERE id = ?";
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
PreparedStatement stmt = connection.prepareStatement(sql)) {
|
||||
int idx = 1;
|
||||
for (Object value : values) {
|
||||
if (value instanceof Integer) {
|
||||
stmt.setInt(idx++, (Integer) value);
|
||||
} else if (value instanceof Double) {
|
||||
stmt.setDouble(idx++, (Double) value);
|
||||
} else {
|
||||
stmt.setString(idx++, String.valueOf(value));
|
||||
}
|
||||
}
|
||||
stmt.setInt(idx, id);
|
||||
stmt.executeUpdate();
|
||||
}
|
||||
|
||||
// Reload emulator item definitions
|
||||
Emulator.getGameEnvironment().getItemManager().loadItems();
|
||||
|
||||
this.client.sendResponse(new FurniEditorResultComposer(true, "Item updated", id));
|
||||
}
|
||||
}
|
||||
+78
-1
@@ -2,16 +2,34 @@ package com.eu.habbo.messages.incoming.guilds;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.guilds.Guild;
|
||||
import com.eu.habbo.habbohotel.guilds.GuildMember;
|
||||
import com.eu.habbo.habbohotel.guilds.GuildState;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.habbohotel.guilds.forums.ForumThread;
|
||||
import com.eu.habbo.messages.incoming.guilds.forums.GuildForumListEvent;
|
||||
import com.eu.habbo.messages.outgoing.guilds.GuildInfoComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import com.eu.habbo.plugin.events.guilds.GuildChangedSettingsEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class GuildChangeSettingsEvent extends MessageHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildChangeSettingsEvent.class);
|
||||
|
||||
// Cooldown for forum toggle per guild: guildId -> last toggle timestamp
|
||||
private static final ConcurrentHashMap<Integer, Long> forumToggleCooldown = new ConcurrentHashMap<>();
|
||||
private static final long FORUM_TOGGLE_COOLDOWN_MS = 30_000; // 30 seconds
|
||||
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 500;
|
||||
return 2000; // 2 seconds between settings saves
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -31,6 +49,34 @@ public class GuildChangeSettingsEvent extends MessageHandler {
|
||||
guild.setState(GuildState.valueOf(settingsEvent.state));
|
||||
guild.setRights(settingsEvent.rights);
|
||||
|
||||
// Read forum toggle
|
||||
boolean forumEnabled = this.packet.readBoolean();
|
||||
boolean wasForumEnabled = guild.hasForum();
|
||||
|
||||
if (forumEnabled != wasForumEnabled) {
|
||||
// Enforce cooldown on forum toggle to prevent rapid enable/disable spam
|
||||
Long lastToggle = forumToggleCooldown.get(guildId);
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (lastToggle != null && (now - lastToggle) < FORUM_TOGGLE_COOLDOWN_MS) {
|
||||
LOGGER.warn("Forum toggle cooldown for guild {} by user {}", guildId, this.client.getHabbo().getHabboInfo().getUsername());
|
||||
} else {
|
||||
forumToggleCooldown.put(guildId, now);
|
||||
guild.setForum(forumEnabled);
|
||||
|
||||
if (!forumEnabled) {
|
||||
// Delete all threads and comments for this guild
|
||||
ForumThread.clearCacheForGuild(guildId);
|
||||
deleteForumData(guildId);
|
||||
}
|
||||
|
||||
// Invalidate caches
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
GuildForumListEvent.invalidateActiveForumsCache();
|
||||
GuildForumListEvent.invalidateMyForumsCache(this.client.getHabbo().getHabboInfo().getId());
|
||||
}
|
||||
}
|
||||
|
||||
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(guild.getRoomId());
|
||||
if(room != null) {
|
||||
room.refreshGuild(guild);
|
||||
@@ -39,7 +85,38 @@ public class GuildChangeSettingsEvent extends MessageHandler {
|
||||
guild.needsUpdate = true;
|
||||
|
||||
Emulator.getThreading().run(guild);
|
||||
|
||||
// Send updated group info back to client so hasForum flag refreshes immediately
|
||||
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guild, this.client.getHabbo());
|
||||
this.client.sendResponse(new GuildInfoComposer(guild, this.client, false, member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteForumData(int guildId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
// Delete comments for all threads in this guild
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guilds_forums_comments` WHERE `thread_id` IN (SELECT `id` FROM `guilds_forums_threads` WHERE `guild_id` = ?)")) {
|
||||
statement.setInt(1, guildId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
// Delete all threads for this guild
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guilds_forums_threads` WHERE `guild_id` = ?")) {
|
||||
statement.setInt(1, guildId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
// Delete forum view records for this guild
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guild_forum_views` WHERE `guild_id` = ?")) {
|
||||
statement.setInt(1, guildId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to delete forum data for guild " + guildId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+43
-2
@@ -14,6 +14,7 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class GuildForumListEvent extends MessageHandler {
|
||||
@Override
|
||||
@@ -23,6 +24,26 @@ public class GuildForumListEvent extends MessageHandler {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildForumListEvent.class);
|
||||
|
||||
// Cache for active forums list (shared across all users)
|
||||
private static volatile THashSet<Guild> activeForumsCache = null;
|
||||
private static volatile long activeForumsCachedAt = 0;
|
||||
private static final long ACTIVE_FORUMS_TTL = 30 * 60 * 1000; // 30 minutes
|
||||
|
||||
// Cache for user's forum list
|
||||
private static final ConcurrentHashMap<Integer, long[]> myForumsCache = new ConcurrentHashMap<>(); // userId -> {cachedAt}
|
||||
private static final ConcurrentHashMap<Integer, THashSet<Guild>> myForumsData = new ConcurrentHashMap<>();
|
||||
private static final long MY_FORUMS_TTL = 10 * 60 * 1000; // 10 minutes
|
||||
|
||||
public static void invalidateActiveForumsCache() {
|
||||
activeForumsCache = null;
|
||||
activeForumsCachedAt = 0;
|
||||
}
|
||||
|
||||
public static void invalidateMyForumsCache(int userId) {
|
||||
myForumsCache.remove(userId);
|
||||
myForumsData.remove(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
int mode = this.packet.readInt();
|
||||
@@ -50,12 +71,18 @@ public class GuildForumListEvent extends MessageHandler {
|
||||
}
|
||||
|
||||
private THashSet<Guild> getActiveForums() {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (activeForumsCache != null && (now - activeForumsCachedAt) < ACTIVE_FORUMS_TTL) {
|
||||
return activeForumsCache;
|
||||
}
|
||||
|
||||
THashSet<Guild> guilds = new THashSet<Guild>();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT `guilds`.`id`, SUM(`guilds_forums_threads`.`posts_count`) AS `post_count` " +
|
||||
"FROM `guilds_forums_threads` " +
|
||||
"LEFT JOIN `guilds` ON `guilds`.`id` = `guilds_forums_threads`.`guild_id` " +
|
||||
"WHERE `guilds`.`read_forum` = 'EVERYONE' AND `guilds_forums_threads`.`created_at` > ? " +
|
||||
"WHERE `guilds`.`forum` = '1' AND `guilds_forums_threads`.`created_at` > ? " +
|
||||
"GROUP BY `guilds`.`id` " +
|
||||
"ORDER BY `post_count` DESC LIMIT 100")) {
|
||||
statement.setInt(1, Emulator.getIntUnixTimestamp() - 7 * 24 * 60 * 60);
|
||||
@@ -73,10 +100,21 @@ public class GuildForumListEvent extends MessageHandler {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(500));
|
||||
}
|
||||
|
||||
activeForumsCache = guilds;
|
||||
activeForumsCachedAt = now;
|
||||
|
||||
return guilds;
|
||||
}
|
||||
|
||||
private THashSet<Guild> getMyForums(int userId) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
long[] cached = myForumsCache.get(userId);
|
||||
if (cached != null && (now - cached[0]) < MY_FORUMS_TTL) {
|
||||
THashSet<Guild> data = myForumsData.get(userId);
|
||||
if (data != null) return data;
|
||||
}
|
||||
|
||||
THashSet<Guild> guilds = new THashSet<Guild>();
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT `guilds`.`id` FROM `guilds_members` " +
|
||||
@@ -97,6 +135,9 @@ public class GuildForumListEvent extends MessageHandler {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(500));
|
||||
}
|
||||
|
||||
myForumsCache.put(userId, new long[]{now});
|
||||
myForumsData.put(userId, guilds);
|
||||
|
||||
return guilds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.eu.habbo.messages.incoming.guilds.forums;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class GuildForumMarkAsReadEvent extends MessageHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildForumMarkAsReadEvent.class);
|
||||
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 2000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
int count = this.packet.readInt();
|
||||
int userId = this.client.getHabbo().getHabboInfo().getId();
|
||||
int timestamp = Emulator.getIntUnixTimestamp();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
int guildId = this.packet.readInt();
|
||||
this.packet.readInt(); // messageId (not used, we track by timestamp)
|
||||
this.packet.readBoolean(); // isRead
|
||||
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(
|
||||
"INSERT INTO `guild_forum_views` (`user_id`, `guild_id`, `timestamp`) VALUES (?, ?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE `timestamp` = ?"
|
||||
)) {
|
||||
statement.setInt(1, userId);
|
||||
statement.setInt(2, guildId);
|
||||
statement.setInt(3, timestamp);
|
||||
statement.setInt(4, timestamp);
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
|
||||
// Invalidate caches so next request gets fresh data
|
||||
GuildForumDataComposer.invalidateLastSeenCache(userId, guildId);
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-4
@@ -18,7 +18,7 @@ import com.eu.habbo.messages.outgoing.handshake.ConnectionErrorComposer;
|
||||
public class GuildForumModerateMessageEvent extends MessageHandler {
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 500;
|
||||
return 2000;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,6 +36,11 @@ public class GuildForumModerateMessageEvent extends MessageHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread.getGuildId() != guildId) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
|
||||
ForumThreadComment comment = thread.getCommentById(messageId);
|
||||
if (comment == null) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(404));
|
||||
@@ -45,19 +50,20 @@ public class GuildForumModerateMessageEvent extends MessageHandler {
|
||||
boolean hasStaffPermissions = this.client.getHabbo().hasPermission(Permission.ACC_MODTOOL_TICKET_Q);
|
||||
|
||||
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, this.client.getHabbo().getHabboInfo().getId());
|
||||
if (member == null) {
|
||||
if (member == null && !hasStaffPermissions) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(401));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isGuildAdministrator = (guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || member.getRank().equals(GuildRank.ADMIN));
|
||||
boolean isGuildAdministrator = (guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || (member != null && member.getRank().equals(GuildRank.ADMIN)));
|
||||
|
||||
if (!isGuildAdministrator && !hasStaffPermissions) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == ForumThreadState.HIDDEN_BY_GUILD_ADMIN.getStateId() && !hasStaffPermissions) {
|
||||
// Restrict state 20 (staff hidden) to staff only
|
||||
if (state == 20 && !hasStaffPermissions) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
|
||||
+45
-5
@@ -10,15 +10,24 @@ import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadMessagesComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadsComposer;
|
||||
import com.eu.habbo.messages.outgoing.handshake.ConnectionErrorComposer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
|
||||
public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GuildForumModerateThreadEvent.class);
|
||||
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 500;
|
||||
return 2000;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -26,8 +35,6 @@ public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
int guildId = packet.readInt();
|
||||
int threadId = packet.readInt();
|
||||
int state = packet.readInt();
|
||||
// STATE 20 - HIDDEN_BY_GUILD_ADMIN = HIDDEN BY GUILD ADMINS/ HOTEL MODERATORS
|
||||
// STATE 1 = VISIBLE THREAD
|
||||
|
||||
Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(guildId);
|
||||
ForumThread thread = ForumThread.getById(threadId);
|
||||
@@ -37,6 +44,11 @@ public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread.getGuildId() != guildId) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
|
||||
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, this.client.getHabbo().getHabboInfo().getId());
|
||||
boolean hasStaffPerms = this.client.getHabbo().hasPermission(Permission.ACC_MODTOOL_TICKET_Q);
|
||||
|
||||
@@ -52,12 +64,22 @@ public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
thread.setState(ForumThreadState.fromValue(state)); // sets state as defined in the packet
|
||||
// State 20 = permanent delete (thread + comments removed from DB)
|
||||
if (state == 20) {
|
||||
deleteThread(threadId);
|
||||
ForumThread.clearCacheForGuild(guildId);
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
|
||||
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FORUMS_THREAD_HIDDEN.key).compose());
|
||||
this.client.sendResponse(new GuildForumThreadsComposer(guild, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
thread.setState(ForumThreadState.fromValue(state));
|
||||
thread.run();
|
||||
|
||||
switch (state) {
|
||||
case 10:
|
||||
case 20:
|
||||
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FORUMS_THREAD_HIDDEN.key).compose());
|
||||
break;
|
||||
case 1:
|
||||
@@ -68,4 +90,22 @@ public class GuildForumModerateThreadEvent extends MessageHandler {
|
||||
this.client.sendResponse(new GuildForumThreadMessagesComposer(thread));
|
||||
this.client.sendResponse(new GuildForumThreadsComposer(guild, 0));
|
||||
}
|
||||
|
||||
private void deleteThread(int threadId) {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guilds_forums_comments` WHERE `thread_id` = ?")) {
|
||||
statement.setInt(1, threadId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `guilds_forums_threads` WHERE `id` = ?")) {
|
||||
statement.setInt(1, threadId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to delete thread " + threadId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -9,6 +9,7 @@ import com.eu.habbo.habbohotel.guilds.forums.ForumThreadComment;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumAddCommentComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
|
||||
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadMessagesComposer;
|
||||
import com.eu.habbo.messages.outgoing.handshake.ConnectionErrorComposer;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class GuildForumPostThreadEvent extends MessageHandler {
|
||||
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 1000;
|
||||
return 2000;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,6 +66,7 @@ public class GuildForumPostThreadEvent extends MessageHandler {
|
||||
|
||||
this.client.getHabbo().getHabboStats().forumPostsCount += 1;
|
||||
thread.setPostsCount(thread.getPostsCount() + 1);
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
this.client.sendResponse(new GuildForumThreadMessagesComposer(thread));
|
||||
return;
|
||||
}
|
||||
@@ -74,6 +76,15 @@ public class GuildForumPostThreadEvent extends MessageHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread.getGuildId() != guildId) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread.isLocked() && !isStaff) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(403));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!((guild.canPostMessages().state == 0)
|
||||
|| (guild.canPostMessages().state == 1 && member != null)
|
||||
@@ -91,6 +102,7 @@ public class GuildForumPostThreadEvent extends MessageHandler {
|
||||
thread.setUpdatedAt(Emulator.getIntUnixTimestamp());
|
||||
this.client.getHabbo().getHabboStats().forumPostsCount += 1;
|
||||
thread.setPostsCount(thread.getPostsCount() + 1);
|
||||
GuildForumDataComposer.invalidateUnreadCache(guildId);
|
||||
this.client.sendResponse(new GuildForumAddCommentComposer(comment));
|
||||
} else {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(500));
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ import com.eu.habbo.messages.outgoing.handshake.ConnectionErrorComposer;
|
||||
public class GuildForumThreadUpdateEvent extends MessageHandler {
|
||||
@Override
|
||||
public int getRatelimit() {
|
||||
return 500;
|
||||
return 2000;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ public class GuildForumThreadsEvent extends MessageHandler {
|
||||
|
||||
Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(guildId);
|
||||
|
||||
if (guild == null || !guild.hasForum()) {
|
||||
if (guild == null) {
|
||||
this.client.sendResponse(new ConnectionErrorComposer(404));
|
||||
return;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user