Browse Category

Programming

Integrating Counter-Strike: Global Offensive with Philips Hue

csgo-c4-hue

Recently, Valve had released their Counter-Strike: Global Offensive game state integration for developers to use. This allows the game client to send all game data, such as the player’s health, guns, ammo, frags, etc. to a HTTP POST endpoint server.

After seeing the PGL Arena Effects in the DreamHack Cluj-Napoca CS:GO tournament a couple of months ago, I decided to make full use of my Philips Hue lights in my room by recreating the C4 bomb lighting effects.

Coding

Since I’ve been coding in Python a lot recently, I stuck with this language for this project. First, I had to create the HTTP server to receive the POST game state data, so I used the Flask microframework. I launched the Flask server and joined a game in CS:GO, and immediately I was seeing tons of JSON being received in the terminal window. All I really needed was the C4 bomb information, which I was able to find in data[’round’][‘bomb’]. By checking that key, I noticed there were 3 values if the key existed: planted, exploded, and defused. If the bomb isn’t planted, there won’t be a bomb key in the round object at all. So after getting the bomb status, I made the server write the result to a file conveniently named bomb_status.

Now I had to do the fun part — making my Philips Hue lights turn on and change colors. I went step by step with the Philips Hue API Getting Started guide, and it was very easy to follow and understand the RESTful interface. Once I understood how to control the Hue lights, I wrote a 2nd Python script for reading the bomb_status file. In this 2nd script, I have a while loop that runs indefinitely, with a 250ms wait time in between each loop. In each loop iteration, it gets the bomb status and goes through a series of if-statements to determine what to do. Here’s the run down of it:

  • if bomb is planted: make each light blink red 1-by-1.
  • if bomb is planted and plant time has been >= 30 seconds: make all lights blink red every second.
  • if bomb is exploded: make all lights turn orange.
  • if bomb is defused: make all lights turn blue.
  • if bomb has no status: make all lights turn white.

And that’s basically it.

Final Result

My Python code is now on GitHub:
https://github.com/doobix/csgo-c4-hue

I have created a Node.js version of the same code, also on GitHub:
https://github.com/doobix/csgo-c4-hue-node

A video of the script in action is uploaded to YouTube:
https://www.youtube.com/watch?v=QBdI54MHB-k

Reddit thread:
https://redd.it/3wjpgg

csgo-c4-hue

Code School

If you’re interested in learning some programming, I recommend checking out some of the free courses on Code School. They have some very good instructional videos, and they give you assignments to do after each video is watched.

If you enjoyed the free courses, you can sign up through this special link to get full access for only $9 for the first month: http://mbsy.co/cr67n. After that, the price goes back to $29/month, but you can cancel the subscription any time.

seeglassdoor – My First Chrome Extension

After graduating from Hack Reactor last month, I’ve been looking for companies to apply to. One thing that I found myself doing a lot of was visiting a company’s website, and then going to Glassdoor to see how employees rate that company. So I thought, wouldn’t it be nice to have a 1-click button that would display the Glassdoor ratings on demand? It would save a lot of time opening a new tab, going to Glassdoor.com, and then searching for the company.

Seeing that no such extension existed in the Chrome Web Store, I decided to create one myself. I had modified existing Chrome extensions before, but this was my first time creating a brand new extension from scratch.

Introducing… seeglassdoor!

The most difficult part was to figure out how to grab a URL from a current tab.

To grab a URL of a tab that you are viewing, you need the “tabs” permission in your manifest.json  file:

"permissions": [
  "tabs"
]

Then in your javascript, you will be able to use this code:

chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
  var url = tabs[0].url;
  console.log(url);
});

Overall, this project was not a very difficult one, and was a good learning experience for my first Chrome extension. Feel free to take a look at my extension below.

Download seeglassdoor from the Chrome Web Store: https://chrome.google.com/webstore/detail/seeglassdoor/cgganckkpjppenjelhbimliplciledbb

GitHub seeglassdoor Project Repo: https://github.com/doobix/seeglassdoor

Quirky JavaScript Object Access

My friend asked me a simple JavaScript question last night… or so I thought.

The premise is that we have a javascript object like this:

var object = {
  1: {
    items: ['a','b','c']
  }
}

Now the question is, how do you access that items  array?

From what I know:

  • An object’s keys are stringified so that they are strings.
  • Using object.X  is the same as writing object[‘X’] .

So, I told my friend that we can access that items  array by using object.1.items  or object[‘1’].items .

However, object[‘1’].items worked, while object.1.items threw an error! To my surprise, object[1].items  also worked!

Now let’s say the javascript object is like this:

var object = {
  one: {
    items: ['a','b','c']
  }
}

In this second case, I tested it in the Chrome browser console and confirmed that it is how I originally thought it would work: object[‘one’].items  and object.one.items  both work (and object[one].items  does not work).

I think I will just use the object[‘X’]  format since it works in both cases: when the key is a number and when it is a word.

I still don’t know the reason for the quirkiness when the key is an integer, so feel free to leave a comment and let me know!

Pigeonhole Sort

Pigeonhole sort is an interesting sorting algorithm that makes use of an array filled with zeros and its indices. It can sort values in linear time and is relatively simple to implement. However, the tradeoff for its sorting speed is the space that it requires. Also, since it makes use of array indices, only positive integers can be sorted using this algorithm.

So the basic idea of pigeonhole sort is:

  • Create a new array (let’s call it pigeonholeArray) and fill it with zeros.
  • Iterate through the unsorted user’s array (inputArray).
  • For each value of the inputArray, add 1 to the index corresponding with the value in the pigeonholeArray.

Let’s say for this example, we want to sort this inputArray:

// inputArray:
[5, 2, 7, 5, 1]

The first thing the algorithm needs to know is what the maximum value of the inputArray is. We need to create a pigeonholeArray that has indices that go up to the max value. If we don’t know the maximum, we’ll have to loop through the inputArray one time to figure it out. In this case, our biggest value is 7, thus we need to create a pigeonholeArray that can hold 8 elements (which will have indices 0 to 7).

// pigeonholeArray:
[0, 0, 0, 0, 0, 0, 0, 0]

Now we need to loop through the unsorted inputArray (again), and as we do that, we will add 1 to the pigeonholeArray’s value at the pigeonholeArray’s index corresponding with the inputArray’s value. So as we iterate our input array, our pigeonhole array will look like this:

// Progress of pigeonholeArray
[0, 0, 0, 0, 0, 1, 0, 0] // when the inputArray value is at 5
[0, 0, 1, 0, 0, 1, 0, 0] // when the inputArray value is at 2
[0, 0, 1, 0, 0, 1, 0, 1] // when the inputArray value is at 7
[0, 0, 1, 0, 0, 2, 0, 1] // when the inputArray value is at 5
[0, 1, 1, 0, 0, 2, 0, 1] // when the inputArray value is at 1

As you can see, after looping through the entire inputArray, our pigeonholeArray will contain all of inputArray’s values. We will just have to make one last iteration through the pigeonholeArray. For each pigeonholeArray’s value that is greater than zero, we’ll add the pigeonholeArray’s index to the inputArray. We can replace the old values in the inputArray so that no new array needs to be created.

Check out the completed algorithm below:

function pigeonholeSort(inputArray) {
  // Find the max value in inputArray
  var max = inputArray[0];
  for (var i = 1; i < inputArray.length; i++) {
    if (max < inputArray[i])
      max = inputArray[i];
  }

  // Create pigeonholeArray filled with zeros
  var pigeonholeArray = [];
  for (var i = 0; i <= max; i++) {
    pigeonholeArray.push(0);
  }

  // Iterate through inputArray and add 1's to pigeonholeArray
  for (var i = 0; i < inputArray.length; i++) {
    pigeonholeArray[inputArray[i]]++;
  }

  // Iterate through the pigeonholeArray and replace values in inputArray
  var inputArrayIndex = 0;
  for (var i = 0; i < pigeonholeArray.length; i++) {
    // Add to inputArray until the current pigeonholeArray index is 0
    while (pigeonholeArray[i]) {
      inputArray[inputArrayIndex] = i;
      pigeonholeArray[i]--;
      inputArrayIndex++;
    }
  }

  // return sorted inputArray
  return inputArray;
}

How the JavaScript Event Loop Works

Event Loop

JavaScript has this system called an event loop. I’m not sure if my idea of it is exactly correct, but I will try to explain it from what I can recall from the lecture.

So basically, the event loop is something that is continuously running in the background of your website only if there is nothing in the call stack. It does 3 things:

  1. Invokes functions sitting in the call stack.
  2. Checks the event table for functions.
  3. Checks the message queue for functions.

Call Stack

The call stack contains functions that need to be invoked. If the functions have any callbacks, those will get added into the event table to be invoked when it is time to do so.

Example: Let’s say the current time is 2:00:00pm, and the call stack contains this function that logs “Hello World” after 1 second.

setTimeout(console.log("Hello World"), 1000);

That will get invoked, and the callback function console.log(“Hello World”)  would be added to the event table with the time set to 2:00:01pm.

