Showing posts with label source. Show all posts
Showing posts with label source. Show all posts

Tuesday, September 17, 2019

Save and Load Binary Files in HTML5

In this example, we firstly draw something (shapes & lines) on the canvas, convert it into a bitmap (pixel data) and save the pixel values as a binary file.

//Draw something
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 60, 60);
ctx.beginPath();
ctx.rect(60, 60, 160, 80);
ctx.fillStyle = "blue";
ctx.fill();
ctx.beginPath();
ctx.lineWidth = "5";
ctx.strokeStyle = "green";
ctx.moveTo(6, 160);
ctx.lineTo(240, 360);
ctx.lineTo(60, 800);
ctx.lineTo(800, 60);
ctx.stroke();//draw
//Convert canvas draws into pixels
var buffer = ctx.getImageData(0, 0, w, h);

//https://stackoverflow.com/questions/23451726/saving-binary-data-as-file-using-javascript-from-a-browser
var saveByteArray = (function () {
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    return function (data, name) {
        var blob = new Blob(data, {type: "octet/stream"}),
            url = window.URL.createObjectURL(blob);
        a.href = url;
        a.download = name;
        a.click();
        window.URL.revokeObjectURL(url);
    };
}());
//Save pixel data (ArrayBuffer) as binary file
saveByteArray([buffer.data], 'example.bf');

Then we load the binary file and display the pixel values it stores (as a bitmap) using a new canvas.
//The requestAnimFrame fallback for better and smoother animation
var buffer = ctx.createImageData(w, h);

//http://qnimate.com/an-introduction-to-javascript-blobs-and-file-interface/
var xhr = new XMLHttpRequest(); 
xhr.open("GET", "./example.bf"); 
//although we can get the remote data directly into an arraybuffer using the string "arraybuffer" assigned to responseType property. For the sake of example we are putting it into a blob and then copying the blob data into an arraybuffer.
xhr.responseType = "blob";

function analyze_data(blob)
{
    var myReader = new FileReader();
    myReader.readAsArrayBuffer(blob);
    
    myReader.addEventListener("loadend", function(e)
    {
        var buf = e.srcElement.result;//arraybuffer object
        var buf8 = new Uint8ClampedArray(buf);//first view for copy pixel data to ImageData
        var data = new Uint32Array(buf);//second view for setting pixel values
        buffer.data.set(buf8);
        //we use putImageData() to copy the image data back to the canvas.
        ctx.putImageData(buffer, 0, 0);
    });
}

xhr.onload = function() 
{
    analyze_data(xhr.response);
}
xhr.send();

Demo & Full Source Code: Create & Save, Load & Show

Monday, September 9, 2019

Fast per pixel bitmap animation in HTML5

According to this tutorial (Faster Canvas Pixel Manipulation with Typed Arrays), we can make the code in my last post (per pixel bitmap animation in HTML5/JavaScript) faster using Typed Arrays:

