Parse and the future of “free”

If you’re reading this, then you’re aware that the Facebook-backed super-giant cloud provider Parse.com is calling for clear skies.

As anyone with a geekery DNA will tell you quite plainly; “Don’t Panic!”

Parse will be closing out over the course of the year. You have some buffer to calculate your next move. Over the next few months we’ll see a deluge of services vying for your data, with simple one-click import tools galore. The worst possible decision a developer could make now is to start frantically jumping to another set of even less well-backed 3rd party services, or worse yet, hobbling together multiple 3rd party services to create “stacks” to match Parse functionality.

Over the next few months we’ll see a deluge of services vying for your data, with simple one-click import tools galore.

It appears that cloud as a core business is not profitable. There have been other shutdowns as well, though not quite as publicized as Parse. As accustomed as we developers are at having free access to almost any functionality, we all know there is no such thing as free.  At the end of the day, someone, somewhere, is paying for it. Either with time, or actual capital.

The conversation has to be happening in companies all over. How do we monetize now that we’ve created this “culture of free” for developers?

At Corona Labs* we’ve been having this conversation not only internally, but with the greater (and great!) Corona SDK community as well. I think it’s imperative for companies to engage directly with the users of their products if the model has any hope of changing. Be prepared for a healthy debate. Change doesn’t come easy.

There is a saying; “A rising tide lifts all boats.” It is associated with the idea that improvements in the general economy will benefit all participants in that economy. This is the model that Corona Labs is working with its community to adopt. The idea that we all share in the success of the platform. If developers succeed, the platform gets even better, offering even more ways for developers to make even more money.

And from a company perspective, this is a catalyst to provide the best product possible. The Corona Labs team have every reason to make a developer as effective and profitable as we can. This is a good thing. When the tool makes you successful, you in turn are investing in, and guaranteeing that the tool will be around so you can keep bringing in revenue.

This leads me to Corona Labs upcoming Corona Cloud offering. Another tool in the ever expanding Corona SDK platform. And guess what? It’s not a core business. Draw your own conclusions, but there is safety in being part of a successful platform. Not the entire platform, but part. Which brings me back to boats. Better get one.

* Full disclosure: I’m the Lead Software Engineer of Cloud Services at Corona Labs. All views, opinions, and  grammatical errors are my own though.

 

 

Adding gutter padding in Atom editor

I love Atom Editor. I’m a recent convert from Sublime, which in and of itself is an amazing product as well; for $70 dollars. Atom is free as a bird, and backed by GitHub. I would go on singing its praises, but that might disturb the cat.

Anyway, one of the things I disliked was no setting for padding between the line numbers and code. I personally need a little breathing room.

It’s surprising that this is not a built-in setting, but supposedly its up to the theme designer.

Here is an example of what I am talking about:

I can't breath!
I love you, but I need more space.

Not to worry, as it turns out the solution is very simple.

Select Open Your Stylesheet… from the Atom menu.

Add the following css code to the file:

atom-text-editor::shadow .gutter {
 margin-right: 12px;
}

And you’re done!

Ahhh...breathing room.
Ahhh…breathing room.

[symple_highlight color=”blue”]Be sure to check out Atom Editor if you’re looking for something new to try, or want a highly capable free editor.[/symple_highlight]

Enjoy, and happy coding.

Coronium GS Panel

Screen Shot 2014-11-26 at 9.11.25 PM

If you’re running the Coronium Game Server version 1.92 or above, you can install a front-end interface to it for monitoring GS connections, etc.

Before starting, be sure to download the latest Coronium GS Bundle for access to device statistics.

To install the panel, log into your Coronium GS instance via SSH, then copy and paste the following into the terminal, and press [Enter].

sudo wget https://s3.amazonaws.com/coronium-gs/gs-panel.sh; sudo chmod +x gs-panel.sh; sudo ./gs-panel.sh

Wait for the installer to finish, then open the GS panel at http://your.coronium-gs.ip:8173

The GS panel default login is admin and coroniumgs

Amazon EC2 users need to add TCP port 8173 to the security profile to access the GS panel.

The framework for the upcoming load balancer feature is also installed with the GS panel.
Coronium Game Server http://coronium.io/gs

Mastering Coronium GS