Event Table

The event table stores more functions with the specific time that they should be ran. If the time in the table is greater or equal to the current time, then the function will be moved to the message queue.

Example: console.log(“Hello World”)  would only be added to the message queue when the clock has turned to 2:00:01pm.

Message Queue

Lastly, the message queue contains functions that are next in line to be ran. These functions will be popped off the queue and be added to the call stack.

Example: console.log(“Hello World”)  will be moved into the call stack. This completes a full circle, and the event loop will repeat again.

My Two-Day Solo MVP Project

YouTube Live

For my two-day solo Minimum Viable Product Project, I decided to make a website for sharing synchronized YouTube videos. This website would be good for people who want to watch a YouTube video together, at the same time, but aren’t in the same room. (Or maybe they are in the same room, but want to watch on separate devices.)

The user who enters a URL (a host) can play, pause, and seek the video. The users who watch the video (the viewers) will have their embedded players behave exactly the same as the host’s player. Pretty simple.

Scoping

My original idea had more features, but I quickly learned that I had to cut down the scope, and then cut it down again for it to be do-able in two days. I was going to have user sessions/authentication, rooms, and a chat box. I was also going to use Angular. But all those went away.

Requirements

The tools that I ended up using for this project are:

  • Node.js for the server
  • Firebase for the database
  • jQuery for getting the URL from the input box
  • YouTube Player IFrame API

Player API Functions

After embedding the YouTube player on my website, I needed access to some of the functionality. With the API, I was able to use these functions:

First, I use loadVideoById()  to load a YouTube video when a URL is submitted.

Then when the host’s player starts playing, the viewers will invoke playVideo() , otherwise pauseVideo()  in all other cases. I don’t use stopVideo()  because that will stop the player from buffering.

When the host is in paused state and moves the video to a new time position, this will invoke getCurrentTime() . The viewers will invoke seekTo()  to be at the new location.

Database

I’ve spent 15 minutes playing with Firebase during one of the other projects before, so I know how easy it is to set and get data. I love how Firebase automatically sends a trigger back to the website when a value is changed in the database.

In my Firebase database, I store 3 things:

  1. The state of the host’s video player (playing, paused, buffering, etc.)
  2. The YouTube video ID that the host is watching
  3. The time position of the host’s video player

Whenever host played or paused a video, Firebase sent a trigger which would invoke a callback function to play/pause all of the viewers’ video players. It was pretty simple.

If the host seeked the video ahead, Firebase would get the new time position, which would then again trigger a callback on the viewers’ video players and move them to the new time.

Same thing will happen when the host enters a new YouTube video ID.

Extra Feature

I also had some extra time, so I was able to add a search feature. The host can input search terms, and the first video result of the query will be loaded.

This is done by using the loadPlaylist()  function and passing this object for the argument:

{
  listType: 'search',
  list: 'the keywords to search'
}

Using this function will actually load a playlist of the first 20 videos found in the search, which I don’t want. So when the first video is playing, I invoke getVideoUrl()  to get the video’s URL, then I save the video ID to Firebase, and then play the video by ID to clear the playlist.

Stumbling Block

An obstacle that I ran into about 1 hour into the project was that I chose to use the YouTube Player JavaScript API. That doesn’t seem like a problem at all, right? Well, it wouldn’t be if the JavaScript API was able to embed the HTML5 video player. However, it only embeds the Flash video player, which iPhones and Androids don’t support! So the solution was kind of simple; I switched to the YouTube Player IFrame API.

Final Result

I deployed my code onto the Microsoft Azure platform, and you can view it here: http://youtube.azurewebsites.net/

YouTube Live Player

Scheduling PHP Cron Jobs with Parameters

I was making a PHP script for reading a resource account’s calendar from an Exchange server and then saving the data to a MySQL database. This script was going to be a cron job on the web server set to run every 2 minutes, and I was going to have multiple cron jobs for reading different resource account calendars.

Instead of having separate cron files for reading each account, I made one cron_rooms.php file that I could use with the HTTP $_GET variable. The PHP script worked great through the web browser, and the URL was something like this: http://website.com/cron_rooms.php?room=[name]

To schedule the cron jobs, I added them easily through the webmin web control panel in the browser.

webminCron

To do it through command line in SSH, I’d run this command: crontab -u <username> -e

Then I’d enter the lines below for updating my 5 rooms every 2 minutes.

