If you're worried about code privacy, I'm pretty sure IL doesn't do much, at least with C#. C#'s IL was designed to be easy to de-compile back to C#, and this may be the same with GDScript. For code privacy, you can encrypt the PCK file when exporting with (I think) AES-256-CBC but I may be wrong on the algorithm. You just need to generate 256 random bits, preferably securely with a cryptographically secure random number generator. If you have access to DevTools in your browser, this snippet of JS can make a secure AES-256 key. Simply open a new tab page, open DevTools (usually ctrl+shift+i) Click on console, and copy-paste this in:
(async function (crypto) {
const key = await crypto.subtle.generateKey(
{
name: "AES-CBC",
length: 256,
},
true,
["encrypt", "decrypt"],
);
const exported = await crypto.subtle.exportKey("raw", key);
const exportedHex = Array.from(new Uint8Array(exported))
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
console.log(`AES key generated using ${key.algorithm.name}`);
console.log(exportedHex);
navigator.clipboard
.writeText(exportedHex)
.then(() => console.log("Key written to clipboard."))
.catch(async () => {
console.log("Couldn't write to clipboard.");
if (
"canShare" in navigator &&
!navigator.canShare({
text: exportedHex,
})
) {
// Usually this has a copy button.
await navigator.share({
text: exportedHex,
});
return;
}
console.log("Web Share API unavailable. You must copy it yourself.");
});
})(
window.crypto ||
(() => {
if (!window.isSecureContext)
throw new Error("Not secure context. Cannot use Web Crypto API.");
throw new Error("Your browser doesn't support the Web Crypto API");
})(),
);
Technically, Godot only requires 256 random bits. While the above method does generate a real AES key, it is just 256 random bits. So here's a potentially easier method and probably faster:
(function (crypto) {
const key = Array.from(crypto.getRandomValues(new Uint8Array(32)))
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
console.log("256 random bits generated:", key);
navigator.clipboard
.writeText(key)
.then(() => console.log("Successfully copied key to clipboard."))
.catch(async () => {
console.error("Failed to copy key.");
if ("canShare" in navigator && navigator.canShare({ text: key }))
await navigator.share({ text: key });
else console.log("Web Share API unavailable. You must copy it yourself.");
});
})(
window.crypto || /* this is premature optimization, yes. It is the root of all evil. Doing it this way on V8 (chromium/node), it is ever so slightly faster than checking in-function. */
(() => {
if (!window.isSecureContext)
throw new Error("Not secure context. Cannot use Web Crypto API.");
throw new Error("Your browser doesn't support the Web Crypto API.");
}),
);
While encrypting the PCK will have the key within the binary itself (so the engine can decrypt it) being in a compiled binary means that it isn't in a simple text file and requires at least some analysis on the binary to find the key. I don't know but maybe Godot obfuscates the key somehow, but you'd have to do your own research on how this actually works. By encrypting the PCK, you protect your game assets from being easily extracted, including your GDScript source code.