mastercourse

Enjoy over 2 1/2 hours of HD video training, and prepare to create your first multiplayer game using the free Coronium Game Server for use with Corona SDK application and game development.

You can watch the whole playlist below, or on YouTube.

Learn more at http://coronium.io/gs

Be sure to turn the quality up to 720p (HD) for the clearest picture.

Chapters

  1. Installing
  2. Server Overview
  3. Client Overview
  4. Server Control
  5. Client Events
  6. Game Events
  7. Game Object
  8. Running Games
  9. Auto-Negotiation
  10. Ending Games
  11. Lobby Chat
  12. Server Classes
  13. Client Classes
  14. Timers
  15. The Prototype

Coronium Cloud 1.92 – Hello Ubuntu!

logo-ubuntu_st_no®-orange-hex

Coronium Cloud 1.92 is a major maintenance and OS release that is highly recommended for all Coronium users.

The Coronium installer has been rebuilt from the ground up to work on Ubuntu 14.04 LTS based servers for greater flexibility and distribution. All of the patches since 1.91 have been incorporated, providing updates to the Mongo module, file upload, and more.

As an added bonus to Amazon EC2 users, instances now cost 60% less to operate than version 1.91, so it’s in your best interest to move over to this release, especially if you’re not on a free Amazon AWS tier.

Ubuntu is a prolific Linux operating system, and while I still recommend DigitalOcean for hosting, Ubuntu is available on almost every major cloud provider, giving you more options for installation locations.

To get started just visit http://coronium.io and follow the instructions.

More details on the Coronium Community forums: http://forums.coronium.io/discussion/96/coronium-cloud-1-92

Enjoy!

Coronium Cloud Connector for Coronium GS

I’ve put together a simple module for Coronium GS that allows you to talk to a Coronium Cloud instance and use its Lua cloud code feature. It’s usage is fairly simple and can be found in the “extras” folder of the Coronium GS Bundle.

To use the module, first place it in the Coronium GS modules folder on your server instance. Then you can include it on your server pages like so:

local Cloud = require( 'CoroniumCloud' )
local cc = Cloud:new( 'your.coronium.ip', 'YOUR_CORONIUM_API_KEY' )

You can now set up the listener, and call the run method and get a result back like so:

local function onCloudData( data )
  p( data.result.key )
end
cc:once( "CloudData", onCloudData )
cc:run( 'my_cloud_file', { param = value } )

The run method is similar to the Coronium Cloud client run method and takes a Lua cloud file name (without the .lua) and an optional parameters table to send along. Notice how the “cc” data listener uses the once method, not the more common on method.

Let’s look at an “echo” example set up:

Coronium Cloud

On the Coronium Cloud server we set up a cloud code file called echo.lua, and place it on the server in the lua directory:

--== Coronium Cloud - lua/echo.lua ==--
local in_data = coronium.input()
coronium.output( coronium.answer( in_data ) )

Coronium GS

--== Coronium GS - main.lua ==--

--== Add and set-up Coronium Cloud connector
local Cloud = require( 'CoroniumCloud' )
local cc = Cloud:new( 'my.coronium.io', 'MY-API-KEY' )

local function onClientData( client, data )
  if data.do_echo then
    --== Set up "once" listener and call code
    cc:once( "CloudData", function( event )
      --== Send Cloud result to GS client
      client:send( event.result )
    end )
    cc:run( 'echo', data )
  end
end

You can find the Coronium Cloud connector in the “extras” folder in the latest Coronium GS Development Bundle.

If you have any questions please visit the Coronium Cloud Community.

Cheers.

Introduction to Coronium GS Alpha 2

Spin up a Coronium GS Instance

See http://coronium.io/gs for the most current installation options.

Download the Development Bundle

Download the Coronium GS Development Bundle from here.

Controlling the Server

Once you have logged into your Coronium GS instance via SSH, you can issue the following commands to control the server state:

You must restart Coronium GS each time you update a server file.

Start Coronium GS

sudo service gs start

Restart Coronium GS

sudo service gs restart

Stop Coronium GS

sudo service gs start

The Log File

From the command line, you can view the log file like so:

sudo tail -f ~/gs.log

Pretty Printer

