Video Game Design and Coding · PLECK lesson
PLECK lesson 113 · suggested week 23, day 3
Validate before trusting loaded values
Objective. Apply type, version, range and allowed-ID checks.
Godot Engine Standard desktop editor 4.7.2-stable, GDScript (Godot 4), Compatibility renderer. Headless Godot 4.7.2.stable.official.ed1daf0bf passed 106 assertions on the supplied scaffolds, including malformed JSON saves. Audio used the Dummy driver and a synthetic WAV, not a speaker. The visual editor, browser export, and signed-in submission were not tested. The desk still does not run a student's own upload.
About 55 minutes. The due date is chosen by a parent.
JSON, user://, schema validation and safe failure
Suggested pace: Recall and predict 5 min; Read instruction and scaffold 12 min; Build or design the assigned artifact 25 min; Run acceptance tests 8 min; Explain and checkpoint 5 min.
Materials: Godot Standard 4.7.2 desktop editor (GDScript, Compatibility renderer), Local course folder and prior checkpoint, No paid assets or public account required.
Comes after VGC-L112.
A save file can be missing, truncated, edited or from another version. C-STATE-SAVE validates the outer Dictionary, version, numeric counts and allowed checkpoint IDs. It clamps counts to zero through ninety-nine and falls back to start for unknown locations. It never loads a script or scene path supplied by the file. Validation is part of program correctness even for a local game: accidental corruption is enough reason to defend the boundary.
Worked example
A validated save contains known scalar fields and a version. An array instead of a dictionary is syntactically valid JSON but should still be rejected by the schema. Scaffold C-STATE-SAVE — Validated inventory and checkpoint data Setup: Attach to a Node named SaveData. This is a separate file from C-SAVE and only reads fixed known scalar fields. get_checkpoint_data returns plain data, not live node references. When applying a loaded checkpoint, the assignment maps checkpoint ID to a teacher-approved safe Marker2D, never to a filename supplied by the save. ```gdscript extends Node const PATH: String = "user://seed_station_v1.json" func defaults() -> Dictionary: return {"version": 1, "seed": 0, "water": 0, "checkpoint": "start"} func _bounded_count(value: Variant) -> int: if value is int or value is float: return clampi(int(value), 0, 99) return 0 func validate(raw: Variant) -> Dictionary: var clean := defaults() if not raw is Dictionary: return clean var data: Dictionary = raw if data.get("version", 0) != 1: return clean clean["seed"] = _bounded_count(data.get("seed", 0)) clean["water"] = _bounded_count(data.get("water", 0)) var checkpoint: Variant = data.get("checkpoint", "start") if checkpoint is String and checkpoint in ["start", "greenhouse"]: clean["checkpoint"] = checkpoint return clean func save_data(raw: Dictionary) -> bool: var file := FileAccess.open(PATH, FileAccess.WRITE) if file == null: return false file.store_string(JSON.stringify(validate(raw))) file.close() return true func load_data() -> Dictionary: if not FileAccess.file_exists(PATH): return defaults() var file := FileAccess.open(PATH, FileAccess.READ) if file == null: return defaults() var text: String = file.get_as_text() file.close() var parser := JSON.new() if parser.parse(text) != OK: return defaults() return validate(parser.data) ```
Validated inventory and checkpoint data
GDScript (Godot 4, not Scratch, Python, JavaScript or C#). Scaffold C-STATE-SAVE. Copy starts from this text. The desk does not run it.
Scene and node setup
Attach to a Node named SaveData. This is a separate file from C-SAVE and only reads fixed known scalar fields. get_checkpoint_data returns plain data, not live node references. When applying a loaded checkpoint, the assignment maps checkpoint ID to a teacher-approved safe Marker2D, never to a filename supplied by the save.
extends Node
const PATH: String = "user://seed_station_v1.json"
func defaults() -> Dictionary:
return {"version": 1, "seed": 0, "water": 0, "checkpoint": "start"}
func _bounded_count(value: Variant) -> int:
if value is int or value is float:
return clampi(int(value), 0, 99)
return 0
func validate(raw: Variant) -> Dictionary:
var clean := defaults()
if not raw is Dictionary:
return clean
var data: Dictionary = raw
if data.get("version", 0) != 1:
return clean
clean["seed"] = _bounded_count(data.get("seed", 0))
clean["water"] = _bounded_count(data.get("water", 0))
var checkpoint: Variant = data.get("checkpoint", "start")
if checkpoint is String and checkpoint in ["start", "greenhouse"]:
clean["checkpoint"] = checkpoint
return clean
func save_data(raw: Dictionary) -> bool:
var file := FileAccess.open(PATH, FileAccess.WRITE)
if file == null:
return false
file.store_string(JSON.stringify(validate(raw)))
file.close()
return true
func load_data() -> Dictionary:
if not FileAccess.file_exists(PATH):
return defaults()
var file := FileAccess.open(PATH, FileAccess.READ)
if file == null:
return defaults()
var text: String = file.get_as_text()
file.close()
var parser := JSON.new()
if parser.parse(text) != OK:
return defaults()
return validate(parser.data)
Checks to run in the editor
- Missing file returns defaults.
- Negative count becomes zero; count 1000 becomes 99.
- Unknown checkpoint becomes start.
- Malformed JSON, arrays and unsupported versions return safe defaults.
Native Godot 4.7.2.stable.official.ed1daf0bf headless compile and representative node-tree/runtime tests passed; see native-qa-results.json. Visual UI, audible output, exports and website submission still need separate checks.
Validate before trusting loaded values — evidence task
- 1. Run validate on a correct dictionary, an array, a string count, negative counts and an unknown checkpoint.
- 2. Record the cleaned result for each.
Deliverable. Validation lab and five-case input/output table.
Acceptance checks
- VGC-L113-T1. Invalid outer data returns defaults.
- VGC-L113-T2. Unknown checkpoint never becomes a file path or node load request.
Understanding question. Why is JSON parsing success not the same as schema validity?
Include a downloadable project ZIP with project.godot, .tscn, .gd and original asset files when code changes. A private, teacher-accessible download link to that ZIP is equivalent. Screenshots or a playthrough do not replace editable source.
If this is still shaky. Use the course-specific save filenames in C-SAVE and C-STATE-SAVE. Never edit unrelated game saves or load scripts from save data. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.
Optional extra. Add a new fixed scalar field with a safe default and explicit validation. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.
- Godot step by step: nodes, scenes, GDScript, input, signals
Free public documentation, no account. Same class help is available within the desktop editor.
· Opening this link does not submit the lesson. - Saving games: FileAccess and JSON
Free public documentation, no account.
· Opening this link does not submit the lesson.
Name ____________________ Date ____________________