lint fixes
Some checks failed
Continuous Integration / Code Formatting (pull_request) Successful in 27s
Continuous Integration / Code Quality Check (pull_request) Successful in 29s
Continuous Integration / Test Execution (pull_request) Failing after 33s
Continuous Integration / CI Summary (pull_request) Failing after 5s

This commit is contained in:
2025-09-28 19:16:20 +04:00
parent c1f3f9f708
commit eb99c6a18e
46 changed files with 2608 additions and 1304 deletions

View File

@@ -14,10 +14,6 @@ const MAX_SCORE: int = 999999999
const MAX_GAMES_PLAYED: int = 100000
const MAX_FILE_SIZE: int = 1048576 # 1MB limit
# Save operation protection - prevents race conditions
var _save_in_progress: bool = false
var _restore_in_progress: bool = false
var game_data: Dictionary = {
"high_score": 0,
"current_score": 0,
@@ -32,6 +28,10 @@ var game_data: Dictionary = {
}
}
# Save operation protection - prevents race conditions
var _save_in_progress: bool = false
var _restore_in_progress: bool = false
func _ready() -> void:
"""Initialize SaveManager and load existing save data on startup"""
@@ -98,12 +98,22 @@ func load_game() -> void:
# Reset restore flag
_restore_in_progress = false
var loaded_data = _load_and_parse_save_file()
if loaded_data == null:
return
# Process the loaded data
_process_loaded_data(loaded_data)
func _load_and_parse_save_file() -> Variant:
"""Load and parse the save file, returning null on failure"""
var save_file: FileAccess = FileAccess.open(SAVE_FILE_PATH, FileAccess.READ)
if save_file == null:
DebugManager.log_error(
"Failed to open save file for reading: %s" % SAVE_FILE_PATH, "SaveManager"
)
return
return null
# Check file size
var file_size: int = save_file.get_length()
@@ -112,14 +122,14 @@ func load_game() -> void:
"Save file too large: %d bytes (max %d)" % [file_size, MAX_FILE_SIZE], "SaveManager"
)
save_file.close()
return
return null
var json_string: Variant = save_file.get_var()
save_file.close()
if not json_string is String:
DebugManager.log_error("Save file contains invalid data type", "SaveManager")
return
return null
var json: JSON = JSON.new()
var parse_result: Error = json.parse(json_string)
@@ -127,48 +137,33 @@ func load_game() -> void:
DebugManager.log_error(
"Failed to parse save file JSON: %s" % json.error_string, "SaveManager"
)
if not _restore_in_progress:
var backup_restored = _restore_backup_if_exists()
if not backup_restored:
DebugManager.log_warn(
"JSON parse failed and backup restore failed, using defaults", "SaveManager"
)
return
_handle_load_failure("JSON parse failed")
return null
var loaded_data: Variant = json.data
if not loaded_data is Dictionary:
DebugManager.log_error("Save file root is not a dictionary", "SaveManager")
if not _restore_in_progress:
var backup_restored = _restore_backup_if_exists()
if not backup_restored:
DebugManager.log_warn(
"Invalid data format and backup restore failed, using defaults", "SaveManager"
)
return
_handle_load_failure("Invalid data format")
return null
return loaded_data
func _process_loaded_data(loaded_data: Variant) -> void:
"""Process and validate the loaded data"""
# Validate checksum first
if not _validate_checksum(loaded_data):
DebugManager.log_error(
"Save file checksum validation failed - possible tampering", "SaveManager"
)
if not _restore_in_progress:
var backup_restored = _restore_backup_if_exists()
if not backup_restored:
DebugManager.log_warn(
"Backup restore failed, using default game data", "SaveManager"
)
_handle_load_failure("Checksum validation failed")
return
# Handle version migration
var migrated_data: Variant = _handle_version_migration(loaded_data)
if migrated_data == null:
DebugManager.log_error("Save file version migration failed", "SaveManager")
if not _restore_in_progress:
var backup_restored = _restore_backup_if_exists()
if not backup_restored:
DebugManager.log_warn(
"Migration failed and backup restore failed, using defaults", "SaveManager"
)
_handle_load_failure("Migration failed")
return
# Validate and fix loaded data
@@ -176,19 +171,21 @@ func load_game() -> void:
DebugManager.log_error(
"Save file failed validation after migration, using defaults", "SaveManager"
)
if not _restore_in_progress:
var backup_restored = _restore_backup_if_exists()
if not backup_restored:
DebugManager.log_warn(
"Validation failed and backup restore failed, using defaults", "SaveManager"
)
_handle_load_failure("Validation failed")
return
# Use migrated data
loaded_data = migrated_data
# Safely merge validated data
_merge_validated_data(loaded_data)
_merge_validated_data(migrated_data)
func _handle_load_failure(reason: String) -> void:
"""Handle load failure with backup restoration attempt"""
if not _restore_in_progress:
var backup_restored = _restore_backup_if_exists()
if not backup_restored:
DebugManager.log_warn(
"%s and backup restore failed, using defaults" % reason, "SaveManager"
)
DebugManager.log_info(
(
@@ -375,6 +372,28 @@ func reset_all_progress() -> bool:
# Security and validation helper functions
func _validate_save_data(data: Dictionary) -> bool:
# Check required fields exist and have correct types
if not _validate_required_fields(data):
return false
# Validate numeric fields
if not _validate_score_fields(data):
return false
# Validate games_played field
if not _validate_games_played_field(data):
return false
# Validate grid state
var grid_state: Variant = data.get("grid_state", {})
if not grid_state is Dictionary:
DebugManager.log_error("Grid state is not a dictionary", "SaveManager")
return false
return _validate_grid_state(grid_state)
func _validate_required_fields(data: Dictionary) -> bool:
"""Validate that all required fields exist"""
var required_fields: Array[String] = [
"high_score", "current_score", "games_played", "total_score", "grid_state"
]
@@ -382,19 +401,21 @@ func _validate_save_data(data: Dictionary) -> bool:
if not data.has(field):
DebugManager.log_error("Missing required field: %s" % field, "SaveManager")
return false
return true
# Validate numeric fields
if not _is_valid_score(data.get("high_score", 0)):
DebugManager.log_error("Invalid high_score validation failed", "SaveManager")
return false
if not _is_valid_score(data.get("current_score", 0)):
DebugManager.log_error("Invalid current_score validation failed", "SaveManager")
return false
if not _is_valid_score(data.get("total_score", 0)):
DebugManager.log_error("Invalid total_score validation failed", "SaveManager")
return false
# Use safe getter for games_played validation
func _validate_score_fields(data: Dictionary) -> bool:
"""Validate all score-related fields"""
var score_fields = ["high_score", "current_score", "total_score"]
for field in score_fields:
if not _is_valid_score(data.get(field, 0)):
DebugManager.log_error("Invalid %s validation failed" % field, "SaveManager")
return false
return true
func _validate_games_played_field(data: Dictionary) -> bool:
"""Validate the games_played field"""
var games_played: Variant = data.get("games_played", 0)
if not (games_played is int or games_played is float):
DebugManager.log_error(
@@ -418,13 +439,7 @@ func _validate_save_data(data: Dictionary) -> bool:
)
return false
# Validate grid state
var grid_state: Variant = data.get("grid_state", {})
if not grid_state is Dictionary:
DebugManager.log_error("Grid state is not a dictionary", "SaveManager")
return false
return _validate_grid_state(grid_state)
return true
func _validate_and_fix_save_data(data: Dictionary) -> bool:
@@ -522,30 +537,71 @@ func _validate_and_fix_save_data(data: Dictionary) -> bool:
func _validate_grid_state(grid_state: Dictionary) -> bool:
# Check grid size
# Validate grid size
var grid_size_validation = _validate_grid_size(grid_state)
if not grid_size_validation.valid:
return false
var width = grid_size_validation.width
var height = grid_size_validation.height
# Validate tile types
var tile_types = _validate_tile_types(grid_state)
if tile_types == -1:
return false
# Validate active gem types
if not _validate_active_gem_types(grid_state, tile_types):
return false
# Validate grid layout if present
var layout: Variant = grid_state.get("grid_layout", [])
if not layout is Array:
DebugManager.log_error("grid_layout is not an array", "SaveManager")
return false
if layout.size() > 0:
return _validate_grid_layout(layout, width, height, tile_types)
return true
func _validate_grid_size(grid_state: Dictionary) -> Dictionary:
"""Validate grid size and return validation result with dimensions"""
var result = {"valid": false, "width": 0, "height": 0}
if not grid_state.has("grid_size") or not grid_state.grid_size is Dictionary:
DebugManager.log_error("Invalid grid_size in save data", "SaveManager")
return false
return result
var size: Variant = grid_state.grid_size
if not size.has("x") or not size.has("y"):
return false
return result
var width: Variant = size.x
var height: Variant = size.y
if not width is int or not height is int:
return false
return result
if width < 3 or height < 3 or width > MAX_GRID_SIZE or height > MAX_GRID_SIZE:
DebugManager.log_error("Grid size out of bounds: %dx%d" % [width, height], "SaveManager")
return false
return result
# Check tile types
result.valid = true
result.width = width
result.height = height
return result
func _validate_tile_types(grid_state: Dictionary) -> int:
"""Validate tile types count and return it, or -1 if invalid"""
var tile_types: Variant = grid_state.get("tile_types_count", 0)
if not tile_types is int or tile_types < 3 or tile_types > MAX_TILE_TYPES:
DebugManager.log_error("Invalid tile_types_count: %s" % str(tile_types), "SaveManager")
return false
return -1
return tile_types
# Validate active_gem_types if present
func _validate_active_gem_types(grid_state: Dictionary, tile_types: int) -> bool:
"""Validate active gem types array"""
var active_gems: Variant = grid_state.get("active_gem_types", [])
if not active_gems is Array:
DebugManager.log_error("active_gem_types is not an array", "SaveManager")
@@ -565,16 +621,6 @@ func _validate_grid_state(grid_state: Dictionary) -> bool:
"active_gem_types[%d] out of range: %d" % [i, gem_type], "SaveManager"
)
return false
# Validate grid layout if present
var layout: Variant = grid_state.get("grid_layout", [])
if not layout is Array:
DebugManager.log_error("grid_layout is not an array", "SaveManager")
return false
if layout.size() > 0:
return _validate_grid_layout(layout, width, height, tile_types)
return true
@@ -757,22 +803,30 @@ func _normalize_value_for_checksum(value: Variant) -> String:
"""
if value == null:
return "null"
elif value is bool:
if value is bool:
return str(value)
elif value is int:
if value is String:
return value
if value is int:
# Convert to int string format to match JSON deserialized floats
return str(int(value))
elif value is float:
# Convert float to int if it's a whole number (handles JSON conversion)
if value == int(value):
return str(int(value))
else:
# For actual floats, use consistent precision
return "%.10f" % value
elif value is String:
return value
else:
return str(value)
if value is float:
return _normalize_float_for_checksum(value)
return str(value)
func _normalize_float_for_checksum(value: float) -> String:
"""Normalize float values for checksum calculation"""
# Convert float to int if it's a whole number (handles JSON conversion)
if value == int(value):
return str(int(value))
# For actual floats, use consistent precision
return "%.10f" % value
func _validate_checksum(data: Dictionary) -> bool:
@@ -790,15 +844,15 @@ func _validate_checksum(data: Dictionary) -> bool:
# Try to be more lenient with existing saves to prevent data loss
var data_version: Variant = data.get("_version", 0)
if data_version <= 1:
(
DebugManager
. log_warn(
(
"Checksum mismatch in v%d save file - may be due to JSON serialization issue (stored: %s, calculated: %s)"
DebugManager.log_warn(
(
"Checksum mismatch in v%d save file - may be due to JSON serialization issue "
+ (
"(stored: %s, calculated: %s)"
% [data_version, stored_checksum, calculated_checksum]
),
"SaveManager"
)
)
),
"SaveManager"
)
(
DebugManager
@@ -810,15 +864,14 @@ func _validate_checksum(data: Dictionary) -> bool:
# Mark for checksum regeneration by removing the invalid one
data.erase("_checksum")
return true
else:
DebugManager.log_error(
(
"Checksum mismatch - stored: %s, calculated: %s"
% [stored_checksum, calculated_checksum]
),
"SaveManager"
)
return false
DebugManager.log_error(
(
"Checksum mismatch - stored: %s, calculated: %s"
% [stored_checksum, calculated_checksum]
),
"SaveManager"
)
return false
return is_valid
@@ -880,7 +933,7 @@ func _handle_version_migration(data: Dictionary) -> Variant:
"Save file is current version (%d)" % SAVE_FORMAT_VERSION, "SaveManager"
)
return data
elif data_version > SAVE_FORMAT_VERSION:
if data_version > SAVE_FORMAT_VERSION:
# Future version - cannot handle
DebugManager.log_error(
(
@@ -890,13 +943,12 @@ func _handle_version_migration(data: Dictionary) -> Variant:
"SaveManager"
)
return null
else:
# Older version - migrate
DebugManager.log_info(
"Migrating save data from version %d to %d" % [data_version, SAVE_FORMAT_VERSION],
"SaveManager"
)
return _migrate_save_data(data, data_version)
# Older version - migrate
DebugManager.log_info(
"Migrating save data from version %d to %d" % [data_version, SAVE_FORMAT_VERSION],
"SaveManager"
)
return _migrate_save_data(data, data_version)
func _migrate_save_data(data: Dictionary, from_version: int) -> Dictionary: