Archive for the '16105' Category

Resoluciones del Nuevo Mes: Diciembre 2012

Tuesday, December 4th, 2012

Parece mentira, llegó el último mes de este año. Bueno, es tiempo de escribir las resoluciones de Diciembre, primero un repaso de las de Noviembre:

– Trabajar en PythonSharp [completo] ver repo
– Dar una charla Implementando Python en PyCon 2012 (Argentina) [completo] ver post
– Trabajar en AjTalk para Java [pending]
– Trabajar en AjTalk para Javascript [pending]
– Trabajar en BasicScript (lo quiero usar como ejemplo en mi proyecto Game Server) [partial] solamente demos y diseño
– Comenzar a pasar a Markdown mi tutorial de Java en español [partial] solo organizar el material
– Comenzar a escribir en Markdown lo que muestro en mi curso de Node.js [pending]

En vez de trabajar en el AjTalk de Java o de JavaScript, estuve trabajando bastante en la versión C#

– Trabajar en AjTalk para C# [completo] see repo

Voy a dar un curso de Node.js, y algún podcast, mis nuevas resoluciones:

– Trabajar en PythonSharp
– Trabajar en AjTalk para C#
– Dar un curso de todo un día de Node.js
– Participar de un podcast sobre Generación de Código
– Comenzar a pasar a Markdown mi tutorial de Java

Agrego:

– Nuevo proyecto en PHP, propiedades inmobiliarias

Espero escribir posts sobre lo nuevo que agregué en AjTalk de C#, ejemplos, etc. Ahora puede leer y grabar imágenes, tiene un ejemplo de web server mínimo, estoy agregando soporte de Traits, tiene un simple assert para hacer TDD simple, environments e import de paquetes, y tutti li fiocci 😉

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Resoluciones del Nuevo Mes: Noviembre 2012

Wednesday, November 7th, 2012

Revisión de mis resoluciones de Octubre:

– Dar una charla sobre lenguajes de programación en Javascript [completo] ver video
– Dar una chalar sobre Node.js/Socket.IO, aplicaciones web de tiempo real [completo] ver post
– Trabajar en PythonSharp [completo] ver repo
– Trabajar en AjTalk para Java [pendiente]
– Trabajar en AjConsorSite [parcial] solo tuve demo y reuniones
– Trabajar en GameServer [parcial] solo tuve demo y reuniones

Adicionalmente, estuve trabajando en:

– Actualizar mis ejemplos de Node.js [completo] ver repo
– Autómata celular en Javascript/Node.js [completo] ver repo
– Mejoras menores de AjTalkJs y AjLispJs
– Comenzar mi Basic Script (para Javascript/Node.js) [completo] ver repo
– Comenzar a portar AjGroups (librería de grupos finitos) a Javascript/Node.js
[completo] ver repo

Para este nuevo mes:

– Trabajar en PythonSharp
– Dar una charla sobre Implementando Python, en PyCon 2012 Argentina
– Trabajar en AjTalk para Java
– Trabajar en AjTalk para Javascript
– Trabajar en BasicScript (quiero comenzar a usarlo para que se pueda definir la lógica de un juego ejemplo de mi GameServer)
– Comenzar páginas markdown con Tutorial de Java, en español 
– Comenzar páginas markdown con Tutorial de Node.js en español

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Un Simple Servidor de Juegos

Saturday, September 15th, 2012

Desde hace unos meses, vengo pensando en desarrollar un servidor simple de juegos, donde el cliente puede ser implementando en varias tecnologías, en especial en el browser. Mi idea es que el servidor pudiera configurarse para distintos juegos. Cada uno de los juegos a definir y configurar tendría:

– Un solo jugador

– El juego tiene un estado

Es decir, un jugador tiene un juego asociado, y va operando sobre el estado del juego, tipo Farmville. No quiero encarar juego de tiempo real o juego multijugador. Para que no sea siempre el mismo juego, pienso implementar lo básico, y que se pueda configurar, logrando así distintos tipos de juegos. Cada tipo de juego podría manejar:

– Uno o varios mundos (en principio de dos dimensiones)

– En cada celda del mundo, podría haber elementos. Por ejemplo, el suelo del elemento (como en una celda de Farmville)

– El jugador tiene a su disposición distintos verbos/comandos/herramientas.

– Puede aplicar una herramienta o verbo a una celda. El estado del juego determinará si es válida esa acción. Por ejemplo, siguiendo con la idea de un Farmville simple, el jugador puede decidir arar una parcela (celda, cambiar el elemento suelo) pero el estado del juego se lo impide porque no tiene el suficiente puntaje, o porque la celda está ocupada y no es arable.

– Tener cero, uno o varios valores (puntajes, acumulación de distintas monedas/fuerzas)

– Tener cero, uno o varias colecciones (como un “bag” o inventario, cada una con un nombre)

– El servidor se encarga de procesar las acciones del cliente, validarlas, y cambiar el estado.

– El programa cliente podría mostrar el estado de distintas maneras. Me imagino un cliente HTML5/Canvas que muestre una porción del mundo en dos dimensiones, y otro que lo muestre en isométrico

– Los elementos gráficos se pueden configurar por tipo de juego y por tipo de cliente (no es lo mismo a mostrar en el mundo dos dimensiones, que en un cliente con mundo isométrico).

– Las reglas y acciones del juego (como la regla que determina si se puede aplicar el verbo “arar” a una celda o no; o la acción “arar” que decrementa un puntaje y cambia el elemento suelo de una celda), deberían poder configurarse. Pienso en un simple lenguaje de scripting, pero primero, usaría javascript cargado dinámicamente, en el cliente y en el servidor.

