A quick look at Touch Handling APIs in Game Engines

Since the iPhone happened in 2007, touch input has been at the forefront of human interaction with machines. But Game Engines have existed for a reasonable long life, preceding the mobile boom. Touch as a means of interaction with the game world in engines is provided in many ways. Below, we are going to look at different touch APIs to see how they have been provided to enable gaming on our phones. Tilt, camera, and other sensors are going to be ignored so we can focus on touch.


Browser - Gold standard of Touch APIs

The browser is not a "game engine" but there are games that run in browsers and it has solid handling of touch input that is used across devices every day in different ways. Mostly, though, they are handled through DOM elements and not directly. But let's look at how their direct handling can be done.

Browser handles 4 touch events:

  • touch start: when the finger starts touching the browser content area
  • touch move: if the finger moves after touching the browser content area
  • touch end: whether the finger moved or not when it's released from the browser content area
  • touch cancel: when the browser tab changes, the finger goes to the browser UI or something else

In code, subscribing to these events requires passing the function that will handle them, and the boolean at the end gives information on how that event propagates. In the browser is also possible to specify the element that is susceptible to that event - these are not specifics for touch but give interesting functionalities.

function startup() {
  var el = document.getElementById("canvas");
  el.addEventListener("touchstart", handleStart, false);
  el.addEventListener("touchend", handleEnd, false);
  el.addEventListener("touchcancel", handleCancel, false);
  el.addEventListener("touchmove", handleMove, false);
}

Each event passes an array of Touch elements, each with the following parameters: identifier, clientX, clientY, pageX, pageY, screenX, screenY, target. More elements are available experimentally depending on the browser. The identifier property is a unique integer for each touch and remains consistent for each event during the duration of each finger's contact with the surface.

Desktop browsers, even when a touch screen is available, can disable these events. A different API for Pointer Events exists and is recommended to use instead, and mixes Mouse Input, Touch input, and Pen input in the same event type.


Unity - The most complete API

In Unity, you can check Input.touchCount to figure out how many touchpoints are available at that frame, and retrieve each one using Input.GetTouch(i).

Each touch will have the following properties: fingerId, phase, position, deltaTime, radius, pressure, rawPosition, deltaPosition, azimuthAngle, altitudeAngle, maximumPossiblePressure, radiusVariance and type (whether a touch was of Direct, Indirect (or remote), or Stylus type).

The following are the possible touch phases: Began (A finger touched the screen), Moved (A finger moved on the screen), Stationary (A finger is touching the screen but hasn't moved), Ended (A finger was lifted from the screen. This is the final phase of a touch) or Canceled (The system cancelled tracking for the touch).

Because both the index i used in Input.GetTouch(i) and fingerId are integers, in some touch APIs that require passing a integer is not obvious what to use. For example EventSystem.current.IsPointerOverGameObject(int) actually requires passing a touch.fingerId


GameMaker - Multiple mouses approach!

Game Maker has two separate APIs for dealing with touch. One is the same used for mouse input, but they work the same way a PC would work if it had multiple mice attached, which makes it different from what we have seen so far. You pass a device number that goes from 0 to n, where the number of devices available appears to be 5 - but I can't find the function that retrieves this number. Then 0 is the first finger that touched the screen (or the mouse in a computer) and 1 is the second one and so on, and you can use a function like device_mouse_check_button(device, button); for retrieving if either the finger is pressed (by checking left click) or the mouse is clicking, with the device being this number that ranges from 0 to n.

The following functions are available: device_mouse_check_button, device_mouse_check_button_pressed, device_mouse_check_button_released, device_mouse_x, device_mouse_y, device_mouse_raw_x, device_mouse_raw_y, device_mouse_x_to_gui, device_mouse_y_to_gui,  and device_mouse_dbclick_enable.

An additional, second API is available that gives access to all common touch gestures and makes them easy to use them. These include tap, drag, flick, pinch, and rotate finger events.


Godot - A minimal approach

Godot has very lean InputEvent for screen touch, with three parameters: index (a number from 0 to n identifying a finger), position (x and y point in the screen), and a boolean named pressed that is false when the finger is released.

At the same time, since it's an InputEvent, it's can propagate through the SceneTree similar to how the browser allows handling events in elements in the DOM. This makes this simple approach still very powerful.

Additionally, Godot also provides TouchScreenButton to design buttons meant to receive the touch of multiple fingers.


Unreal - Traditional with a timestamp

Unreal TouchEvents are similar to the ones we have seen so far, the touch has a unique Handle per finger, it happens at a TouchLocation (x,y position), it has a Type (similar to previous seen phase), it has a float indicating a Force and it has a DeviceTimestamp so you can precisely know at which time the specific touch occurred.

A touch Type can be one of the following enum values Began, Moved, Stationary, ForceChanged, FirstMove, Ended, NumTypes (used when iterating across other types).

Overall, it's a very standard API. Events are also available for On Input Touch Begin, On Input Touch End and others.


Ogre3D - Very similar to SDL2

Ogre3D provides TouchFingerEvent where each finger has a unique integer fingerId, a type, an x and y position, and additionally a dx and dy is provided for the delta of that position.


Love 2D - Polling and events and Lua

Love2D allows to both poll for the touch on an engine update or to use a function to work with touch events.

When polling, love.touch.getTouches() retrieves a table named touches, which has a list of ids for each touch. The position of a touch can then be retrieved by using local x, y = love.touch.getPosition(id) for each id. Because Love2D uses Lua, polling is very fast.

The events available are love.touchpressed, love.touchmoved and love.touchreleased. In each of them, the passed parameters are id, x, y, dx, dy and pressure.


Ren'Py - Gestures

Using gestures instead of handling the position of where a touch happens is a very different approach, Ren'Py docs explain this neatly:

The gesture recognizer first classifies swipes into 8 compass directions, "n", "ne", "e", "se", "s", "sw", "w", "nw". North is considered to be towards the top of the screen. It then concatenates the swipes into a string using the "_" as a delimiter. For example, if the player swipes down and to the right, the string "s_e" will be produced.

This allows building gestures in string, config.gestures is available as a dictionary of sorts where a string representing a gesture maps to a function (an action): define config.gestures = { "n_s_w_e_w_e" : "progress_screen" } .


Conclusion

When researching for this topic I looked into HaxeFlixel Actions, and while there's no obvious way of handling touch there other than the mouse, it has a very interesting system of matching input to a function that will execute its gameplay result and it seems similar to what is available in both Ren'Py and Game Maker premade gestures. Still, gesture handling in the engine seems to be a minority. 

Looking at the touch APIs of most engines above, they seem decided to give you the meaningful data available that is as close as the user input and let you handle and interpret it as you wish. In the early days of multi-touch APIs I believe I saw even one x and y position per "pixel" touched which was very demanding to go through. By giving a position per finger per frame it's data that can be handled without as much effort and from the documentation it appears to be what is being made available in the current APIs. In some engines though, the position is a normalized float between 0 and 1 and you are on your own to convert this in your world coordinates.

Godot seem to work with the leanest at just an ID per finger, a position, and information so you can tell when the finger is down and when it leaves the screen. If you are looking to the minimal you can have to work with mobile devices when building your game, I believe it hits it. 

Gitlab Runner with self-hosted Gitlab and Sonatype Nexus with SSL

A Sonatype Nexus and the internal self signed Gitlab instances were the only resources available to this CentOS 7 Server we were dealt. Recently I and a dev configured a Gitlab Runner Docker for CI builds. This is our story. Dun Dun

Note: I am assuming that you don't want to use proxy for some reason. If you can access external resources through some corporate proxy, you may configure that, and it will probably be easy and work. We found out that using Sonatype Nexus was faster than reaching outside using proxies, and in this particular machine we configured, we couldn't access through proxy unless with our personal keys for authentication, which was undesirable.

Configuration with Self Signed Certificates

We need to have self-signed certificates that are available for install on the machines. Since this Gitlab uses these certificates, connecting directly to it without them will result in SSL errors. The first thing to do is downloading them, and if you don't know where they are available you may need to contact someone to point the URL to you, but they are most sure available.

The first thing we will need is the package ca-certificates (which may be already installed).
sudo yum install ca-certificates

We need to activate certificate management
sudo update-ca-trust enable

Go where the self signed certificates you need are made available, and download them. You will need both the emitter and root certificates. You can get the URL of the root certificate from other certificate by issuing the following command:
openssl x509 -text -inform DER -in justDownloadedCertificate.cer

Name both downloaded certificates as ca-mydomain-root.crt and ca-mydomain-emitter.crt.

Copy these files to /etc/pki/ca-trust/source/anchors

Execute the following command to update managed certificates
update-ca-trust extract

Test the SSL negotiation with your Gitlab server
openssl s_client -connect gitlab.mydomain.com:443

At the end of the output, if everything is fine, you will get the following: Verify return code: 0 (ok)

Configuration of Docker on the Host with Nexus

Here we are going to use Docker to get Gitlab Runner. So first thing to do is installing Docker. We really want RedHat fork of Docker, because it allows easily using a different Docker Registry than DockerHub, so this is done like this.
yum install docker

Now that Docker has been installed, you may create a docker user and group if you like. Notice Docker Daemon needs root access to your computer. We will assume your self-hosted internal Sonatype Nexus is available at http://nexus.mydomain.com/nexus/ . Note we can also use https here, but we will need the certificate, like we did at the previous step. If your self-hosted Nexus uses the same root certificate, then using the https Nexus URL should work.

I am assuming you have everything available on Sonatype Nexus, so we are going to configure the CentOS server to pull Docker images from there.

First, you need to figure out your Nexus Docker Registry proxy port! This will be under the Docker Registry Repository HTTP or HTTPS connector. I didn't had access to read this information directly through Sonatype Nexus interface, so it required a phone call. So this is how the number 8123 will appear from magic below.

Edit /etc/sysconfig/docker text file with any editor (eg: sudo vi /etc/sysconfig/docker ) and add the lines at the end.
BLOCK_REGISTRY='--block-registry=all'
ADD_REGISTRY='--add-registry=http://nexus.mydomain.com:8123'

Restart Docker service
sudo systemctl restart docker

Everything should be good now, let's pull the Gitlab Runner image
docker pull gitlab/gitlab-runner

Assuming everything worked out, we can move on.

Configuring Gitlab Runner through Docker

Now the machine can get packages and has SSL access to internal network websites, we can configure Gitlab Runner to have access to all this too. It's useful to read the documents regarding Gitlab Runner and Docker for Gitlab Runner.

Let's create a folder to store configurations
mkdir /opt/gitlab-runner

Now, let's create a folder to store the certificates
mkdir /opt/gitlab-runner/certs

We need to convert the certificates to PEM using the following command line
openssl x509 -in ca-mydomain-emitter.cer -inform DER -out ca-mydomain-emitter.pem -outform PEM
openssl x509 -in ca-mydomain-root.crt -inform DER -out ca-mydomain-root.pem -outform PEM

Now we need to create a bundle from these certificates, to connect to the Gitlab server with SSL.
cat ca-mydomain-root.pem ca-mydomain-emitter.pem > /opt/gitlab-runner/certs/ca-mydomain-bundle.pem

A small note here, if you are working with Java, it unfortunately doesn't use the system certificates and instead it has it's own folder, so you will need to add these certificates there too, and possibly your Sonatype Nexus instance certificate if it uses a different root certificate, to make sure Gradle works.

Almost all set, on Gitlab, go into the settings for the repository you wish to build and get the identifier token for it. At the time of writing, typically under Settings, CD/CI, Runners, Specific Settings, with some name like xxxxYYY_ZZ4S.

Let's register the runner with the internal Gitlab Server.
docker run --rm -t -i -v \
/opt/gitlab-runner:/etc/gitlab-runner \
--name gitlab-runner gitlab/gitlab-runner register --non-interactive \
--url "https://gitlab.mydomain.com" \
--registration-token "xxxxYYY_ZZ4S" \
--description "my-docker-runner" \
--tls-ca-file "/etc/gitlab-runner/certs/ca-mydomain-bundle.pem" \
--run-untagged \
--locked="false" \
--executor "docker" \
--docker-image "docker:stable" \
--docker-privileged \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock

You may add a specific tag for your runner too with --tag-list "mytag", just make sure to actually have it on your repository otherwise it may prevent the runner from starting.

Last step, let's initialize the Gitlab Runner with Restart Always. This will ensure that when the Docker is initialized, it will already start the runner.
docker run -d --name gitlab-runner --restart always \
-v /opt/gitlab-runner:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest

It works!

It really does, just push your commits and see thing happening!

Tea for Two - Development during Adventure Jam

Hello, I would like to talk a bit on Tea for Two which is my entry for Adventure Jam 2019, and the ideas and inspirations for it.
This is the first Adventure Game I write and design, along with the usual management and coding. Adventure Jam is a two weeks event, I only selected the tools and thought about the prompt before, and used the first week to actually build the story and design the game, mostly on paper, and the second week to implement everything. One base idea was to explore the mood as dark days, but without taking itself too serious.
I decided to use the opportunity to create a game that relates to Future Flashback. In previous jam I participated, there is always a point when the team thinks "we can grow this game later" but after the jam each person goes back to do their thing, so with this in mind, I decided to make a small story, that is self-contained, but can be expanded. Guilherme has built a whole universe and timeline for Future Flashback, while that game focus more on the characters and their stories, this leaves a lot to explore by picking specific events and particularities from this universe. I briefly talked with him about the idea I had to play around Logan, and build a part of his past, and he gave comments around, but ultimately said don't worry, do your thing - he was very focused on the level he was working on Future Flashback.

Since Logan is a detective, this would allow me to make a detective story, which I have been reading about on scriptwriting books. And with this came to the part of figuring out the crime, the events that lead to that crime, and the minimal environments needed for the crime. This made me create the first sketch of what the story would be.

Once I had this in mind, I started piecing how the game would work, and to me, I really wanted to pick similarities on the Sierra Interface, where you pick special mouse cursors to do things, but I wanted to do something different for the buttons. I really liked how Wadjet Eye solves in Blackwell games how Joey is always there to make the look ats conversations instead of a person talking to themselves, and I also like a lot on Firewatch when the player talks through, and I liked the over radio sound too, but I didn't want to lock myself at explaining things over radio. I also liked the Unavowed proposal of having an inventory of character abilities, so I decided to juggle around these ideas.
I wanted to make the character talk about the room environment, and talk about clues, and being able to figure clues from the environment by talking. To reduce scope, I decided there would be no objects inventory.

When I was a kid, I played a lot of times with my sister a game that here in Brazil is called Scotland Yard, but elsewhere, in US and UK, it's called 221 B Baker Street. In this game, each time there's a case, the case presents elements from it that you have to figure out things, like a murder weapon, where the murder happened, why and other elements, and you play walking around in a board, competing with other players to solve the crime first.

Originally I wanted the player to be able to talk about things in the scene, and the clues at any time, but this proved challenging to write the dialogs during the jam timespan. I mostly wanted the dialogs to be interesting and do at least one thing:
  • help you solve the crime, 
  • give a piece of background information on a character, 
  • give a piece of background information on the world, and
  • be fun. 
Another idea in the scope was that the player should be able to talk to any character about anything, not just specific police specialists, but this proved difficult to write and not be boring, for me. Now I need to do a pause and tell you that writing in English, and specifically writing dialogs in English, for me, it's very hard, so my speed of writing to make it work is slow. I spent some days with a paper notebook writing the dialogue.
After I had written, and typed everything, I showed to the musician of Future Flashback, Jordan, and we talked about the script and the dialogs, and many details and reviews came up during our conversations, and he helped make my jokes work in English too, but the main characters, even though they are Americans, they have families from foreigner background, and I wanted to keep some ideas I originally had.

Once the basic dialog lines were figured out, I showed the initial build to some people. It had just bad placeholder graphics, and no sound of any type, and the reception was bad, people complained they didn't want to be clicking and reading a text, they thought the game made no sense. I then added some game elements that to me gave the game a lot of rhythm, and I showed it to more people, and I got back the complaint "these game elements, they remove me from immersion, I like the texts, but the game elements don't feel rewarding at all". So now at this point I have no idea what to do. I decided to go with the vision and finish as is.

At this time, Jordan gives me his last review of the game dialogs, and I ask help for Morgan Willcock, from AGS Forums, to give me some input and he decided to give me his review of the game dialogs.

From there on, the pieces eventually just fell in, after reading the many encouraging messages from Sally Beaumont on the Adventure Jam discord channel I eventually gather strength to start messaging the voice actors. Every actor I approached, agreed with the role and the conversations with them are way easier than I could imagine and they start working on their lines quickly. Francisco warns me that cutting the audio files will take double the time I imagined, and he was right, it was the most time consuming task. Meanwhile Ricardo reached a good spot of the level he was making for Future Flashback and we talk and he is focused on delivering the needed backgrounds, meanwhile, he convinces his wife Melany to join us, and she quickly makes the awesome cover the game ended having. At this point, I also am in need of music and ambient sounds, and after asking Edwyn Tiong and Arishgokol, both join in and quickly deliver their contributions. After each Voice Actor delivers their voices, I deliver them to Edwyn to add the radio effect that ended in the final version.

The end of the development is a blur of lack of sleep, cursing the computer, all I remember is that the game got done in the end and I won a weekend laying in the sofa alternating between sleeping, watching tv and eating.

Completely unhealthy development but got it done.


It's 2018 already!



So 2016 passed, and then 2017 and it's now 2018. Life just goes faster the older you get... Time for some updates.

update time!

So I decided to give a pause on A Glass of Lores - starting out building a full blown RPG and engine as my first project maybe wasn't very smart. The story keeps refining slowly on my Google docs... But, cool thing. I started to make a new game! And I am NOT making an engine this time.
This game is Future Flashback and I've been screaming a lot about it on social networks, so if you never heard about it, please click through the website, there is a lot of material there.
I usually throw a single thing I made or some instruction here, but instead I will just flow through some things...

book I've read: Driving Results through Social Networks by Robert J Thomas

It states the need to align the culture of an organisation with its business strategy, and the importance of finding the influencers in the company social network. It also presents the hypothesis that innovation usually arrives from teams instead of a single person, so to favour innovation you have to have more teams, and offer the idea that if people in the organisation can have more connections, you have a bigger flow of ideas that are possible to come to fruition - you need a multitude of disciplines to generate good profitable innovation.
Also the network will have people working against innovation, which is important to take note. Also a risk is presented when a single person gets too many connections but accepts all incoming demands, becoming a bottleneck in the network. One interesting passage attribute the managers the work of exception-handlers, and so the less exceptions and more routines the organisation encounters, less managers will be necessary.

Creepy writing

Some years ago, I've read Writing Ethnographic Fieldnotes , which talks about writing fieldnotes from observed behaviours, experiences and interactions with people and between people. I found this a good hobby for when I am alone travelling, eating and only have my phone, so I take the opportunity to write about some relation I see around me. My Google Docs file where I write this is called Everyday Scenes. Here is an excerpt:
The woman waits patiently. Sit shrunked in her chair, she swipes throgh group conversations in her phone. The man she was waiting appears, and sits besides here. She doesn't realize he is there until he touches her neck. She breaks from her phone. From their gestures, they appear late to the cinema, and they rush on the movies direction. They looked very happy.

Coding things

Future Flashback is made in Adventure Game Studio, so lots of recently things have been AGS related. You can skim through either my github profile or my brand new portfolio page. That page is made using Jekyll, I used a prebuilt theme and just customised some details, which is why I was able to create it in a weekend.

I want to write more here! Hopefully before 2019! 

Rocambolli and other good things from 2016

So, I have been working in this javascript game for almost two years now! The first A Glass of Lores post was in 31 january 2015! So I have been learning, and you can say some piles of javascript code has been lying around. 😁

I have been learning a lot for the past years, I have recently read A Theory of Fun, for more input on how to make fun games, and I also needed some help on code, so I've read Game Programming Patterns, and a long list of web articles that I can put on a new post once I can find all of them! Also, with Influence: The Psychology of Persuasion and Contagious: Why Things Catch On I started my foray in understanding basic marketing. I am trying to also learn WebGL by reading WebGL Programming Guide: Interactive 3D Graphics Programming and at last I decided to install Unity in Ubuntu and try to learn C# - I am approaching this in a slow pace since I plan to finish my main game before diving too much on it. Let's see things done...

Rocambolli

Rocambolli The Game


Recently I participated in Ludum Dare 37, which had the theme One Room. It has been a ton of time since I participated in similar events, and this one was marked by going outside of my home during theme announcement, and meeting the really great Rafael Giordanno who motivated me a lot by talking his previous 7 short games, his learning, and his teaching. I was a bit unsure if I would be able to do something, going compo, which means doing a game alone, and doing everything, planning, level design, code, graphics, music, sounds! But after he told me how prepared he was, and that he was curious to what I was going to do, damn, I was going to start and finish something in that weekend.


What followed next was some crazy fast coding, in javascript, and 25 hour later I had a really ugly code, that generated this platformer game, Rocambolli, with sounds and everything working. Oh, and a Itch.io account! And the github with everything is here. This was awesome, and I want to do this again!

KTG - Keyboard Touch and Gamepad

Under heavy time constraints I coded a spaghetti interface.js, this code is awful, but it worked, and was great with keyboard and gamepad working! Rocambolli could be played. Two days later I had an idea to do this proper, and I created the KTG - Keyboard, Touch and Gamepad project. The idea is to have a simple javascript library that could unify how a simple javascript game would deal with keys, be them on touch screen, a keyboard or a joystick. I really like it! If you are doing something with javascript, this could be a simple drop in solution. I plan to mature it and migrate it into my fgmk project! Now after Ludum Dare I have moved KTG to Rocambolli so it can be played in mobile too!

Fullscreen Button

Also, when doing a tiny javascript game, I find myself often in need for a fullscreen button, since going fullscreen is still not easy on mobile without adding the page to home screen. So if you are ever in need of this, I got you covered now!

A Glass of Lores



I will leave just a link to a youtube video of a somewhat recent status of the first scene in A Glass of Lores, just so people can know the project is going forward! Here's something: making a RPG takes a long time.

Happy holidays, Merry Christmas, and see you next time. 🙌



png-font.js

using a bitmap font to write text on canvas. 

When I made the game engine, my first idea was to make everything in English. But then I noticed most people really like localized text, and supporting more languages is as important as supporting many platforms.

When I started writing what you have to be different so I could support different languages, at the end point, I had a problem with my selected pixel font, which didn't supported many characters. And fonts with more characters just were too big in size.

After asking on Twitter, people pointed me to the GNU UNIFONT, and since the Font is awesome and actively maintained, I decided to use it! Paul Hardy sent me a lot of material so I could learn the basics on fonts, which I knew nothing.

Instead of using the true type font, which is around 12MB, I got it's source bitmap, converted it to png, and wrote a js script to allow me get the glyphs from the png and place on html canvas, with the minimal things for a sane use - like word wrapping. Total size is now 840kB, which loads faster!

This lead me to build this tiny js script, the png-font, which you can get here: github.com/ericoporto/png-font .  I sent an email to FSF regarding licensing, but I read the GNU embedding exception clause and interpreted that it's ok to use it in this lib with MIT license.

There is also a demo here: ericoporto.github.io/png-font


Just a video demonstration

This is just a video of the FGMK making a chest by modifying the tile map.

One of the important concepts is that you are free to edit any tile - the building blocks of the these 2D world - in any place in the map, and it's ok to do it.
FGMK 

FGMK is my first attempt at a game maker

Since the post A tilemap in PyQt for a bigger game Project I have been working in something that at the time I wasn't quite sure of what it was. And now I think I am going somewhere. I present you my game maker, named FGMK.


At the time of this writing, I am at version 0.5.3, I have a huge backlog of things I want to implement: scripting, plugins, monster design, hero design, skill design, scripted battles, ...

So what's this? Well, a long time ago, I participated in the 2012 edition of the Global Game Jam. At the time, the game we made (with Unity) could be played in the browser, but required plugins. So the person that wanted to play your game had to install the Unity plugin. The Unity plugin was available only to Windows, so not only you had to convince the user to install it, he had to use the specific system to play the game. 

At the same Global Game Jam, a guy made a game with HTML and Javascript. The game was great, and you only needed a browser to play the game. And no plugins. I was impressed. Small code, that worked well anywhere. And the code at the time was just there, no license. I talked to the single developer of the game, asked if I could take the code, look at it, and build something VERY similar. This guy is Lino and the game he made was Redo. He said "sure, code is fine, the assets I have to ask to not to use because I am using in a game I am publishing".

So at first I made a simple python visualizer for the maps in Redo (this was around August 2014). And then I decided, ok, time to make an engine I can freely script. The engine evolved, I decided to try to build a map editor... And after lots of commits, and lots of refactoring to have something that you could run in most operating systems (Win, OSX, Linux)... Now I have something I feel it's ok to be kind of proud.

So FGMK was to be an acronym of Fan Game MaKer. It's also easy to type in the shell, because I typed this string a lot.

The Github page is github.com/ericoporto/fgmk . If you have Python3 (and pip), you can install using:

pip3 install fgmk

And that's it. When I reach a nice 1.0.0 I will release real packages for the three main operating systems.

If you are only curious about the engine, the code is in github.com/ericoporto/fgmkJsEngine and there is a demo here. The demo should run in anything that has a keyboard or a touchscreen. And you only need a browser.

NIK Filters in GIMP, Wallpapers, a book and some more!

Been a long time since I've written something here. So I thought I should post some small things I've done in the mean time.

Running NIK Collection filters on GIMP

I am slowly transitioning to use GIMP more. It's far easier once you've made it to look just like Photoshop - look this guide or this theme - if that's what you are coming from. 

Recently, Google made some filters from NIK, a company it acquired that also makes the app Snapseed for Android, available for download for free. If you look the website, the filters seem awesome. Looking on the web you will findout that people have made it run with GIMP in Windows.

But I also ditched Windows some time ago, and am using Ubuntu. So I decided I would use them in Ubuntu. There isn't much to say here other than, yeah, I've done it, using Play On Linux, both the script for Play On Linux and for Gimp are available for download in the repo below.


This was very useful and allowed me to use them on some images...

A Repository for my Wallpapers

So I don't have a Camera. I mean, a real one, with lots of fun manual controls. My smartphone recently died and I changed to using a Moto X Play - which I feel it's an awesome phone. And it has a 21 Megapixel camera, that although isn't the best on market, can take decent photos on good lighting.

With this, and the new filters, and the fact that I am learning how to use Darktable, I decided to post online the photos that I feel are nice enough to be used as wallpaper by other people. Link below.



And that thing about a book...

Writing is something I am very slow. Like I take a lot of time between writing, reading, rewriting, rereading... But I decided to write something that I think should be easy for everyone, which is building your own computer. I know, desktops aren't exactly the hottest topic in 2016. But building PCs is something I really enjoy doing, reading on the trends, benchmarks, looking up YouTube videos showing the fps for games. Yeah, so hey, it's right here, How to Build A PC.



And what more?...


I feel like I have less and less free time in life the older I get. So there isn't much more. I've made a gist in github in the mean time to throw some homeless scripts and texts, maybe some stuff there will grow in the future. There is a link to my gist down here.


bye for now!


A new blog for fast command line hacks



So I plan to post here and update on what I've been doing. But since I don't really have the time, today I'm just going to link to a small side project!

This side project is Command Line Erico, a small blog, to which I post through a bash script to Github Pages. This script is a fork of the bash script from Carlos Fenollosa, available here.

The idea of this blog came from giving a use for my old Asus EEEPC 701 4G as a distraction free production environment. Since it's too slow to do much bells and whistles stuff, I've been using it as a way to work on small projects. Because it's silent, small and robust, it's also something I can carry on.

So since every once in a while I do some stuff with very simple command line commands, I'm planning on documenting the results on there. That pc also makes as a very good way to access and manage a Raspberry Pi remotely, so stay put for some Raspberry Pi tips appearing there. The only con I've found on using it to blog so far is not having spell check, and since English is not my first language, expect grammatical aberrations.

A Glass of Lores

Hello. This a different post… Nothing was lying around...


I’ve read in a book recently - Steal Like an Artist - a phrase, that’s attributed to a cartoonist named Tom Gauld, “that once the computer is involved, ‘things are on an inevitable path to being finished. Whereas in my sketchbook the possibilities are endless.’

Since the Moleskines brought the paper as something cool again - I use most the less expensive alternatives -, my sketches proved this to be true.

Ok, so in the post long before I talked about how I was wanting to make a game, then came some programming stuff later. Ok, so a lot of the basic programming stuff is now finished, someone somewhere in the internet said that at the conception a game needs gameplay, and not story. So I followed this advice and worked without story and few graphics to get some gameplay. But I’m making a RPG. A RPG needs a story that connects very well to it’s gameplay.

Ok, so, to this RPG I’m making, I needed some basic background, the story of my world, to build the player story on top. Here I’m presenting a small but significant portion of this story. There is probably a lot of spelling and semantics error, and the names are inclined to change.

A Glass of Lores

My mom told me this story when I was very little, maybe some parts I modified, hearing others in the village. We came from the Unseeable Continent, where existed three small cities, each one with different focus, so near one from the other, with a plain plaza in the heart, where people gathered to discuss their dilemmas, drinked for love, wrote poems, stories and pursued the reason of their own existence. Novatetks produced wine, Ecteletikos focused on the rice crops, and Ykiniantopi breed the Yaks. Their texts travelled the world, and their readers got interested in these small cities.

One of this readers was an Emperor, from a far away land. That emperor was very young, and very impressed with the quality, and requested a visit to Trisan. The emperor visit was not well received by the citizens, who appreciated the isolation from the rest of the Unseeable Continent. The emperor came and went, and during his stay in the land, he noted, looking in the sand, a bright mineral, a very rare one, that he had not seen before in the vast amount as it was there available to the citizens of the three cities. 

Unfortunately, not only did the people from Trisan knew no way of how to extract that mineral from the sand, they had no army, and no way to defend themselves. The emperor sent a messenger to deliver a big floured text that read as threat, “send the mineral or I will send my troops, and I will send my troops to bring your three cities to ashes in less than ten years”.

The people from Trisan decided they needed to be capable of defending itself, they looked the old texts on how to build weapons and started reading on how to fight a combat. A small explorer group was united to find the resources. The scouter group decided the near islands were safer places to explore, because it was known they were uninhabited.

A big island turned out to be the perfect finding - a big mountain made almost solely of iron, so hot in places that it melted to the surface. Weapons could be forged there easily. The island had no fertile ground, and the water there only existed in small lakes that were created by rain. Since the mountain was very tall, clouds always stood over and the rain was certain even if not constant. To make use of Forge Island, the same ships that went to land to bring weapons had to keep returning providing food.

People on Forge Island became united on the task that brought them there. A Forge is a very unsuited place to be working straight, but since work was all that was there, and small tasks, like caring for the crops were not needed anymore, and discussing politics suddenly didn’t seem to matter. But they were very aware of their importance, and they felt like a big family.

Forge Island was unfortunately very open, had a deep sea and with no rocks to protect it from ships that could some day reach it in the imminent war. The people on Forge Island, which was by then a village already, decided to send a small group of people, in two ships, to the nearest and most rocky island, to build a fortress and a lighting post there to warn and defend the people on Forge Island.

This was a very risky thing to do because the nearest rocky island, that was so perfect to have a fort, was so good at it because it has very hard to successfully dock in. When the ships reached this island, was already night, and rain started to pour. The ships crashed near land, and started to sunken, people fled, swimming. Most survived. At the beach they gathered, with much enthusiasm for being alive. But that enthusiasm was soon to vanish.

You see, right after this gathering, which was at night, they turned their heads. Still a little deafed by the accident they didn’t realized that the rain in the island they now were was a storm in the island they come. Forge Island, that was mostly a volcano, erupted, and the night turned red, with white flashes from the distant lightnings.


That eruption lasted for days, we don’t know what happened after in the Unseeable Continent or if there were any survivors in Forge Island. What I do know is the people who survived the crash were too outnumbered to care, and with their knowledge and with what was salvaged from the ships they found this village. We do know we’ve made no contact with people outside this small island. And we did learn how to sharpen our remaining blades to defend from the monsters that inhabit this island with us.
---

We came to the island with the task of building a fort to defend Forge Island. It seemed to matter. We started to built it because it made we feel we had a purpose. We finished it to defend ourselves.

-The book of memories, Chapter 2.

Making an engine to run with custom game code

So, I'm working on my game and decided to share some simplified version of my code here. I'm making a game engine in javascript, and needed some way to code some actions.

The code is here, you can save it as example.html (http://jsfiddle.net/e3b0kocc/):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    <!DOCTYPE html> <html> <body> <script>
    var engine={}, actions={};
    engine.atomStack=new Array();

    engine.runatomStack = function(){
        while(engine.atomStack.length > 0){
            var actionToRun = engine.atomStack.shift();
            actionToRun[0](actionToRun[1]);
        } 
    };

    engine.action1 = function( param ) {
        //execute something param[0]
        console.log("executed action 1, param " + param[0] ); }

    engine.action2 = function( param ) {
        //execute something param[0] and param[1]
        console.log("executed action 2, params " + param[0] + " " + param[1] ); }

    actions.action1 = function( param ) {
        //do something param[0]
        var params = param.split(';');
        engine.atomStack.push([engine.action1,params]); }

    actions.action2 = function( param ) {
        //do something param[0] and param[1]
        var params = param.split(';');
        params[1]=parseInt(params[1],10)+2
        engine.atomStack.push([engine.action2,params]); }

    translateActions = function(action, param) {    
        actions[action](param); };

    eventActivate = function(event) {
        for (var i = 0; i < events[event].length ; i++) {
            var action = events[event][i];
            var actionAndParam = action.split('|');
            translateActions(actionAndParam[0],actionAndParam[1]);
        }
    };

    events = {
        1: ["action1|5","action2|2;2","action1|2"],
        2: ["action2|5;2","action2|2;2"],
        3: ["action2|5;2","action1|2"] };
    </script> </body> </html>

Something happens, and I need to run the actions inside an event. I call eventActivate passing the event that should happen. The function translateAction read this information and calls the function that set up the actions. My logic is based that a level contain events, an event can contain actions, and each different action contain atoms.

So, for example, at some point you call eventActivate(1) and that will push the relative events on the stack. Then from time to time the engine is used and calls engine.runatomStack() to execute whatever is there. Below is an example using f12 to call developer console and insert javascript (used Firefox).

//engine.atomStack is Array [  ]

eventActivate(2)
//engine.atomStack is Array [ Array[2], Array[2] ]

engine.runatomStack()

//prints:
//   "executed action 2, params 5 4" example.html:18
//   "executed action 2, params 2 4" example.html:18

//engine.atomStack is Array [  ]


I decided to make this post because I asked a question on stackoverflow and had to make a simpler version of my code to show there and thought that it could be useful to show here too!

Easy way to test a tiled and animated background sprite

I'm working in my game, it's going be awesome. Ok, so while working in it, I need a fast way to test my animated background sprites. I use Aseprite for drawing - it's great, and if you use Ubuntu, you can  sudo apt-get install aseprite. Go to the website for more info on other OS: http://www.aseprite.org/

Let's try an example:

save this water sprite! I made it myself! :D (that's why it's ugly..)

Aseprite can export your sprite sheet to gif easily:
file->import sprite sheet
Choose your file, and set width and height to the sprite size (mine is 32 x 32 pixels)
Now in frame->play animation you can see your animation is working!
Now in file->save as... select .gif and we have a beautiful animated gif.


This is the gif!

Ok, problem is it's difficult to have an idea if the water will turn out ok because we haven't seem it animated AND tiled. So how can we do this? I was thinking about it and decided to code an easy solution to the viewing problem! Let's make and html!

 <html>  
 <head>  
 <title>Animated Background Tile Tester</title>  
 <style TYPE="text/css">  
 <!-- body {  
 background-image: url("animateWater1.gif");  
 background-size: 64px 64px;  
 image-rendering: -moz-crisp-edges;  
 image-rendering: -o-crisp-edges;  
 image-rendering: -webkit-optimize-contrast;  
 -ms-interpolation-mode: nearest-neighbor;  
 } -->  
 </style>  
 </head>  
 <body></body>   
 </html>  

Ok, so this is a very simple html for testing sprite. Let's break it down:
background-image is where the filename of your image goes.
background-size is the size. I'm using it to double the scale of my gif, because my game will use double pixel scale.
image-rendering lines and -ms-interpolation are forcing use nearest-neighbor for aliasing option - like no anti-aliasing.


I couldn't find a way to embedded the example above in this blog post, if you know how to do the Xzibit way and put a html page inside a html page, please tell me.

Nearest-neighbor doesn't work

Ok, I've only tested in Firefox 31.0. I know this example doesn't work in Chrome. I'm using because I had to disable caching - I use SSD and there is a lot of people on the web saying that caching is bad for it. Don't know if it's true, but with no cache it's better for web development.

I can't resize Aseprite on Ubuntu!!!

Yeah, unfortunately this is true for the Aseprite in the repository. But there is one way out. Close it. In your home folder, find the .asepriterc file and open it. Find the lines that look like this:

 ...  
 [GfxMode]  
 Maximized = no  
 Width = 1800  
 Height = 960  
 ...  

And change the width height to one of your choice, and then reopen Aseprite. It's not the best solution, but it's easy and works! There are other solutions in the web, Google is your friend.



A tilemap in PyQt for a bigger game project



I wanted to get back into game development, as a hobby, for some time now. I have some idea of what I want: a simple 2d rpg game, like Pokemon in Game Boy. So for starts, I needed a way to draw tilemaps, a good editor, something I could customize for my needs.

But I really couldn't find anything as easy to use as I wanted so I decided to write my own. I opted to use Python and PyQt - and I don't know much about any of them, so I had a slow start. I'm reading about PyQt for two days in all my free time.

Until now I just have a window showing a hardcoded tilemap on screen. I'm sharing it, even unfinished, since I had a really hard time figuring out the steps to do this.

Since python cares about identation, I'm sharing a link: http://pastebin.com/d1FGDCJf

 #!/usr/bin/env python  
 # display a tiled image from tileset with PyQt  
 import sys  
 from PIL import Image  
 from PIL.ImageQt import ImageQt  
 from PyQt4 import QtGui, QtCore  
 from PyQt4.QtGui import QImage  
 from numpy import ndarray  
 # Simple background, will use open from a file in future  
 background =   [[12, 2,12, 2, 1, 2, 3, 3, 1, 1],  
      [ 1, 1, 7, 8, 5, 6, 7, 8,12, 3],  
      [ 1, 3, 1, 3,12,10,10, 1,12,12],  
      [ 2,12, 0, 4,10, 3,12, 2,12,12],  
      [12,12, 1, 1,10, 3,12, 2,12, 1],  
      [12,12,12, 0,10, 2, 1,12, 1,12],  
      [ 3,12, 3,12, 0, 2, 2,12,12, 3],  
      [ 1,12, 1,12, 1, 1,12,12, 3,12],  
      [ 3,12, 0,12,12,12,12,12, 3, 3],  
      [12, 3, 1, 2, 3,12,12,12, 1,12]]  
 # This will have the tileset  
 tileset = []   
 # last time I was writing this save function!  
 def save():  
   f = open( "map.txt" , "wb" )  
   f.write( "background :   [" )  
   for i in range(len(background) ):  
         f.write( "[" )  
     for j in range(len(background[0])):  
       f.write( str(background[j][i]) )  
       f.write( "," ) if j != len(background[0])-1 else (f.write( "]," ) if i != len(background)-1 else f.write( "]" ))  
     f.write( "\n" ) if i != len(background)-1 else f.write( "]" )  
   f.close()  
 class MyImage(QtGui.QWidget):  
   def __init__(self, parent, width, height):  
     QtGui.QWidget.__init__(self, parent)  
     BOX_SIZE = 32  
     image_file = Image.open("simpletile.png")  
     self.setWindowTitle("View tiled background")  
     # get tileset file and split it in images that can be pointed through array  
     if image_file.size[0] % BOX_SIZE == 0 and image_file.size[1] % BOX_SIZE ==0 :  
       currentx = 0  
       currenty = 0  
       tilei = 0  
       while currenty < image_file.size[1]:  
         while currentx < image_file.size[0]:  
           print currentx,",",currenty  
           tileset.append( image_file.crop((currentx,currenty,currentx + BOX_SIZE, currenty + BOX_SIZE)) )  
           tilei += 1  
           currentx += BOX_SIZE  
         currenty += BOX_SIZE  
         currentx = 0  
     # get the background numbers and use to get the tiles    
     for i in range(len(background) ):  
       for j in range(len(background[0])):  
         image = ImageQt( tileset[ background[j][i] ] )  
         pixmap = QtGui.QPixmap.fromImage(image)  
         image = QtGui.QPixmap(pixmap)  
         label = QtGui.QLabel(self)  
         label.setGeometry(i*BOX_SIZE+10, j*BOX_SIZE+10, BOX_SIZE, BOX_SIZE)  
         label.setPixmap(image)  
 save()  
 app = QtGui.QApplication(sys.argv)  
 width = 320  
 height = 320  
 w = MyImage(None, width, height)  
 w.setGeometry(100, 100, width+20, height+20)  
 w.show()  
 app.exec_()  

Had my computer lying around...



Been away from here for some time, basically I haven't been doing much fun stuff lately that seemed important to share here. But, last week, a friend asked me if I could help him counting people heads in a store. Basically, there is this very expensive hardware that use two cameras side by side, with an IR only filter and an IR emitter, so the idea is to find what is a head in a image and at what distance this head is from the camera pair, that's supposed to be installed on the ceiling, if the heads are in some range of height, they are probably human, so the camera counts plus one.

Ok so he asked me: can we do it? I thought for a minute, and replied "Yes, but you will need to buy some hardware to test it, don't know if it's worth the time", because it would take some time to receive the product and only then start to prototyping... And then it hit me! My laptop has stereoscopic camera! Emailed him, "give me two hours". Short story, in two hours, knowing near nothing on video processing, I could put together this code - using also code from around the web - that could do something near what he needed.

Ok, this codes needs to calculate the distance of something to the camera, so the idea is simple, get whatever moves more in one image, get the same for the other camera, assume that it's the same thing, and use math to calculate the distance. My equation came from this article: http://photon07.pd.infn.it:5210/users/dazzi/Thesis_doctorate/Info/Chapter_6/Stereoscopy_(Mrovlje).pdf

Viewing angle was measured using my bedroom wall, the points at each top corners of the screen where marked on the wall, and distance between then was measured and also the distance from the wall to the computer, the angle came from the resulting triangle. Did this once for each camera.

 // First example on image processing  
 // Finds the square with bigger difference from the latter  
 // Do it for both cams  
 // Guess that it's in the same object  
 // Tell the distance from the object  
 // Prototype to be further rewritten in OpenCV  
 // This code uses snippets form this great german website:  
 // http://www.creativecoding.org/lesson/topics/video/video-in-processing  
 // Aktivität in Bildbereichen feststellen in Processing  
 import processing.video.*;  
 final int VIDEO_WIDTH = 320;  
 final int VIDEO_HEIGHT = 240;  
 final int VIDEO_COLS  = 16;  
 final int VIDEO_ROWS  = 12;  
 float[] activityR = new float[VIDEO_COLS * VIDEO_ROWS];  
 float[] buffer1R = new float[VIDEO_WIDTH * VIDEO_HEIGHT];  
 float[] buffer2R = new float[buffer1R.length];  
 float[] buffer3R = new float[buffer1R.length];  
 float[] activityL = new float[VIDEO_COLS * VIDEO_ROWS];  
 float[] buffer1L = new float[VIDEO_WIDTH * VIDEO_HEIGHT];  
 float[] buffer2L = new float[buffer1L.length];  
 float[] buffer3L = new float[buffer1L.length];  
 float Max=0;  
 int maxIndex;   
 Capture camR = null;  
 Capture camL = null;  
 int POSITIONXR = 0;  
 int POSITIONXL = 0;  
 float Distance = 0;  
 int maxIndexN(float[] array) {  
  float Max=0;  
  int maxIndex = 0;   
  for(int i = 0; i<array.length; i++){   
    if(array[i]> Max){   
     Max=array[i];   
     maxIndex = i;    
   }   
  }  
  return maxIndex;  
 }   
 void setup () {  
  size (640, 240);  
  camR = new Capture (this, VIDEO_WIDTH, VIDEO_HEIGHT,"LG 3D R Webcam", 30);  
  camL = new Capture (this, VIDEO_WIDTH, VIDEO_HEIGHT,"LG 3D L Webcam", 30);  
  frameRate (15);  
  camR.start();     
  camL.start();   
 }  
 void draw () {  
  if (camR.available ()) {  
   camR.read ();  
   int index;  
   int pxPerCol = VIDEO_WIDTH / VIDEO_COLS;  
   int pxPerRow = VIDEO_HEIGHT / VIDEO_ROWS;  
   image (camR, 0, 0);  
   for (int i=0; i < activityR.length; i++) {  
    activityR[i] = 0;  
   }  
   for (int i=0; i < camR.pixels.length; i++) {  
    //Calculates activity for the Right Camera   
    int x = (int) ((i % camR.width) / pxPerCol);  
    int y = (int) ((i / camR.width) / pxPerRow);  
    index = y * VIDEO_COLS + x;  
    color col = camR.pixels[i];  
    float sum = red (col) + green (col) + blue (col);  
    float deltaPixel = (buffer1R[i] + buffer2R[i] + buffer3R[i]) / 3 - sum;  
    if (deltaPixel < 0) {  
     deltaPixel *= -1;  
    }  
    activityR[index] += deltaPixel;  
    buffer3R[i] = buffer2R[i];  
    buffer2R[i] = buffer1R[i];  
    buffer1R[i] = sum;  
   }  
   int numeroQ = maxIndexN(activityR);  
   // This simply plots the X,Y position of the maxActivity index    
   textSize(32);  
   fill(255, 255, 255);  
   text(numeroQ%VIDEO_COLS, 10, 30);  
   text(",", 50, 30);  
   text(numeroQ/VIDEO_COLS, 60, 30);  
   POSITIONXR = numeroQ%VIDEO_COLS;  
   // Set activity for the right camera  
   for (int i=0; i < activityR.length; i++) {  
    activityR[i] /= pxPerCol* pxPerRow;  
    stroke (255, 20);  
    fill (0, 255, 230, activityR[i]);  
    rect ((i % VIDEO_COLS) * pxPerCol, (i / VIDEO_COLS) * pxPerRow, pxPerCol, pxPerRow);  
   }  
  }  
   if (camL.available ()) {  
   camL.read ();  
   int index;  
   int pxPerCol = VIDEO_WIDTH / VIDEO_COLS;  
   int pxPerRow = VIDEO_HEIGHT / VIDEO_ROWS;  
   image (camL, 320, 0);  
   for (int i=0; i < activityL.length; i++) {  
    activityL[i] = 0;  
   }  
   for (int i=0; i < camL.pixels.length; i++) {  
    // Calculate activity for the Left Camera  
    int x = (int) ((i % camL.width) / pxPerCol);  
    int y = (int) ((i / camL.width) / pxPerRow);  
    index = y * VIDEO_COLS + x;  
    color col = camL.pixels[i];  
    float sum = red (col) + green (col) + blue (col);  
    float deltaPixel = (buffer1L[i] + buffer2L[i] + buffer3L[i]) / 3 - sum;  
    if (deltaPixel < 0) {  
     deltaPixel *= -1;  
    }  
    activityL[index] += deltaPixel;  
    buffer3L[i] = buffer2L[i];  
    buffer2L[i] = buffer1L[i];  
    buffer1L[i] = sum;  
   }  
   int maxIndexN = maxIndexN(activityL);  
   // Just here to write the maxActivity X,Y position    
   textSize(32);  
   fill(255, 255, 255);  
   text(maxIndexN%VIDEO_COLS, 330, 30);  
   text(",", 370, 30);  
   text(maxIndexN/VIDEO_COLS, 380, 30);  
   POSITIONXL = maxIndexN%VIDEO_COLS;  
   for (int i=0; i < activityL.length; i++) {  
    //Just to ´rint out the activity  
    activityL[i] /= pxPerCol* pxPerRow;  
    stroke (255, 20);  
    fill (0, 255, 230, activityL[i]);  
    rect ((i % VIDEO_COLS) * pxPerCol+320, (i / VIDEO_COLS) * pxPerRow, pxPerCol, pxPerRow);  
   }  
   // Calculates distance, uses 50° as the viewing angle  
   Distance = 35*VIDEO_COLS / (2*tan(0.436)*(POSITIONXL-POSITIONXR));  
   // Prints out the calculated distance  
   textSize(32);  
   fill(255, 255, 255);  
   text(Distance, 200, 200);  
   text("DISTANCE =", 10, 200);   
  }  
 }  
 // Conclusion here is that the most hard is how to tell that whatever you select on left camera to find  
 // the distance from camera, what's the same thing on the right camera.
 //
 // Copyright 2014 Érico Porto
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
 //    http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
   

Powered by Blogger.