PHP, Ramblings, Web Development

Why I refuse to earn money from Facebook games

I was recently approached, through various channels, to co-found a Facebook game.

Essentially the primise of it is to make something like Tetrus or Diamonds and add micro-transactions for bonuses and what not to make the users life on the game easier.

I will get right to the point and state that I turned down the offer.

Sure it is a quick buck, use about 5 hours making the game, £300 of Facebook advertising/marketing and “bingo”, you have something that could earn you double your normal wage in the space of a year. An easy and sustainable income if I may be honest. Once every few years you release a new game and solely you can hold down a wage. No need to work for Zenga or one of those other big companies, just you can earn the money.

So with the last paragraph in mind it seems like a no brainer to make a Facebook game and earn some easy money, and many do, just look at how many games there are; with an added audience of 1 billion users how can you go wrong? There will always be a suckers addiction and/or misery to feed off.

Wait, what? Addiction and/or misery? Did I really learn to program to do this? And this follows onto my reasoning for refusing to make this game.

Sure I could make this game, sure I could earn a lot of money, but can I really?

Like many others, I learnt to program in my spare time because I wanted to, not to make money. I was in the final year of my university Honors degree in Computing and I had to come up with a final project idea so I chose to make a YouTube replica from PHP. I had never touched PHP before that time nor any web development frankly, aside form a small Java project in my first year to make a simple HTML website running on Tomcat.

To cut to the point: despite doing all that work and still being one of the few in the class who actually completed a project I got a shitty grade; 41.4% in fact which almost failed me (39% would have been a fail) and caused me to lose the 2:1 mark I was heading for.

But that didn’t stop me and I went onto a job which involved that exact work and I can feel pride in my work.

How, after everything I went through to get this expertise and knowledge, could I feel pride in something that feeds on misery? I think I probably speak for quite a few programmers when I say that this isn’t what we learnt to program for.

That’s why I feel sorry for people who work for Zenga, they have to work knowing that they have to take pride in something that brings addiction and misery to many millions of Facebook users, stealing their money in the process; their programs are effectively a plague upon the internet. Maybe that is why I keep hearing such low morale at the company in question, aside from management issues.

Every worth while programmer knows that they need to feel pride for what they have accomplished, sure there will be those who learn to program to “get ahead” in the market or to earn money. But the real programmers, those who make the internet what it is today love to program and they need to feel pride in their work and what they do and know that it is benefiting others, not sucking their money using their misfortunes as an excuse.

I could not feel pride in making a Facebook game that makes a quick buck, though I must admit myself that the thought of doing such a thing is tempting, but for the minute I will resist. I will not convert over to the dark side and become effectively Lord Vader. I still have some moralities left in me.

As such I regretfully but honestly decline to make a Facebook game.

JQuery

Facebook dropdown menu using Jdropdown

This now has a github home: here. I’m new to Github so sorry if it’s not done right 😛

Please Note: You must have the latest version of jQuery to use this plugin (v 1.7.x and above). Please get this before you attempt to use this plugin otherwise the plugin will not work.

Ok so here is an update to the article about facebook dropdowns.

This time I have made an actual plugin that can handle dropdown menus from a single anchor. I made this plugin since there was, tbh, no real decent plugin for it surprisingly. There are plugins for nav bars, select boxs, option boxes, etc but none for making a simple menu from a random anchor on your page.

So here it is, my plugin version of the facebook dropdown: facebook dropdown example. The first dropdown shows with items provided in the plugin declaration and the second shows the items provided in the html code of the anchor.

The page source code will show you everything you need to know about the how it is styled etc but I will walk you through a little of how the plugin works and how it actually does stuff.