– A decidir: serialización del estado (podría ser un NoSql, un blob storage en la nube, etc)

– Trataría en algún momento de usar autenticación social (Facebook, Twitter, etc…) para que el jugador comience a jugar.

El principal lenguaje de implementación sería Javascript, en el cliente y el servidor, con lo que seguramente empezaré usando Node.js cuando llegue a la parte de programar el servidor. Pero podría adoptar en algún momento otro lenguaje y otra tecnología para el servidor. Todo esto quiero armarlo con “baby steps”, e irme adaptando a cualquier “feedback” que reciba.

– Experimentos en el cliente, como manejo de canvas, animación, alguna librería auxiliar.

– Un juego no configurable (es decir, fijo, predeterminado), con estado volátil, en el cliente.

– Un juego no configurable, con estado volátil, pero con cliente y servidor. Es decir, cada acción pedida por el cliente, se envía al servidor y éste responde.

– Un juego no configurable, con lógica en el cliente, replicada en el servidor. El servidor revalida las acciones, y puede interrumpir al cliente si ve que alguna operación no es válida. Esto aceleraría la experiencia del jugador con el programa cliente (cada acción se procesa ahí, sin esperar al servidor; al servidor se envía la acción en paralelo, para validarla) e impediría que el programa cliente fuera manipulado para permitir operaciones no válidas.

– Juego configurable: las reglas, el estado inicial del mundo, las acciones posibles, se podrían configurar, desde programas javascript, archivos json, o pequeños programas/rutinas en un lenguaje de scripting neutro y controlado.

– Juego configurable, con estado no volátil, y autenticación del jugador. Sería el primer paso para poder ofrecerlo como servicio. Pienso que gran parte del desarrollo de un juego de este tipo es la idea, las reglas, la parte gráfica, la música, la animación, la adaptación a distintos clientes. Con el programa que quiero ir armando, todo eso es configurable, lo que permitiría a otros armar sus propios juegos simples sin necesidad de adaptar código o escribir todo desde cero. Quisiera tomar ya ese nivel como un servicio que resuelva un problema, y explotarlo comercialmente. Una especie de “your game as a service” 🙂 Estoy trabajando con gente que sabe más que yo del tema emprender y diseño, para conseguir un “Minimal Viable Product”.

He visto que hay ideas más avanzadas que éstas, ya implementadas. Pero me gusta explorar por mí mismo, viendo qué es lo que se puede hacer fácil, qué es lo que se complica, y tratando de agregar el valor de la abstracción: descubrir los elementos genéricos que están por debajo de toda una clase de juegos. Como siempre, estos experimentos quedarán en mi cuenta de Github, ya hoy empecé a jugar con canvas :-). Si puedo, escribiré sobre la experiencia, pero no prometo nada. Ya el código será bastante evidencia. Temas para más adelante: 3 dimensiones, WebGL, ¿un Minecraft?

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Programando Juegos Sociales en Línea (Parte 8) Agregando Node.Js

Thursday, January 12th, 2012

Anterior Post

En el anterior post, mostré el procesamiento del juego en el  Windows Azure Toolkit for Social Gaming (versión 1.1.1, hay una nueva 1.2.0 pero aún en beta). Ahora, es tiempo de ver cómo agregar Node.js como servidor de juego a nuestro simple juego de Ta Te Ti.

Vean que todo el código client de Game Service es agnóstico del juego, es decir, no depende del juego que se está implementando. Y vean también que ese Game Service en javascript puede ser cambiado para usar otros servicios. En este post, veremos cómo cambiar el procesamiento de las jugadas para usar una instancia del servidor Node.js.

Primero, bajarse el código a agregar de mi GitHub:

https://github.com/ajlopez/SocialGamingExtensions

Entonces:

1 – Bajar e instalar el Windows Azure Toolkit for Social Gaming versión 1.1.1, desde:

http://watgames.codeplex.com/releases/view/77091

1 – Instalar Node.js para Windows, desde http://nodejs.org/#download

2 – Cambiar al subdirectorio server de mi código, y ejecutar el comando:

npm install socket.io

La librería socket.io se bajará e instalará en el subdirectorio node_modules:

Vean que baja bastante:

3 – Iniciar el servidor de node:

node gameex.js

El servidor comienza a escuchar:

4 – Copiar el directorio client de mi código al directorio del proyecto SocialGaming.Web. Los archivos Web.config, BaseController.cs, TicTacToeController.cs serán reemplazadas. Hay nuevos archivos: Scripts\game\GameServiceNodeJs.js, Areas\Samples\Views\TicTacToe\NodeJs.cshtml.

5 – Abrir la solución de Microsoft en el Visual Studio 2010 (run as administrator) y agregar esos nuevos archivos al proyecto SocialGaming.Web. El nuevo GameService a usar para Node.js:

La nueva vista TicTacToe que usa al Node.js:

La nueva acción en el controlador TicTacToe:

Y la nueva entrada en el web.config:

El reemplazado BaseController lee esa nueva configuración, que queda disponible para ser usada por la vista:

6 – Iniciar la aplicación, debería ejecurtarse como http://127.0.0.1:81 (en puerta 81) para ser aceptada por la seguridad federada configurada en el portal de Access Control Service.

7 – Navegar a /Samples/TicTacToe/NodeJs. Ver la URL de invitación:

8 – El cliente se conecta con el servidor de Node.js. Dependiendo de las capacidades del cliente, socket.io usará WebSockets o XHR long polling:

 

9 – Abrir un nuevo explorador, en una sesión privada, y navegar a la URL provista en el paso 7

10 – Hay dos jugadores. El de la derecha (el invitado) jugó en la celda del top izquierdo. El primer jugador (el de la izquierda) recibe la nueva movida y actualiza su tablero.

11 – Ver la consola del servidor Node.js: la movida fue recibida y enviada a todos los participantes de esta instacia de juego:

Bien! Trabajo para el hogar: agreguen el Node.js para publicarse en un worker role. Vean el post de @ntotten NodeJS on Windows Azure).

Próximos temas: explicar en detalle el código del servidor que ejecuta Node.js (por ejemplo, soporta sockets TCP además de socket.io; de esta manera podemos programar un cliente en otras plataformas, desde Silverlight a IPhone a Android, sin depender de tener que usar una librería Socket.io en esos clientes).

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Programando Juegos Sociales en Línea (Parte 7) Procesamiento de las Jugadas

Wednesday, January 11th, 2012

Anterior Post

Quiero en este post presentar el procesamiento de las jugadas del ejemplo simple de Ta Te Ti. Involucra varias partes, desde la vista Razor que usa Javascript, hasta el Web Role, hasta el Azure Blob Storage.

Estos son los archivos javascript referenciados en la vista TicTacToe (en el archivo SocialGame.Web/Areas/Samples/Views/TicTacToe/Index.cshtml):

<script src="@Url.AreaContent("Scripts/jQuery.tmpl.js")" type="text/javascript"></script>
<script src="@Url.AreaContent("Scripts/knockout-1.2.1.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/game/ServerInterface.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/game/GameService.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/game/UserService.js")" type="text/javascript"></script>
<script src="@Url.AreaContent("Scripts/game/TicTacToeBoard.js")" type="text/javascript"></script>
<script src="@Url.AreaContent("Scripts/game/TicTacToeGame.js")" type="text/javascript"></script>
<script src="@Url.AreaContent("Scripts/game/TicTacToeViewModel.js")" type="text/javascript"></script>
<script src="@Url.AreaContent("Scripts/game/TicTacToeController.js")" type="text/javascript"></script>

Como escribí en el aterior post, tanto ServerInterface, GameService como UserService son agnósticos del juego, pueden reusarse para cualquier juego. Para cada nuevo tipo de juego X, lo que tenemos que programar son los XBoard, XGame con la lógica del juego, el XViewModel y el apropiado XController.

Al final de esa vista, se encuentra la creación de los servicios agnósticos del juego a usar desde el cliente:

var apiURL = "@this.ViewBag.ApiUrl";
var blobURL = "@this.ViewBag.BlobUrl";
var si = new ServerInterface();
var gs = new GameService(apiURL, blobURL, si);
var user = new UserService(apiURL, blobURL, si);

Las propiedades @this.ViewBag son llenadas en el servidor, en el controlodor de ASP.NET MVC (ver BaseController.cs)

La creación del controlador del juego:

// check for canvas, show an "Upgrade your browser" screen if they don't have it.
var canvas = document.getElementById('board');
if (canvas.getContext == null || canvas.getContext('2d') == null) {
    $("#game").toggle();
    $("#notSupported").toggle();
    return;
}
var board = new TicTacToeBoard(canvas);
var game = new TicTacToeGame();
var controller = new TicTacToeController(viewModel, gs, board, game);
controller.setGameQueueId(gameQueueId);
controller.start();


Pero la parte interesante está en el constructor del controlador:

function TicTacToeController(viewModel, gameService, board, game) {
    this.viewModel = viewModel;
    this.gameService = gameService;
    this.board = board;
    this.game = game;
    this.started = false;
    var controller = this;
    this.board.onMove = function (x, y) { controller.onMove(x, y); };
};


Notemos el this.board.onMove: el controlador se registra a sí mismo para procesar las nuevas movidas detectadas por el componente que maneja el tablero. En su método start(), el controlador se registra a sí mismo para procesar las actualizaciones detectas por el servicio de gameService:

controller.gameService.process(
        gameQueueId,
        function (queue) { controller.processGameQueue(queue); },
        function (action) { controller.processAction(action); }
        );

Examinemos el procesamiento de las jugadas.

1 – El componente del tablero detecta un click, determina la casilla, y envía una nueva movida al controlador, usando el callback onMove

2 – El controlador envía la nueva movida para la lógica del juego, para actualizar el estado (he omitido el view model en este gráfico)

3 – El controlador envía la nueva movida al game service.

4 – El game service envía un nuevo comando al web role, usando el server interface

5 – La API del web role recibe el nuevo comando

6 – La información del comando es agregada al blob que refleja el estado del juego, almacenado en el Azure Storage

Ahora, veamos el procesamiento en el otro cliente:

1 – El game service, usando un timer, va leyendo reiteradamente el stado del juego, usando la service interface

2 – La service interface, usando JSONP, recupera el estado actual del juego, desde un blob de Azure Storage

3 – Si una nueva movida es detectada en ese estado, el game service llama a una función callback provista por el controlador (el game service no tiene referencia al controlador).

4 – El controlador envía la nueva movida a la lógical de juego, que actualiza su estado (omití el view model en este gráfico)

5 – El controlador envía la nueva movida al componente de tablero, para actualizar el canvas

