EMI INDO's Forum Posts

  • Haha, thanks Ashley! I know, on paper, "Remote Code Execution as a Service" sounds like a sysadmin's nightmare. 😂

    That is exactly why we spent months designing the security architecture before opening this public door. Just to clarify how we handle the "Oof" factor:

    1. Total Isolation (Air-gapped Logic)

    The Public Runners (GitHub Actions) are physically separated from our Private/Production infrastructure. Even if the public instance melts down or gets compromised, it touches nothing else.

    2. "Blind" Orchestrator & Clean Images

    Our security relies on a rigorous chain of custody:

    • Clean Images: The Docker images used are public (GHCR) and contain zero baked-in secrets.
    • No Local Secrets: Our backend (`server.js`) is stateless and has no `.env` file with credentials.
    • Just-in-Time Access: If a key is ever needed, the container asks the Server -> the Server asks a Cloudflare Worker -> the Worker retrieves it from the encrypted Secrets Store.

    Credentials exist in memory only for the millisecond they are needed, never stored on disk.

    3. Abuse Prevention

    We have strict hard-limits on CPU usage, execution time (to prevent crypto-mining), and network egress. We actually just successfully mitigated a DDoS attempt on December 11th with zero downtime, so the Cloudflare WAF or super bot fight mode is doing its job well so far.

    It is a calculated risk, but I really wanted to give Addon Devs a way to test "Cloud Compatibility" for their plugins without bothering your team with whitelist requests for broken plugins, or just for testing.

    Fingers crossed! 🤞

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Hi everyone,

    I want to share a free, open-source utility I built to assist Construct 3 addon developers and those dealing with complex Cordova dependencies.

    As we know, the official Construct 3 Build Service is excellent, but for security reasons, it maintains a strict whitelist for Cordova plugins. If you are developing a new C3 Addon that uses a custom Cordova plugin, you typically need to:

    • Test locally (which might work fine because you have full control).
    • Submit a whitelist request to Scirra on GitHub.
    • Wait for review.

    The Problem

    Sometimes, a plugin works locally but fails in a strict Cloud Build environment (due to hook permission scripts, specific Gradle versions, Kotlin incompatibilities, or AndroidX conflicts). If this happens after you requested a whitelist, it wastes the Scirra team's time reviewing a broken plugin.

    The Solution: Public Builder (Beta)

    I created a Public, No-Sign-In build runner that has NO Whitelist restrictions.

    👉 public.swaplab.net

    How it helps the C3 Community:

    You can use this to "Stress Test" your C3 exported project with your custom plugins in a real Cloud Environment (GitHub Actions Standard Runner).

    1. Export C3 project as "Cordova Project".
    2. Zip and upload to the Public Builder.
    3. If it builds successfully here, it is highly likely to be "Cloud Safe".
    4. Then, you can confidently submit your whitelist request to Scirra's Bug Tracker.

    📺 Video Proof: Construct 3 AdMob via Capacitor Engine

    Does it actually work? Here is a step-by-step video demonstrating a Construct 3 project (with AdMob Cordova plugin) being built using the Modern Capacitor Engine on the public builder:

    Subscribe to Construct videos now

    Video Highlights:

    • Export: Construct 3 → Cordova Project.
    • Engine: capacitor-build-android-engine (Yes, it automatically wraps your C3/Cordova project!).
    • Result: Running AdMob Ads on the Emulator.

    💰 Sell Addons with Confidence

    For those creating paid addons for the Construct 3 Asset Store, this tool is your safety net.

    By verifying that your addon compiles correctly in a standard Linux/Docker cloud environment (similar to the official service) before listing it, you ensure your customers receive a robust product. This minimizes support tickets regarding "Build Errors" and helps you maintain a high reputation as a seller.

    🔧 Tech Stack & Transparency

    This runs on 100% Open Source infrastructure. You can watch the build queue and verify the workflows here:

    • Public Runner: swaplab-engine/public-build-swaplab-engine
    • Limits: Unlimited usage, strictly generates Debug APKs for testing.
    • Privacy: Zero-retention. Files are deleted immediately after the build job triggers.

    ⚡ Supported Engines for C3

    When you upload your

    Cordova Project

    zip, you can choose 3 different Docker Images to build your game:

    1. cordova-build-android-engine

    • The classic standard. Best for verifying plugin compatibility for the official build service.
    • View Docker Image Source

    2. capacitor-build-android-engine (Recommended)

    • Wraps your C3 project in the CapacitorJS runtime.
    • Great for modernizing your game stack or using Capacitor-specific plugins later.
    • View Docker Image Source

    3. framework7-build-android-engine

    All Docker Packages: github.com/orgs/swaplab-engine/packages

    I hope this tool helps addon developers iterate faster and reduces the "Build Error" headache when dealing with native dependencies.

    Let me know if you run into any issues with specific C3 exports!

    🔒 Privacy & Terms

    We understand that your source code is your valuable asset. This service operates under a strict policy to ensure your intellectual property remains yours.

  • Yes, I just returned to the forum after being away for four months. Sorry for the late reply.

    I will check it later. I need to review all the addons in the store first to make sure everything is still working normally.

    First, I have to be responsible for what I’m selling.

    Once I’m done, I’ll try the addon you want to port.

  • What should I fix? You didn't provide the error log details.

    open Developer Tools.

  • Note

    construct.net/en/forum/construct-3/plugin-sdk-10/porting-list-addon-sdk-v2-185208

  • > brobiggame

    >

    > The automatic tool will not work for the add-on you requested, as this add-on has API SDK v1 logic that must be rewritten to API SDK v2.

    Does this mean that this behavior will remain in the past ?😥

    Although I can make improvements to the logic of the new API, I don't have the time right now, so it would be better to contact the developer.

  • brobiggame

    The automatic tool will not work for the add-on you requested, as this add-on has API SDK v1 logic that must be rewritten to API SDK v2.

  • Challenges and a Suggestion for the Future

    As a suggestion for an initial step, it would be very helpful if a future version of Construct 3 added a direct export option for Capacitor. At a minimum, this option could generate a basic capacitor.config.json file. For the rest of the process, such as building the app, users could then proceed locally/manually.

  • I'd like to share a bit of my experience: I have been building projects with Construct 3 using Capacitor and Framework7 since 2023. If anyone is interested in experimenting with this setup, I plan to create some brief documentation.

    Compatibility and Configuration

    All Cordova plugins are fully functional in Capacitor. However, it's important to note that Capacitor does not support variables in plugin.xml. You can manipulate these Cordova variables using hooks/scripts instead.

    Capacitor also replaces config.xml with capacitor.config.json. My workflow is as follows:

    1. Export the project from Construct 3 as a Cordova project.
    2. Use a hook/script to port the package.json configuration into capacitor.config.json.

    For example, the script would take this data from your Cordova project and automatically generate a capacitor.config.json file like the one below:

    {
     "appId": "io.cordova.hellocordova",
     "appName": "HelloCordova",
     "webDir": "www",
     "bundledWebRuntime": false,
     "plugins": {
     "AdMob": {
     "APP_ID_ANDROID": "ca-app-pub-3940256099942544~3347511713",
     "APP_ID_IOS": "ca-app-pub-3940256099942544~1458002511"
     }
     }
    }
    

    Additionally, the icon URI paths are different in Cordova and Capacitor, so the hook/script must be designed to handle this automatically.

    Differences in Plugin Installation

    The way you install plugins is also different. For example:

    • Cordova: cordova plugin add plugin-name
    • Capacitor: npm i plugin-name

    For simple projects, the difference between Cordova and Capacitor might not be very noticeable. However, on larger projects, the difference is quite significant. I personally prefer Capacitor because its code is based on JavaScript modules, which feels more modern and structured. It took me over a month to get used to the Capacitor workflow, but the results have been well worth it.

  • Update AdMob-Plus-PRO_v3.0.2.9

    construct.net/en/game-assets/addons/admob-next-gen-768

  • Update AdMob-Plus-PRO_v3.0.1.9

    Update idfa-aaid_v1.0.0.2

    1. adding two new conditions
    2. Is native load
    3. Is banner ad load
    4. and several bug fixes

    construct.net/en/game-assets/addons/admob-next-gen-768

  • The Firebase Sparsha plugin at least has the Web client ID option in Google Cloud Console (OAuth 2.0 → Credentials).

    usually forms com.googleusercontent.apps.XXXXXXXXXX....

    if there is no OAuth 2.0 option like the Firebase Sparsha plugin only returns the token ID (idToken) for Firebase authentication, not the serverAuthCode that PlayFab needs for Google OAuth validation on the PlayFab server.

  • you must lock your project in SDK v1 with the end url rxxx

    example: editor.construct.net/r440-2

    If there are new features either from the SDK or from the editor above the rxxxx you locked, you cannot use them.

  • Yes, now you can test it, is it working?

    const testProperty = properties[0]; // index 0
    // group 
    const appName = properties[2]; // index 2
    const appVersion = properties[3]; // index 3
    const appDescription = properties[4]; // index 4
    const permissions = properties[5]; // index 5
    
    console.log("testProperty: ", testProperty);
    console.log("appName: ", appName);
    console.log("appVersion: ", appVersion);
    console.log("appDescription: ", appDescription);
    console.log("permissions: ", permissions);
    
  • {
    
    
    	"languageTag": "en-US",
    	"fileDescription": "Strings for DOMMessaging.",
    	"text": {
    		"plugins": {
    			"mycompany_dommessaging": {
    				"name": "MyDOMPlugin",
    				"description": "An example third-party plugin using DOM messaging.",
    				"help-url": "https://www.construct.net",
    				"properties": {
    					"test-property": {
    						"name": "Test property",
    						"desc": "A test number property."
    					},
    					"app-config": {
    						"name": "app-config",
    						"desc": "app-config."
    					},
    					"app-name": {
    						"name": "app-name",
    						"desc": "app-name."
    					},
    					"app-version": {
    						"name": "app-version",
    						"desc": "app-version"
    					},
    					"app-description": {
    						"name": "app-description",
    						"desc": "app-description"
    					},
    					"permissions": {
    						"name": "permissions",
    						"desc": "permissions"
    					},
    					"layout-config": {
    						"name": "layout-config",
    						"desc": "layout-config"
    					},
    					"popup-layout": {
    						"name": "popup-layout",
    						"desc": "popup-layout"
    					},
    					"options-layout": {
    						"name": "options-layout",
    						"desc": "options-layout"
    					},
    					"background-layout": {
    						"name": "background-layout",
    						"desc": "background-layout"
    					}
    				},
    				"aceCategories": {
    					"custom": "Custom"
    				},
    				"conditions": {
    				},
    				"actions": {
    					"set-document-title": {
    						"list-name": "Set document title",
    						"display-text": "Set document title to [b]{0}[/b]",
    						"description": "Set the document title to a string.",
    						"params": {
    							"title": {
    								"name": "Title",
    								"desc": "The document title to set."
    							}
    						}
    					}
    				},
    				"expressions": {
    					"get-document-title": {
    						"description": "Get the current document title.",
    						"translated-name": "DocumentTitle"
    					}
    				}
    			}
    		}
    	}
    }
    
    
    
    const SDK = globalThis.SDK;
    
    ////////////////////////////////////////////
    // The plugin ID is how Construct identifies different kinds of plugins.
    // *** NEVER CHANGE THE PLUGIN ID! ***
    // If you change the plugin ID after releasing the plugin, Construct will think it is an entirely different
    // plugin and assume it is incompatible with the old one, and YOU WILL BREAK ALL EXISTING PROJECTS USING THE PLUGIN.
    // Only the plugin name is displayed in the editor, so to rename your plugin change the name but NOT the ID.
    // If you want to completely replace a plugin, make it deprecated (it will be hidden but old projects keep working),
    // and create an entirely new plugin with a different plugin ID.
    const PLUGIN_ID = "MyCompany_DOMMessaging";
    ////////////////////////////////////////////
    
    const PLUGIN_CATEGORY = "general";
    
    const PLUGIN_CLASS = SDK.Plugins.MyCompany_DOMMessaging = class MyCustomPlugin extends SDK.IPluginBase
    {
    	constructor()
    	{
    		super(PLUGIN_ID);
    		
    		SDK.Lang.PushContext("plugins." + PLUGIN_ID.toLowerCase());
    		
    		this._info.SetName(globalThis.lang(".name"));
    		this._info.SetDescription(globalThis.lang(".description"));
    		this._info.SetCategory(PLUGIN_CATEGORY);
    		this._info.SetAuthor("Scirra");
    		this._info.SetHelpUrl(globalThis.lang(".help-url"));
    		this._info.SetIsSingleGlobal(true);
    		this._info.SetRuntimeModuleMainScript("c3runtime/main.js");
    
    		// Set the domSide.js script to run in the context of the DOM
    		this._info.SetDOMSideScripts(["c3runtime/domSide.js"]);
    		
    		SDK.Lang.PushContext(".properties");
    		
    		this._info.SetProperties([
    			new SDK.PluginProperty("integer", "test-property", 0),
    			new SDK.PluginProperty("group", "app-config"), // [???] - Should show "Application Configuration"
     new SDK.PluginProperty("text", "app-name", "My Application"), // [???] - Should show "Application Name"
     new SDK.PluginProperty("text", "app-version", "1.0.0"), // [???] - Should show "Application Version"
     new SDK.PluginProperty("longtext", "app-description", "A web application built with Construct 3"), // [???]
     new SDK.PluginProperty("text", "permissions", "storage,activeTab"), // [???] - Should show "Permissions"
    
     new SDK.PluginProperty("group", "layout-config"), // [???] - Should show "Layout Configuration"
     new SDK.PluginProperty("text", "popup-layout", ""), // [???] - Should show "Popup Layout"
     new SDK.PluginProperty("text", "options-layout", ""), // [???] - Should show "Options Layout"
     new SDK.PluginProperty("text", "background-layout", "") // [???] - Should show "Background Layout"
    		]);
    		
    		SDK.Lang.PopContext();		// .properties
    		
    		SDK.Lang.PopContext();
    	}
    };
    
    PLUGIN_CLASS.Register(PLUGIN_ID, PLUGIN_CLASS);