So the plugin itself is called Jdropdown (can be written with upper and lowercase j), you can call the plugin on any element like so $(‘.someel’).jdropdown({ ‘container’: ‘somecontainer’ }) (And I mean almost any element, this thing has little restrictions) and it takes 3 params atm:

  • container – This is the jQuery selector (minus the $() around the selector since thats not actually part of the selector) for the item which the menu items will be put in.
  • orientation – This param tells the script whether to align the menu to the left or right of its anchor and it takes either “left” or “right” (will make this more comlpex soon but atm its just that).
  • items – This params is optional and it is to tell the widget what items will be in the menu. The value is an array of object arrays (i.e. [{//item one}, {//item two}]) and any data can be put into these items but there must be at least a “label” field if your not using your own renderItem function in which case anyhting can be done

Now the plugin has currently two functions you can override:

  • renderMenu – This basically gives off some html for the items to go into in the form $(‘<ul></ul>’)
  • renderItem – This works a lot like jQuery UI plugins and it just appends each item to the menu container

Please note: If you override the renderMenu function with complex html you may be required to also override the renderItem to append/prepend to the correct element within that menu.

Now the plugin also has a number of api functions which you can hook into:

  • open – is triggered when the menu opens. This has a $(this) of the anchor which holds all data in the data() function about the menu
  • selectItem – is triggered when an item in the menu is selected. It has a $(this) of the menu item , its parent <li/>
  • holds all information in its data() function about the item
  • close – This is triggered upon close the menu and because of its currently global scope, has no $(this)

Now for an example of how to use one of these events lets draw code from the example:

$(document).on('jdropdown.selectItem', '#fb_menu a', function(e){
    e.preventDefault();
    alert('You selected: '+$(this).parent().data('jdropdown.item').label);

    // Do some crazy AJAXy weirdy stuff here to make your app awesome
});

This function binds to document that when a item in the #fb_menu container is clicked it will run that function.

Ok so that is the basic description of the plugin. Now I will state a few last words and then leave any further definition upto comments.

  • As I said items is optional in the plugin definition this is because you can actually attach items to the anchors themselves allowing you to have the same styled menu with different options without having to have duplicated code etc etc. The simple way of doing this is to add a “data-items” attribute to your anchors html and make the value a (valid to: parseJSON Doc) JSON string. An example is shown in the example link at the top (its the second drop down, check its html).
  • Also note the item itself can take ANY data and the jdropdown plugin will just attach that data to the data() of the li for that item allowing you to access it when it is selected. So for example you can place “id” in there or “woop” with values of 3 and access it like so: $(this).parent().data(‘jdropdown.item’).id .
  • You must put the container into the HTML page first, best place is at the end of the doc just before the </body> tag
  • You can call this on almost any element so long as you have provided items in some manner by using (for example) $(‘.someel’).jdropdown({ ‘container’: ‘somecontainer’ })

I can’t think of anything else off the top my head. So I am gonna leave it at that for the minute.

The example shows two drop downs, top one with full constructor with items provided in it and the second having items in the html of the object.

Have fun and any questions or if you need clarification etc just post a comment.

P.S almost forgot the plugin source code:

/**
 * Jdropdown
 *
 * @author Sam Millman
 * @licence GNU or whatever
 *
 * This plugin basically allows you to connect a menu to an anchor.
 * This anchor can be literally anything, from a <div/> to an <a/> and even a <li/>.
 */
/**
 * Jdropdown
 *
 * @author Sam Millman
 * @licence GNU or whatever
 *
 * This plugin basically allows you to connect a menu to an anchor.
 * This anchor can be literally anything, from a <div/> to an <a/> and even a <li/>.
 */
(function($){
	var methods = {
		init: function(options){
			return this.each(function(){
				var $this = $(this),
				items = $this.data('items');

				if(!$this.data('jdropdown')){
					$(options.container).addClass("jdropdown-menu");
					$(this).addClass('jdropdown-anchor').data('jdropdown', {
						'items': typeof items === 'object' ? items : options.items,
						'anchor': $(this),
						'menu': $(options.container),
						'options': options
					}).on({ 'click': open });
				}
				return this;
			});
		},
		destroy: function(){}
	},
	open = function(event){
		event.preventDefault();
		if($(this).hasClass('jdropdown-active')){
			close();
			return;
		}else{
			close();
		}

		var data  = $(this).data('jdropdown'),
		offset = $(this).offset(),
		container = data.menu;
		container.data('jdropdown', data);
		container.empty();

		if($.isFunction(data.renderMenu)){
			if($.isFunction(data.renderItem)){
				var ul = data.renderItem(data.renderMenu(), data.items);
			}else{
				var ul = renderItem(data.renderMenu(), data.items);
			}
		}else{
			if($.isFunction(data.renderItem)){
				var ul = data.renderItem($( '<ul></ul>' ), data.items);
			}else{
				var ul = renderItem($( '<ul></ul>' ), data.items);
			}
		}
		ul.appendTo( container );

		if(data.options.orientation == 'left'){
			data.menu.css({
				'position': 'absolute',
				'left': offset.left,
				'top': (offset.top + $(this).outerHeight()),
				'display': 'block'
			});
		}else{
			data.menu.css({
				'position': 'absolute',
				'left': (offset.left - container.outerWidth()) + $(this).outerWidth(),
				'top': (offset.top + $(this).outerHeight()),
				'display': 'block'
			});
		}
		$(this).addClass('jdropdown-active').trigger('jdropdown.open');
	},
	renderItem = function(menu, items){
		$.each(items, function(i, item){
			$('<li></li>').data('jdropdown.item', item).append(
				$( "<a></a>" ).attr({
					'href': '#', 'class': item['class']
				}).text( item.label ).on({ 'click': selectItem })
			).appendTo( menu );
		});
		return menu;
	},
	selectItem = function(e){
		close();
		$(this).trigger('jdropdown.selectItem', e);
	},
	close = function(){
    	$('.jdropdown-menu').css({ 'display': 'none' }); //hide all drop downs
    	$('.jdropdown-anchor').removeClass("jdropdown-active");
		$(this).trigger('jdropdown.close');
	};

	$(document).on('click', function(e) {
	    // Lets hide the menu when the page is clicked anywhere but the menu.
	    var $clicked = $(e.target);
	    if (!$clicked.parents().hasClass("jdropdown-menu") && !$clicked.parents().hasClass("jdropdown-anchor") && !$clicked.hasClass("jdropdown-menu") && !$clicked.hasClass("jdropdown-anchor")){
	    	close();
		}
	});

	$(window).resize(function(){
		if($('.jdropdown-active').length > 0){
			var offset = $('.jdropdown-active').offset(),
				data  = $('.jdropdown-active').data('jdropdown'),
				container = data.menu;

			if(data.options.orientation == 'left'){
				data.menu.css({
					'position': 'absolute',
					'left': offset.left,
					'top': (offset.top + $('.jdropdown-active').outerHeight()),
					'display': 'block'
				});
			}else{
				data.menu.css({
					'position': 'absolute',
					'left': (offset.left - container.outerWidth()) + $('.jdropdown-active').outerWidth(),
					'top': (offset.top + $('.jdropdown-active').outerHeight()),
					'display': 'block'
				});
			}
		}
	});

	$.fn.jdropdown = function(method){
		// Method calling logic
		if ( methods[method] ) {
			return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
		} else if ( typeof method === 'object' || ! method ) {
			return methods.init.apply( this, arguments );
		} else {
			$.error( 'Method ' +  method + ' does not exist on jQuery.j_slider' );
		}
	};
})(jQuery);

Notes:

Added bug fix for resizing the window.

PHP

Login User for a Facebook Application

This tutorial will teach the user how to use the Facebook platform to control user logins to their Facebook application.
//some more content

Here is an example class of what I created for some other random app. As you can see I have created a class (acting much like a wrapper really) around the Facebook login method: facebook->require_login(). This particular app required the user to be registered on their own database after connecting through Facebook. The function _f_checkRegistered($fb_user) will check first time registration, this could act like a start screen for a new player to a Facebook game for example.

<?php
class Facebook_User{
	
	var $db = null;
	var $msg = '&nbsp;';
	var $date;
	var $ip = '';
	var $myKey = '';
	var $facebook = null;
	var $user_details = '&nbsp;';
	
	function Facebook_User(&$db, &$facebook){
		$this->db = $db;
		$this->facebook = $facebook;
		$this->date = $GLOBALS['date'];
		$this->ip = $_SERVER['REMOTE_ADDR'];
		$this->myKey = "54M_i1lM4N";
		
		$expires = $facebook->session_expires;
		$time = time();
		if($expires < $time){
			//$facebook->expire_session();
		}
		if($_SESSION['logged']){
		}else{
			if($_SESSION['F_LOG']){
				if($_SESSION['logged']){
					$this->_f_sessionDefaults();
				}
				$this->_f_checkSession();
			}
		}
	}
	
	function generatePassword() {
		$length=9; 
		$strength=8;
		$vowels = 'aeuy';
		$consonants = 'bdghjmnpqrstvz';
		if ($strength & 1) {
			$consonants .= 'BDGHJLMNPQRSTVWXZ';
		}
		if ($strength & 2) {
			$vowels .= "AEUY";
		}
		if ($strength & 4) {
			$consonants .= '23456789';
		}
		if ($strength & 8) {
			$consonants .= '@#$%';
		}
	 
		$password = '';
		$alt = time() % 2;
		for ($i = 0; $i < $length; $i++) {
			if ($alt == 1) {
				$password .= $consonants[(rand() % strlen($consonants))];
				$alt = 0;
			} else {
				$password .= $vowels[(rand() % strlen($vowels))];
				$alt = 1;
			}
		}
		return $password;
	}
	
	function linEncrypt($pass) {
		$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
		$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); //Creating the vector
		$cryptedpass = mcrypt_encrypt (MCRYPT_RIJNDAEL_256, $this->mykey, $pass, MCRYPT_MODE_ECB, $iv);
		return $cryptedpass;
	}
	
	
	function _f_sessionDefaults(){
		$_SESSION['uid'] = 0;
		$_SESSION['name'] = '';
		$_SESSION['FP_TEMP'] = '';
		$_SESSION['F_LOG'] = false;
	}
	
	function _f_checkSession(){
		
		$facebook_uid = $this->db->quote($_SESSION['uid']);
		$fullName = $this->db->quote($_SESSION['name']);
		
		$sql = "SELECT * FROM facebook_User WHERE UID = $facebook_uid AND Name = $fullName";
		
		$result = $this->db->getRow($sql);
		
		if(is_object($result)){
			if($_SESSION['FP_TEMP'] == md5($result->TMP_PW)){
				$this->_f_login();
			}else{
				$this->_f_sessionDefaults();
				return false;				
			}
		}else{
			$this->_f_sessionDefaults();
			return false;
		}
		
	}
	
	function _f_login(){
			
		$fb_user = $this->facebook->require_login();
		$this->_f_checkRegistered($fb_user);
	}
	
	function _f_Logout(){
		$this->facebook->expire_session();
		$this->facebook->clear_cookie_state();
	}
	
	function _f_assignCred($facebook_uid, $fullName){
		
		$TMP_PW = $this->linEncrypt($this->generatePassword());
		$F_PWTMP = $this->db->quote($TMP_PW);
		
		$_SESSION['uid'] = $facebook_uid;
		$_SESSION['name'] = $fullName;
		$_SESSION['FP_TEMP'] = md5($TMP_PW);
		$_SESSION['F_LOG'] = true;
		
		$sql = "UPDATE facebook_User SET TEMP_PW = $F_PWTMP WHERE UID = $facebook_uid AND Name = $fullName";
		$this->db->query($sql);
		
	}
	
	function _f_checkRegistered($fb_user){
		
		$user_details = $this->facebook->api_client->users_getInfo($fb_user, array('name'));
		$facebook_uid = $this->db->quote($fb_user); 
		$fullName = $this->db->quote($user_details[0]['name']);
		
		$sql = "SELECT * FROM facebook_User WHERE UID = $facebook_uid AND Name = $fullName";
		$result = $this->db->getRow($sql);
		
		if(is_object($result)){
			$this->_f_assignCred($facebook_uid, $fullName);
			return true;
		}else{ 
			return false;
		}
	}
	
	function _f_URegister($fb_user){
		//XXX @todo: make registration script
	}
}
?>

I should just mention this class is incomplete so don’t blame me if all of it does not work. But this is the basic structure I use for Facebook apps.