Un punto clave: el controllador no conoce NADA acerca de la service inteface, la API de WCF Web, el blob storage. Entonces, podemos cambiar el game service para que procese las movidas de otras maneras. Próximo post: modificar el game service para usar a Node.js como servidor de las movidas del juego.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Programando Juegos Sociales en Línea (Parte 6) Armando y Probando el Juego y los Servicios con TDD y QUnit

Friday, December 30th, 2011

Anterior Post

En mi anterior post, comenté la nueva versión 1.1 (hay ahora una 1.2 Beta) del Windows Azure Toolkit for Social Games. Tiene juegos simples para demostrar el uso de Javascript, HTML5, procesamiento de movidas del juego, uso de Azure web roles y worker roles. Veamos de explorar en este post el armado de la lógica del juego, en Javascript, usando TDD (Test-Driven Development) y QUnit.

Hay tests en línea en:

http://watgames4.cloudapp.net/Test

Ejecutemos los test de Tic Tac Toe Game Logic:

http://watgames4.cloudapp.net/Samples/ClientTest/TicTacToeGameTest

Esta página está usando QUnit para tests en el cliente usando Javascripot. Escribí posts introductorios:

TDD with Javascript and QUnit
TDD con Javascript y QUnit

La página que visitamos está probando la lógica del juego de Ta Te Ti. Recordemos, cada juego está implementado en partes, la lógica es una de ellas:

El código cliente reside en TicTacToeGame.js dentro del proyecto SocialGames.Web. Sus primeras líneas:

TTTColor = { Empty: 0, Cross: 1, Circle: 2 };
function TicTacToeGame() {
    this.board = [
     [TTTColor.Empty, TTTColor.Empty, TTTColor.Empty],
     [TTTColor.Empty, TTTColor.Empty, TTTColor.Empty],
     [TTTColor.Empty, TTTColor.Empty, TTTColor.Empty]
     ];
}
TicTacToeGame.prototype.move = function (x, y, color) {
    this.board[x][y] = color;
};
TicTacToeGame.prototype.isEmpty = function (x, y) {
    return this.board[x][y] == TTTColor.Empty;
};
....

La vista razor (TicTacToeGameTest.cshtml) fue escrita al mismo tiempo que la lógica, usando TDD (Test-Driven Development). Veamos los primeros tests:

test("Create Empty Board", function () {
    var game = new TicTacToeGame();
    for (var x = 0; x < 3; x++)
        for (var y = 0; y < 3; y++)
            ok(game.isEmpty(x, y));
    equal(game.isTie(), false);
    equal(game.hasWinner(), false);
});
test("Valid Moves on Empty Board", function () {
    var game = new TicTacToeGame();
    for (var x = 0; x < 3; x++)
        for (var y = 0; y < 3; y++) {
            ok(game.isValid(x, y, TTTColor.Cross));
            ok(game.isValid(x, y, TTTColor.Circle));
        }
});
test("No Winner in Empty Board", function () {
    var game = new TicTacToeGame();
    equal(game.getWinner(), TTTColor.Empty);
});
test("Get Winner in First Row", function () {
    var game = new TicTacToeGame();
    game.move(0, 0, TTTColor.Cross);
    game.move(1, 0, TTTColor.Cross);
    game.move(2, 0, TTTColor.Cross);
    equal(game.getWinner(), TTTColor.Cross);
    equal(game.isTie(), false);
    equal(game.hasWinner(), true);
});

La idea es avanzar de a pequeños pasos, test por test, diseñando la API de la lógica del juego, y su conducta esperada. De esta manera, vamos avanzando sin gastar tanto tiempo en prueba manual, y menos tiempo en depuración. Esto es recomendable, pero más aún cuando se trabaja con un lenguaje dinámico como Javascript. Una batería de pruebas puede salvarnos el día en caso de un refactoreo grande. Vean que el Four In A Row siguió un camino similar.

Bien, no todo puede ser testeado fácilmente, o construido usando TDD. Algunos de los servicios agnósticos del juego (es decir, independientes del juego que se implemente) estan usando Ajax y Blob Storage, y para probarlos debemos considerar el uso de Ajax asincrónico (pueden comenzar construyendo tests sincrónicos si quieren). Pueden ver:

http://watgames4.cloudapp.net/Test/ServerInterfaceTest

(Para esta página, deben ingresar usando una cuenta de Facebook o de Windows Live ID, el ejemplo usa Federated Security y Access Control Service (ACS))

Esta vez, el sistema bajo prueba es el Service Interface:

Hay algunos trucos en el código de prueba (ServerInterfaceTest.cshmlt), un fragmento:

test("Call User/Verify", function () {
    var success = function (result) { ok(true); start(); };
    var error = ajaxGetError;
    stop(10000);
    expect(1);
    si.sendAjaxGet(apiURL + "user/verify", success);
});

expect es una función provista por QUnit que prepara al framework para recibir un ok(true) alguna vez durante la ejecución de la prueba. Esa confirmación se incluye en la función callback success que será llamada luego de la ejecución exitosa de la llamada asincrónica .sendAjaxGet. La vida Async no es fácil 😉

Más análisis del código en próximos posts. Debería adaptar algún juego para que use Node.js para procesar sus jugadas.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Desarrollando un juego en la nube con HTML5, JavaScript y Node.js

Friday, December 16th, 2011

Es el título de la próxima charla gratuita que @woloski y yo daremos en el Microsoft User Group de Buenos Aires, Argentina. Ver detalles en

http://www.mug.org.ar/Eventos/3715.aspx