0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58 * * * * /usr/bin/php -q /var/www/cron_rooms.php?room=dsv #Update DSV
0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58 * * * * /usr/bin/php -q /var/www/cron_rooms.php?room=9991 #Update 9991
0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58 * * * * /usr/bin/php -q /var/www/cron_rooms.php?room=9992 #Update 9992
0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58 * * * * /usr/bin/php -q /var/www/cron_rooms.php?room=9993 #Update 9993
0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58 * * * * /usr/bin/php -q /var/www/cron_rooms.php?room=9994 #Update 9994

So anyway, all is good and ready to go, right?… Wrong!

When the cron job runs, it’s giving me these errors:

Could not open input file: /var/www/cron_rooms.php?room=dsv
Could not open input file: /var/www/cron_rooms.php?room=9991
Could not open input file: /var/www/cron_rooms.php?room=9992
Could not open input file: /var/www/cron_rooms.php?room=9993
Could not open input file: /var/www/cron_rooms.php?room=9994

Why was the script working in the browser, but not in cron? Simple, the script I made was using HTTP GET requests that works for URL’s. When running in command line mode (which is what cron is using) I need to pass the parameters using arguments.

Solution: I replaced the $_GET[‘room’] variable in my script with $argv[1]. Then change each line in cron from this:

/usr/bin/php -q /var/www/cron_rooms.php?room=[name]

to this:

/usr/bin/php -q /var/www/cron_rooms.php [name]

In command line mode, $argv[0] will always contain the path of the script: “/var/www/cron_rooms.php”

And $argv[1] would be the first argument after the path: [name]

If we had more arguments, we can access them with $argv[2], $argv[3], etc.

Bash Script Not Writing to File

I was making a bash script, cron_its_servicenow.sh, that uses cURL and regex to grab 6 chart images (that are encoded in base64) from 6 ServiceNow published reports, and then writing them to a file, page_its.php. Here’s the code:

#!/bin/bash

file=/var/www/dashboard/it/page_its.php

urls=(
	"https://*.service-now.com/sys_report_display.do?sysparm_report_id=*1*"
	"https://*.service-now.com/sys_report_display.do?sysparm_report_id=*2*"
	"https://*.service-now.com/sys_report_display.do?sysparm_report_id=*3*"
	"https://*.service-now.com/sys_report_display.do?sysparm_report_id=*4*"
	"https://*.service-now.com/sys_report_display.do?sysparm_report_id=*5*"
	"https://*.service-now.com/sys_report_display.do?sysparm_report_id=*6*"
)

for (( i = 0 ; i < ${#urls[@]} ; i++ )) do
	result[$i]=`curl -s ${urls[$i]} | sed -rn 's/.*<img usemap="#" class="chart" src="(.+)"><\/img>.*/\1/p'`
done

echo '<div class="row shadow">' > $file
echo '<h3>IT Services</h3>' >> $file
echo '<div class="col-md-4"><img src="'${result[0]}'" class="img-responsive" /></div>' >> $file
echo '<div class="col-md-4"><img src="'${result[1]}'" class="img-responsive" /></div>' >> $file
echo '<div class="col-md-4"><img src="'${result[2]}'" class="img-responsive" /></div>' >> $file
echo '</div>' >> $file
echo '<div class="row shadow">' >> $file
echo '<div class="col-md-4"><img src="'${result[3]}'" class="img-responsive" /></div>' >> $file
echo '<div class="col-md-4"><img src="'${result[4]}'" class="img-responsive" /></div>' >> $file
echo '<div class="col-md-4"><img src="'${result[5]}'" class="img-responsive" /></div>' >> $file
echo '</div>' >> $file

It was working fine when I was running it in SSH like this:

root@server ~# /var/www/dashboard/it/cron_its_servicenow.sh

No errors, and the page_its.php file was getting the data written to it.

But then I logged into the web control panel and set up the Scheduled Cron Job, all these errors popped up when I ran the cron job:

Execute Cron Job
Output from command /var/www/dashboard/it/cron_its_servicenow.sh ..

/var/www/dashboard/it/cron_its_servicenow.sh: line 18: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 19: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 20: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 21: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 22: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 23: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 24: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 25: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 26: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 27: /var/www/dashboard/it/page_its.php: Permission denied
/var/www/dashboard/it/cron_its_servicenow.sh: line 28: /var/www/dashboard/it/page_its.php: Permission denied

What??

I couldn’t find out what was wrong. I was trying things like changing echo to cat, adding tee, using sudo, etc. It turns out that it was such a simple mistake… I forgot to set the file permissions of page_its.php to be writable.

Solution: I did chmod 666 page_its.php, and all is working well now.

Execute Cron Job
Output from command /var/www/dashboard/it/cron_its_servicenow.sh ..

No output generated

Excellent!

  • 1
  • 2