EMI INDO's Forum Posts

  • 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

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • construct.net/en/game-assets/addons/firebase-web-gt-mobile-gt-1881

    ALL-Firebase WEB-MOBILE-DESKTOP

    1. New Auth action relatime database: Write data advanced
    2. New fiture Firestore leaderboard manual (6 action)
    3. New modules JSON5 npm i json5
    4. New Video

    New c3p

    1. Leaderboard manual.c3p
    2. Realtime database: JS textInput.c3p
    3. Realtime database: JSON5 vs JSON-str.c3p
    4. Update: Realtime database.c3p
  • cordova-plugin-save-blob (registerWebRTC)

    WebRTC Convert Blob to base64Data save/downlaod

    WebRTC: webrtc.github.io/samples

    WebRTC: full example github.com/webrtc/samples/tree/gh-pages/src/content

    cordova plugin add cordova-plugin-save-blob

    clobbers: cordova.plugin.CordovaSaveBlob

      1. registerWebRTC()
      2. checkAndRequestPermissions({ permissions: ["CAMERA"] })
      3. selectFiles({ mime: ""})
      4. selectTargetPath()
      5. downloadBlob({saveToPath: "", base64Data: "", fileName: "" })
      6. downloadFile({fileUrl: "", fileName: ""})

    selectFiles

    example > all mime: "" | image/* | video/* | audio/* | application/pdf | image/png | image/jpeg | video/mp4 | audio/mpeg
    

    checkAndRequestPermissions

    1. CAMERA
    2. RECORD_AUDIO
    3. MODIFY_AUDIO_SETTINGS
    4. READ_EXTERNAL_STORAGE
    5. WRITE_EXTERNAL_STORAGE
    6. MANAGE_EXTERNAL_STORAGE
    7. READ_MEDIA_AUDIO
    8. READ_MEDIA_VIDEO
    9. READ_MEDIA_IMAGES

    Support android min 6 to 15 or higher

    Depends on the user whether permission has been granted

    Shared storage location (selectTargetPath)

    set by the user to avoid errors on android 15 or higher.

    example

    1. video call plugin
    2. live streaming plugin
    3. screenshot plugin
    4. screen record plugin
    5. Video chat plugin
    6. Transfer a file plugin
    7. data transfer plugin
    8. send text messages
    9. and many more

    You can also make some combinations such as with scoket.io, arduino and the like.

    	
    
    <!DOCTYPE html>
    <html>
    <head>
     <meta charset="UTF-8">
     <title>Complete WebRTC Demo - Video Call</title>
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
     <style>
     body {
     font-family: Arial, sans-serif;
     margin: 10px;
     padding: 0;
     background-color: #f7f7f7;
     }
     h1 {
     text-align: center;
     color: #333;
     }
     .section {
     background-color: #fff;
     margin: 10px auto;
     padding: 15px;
     border: 1px solid #ccc;
     border-radius: 5px;
     max-width: 800px;
     }
     .section h2 {
     margin-top: 0;
     color: #444;
     }
     video {
     width: 100%;
     max-width: 320px;
     height: auto;
     background-color: #000;
     margin: 5px;
     border: 1px solid #333;
     }
     textarea, select, .output {
     width: 90%;
     max-width: 700px;
     display: block;
     margin: 10px auto;
     padding: 8px;
     font-size: 14px;
     }
     button {
     margin: 5px;
     padding: 8px 12px;
     font-size: 14px;
     cursor: pointer;
     border: none;
     background-color: #4285F4;
     color: #fff;
     border-radius: 3px;
     }
     button:hover {
     background-color: #3367D6;
     }
     .output {
     border: 1px solid #ccc;
     background-color: #eef;
     border-radius: 3px;
     min-height: 30px;
     padding: 5px;
     }
     @media (max-width: 600px) {
     button, textarea, select, .output { width: 95%; }
     }
     </style>
     <!-- cordova.js will be injected during build -->
     <script type="text/javascript" src="cordova.js"></script>
    </head>
    <body>
    <h1>Complete WebRTC Demo - Video Call</h1>
    
    <!-- Media & Camera Section -->
    <div class="section">
     <h2>Media & Camera</h2>
     <!-- Local video for displaying stream -->
     <video id="localVideo" autoplay muted></video>
     <!-- Remote video for the video call -->
     <video id="remoteVideo" autoplay style="display:none;"></video>
     <br>
     <!-- Buttons to control media & switch camera -->
     <button id="startButton">Start Media (Default)</button>
     <button id="useFrontCamera">Front Camera</button>
     <button id="useBackCamera">Back Camera</button>
     <button id="closeCamera">Close Camera</button>
    </div>
    
    <!-- Video Call Section (Manual Signaling) -->
    <div class="section">
     <h2>Video Call</h2>
     <button id="startCall">Create Offer</button>
     <button id="answerCall">Create Answer</button>
     <button id="setRemoteSDP">Set Remote SDP</button>
     <br>
     <textarea id="sdpArea" placeholder="Paste or copy SDP here..."></textarea>
    </div>
    
    <!-- Additional Features Section: Screenshot & Recording -->
    <div class="section">
     <h2>Additional Features</h2>
     <button id="captureScreenshot">Capture Screenshot</button>
     <button id="startRecording">Start Recording</button>
     <button id="stopRecording">Stop Recording</button>
    </div>
    
    <!-- File Functions Section: Select Files & Select Target Path -->
    <div class="section">
     <h2>File Functions</h2>
     <button id="selectFiles">Select Files</button>
     <div id="selectFilesOutput" class="output" placeholder="selectFiles output"></div>
     <br>
     <button id="selectTargetPath">Select Target Path</button>
     <div id="selectTargetPathOutput" class="output" placeholder="selectTargetPath output"></div>
    </div>
    
    <!-- Permissions Section: Dropdown & Check Permissions -->
    <div class="section">
     <h2>Check Permissions</h2>
     <label for="permissionsDropdown">Select permissions (multiple):</label>
     <select id="permissionsDropdown" multiple>
     <option value="CAMERA">CAMERA</option>
     <option value="RECORD_AUDIO">RECORD_AUDIO</option>
     <option value="MODIFY_AUDIO_SETTINGS">MODIFY_AUDIO_SETTINGS</option>
     <option value="MANAGE_EXTERNAL_STORAGE">MANAGE_EXTERNAL_STORAGE</option>
     <option value="READ_EXTERNAL_STORAGE">READ_EXTERNAL_STORAGE</option>
     <option value="WRITE_EXTERNAL_STORAGE">WRITE_EXTERNAL_STORAGE</option>
     <option value="READ_MEDIA_AUDIO">READ_MEDIA_AUDIO</option>
     <option value="READ_MEDIA_VIDEO">READ_MEDIA_VIDEO</option>
     <option value="READ_MEDIA_IMAGES">READ_MEDIA_IMAGES</option>
     </select>
     <button id="checkPermissionsBtn">Check Permissions</button>
     <div id="permissionsOutput" class="output"></div>
    </div>
    
    <script>
    
    
     // WebRTC: https://webrtc.github.io/samples/
    
     document.addEventListener("deviceready", function () {
    
     // Initialize the CordovaSaveBlob plugin
     cordova.plugin.CordovaSaveBlob.registerWebRTC();
     // Call initial checkAndRequestPermissions with default permissions
     cordova.plugin.CordovaSaveBlob.checkAndRequestPermissions({
     permissions: ["CAMERA", "RECORD_AUDIO", "MODIFY_AUDIO_SETTINGS", "MANAGE_EXTERNAL_STORAGE"]
     }, function(success) {
     console.log("Initial Permissions:", success);
     }, function(error) {
     console.error("Initial Permissions error:", error);
     });
    
     let localStream;
     let mediaRecorder;
     let recordedChunks = [];
     let pc; // RTCPeerConnection for video call
     let currentCamera = "user"; // Default: front camera
    
     const configuration = {
     iceServers: [
     { urls: 'stun:stun.l.google.com:19302' }
     ]
     };
    
     const localVideo = document.getElementById("localVideo");
     const remoteVideo = document.getElementById("remoteVideo");
     const sdpArea = document.getElementById("sdpArea");
    
     let setPath = "";
    
     // Function to start media with the selected camera
     async function startMedia() {
     if (localStream) {
     localStream.getTracks().forEach(track => track.stop());
     }
     try {
     const constraints = {
     video: { facingMode: currentCamera },
     audio: true
     };
     localStream = await navigator.mediaDevices.getUserMedia(constraints);
     localVideo.srcObject = localStream;
     alert("Media stream started with camera: " + currentCamera);
     } catch (err) {
     console.error("Failed to get media:", err);
     alert("Failed to get media: " + err);
     }
     }
    
     // Media & Camera control event listeners
     document.getElementById("startButton").addEventListener("click", startMedia);
     document.getElementById("useFrontCamera").addEventListener("click", function() {
     currentCamera = "user";
     startMedia();
     });
     document.getElementById("useBackCamera").addEventListener("click", function() {
     currentCamera = "environment";
     startMedia();
     });
     document.getElementById("closeCamera").addEventListener("click", function() {
     if (localStream) {
     localStream.getTracks().forEach(track => track.stop());
     localStream = null;
     localVideo.srcObject = null;
     alert("Camera has been closed.");
     } else {
     alert("Camera is not active.");
     }
     });
    
     // -------------------------------
     // VIDEO CALL FUNCTIONS (Manual Signaling)
     // -------------------------------
     document.getElementById("startCall").addEventListener("click", async function() {
     if (!localStream) {
     alert("Media is not started. Please click 'Start Media' first.");
     return;
     }
     if (!pc) {
     pc = new RTCPeerConnection(configuration);
     localStream.getTracks().forEach(track => {
     pc.addTrack(track, localStream);
     });
     pc.addEventListener('track', event => {
     remoteVideo.style.display = "block";
     remoteVideo.srcObject = event.streams[0];
     });
     pc.addEventListener('icecandidate', event => {
     if (event.candidate) {
     console.log("ICE Candidate:", event.candidate);
     }
     });
     }
     try {
     const offer = await pc.createOffer();
     await pc.setLocalDescription(offer);
     sdpArea.value = JSON.stringify(pc.localDescription);
     alert("Offer created. Copy the SDP from the textarea and share with the remote peer.");
     } catch (err) {
     console.error("Error creating offer:", err);
     alert("Error creating offer: " + err);
     }
     });
    
     document.getElementById("answerCall").addEventListener("click", async function() {
     if (!localStream) {
     alert("Media is not started. Please click 'Start Media' first.");
     return;
     }
     if (!pc) {
     pc = new RTCPeerConnection(configuration);
     localStream.getTracks().forEach(track => {
     pc.addTrack(track, localStream);
     });
     pc.addEventListener('track', event => {
     remoteVideo.style.display = "block";
     remoteVideo.srcObject = event.streams[0];
     });
     pc.addEventListener('icecandidate', event => {
     if (event.candidate) {
     console.log("ICE Candidate:", event.candidate);
     }
     });
     }
     try {
     const remoteDesc = JSON.parse(sdpArea.value);
     await pc.setRemoteDescription(remoteDesc);
     const answer = await pc.createAnswer();
     await pc.setLocalDescription(answer);
     sdpArea.value = JSON.stringify(pc.localDescription);
     alert("Answer created. Share the SDP with the remote peer.");
     } catch (err) {
     console.error("Error creating answer:", err);
     alert("Error creating answer: " + err);
     }
     });
    
     document.getElementById("setRemoteSDP").addEventListener("click", async function() {
     if (!pc) {
     alert("RTCPeerConnection is not created. Please start a call first.");
     return;
     }
     try {
     const remoteDesc = JSON.parse(sdpArea.value);
     await pc.setRemoteDescription(remoteDesc);
     alert("Remote SDP is set.");
     } catch (err) {
     console.error("Error setting remote SDP:", err);
     alert("Error setting remote SDP: " + err);
     }
     });
    
     // -------------------------------
     // ADDITIONAL FEATURES: SCREENSHOT & RECORDING
     // -------------------------------
     document.getElementById("captureScreenshot").addEventListener("click", function() {
     if (!localStream) {
     alert("Media is not started. Please click 'Start Media' first.");
     return;
     }
     const canvas = document.createElement("canvas");
     const videoWidth = localVideo.videoWidth;
     const videoHeight = localVideo.videoHeight;
     if (videoWidth === 0 || videoHeight === 0) {
     alert("Video is not ready for capture. Please try again later.");
     return;
     }
     canvas.width = videoWidth;
     canvas.height = videoHeight;
     const ctx = canvas.getContext("2d");
     ctx.drawImage(localVideo, 0, 0, videoWidth, videoHeight);
    
     canvas.toBlob(function(blob) {
     if (blob) {
     const reader = new FileReader();
     reader.onloadend = function() {
     const dataUrl = reader.result; // Format: data:image/png;base64,...
     const base64Data = dataUrl.replace(/^data:.*;base64,/, '');
     const uniqueName = "screenshot_" + new Date().getTime() + ".png";
     // Call plugin to save the file
     cordova.plugin.CordovaSaveBlob.downloadBlob({
     saveToPath: setPath,
     base64Data: base64Data,
     fileName: uniqueName
     },
     function(msg) { alert("Screenshot saved successfully: " + msg); },
     function(err) { alert("Download error: " + err); });
     };
     reader.readAsDataURL(blob);
     } else {
     alert("Failed to create blob from screenshot.");
     }
     }, "image/png");
     });
    
     document.getElementById("startRecording").addEventListener("click", function() {
     if (localStream) {
     recordedChunks = [];
     const options = { mimeType: 'video/webm; codecs=vp9' };
     try {
     mediaRecorder = new MediaRecorder(localStream, options);
     } catch (e) {
     console.error("MediaRecorder does not support the given options:", e);
     alert("MediaRecorder not supported: " + e);
     return;
     }
     mediaRecorder.ondataavailable = (event) => {
     if (event.data && event.data.size > 0) {
     recordedChunks.push(event.data);
     }
     };
     mediaRecorder.start();
     console.log("Recording started.");
     alert("Recording started.");
     } else {
     alert("Media is not started. Please click 'Start Media' first.");
     }
     });
    
     document.getElementById("stopRecording").addEventListener("click", function() {
     if (mediaRecorder && mediaRecorder.state !== "inactive") {
     mediaRecorder.stop();
     mediaRecorder.onstop = function() {
     const blob = new Blob(recordedChunks, { type: "video/webm" });
     console.log("Recording finished. Blob size:", blob.size);
     const reader = new FileReader();
     reader.onloadend = function() {
     const dataUrl = reader.result;
     const base64Data = dataUrl.replace(/^data:.*;base64,/, '');
     const uniqueName = "recording_" + new Date().getTime() + ".webm";
     cordova.plugin.CordovaSaveBlob.downloadBlob({
     saveToPath: setPath,
     base64Data: base64Data,
     fileName: uniqueName
     },
     function(msg) { alert("Recording saved successfully: " + msg); },
     function(err) { alert("Download error: " + err); });
     };
     reader.readAsDataURL(blob);
     };
     } else {
     alert("Recording is not started or already stopped.");
     }
     });
    
     // -------------------------------
     // FILE FUNCTIONS: Select Files & Select Target Path with Callback Outputs
     // -------------------------------
     document.getElementById("selectFiles").addEventListener("click", function() {
     if (cordova.plugin.CordovaSaveBlob && cordova.plugin.CordovaSaveBlob.selectFiles) {
     // example > all mime: "" | image/* | video/* | audio/* | application/pdf | image/png | image/jpeg | video/mp4 | audio/mpeg
     cordova.plugin.CordovaSaveBlob.selectFiles({ mime: "image/*" },
     function(response) {
     document.getElementById("selectFilesOutput").innerText = "Response: " + response;
     },
     function(error) {
     document.getElementById("selectFilesOutput").innerText = "Error: " + error;
     }
     );
     } else {
     alert("selectFiles function is not available.");
     }
     });
    
     document.getElementById("selectTargetPath").addEventListener("click", function() {
     if (cordova.plugin.CordovaSaveBlob && cordova.plugin.CordovaSaveBlob.selectTargetPath) {
     cordova.plugin.CordovaSaveBlob.selectTargetPath(
     function(response) {
    
     setPath = response; // save to local storage
    
     document.getElementById("selectTargetPathOutput").innerText = "Response: " + response;
     },
     function(error) {
     document.getElementById("selectTargetPathOutput").innerText = "Error: " + error;
     }
     );
     } else {
     alert("selectTargetPath function is not available.");
     }
     });
    
     // -------------------------------
     // PERMISSIONS: Dropdown & Check Permissions
     // -------------------------------
     document.getElementById("checkPermissionsBtn").addEventListener("click", function() {
     const dropdown = document.getElementById("permissionsDropdown");
     const selectedOptions = Array.from(dropdown.selectedOptions).map(opt => opt.value);
     // Call checkAndRequestPermissions with selected permissions
     cordova.plugin.CordovaSaveBlob.checkAndRequestPermissions({ permissions: selectedOptions },
     function(success) {
     document.getElementById("permissionsOutput").innerText = "Success: " + success;
     },
     function(error) {
     document.getElementById("permissionsOutput").innerText = "Error: " + error;
     }
     );
     });
    
     }, false);
    </script>
    </body>
    </html>
    
    
    
  • You do not have permission to view this post

  • Update Applovin-MAX PRO

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

    1. > FULL Upgrade.
    2. > Code rewritten.
    3. > New icons.
    4. > New c3p.
    5. > Support custom Mediation network (Android | IOS).
    6. > New documentation is easy to understand, including step-by-step videos.
    7. error c3 build service > Mediation network (Android | IOS)
    8. Consent not show
  • You do not have permission to view this post

  • Update AdMob Plus PRO

    construct.net/en/game-assets/addons/admob-plus-pro-768

    1. New action: Consent Show privacy options form
    2. Fix bug: Adaptive banner ad
    3. New example AdMobPro-full-2025.c3p
    4. ACE display text tidied up
    5. New plugin property Is Debug log: boolean
    6. New plugin icons https://iconduck.com/icons/26996/admob
    7. New video:

      youtube.com/watch

  • I will not create a new problem, it will be considered a duplicate problem.

    I have been using Monaco Editor for a long time in the tool I created earlier, and I also have a C3 Monaco Editor plugin.

    So, I know the necessary improvements or new suggestions that should be added.

    Monaco Editor is indeed more powerful than CodeMirror.

    CodeMirror is lightweight but lacks complete features.

    Meanwhile, Monaco Editor is quite heavy, but its features justify the performance cost.

    Here are some additional feature suggestions:

    reference code snippet from my plugin or from my tool.

    suggest-widget reference code

    function setSuggestionItemAlignment(sizeCompletion) {
     const style = document.createElement('style');
     style.innerHTML = `
     .monaco-editor .suggest-widget .monaco-list-row {
     width: ${rowWidth}px !important;
     min-width: ${rowWidth}px !important;
     max-width: ${rowWidth}px !important;
     border-radius: 4px !important;
     transition: background-color 0.2s ease-in-out;
     }
     .monaco-editor .suggest-widget .monaco-list-row:hover {
     background-color: #171515 !important;
     }
     .monaco-editor .suggest-widget .monaco-list-row.selected {
     background-color: #171515 !important;
     }
     .monaco-editor .suggest-widget .monaco-list-row .monaco-icon-label-container {
     text-align: left !important;
     font-size: ${sizeCompletion}px !important;
     margin: 0 !important;
     left: ${paddingLeft}px !important; 
     }
     
     .monaco-editor .suggest-widget .monaco-icon-label {
     flex: 1 !important;
     display: block !important;
     text-align: left !important;
     align-items: flex-start !important;
     }
     `;
     
     document.head.appendChild(style);
     }
    
    

    suggest-widget reference css

    /* Main container for the suggest widget */
    .monaco-editor .suggest-widget {
     background-color: #1e1e1e !important; /* Main background color */
     border: 1px solid #3c3c3c !important; /* Widget border */
     color: #ffffff !important; /* Default text color */
     font-size: 14px !important; /* Overall font size */
     font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif !important;
    }
    
    /* List of items within the suggest widget */
    .monaco-editor .suggest-widget .monaco-list {
     background-color: #252526 !important; /* List background color */
     font-size: 14px !important; /* Font size for items */
    }
    
    /* Each row/item in the list */
    .monaco-editor .suggest-widget .monaco-list .monaco-list-row {
     padding: 6px 12px !important; /* Padding around each item */
     border-bottom: 1px solid #3c3c3c !important; /* Separator line between items */
    }
    
    /* Appearance on item hover */
    .monaco-editor .suggest-widget .monaco-list .monaco-list-row:hover {
     background-color: #007acc !important; /* Background color on hover */
    }
    
    /* Style for highlighted text within an item */
    .monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-highlighted-label {
     color: #ffcc00 !important; /* Highlighted text color */
     font-size: 14px !important; /* Font size for highlighted label */
    }
    
    /* If there is a scrollbar within the widget (optional) */
    .monaco-editor .suggest-widget .scrollable-element {
     scrollbar-color: #007acc #252526 !important; /* Color for scrollbar thumb and track */
    }
    
    /* Style for the currently selected item */
    .monaco-editor .suggest-widget .monaco-list .monaco-list-row.selected {
     background-color: #005f9e !important; /* Background color for the selected item */
    }
    
    

    Add new existing api scripting including new api in future releases

    construct.net/en/make-games/scripting-updates-8

    function registerCustomSuggestions() {
    
     if (langSet) {
     // Adding custom suggestions
     monaco.languages.registerCompletionItemProvider(langSet, {
     provideCompletionItems: function (model, position) {
     var suggestions = [
     {
     label: 'this._info.SetDOMSideScripts',
     kind: monaco.languages.CompletionItemKind.Function,
     insertText: 'this._info.SetDOMSideScripts(["c3runtime/domSide.js"]);',
     detail: 'Using a DOM script'
     },
     {
     label: 'this._info.AddC3RuntimeScript',
     kind: monaco.languages.CompletionItemKind.Function,
     insertText: 'this._info.AddC3RuntimeScript("c3runtime/bundle.js");',
     detail: 'Adding a module script'
     },
     {
     label: 'this._info.AddRemoteScriptDependency',
     kind: monaco.languages.CompletionItemKind.Function,
     insertText: 'this._info.AddRemoteScriptDependency("https://example.com/api-module.js", "module");',
     detail: 'Load remote module script || Load remote "classic" script'
     },
     {
     label: 'this._info.AddCordovaResourceFile',
     kind: monaco.languages.CompletionItemKind.Function,
     insertText: 'this._info.AddCordovaResourceFile({ src: "myfile.txt" });',
     detail: 'Cordova resource file dependencies'
     },
     {
     label: 'this._info.AddCordovaPluginReference',
     kind: monaco.languages.CompletionItemKind.Function,
     insertText: 'this._info.AddCordovaPluginReference({id: "cordova-plugin-inappbrowser"});',
     detail: 'Cordova plugin dependencies'
     },
     {
     label: 'this._info.AddFileDependency',
     kind: monaco.languages.CompletionItemKind.Function,
     insertText: 'this._info.AddFileDependency({ filename: "mydependency.js", type: "external-dom-script" });',
     detail: 'File dependencies'
     },
     {
     label: 'this._info.SetRuntimeModuleMainScript',
     kind: monaco.languages.CompletionItemKind.Function,
     insertText: 'this._info.SetRuntimeModuleMainScript("c3runtime/main.js");',
     detail: 'Configuring use of modules'
     },
     {
     label: 'this._info.SetTypeScriptDefinitionFiles',
     kind: monaco.languages.CompletionItemKind.Function,
     insertText: 'this._info.SetTypeScriptDefinitionFiles(["c3runtime/ISpriteInstance.d.ts"]);',
     detail: 'SetTypeScriptDefinitionFiles(arr)'
     },
    
    
    
    
    ...........
    .........
    ........ other
    
    
     ];
     return { suggestions: suggestions };
     }
     });
    
     }
    }
    
    
    
    
    
  • Manual Porting Plugin

    1. editorTextPlugin
    2. drawingPlugin

    Update editorTextPlugin to addon SDK v2

    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/addon.json'
    ❌ SDK v1: "sdk-version": 1, ➝ ✅ SDK v2: "sdk-version": 2,
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/c3runtime/actions.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/c3runtime/conditions.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/c3runtime/expressions.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/c3runtime/instance.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    ❌ SDK v1: C3.Plugins.MyCompany_TextPlugin.Instance = class MyTextInstance extends C3.SDKWorldInstanceBase ➝ ✅ SDK v2: C3.Plugins.MyCompany_TextPlugin.Instance = class MyTextInstance extends globalThis.ISDKWorldInstanceBase
    ❌ SDK v1: constructor(inst, properties) ➝ ✅ SDK v2: constructor()
    ❌ SDK v1: super(inst); ➝ ✅ SDK v2: super();
    ✅ New SDK v2: const properties = this._getInitProperties();
    ❌ SDK v1: Release() ➝ ✅ SDK v2: _release()
    ❌ SDK v1: super.Release(); ➝ ✅ SDK v2: super._release();
    ❌ SDK v1: _MaybeCreateRendererText(renderer) ➝ ✅ SDK v2: _maybeCreateRendererText(renderer)
    ❌ SDK v1: this._rendererText.SetIsAsync(false); ➝ ✅ SDK v2: this._rendererText = renderer.createRendererText();
    ✅ New SDK v2: this._rendererText.sizePt = 12;
    ❌ SDK v1: Draw(renderer) ➝ ✅ SDK v2: _draw(renderer)
    ✅ New SDK v2: this._maybeCreateRendererText(renderer);
    ✅ New SDK v2: 
    ✅ New SDK v2: const layer = this.layer;
    ✅ New SDK v2: const textZoom = layer.renderScale;
    ✅ New SDK v2: this._rendererText.setSize(this.width, this.height, textZoom);
    ✅ New SDK v2: 
    ✅ New SDK v2: this._rendererText.fontFace = this._font;
    ✅ New SDK v2: this._rendererText.text = this._text;
    ✅ New SDK v2: 
    ✅ New SDK v2: let quad = this.getBoundingQuad();
    ✅ New SDK v2: const texture = this._rendererText.getTexture();
    ✅ New SDK v2: 
    ✅ New SDK v2: 
    ❌ SDK v1: if (wi.GetAngle() === 0 && wi.GetLayer().GetAngle() === 0) ➝ ✅ SDK v2: if (this.angle === 0 && layer.angle === 0)
    ❌ SDK v1: tempQuad.setFromRect(tempRect); ➝ ✅ SDK v2: const [dl, dt] = layer.layerToDrawSurface(quad.p1.x, quad.p1.y);
    ✅ New SDK v2: const [dr, db] = layer.layerToDrawSurface(quad.p3.x, quad.p3.y);
    ✅ New SDK v2: const ox = Math.round(dl) - dl;
    ✅ New SDK v2: const oy = Math.round(dt) - dt;
    ✅ New SDK v2: quad = new DOMQuad(new DOMPoint(dl + ox, dt + oy),
    ✅ New SDK v2: new DOMPoint(dr + ox, dt + oy),
    ✅ New SDK v2: new DOMPoint(dr + ox, db + oy),
    ✅ New SDK v2: new DOMPoint(dl + ox, db + oy));
    ❌ SDK v1: this._runtime.GetCanvasManager().SetDeviceTransform(renderer); ➝ ✅ SDK v2: renderer.setDeviceTransform();
    ❌ SDK v1: renderer.Quad3(tempQuad, this._rendererText.GetTexRect()); ➝ ✅ SDK v2: renderer.setTexture(texture);
    ✅ New SDK v2: renderer.quad3(quad, this._rendererText.getTexRect());
    ❌ SDK v1: layer._SetTransform(renderer); ➝ ✅ SDK v2: renderer.setLayerTransform(layer);
    ❌ SDK v1: let offY = 0; ➝ ✅ SDK v2: let ox = 0;
    ✅ New SDK v2: let oy = 0;
    ❌ SDK v1: if (this._runtime.IsPixelRoundingEnabled()) ➝ ✅ SDK v2: if (this.runtime.isPixelRoundingEnabled)
    ❌ SDK v1: quad = tempQuad; ➝ ✅ SDK v2: ox = Math.round(quad.p1.x) - quad.p1.x;
    ✅ New SDK v2: oy = Math.round(quad.p1.y) - quad.p1.y;
    ✅ New SDK v2: 
    ✅ New SDK v2: if (ox !== 0 || oy !== 0)
    ✅ New SDK v2: {
    ✅ New SDK v2: quad = new DOMQuad(new DOMPoint(quad.p1.x + ox, quad.p1.y + oy),
    ✅ New SDK v2: new DOMPoint(quad.p2.x + ox, quad.p2.y + oy),
    ✅ New SDK v2: new DOMPoint(quad.p3.x + ox, quad.p3.y + oy),
    ✅ New SDK v2: new DOMPoint(quad.p4.x + ox, quad.p4.y + oy));
    ✅ New SDK v2: }
    ❌ SDK v1: renderer.Quad3(quad, this._rendererText.GetTexRect()); ➝ ✅ SDK v2: renderer.setTexture(texture);
    ✅ New SDK v2: renderer.quad3(quad, this._rendererText.getTexRect());
    ❌ SDK v1: SaveToJson() ➝ ✅ SDK v2: _saveToJson()
    ❌ SDK v1: LoadFromJson(o) ➝ ✅ SDK v2: _loadFromJson(o)
    ❌ SDK v1: _SetText(text) ➝ ✅ SDK v2: _setText(text)
    ❌ SDK v1: _GetText() ➝ ✅ SDK v2: _getText()
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/c3runtime/plugin.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    ❌ SDK v1: C3.Plugins.MyCompany_TextPlugin = class MyTextPlugin extends C3.SDKPluginBase ➝ ✅ SDK v2: C3.Plugins.MyCompany_TextPlugin = class MyTextPlugin extends globalThis.ISDKPluginBase
    ❌ SDK v1: constructor(opts) ➝ ✅ SDK v2: constructor()
    ❌ SDK v1: super.Release(); ➝ ✅ SDK v2: super();
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/c3runtime/type.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    ❌ SDK v1: C3.Plugins.MyCompany_TextPlugin.Type = class MyTextType extends C3.SDKTypeBase ➝ ✅ SDK v2: C3.Plugins.MyCompany_TextPlugin.Type = class MyTextType extends globalThis.ISDKObjectTypeBase
    ❌ SDK v1: constructor(objectClass) ➝ ✅ SDK v2: constructor()
    ❌ SDK v1: super(objectClass); ➝ ✅ SDK v2: super();
    ❌ SDK v1: OnCreate() ➝ ✅ SDK v2: _onCreate()
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/instance.js'
    ❌ SDK v1: const SDK = self.SDK; ➝ ✅ SDK v2: const SDK = globalThis.SDK;
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/plugin.js'
    ❌ SDK v1: const SDK = self.SDK; ➝ ✅ SDK v2: const SDK = globalThis.SDK;
    ❌ SDK v1: this._info.SetVersion(PLUGIN_VERSION); ➝ ✅ SDK v2: this._info.SetName(globalThis.lang(".name"));
    ✅ New SDK v2: this._info.SetDescription(globalThis.lang(".description"));
    ❌ SDK v1: this._info.SetHelpUrl(self.lang(".help-url")); ➝ ✅ SDK v2: this._info.SetHelpUrl(globalThis.lang(".help-url"));
    
    📁 filePath: 'a/plugin-sdk/v2/editorTextPlugin/type.js'
    ❌ SDK v1: const SDK = self.SDK; ➝ ✅ SDK v2: const SDK = globalThis.SDK;
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/addon.json'
    ❌ SDK v1: "sdk-version": 1, ➝ ✅ SDK v2: "sdk-version": 2,
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/c3runtime/actions.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    ❌ SDK v1: alert("Test property = " + this._GetTestProperty()); ➝ ✅ SDK v2: alert("Test property = " + this._getTestProperty());
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/c3runtime/conditions.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/c3runtime/expressions.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/c3runtime/instance.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    ❌ SDK v1: C3.Plugins.MyCompany_DrawingPlugin.Instance = class DrawingInstance extends C3.SDKWorldInstanceBase ➝ ✅ SDK v2: C3.Plugins.MyCompany_DrawingPlugin.Instance = class DrawingInstance extends globalThis.ISDKWorldInstanceBase
    ❌ SDK v1: constructor(inst, properties) ➝ ✅ SDK v2: constructor()
    ❌ SDK v1: super(inst); ➝ ✅ SDK v2: super();
    ✅ New SDK v2: const properties = this._getInitProperties();
    ❌ SDK v1: Release() ➝ ✅ SDK v2: _release()
    ❌ SDK v1: super.Release(); ➝ ✅ SDK v2: super._release();
    ❌ SDK v1: Draw(renderer) ➝ ✅ SDK v2: _draw(renderer)
    ❌ SDK v1: const texture = imageInfo.GetTexture(); ➝ ✅ SDK v2: const imageInfo = this.objectType.getImageInfo();
    ✅ New SDK v2: const texture = imageInfo.getTexture(renderer);
    ❌ SDK v1: renderer.SetTexture(texture); ➝ ✅ SDK v2: let quad = this.getBoundingQuad();
    ✅ New SDK v2: const rcTex = imageInfo.getTexRect();
    ❌ SDK v1: else ➝ ✅ SDK v2: renderer.setTexture(texture);
    ✅ New SDK v2: 
    ✅ New SDK v2: if (this.runtime.isPixelRoundingEnabled)
    ❌ SDK v1: renderer.Quad3(quad, rcTex); ➝ ✅ SDK v2: const ox = Math.round(this.x) - this.x;
    ✅ New SDK v2: const oy = Math.round(this.y) - this.y;
    ✅ New SDK v2: 
    ✅ New SDK v2: if (ox !== 0 && oy !== 0)
    ✅ New SDK v2: {
    ✅ New SDK v2: quad = new DOMQuad(new DOMPoint(quad.p1.x + ox, quad.p1.y + oy),
    ✅ New SDK v2: new DOMPoint(quad.p2.x + ox, quad.p2.y + oy),
    ✅ New SDK v2: new DOMPoint(quad.p3.x + ox, quad.p3.y + oy),
    ✅ New SDK v2: new DOMPoint(quad.p4.x + ox, quad.p4.y + oy));
    ✅ New SDK v2: }
    ✅ New SDK v2: 
    ✅ New SDK v2: renderer.quad3(quad, rcTex);
    ❌ SDK v1: SaveToJson() ➝ ✅ SDK v2: _saveToJson()
    ❌ SDK v1: LoadFromJson(o) ➝ ✅ SDK v2: _loadFromJson(o)
    ❌ SDK v1: _SetTestProperty(n) ➝ ✅ SDK v2: _setTestProperty(n)
    ❌ SDK v1: _GetTestProperty() ➝ ✅ SDK v2: _getTestProperty()
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/c3runtime/plugin.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    ❌ SDK v1: C3.Plugins.MyCompany_DrawingPlugin = class DrawingPlugin extends C3.SDKPluginBase ➝ ✅ SDK v2: C3.Plugins.MyCompany_DrawingPlugin = class DrawingPlugin extends globalThis.ISDKPluginBase
    ❌ SDK v1: constructor(opts) ➝ ✅ SDK v2: constructor()
    ❌ SDK v1: super.Release(); ➝ ✅ SDK v2: super();
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/c3runtime/type.js'
    ❌ SDK v1: const C3 = self.C3; ➝ ✅ SDK v2: const C3 = globalThis.C3;
    ❌ SDK v1: C3.Plugins.MyCompany_DrawingPlugin.Type = class DrawingType extends C3.SDKTypeBase ➝ ✅ SDK v2: C3.Plugins.MyCompany_DrawingPlugin.Type = class DrawingType extends globalThis.ISDKObjectTypeBase
    ❌ SDK v1: constructor(objectClass) ➝ ✅ SDK v2: constructor()
    ❌ SDK v1: super(objectClass); ➝ ✅ SDK v2: super();
    ❌ SDK v1: Release() ➝ ✅ SDK v2: _onCreate()
    ❌ SDK v1: this.GetImageInfo().LoadAsset(this._runtime); ➝ ✅ SDK v2: this.runtime.assets.loadImageAsset(this.getImageInfo());
    ❌ SDK v1: LoadTextures(renderer) ➝ ✅ SDK v2: _loadTextures(renderer)
    ❌ SDK v1: sampling: this._runtime.GetSampling() ➝ ✅ SDK v2: return renderer.loadTextureForImageInfo(this.getImageInfo(), {
    ✅ New SDK v2: sampling: this.runtime.sampling
    ❌ SDK v1: ReleaseTextures() ➝ ✅ SDK v2: _releaseTextures(renderer)
    ❌ SDK v1: this.GetImageInfo().ReleaseTexture(); ➝ ✅ SDK v2: renderer.releaseTextureForImageInfo(this.getImageInfo());
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/instance.js'
    ❌ SDK v1: const SDK = self.SDK; ➝ ✅ SDK v2: const SDK = globalThis.SDK;
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/plugin.js'
    ❌ SDK v1: const SDK = self.SDK; ➝ ✅ SDK v2: const SDK = globalThis.SDK;
    ❌ SDK v1: this._info.SetVersion(PLUGIN_VERSION); ➝ ✅ SDK v2: this._info.SetName(globalThis.lang(".name"));
    ✅ New SDK v2: this._info.SetDescription(globalThis.lang(".description"));
    ❌ SDK v1: this._info.SetHelpUrl(self.lang(".help-url")); ➝ ✅ SDK v2: this._info.SetHelpUrl(globalThis.lang(".help-url"));
    
    📁 filePath: 'a/plugin-sdk/v2/drawingPlugin/type.js'
    ❌ SDK v1: const SDK = self.SDK; ➝ ✅ SDK v2: const SDK = globalThis.SDK;
    
    
    
    
  • I mean screenshot of doqqs initial post

    Not untrue, but not quite right.

    On Start of layout: create banner > show false

    in the same aciton block: banner show

    It's better if show false

    Condition on banner load: banner show

    or On Start of layout: create banner > show true

    On Start of layout:

    |_triger once: create banner > show true

  • Are you using my plugin AdmobPro, if there is a problem contact me, and as you can see from the screenshot the logic is not correct.

    cordova.c3addonyeu@gmail.com