On both the server and client side you can use the Pretty Printer to output tables, and other values in a human readable format.

  p( some_table_of_data )

Server and Client main.lua Templates

Server main.lua template can be viewed here.

Client main.lua template can be viewed here.

Coronium GS Events

Coronium GS is an event based framework. Every action the server makes can be listened for using the event listener callbacks. Both the server and client share similar events, making multi-user development faster.

The server and the client both use a main.lua file are interfaces to the Coronium GS actions, and provide a template for your application events. You can see and an example file for the Server/main.lua here, and the Client/main.lua here.

Server-Side Events

Set up the Coronium GS server listener

local gs = require( 'CoroniumGS' ):new( 7173, 'abc' )

ClientConnect

A client has connected to the server. They are not currently in a game yet.

gs:on( "ClientConnect", function( client )
  p( client:getHost() )
end )

ClientData

The client has sent a data table to the server. You can read these values to determine what to do next. The client may or may not be in a game. You can check the client:getGameId() for a value to know if they are in a game. It will be nil if they are not in a game.

gs:on( "ClientData", function( client, data )
  p( data.some_table_key )
end )

ClientTimeout

The client has timed-out because of inactivity. The connection is closed.

gs:on( "ClientTimeout", function( client )
  p( "client timed out" )
end )

ClientError

The client has had an error in the connection. The connection is closed.

gs:on( "ClientError", function( client, error )
  p( "error: " .. error )
end )

ClientClose

The client connection has closed.

gs:on( "ClientClose", function( client )
  p( "client closed" )
end )

GameCreate

A client has created a new game using the client-side createGame() method. The client is now a player as well. The game has not started.

gs:on( "GameCreate", function( game )
  p( game:getId() )
end )

GameJoin

A client has joined a game using the client-side joinGame() method.

gs:on( "GameJoin", function( game, player )
  p( game:getId(), player:getId() )
end )

GameStart

The game has the proper amount of players and is now running. All players are connected.

