Skip to content

Fix v2.058 compatibility: replace eval/toString with safe wrappers#228

Open
KingIKO wants to merge 5 commits into
Icehawk78:mainfrom
KingIKO:fix/v2058-compat
Open

Fix v2.058 compatibility: replace eval/toString with safe wrappers#228
KingIKO wants to merge 5 commits into
Icehawk78:mainfrom
KingIKO:fix/v2058-compat

Conversation

@KingIKO

@KingIKO KingIKO commented May 9, 2026

Copy link
Copy Markdown

Summary

Fix Cookie Clicker v2.058 compatibility by replacing the fragile eval(fn.toString().replace(...)) pattern with safe function wrappers.

  • Replace eval/toString with wrapper hooks: Game.UpdateWrinklers, Game.shimmerTypes.golden.popFunc, and Game.Popup are now wrapped rather than rewritten via string replacement. A context-tagging pattern (FrozenCookies._popupContext) lets the Popup wrapper distinguish golden cookie vs wrinkler popups without needing to parse function internals.
  • Replace safeGainsCalc eval clone with save/restore: Instead of cloning CalculateGains via string manipulation, calls the real Game.CalculateGains(), copies the result, then restores the three mutated globals (cookiesPs, globalCpsMult, recalculateGains).
  • Fix baseUrl detection for Game.LoadMod loading: Game.LoadMod generates script element IDs dynamically (e.g. modscript_https___cdn_...), not the hardcoded modscript_frozen_cookies that FC expected. Added a querySelector('script[src*=\"frozen_cookies.js\"]') fallback.
  • Bump lastCompatibleVersion to 2.058

What was broken

FC used eval(Game.UpdateWrinklers.toString().replace(...)) to inject logging into game functions. This works only when:

  1. The function source contains the exact variable names FC expects (vn, oo for minified builds, or named variables for unminified)
  2. The re-eval'd function can access all closure-scoped variables from its original scope

In CC v2.058, both conditions can fail — minified Steam builds use different variable names, and closure-scoped helpers like inRect, choose, LBeautify become undefined when the function is re-evaluated in FC's global scope.

Design decisions

  • Wrapper pattern over eval: Wrapping preserves the original function in its native closure scope. No regex fragility, no closure leakage.
  • Context tagging for popup routing: The Popup wrapper checks FrozenCookies._popupContext (set by the UpdateWrinklers/popFunc wrappers via try/finally) to determine whether a popup is a golden cookie or wrinkler event. This avoids needing to parse popup text.
  • logEvent(..., false) in wrappers: The wrappers pass popup=false to logEvent because the original Game._fc_originalPopup is already called by the wrapper — passing true would cause logEvent to call Game.Popup again, creating infinite recursion.

Test plan

  • Load via Game.LoadMod on CC v2.058 — no ReferenceErrors for 60+ seconds
  • FC info panel visible with non-zero CpS calculations
  • Auto-buy purchases buildings and upgrades
  • Golden cookie popups logged as "GC" events
  • Wrinkler popups logged as "Wrinkler" events
  • No infinite recursion / stack overflow from Popup wrapper

Files changed

File Change
frozen_cookies.js Bump version to 2.058, fix baseUrl detection for Game.LoadMod
fc_main.js Replace 3 eval/toString blocks with safe wrappers + save/restore safeGainsCalc
.gitignore Add PEM and temp files
RUN_LOCALLY.md Rewrite with working HTTPS loading instructions

KingIKO added 4 commits May 9, 2026 12:42
The three eval(fn.toString().replace(...)) calls at fc_main.js:174-193
broke on Cookie Clicker v2.058 because the re-eval'd functions lost
access to closure-scoped variables (inRect, choose, LBeautify, loc,
etc.) from the original Game scope, causing ReferenceErrors for
minified names like `vn` and `oo`.

Fix: replace all three eval/toString patterns with safe alternatives:
- Game.UpdateWrinklers: wrapped to tag popups as Wrinkler events
- shimmerTypes.golden.popFunc: wrapped to tag popups as GC events
- Game.Popup: wrapped to intercept and log tagged popup events
- FrozenCookies.safeGainsCalc: calls real Game.CalculateGains() then
  saves/restores Game.cookiesPs and Game.globalCpsMult

Also bumps lastCompatibleVersion to 2.058.
Game.LoadMod generates a dynamic script element ID based on the URL,
not the fixed modscript_frozen_cookies that FC looks for. Added a
querySelector fallback to find the script tag by its src attribute.
Also strip query strings from the src when deriving baseUrl.
logEvent calls Game.Popup when popup=true, which triggers the wrapper
again. Pass popup=false since the wrapper already calls the original
Game.Popup — the popup still shows, just without re-entering logEvent.
Add PEM and temp files to .gitignore. Rewrite RUN_LOCALLY.md to document
the jsDelivr approach (which works with HTTPS) and an optional local HTTPS
server via mkcert, replacing the broken HTTP localhost instructions.
@KingIKO KingIKO force-pushed the fix/v2058-compat branch from e6ce20c to 176023d Compare May 9, 2026 17:11
In v2.058 Cookie Clicker, Diamond-slot Godzamok automatically grants a
Devastation buff at the start of any click buff (Click Frenzy, Dragonflight,
Cursed Finger). FC's autoGodzamokAction had !Game.hasBuff("Devastation") as
an anti-spam check, which now permanently blocks selling for any user with
Godzamok in the diamond slot — the standard Godzamok build.

Replace the Devastation check with a timestamp-based anti-spam guard
(FrozenCookies._lastGodzamokSell). 12-second cooldown roughly matches the
Devastation buff duration so the original intent is preserved.
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.

1 participant