diff --git a/lib/src/core/engine/combat_tick_service.dart b/lib/src/core/engine/combat_tick_service.dart index 280ca48..b30d01a 100644 --- a/lib/src/core/engine/combat_tick_service.dart +++ b/lib/src/core/engine/combat_tick_service.dart @@ -179,12 +179,14 @@ class CombatTickService { if (monsterStats.isAlive && monsterAccumulator >= monsterStats.attackDelayMs) { // 방어력/회피율 버프 적용 - final buffedPlayerForDefense = (buffMods.defMod != 0 || - buffMods.evasionMod != 0) + final buffedPlayerForDefense = + (buffMods.defMod != 0 || buffMods.evasionMod != 0) ? playerStats.copyWith( def: (playerStats.def * (1.0 + buffMods.defMod)).round(), - evasion: (playerStats.evasion + buffMods.evasionMod) - .clamp(0.0, 1.0), + evasion: (playerStats.evasion + buffMods.evasionMod).clamp( + 0.0, + 1.0, + ), ) : playerStats; diff --git a/lib/src/core/logging/error_logger.dart b/lib/src/core/logging/error_logger.dart index e941ae9..729f227 100644 --- a/lib/src/core/logging/error_logger.dart +++ b/lib/src/core/logging/error_logger.dart @@ -122,8 +122,7 @@ class ErrorLogger { if (size <= maxLogBytes) return; final dir = file.parent.path; - final baseName = - _logFileName.replaceAll('.jsonl', ''); + final baseName = _logFileName.replaceAll('.jsonl', ''); // 가장 오래된 백업 삭제 final oldest = File('$dir/$baseName.$maxBackupCount.jsonl'); diff --git a/lib/src/core/storage/save_integrity.dart b/lib/src/core/storage/save_integrity.dart index 3fa55bb..db729e2 100644 --- a/lib/src/core/storage/save_integrity.dart +++ b/lib/src/core/storage/save_integrity.dart @@ -21,18 +21,60 @@ class SaveIntegrity { static List get _hmacKey { // 파트 A: 원본 키의 전반부 const partA = [ - 0x41, 0x73, 0x63, 0x69, 0x69, 0x4e, 0x65, 0x76, - 0x65, 0x72, 0x44, 0x69, 0x65, 0x53, 0x61, 0x76, + 0x41, + 0x73, + 0x63, + 0x69, + 0x69, + 0x4e, + 0x65, + 0x76, + 0x65, + 0x72, + 0x44, + 0x69, + 0x65, + 0x53, + 0x61, + 0x76, ]; // 파트 B: XOR 마스크(mask) const mask = [ - 0x7a, 0x1c, 0x0f, 0x05, 0x0d, 0x22, 0x09, 0x1a, - 0x09, 0x1e, 0x28, 0x05, 0x09, 0x3f, 0x0d, 0x1a, + 0x7a, + 0x1c, + 0x0f, + 0x05, + 0x0d, + 0x22, + 0x09, + 0x1a, + 0x09, + 0x1e, + 0x28, + 0x05, + 0x09, + 0x3f, + 0x0d, + 0x1a, ]; // 파트 C: partA XOR mask 결과 (키 후반부) const partC = [ - 0x3b, 0x6f, 0x6c, 0x6c, 0x64, 0x6c, 0x6c, 0x6c, - 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x3b, + 0x6f, + 0x6c, + 0x6c, + 0x64, + 0x6c, + 0x6c, + 0x6c, + 0x6c, + 0x6c, + 0x6c, + 0x6c, + 0x6c, + 0x6c, + 0x6c, + 0x6c, ]; // 전반부(partA) + 후반부(partC XOR mask)로 32바이트 키 생성 @@ -107,10 +149,7 @@ class SaveIntegrity { /// HMAC 검증 결과(result) class SaveIntegrityResult { - const SaveIntegrityResult({ - required this.gzipBytes, - required this.isLegacy, - }); + const SaveIntegrityResult({required this.gzipBytes, required this.isLegacy}); /// HMAC을 제외한 순수 GZip 데이터 final Uint8List gzipBytes; diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 65ef90f..9f3de00 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,6 +1,8 @@ PODS: - audio_session (0.0.1): - FlutterMacOS + - flutter_secure_storage_macos (6.1.3): + - FlutterMacOS - FlutterMacOS (1.0.0) - in_app_purchase_storekit (0.0.1): - Flutter @@ -22,6 +24,7 @@ PODS: DEPENDENCIES: - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - in_app_purchase_storekit (from `Flutter/ephemeral/.symlinks/plugins/in_app_purchase_storekit/darwin`) - just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/darwin`) @@ -33,6 +36,8 @@ DEPENDENCIES: EXTERNAL SOURCES: audio_session: :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos FlutterMacOS: :path: Flutter/ephemeral in_app_purchase_storekit: @@ -50,6 +55,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: audio_session: 728ae3823d914f809c485d390274861a24b0904e + flutter_secure_storage_macos: c2754d3483d20bb207bb9e5a14f1b8e771abcdb9 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 in_app_purchase_storekit: 2342c0a5da86593124d08dd13d920f39a52b273a just_audio: a42c63806f16995daf5b219ae1d679deb76e6a79 diff --git a/test/core/model/monetization_state_test.dart b/test/core/model/monetization_state_test.dart index 855974c..7bae35e 100644 --- a/test/core/model/monetization_state_test.dart +++ b/test/core/model/monetization_state_test.dart @@ -60,8 +60,7 @@ void main() { }); test('종료 시점이 미래면 true 반환', () { - final futureMs = - DateTime.now().millisecondsSinceEpoch + 60000; // 1분 후 + final futureMs = DateTime.now().millisecondsSinceEpoch + 60000; // 1분 후 final state = MonetizationState.initial().copyWith( autoReviveEndMs: futureMs, ); @@ -70,8 +69,7 @@ void main() { }); test('종료 시점이 과거면 false 반환', () { - final pastMs = - DateTime.now().millisecondsSinceEpoch - 1000; // 1초 전 + final pastMs = DateTime.now().millisecondsSinceEpoch - 1000; // 1초 전 final state = MonetizationState.initial().copyWith( autoReviveEndMs: pastMs, ); @@ -102,8 +100,7 @@ void main() { }); test('무료 사용자 - 종료 시점이 미래면 true 반환', () { - final futureMs = - DateTime.now().millisecondsSinceEpoch + 60000; + final futureMs = DateTime.now().millisecondsSinceEpoch + 60000; final state = MonetizationState.initial().copyWith( speedBoostEndMs: futureMs, ); @@ -112,8 +109,7 @@ void main() { }); test('무료 사용자 - 종료 시점이 과거면 false 반환', () { - final pastMs = - DateTime.now().millisecondsSinceEpoch - 1000; + final pastMs = DateTime.now().millisecondsSinceEpoch - 1000; final state = MonetizationState.initial().copyWith( speedBoostEndMs: pastMs, ); diff --git a/test/core/storage/save_integrity_test.dart b/test/core/storage/save_integrity_test.dart index fbea24c..e2a7c0e 100644 --- a/test/core/storage/save_integrity_test.dart +++ b/test/core/storage/save_integrity_test.dart @@ -15,7 +15,10 @@ void main() { final signed = SaveIntegrity.sign(original); // 서명된 데이터 = HMAC(32바이트) + 원본 - expect(signed.length, equals(SaveIntegrity.hmacLength + original.length)); + expect( + signed.length, + equals(SaveIntegrity.hmacLength + original.length), + ); final result = SaveIntegrity.verify(signed); @@ -101,7 +104,9 @@ void main() { test('레거시 포맷은 HMAC 검증을 건너뛰고 원본 데이터 반환', () { // GZip 매직 바이트로 시작하는 큰 데이터 final legacyData = Uint8List.fromList([ - 0x1f, 0x8b, ...List.generate(100, (i) => i % 256), + 0x1f, + 0x8b, + ...List.generate(100, (i) => i % 256), ]); final result = SaveIntegrity.verify(legacyData);