Video Game Design and Coding

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. 1. Run validate on a correct dictionary, an array, a string count, negative counts and an unknown checkpoint.
  2. 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.

Turn in typed notes and a JPEG, PNG, or WebP screenshot. One https address is stored as text and is not opened, extracted, or run. An MP3, WAV, M4A, MP4, or WebM file up to 4 MB can show a demo. The demo does not replace editable source. Zip and code files are refused. If there is no private link, keep the project on the family computer and review the code in person.

This is a preview. A parent assigns the lesson and chooses the due date after the student is enrolled. The answer key is not on this page.

Print this lesson