El miércoles pasado, 20 de Julio, Microsoft liberó una versión “preview” del Windows Azure Toolkit for Social Games, y publicó una versión beta (con código) de un primer juego de demostración.
Pueden bajar el código desde Codeplex: http://watgames.codeplex.com/
Pueden jugar el juego en línea en: http://www.tankster.net/
La solución actual tiene estos proyectos:
Tankster.GamePlay es un Web Role. Hay un único WorkerRole. Tankster.Core es una librería de clases. Hay código interesante en Tankster.Common: utilitarios Azure para acceder a repositorios, un “job engine”, y más; todo ese código es independiente del juego, podría usarse en cualquier proyecto Azure.
Estos son mis primeros cortos comentarios sobre el código y la implementación de las “features” de este juego (recuerden, es un beta! Algunas de estas implementaciones podrían cambiar en el próximo “release”):
– Tecnología cliente: HTML5, Javascript, EaselJs (para programación del canvas, que permite implementar la parte gráfica).
– Tecnología servidor: ASP.NET MVC 3, algunas vistas Razor para pruebas (tema interesante: ¿Cómo probar el juego cuando sin usar el juego REAL), WCF Web API (otro tema interesante: ver tecnologías alternativas para manejar la actividad del juego. Es importante: un tema a cuidar en este tipo de aplicaciones es cómo responder a una gran carga desde el cliente)
– Hay un modelo en el cliente y entidades en Javascript. Vean See src/model, src/game.
– Hay un modelo del juego There is a server game model (see Tankster.Core class library project)
– Pueden jugar en modo “single player” o pueden elegir “multi-player online”. En este caso, el juego usa el ACS Portal para permitir la autenticación usando Federated Authentication:
– El código cliente reside en una sola página: index.html (con montones de archivos javascript referenciados desde ahí)
– El código cliente envía JSON (comandos) a los WCF Web API endpoints, usando Ajax/JQuery. Hay varios servicios publicados, exponiendo una interfaz tipo REST
routes.MapServiceRoute<GameService>("game"); routes.MapServiceRoute<AuthService>("auth"); routes.MapServiceRoute<UserService>("user"); routes.MapServiceRoute<TestService>("test");– Mucha de la actividad del juego es enviado al servicio game/command. El servicio (en el web role) actualiza el estado del juego guardándolo en un blob en el Azure Storage. Fragmento de código:
// 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);– El estado del juego es “poleado” por los clientes javascript consultando directamente el blob storage. De esta forma, el Web Role ASP.NET MVC tiene menos carga.
– El blob reside en el mismo dominio, así que no son necesarias llamadas Ajax cross-domain. Pero el juego está preparado para ese tipo de llamadas, reemplezando el objeto XmlHttpRequest por un componente Flash.
– El modo de juego Skirmish (cinco jugadores en una batalla) está hecho encolando los jugadores hasta llegar a cinco. Ese trabajo lo hace el Worker Role. Va publicando el estado en un blob que es “poleado” por los clientes.
– Las estadísticas son procesadas en el worker role: los clientes javascrip envían comando al Web Role, éste selecciona algunos para estadísticas y los envía a una cola Azure. No se ocupa de las estadísticas en el momento, para no afectar al cliente. Finalmente, esos datos para estadísticas se manejan en el Worker Role.
– User status, Game status, Skirmish Game Queue status son blobs.
– Las estadísticas se guardan en SQL Azure.
– El único worker role tiene un Job Engine (motor de trabajos) para procesar varias tareas, ejemplo “fluent”:// Game Queue for Skirmish game Task.TriggeredBy(Message.OfType<SkirmishGameQueueMessage>()) .SetupContext((message, context) => { context.Add("userId", message.UserId); }) .Do( new SkirmishGameQueueCommand(userRepository, gameRepository, workerContext)) .OnError(logException) .Start();Hay varios puntos para comentar y discutir, alimento para futuros posts. Seguramente la solución actual evolucionará y nuevas versiones serán publicadas (¿esta semana? ¿la próxima?). Pero es interesante ver publicado un juego en línea y su código disponible para analizarlo.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez