Does modern Javascript have a way to make an integer handle usable?

0 favourites
  • 12 posts
From the Asset Store
Single Track kit that includes wav files for the Beat, Lead, Sub and the MP3 loop.
  • I've been battling with this specific Greenworks (Steam API) feature for years now. So I figured I'd give it another try in 2023, since a lot has changed.

    My Goal:

    1. Fetch user steam avatar using Greenworks (returns integer handle)
    2. Convert integer handle to any format that Construct can use
    3. Apply converted image data to sprite using "Load image from URL" action (e.g. base64)

    The official documentation states to use a node module called Jimp. This process basically works by converting the integer handle into a buffer (in RGBA format) and then write it to an image. After many attempts, any resulting written images were always just black or corrupted for me.

    Now to my question. Are there any modern Javascript or 3rd party Javascript API alternatives to convert the integer handle that the Steamworks API provides into something usable in Construct?

  • It looks like it provides the handle (just an int tag) to be used by another greenworks function greenworks.getImageRGBA(handle); which returns a color 1d array, arranged in byte order of a[idx] = R, a[idx+1] = G, a[idx+2] = B, a[idx+3] = A.

    You can combine these together to make a RGBA color, using c3 expressions and write them into a C3 canvas for example.

    That's the theory at least :) I'm going to try it out, by adding the call to Greengrinds.

  • Ok, got something working. Though, I am doing it for local user who's avatar may already be cached. So it may require another ACE to get the userInfo, wait for that to be available, then getFriendAvatar.

    The test project includes the function to change the data to a dataURL which a sprite can load.

    Let me know if you have any questions - it's a basic test case.

    I find that I need to use the steam ID from looking at my profile on steam website my profile URL.

    (Put the steam ID in the top text box, then press Get Avatar button. Need to export as nw.js 0.82 and add steam_appid.txt to the usual spot.

    construct.net/en/make-games/addons/244/greengrinds

  • It looks like it ultimately is calling this method, in which case "an integer handle" is really just an actual "int", i.e. a normal 32-bit number. So you can just pass that around the event system as a standard number parameter.

  • Another interesting note in terms of my implementation, I got the function for creating a data URI from the original array, by describing the data format in my first post and then asked chatgpt4 to create the function for me in JS and the result worked w/o any changes, pretty nice!

  • It looks like it ultimately is calling this method, in which case "an integer handle" is really just an actual "int", i.e. a normal 32-bit number. So you can just pass that around the event system as a standard number parameter.

    I'm a bit confused and not sure what this means. Does that mean that the sprite object can make use of the 32bit number already?

  • Ok, got something working. Though, I am doing it for local user who's avatar may already be cached. So it may require another ACE to get the userInfo, wait for that to be available, then getFriendAvatar.

    The test project includes the function to change the data to a dataURL which a sprite can load.

    Let me know if you have any questions - it's a basic test case.

    I find that I need to use the steam ID from looking at my profile on steam website my profile URL.

    (Put the steam ID in the top text box, then press Get Avatar button. Need to export as nw.js 0.82 and add steam_appid.txt to the usual spot.

    https://www.construct.net/en/make-games/addons/244/greengrinds

    ...

    Thanks a lot Mikal. This seems to work perfectly fine in Construct 3 as is. Also thanks for updating the plugin!

    Since you are well experienced with this. Would it be possible to make this more compact and JS only? I basically want this to be compatible with C2 as well. The customer would just need to use the existing Greenworks plugin and execute your code. The resulting image data could just be passed using: c2_callFunction("steamAvatarData", ["DATAHERE"])

    It's alright if you are too busy to do backporting work. I still appreciate solving this long issue of mine and making me ditch JIMP for good.

  • I can give the basics, but I don't use c2 anymore. Here's generally how I would do it (untested):

     async function GetFriendAvatar(e) {
     return new Promise((resolve, reject) => {
     const requestResult = this._greenworks["requestUserInformation"](e["steam-id"], false);
     console.log('*** INFO *** domSide.js _OnGetFriendAvatar requestResult', requestResult);
     console.log('*** INFO *** domSide.js _OnGetFriendAvatar', e);
     var handle = this._greenworks["getSmallFriendAvatar"](e["steam-id"]);
     console.log('*** INFO *** domSide.js _OnGetFriendAvatar handle', handle);
     var data = this._greenworks["getImageRGBA"](handle);
     console.log('*** INFO *** domSide.js _OnGetFriendAvatar data', data);
     var size = this._greenworks["getImageSize"](handle);
     console.log('*** INFO *** domSide.js _OnGetFriendAvatar size', size);
     const result = {size, data};
     resolve(JSON.stringify(result));
     });
    	const e = {"steam-id": "483094812098"}
    	const result = await GetFriendAvatar(e);
    
    function createImageDataUrl(pixelArray, width, height) {
     // Create a canvas element
     const canvas = document.createElement('canvas');
     canvas.width = width;
     canvas.height = height;
    
     // Get the 2D context of the canvas
     const ctx = canvas.getContext('2d');
    
     // Create a new ImageData object
     let imageData = ctx.createImageData(width, height);
    
     // Set the pixel values
     for (let i = 0; i < pixelArray.length; i++) {
     imageData.data[i] = pixelArray[i];
     }
    
     // Put the image data onto the canvas
     ctx.putImageData(imageData, 0, 0);
    
     // Convert the canvas to a data URL and return it
     return canvas.toDataURL();
    }
    
    const imageData = result
    const height = imageData.size.height
    const width = imageData.size.width
    const pixelArray = imageData.data.data
    
    const dataURL = createImageDataUrl(pixelArray, width, height)
    
    console.log('dataURL', dataURL)
    

    replace this._greenworks with your pointer to greenworks

    I can help debug, if you try and it is not working for you. Just post the code.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • I can give the basics, but I don't use c2 anymore. Here's generally how I would do it (untested):

    > async function GetFriendAvatar(e) {
    return new Promise((resolve, reject) => {
    const requestResult = this._greenworks["requestUserInformation"](e["steam-id"], false);
    console.log('*** INFO *** domSide.js _OnGetFriendAvatar requestResult', requestResult);
    console.log('*** INFO *** domSide.js _OnGetFriendAvatar', e);
    var handle = this._greenworks["getSmallFriendAvatar"](e["steam-id"]);
    console.log('*** INFO *** domSide.js _OnGetFriendAvatar handle', handle);
    var data = this._greenworks["getImageRGBA"](handle);
    console.log('*** INFO *** domSide.js _OnGetFriendAvatar data', data);
    var size = this._greenworks["getImageSize"](handle);
    console.log('*** INFO *** domSide.js _OnGetFriendAvatar size', size);
    const result = {size, data};
    resolve(JSON.stringify(result));
    });

    > 	const e = {"steam-id": "483094812098"}
    	const result = await GetFriendAvatar(e);
    

    > function createImageDataUrl(pixelArray, width, height) {
    // Create a canvas element
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    
    // Get the 2D context of the canvas
    const ctx = canvas.getContext('2d');
    
    // Create a new ImageData object
    let imageData = ctx.createImageData(width, height);
    
    // Set the pixel values
    for (let i = 0; i < pixelArray.length; i++) {
    imageData.data[i] = pixelArray[i];
    }
    
    // Put the image data onto the canvas
    ctx.putImageData(imageData, 0, 0);
    
    // Convert the canvas to a data URL and return it
    return canvas.toDataURL();
    }
    
    const imageData = result
    const height = imageData.size.height
    const width = imageData.size.width
    const pixelArray = imageData.data.data
    
    const dataURL = createImageDataUrl(pixelArray, width, height)
    
    console.log('dataURL', dataURL)
    

    replace this._greenworks with your pointer to greenworks

    I can help debug, if you try and it is not working for you. Just post the code.

    Thanks a lot again! I'll try to figure things out. I initially thought the canvas stuff was a reference to the C3 canvas plugin, so I thought it would require a C2 specific workaround.

  • Ah, yes this is a traditional canvas. I assume this function will be used multiple times, so I would also suggest creating a single canvas outside of the function and pass it to the function to be reused (so you are not creating a canvas per function call.)

  • Ah, yes this is a traditional canvas. I assume this function will be used multiple times, so I would also suggest creating a single canvas outside of the function and pass it to the function to be reused (so you are not creating a canvas per function call.)

    I guess the best way to do this would be to just check if the canvas element exists and running the 3rd line if required. Thanks again for laying the groundwork for me.

    As a side note. I had to ditch the async function because for some reason it didn't work as expected inside the browser execute action. Most likely an issue caused by my lack of experience with async/await in Javascript. I still lack the experience but the years of feature workarounds, I had to implement in C2/C3 over the years taught me a little bit I suppose.

  • Ah, could be an issue with what version of JS the C2 export supports or it could be async nit supporyed at top level, not in module. Does it still work without async?

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)