EMI INDO's Forum Posts

  • construct.net/en/make-games/addons/1452/document-file

    1. New Downlaod file with URL.c3p
    2. Fix response action: Download file from url
    Subscribe to Construct videos now
  • construct.net/en/make-games/addons/1453/webrct/versions

    1. Update c3p
    2. New action Apply Filter
    3. New action Apply Effect
    4. Fix android save Video Recording to SAF

    Screenshot

    Subscribe to Construct videos now
  • Supports c3 build service = YES

    Support platform

    1. Android = YES
    2. IOS = NO
    3. Browser = YES only webrct

    Download addon Document file

    Download addon WebRTC

    Description plugin Document file

    Action

    If Android denies permission to access non-public file paths, such as files inside the WhatsApp folder, use the action "Copy file to cache."

    Note: The action "Show overlay all audio list" may also be denied by Android if the clicked item comes from a non-public folder, even if permission has already been granted. In this case, also use the "Copy file to cache" action.

    Important: The "Copy file to cache" action may consume significant device storage. If necessary, use the "Clear app old cache" action to free up space.

    Condition

    every action list is triggered if an error occurs, the condition is in the ON ERROR (COMBO) category.

    then get the response from the Expression name Error message.

    Expression

    Upload file to server

    Save this code as an upload.php file on your server. This script already handles CORS, validation, security, and generates the reply URL dynamically.

    • This works immediately without changing anything
    • or modify upload.php to your liking.
    <?php
    // ======================================================================
    // 1. CORS HANDLING (MUST BE AT THE VERY TOP)
    // ======================================================================
    $origin = $_SERVER['HTTP_ORIGIN'] ?? '*';
    header('Access-Control-Allow-Origin: ' . $origin);
    header('Access-Control-Allow-Methods: POST, OPTIONS');
    header('Access-Control-Allow-Headers: Content-Type');
    header('Access-Control-Allow-Credentials: true');
    
    // Respond to preflight requests
    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
     http_response_code(204);
     exit();
    }
    
    // ======================================================================
    // 2. HELPER FUNCTIONS
    // ======================================================================
    
    /**
     * Send a JSON response and terminate execution.
     * @param bool $success Operation status.
     * @param array $data Data to include in the response.
     * @param int $httpCode HTTP status code.
     */
    function sendJsonResponse(bool $success, array $data, int $httpCode = 200) {
     http_response_code($httpCode);
     header('Content-Type: application/json');
     echo json_encode(['success' => $success] + $data);
     exit();
    }
    
    /**
     * Translate PHP file upload error codes into human-readable messages.
     * @param int $errorCode The error code from $_FILES['file']['error'].
     * @return string Error message.
     */
    function handleUploadError(int $errorCode): string {
     switch ($errorCode) {
     case UPLOAD_ERR_INI_SIZE:
     case UPLOAD_ERR_FORM_SIZE:
     return 'File size exceeds the limit.';
     case UPLOAD_ERR_PARTIAL:
     return 'The file was only partially uploaded.';
     case UPLOAD_ERR_NO_FILE:
     return 'No file was uploaded.';
     case UPLOAD_ERR_NO_TMP_DIR:
     return 'Missing a temporary folder on the server.';
     case UPLOAD_ERR_CANT_WRITE:
     return 'Failed to write file to disk.';
     case UPLOAD_ERR_EXTENSION:
     return 'A PHP extension stopped the file upload.';
     default:
     return 'An unknown upload error occurred.';
     }
    }
    
    // ======================================================================
    // 3. MAIN LOGIC WITH CENTRALIZED ERROR HANDLING
    // ======================================================================
    
    try {
     // Ensure this is a POST request
     if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
     throw new Exception('Only POST method is allowed.', 405);
     }
    
     // Validate required parameters
     if (empty($_POST['userId']) || empty($_POST['extension']) || !isset($_FILES['file'])) {
     throw new Exception('Missing required parameters (userId, extension, or file).', 400);
     }
    
     // Check for upload errors
     if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
     throw new Exception(handleUploadError($_FILES['file']['error']), 500);
     }
    
     // Sanitize userId and extension from the client
     $userId = preg_replace('/[^a-zA-Z0-9_-]/', '', $_POST['userId']);
    
     // Create user directory if it doesn't exist
     $mainUploadDir = 'uploads/';
     $userUploadDir = $mainUploadDir . $userId . '/';
     if (!is_dir($userUploadDir) && !mkdir($userUploadDir, 0755, true)) {
     throw new Exception('Failed to create user directory. Check server permissions.', 500);
     }
    
     // Obtain file information from the server side (more reliable)
     $originalFileName = basename($_FILES['file']['name']);
    
     // Further sanitize the file name for security
     $safeFileName = preg_replace('/[^a-zA-Z0-9-_.]/', '', $originalFileName);
     $serverExtension = strtolower(pathinfo($safeFileName, PATHINFO_EXTENSION));
    
     // ## CLIENT-SIDE EXTENSION VALIDATION REMOVED AS PER REQUEST ##
    
     // File type validation (whitelist) remains for security
     $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'mp3', 'mp4', 'm4a', 'aac', 'webm'];
     if (!in_array($serverExtension, $allowedExtensions)) {
     throw new Exception('File type "' . $serverExtension . '" is not allowed.', 415);
     }
    
     // Validate file size
     $maxFileSize = 20 * 1024 * 1024; // 20 MB
     if ($_FILES['file']['size'] > $maxFileSize) {
     throw new Exception('File size exceeds the 20MB limit.', 413);
     }
    
     // Generate a unique new file name
     $newFileName = time() . '_' . $safeFileName;
     $targetFilePath = $userUploadDir . $newFileName;
    
     // Move the file to permanent location
     if (!move_uploaded_file($_FILES['file']['tmp_name'], $targetFilePath)) {
     throw new Exception('Failed to save the uploaded file on the server.', 500);
     }
    
     // If successful, send a success response in JSON format
     $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
     $host = $_SERVER['HTTP_HOST'];
     $finalFileUrl = $protocol . '://' . $host . '/' . $targetFilePath;
    
     sendJsonResponse(true, [
     'message' => 'File uploaded successfully.',
     'url' => $finalFileUrl,
     'fileName' => $newFileName
     ]);
    
    } catch (Exception $e) {
     // Handle all errors in one place and send a JSON response
     sendJsonResponse(false, ['error' => $e->getMessage()], $e->getCode());
    }
    
    
    
    

    Old documentation

    Simple example c3p: Video Recording and Take Capture image

    select file, upload, download, will return a response

    Response (Success):

    Object: A JSON object containing metadata of the successfully.

    • uri (String): The content URI (content://) of the saved file.
    • traditionalPath (String, optional): The traditional file system path (e.g., /storage/emulated/0/Music/test.mp3)
    • name (String): The display name of the file.
    • size (Number): The size of the file in bytes.
    • sizeReadable (String): The file size in a human-readable format. (e.g., "1.23 MB").
    • mimeType (String): The MIME type of the file.
    • lastModified (Number): The last modified timestamp of the file in milliseconds.
    • isDirectory (Boolean): true if it's a directory, false if it's a file.
    • base64: Base64-encoded string from the contents of a stored or selected file, or from an upload/download response.

    (provided that permission has been granted)

    You can handle, manipulate, image, audio, video, pdf, and other files, whatever the user chooses on their device, to be rendered into HTML elements, or files processed by your own hosting server, or an AI server.

    The response from the server can be directly downloaded to the user's device, or just rendered to the HTML element.

    The upload, download or response file select will return metadata for use as needed, (provided permission has been granted),

    If the user denies permission, the metadata will not be obtained, the process on the android device will fail.

    all processes on the user's device must go through the SAF URI path, not the traditional file system path traditional file system path (e.g., /storage/emulated/0/Download/filename.pdf).

    For certain categories of apps only

    developer.android.com/training/data-storage/manage-all-files

    1. File managers
    2. Backup and restore apps
    3. Anti-virus apps
    4. Document management apps
    5. On-device file search
    6. Disk and file encryption
    7. Device-to-device data migration

    Screenshot of example permissions (Video Recording and Take Capture image)

    NOTE sensitive permissions

    Subscribe to Construct videos now
  • Constant777

    You can use this addon construct.net/en/make-games/addons/1452/document-file

    I will make an example later,

    after the review is completed/approved.

    github.com/Scirra/Construct-bugs/issues/8649

  • Plugin Meta Audience Network.

    construct.net/en/game-assets/addons/meta-audience-network-383

    1. create new c3addon 1/May/2025
    2. The old c3addon is no longer in use.
    3. c3addon is recreated with typescript.
    Subscribe to Construct videos now
  • Build Failed - Applovin Plugin Problem c3 build service

    github.com/EMI-INDO/c3addon-bug/issues/13

    1. Update c3addon v1.0.5.9 to v1.0.6.9
    2. FIX error c3 build service

    construct.net/en/game-assets/addons/applovin-max-ads-1847

  • Sorry, I meant when exporting there is an option whether Edge-to-Edge wants to be enabled or not.

    It's okay if I enable it with the local build only.

  • Ashley

    <preference name="AndroidEdgeToEdge" value="true" />

    github.com/apache/cordova-android/issues/1794

    Setting true will affect the position of mobile ads,

    I prefer AndroidEdgeToEdge = false

    Tagged:

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • with CORDOVA CLI

    cordova plugin rm plugin-id

    example

    cordova plugin rm cordova-plugin-inappbrowser

    path

    ProjectPath/

    plugins/

    plugin-id-1

    plugin-id-2

    plugin-id-3

  • darren-chung

    In a future release it would be best to change rex_csvToArray.js using the CSVToArray export function module

    then in the base/plugin.js no longer use AddFileDependency replace it with

    this._info.AddC3RuntimeScript("c3runtime/rex_csvToArray.js");
    

    then in actions/canditions just

     import { CSVToArray } from "./rex_csvToArray.js";
    
  • darren-chung

    I don't have much time for testing, so I updated without testing, sorry.

    github.com/EMIINDO/csv-array-c3-runtime-1.0.0.1-stable-c3addon/commit/58a160e0b7194b9f07bf66bb91e67c50da0d1ee6

  • Tom

    If the video is too wide the right sidebar will be cut off

    Are you going to add overflow, or should I change the video

    construct.net/en/game-assets/addons/firebase-web-gt-mobile-gt-1881

  • brobiggame

    Sorry, I haven't finished the tool yet, to port the addon type behavior.

  • Do you really want it, I can prepare it, in the next few days, for free.

    I also have my own server side validation, to handle subscriptions, cloud vps based, but this is for my own project.

    My server validation with GCP

    developers.google.com/android-publisher/api-ref/rest/v3/purchases.subscriptions/get