Skip to content

Conversation

@mpereiraesaa
Copy link

@mpereiraesaa mpereiraesaa commented Nov 15, 2025

Summary by CodeRabbit

  • Chores
    • Added internal infrastructure for managing game state persistence and process lifecycle.

Note: This release contains internal technical changes with no direct impact on user-facing features or gameplay.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 15, 2025

Walkthrough

A new payload module is introduced that embeds a YARPE persistence mechanism into Ren'Py save files. It injects a custom pickle object into the persistent save, which triggers bootstrap code execution when the save is loaded, then overwrites the serialized persistent file to disk.

Changes

Cohort / File(s) Summary
New payload module
payloads/force_persistent.py
Introduces YARPE bootstrap injection into Ren'Py persistent saves via custom pickle object; includes constants for persistent path and bootstrap script, serializable Yummy class with __reduce__ hook, system call utilities (kill_game), and main orchestration function that modifies and rewrites the persistent save file.

Sequence Diagram

sequenceDiagram
    participant P as Payload Module
    participant R as Ren'Py Persistent
    participant D as Disk (/saves/persistent)
    
    rect rgb(200, 220, 240)
    Note over P,D: Injection Phase
    P->>R: Attach Yummy trigger object
    P->>R: Mark persistent as changed
    end
    
    rect rgb(240, 220, 200)
    Note over P,D: Serialization Phase
    P->>P: Pickle persistent (protocol 2)
    P->>P: Compress with zlib
    end
    
    rect rgb(220, 240, 200)
    Note over P,D: Write Phase
    P->>D: Overwrite /saves/persistent
    end
    
    rect rgb(240, 200, 220)
    Note over P,D: Trigger Phase (on game load)
    D->>R: Load persistent from disk
    R->>R: Unpickle & decompress
    R->>R: Call Yummy.__reduce__()
    R->>R: Execute SCRIPT via Ren'Py globals
    end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Pickle manipulation and custom __reduce__ method require careful examination of serialization behavior
  • System call wrapping (getpid, kill, SIGKILL) and process termination logic require security review
  • File I/O operations and compression handling should be verified for proper error cases
  • Embedded Ren'Py/YARPE bootstrap script logic is difficult to verify without broader context

Poem

Hop, hop! The payload finds its way,
Through Ren'Py saves where triggers play,
A Yummy pickle, YARPE's call,
Bootstrap magic conquers all! 🐰✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a payload mechanism for YARPE auto-load functionality via Ren'Py's persistent save file.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sorry, something went wrong.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
payloads/force_persistent.py (3)

12-59: Label callback override may clobber any existing game callback

The bootstrap script unconditionally assigns renpy.config.label_callback = yarpe_label_callback, which will discard any label callback the host game (or other mods) may have installed. That’s fine if you don’t care about compatibility, but if you want to be less invasive you could capture and chain the previous callback, e.g. inside SCRIPT:

old_cb = renpy.config.label_callback

def yarpe_label_callback(label, abnormal):
    ylog("LABEL: %s (abnormal=%r)" % (label, abnormal))

    if label == "splashscreen" and not ys.game_initialized:
        ys.game_initialized = True
        ylog("YARPE: Entered _call__enter_game_menu_0 → autoloading slot '1-1'.")
        try:
            renpy.load("1-1")
        except Exception as e:
            ylog("YARPE ERROR: renpy.load('1-1') failed: %r" % e)

    if old_cb is not None:
        try:
            old_cb(label, abnormal)
        except Exception as e:
            ylog("YARPE: original label_callback raised: %r" % e)

This keeps your behavior while preserving any existing callback.


66-94: Unify syscall error typing and consider wiring guarantees for getpid/kill

Two minor points here:

  1. getpid failure raises a bare Exception while kill raises SocketError. For consistency (and to satisfy linters complaining about generic exceptions), consider using the same domain-specific exception in both branches, e.g.:
-    if pid < 0:
-        raise Exception(
+    if pid < 0:
+        raise SocketError(
             "getpid failed with return value %d, error %d\n%s"
             % (
                 pid,
                 sc.syscalls.getpid.errno,
                 sc.syscalls.getpid.get_error_string(),
             )
         )
  1. kill_game assumes sc.syscalls.getpid and sc.syscalls.kill have already been bound using the updated SYSCALL entries. It’s worth double‑checking that your initialization path guarantees these call wrappers exist before this payload runs; otherwise, you could get an AttributeError here instead of a clean error.

95-138: Broad Exception catches are acceptable here but can be tightened or made more debuggable

Given this is a one‑shot payload, catching Exception around pickle.dumps, zlib.compress, and the file write to log and bail is reasonable. If you want to placate stricter linters and improve diagnostics:

  • Narrow the exceptions to likely failures, e.g. pickle.PicklingError, zlib.error, and OSError/IOError for the file operations.
  • Or, keep Exception but log a traceback as well so you don’t lose context during debugging.

Functionally it’s fine as is; this is more about tooling friendliness and future debugging ease.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc04aed and 820330b.

📒 Files selected for processing (1)
  • payloads/force_persistent.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
payloads/force_persistent.py (3)
src/utils/rp.py (1)
  • log (45-52)
payloads/updater_for_up_to_2.x.x.py (1)
  • sc (16-34)
renpy/python.py (1)
  • py_exec (1-2)
🪛 Ruff (0.14.4)
payloads/force_persistent.py

75-82: Create your own exception

(TRY002)


116-116: Do not catch blind exception: Exception

(BLE001)


122-122: Do not catch blind exception: Exception

(BLE001)


136-136: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (2)
payloads/force_persistent.py (2)

1-11: Imports and base constants look tight and purposeful

All imported symbols are used, and the single PERSISTENT_PATH constant keeps the file’s external contract minimal. No issues here.


62-64: Pickle-based bootstrap via Yummy.__reduce__ is coherent

Using __reduce__ to return renpy.python.py_exec with the embedded SCRIPT ensures the payload runs on unpickle as intended, and the tuple shape matches what pickle expects. Just make sure renpy.python.py_exec exists for all Ren’Py versions/platforms you target.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

None yet

1 participant