//The requestAnimFrame fallback for better and smoother animation
window.requestAnimFrame = (function () {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || 
 window.mozRequestAnimationFrame || window.oRequestAnimationFrame || 
 window.msRequestAnimationFrame || function (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

//Prepare our canvas
var canvas = document.querySelector('#render');
var w = window.innerWidth;
var h = window.innerHeight;
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');

var time = Date.now();//record initial time
var buffer = ctx.createImageData(w, h);//The back buffer we used to paint the result into the canvas
//The main render function
//Calculate a color value from elapsed time and [x,y] coordinates (scaled to [0,1])
function render(time, fragcoord) {
    /* put the GLSL fragment shader's JavaScript equivalent here. */
    //begin of per pixel bitmap manipulation
    var x = fragcoord[0]; var y = fragcoord[1];
    var red = x;
    var green = y;
    var blue = 1/(1+time);
    var alpha = 1;
    //end of per pixel bitmap manipulation
    return [red,green,blue,alpha]; //the final color value (scaled to [0,1])
};
var buf;
function animate() {
    var delta = (Date.now() - time) / 1000;
    buffer = ctx.createImageData(w, h);
    //
    /*
    Next we create two ArrayBuffer views. 
    One that allows us to view buf as a one-dimensional array of unsigned 8-bit values 
    and another that allows us to view buf as a one-dimensional array of unsigned 32-bit values.
    */
    buf = new ArrayBuffer(buffer.data.length);
    var buf8 = new Uint8ClampedArray(buf);//first view for copy pixel data to ImageData
    var data = new Uint32Array(buf);//second view for setting pixel values
    //
    ctx.clearRect(0, 0, w, h);
    for (var x = 0; x < w; x++) {
        for (var y = 0; y < h; y++) {
            var ret = render(delta, [x/w, y/h]);
            //var i = (y * buffer.width + x) * 4;
            //buffer.data[i] = ret[0] * 255;//red
            //buffer.data[i + 1] = ret[1] * 255;//green
            //buffer.data[i + 2] = ret[2] * 255;//blue
            //buffer.data[i + 3] = ret[3] * 255;//alpha  
            data[y * w + x] =
            (ret[3]*255 << 24) | // alpha
            (ret[2]*255 << 16) | // blue
            (ret[1]*255 <<  8) | // green
             ret[0]*255;  // red
        }
    }
    /*
    now assign the contents of the ArrayBuffer buf to imageData.data. 
    We use the Uint8ClampedArray.set() method to set the data property 
    to the Uint8ClampedArray view of our ArrayBuffer by specifying buf8 as the parameter.
    */
    buffer.data.set(buf8);
    //Finally, we use putImageData() to copy the image data back to the canvas.
    ctx.putImageData(buffer, 0, 0);
    requestAnimFrame(animate);
};

window.onresize = function () {
    w = window.innerWidth;
    h = window.innerHeight;
    canvas.width = w;
    canvas.height = h;
};

animate();

Demo & Full Source Code: http://vvv.flaswf.tk/demo/?url=HTML5Pixelsfast

The difference: Here we use an ArrayBuffer "buf" to hold the ImageData "buffer", and create two ArrayBuffer views of "buf"; one as an array of unsigned 8-bit values for using "putImageData()" function to copy the image data back to the canvas, and the other one as unsigned 32-bit values for setting pixel values (just like in AS3, allowing you to use only one, instead of four, array assignment to set a pixel's value).

Sunday, August 25, 2019

Per pixel bitmap animation in HTML5/JavaScript

LICSON showed how to simulate GLSL shader effects on HTML5 Canvas using pure JavaScript:

//The requestAnimFrame fallback for better and smoother animation
window.requestAnimFrame = (function () {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || 
 window.mozRequestAnimationFrame || window.oRequestAnimationFrame || 
 window.msRequestAnimationFrame || function (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

//Prepare our canvas
var canvas = document.querySelector('#render');
var w = window.innerWidth;
var h = window.innerHeight;
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');

var time = Date.now();//record initial time
var buffer = ctx.createImageData(w, h);//The back buffer we used to paint the result into the canvas

//The main render function
//Calculate a color value from elapsed time and [x,y] coordinates (scaled to [0,1])
function render(time, fragcoord) {
    /* put the GLSL fragment shader's JavaScript equivalent here. */
    //begin of per pixel bitmap manipulation
    var x = fragcoord[0]; var y = fragcoord[1];
    var red = x;
    var green = y;
    var blue = 1/(1+time);
    var alpha = 1;
    //end of per pixel bitmap manipulation
    return [red,green,blue,alpha]; //the final color value (scaled to [0,1])
};

function animate() {
    var delta = (Date.now() - time) / 1000;
    buffer = ctx.createImageData(w, h);
    ctx.clearRect(0, 0, w, h);
    for (var x = 0; x < w; x++) {
        for (var y = 0; y < h; y++) {
            var ret = render(delta, [x/w, y/h]);
            var i = (y * buffer.width + x) * 4;
            buffer.data[i] = ret[0] * 255;//red
            buffer.data[i + 1] = ret[1] * 255;//green
            buffer.data[i + 2] = ret[2] * 255;//blue
            buffer.data[i + 3] = ret[3] * 255;//alpha
        }
    }
    ctx.putImageData(buffer, 0, 0);
    requestAnimFrame(animate);
};

window.onresize = function () {
    w = window.innerWidth;
    h = window.innerHeight;
    canvas.width = w;
    canvas.height = h;
};

animate();

Demo & Full Source Code: http://vvv.flaswf.tk/demo/?url=HTML5Pixels

The difference: In AS3/Haxe, a color value is represented by a single Unsigned Int value (0xAARRGGBB). In HTML5, the color data (BitmapData) array stores a whole color value as four neighboring elements (integer between 0 and 255), representing the red, green, blue and alpha values, respectively. To get/set the color value at coordinate (x,y), you should go to index "i=(y*buffer.width+x)*4", then the red, green, blue and alpha values are respectively "buffer.data[i], buffer.data[i+1], buffer.data[i+2], buffer.data[i+3]".

Links:
https://licson.net/post/glsl-fragment-shaders-in-javascript/
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas
https://bruce-lab.blogspot.com/2019/02/four-ways-for-per-pixel-bitmap.html
Faster Canvas Pixel Manipulation with Typed Arrays
References:
HTML5 Canvas: Native Interactivity and Animation for the Web, see Animation Loop (P.27), Pixel Manipulation (P.170).
Foundation HTML5 Animation with JavaScript, see Animation loops (P.16), Pixel manipulation (P.94).
HTML5 Game Development Insights, see High-Performance Update Loops (P.106), A Bitmap API Example (P.246).
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
https://www.w3schools.com/js/js_htmldom_animate.asp
https://www.w3schools.com/tags/canvas_putimagedata.asp

Friday, February 8, 2019

Four ways for per pixel bitmap manipulation in OpenFL

A BitmapData can be seen as an array of length width*height, holding unsigned int colors values. There are at least four ways to manipulate pixels of a bitmap image(Bitmap/BitmapData) in OpenFL. The first way is using setPixel() or setPixel32() function:

var myBitmapData:BitmapData = new BitmapData(800, 600, true, 0);
myBitmapData.lock();
for (j in 0...600)
    {
    for (i in 0...200)
        {
         myBitmapData.setPixel32(i, j, (i % 255) << 24 | 0x0000ff);
        }
    }
myBitmapData.unlock();
You can use lock() and unlock() function before and after multiple calls of setPixel() function, so the BitmapData will only be updated on Screen after unlock(). The second way is using setVector() function, and a Vector of UInt to hold pixel values:
var myVector:Vector = new Vector(200 * 600, true);
for (j in 0...600)
    {
    for (i in 0...200)
        {
        myVector[j * 200 + i] = (i % 255) << 24 | 0x0000ff;
        }
    }
var myRect2:Rectangle = new Rectangle(200, 0, 200, 600);
myBitmapData.setVector(myRect2,myVector);
The third way is using setPixels() function, and a ByteArray to hold pixel values:
var myByteArray:ByteArray = new ByteArray(200 * 600 * 4);
for (j in 0...600)
   {
   for (i in 0...200)
       {
       //myByteArray.position = (j * 200 + i) * 4;
       myByteArray.writeUnsignedInt((i % 255) << 24 | 0x0000ff);
       }
   }
var myRect4:Rectangle = new Rectangle(400, 0, 200, 600);
myByteArray.position = 0;
myBitmapData.setPixels(myRect4,myByteArray);
Remember to set the position of the ByteArray to 0 before calling setPixels(). The position "(j * width + i) * 4" of the ByteArray is associated with the pixel at (x=i,y=j) in the BitmapData. There is no need to set the position of the ByteArray to "(j * 200 + i) * 4" above since in the double for loops the "writeUnsignedInt()" function will update the position automatically. The fourth way is using the Memoery API, which is similar to the third way. See https://bruce-lab.blogspot.com/2013/03/fast-way-for-per-pixel-bitmap.html for more details.
var myMem:ByteArray = new ByteArray(200 * 600 * 4);
Memory.select(myMem);
for (j in 0...600)
    {
    for (i in 0...200)
        {
        Memory.setI32((j * 200 + i) * 4, (i % 255) << 24 | 0x0000ff);
        }
    }
var myRect4:Rectangle = new Rectangle(600, 0, 200, 600);
myMem.position = 0;
myBitmapData.setPixels(myRect4,myMem);
The full source code: See the result here (HTML5): http://vvv.flaswf.tk/demo/?url=BitmapDataPixel

Saturday, January 27, 2018

Saturday, November 18, 2017

Deploy ZeroNet to Heroku as a Public or Private ZeroNet Proxy Service

Note: due to formatting issues, some bash commands below have incomplete display. Just copy and paste the bash code block somewhere for the complete commands.
Deploy ZeroNet to Heroku as a Public or Private ZeroNet Proxy Service

Introduction

This is a tutorial on deploying ZeroNet to Heroku as a Python web app. You can host a (1) public or (2) private ZeroNet proxy, or only host (3) your personal zsites (ZeroNet site) statically (with all your zsite contents there) or dynamically (as an open gate to the clearnet and fetch your zsite contents from the zero network) on Heroku. An example zsite hosted on Heroku:
https://dcentral.herokuapp.com

Things are much easier if you have a VPS and wish to host ZeroNet on it, please check the following two video tutorials:
How to Create a Private ZeroNet Proxy (for Phones, only for yourself)
How to Create a ZeroNet Public Proxy for Everyone to Use
However, VPS will cost you some money. On the other hand, you can deploy and use ZeroNet on Heroku for free, although with some limitations.

Limitations

  • No persistent storage with Heroku dynos.
    • If you host a (1) public or (2) private ZeroNet proxy, your user data will not be saved on Heroku. For example, if you visit your proxy and subscribed some new sites, and next time you visit the proxy again, all subscriptions will disappear and it is like a fresh new deployment. The reason is data/zsites downloaded on Heroku are on a temporary space and will be erased whenever a session ends (e.g., when your dyno sleeps and restarts, at most every 24 hours).
    • If you want to host (3) your personal zsites statically, you need to deploy every change of your zsite (e.g., after you published a new post) to Heroku. Otherwise, it is equivalent to host it dynamically because user interactions through the ZeroNet UI will not be saved, and the Heroku copy of your zsite files is always the same as your last deployment, which can be older than the latest copy in the zero network. Although it will update automatically and fetch your latest zsite contents from the zero network whenever you visit your zsite hosted on Heroku, the updates can not be saved for your next visit, which means it has to download even the same updates from the zero network next time — in other words, it is like to host your zsite dynamically.
  • Heroku does not allow opening ports or use multiple ports. By default, ZeroNet use the port 43110 to serve the web UI, and also want the port 15441 for peer communication, although the second port is optional. However, Heroku only allows you to use one port, so we have to use the only one for ZeroNet web UI. Without a port for peer communication means we have to run ZeroNet in slow mode and creating new sites or publishing new contents may not work well (but you can do that locally).
  • The free dyno of Heroku has some quota limitations: https://devcenter.heroku.com/articles/free-dyno-hours

Tips: If you want to host a public ZeroNet proxy without any restriction on users, you can do it in just one-click by using the Heroku deployment button in this repository:
https://github.com/BruceJawn/HeroNet
And if you want to host a private ZeroNet proxy for yourself, try this repository:
https://github.com/BruceJawn/ZeroNet-private-proxy
Otherwise, you should read the following contents.

Assume you’re with Ubuntu/Linux. If you’re using Mac or Windows, it’s better to use VirtualBox with a pre-installed Ubuntu desktop image http://www.osboxes.org/ubuntu/ to save time.

Step 1. Register a Heroku account.

Register your account here: https://signup.heroku.com/signup/dc

Step 2. Install Python 2. (Optional)

Python version 2.X (instead of Python 3.X) is needed for testing and using ZeroNet locally. If you only want to host a (2) private ZeroNet proxy, you can skip this step. If you want to host a (1) public ZeroNet proxy without a zsites whitelist so any user can visit any zsite through your proxy, you can also skip this step. But if you want to host a (1) public ZeroNet proxy and only allow users to visit some whitelisted zsites (disallow users to add new sites), or host (3) your personal zsites, you must be able to run ZeroNet locally.

Ubuntu should have Python 2 installed by default, you can check it by:

python2 --version

If not, to install Python 2.X, you can refer to http://docs.python-guide.org/en/latest/starting/install/linux/

Step 3. Install Git.

sudo apt-get update
sudo apt-get install git

Step 4. Install Heroku CLI.

# Run this from your terminal.
# The following will add our apt repository and install the CLI:
sudo add-apt-repository "deb https://cli-assets.heroku.com/branches/stable/apt ./"
curl -L https://cli-assets.heroku.com/apt/release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install heroku

Now login Heroku,

heroku login

then input your registered email and password for Heroku.

Step 5. Prepare the ZeroNet app.

Firstly, clone the ZeroNet source code:

git clone https://github.com/HelloZeroNet/ZeroNet.git
cd ZeroNet

Now, add the Python runtime requirement file for Heroku:

cat > runtime.txt << EOF
python-2.7.14
EOF

Alternatively, manually create a file named “runtime.txt” in the folder “ZeroNet”, with the following content

python-2.7.14

Now go to Step 6 if you want to host a (1) public ZeroNet proxy.
Or go to Step 7 if you want to host a (2) private ZeroNet proxy.

Step 6. This step is for hosing a (1) public ZeroNet proxy.

If you want to disallow users to add new sites to your proxy, go to Step 6.1.
If you want to allow users to access any zsite, go to Step 6.2 directly.

Step 6.1.

Run ZeroNet locally, and visit all the zsites you want to be whitelisted for your users.

sudo apt-get update
sudo apt-get install msgpack-python python-gevent
python2 zeronet.py

Open http://127.0.0.1:43110/ in your browser to visit zsites.

You now can delete the file “GeoLite2-City.mmdb” in the “ZeroNet/data” folder to save space for deployment. The file “users.json” in the “ZeroNet/data” folder contains a “master_seed” and a list of zsites you just visited/subscribed. You can login using the “master_seed” as the administrator later to add and delete subscribed sites through the ZeroNet UI in multi-user mode. Make sure your “ZeroNet/data” folder contains a complete copy of the default homepage site “1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D” in the folder “ZeroNet/data/1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D”.

Now enable git to include files in “ZeroNet/data” folder:

nano .gitignore

find and change the following two lines

# Data dir
data/*
*.db

to

# Data dir
#data/*
#*.db

then ctrl+o, enter key to save and ctrl+x to exit.

Step 6.2.

Enable multi-user mode for ZeroNet,

mv  ./plugins/disabled-Multiuser ./plugins/Multiuser

Alternatively, manually rename the folder “disabled-Multiuser” in “ZeroNet/core/plugins/” to “Multiuser”.

Step 6.3.

Now, add the Procfile for Heroku:
If you want to disallow users to add new sites to your proxy,

cat > Procfile << EOF
web: python zeronet.py --ui_ip "*" --ui_port \$PORT --multiuser_no_new_sites
EOF

If you want to allow users to add new sites to your proxy,

cat > Procfile << EOF
web: python zeronet.py --ui_ip "*" --ui_port \$PORT
EOF

Alternatively, manually create a file named “Procfile” in the “ZeroNet” folder with the following content

web: python zeronet.py --ui_ip "*" --ui_port $PORT --multiuser_no_new_sites

or

web: python zeronet.py --ui_ip "*" --ui_port $PORT

depending on whether you want to disallow users to add new sites or not.

Note, ZeroNet use the default local IP 127.0.0.1 and port 43110 to serve the web UI. However, as you will install ZeroNet on a remote machine, you must set the UI IP as “*“, as described in https://zeronet.readthedocs.io/en/latest/faq/#is-it-possible-to-install-zeronet-to-a-remote-machine. Also, Heroku requires all web app to bind a dynamic port (which can be accessed by the environment var $PORT) it assigns you when your app starts, so you need to change the default port for ZeroNet UI to $PORT.

Now go to Step 9.

Step 7. This step is for hosing a (2) private ZeroNet proxy.

Now enable the UiPassword, so a user can only access the UI with the password.

mv  ./plugins/disabled-UiPassword ./plugins/UiPassword

Alternatively, manually rename the folder “disabled-UiPassword” in “ZeroNet/core/plugins/” to “UiPassword”.

Now add the Procfile for Heroku:

cat > Procfile << EOF
web: python zeronet.py --ui_ip "*" --ui_port \$PORT --ui_password yourpassword
EOF

Alternatively, manually create a file named “Procfile” in the “ZeroNet” folder with the following content

web: python zeronet.py --ui_ip "*" --ui_port $PORT --ui_password yourpassword

Change “yourpassword” above to your desired password to access the ZeroNet UI.

Tips: you can use Heroku’s config variables to set your password later. Just replace “–ui_password yourpassword” above by “–ui_password $mypassword” and you can later add and set the config variable “mypassword” in your Heroku dashboard, https://dashboard.heroku.com/apps/your_heroku_app_name/settings → Config Variables, then restart your dyno,

heroku restart

or you can use Heroku CLI to set the config variable

heroku config:set mypassword=yourpassword

Now go to Step 9.

Step 8. This step is for hosing (3) only your zsite.

Assume you have a zsite with address “1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui”. Check the tutorial: https://zeronet.readthedocs.io/en/latest/using_zeronet/create_new_site/ for creating your zsites.
Run ZeroNet locally, and visit your zsite “1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui”

sudo apt-get update
sudo apt-get install msgpack-python python-gevent
python2 zeronet.py

Open http://127.0.0.1:43110/1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui in your browser to visit your zsite.
If you want to host your zsite, make sure a full updated copy of your latest zsite is downloaded to, e.g., “ZeroNet/Data/1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui”.
Now enable git to include files in “ZeroNet/data” folder:

nano .gitignore

find and change the following two lines

# Data dir
data/*
*.db

to

# Data dir
#data/*
#*.db

then ctrl+o to save and ctrl+x to exit.
Next, enable multi-user mode for ZeroNet,

mv ./plugins/disabled-Multiuser ./plugins/Multiuser

Alternatively, manually rename the folder “disabled-Multiuser” in “ZeroNet/core/plugins/” to “Multiuser”.
Now add the Procfile for Heroku:

cat > Procfile << EOF
web: python zeronet.py --ui_ip "*" --ui_port \$PORT --multiuser_no_new_sites --homepage 1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui
EOF

Alternatively, manually create a file named “Procfile” in the “ZeroNet” folder with the following content

web: python zeronet.py --ui_ip "*" --ui_port $PORT --multiuser_no_new_sites --homepage 1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui

Change “1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui” above to your zsite address.

Note the default homepage for ZeroNet UI web interface is “1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D”, you can change it to your zsite using the flag “–homepage”.

Tips: you can use Heroku’s config variables to set your site address later. Just replace “–homepage 1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui” above by “–homepage $mysiteaddress” and you can later add and set the config variable “mysiteaddress” in your Heroku dashboard, https://dashboard.heroku.com/apps/your_heroku_app_name/settings → Config Variables, then restart your dyno,

heroku restart

or just use Heroku CLI to set the config variable

heroku config:set mysiteaddress=1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui

Also, you can use a custom domain and point its root to https://yourappname.herokuapp.com/1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui.

Step 9. Deploy ZeroNet to Heroku.

#Create an app on Heroku with a random app name
heroku create
#Use the following if you want to specify your app name
# heroku apps:create myappname
#Commit all changes, you need to do the following Steps A, B, C
#every time you modified something before deploy/update your changes to Heroku
#(A) add the modified files to the local git repository:
git add .
#(B) commit the changes to the repository:
git commit -m "notes_on_changes"
#(C) deploy the code:
git push heroku master
#Ensure that at least one instance of the app is running:
heroku ps:scale web=1
#Now visit the app at the URL generated by its app name
heroku open
#View logs
heroku logs --tail

In you browser, you should be able to see the ZeroNet web UI served by Heroku at your Heroku app URL.

Step 10. If you want to update something.

Firstly, make the changes using your local copy of ZeroNet. For example, you can update “ZeroNet/data/users.json” to whitelist some new zsites by visiting them using your local ZeroNet copy. Or if you’re hosting your own zsite statically, you can post some new contents through your local ZeroNet, so your local copy of “ZeroNet/Data/1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui” is newer and has more contents than the Heroku copy.

Then, deploy the changes,

#(A) add the modified files to the local git repository:
git add .
#(B) commit the changes to the repository:
git commit -m "notes_on_changes"
#(C) deploy the code:
git push heroku master

If you only want to host your zsite dynamically, you don’t need to deploy again after you updated your zsite.

References:

https://github.com/HelloZeroNet/ZeroNet/issues/824 (Other modification is if you enable –multiuser_no_new_sites, then normal users will not able to add new sites (users in data/users.json still can))
https://github.com/HelloZeroNet/ZeroNet/issues/1011 (–multiuser_no_new_sites)
https://devcenter.heroku.com/articles/getting-started-with-python
https://stackoverflow.com/questions/21984960/escaping-a-dollar-sign-in-unix-inside-the-cat-command

Sunday, May 8, 2016

Find the IP, Country and Location of Your Visitors Using JavaScript

Note: You must visit this page using "http" instead of "https" to display the example correctly.


Geolocation by geoPlugin is a free service providing Javascript, PHP, JSON, XML and ASP APIs for you to identify a visitor's IP, location such as country, city, latitude, longitude and so on.



Geolocation by geoPlugin

More Examples and Source Code: http://www.geoplugin.com/examples
JavaScript API: http://www.geoplugin.com/webservices/javascript

Links:
http://www.geoplugin.com/
https://developers.google.com/maps/documentation/javascript/
http://stackoverflow.com/questions/391979/get-client-ip-using-just-javascript
http://stackoverflow.com/questions/12553160/getting-visitors-country-from-their-ip
http://stackoverflow.com/questions/5206015/fastest-way-of-detecting-users-country

Saturday, January 24, 2015

Destructor 2015 - The Updated FPS Game Based on Bengine with Source Code

Destructor is the voxel based FPS game power by Bengine I made for the 7DFPS in 2012. Since Mochi Media was down, high score submission doesn't work any more. Recently, I finally updated the game and cleaned up the source code for a new release. This re-release comes with updated control, and on screen joystick for windows/android tablets. I also implemented high score submission using the newgrounds, kongregate and gamersafe API.

Play the game here:
http://www.newgrounds.com/portal/view/652322

Source Code (SVN, code only): https://flaswf.googlecode.com/svn/trunk/Games/Destructor/Destructor2015/

Source Code with Assets (All in one package): TO DO.

Credits:

Music: Theme Crystalized by Ove Melaa [CC-BY 3.0]: http://opengameart.org/content/theme-crystalized-orchestral-epic-scoresong
and see also http://bruce-lab.blogspot.com/2012/06/destructor-voxel-based-fps-game-power.html

Explosion effect forked from http://wonderfl.net/c/cWPq

Note: To compile the C source code to Bengine.swc, you may need the old Adobe Alchemy compiler. (You can find the backup download here.)

Tuesday, November 18, 2014

Get URL and Read URL Parameters in JavaScript, HaXe and AS3

Find the URL of the embedding page of a swf is a very basic way for domain locking flash games. Similar things can be done for JavaScript based online games. To get the URL, in JavaScript you can use

var myURL=document.URL;
In HaXe (targeting JavaScript or HTML5), you can use
var myURL:String=js.Browser.window.document;
In AS3, to find the path url of the swf, you can use
root.loaderInfo.loaderURL;
//or
root.loaderInfo.url;
and for finding the path url of the embedding page, you can use
ExternalInterface.call("window.location.href");
Besides, it's common to see url parameters, for example: "http://mysite.com/index.html?param1=1234&param2=somestr&param2=someotherstr" To read the parameters, in the above example, that is "1234", "somestr" and "someotherstr", in JavaScript, you can use the snippet provided by http://stackoverflow.com/a/979995/1100006 or the function given at http://css-tricks.com/snippets/javascript/get-url-variables
In HaXe, you can use the following HaXe function
//translated from http://css-tricks.com/snippets/javascript/get-url-variables/
function getQueryVariable(variable):String
{
       var query:String = js.Browser.window.location.search.substring(1);
       var vars:Array = query.split("&");
       for ( i in 0...vars.length) {
               var pair:Array = vars[i].split("=");
               if(pair[0] == variable){return pair[1];}
       }
       return("null");
}
In AS3, if the parameters are given in the path of the swf' url, for example, "http://mysite.com/myswf.swf?param1=1234&param2=somestr&param2=someotherstr", or if the parameters are declared in flashvars, then you can simply use "root.loaderInfo.parameters" object to access all the parameters, for example,
var myStr:String = root.loaderInfo.parameters.param1;
However, to read parameters of the embedding page' url, you still need the help of JavaScript, see the following pages for an example:
http://snipplr.com/view/44852/how-to-access-query-string-arguments-with-as3/ http://www.abdulqabiz.com/blog/archives/2006/03/06/how-to-get-url-query-string-variables-within-flex-application/

References:
http://stackoverflow.com/questions/979975/how-to-get-the-value-from-url-parameter http://www.javascriptcookbook.com/article/Get-the-current-URL-via-JavaScript
http://stackoverflow.com/questions/2127962/get-current-browser-url-actionscript-3
http://snipplr.com/view/47055/get-url-of-the-page-where-swf-is-embedded/ http://snipplr.com/view/28103/as3-get-url-of-current-flash-movie-swf/

Tuesday, November 11, 2014

TextParser - A Simple Class for Parsing Text Files

TextParser is a simple utility HaXe class for parsing text files. You can use this class to read String, Int variables from a text file. For example, the JiuGongGe UI uses a text file to configure the UI layout, so it needs to parse the text configuration file to do the set up.

The source code of the class is released with the JiuGongGe UI:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/src/TextParser.hx
TextParser is also available in AS3 as a swc:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/swc/TextParser.swc
To use the class, add the following line in your main class first:

haxe.initSwc(this);

The following is a simple AS3 example for using the TextParser class to parse a text file line by line and read some strings and integers needed:
var myUILayout:ByteArray = new myUILayoutClass() as ByteArray;
var myTextParser:TextParser = new TextParser(myUILayout.toString());
while (!myTextParser.EndofFile())
{
var Name:String = myTextParser.ReadString();
trace(Name);
if (Name == "#") //comment line
{
    myTextParser.GotoNextLine();
    continue;
}
var CallBackStr:String = myTextParser.ReadString();
trace(CallBackStr);
var IconStr:String = myTextParser.ReadString();
trace(IconStr);
var Color:uint = myTextParser.ReadInteger();
trace(Color);
var Label:String = myTextParser.ReadString();
trace(Label);
var LevelStr:String = myTextParser.ReadString();
trace(LevelStr);
myTextParser.GotoNextLine();
}
Basically, you need to put the parsing process inside a while loop. Where the function EndofFile() is used to check whether it is the end of the text file. The function GotoNextLine() will move the position of the file pointer to the next line. The functions ReadString() and ReadInteger() are used to read string and integer from the text file and update the file pointer's position.

Full source code for the example:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/swc/src/Main_TP.as

For a HaXe code example, please check the JiuGongGe UI's source code. 

Monday, November 10, 2014

JiuGongGe.swc - JiuGongGe UI for Flash AS3 Projects

For using the JiuGongGe UI in your Flash/AS3 projects, here is the pre-compiled swc file:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/swc/JiuGongGe.swc
An example FlashDevelop project:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/swc

To use "JiuGongGe.swc", first add the swc file to your project's library (path). Since the swc is built directly from original HaXe source code, there're two requirements:
1. Your main class must be a sub-class of "MovieClip" instead of "Sprite".
2. Add the following code before using the "JGG" class:

haxe.initSwc(this);

Check the post http://bruce-lab.blogspot.com/2014/11/jiugongge-ui-simple-open-source-ui.html for how to using the UI library - most of the HaXe code in the post also works in Flash. Note that the function "Assets.getText()" and "Assets.getBitmapData()" are provided by OpenFL, so you may need to embed the assets in your AS3 code like the example Flash project.

Wednesday, November 5, 2014

JiuGongGe UI - A Simple Open Source UI Framework in HaXe

JiuGongGe (means 9-cell) UI is a UI framework written in HaXe with OpenFL for all platforms, including Flash, HTML5, Android/iOS and Windows. This UI framework is designed to be lightweight and mobile friendly.

The framework is actually a set of buttons (clickable cells) in a nested hierarchy, with each level containing at most 9 cells. The UI framework is firstly created for the BNote application.

Basic Usage
The framework contains two main classes, "JGGCell" represents a single cell and "JGG" is the whole set of cells. You can customize the look and feel for the cells by modifying and extending the first class. For the most time, you're working with the second class. Firstly, you need to create an instance of the "JGG" class, which is a subclass of "Sprite", so you can add it to the stage and set the positions:

var myJGG:JGG = new JGG();
addChild(myJGG);
myJGG.x = 300;
myJGG.y = 200;
But nothing happens until you initialize the UI:
  
myJGG.init(this,this,Assets.getText("assets/UILayout.txt"));
Where the first parameter is the UI host,usually the parent of the the UI object. All event handler function of the click event triggered by cells should be declared in the UI host as public functions. For example, if you use the stage as the UI host, the following function in your Main class can be used as on click event handler:
 
public function CallBack(event:Event)
 {   
  trace(event.target.name);
 }

The second parameter is the assets host, which will provide the UI framework with icons. The assets host must implement the function "getIcon(ID:String):DisplayObject" as a public method, which will be called by the JGG class to fetch icon resources. For example:
 
public function getIcon(ID:String)
 {   
  return new Bitmap(Assets.getBitmapData("assets/" + ID));
 }
The return value can be a Bitmap, Sprite, or any other display object.

The third parameter is the String of the config file. The cells' hierarchy is declared in the string. Usually, it's better to write the config file in a text file, then you can use "Assets.getText" to get the string and pass it to the JGG's init function, other than directly embed the string content in your code. For each line of the config file, you declare one cell and specify the cell's property. It's not necessary to give all the 9 cells for a level. Just declare used cells only. You should give the following things (with out the "[" and "]") in order and delimited by space:
 
[name_of_cell] [on_click_callback_function_name] [background_color] [icon] [text_label] [index][newline] 
Note strictly one row for one cell, and no spaces within [...] - use "_" or "-" instead. For example,
StartCell JGG_ChangeLevel 0xffffff null Start 0
You can use "#" at the beginning of the line to comment:
# This is a comment line.

Now about some details of each property declaration.
[name_of_cell]: Will be used as the name of the JGGCell class instance. [on_click_callback_function_name]: Use the same name of the public method in the UI host. When the cell is clicked by user, this function will be called. You can use the shortcut string "JGG_ChangeLevel" for folding (for non-center cell)or unfolding (if it is the center cell) levels, without implementing the function in the UI host.
[background_color]: Background color of the cell, e.g., 0xffffff.
[icon]: An ID string used as a parameter of Assets host's public function "getIcon". Use the shortcut string "null" if you want to simply use a text label instead.
[text_label]: This is usually used for debugging or quick prototyping purpose. It's recommended use an icon instead of a text label. You can use "_" for space, "/" for newline in the label string. The label text will shown only if the icon string is "null".
[index]: To give the level information of the cell. For example "0,1,0" - integer sequence separated by ','. It is an array of indices for different levels. "0,1,0" means the cell's index in the root level is "0", in the first level is "1" and in the second level is "0". For each level, there're at most 9 cells, so the indices are all staring from "0" to "8". The cell's position is determined by the index of its last level. Starting from center as "0", go left as "1" then up as "2", then along a clockwise circle to "8".
How to interpret the index: Take "0,2,4,3,5" as an example. Look at the last (5-th) integer "5", it means the cell is in the 4-th level with index "5", so it's position is the center one on the right panel. Now the second last (4-th) integer "3", it means the cell's parent's index (in the 3-rd level) is 3. And the integer "4" means the cells' parent' parent's index is 4... In short, you can think the indices as an index of a multidimensional array, which specifies the position of the the cell in the hierarchy.

For a complete example of the config file, please check: https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/assets/UILayout.txt

Advanced Usage
To change cells' behavior, customize cell's look and feel etc.: You can use public functions foldLevel() and unfoldLevel(TargetCell:JGGCell) to change the levels manually, and getCell(name:String) to get a specific cell. The JGGCell class is a subclass of Sprite, so you can redraw the cell using its Graphics property. See the example's source code for more details: https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/src/Main.hx

Although the UI framework is very simple, I believe it can satisfy the most needs for a simple application with some customization. For example, check the BNote app. However, if you're looking for a more feature complete UI framework in HaXe, try http://haxeui.org/ or http://ui.stablex.ru.

Links:
Flash demo: http://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/bin/flash/bin/JiuGongGe.swf
HTML5 demo: http://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/bin/html5/bin/index.html
Real Example (BNote): http://bruce-lab.blogspot.com/2014/10/bnote-free-online-taking-handwriting-notes.html
Source Code v0.2 (MIT): https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2
SWC for Flash/AS3: http://bruce-lab.blogspot.com/2014/11/jiugonggeswc-jiugongge-ui-for-flash-as3.html

Thursday, August 14, 2014

AS3 Eval/Scripting Engines

AS3 Scripting Lib:

Compile AS3/JavaScript at runtime within the Flash Player
Execute compiled code in the scope of any object
Control which classes and functions are exposed to the script domain
https://code.google.com/p/as3scriptinglib/

D.eval:
The D.eval API brings the eval() functionality to ActionScript3 programming.
http://o-minds.com/deval/
http://www.riaone.com/products/deval

Runtime expression evaluation in ActionScript:
http://www.sephiroth.it/weblog/archives/2008/04/runtime_expression_evaluation_in_acti.php

ActionScript 3 Mathematics Expression Parser:
http://www.flashandmath.com/intermediate/mathparser

Lua for Flash:
http://code.google.com/p/lua-alchemy/ (Lua for Flash via Alchemy)
https://github.com/adobe-flash/crossbridge/tree/master/samples/Example_Lua
http://gonchar.me/blog/goncharposts/2121 (Lua for Flash via CrossBridge)
https://github.com/chadrem/easy_lua (Another Lua for Flash port using CrossBridge)
http://code.google.com/p/lufacode/ (Lua for Flash in pure AS3)

ActionScript 3 Eval Library:
As3Eval is a library that packages the Tamarin ESC compiler to work within a run-of-the-mill flash player.
http://eval.hurlant.com/

Governor:
Governor is a script engine written in AS3. It provides all functions and operators you know from AS3; operators, math functions, math constants. Additionally it provides multithreading functionality for parallel execution of code. !The flash player is design as a single thread application, so governor is providing green threads! 
http://code.google.com/p/governor/

BISE Scripting Engine:(Recommended)
The AS2-based engine could run a subset of ECMAScript, and it allowed users to write interpreted scripting for Flash games or applications. It also had some useful features, such as coroutines, a type of function that had the ability to suspend in the middle of its scripting.
http://kinsmangames.wordpress.com/bise-scripting-engine/ (AS3 Port)
http://kinsmangames.com/beinteractive-scripting-engine-bise-as3-port/
http://sourceforge.net/projects/biseas3/files/beinteractivescriptingengine_as3port_version104.zip/download
http://www.be-interactive.org/index.php?itemid=7 (AS2)

eval() for Actionscript 3:
http://www.brainblitz.org/anthony/130

AS3 Commons Bytecode:
AS3Commons-bytecode is an open-source library providing a ABC bytecode parsing and publishing and bytecode based reflection API for ActionScript 3.0. 
http://www.as3commons.org/as3-commons-bytecode/

XML to ActionScript(asXML):
http://actionsnippet.com/?p=1611

TinyBasicAS: Flex version of TinyBasic
http://www.thisiscool.com/tinybasicas.htm

Java runs in Flash-Waba VM Alchemy:
http://www.thisiscool.com/j2f.htm

The brainfuck programming language interpreter:
http://blog.onthewings.net/2009/10/08/brainflash-the-as3-brainfuck-interpreter/
http://wonderfl.net/c/njuL

Ascript:
http://code.google.com/p/ascript-as3/
https://github.com/softplat/ascript

hscript:
Parse and evalutate Haxe expressions. Haxe script is a complete subset of the Haxe language.
https://github.com/HaxeFoundation/hscript

Simplified JavaScript interpreter:
A JavaScript interpreter you can embed in your Flash ActionScript 3 projects. Can parse and execute most JavaScript and support async "await" statement that pauses the VM until a Promise is fulfilled.
https://github.com/sixsided/Simplified-JavaScript-Interpreter

Wednesday, July 30, 2014

Migrating from Alchemy to CrossBridge/FlasCC - Interop between C/C++ and ActionScript

Part I - Calling C/C++ functions from AS3

For calling C/C++ functions from AS3, one way is use the "as3sig:" annotation to expose some C/C++ functions to AS3. See the CrossBridge Sample 5 SWC and my previous post http://bruce-lab.blogspot.com/2012/12/migrating-from-alchemy-to-flascc.html. Another way is to use "CModule.callI(CModule.getPublicSymbol("youCfunctionname"), args)", see the Sample 4 Animation, where for C++, you will need "extern" declaration before your function definition to prevent the compiler renaming the function.

Part II - Calling AS3 functions from C/C++

To call build-in AS3 functions, you can either use "inline_as3" (see Sample 2 Interop between C/C++ and ActionScript. Actually, asm was also available in Alchemy, although not officially documented) or the API provided by "AS3.h" and "Flash++.h". You can also define local AS3 functions or equivalent C/C++ functions using the two methods in your C/C++ source file. However, if you want to call an AS3 function outside your C/C++ file, e.g., in the "Main.as/Console.as", you may need to pass the reference of your AS3 function or the function host - your main AS3 class to C/C++. There are two different cases depending whether your run the C/C++ code in the background worker.

Case 1 - C/C++ code in UI worker.

If you invoke your C/C++'s main function using "CModule.start()", or "CModule.startAsync()", or just simply call some C/C++ function using the methods in Part I without executing the C/C++ main function, the C/C++ code will run in the UI worker, the same domain as your main AS3 console class. Then things are easy and there are various ways for passing your Main class's reference, see my examples:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/CB_callAS3fromC/nobgcall

Case 2 - C/C++ code in background worker.

If you invoke your C/C++'s main function using "CModule.startBackground()", and want to call some outside AS3 function from your C/C++'s main function, then the thing become a little tricky, because you're not able to share a function reference between workers, i.e., there is no way to pass a function reference from the UI worker - your Main/Console class to the background worker - your C/C++ code, as the class/function reference can't be serialized in AMF3 format. There is one way to circumvent the problem, using MessageChannels:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/CB_callAS3fromC/bgcall

In other situations, you can access your Main UI class's properties from the background worker by the "avm2_ui_thunk" and "CModule.serviceUIRequests" combinations, see http://bruce-lab.blogspot.com/2014/07/crossbridge-quake1-example-simplified.html for details.


Links:
http://forums.adobe.com/message/6002640#6002640

http://www.bytearray.org/?p=4423
http://www.bytearray.org/?p=4423#comment-494548

http://help.adobe.com/en_US/as3/dev/WS2f73111e7a180bd0-5856a8af1390d64d08c-8000.html
http://help.adobe.com/en_US/as3/dev/WS2f73111e7a180bd0-5856a8af1390d64d08c-7fff.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Worker.html

http://jacksondunstan.com/articles/tag/workers
http://esdot.ca/site/2012/intro-to-as3-workers-hello-world
http://probertson.com/articles/2012/11/07/as3-concurrency-workers-use-cases-best-practices-links/

Tuesday, July 15, 2014

Crossbridge Quake1 Example Simplified

I made some minor modifications of the official Crossbridge Quake1 example so that the source code is easier to use and mod. The original source code uses a very different workflow from the old Alchemy, that is, compile everything, including the C/C++ code, AS code and data file, into the final swf file directly via a single Makefile. Although this approach is elegant and simple as a sample of the Crossbrige SDK, it will cause several inconveniences for a real project. The drawback is obvious, once you changed something, either the C/C++ code, AS code or the data file, you need to recompile everything.

On the other hand, the old Alchemy workflow, that is, compile all the C/C++ code into an independent swc first, then import the swc in the AS project, is more efficient. Although it is possible to use several separated makefiles for different steps of the whole compiling process, the old Alchemy workflow is more friendly to Flash developers.

So what I did in the simplified version is

1. Changed Makefile to compile the swc instead of swf.
The original Makefile mixes gcc and asc, where gcc is used to compile all the C source files into .o files and the final swf file, and asc is used to compile the "Console.as" file. I just removed the part for compiling the "Console.as" file. (The modified "Console.as" file will be used as the Main class for the AS project, in which it will be linked with the swc file later.) Then, changed the option "-emit-swf" for gcc to "-emit-swc" so the swc file, instead of the swf file, will be created.

2. Simplified the way for supplying the file "pak0.pak" to C/C++, so no "genfs" needed.
Crossbrige introduces genfs for the file system. This can be seen as an advantage over Alchemy, but personally, I feel it is not so easy to use. Crossbrige uses the genfs tool to convert all your files/folders needed in your C/C++ code into plain AS text files so the compiler can compile the converted data files into your swf. However, this is a disaster for modding - every time you change the data files, you need to genfs them into AS files first before they can be used in your Corssbrige projects. Fortunately, there is a simple alternative way (the Alchemy-like way, not officially documented, but actually use the same API as the genfs way) for supplying data to C/C++, see this post http://bruce-lab.blogspot.com/2013/11/migrating-from-alchemy-to.html for details. In this way, you handle all the data using AS3 only, so you can embed files using AS3 code, or load them on the run using a URLLoader.

There is one problem, beyond the quake example. That is when you need to supply many files or folders to C/C++. Manually embedding them in pure AS3 is troublesome. In this scenario, it seems the genfs tool will save your the trouble. However, this can also be solved by pure AS3. My solution is zip everything as one package first, then use some AS3 zip library (such as http://nochump.com/blog/archives/15, http://codeazur.com.br/lab/fzip/) to supply all files programmably. (Actually, the quake data file is a zipped package of many files and folders in a custom format ".pak", and the engine itself takes care of all the unzipping, parsing and loading processes.)

3. Created the FlashDevelop project.
Added the swc file generated previously to lib, modified the "Console.as" file, which imports classes/packages needed from the swc file, and embed the game data file in the Main class "Console.as". Besides, an unimplemented preloader class is added in the AS project. The official way for adding preloader directly for Crossbrige generated swf is also not very handy. With an AS project, it's trivial work to implement the preloader.

You can find the source code:
(SVN, source code only) http://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/CrossBridge_Example_Quake1_Simplified/
(All in One Package, everything you need to compile) http://code.flaswf.tk/2014/07/sdlquake1-for-crossbridge-simplified.html

What I learned from the original quake example

The most important files in the example are "Console.as", "sys_sdl.c", "snd_mix.c". The file "sys_sdl.c" is where you can find the C main function, besides, in the file "sys_sdl.c", function
engineTick() is the main game loop, and
engineTickSound() is the main sound loop.

The sdlquake example almost answers most questions when you want to port an SDL based application or game to Flash with Crossbridge.

Q1. How to start the main game loop?
There are two ways showed in the example to run the main game loop in each frame. The first one is to call the C/C++ main loop function engineTick() from AS3 in an EnterFrame event handler, see the line

CModule.callI(enginetickptr, emptyArgs)
in "Console.as". This is single threaded and everything, including both the main game loop and the screen buffer rendering, runs in the main UI worker only.
The second one is to use a background worker, so you can put the main loop function in an infinite while(true) loop in the C main function. In this case, you need two threads, the background worker (all C code) is running the main game loop and does the blitting job while the main UI worker only renders the screen buffer in the EnterFrame event handler. see this post http://bruce-lab.blogspot.com/2014/05/migrating-from-alchemy-to.html for more.

Q2. How to render the ScreenBuffer?
As I explained in http://bruce-lab.blogspot.com/2012/12/migrating-from-alchemy-to-flascc.html, all you need to do is to get pointer to the Screen Buffer(data/array of colour values) of your C/C++ code, see the line
vbuffer = CModule.getPublicSymbol("__avm2_vgl_argb_buffer")
in "Console.as", then create a BitmapData and use the setPixels method to render the buffer.

Q3. How to get the Keyboard input?
Of course you need to listen the KEY_UP and KEY_DOWN events in AS3. Then you can implement the "read" function in "Console.as", so that key inputs can be read by C using normal C IO.

Q4. How to get the Mouse input?
The example showed how to let C know the mouse position. Firstly, mouse position "mx" and "my" can be captured in AS3.
If you're running the main loop in the UI worker (ST), get the pointer of the C variables for storing mouse position:
vgl_mx = CModule.getPublicSymbol("vgl_cur_mx")
vgl_my = CModule.getPublicSymbol("vgl_cur_my")
Then use domain memory to update the values of the two C variables directly in AS3:
CModule.write32(vgl_mx, mx)
CModule.write32(vgl_my, my)

If you're in MT mode, things are a little tricky. The "handleFrame()" in "sys_sdl.c" is for getting mouse input. The function used inline asm to get the values for the mouse positions.
inline_as3(
"import com.adobe.flascc.CModule;\n"
"%0 = CModule.activeConsole.mx\n"
"%1 = CModule.activeConsole.my\n"
: "=r"(vgl_cur_mx),"=r"(vgl_cur_my) :
);
Since you're running the main loop in the background and mouse position can only be retrieved from the UI worker, you need the
avm2_ui_thunk(handleFrame, NULL);
(in C, the main game loop, where you need to update the mouse position, to queue up uiThunk request for calling handleFrame on the UI Worker)
and
CModule.serviceUIRequests()
(in AS3, the EnterFrame handler, to service the pending uiThunk request) combination.

Q5. How to play the sound data?
Use Sound and SoundChannel class in AS3 to play sound data. To get the sound sample data, use inline asm to writeFloat, and the proxy variable "sndDataBuffer" in AS3, see "snd_mix.c" and "Console.as" for details.

Q6. How to supply the files to C?
See the previous section where I talked about the file system.

Some other notes:

1. If you're using 32-bit system with 32-bit java, you need to pass the option -jvmopt="-Xmx1G" to gcc/g++, otherwise, you may get the error "LLVM ERROR: Error: Unable to launch the Java Virtual Machine. This usually means you have a 32bit JVM installed or have set your Java heap size too large. Try lowering the Java heap size by passing "-jvmopt=-Xmx1G" to gcc/g++."

2. On windows, when compiling the original source code, if the compiler complains that "Console.as" is locked and not accessible, try to use some software to unlock the file "Console.as", then compile again.

Special thanks to Michael Guidry (MadGypsy on quakeone.com) for motivating me to write this post.

Sponsors