Просмотр исходного кода

Fixed gaps in random options (#7449)

Fixes #7447

Thanks to @Abszoluto
Lemongrass3110 2 лет назад
Родитель
Сommit
eda702ca2c
3 измененных файлов с 857 добавлено и 23 удалено
  1. 809 0
      sql-files/upgrades/upgrade_20221218.sql
  2. 18 23
      src/map/clif.cpp
  3. 30 0
      src/map/itemdb.cpp

+ 809 - 0
sql-files/upgrades/upgrade_20221218.sql

@@ -0,0 +1,809 @@
+/**
+	ATTENTION!!!
+	Please make sure to create a backup of your live data before running this update.
+	This update is rather complicated and although I have tried my best I cannot guarantee that I did not make any mistake. [Lemongrass]
+	We would also advise you to check the data in the temporary table after the insert statement and before the update statements.
+	You could do so by running
+		select * from `tmp_randomoptionfix` where `new_index` <> `old_index`;
+	to see where changes happen and options will be moved.
+	For executing this update your user needs create table and drop table permissions. Either run the update with another user [admin or root for example] or give your database user these permissions temporarily.
+	Do not forget to remove these permissions again, as they are usually not necessary and it is rather dangerous, if someone gets access to your database user.
+	Additionally if you have more than one storage table (see conf/inter_server.yml) make sure to also update those tables (see instructions at the bottom of this script).
+
+
+
+
+
+
+
+
+	Comment the line below to really run the script, this is a safeguard that you confirm you have read and understood what was written above.
+**/
+cancel;
+
+-- Create a temporary table to store the data for the update
+create table `tmp_randomoptionfix` (
+	`id` int(11) unsigned NOT NULL,
+	`new_index` smallint(5) NOT NULL,
+	`old_index` smallint(5) NOT NULL,
+	`option_id` smallint(5) NOT NULL,
+	`option_val` smallint(5) NOT NULL,
+	`option_parm` tinyint(3) NOT NULL,
+	PRIMARY KEY ( `id`, `old_index` )
+);
+
+-- Create auction data
+insert into `tmp_randomoptionfix` ( `id`, `new_index`, `old_index`, `option_id`, `option_val`, `option_parm` )
+select
+	`id`,
+	row_number() over( partition by `id` ) - 1 as `new_index`,
+	`old_index`,
+	`option_id`,
+	`option_val`,
+	`option_parm`
+from (
+	select
+			`auction_id` as `id`,
+			0 as `old_index`,
+			`option_id0` as `option_id`,
+			`option_val0` as `option_val`,
+			`option_parm0` as `option_parm`
+		from `auction`
+		where `option_id0` <> 0
+	union
+		select
+			`auction_id` as `id`,
+			1 as `old_index`,
+			`option_id1` as `option_id`,
+			`option_val1` as `option_val`,
+			`option_parm1` as `option_parm`
+		from `auction`
+		where `option_id1` <> 0
+	union
+		select
+			`auction_id` as `id`,
+			2 as `old_index`,
+			`option_id2` as `option_id`,
+			`option_val2` as `option_val`,
+			`option_parm2` as `option_parm`
+		from `auction`
+		where `option_id2` <> 0
+	union
+		select
+			`auction_id` as `id`,
+			3 as `old_index`,
+			`option_id3` as `option_id`,
+			`option_val3` as `option_val`,
+			`option_parm3` as `option_parm`
+		from `auction`
+		where `option_id3` <> 0
+	union
+		select
+			`auction_id` as `id`,
+			4 as `old_index`,
+			`option_id4` as `option_id`,
+			`option_val4` as `option_val`,
+			`option_parm4` as `option_parm`
+		from `auction`
+		where `option_id4` <> 0
+	order by `id`, `old_index`
+) t ;
+
+-- Fix option 0
+update `auction`
+left join `tmp_randomoptionfix`
+on `auction`.`auction_id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 0
+set
+	`option_id0` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val0` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm0` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 1
+update `auction`
+left join `tmp_randomoptionfix`
+on `auction`.`auction_id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 1
+set
+	`option_id1` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val1` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm1` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 2
+update `auction`
+left join `tmp_randomoptionfix`
+on `auction`.`auction_id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 2
+set
+	`option_id2` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val2` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm2` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 3
+update `auction`
+left join `tmp_randomoptionfix`
+on `auction`.`auction_id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 3
+set
+	`option_id3` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val3` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm3` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 4
+update `auction`
+left join `tmp_randomoptionfix`
+on `auction`.`auction_id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 4
+set
+	`option_id4` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val4` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm4` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Delete the data to have a clean table for the next run
+delete from `tmp_randomoptionfix`;
+
+-- Create cart_inventory data
+insert into `tmp_randomoptionfix` ( `id`, `new_index`, `old_index`, `option_id`, `option_val`, `option_parm` )
+select
+	`id`,
+	row_number() over( partition by `id` ) - 1 as `new_index`,
+	`old_index`,
+	`option_id`,
+	`option_val`,
+	`option_parm`
+from (
+	select
+			`id`,
+			0 as `old_index`,
+			`option_id0` as `option_id`,
+			`option_val0` as `option_val`,
+			`option_parm0` as `option_parm`
+		from `cart_inventory`
+		where `option_id0` <> 0
+	union
+		select
+			`id`,
+			1 as `old_index`,
+			`option_id1` as `option_id`,
+			`option_val1` as `option_val`,
+			`option_parm1` as `option_parm`
+		from `cart_inventory`
+		where `option_id1` <> 0
+	union
+		select
+			`id`,
+			2 as `old_index`,
+			`option_id2` as `option_id`,
+			`option_val2` as `option_val`,
+			`option_parm2` as `option_parm`
+		from `cart_inventory`
+		where `option_id2` <> 0
+	union
+		select
+			`id`,
+			3 as `old_index`,
+			`option_id3` as `option_id`,
+			`option_val3` as `option_val`,
+			`option_parm3` as `option_parm`
+		from `cart_inventory`
+		where `option_id3` <> 0
+	union
+		select
+			`id`,
+			4 as `old_index`,
+			`option_id4` as `option_id`,
+			`option_val4` as `option_val`,
+			`option_parm4` as `option_parm`
+		from `cart_inventory`
+		where `option_id4` <> 0
+	order by `id`, `old_index`
+) t ;
+
+-- Fix option 0
+update `cart_inventory`
+left join `tmp_randomoptionfix`
+on `cart_inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 0
+set
+	`option_id0` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val0` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm0` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 1
+update `cart_inventory`
+left join `tmp_randomoptionfix`
+on `cart_inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 1
+set
+	`option_id1` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val1` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm1` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 2
+update `cart_inventory`
+left join `tmp_randomoptionfix`
+on `cart_inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 2
+set
+	`option_id2` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val2` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm2` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 3
+update `cart_inventory`
+left join `tmp_randomoptionfix`
+on `cart_inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 3
+set
+	`option_id3` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val3` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm3` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 4
+update `cart_inventory`
+left join `tmp_randomoptionfix`
+on `cart_inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 4
+set
+	`option_id4` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val4` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm4` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Delete the data to have a clean table for the next run
+delete from `tmp_randomoptionfix`;
+
+-- Create guild_storage data
+insert into `tmp_randomoptionfix` ( `id`, `new_index`, `old_index`, `option_id`, `option_val`, `option_parm` )
+select
+	`id`,
+	row_number() over( partition by `id` ) - 1 as `new_index`,
+	`old_index`,
+	`option_id`,
+	`option_val`,
+	`option_parm`
+from (
+	select
+			`id`,
+			0 as `old_index`,
+			`option_id0` as `option_id`,
+			`option_val0` as `option_val`,
+			`option_parm0` as `option_parm`
+		from `guild_storage`
+		where `option_id0` <> 0
+	union
+		select
+			`id`,
+			1 as `old_index`,
+			`option_id1` as `option_id`,
+			`option_val1` as `option_val`,
+			`option_parm1` as `option_parm`
+		from `guild_storage`
+		where `option_id1` <> 0
+	union
+		select
+			`id`,
+			2 as `old_index`,
+			`option_id2` as `option_id`,
+			`option_val2` as `option_val`,
+			`option_parm2` as `option_parm`
+		from `guild_storage`
+		where `option_id2` <> 0
+	union
+		select
+			`id`,
+			3 as `old_index`,
+			`option_id3` as `option_id`,
+			`option_val3` as `option_val`,
+			`option_parm3` as `option_parm`
+		from `guild_storage`
+		where `option_id3` <> 0
+	union
+		select
+			`id`,
+			4 as `old_index`,
+			`option_id4` as `option_id`,
+			`option_val4` as `option_val`,
+			`option_parm4` as `option_parm`
+		from `guild_storage`
+		where `option_id4` <> 0
+	order by `id`, `old_index`
+) t ;
+
+-- Fix option 0
+update `guild_storage`
+left join `tmp_randomoptionfix`
+on `guild_storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 0
+set
+	`option_id0` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val0` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm0` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 1
+update `guild_storage`
+left join `tmp_randomoptionfix`
+on `guild_storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 1
+set
+	`option_id1` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val1` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm1` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 2
+update `guild_storage`
+left join `tmp_randomoptionfix`
+on `guild_storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 2
+set
+	`option_id2` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val2` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm2` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 3
+update `guild_storage`
+left join `tmp_randomoptionfix`
+on `guild_storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 3
+set
+	`option_id3` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val3` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm3` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 4
+update `guild_storage`
+left join `tmp_randomoptionfix`
+on `guild_storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 4
+set
+	`option_id4` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val4` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm4` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Delete the data to have a clean table for the next run
+delete from `tmp_randomoptionfix`;
+
+-- Create inventory data
+insert into `tmp_randomoptionfix` ( `id`, `new_index`, `old_index`, `option_id`, `option_val`, `option_parm` )
+select
+	`id`,
+	row_number() over( partition by `id` ) - 1 as `new_index`,
+	`old_index`,
+	`option_id`,
+	`option_val`,
+	`option_parm`
+from (
+	select
+			`id`,
+			0 as `old_index`,
+			`option_id0` as `option_id`,
+			`option_val0` as `option_val`,
+			`option_parm0` as `option_parm`
+		from `inventory`
+		where `option_id0` <> 0
+	union
+		select
+			`id`,
+			1 as `old_index`,
+			`option_id1` as `option_id`,
+			`option_val1` as `option_val`,
+			`option_parm1` as `option_parm`
+		from `inventory`
+		where `option_id1` <> 0
+	union
+		select
+			`id`,
+			2 as `old_index`,
+			`option_id2` as `option_id`,
+			`option_val2` as `option_val`,
+			`option_parm2` as `option_parm`
+		from `inventory`
+		where `option_id2` <> 0
+	union
+		select
+			`id`,
+			3 as `old_index`,
+			`option_id3` as `option_id`,
+			`option_val3` as `option_val`,
+			`option_parm3` as `option_parm`
+		from `inventory`
+		where `option_id3` <> 0
+	union
+		select
+			`id`,
+			4 as `old_index`,
+			`option_id4` as `option_id`,
+			`option_val4` as `option_val`,
+			`option_parm4` as `option_parm`
+		from `inventory`
+		where `option_id4` <> 0
+	order by `id`, `old_index`
+) t ;
+
+-- Fix option 0
+update `inventory`
+left join `tmp_randomoptionfix`
+on `inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 0
+set
+	`option_id0` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val0` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm0` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 1
+update `inventory`
+left join `tmp_randomoptionfix`
+on `inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 1
+set
+	`option_id1` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val1` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm1` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 2
+update `inventory`
+left join `tmp_randomoptionfix`
+on `inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 2
+set
+	`option_id2` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val2` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm2` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 3
+update `inventory`
+left join `tmp_randomoptionfix`
+on `inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 3
+set
+	`option_id3` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val3` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm3` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 4
+update `inventory`
+left join `tmp_randomoptionfix`
+on `inventory`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 4
+set
+	`option_id4` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val4` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm4` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Delete the data to have a clean table for the next run
+delete from `tmp_randomoptionfix`;
+
+-- Create mail_attachments data
+insert into `tmp_randomoptionfix` ( `id`, `new_index`, `old_index`, `option_id`, `option_val`, `option_parm` )
+select
+	`id`,
+	row_number() over( partition by `id` ) - 1 as `new_index`,
+	`old_index`,
+	`option_id`,
+	`option_val`,
+	`option_parm`
+from (
+	select
+			`id`,
+			0 as `old_index`,
+			`option_id0` as `option_id`,
+			`option_val0` as `option_val`,
+			`option_parm0` as `option_parm`
+		from `mail_attachments`
+		where `option_id0` <> 0
+	union
+		select
+			`id`,
+			1 as `old_index`,
+			`option_id1` as `option_id`,
+			`option_val1` as `option_val`,
+			`option_parm1` as `option_parm`
+		from `mail_attachments`
+		where `option_id1` <> 0
+	union
+		select
+			`id`,
+			2 as `old_index`,
+			`option_id2` as `option_id`,
+			`option_val2` as `option_val`,
+			`option_parm2` as `option_parm`
+		from `mail_attachments`
+		where `option_id2` <> 0
+	union
+		select
+			`id`,
+			3 as `old_index`,
+			`option_id3` as `option_id`,
+			`option_val3` as `option_val`,
+			`option_parm3` as `option_parm`
+		from `mail_attachments`
+		where `option_id3` <> 0
+	union
+		select
+			`id`,
+			4 as `old_index`,
+			`option_id4` as `option_id`,
+			`option_val4` as `option_val`,
+			`option_parm4` as `option_parm`
+		from `mail_attachments`
+		where `option_id4` <> 0
+	order by `id`, `old_index`
+) t ;
+
+-- Fix option 0
+update `mail_attachments`
+left join `tmp_randomoptionfix`
+on `mail_attachments`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 0
+set
+	`option_id0` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val0` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm0` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 1
+update `mail_attachments`
+left join `tmp_randomoptionfix`
+on `mail_attachments`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 1
+set
+	`option_id1` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val1` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm1` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 2
+update `mail_attachments`
+left join `tmp_randomoptionfix`
+on `mail_attachments`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 2
+set
+	`option_id2` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val2` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm2` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 3
+update `mail_attachments`
+left join `tmp_randomoptionfix`
+on `mail_attachments`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 3
+set
+	`option_id3` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val3` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm3` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 4
+update `mail_attachments`
+left join `tmp_randomoptionfix`
+on `mail_attachments`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 4
+set
+	`option_id4` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val4` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm4` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Delete the data to have a clean table for the next run
+delete from `tmp_randomoptionfix`;
+
+-- Create storage data
+insert into `tmp_randomoptionfix` ( `id`, `new_index`, `old_index`, `option_id`, `option_val`, `option_parm` )
+select
+	`id`,
+	row_number() over( partition by `id` ) - 1 as `new_index`,
+	`old_index`,
+	`option_id`,
+	`option_val`,
+	`option_parm`
+from (
+	select
+			`id`,
+			0 as `old_index`,
+			`option_id0` as `option_id`,
+			`option_val0` as `option_val`,
+			`option_parm0` as `option_parm`
+		from `storage`
+		where `option_id0` <> 0
+	union
+		select
+			`id`,
+			1 as `old_index`,
+			`option_id1` as `option_id`,
+			`option_val1` as `option_val`,
+			`option_parm1` as `option_parm`
+		from `storage`
+		where `option_id1` <> 0
+	union
+		select
+			`id`,
+			2 as `old_index`,
+			`option_id2` as `option_id`,
+			`option_val2` as `option_val`,
+			`option_parm2` as `option_parm`
+		from `storage`
+		where `option_id2` <> 0
+	union
+		select
+			`id`,
+			3 as `old_index`,
+			`option_id3` as `option_id`,
+			`option_val3` as `option_val`,
+			`option_parm3` as `option_parm`
+		from `storage`
+		where `option_id3` <> 0
+	union
+		select
+			`id`,
+			4 as `old_index`,
+			`option_id4` as `option_id`,
+			`option_val4` as `option_val`,
+			`option_parm4` as `option_parm`
+		from `storage`
+		where `option_id4` <> 0
+	order by `id`, `old_index`
+) t ;
+
+-- Fix option 0
+update `storage`
+left join `tmp_randomoptionfix`
+on `storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 0
+set
+	`option_id0` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val0` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm0` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 1
+update `storage`
+left join `tmp_randomoptionfix`
+on `storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 1
+set
+	`option_id1` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val1` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm1` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 2
+update `storage`
+left join `tmp_randomoptionfix`
+on `storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 2
+set
+	`option_id2` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val2` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm2` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 3
+update `storage`
+left join `tmp_randomoptionfix`
+on `storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 3
+set
+	`option_id3` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val3` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm3` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Fix option 4
+update `storage`
+left join `tmp_randomoptionfix`
+on `storage`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 4
+set
+	`option_id4` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+	`option_val4` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+	`option_parm4` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+;
+
+-- Delete the data to have a clean table for the next run
+delete from `tmp_randomoptionfix`;
+
+-- Repeat this for any other storage tables that you have created (for example VIP)
+-- To do this copy paste the code below and replace ${other_storage} with the name of your table
+/**
+	-- Create ${other_storage} data
+	insert into `tmp_randomoptionfix` ( `id`, `new_index`, `old_index`, `option_id`, `option_val`, `option_parm` )
+	select
+		`id`,
+		row_number() over( partition by `id` ) - 1 as `new_index`,
+		`old_index`,
+		`option_id`,
+		`option_val`,
+		`option_parm`
+	from (
+		select
+				`id`,
+				0 as `old_index`,
+				`option_id0` as `option_id`,
+				`option_val0` as `option_val`,
+				`option_parm0` as `option_parm`
+			from `${other_storage}`
+			where `option_id0` <> 0
+		union
+			select
+				`id`,
+				1 as `old_index`,
+				`option_id1` as `option_id`,
+				`option_val1` as `option_val`,
+				`option_parm1` as `option_parm`
+			from `${other_storage}`
+			where `option_id1` <> 0
+		union
+			select
+				`id`,
+				2 as `old_index`,
+				`option_id2` as `option_id`,
+				`option_val2` as `option_val`,
+				`option_parm2` as `option_parm`
+			from `${other_storage}`
+			where `option_id2` <> 0
+		union
+			select
+				`id`,
+				3 as `old_index`,
+				`option_id3` as `option_id`,
+				`option_val3` as `option_val`,
+				`option_parm3` as `option_parm`
+			from `${other_storage}`
+			where `option_id3` <> 0
+		union
+			select
+				`id`,
+				4 as `old_index`,
+				`option_id4` as `option_id`,
+				`option_val4` as `option_val`,
+				`option_parm4` as `option_parm`
+			from `${other_storage}`
+			where `option_id4` <> 0
+		order by `id`, `old_index`
+	) t ;
+
+	-- Fix option 0
+	update `${other_storage}`
+	left join `tmp_randomoptionfix`
+	on `${other_storage}`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 0
+	set
+		`option_id0` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+		`option_val0` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+		`option_parm0` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+	;
+
+	-- Fix option 1
+	update `${other_storage}`
+	left join `tmp_randomoptionfix`
+	on `${other_storage}`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 1
+	set
+		`option_id1` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+		`option_val1` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+		`option_parm1` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+	;
+
+	-- Fix option 2
+	update `${other_storage}`
+	left join `tmp_randomoptionfix`
+	on `${other_storage}`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 2
+	set
+		`option_id2` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+		`option_val2` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+		`option_parm2` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+	;
+
+	-- Fix option 3
+	update `${other_storage}`
+	left join `tmp_randomoptionfix`
+	on `${other_storage}`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 3
+	set
+		`option_id3` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+		`option_val3` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+		`option_parm3` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+	;
+
+	-- Fix option 4
+	update `${other_storage}`
+	left join `tmp_randomoptionfix`
+	on `${other_storage}`.`id` = `tmp_randomoptionfix`.`id` and `tmp_randomoptionfix`.`new_index` = 4
+	set
+		`option_id4` = coalesce( `tmp_randomoptionfix`.`option_id`, 0 ),
+		`option_val4` = coalesce( `tmp_randomoptionfix`.`option_val`, 0 ),
+		`option_parm4` = coalesce( `tmp_randomoptionfix`.`option_parm`, 0 )
+	;
+
+	-- Delete the data to have a clean table for the next run
+	delete from `tmp_randomoptionfix`;
+**/
+
+drop table `tmp_randomoptionfix`;

+ 18 - 23
src/map/clif.cpp

@@ -2734,17 +2734,12 @@ static void clif_addcards( struct EQUIPSLOTINFO* buf, struct item* item ){
 
 /// Fills in part of the item buffers that calls for variable bonuses data. [Napster]
 /// A maximum of 5 random options can be supported.
-static uint8 clif_add_random_options( struct ItemOptions buf[MAX_ITEM_RDM_OPT], struct item* it ){
-	nullpo_retr( 0, it );
-
-	uint8 count = 0;
-
+static void clif_add_random_options( struct ItemOptions buf[MAX_ITEM_RDM_OPT], struct item& it ){
 	for( int i = 0; i < MAX_ITEM_RDM_OPT; i++ ){
-		if( it->option[i].id ){
-			buf[i].index = it->option[i].id;	// OptIndex
-			buf[i].value = it->option[i].value;	// Value
-			buf[i].param = it->option[i].param;	// Param1
-			count++;
+		if( it.option[i].id ){
+			buf[i].index = it.option[i].id; // OptIndex
+			buf[i].value = it.option[i].value; // Value
+			buf[i].param = it.option[i].param; // Param1
 		}else{
 			buf[i].index = 0;
 			buf[i].value = 0;
@@ -2759,8 +2754,6 @@ static uint8 clif_add_random_options( struct ItemOptions buf[MAX_ITEM_RDM_OPT],
 		buf[i].param = 0;	// Param1
 	}
 #endif
-
-	return count;
 }
 
 /// Notifies the client, about a received inventory item or the result of a pick-up request.
@@ -2803,7 +2796,7 @@ void clif_additem( map_session_data *sd, int n, int amount, unsigned char fail )
 		 */
 		p.bindOnEquipType = sd->inventory.u.items_inventory[n].bound && !itemdb_isstackable2( sd->inventory_data[n] ) ? 2 : sd->inventory_data[n]->flag.bindOnEquip ? 1 : 0;
 #if PACKETVER >= 20150226
-		clif_add_random_options( p.option_data, &sd->inventory.u.items_inventory[n] );
+		clif_add_random_options( p.option_data, sd->inventory.u.items_inventory[n] );
 #if PACKETVER >= 20160921
 		p.favorite = sd->inventory.u.items_inventory[n].favorite;
 		p.look = sd->inventory_data[n]->look;
@@ -2918,7 +2911,9 @@ static void clif_item_equip( short idx, struct EQUIPITEM_INFO *p, struct item *i
 #endif
 
 #if PACKETVER >= 20150226
-	p->option_count = clif_add_random_options( p->option_data, it );
+	clif_add_random_options( p->option_data, *it );
+	// Client ingores the value anyway and recalculates it
+	p->option_count = 0;
 #endif
 
 #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
@@ -4647,7 +4642,7 @@ void clif_tradeadditem( map_session_data* sd, map_session_data* tsd, int index,
 		p.refine = sd->inventory.u.items_inventory[index].refine;
 		clif_addcards( &p.slot, &sd->inventory.u.items_inventory[index] );
 #if PACKETVER >= 20150226
-		clif_add_random_options( p.option_data, &sd->inventory.u.items_inventory[index] );
+		clif_add_random_options( p.option_data, sd->inventory.u.items_inventory[index] );
 #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
 		p.location = pc_equippoint_sub( sd, sd->inventory_data[index] );
 		p.look = sd->inventory_data[index]->look;
@@ -4791,7 +4786,7 @@ void clif_storageitemadded( map_session_data* sd, struct item* i, int index, int
 	p.refine = i->refine; //refine
 	clif_addcards( &p.slot, i );
 #if PACKETVER >= 20150226
-	clif_add_random_options( p.option_data, i );
+	clif_add_random_options( p.option_data, *i );
 #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
 	p.enchantgrade = i->enchantgrade;
 #endif
@@ -7274,7 +7269,7 @@ void clif_cart_additem( map_session_data *sd, int n, int amount ){
 	p.refine = sd->cart.u.items_cart[n].refine;
 	clif_addcards( &p.slot, &sd->cart.u.items_cart[n] );
 #if PACKETVER >= 20150226
-	clif_add_random_options( p.option_data, &sd->cart.u.items_cart[n] );
+	clif_add_random_options( p.option_data, sd->cart.u.items_cart[n] );
 #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
 	p.grade = sd->cart.u.items_cart[n].enchantgrade;
 #endif
@@ -7599,7 +7594,7 @@ void clif_vendinglist( map_session_data* sd, map_session_data* vsd ){
 		p->items[i].refine = vsd->cart.u.items_cart[index].refine;
 		clif_addcards( &p->items[i].slot, &vsd->cart.u.items_cart[index] );
 #if PACKETVER >= 20150226
-		clif_add_random_options( p->items[i].option_data, &vsd->cart.u.items_cart[index] );
+		clif_add_random_options( p->items[i].option_data, vsd->cart.u.items_cart[index] );
 #if PACKETVER >= 20160921
 		p->items[i].location = pc_equippoint_sub( sd, data );
 		p->items[i].viewSprite = data->look;
@@ -7692,7 +7687,7 @@ void clif_openvending( map_session_data* sd, int id, struct s_vending* vending )
 		p->items[i].refine = sd->cart.u.items_cart[index].refine;
 		clif_addcards( &p->items[i].slot, &sd->cart.u.items_cart[index] );
 #if PACKETVER >= 20150226
-		clif_add_random_options( p->items[i].option_data, &sd->cart.u.items_cart[index] );
+		clif_add_random_options( p->items[i].option_data, sd->cart.u.items_cart[index] );
 #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
 		p->items[i].grade = sd->cart.u.items_cart[index].enchantgrade;
 #endif
@@ -15624,7 +15619,7 @@ void clif_Mail_setattachment( map_session_data* sd, int index, int amount, uint8
 		p.IsDamaged = item->attribute ? 1 : 0;
 		p.refiningLevel = item->refine;
 		clif_addcards( &p.slot, item );
-		clif_add_random_options( p.optionData, item );
+		clif_add_random_options( p.optionData, *item );
 
 		p.weight = 0;
 		for( int i = 0; i < MAIL_MAX_ITEM; i++ ){
@@ -16145,7 +16140,7 @@ void clif_Mail_read( map_session_data *sd, int mail_id ){
 				mailitem->viewSprite = data->look;
 				mailitem->bindOnEquip = item->bound ? 2 : data->flag.bindOnEquip ? 1 : 0;
 				clif_addcards( &mailitem->slot, item );
-				clif_add_random_options( mailitem->option_data, item );
+				clif_add_random_options( mailitem->option_data, *item );
 #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
 				mailitem->grade = item->enchantgrade;
 #endif
@@ -19212,7 +19207,7 @@ void clif_search_store_info_ack( map_session_data* sd ){
 
 		clif_addcards( &p->items[i].slot, &it );
 #if PACKETVER >= 20150226
-		clif_add_random_options( p->items[i].option_data, &it );
+		clif_add_random_options( p->items[i].option_data, it );
 #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
 		p->items[i].grade = ssitem->enchantgrade;
 #endif
@@ -23186,7 +23181,7 @@ static void clif_item_preview( map_session_data *sd, int16 index ){
 	p.grade = item->enchantgrade;
 #endif
 	clif_addcards( &p.slot, item );
-	clif_add_random_options( p.option_data, item );
+	clif_add_random_options( p.option_data, *item );
 
 	clif_send( &p, sizeof( p ), &sd->bl, SELF );
 #endif

+ 30 - 0
src/map/itemdb.cpp

@@ -4380,6 +4380,36 @@ void s_random_opt_group::apply( struct item& item ){
 			}
 		}
 	}
+
+	// Fix any gaps, the client cannot handle this
+	for( size_t i = 0; i < MAX_ITEM_RDM_OPT; i++ ){
+		// If an option is empty
+		if( item.option[i].id == 0 ){
+			// Check if any other options, after the empty option exist
+			size_t j;
+			for( j = i + 1; j < MAX_ITEM_RDM_OPT; j++ ){
+				if( item.option[j].id != 0 ){
+					break;
+				}
+			}
+
+			// Another option was found, after the empty option
+			if( j < MAX_ITEM_RDM_OPT ){
+				// Move the later option forward
+				item.option[i].id = item.option[j].id;
+				item.option[i].value = item.option[j].value;
+				item.option[i].param = item.option[j].param;
+
+				// Reset the option that was moved
+				item.option[j].id = 0;
+				item.option[j].value = 0;
+				item.option[j].param = 0;
+			}else{
+				// Cancel early
+				break;
+			}
+		}
+	}
 }
 
 /**