You hold onto friends by keeping your heart a little softer than your head
GameOptions are engine-level toggles that instantly apply changes when switched. They're also present in .ini configuration files within the engine directory. With every major patch, some options are added, removed, or modified. Not all of them have a noticeable effect — or work at all. It’s trial and error. 😉
Personally, I refer to each option by its PATH, but in-game and in .ini files, the final part needs to be split into two: what I call GROUP and OPTION.
To use these options in-game, you’ll need Cyber Engine Tweaks. Each data type requires a different command — the most common types are INT, BOOL, and FLOAT.
As an example, let’s use the path: RayTracing/Diffuse/UseScreenSpaceData. This is a boolean option — it can only be set to true or false.
GameOptions.SetBool('RayTracing/Diffuse', 'UseScreenSpaceData', true)
To make your options persistent, you’ll need to write them to an .ini file. Cyberpunk reads these at startup and applies the values automatically. The typical location for these files is: <INSTALL DIR>\engine\config\platform\pc
[RayTracing/Diffuse]
UseScreenSpaceData = true
The full list includes 1500+ entries, so I’ve moved it to /gameoptions (not optimized for mobile). Includes version-specific filtering and search support!
Sometimes I like to play older versions of Cyberpunk, especially 1.31, because the rendering engine changed significantly between versions 1.2 and 1.5+. I wasn’t able to download anything earlier than 1.31 via GOG, so I focused my efforts on Steam.
To get older Cyberpunk versions, I used the DepotDownloader from GitHub, which is built on .NET by Microsoft. All you need is the AppID, the DepotID, and the Manifest. A great resource for that is SteamDB - the Manifest determines which version from the app's depot you’ll download.
Open a terminal, navigate to the unpacked DepotDownloader folder, and run:
DepotDownloader.exe -app 1091500 -depot 1091501 -manifest <Manifest> -username <Username> -password <Password>
If you’ve got Steam Guard enabled, you’ll likely receive an authentication code via email. DepotDownloader will prompt you to enter it.
Below are the major versions with their final patch.
Version | Manifest | Patch | Manifest | ||
---|---|---|---|---|---|
1.0 | 7287387370544759862 | 7 December 2020 | 1.06 | 583937837845971710 | 23 December 2020 |
1.1 | 3852482208634779141 | 22 January 2021 | 1.12 | 6404500526474240765 | 5 February 2021 |
1.2 | 912635795014659773 | 29 March 2021 | 1.23 | 5662812629377532750 | 17 June 2021 |
1.3 | 4226367547239603660 | 18 August 2021 | 1.31 | 3225568184502668130 | 14 September 2021 |
1.5 | 4596392258055187917 | 15 February 2022 | 1.52 | 5869743417560122937 | 5 April 2022 |
1.6 | 9024153387735370035 | 6 September 2022 | 1.63 | 6594873489665865240 | 23 June 2023 |
2.0 | 1799993379545736809 | 21 September 2023 | 2.02 | 4882158097132343077 | 26 October 2023 |
2.1 | 2238892413801664242 | 5 December 2023 | 2.13 | 4350623720177810551 | 12 September 2024 |
2.2 | 6971552143247463690 | 10 December 2024 | 2.21 | 8420445566849588826 | 23 January 2025 |
Patch 1.31 / CET 1.16.4
The last update with Static Resolution Scaling (SRS), which allowed overriding up to 200% and beyond. At double resolution, the rendered image appeared noticeably smoother – ideal for high-quality screenshots, as anti-aliasing had more pixels to work with.
Patch 1.61 (DLSS) / CET 1.22.0
Introduced DLSS 3 support in Cyberpunk.
CET 1.16.4
Upgraded ImGui to 1.84.2, resolving issues with BeginDisabled() and EndDisabled()
CET 1.18.0
First version to support symbolic links in the filesystem. Also added the Lua function ModArchiveExists("example.archive")
CET 1.20.0
Transitioned to OpenResty LuaJIT
CET 1.21.0
Upgraded to ImGui 1.88 (with docking support) and adopted Noto Sans as the default font. Introduced scalable UI. The window arrow was removed in this version but brought back in the next.
CET 1.31.3
Disabled console logging for changes to game options.
ImGui styling is a kind of art in itself — but it’s not all that difficult. If you want a unique UI, you’ll need to work with color definitions, padding, and line flow. ImGui renders elements line by line, combining the padding from both the element and its window, so there are certain layout limitations — it’s not directly comparable to HTML + CSS.
My approach is a little complex, but it results in precise positioning across all resolutions. CET’s built-in scaling works surprisingly well and can be a big help if you don’t want to go full manual.
The reference documentation can be found in the CET GitHub repo. It lists all commands, though not very extensively.
Examples below are taken from CharLi, my playground for advanced ImGui styling.
There are four main commands for styling and coloring elements. These are stack-based, so make sure to pop what you push — otherwise you’ll override other mods or even CET’s own windows. Failure to do so can lead to glitches or crashes.
Color definitions:
ImGui.PushStyleColor(<PART OF ELEMENT>, <COLOR>)
ImGui.PopStyleColor(<NUMBER OF COLOR DEFINITIONS>)
Style variables (like padding or alignment):
ImGui.PushStyleVar(<PART OF ELEMENT>, <VALUE>, <OPTIONAL>)
ImGui.PopStyleVar(<NUMBER OF STYLE DEFINITIONS>)
Each element has its own styleable parts — too many to list here. To explore them all, check the ImGui source. Color enums begin here.
For example, FramePadding affects windows, tab bars, tabs, collapsing headers, and more. Here's a basic window using padding and custom flags:
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, 5, 7)
ImGui.Begin("Window Title", ImGuiWindowFlags.NoTitleBar + ImGuiWindowFlags.NoScrollbar + ImGuiWindowFlags.NoScrollWithMouse)
ImGui.End()
ImGui.PopStyleVar(1)
Styling colors works just like style vars. I like using 'ImGui.GetColorU32(r, g, b, a)' for RGBA colors. Values range from 0.0 to 1.0 for each channel.
The alpha channel controls transparency: 0.0 = fully transparent, 1.0 = fully opaque.
If your button is transparent and your window has its own background color, the button will inherit that look visually.
ImGui.PushStyleColor(ImGuiCol.WindowBg, ImGui.GetColorU32(1, 0, 0, 0.5))
ImGui.Begin("Name of the Window", ImGuiWindowFlags.NoTitleBar + ImGuiWindowFlags.NoScrollbar + ImGuiWindowFlags.NoScrollWithMouse)
ImGui.End()
ImGui.PopStyleColor(1)
Now you’ve got a semi-transparent red window — no title bar, just vibes 😎
CET comes with a built-in library of glyphs (icon characters). They can be used anywhere text is accepted: button labels, window titles, tooltips, etc.
Browse icons at Material Design Icons, but note: the names are slightly different in CET’s file \cyber_engine_tweaks\scripts\IconGlyphs\icons.lua. For example: clock-outline on the MDI site = ClockOutline in CET.
We’ll give our red ghost window an absurd glyph-based title:
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, 5, 7)
ImGui.PushStyleColor(ImGuiCol.WindowBg, ImGui.GetColorU32(1, 0, 0, 0.5))
ImGui.Begin(IconGlyphs.ClockOutline..IconGlyphs.AlphaS..IconGlyphs.AlphaA..IconGlyphs.AlphaL..IconGlyphs.AlphaA..IconGlyphs.AlphaD)
ImGui.End()
ImGui.PopStyleColor(1)
ImGui.PopStyleVar(1)
This is a sneaky trick that works surprisingly well. ImGui allows negative spacers using ImGui.Dummy(), which acts as offset padding.
If you know the dimensions of an element, you can "rewind" the layout and overlap another — useful for custom toggles or visual gimmicks.
For instance, in CharLi I use a fake slider overlaying a transparent button — so the button handles input, while the slider delivers the style.
Step 1: Transparent Button
ImGui.PushStyleColor(ImGuiCol.Button, ImGui.GetColorU32(0, 0, 0, 0))
ImGui.PushStyleColor(ImGuiCol.ButtonActive, ImGui.GetColorU32(0, 0, 0, 0))
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, ImGui.GetColorU32(0, 0, 0, 0))
ImGui.PushID("Unique_Button_Name")
ImGui.Button("", 32, 18)
ImGui.PopID()
ImGui.PopStyleColor(3)
Step 2: Rewind
ImGui.SameLine()
ImGui.Dummy(-32, 0)
ImGui.SameLine()
Step 3: Styled Slider Overlay
ImGui.PushStyleVar(ImGuiStyleVar.GrabRounding, 10)
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 10)
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, 0, 0)
ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 2)
ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetColorU32(0, 0, 0, 0))
ImGui.PushStyleColor(ImGuiCol.Border, ImGui.GetColorU32(0.3, 0.3, 0.37, 0.7))
ImGui.PushStyleColor(ImGuiCol.FrameBg, ImGui.GetColorU32(0.2, 0.2, 0.27, 0.5))
ImGui.PushStyleColor(ImGuiCol.FrameBgActive, ImGui.GetColorU32(0.2, 0.2, 0.27, 0.5))
ImGui.PushStyleColor(ImGuiCol.FrameBgHovered, ImGui.GetColorU32(0.2, 0.2, 0.27, 0.5))
ImGui.PushStyleColor(ImGuiCol.SliderGrab, ImGui.GetColorU32(1, 0.56, 0.13, 0.85))
ImGui.PushStyleColor(ImGuiCol.SliderGrabActive, ImGui.GetColorU32(1, 0.56, 0.13, 0.7))
ImGui.SetNextItemWidth(32)
ImGui.PushID("Unique_Slider_Name")
ImGui.SliderInt("", value, 1, 0)
ImGui.PopID()
ImGui.PopStyleColor(7)
ImGui.PopStyleVar(4)
ImGui.SameLine()
ImGui.Text("The Real Title")
IMPORTEND: If a widget has no label (""), use PushID / PopID — otherwise, identical elements will share state and behave unpredictably.
And voilà — you’ve got a fully-styled, interactable FrankenWidget with decoupled input and visuals. Like dark UI necromancy in Lua 😄
This technique only works when you know the exact width of the elements and apply it on the same horizontal line. Vertical overlap between different rows isn’t possible — ImGui’s layout is strictly line-based.
to be continued...
Unfortunately, there's no method in the GameOptions family to check whether an option actually exists. Even worse: the return values of Get* methods can be misleading. If GameOptions.GetBool() returns false, there's no way to tell whether that's the actual setting — or just the fallback response for a non-existent option.
So I wrote a small workaround that helps with auto-detection. Note: It doesn’t prevent the “not found” message from appearing in the CET console.
function GameOptions.Has(group, option)
if GameOptions.Get(group, option) ~= "" then
return true
end
return false
end
Some bugs only show up once you launch the game — not while writing your CET mod. You might wonder why the game won’t start and spend half a day hunting for the cause. So I’m documenting these to help others avoid the same traps.
Defining a local variable with a leading underscore in global space can prevent Cyberpunk (and CET) from launching altogether.
local _TRANSPARENCY = ImGui.GetColorU32(0, 0, 0, 0)
Back when the web was still young, every corner buzzed with creativity and playful experimentation. Surfing the net was exciting — full of surprises and character. But as success poured in, the web grew dull. Webshops now look eerily alike, blogs became visually and technically interchangeable, and the major platforms began to define what the internet should look like. It’s not that everything brought by the years was bad, but the web feels increasingly generic — especially when browsing without accepting cookies or with JavaScript turned off. Suddenly, many sites fall apart or reveal how little attention was paid to detail.
So I decided to explore how far one can go with a rather classical structure, fused with modern techniques. The result: RootPunk.com, a multithreaded, microcached, single-request one-page monster. It uses entirely optional JavaScript and runs cookie-free. It’s effectively a counter-proposal to the bloated complexity of many modern websites.
The goal was to cover all essentials with a single request — background, logo, font, icons. Even the favicon is injected via base64, and the music player’s default track loads inline. Only gallery images are deferred, pulled in via the browser’s native lazy load once a section is opened. In short, visitors receive a complete package — self-contained, efficient.
The entire payload of that single request clocks in at under 130KB — about the size of a single background image on other sites.
If you ask the internet, base64 injection is “bad practice” — but it forgets that gzip compression easily offsets any added bulk. You save on extra requests, and when your entire site is effectively a single page, the browser doesn’t need to cache fragmented resources. It just needs one shot.
The foundation is a lean Linux system powered by a custom-built Nginx and a tuned PHP FastCGI backend. Optimized builds of Zlib, OpenSSL, and MozJPEG support the infrastructure. Thanks to its lightweight worker architecture, Nginx is the perfect base to execute multithreading via asynchronous SSI. With enough workers and processor cores, requests are handled extremely quickly and bottlenecks are avoided. The asynchronous nature requires some attention, especially when passing data to subrequests. While Base64 proved unsuitable for SSI requests, Base58 turned out to be a viable alternative — available via Pecl as a PHP extension.
Additionally, Nginx is instructed to process SSI requests inside CSS and JavaScript files, adding even more flexibility:
ssi on;
ssi_types text/css text/javascript;
Since Nginx reacts to any SSI instruction, it also processes dynamically generated requests from the PHP backend. This enables near-unlimited branching — provided the system has enough workers ready. Everything runs asynchronously, creating an environment that scales beautifully across multicore machines.
To bypass browser-imposed connection limits, Nginx serves static, cached images from three shard domains. This works for both HTTP and HTTPS and is configured directly in the Nginx setup — no need to wait for headers in the HTML:
add_header Link '<//0x1.rootpunk.com/>; rel=dns-prefetch';
add_header Link '<//0x2.rootpunk.com/>; rel=dns-prefetch';
add_header Link '<//0x3.rootpunk.com/>; rel=dns-prefetch';
There are also dedicated location blocks in Nginx to enable direct access to thematic categories:
location ~* ^/(a|ai)/?$ {
try_files $uri /<boot here>?a;
}
location ~* ^/(m|mod)/?$ {
try_files $uri /<boot here>?m;
}
location ~* ^/(t|tech)/?$ {
try_files $uri /<boot here>?t;
}
The filesystem cache from Linux and the PHP OpCache already do a solid job — but why stop when there’s room for more? Almost all backend helpers are equipped with APCu caching, accelerating compute-heavy tasks and filesystem operations. The APCu PHP extension is available via Pecl. Whether it's the Markdown parser, the Base64 encoder, or the CSS/JS minifier — every part is cached.
The core idea is to use APCu TTL-based keys, allowing automatic cleanup without the need for extra logic. For larger projects, it's wise to keep an eye on memory consumption or lower the TTL. A typical key looks like this:
$apkey = 'B64:' . filemtime($<path to resource>) . ':' . $<path to resource>;
So if a CSS file changes, it's granularly re-cached without affecting the rest of the cache. The TTL here defaults to 7 days, after which keys expire on their own. The same functionality could be implemented with Redis — but APCu tends to be a bit faster thanks to its direct PHP integration. Beyond that, specialized helpers generate CSS (e.g. for the image gallery) and the corresponding HTML. Thanks to the SSI architecture, this works in parallel and scales elegantly.
Essential classes like Shared, Parsedown, and a custom parser are preloaded in PHP for immediate availability — paired with a fallback mechanism, just in case. A simple ping is all it takes:
public static function Ping()
{
return true;
}
Each helper pings Shared first and loads it only if not yet present:
if (!Shared::Ping()) {
require_once('<path to shared>');
}
PLAY-JS → Generates the JavaScript playlist
PLAY-LIST → Builds the HTML playlist for the music player
VIEW-CSS → Creates CSS IDs to expand gallery views
VIEW-LIST → Builds gallery scaffolding from its config
VIEW-SCAN → Generates HTML for images & overlays, distributing content randomly across shards
VIEW-LIST is unique in that it fires further SSI requests to VIEW-SCAN, letting images load asynchronously and in parallel.
The main and subnavigation are controlled via hidden radio buttons whose state determines visual flow. Thanks to dedicated Nginx location blocks, the appropriate radio is preselected using a checked attribute right from the start. The music player is activated via a checkbox, as is the expansion of gallery views.
Certain HTML elements like <s>, <i>, <b>, and <u> have been repurposed to conserve markup where possible. Overall, excessive layering has been avoided — the structure relies on minimal HTML to keep the request payload as lean as possible.
The typeface used is a stripped-down font where only the necessary icons were inserted manually. With a footprint of just 18.5 KB, it’s highly efficient and is embedded directly, making it instantly available — no font swaps required. The largest asset is the background image: an AVIF file at 50% quality, clocking in at 29.5 KB. The small and large logos were compressed using an SVG optimizer and weigh in at just 1.7 KB and 2.5 KB, respectively — almost negligible.