Será el próximo miércoles, 21 de Diciembre, a las 18:30hs. Tienen que visitar ese link si quieren inscribirse (hay inscripción previa).

La idea a presentar está basada en desarrollos que hicimos para:

Windows Azure Toolkit for Social Games

Ya escribí sobre Azure y Social Games (con Tankster y los nuevos ejemplos simples) en:

Social Games Programming
Programando Juegos Sociales

Para la charla visitaremos:

– HTML5, en especial su uso de Canvas para dibujar un simple tablero

– Javascript, por ejemplo Knockout para tener un ViewModel en el cliente que al cambiar refresca elementos de la página

– Uso de TDD en Javascript, con QUnit

– Socket.IO cliente para comunicarse con un servidor

– Node.js para levantar un servidor de jugadas, usando Socket.IO (quiero también escribir un ejemplo con sockets TCP)

– Elementos de Tankster (seguridad federada en Azure, etc…) aunque lo principal de la charla se basa en los nuevos ejemplos simples (Tankster puede ser una aplicación muy grande para estudiar como primer ejemplo).

Si quieren leer sobre Tankster y los ejemplos simples nuevos, les recomiendo los posts en los enlaces que mencioné arriba.

Sobre temas de HTML5, Node.js, Javascript, TDD ya tengo escritos:

HTML5: Links, News and Resources
Javascript: Links, News and Resources
Node.js: Links, News and Resources
TDD With Javascript and QUnit
My posts about QUnit

Y estos son los enlaces que fui descubriendo mientras estudiaba estos temas:

http://www.delicious.com/ajlopez/javascript
http://delicious.com/ajlopez/html5
http://delicious.com/ajlopez/socketio
http://www.delicious.com/ajlopez/nodejs

Mañana Sábado Diciembre 17, el bueno de @cprieto dará una VAN (reunión virtual) en AltNet Hispano:

http://cprieto.com/2011/12/13/proxima-van-html5-para-los-no-iniciados/

El bueno de @theprogrammer ya dio una VAN sobre Node.js:

http://altnethispano.org/wiki/van-2011-08-06-nodejs.ashx

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Programando Juegos Sociales en Línea (Parte 5) Nuevo Azure Toolkit

Thursday, December 1st, 2011

Anterior Post 
Siguiente Post 

Dos semanas atrás, fue publicada una nueva versión del Windows Azure Toolkit for Social Games. Vean los posts de @ntotten:

Windows Azure Toolkit for Social Games Version 1.1.1
Windows Azure Toolkit for Social Games Version 1.1

La nueva versión implementa dos juegos simples en HTML5: Ta Te Ti, y Cuatro en Raya, usando vistas ASP.NET MVC, servicios WCF Web API, seguridad federada usando ACS, y Azure Storage. Pueden jugar en línea en:

http://watgames4.cloudapp.net/

Pienso que éste es un ejemplo más claro que el anterior (Tankster) que era una gran aplicación pero algo excesiva ;-). Veamos de entrar en algunos detalles de implementación de este nuevo ejemplo.

Totten escribió:

The biggest change we have made in this release is to separate the core toolkit from the Tankster game. After we released the Tankster sample game we received a lot of feedback asking for a simpler game that developers could use to learn. To meet this need we developed two simple games, Tic-Tac-Toe and Four in a Row, and included in the toolkit. The Tankster game is now available separately as a sample built on top of the toolkit.

While the new games included in the toolkit are much simpler than Tankster, they still show the same core concepts. You can easily use these samples as a starting point to build out any number of types of games. Additionally, you will find that many of the core components of the game such as the leaderboard services, game command services can be used without any modification to the server side or client side code.

En mi anterior post, mencioné un pequeño pero importante cambio en el proceso de acciones de juego: toda la lógica fue removida del código del servidor. Adoptando este camino, podemos escribir nuevos juegos sin cambiar el código del servidor. Podemos seguir agregando código en el servidor si lo necesitamos (por ejemplo, para agregar control al juego, detectar operaciones inválidas enviadas desde algún cliente, etc) pero es interesante tener una base de código que sea agnóstica del juego.

Abriendo la solución en Visual Studio, encontraremos archivos Javascript usados por los dos juegos. Podemos escribir un nuevo juego, reusando estos archivos sin cambios:

Los juegos están implementados como áreas:

Podríamos escribir nuevos juegos y publicarlos como paquetes NuGet.

Visualmente, el código Javascript cliente está organizado de esta manera:

Cada juego X (X = Ta Te Ti, Cuatro en Raya, uno nuestro) tiene:

XGame: la lógica del juego

XBoard: para dibujar el tablero en un elemento canvas de HTML5, y para detectar eventos de click en el mismo.

XViewModel: contiene el jugador actual y otros datos para ser usandos en el armado de la vista (los ejemplos usan  knockout.js, como MVC en Javascript)

XController: para procesar nuevos eventos y para coordinar los elementos de arriba.

La parte genérica:

UserService: métodos relacionados a usuarios: login, lista de amigos, etc…

GameService:  jugar las movidas, recibir movidas de otros jugadores, otras acciones (por ejemplo, se podrían enviar mensajes de chat).

ServerInterface: Llamadas Ajax (usando GET, POST, JSONP, Azure storage….) que son usados por la implementación de User y Game Service.

Temas para próximos posts: analizar el código Javascript, el uso del Canvas, tests de Javascript usando QUnit, comunicación con el servidor usando Ajax, cambio del Game Service (en Javascript) para usar un Node.js como servidor que reciba y reparta las acciones de juego.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Programando Juegos Sociales en Línea (Parte 4) Procesando Acciones De Juego Arbitrarias

Wednesday, August 10th, 2011

Anterior Post 
Siguiente Post 

Hay un nuevo release del Windows Azure Toolkit for Social Games:

http://watgames.codeplex.com/releases/view/70342

Examinemos un cambio clave en esta versión. Hay un nuevo punto de entrada en Tankster.GamePlay\Services\IGameService.cs:

[WebInvoke(Method = "POST", UriTemplate = "command/{gameId}")]
HttpResponseMessage Command(Guid gameId, HttpRequestMessage request);
[WebInvoke(Method = "POST", UriTemplate = "command/v2/{gameId}")]
HttpResponseMessage Command2(Guid gameId, HttpRequestMessage request);

Hay DOS puntos de entrada para enviar comandos de juego desde los clientes. ¿Por qué? Al tener dos URIs el servidor puede ser usado por los clientes antiguos y por los nuevos. Noten el uso de  “v2” en el UriTemplate.

El nuevo código (parcial) de Command2 en su implementación (GameService.cs):

// Add gameAction
var gameAction = new GameAction
{
    Id = Guid.NewGuid(),
    Type = commandType,
    CommandData = commandData,
    UserId = this.CurrentUserId,
    Timestamp = DateTime.UtcNow
};
// Cleanup game actions lists
for (int i = 0; i < game.GameActions.Count(); i++)
{
    if (game.GameActions[i].Timestamp < DateTime.UtcNow.AddSeconds(-10))
    {
        game.GameActions.RemoveAt(i);
        i--;
    }
}
game.GameActions.Add(gameAction);
this.gameRepository.AddOrUpdateGame(game);
return SuccessResponse;

Si comparamos éste con el viejo código (que sigue estando en el método Command) con el código de arriba, la principal diferencia es:

– El servidor no maneja la lógica de “quién es el próximo jugador en la ronda de turnos”

– El servidor no distribuye los comandos recibidos usando un GameActionProcessor

Y otro cambio: el tipo de una acción de juego es ahora un entero, un int. En la anterior versión del ejemplo era un enum, reflejando un conjunto fijo de tipos.

Todavía actualiza el blob de estado del juego agregando el nuevo comando (un comando puede ser un mensaje de chat, o un disparo). De esta nueva manera, el CODIGO DEL SERVIDOR es más independiente de la lógica del juego: el código cliente tiene la responsabilidad de elegir el próximo jugador. La noción de turno se deja al desarrollo del código cliente. Ahora, el código servidor puede ser usado para OTROS juegos multi-jugador. Y podríamos tener nuestro propio “Social Game”! 😉

Entonces, los roles web y worker de Windows Azure están a cargo de la autenticación federada, la formación de nuevos juegos (al sumar jugadores que quieran participar), etc. Pero el motor es más “game agnostic” en este nuevo release.

Repasando como queda ahora. El código cliente envía comandos:

Pasos:

1- El código cliente envía una nueva Game Action en JSON (por ejemplo, información de un nuveo disparo (posición, ángulo, fuerza)).

2- Una de las instancias del web role recibe el Game Action y la agrega a la lista de acciones en el correspondiente blobg del juego (guardando solamente las acciones de los últumos 10 segundos)

3- Los otros jugadores están “poleando” el estado del juego, notando cuándo una game action implica un cambio de turno. Y si reciben disparos, los van mostrando en cada browser.

El código cliente podría manejar también el caso de un jugador que pasa a “offline”, mediante un “timeout”.

Pros de esta nueva forma de manejar las acciones:

– El código del servidor es más genérico, y puede ser usado para otros juegos

Cons:

– El código cliente puede ser engañado (no hay controles, validaciones en el servidor)

– Más complejidad a implementar en el código cliente: manejo de turnos, “timeouts”, latencia, control de “cheat”.

La prueba ácida: implementar un nuevo juego usando el mismo código servidor. Algunos puntos para pulir: el modo Skirmish (cada jugador entra en un juego, esperando a que otros también entren) está todavía “hardcodeado” a un máximo de 5 jugadores; la ventana de 10 segundos para las acciones es arbitraria. Posibles soluciones: cada cliente de juego crea un nuevo juego (esperando a otros jugadores, o invitándolos) especificando el número mínimo, máximo de jugadores, el “timeout” en segundos,  y la ventana de tiempo para mantener las acciones.

Pero recuerden: es todavía una beta. Una nueva versión está en camino.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Programando Juegos Sociales en Línea (Parte 3) Tankster, Blob Storage y JSONP

Wednesday, August 3rd, 2011

Anterior Post
Siguiente Post

En mi anterior post escribí sobre el Windows Azure Social Gaming Toolkit, y su ejemplo de juego en línea, para varios jugadores. Ver:

http://watgames.codeplex.com/
http://www.tankster.net/