gs:on( "GameStart", function( game, players )
  p( game:getId(), #players )
end )

GameLeave

A player has left the game. The game is still running, but you’ll need to handle the player leaving.

gs:on( "GameLeave", function( game, player )
  p( game:getId(), player:getId() )
end )

GameClose

A game has no more players present and will be cleared from the system.

gs:on( "GameClose", function( game_id )
  p( game_id )
end )

Client-Side Events

You can set up listeners with the client-side “events” key.

ClientConnect

The client has connected to the server successfully.

gs.events:on( "ClientConnect", function()
  p( "client connected" )
end )

ClientData

The server has sent a data table to the client.

gs.events:on( "ClientData", function( event )
  p( event.data.some_table_key )
end )

ClientPing

The server has responded to a client ping.

gs.events:on( "ClientPing", function( event )
  p( "timestamp: " .. event.data )
end )

ClientError

The client has had a connection error. The connection is closed.

gs.events:on( "ClientError", function( event )
  p( "error: " .. event.data )
end )

ClientClose

The connection has closed.

gs.events:on( "ClientClose", function()
  p( "client closed" )
end )

GameCreate

The client has created a game successfully using createGame().

gs.events:on( "GameCreate", function()
  p( gs:getGameId() )
end )

GameJoin

A client has joined a game successfully using joinGame().

gs.events:on( "GameJoin", function( event )
  p( event.data.player .. " joined" )
end )

GameStart

The game has started and all players are present.

gs.events:on( "GameStart", function()
  p( gs:getPlayerHandle() )
  p( gs:getGameId() )
  p( gs:getPlayerNum() )
end )

GameData

The current persistent game data object has been received.

GameLeave

A client has left the game. The game is still running.

gs.events:on( "GameLeave", function( event )
	print( event.data.player .. " left" )
end )

GameDone

The GameDone event has been sent from the server, indicating the game is over.

gs.events:on( "GameDone", function()
	print( "game done" )
end )

Client Connection – Client-Side

You connect to the server with the Client-side connect() method, passing a “connection table”. The connection table can have the following keys, some are optional.

--== Set up connection table
local connection =
{
  host = "your.coroniumgs.address",
  port = 7173, --optional
  handle = "Username", --optional player 'handle'
  data = { fav_color = "Blue", dogs = true }, --optional connection data
  key = 'abc', --optional client/server key to pair by.
  ping = true --optional, whether client sends ping.
}
--== Connect to the server
coroniumgs:connect( connection )

Client Connection – Server-Side

After the server-side client connect event, you can access the optional data on the client.

--== Set up server-side client connect handler.
function onClientConnect( client )
  local player_name = client:getPlayerHandle()
  local player_fav_color = client:getPlayerData().fav_color
  --...
end
--== Set up listener.
coroniumgs:on( "ClientConnect", onClientConnect )

Client Objects

The client object is the main entity of the server. You can send and receive messages from the client, which is how you fashion your responses to and from the server, and ultimately control you game.

Messaging

To send a message you can use the send( data_table ) method on both the client and server-side. Your data must be in a keyed table.

coroniumgs:send( { msg = "Hello", score = 1000 } )

You can receive this data on both the client and server by listening for the ClientData event.

--== Server side
function onClientData( client, data )
  if data.msg == "Hello" then
    p( "score " .. data.score )
    client:send( { greeting = "Howdy to you" } )
  end
end
--== Set up listener.
coroniumgs:on( "ClientData", onClientData )

--== Client side
function onClientData( event )
  if event.data.greeting then
    print( "Server says " .. event.data.greeting )
  end
end
--== Set up listener.
coroniumgs.events:on( "ClientData", onClientData )

You can view all of the available Server-side client methods here.

You can view all of the available Client-side client methods here.

Working with Games

Creating and Joining Games – The Client-Side

To create games on the server for clients to play, you must issue a createGame() client-side call from any of the connected clients. This is usually presented as an option to the client, as is “Join a game”, using the joinGame() client-side method. A client will only either create a game or join a game, but not both.

Once a game has been created, the initial player will automatically join the game “room” and wait for others to arrive. Once the specified number of players has joined, the game will start and no other clients will be able to connect. You can specify the number of players for the game, as well as optional game criteria to search for.

--== Create Game

--== Listen for GameCreate event.
--== Only game creator will get this.
function onGameCreate( event )
  p( "game created" )
end
--== Set up listener.
coroniumgs.events:on( "GameCreate", onGameCreate )

--== Create a 2 player game
coroniumgs:createGame( 2 )

OR…

--== Join Game

--== Listen for GameJoin event.
--== All players will get this.
function onGameJoin( event )
  p( event.data.player .. " joined the game!" )
end
--== Set up listener.
coroniumgs.events:on( "GameJoin", onGameJoin )

--== Join a 2 player game
--== If not available, wait.
coroniumgs:joinGame( 2 )

You can view all of the available Client-side client methods and events here.

Managing Games – The Server-Side

Once a game is running, you will have created a unique game instance per game. It’s up to you to manage its state via control messages that you send, and receive, from the client. You can access these instances to control your game data and other resources. As long as you have a client reference, or game identifier, you can get a ‘handle’ to a game.

--== Server-side

function onClientData( client, data )

  --== Get this clients game instance.
  local game = coroniumgs:getPlayerGame( client )

  --== Send a message to all game players.
  --== Player will receive it in the ClientData event.
  game:broadcast( { msg = "Your turn", player_turn = 2 } )

end
--== Set up listener.
coroniumgs:on( "ClientData", onClientData )

Working with Game Data – The Server Side

Each game instance has a data storage component that you can use to store persistent data for each game session. To access the data, use the `data` key on the game instance.

--== Server-side

function onGameCreate( game )
  --== Set some starting data for the game.
  game.data.starting_tokens = 1000
  game.data.score_to_beat = 5000

  --==OR
  game:setData( { starting_tokens = 1000, score_to_beat = 5000 } )
end

function onClientData( client, data )
  --== Check incoming keyed event.
  if data.give_tokens then
    --== Get game instance.
    local game = client:getPlayerGame( client )
    --== Send client tokens from game data.
    client:send( { tokens = game.data.starting_tokens } )
  end
end
--== Set up listeners.
coroniumgs:on( "GameCreate", onGameCreate )
coroniumgs:on( "ClientData", onClientData )

You can view all of the available Server-side game instance methods here.

Resources

Corona Coronium Forums

Development Guides: Server | Client

Module Docs: Server | Client