1111import org .bukkit .Material ;
1212import org .bukkit .OfflinePlayer ;
1313import org .bukkit .inventory .ItemStack ;
14+ import org .jetbrains .annotations .NotNull ;
1415import org .jetbrains .annotations .Nullable ;
1516
1617import java .io .File ;
@@ -74,6 +75,11 @@ private static boolean saveSinglePage(Connection conn, GroupData g, String pageI
7475 return false ;
7576 }
7677
78+ if (isInventoryDataSame (conn , g , pageId , inv )) {
79+ Bukkit .getLogger ().info ("[BetterStorage][debug] データが変更されていないため更新しません。" );
80+ return true ; // データが一致していれば更新しない
81+ }
82+
7783 // ✅ 楽観ロック:保存前に version を進める
7884 inv .version ++;
7985
@@ -172,6 +178,8 @@ private static boolean saveSinglePage(Connection conn, GroupData g, String pageI
172178 ps .executeBatch ();
173179 }
174180
181+
182+
175183 // ---------- tag_table ----------
176184 String tagSql = "REPLACE INTO tag_table (group_uuid, plugin_name, page_id, user_tag) VALUES (?, ?, ?, ?)" ;
177185 try (PreparedStatement ps = conn .prepareStatement (tagSql )) {
@@ -190,6 +198,87 @@ private static boolean saveSinglePage(Connection conn, GroupData g, String pageI
190198 return true ;
191199 }
192200
201+ // 新しいデータとDB上のデータが同じかチェック
202+ private static boolean isInventoryDataSame (Connection conn , GroupData g , String pageId , InventoryData inv ) throws SQLException {
203+ // DBから現在のデータを取得
204+ String sql = "SELECT display_name, row_count, require_permission, version FROM inventory_table WHERE group_uuid = ? AND page_id = ?" ;
205+ try (PreparedStatement ps = conn .prepareStatement (sql )) {
206+ ps .setString (1 , g .groupUUID .toString ());
207+ ps .setString (2 , pageId );
208+ try (ResultSet rs = ps .executeQuery ()) {
209+ if (rs .next ()) {
210+ String dbDisplayName = rs .getString ("display_name" );
211+ int dbRowCount = rs .getInt ("row_count" );
212+ String dbRequirePermission = rs .getString ("require_permission" );
213+ long dbVersion = rs .getLong ("version" );
214+
215+ // displayNameの比較(nullでも比較できる)
216+ if ((inv .displayName == null && dbDisplayName != null ) || (inv .displayName != null && !inv .displayName .equals (dbDisplayName ))) {
217+ return false ;
218+ }
219+
220+ if (dbRowCount != inv .rows ) {
221+ return false ;
222+ }
223+
224+ // require_permissionの比較(必要に応じてJSON解析)
225+ if (!dbRequirePermission .equals (gson .toJson (inv .requirePermission ))) {
226+ return false ;
227+ }
228+
229+ // バージョンチェック
230+ if (dbVersion != inv .version ) {
231+ return false ;
232+ }
233+
234+ // アイテムスロットの比較(インベントリ内容の差異をチェック)
235+ String fetchItemsSql = "SELECT slot, itemstack FROM inventory_item_table WHERE group_uuid = ? AND page_id = ?" ;
236+ Map <Integer , String > dbItemsMap = new HashMap <>();
237+ try (PreparedStatement psItems = conn .prepareStatement (fetchItemsSql )) {
238+ psItems .setString (1 , g .groupUUID .toString ());
239+ psItems .setString (2 , pageId );
240+ try (ResultSet rsItems = psItems .executeQuery ()) {
241+ while (rsItems .next ()) {
242+ dbItemsMap .put (rsItems .getInt ("slot" ), rsItems .getString ("itemstack" ));
243+ }
244+ }
245+ }
246+
247+ // 現在のアイテムスロットとDBのアイテムスロットを比較
248+ if (!compareInventoryItems (dbItemsMap , inv .itemStackSlot )) {
249+ return false ;
250+ }
251+
252+ return true ;
253+ }
254+ }
255+ }
256+ return false ;
257+ }
258+
259+ // アイテムの比較メソッド
260+ private static boolean compareInventoryItems (Map <Integer , String > dbItems , Map <Integer , ItemStack > clientItems ) {
261+ // アイテムの数が違う場合は即座に差異あり
262+ if (dbItems .size () != clientItems .size ()) {
263+ return false ;
264+ }
265+
266+ // スロットごとにアイテムを比較
267+ for (Map .Entry <Integer , ItemStack > clientEntry : clientItems .entrySet ()) {
268+ int slot = clientEntry .getKey ();
269+ ItemStack clientItem = clientEntry .getValue ();
270+ String dbItemBase64 = dbItems .get (slot );
271+
272+ // DBアイテムとクライアントアイテムが異なる場合
273+ String clientItemBase64 = ItemSerializer .serializeToBase64 (clientItem );
274+ if (!clientItemBase64 .equals (dbItemBase64 )) {
275+ return false ;
276+ }
277+ }
278+
279+ return true ;
280+ }
281+
193282
194283
195284
@@ -664,6 +753,31 @@ public static long getInventoryPageVersion(Connection conn, UUID groupUUID, Stri
664753 }
665754 }
666755
756+ /** プレイヤーが属しているすべてのGroupDataを取得(group_member_tableベース) */
757+ public static @ NotNull List <GroupData > loadGroupsByPlayer (OfflinePlayer player ) {
758+ List <GroupData > result = new ArrayList <>();
759+ UUID playerUUID = player .getUniqueId ();
760+
761+ try (Connection conn = db .getConnection ()) {
762+ String sql = "SELECT DISTINCT group_uuid FROM group_member_table WHERE member_uuid = ?" ;
763+ try (PreparedStatement ps = conn .prepareStatement (sql )) {
764+ ps .setString (1 , playerUUID .toString ());
765+ try (ResultSet rs = ps .executeQuery ()) {
766+ while (rs .next ()) {
767+ UUID groupUUID = UUID .fromString (rs .getString ("group_uuid" ));
768+ GroupData gd = loadGroupData (groupUUID );
769+ if (gd != null ) {
770+ result .add (gd );
771+ }
772+ }
773+ }
774+ }
775+ } catch (SQLException e ) {
776+ Bukkit .getLogger ().warning ("プレイヤー所属グループ取得に失敗: " + e .getMessage ());
777+ }
778+
779+ return result ;
780+ }
667781
668782 // ===========================================================
669783 // ===== 3. DELETE ==========================================
@@ -672,9 +786,9 @@ public static long getInventoryPageVersion(Connection conn, UUID groupUUID, Stri
672786 public static void deletePageData (Connection conn , UUID groupUUID , String pluginName , String pageId , String executedBy ) throws SQLException {
673787
674788 // 差分ログを削除前に保存
675- GroupData group = GroupManager . getGroupByUUID (groupUUID );
789+ GroupData group = loadGroupData (groupUUID );
676790 if (group != null ) {
677- UnifiedLogManager .saveDiffLogs ( BetterStorage . BSPlugin . getDatabaseManager (), group );
791+ UnifiedLogManager .saveBackupSnapshot ( group );
678792 }
679793
680794 String [] sqls = {
0 commit comments