(El lunes 1ro de Agosto, salió una nueva beta: http://watgames.codeplex.com/releases/view/70342)

Hay dos post interesantes de @ntotten acerca de los “internals” del juego y las decisiones que se tomaron:

Architecture of Tankster – Introduction to Game Play (Part 1)
Architecture of Tankster– Scale (Part 2)

Vale la pena mencionar acá el fragmento de Nathan:

Before we begin with an explanation of the implementation, it is important to know what our goals where for this game. We had three primary goals when building this game.

  1. The game must scale to handle several hundred thousand concurrent users.*
  2. The architecture must be cost effective.
  3. The architecture must be flexible enough for different types of games.

* This is the goal, but there are still some known limitations in the current version that would prevent this. I will be writing more about that later.

Ahora, en este post, quiero escribir sobre el uso e implementación de blob storage en Tankster.

Primero: ¿por qué usar blob storage? Si jugamos Tankster en modo de práctica, el cliente Javascript no necesita usar el backend de Azure. El código Javascript puede acceder al web role y al blob storage usando llamadas Ajax/JSON. Y en juego multi-jugador (en el modo de juego de Tankster llamado Skirmish), cada cliente “pollea” el blob storage para conseguir el estado del juego (los disparos de los tanques, mensajes de chat, otro estado). Pero de nuevo: ¿por qué blob storage? Hay razones monetarias (costo de acceder a la Web Role API vs el costo de leer un blob), pero no conozco los precios de Azure (ya saben, “el maestro no toca la plata” ;-). Pero hay otra razón: repartir el trabajo, dando algo de aire a las instacias de web role. En vez de hacer todo con la API alojada en el web role, se leen blobs desde Azure storage.

Hay una clase AzureBlobContainer en el proyecto de librería de clases Tankster.Common:

public class AzureBlobContainer<T> : IAzureBlobContainer<T>
{
    private readonly CloudBlobContainer container;
    private readonly bool jsonpSupport;
    private static JavaScriptSerializer serializer;
    public AzureBlobContainer(CloudStorageAccount account)
        : this(account, typeof(T).Name.ToLowerInvariant(), false)
    {
    }
    public AzureBlobContainer(CloudStorageAccount account, bool jsonpSupport)
        : this(account, typeof(T).Name.ToLowerInvariant(), false)
    {
    }
    public AzureBlobContainer(CloudStorageAccount account, string containerName)
        : this(account, containerName.ToLowerInvariant(), false)
    { 
    }
	//....

 

La clase usa generics. El tipo T puede ser un Game, GameQueue u otro tipo .NET. Pueden reusar esta clase en otros proyectos: es “game agnostic”, no está escrita para este proyectos, sino que se puede usar en otros. Los varios constructores le agregan flexibilidad para tests.

Este es el código para grabar un objeto tipado en un blob:

public void Save(string objId, T obj)
{
    CloudBlob blob = this.container.GetBlobReference(objId);
    blob.Properties.ContentType = "application/json";
    var serialized = string.Empty;
    serialized = serializer.Serialize(obj);
    if (this.jsonpSupport)
    {
		serialized = this.container.Name + "Callback(" + serialized + ")";
    }
    blob.UploadText(serialized);
}

El objeto (tipo T) es serializado a JSON. Y no sólo JSON: noten que hay un Callback (podríamos remover esto, si nuestro proyecto no lo necesita). Entonces, un objeto juego se graba como (he robado…. eh… tomado prestado 😉 este código del post de Nathan):

gamesCallback(
    {"Id":"620f6257-83e6-4fdc-99e3-3109718934a6"
    ,"CreationTime":"\/Date(1311617527935)\/"
    ,"Seed":1157059416
    ,"Status":0
    ,"Users":[
        {"UserId":"MxAb1iZtey732BGsWsoMcwx3JbklW1xSnsxJX9+KanI="
        ,"UserName":"MxAb1iZtey732BGsWsoMcwx3JbklW1xSnsxJX9+KanI="
        ,"Weapons":[]
        },
        {"UserId":"ZXjeyzvw7WTdP8/Uio4P6cDZ8jmKvCXCDp7JjWolAOY="
        ,"UserName":"ZXjeyzvw7WTdP8/Uio4P6cDZ8jmKvCXCDp7JjWolAOY="
        ,"Weapons":[]
        }]
    ,"ActiveUser":"MxAb1iZtey732BGsWsoMcwx3JbklW1xSnsxJX9+KanI="
    ,"GameRules":[]
    ,"GameActions":[]
    })

¿Por qué el callback? Para soportar JSONP:

http://en.wikipedia.org/wiki/JSONP

JSONP or “JSON with padding” is a complement to the base JSON data format, a pattern of usage that allows a page to request data from a server in a different domain. As a solution to this problem, JSONP is an alternative to a more recent method called Cross-Origin Resource Sharing.

Under the same origin policy, a web page served from server1.example.com cannot normally connect to or communicate with a server other than server1.example.com. An exception is the HTML <script> element. Taking advantage of the open policy for <script> elements, some pages use them to retrieve Javascript code that operates on dynamically-generated JSON-formatted data from other origins. This usage pattern is known as JSONP. Requests for JSONP retrieve not JSON, but arbitrary JavaScript code. They are evaluated by the JavaScript interpreter, not parsed by a JSON parser.

Si Ud. no conoce JSON y JSONP, acá hay un tutorial con ejemplo usando JQuery (la misma librería usada por el código de Tankster):

Cross-domain communications with JSONP, Part 1: Combine JSONP and jQuery to quickly build powerful mashup

Pero (ya saben, siempre hay un “pero” en todo proyecto de software) el blob storage de Azure no soporta el uso de JSONP URLs (que tienen un parámetro indicando el id de un callback generado al azar):

Query JSON data from Azure Blob Storage with jQuery

Hay una solución propuesta en un hilo de Stack Overflow. El agente Maxwell Smart diría: ah! el “viejo truco” de tener el callback ya especificado en el blob!

dataCallback({"Name":"Valeriano","Surname":"Tortola"})

Pueden leer una más detallada descripción del problema y solución con código de ejemplo en el post de @woloski’s post (Buen práctica Matías! escribir un post con código!):

Ajax, Cross Domain, jQuery, WCF Web API or MVC, Windows Azure

Un problema a tener en cuenta: vean que el nombre del callback está “hardcodeado” en el blob. El gamesCallback presente en el blob de Game Status debe ser el nombre de una función global. Pero pueden cambiar el texto del blob a cualquier Javascript válido.

¿Y del lado cliente? Pueden estudiar el código de gskinner en el proyecto Tankster.GamePlay, en src\ui\net\ServerDelegate.js:

(function (window) {
    goog.provide('net.ServerDelegate');
    goog.require('net.ServerRequest');
    
    var ServerDelegate = function () {
        throw new Error('ServerDelegate cannot be instantiated.');
    };
    
    var p = ServerDelegate;
    
    p.BASE_API_URL = "/";
    p.BASE_BLOB_URL = "http://tankster.blob.core.windows.net/";
    p.BASE_ASSETS_URL = "";
    p.load = function (type, data, loadNow) {        
        var req;
        if (p.CLIENT_URL == null) {
            p.CLIENT_URL = window.location.toString();
        }
//....

There is an interesting switch:

switch (type) {
    //Local / config calls
    case 'strings':
        req = new ServerRequest('locale/en_US/strings.js', 
		   null, 'jsonp', null, 'stringsCallback'); break;
    //Game calls
    case 'endTurn':
        req = new ServerRequest(apiURL+'Game/EndTurn/', 
		    null, 'xml'); break;
    case 'leaveGame':
        req = new ServerRequest(apiURL+'Game/Leave/'+data.id, 
		    {reason:data.reason}, 'xml', ServerRequest.POST); break;
    case 'playerDead':
        req = new ServerRequest(apiURL+'Game/PlayerDead/', 
		    null, 'json'); break;
    case 'gameCreate':
        req = new ServerRequest(apiURL+'Game/Queue', data, 
		    'xml', ServerRequest.POST); break;
    case 'usersGame':
        req = new ServerRequest(blobURL+'sessions/'+data, 
		    null, 'jsonp', null, 'sessionsCallback'); break;
    case 'gameStatus':
        req = new ServerRequest(blobURL+'games/'+data, 
		    null, 'jsonp', null, 'gamesCallback'); break;
    case 'gameQueueStatus':
        req = new ServerRequest(blobURL+'gamesqueues/'+data, 
		    null, 'jsonp', null, 'gamesqueuesCallback'); break;
    case 'notifications':
        req = new ServerRequest(blobURL+'notifications/'+data, 
		    null, 'jsonp'); break;
    
    //User calls
    case 'verifyUser':
//...

Vean el ‘gameStatus’: usa ‘jsonp’ como formato. Pienso que el  ‘gamesCallback’ como parámetro no es necesario: pueden usar cualquier otro nombre, la función a llamar reside en el contenido del blob.

Yo pienso que hay alternativas a esta forma de mantener el estado del juego. El blob refleja a la entidad Game (ver Tankster.Core\Entities\Game.cs). Pero podría ser implementado en dos blobs por juego:

– Uno de los blobs, conteniendo el estado inicial (jugadores, posiciones, armas…) y la lista de TODA la historia del juego (pienso que todo esto se puede reusar para juegos de tablero, como ajedrez o go, donde puede ser interesante tener la historia de las movidas del juego).

– Otro blob, conteniendo las “novedades” (los últimos 10 segundos de las acciones del juego). De esta forma, los clientes “pollean” este blob, que debería ser más corto que el actual.

El precio a pagar: el servicio en el Web Role que atiende las acciones de los juegos DEBE actualizar LOS DOS blobs. Pero esta actualización sólo ocurren cuando un jugador envía una movida (disparo, chat..). La consulta reiterada desde cada cliente se dispara, digamos, cada un segundo. Actualmente, el blob del juego mantiene los últimos 10 segundos de las acciones Y ADEMAS el estado inicial, jugadores, armas, etc… CADA CLIENTE lee reiteradamente esa información. En la implementación que sugiero, los datos a leer cada vez son menos. En el caso que un cliente se desconecte y reconecte, podría leer el blob que contiene el juego completo, para volver a sincronizar su estado.

La separación de esta información en dos blobs podría mejorar la escalabilidad de la solución. Pero ya saben: premature optimization is the root of all evil ;-). Si tomamos ese camino para almacenar y recuperar los juegos, deberíamos ejecutar algunos tests de carga para realmente conocer las consecuencias de esa decisión.

Tengo que seguir con más análisis de código de Tankster, patrones y desarrollo de juegos sociales en general.

Algunos enlaces:

WAT is to Social Gaming, as Windows Azure is to… | Coding4Fun Blog | Channel 9
Gracias! “Vieron a luz” y mencionan mi post 😉
Episode 52 – Tankster and the Windows Azure Toolkit for Social Games | Cloud Cover | Channel 9
Microsoft tailors Azure cloud services to social game developers | VentureBeat
Windows Azure Toolkit for Social Games Released to CodePlex – ‘Tankster’ Social Game Built for Windows Azure
Tankster, a Social Game Built for Windows Azure | Social Gaming on Windows Azure | Channel 9
Social Gaming on Windows Azure | Channel 9
Build Your next Game with the Windows Azure Toolkit for Social Games
Microsoft delivers early build of Windows Azure toolkit for social-game developers | ZDNet

http://www.delicious.com/ajlopez/tankster

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez