Playing between the
                             clouds
                           Daniele Montagni
                      ...
Who we are



                      http://exmachina.ch   http://pronetics.it

                        Davide Cerbo       ...
Gameplay
                      Gameset:
                      Tankattack is a turn-based strategy game. The setting is a fi...
Gameplay
                      Rules:
                       •Map
                           •
                           ...
Multiplayer / Multiplatform




Friday, May 7, 2010
Google App Engine




Friday, May 7, 2010
Everybody can play
                •     A successful game must have a huge players number
                •     Players w...
Dilemma: Cluster? Cloud? Grid?

                 Cloud:                   Grid:

                 • More requests number  ...
Searching on Google and...
                         ...found companies that offers
                       Cloud or Grid Co...
If I want to make it by my own?




Friday, May 7, 2010
...but we can’t do it !




Friday, May 7, 2010
Why Google App Engine?
                •     System Administration for dummies
                •     Cheap, pay as much as...
How it works




Friday, May 7, 2010
Standard?




Friday, May 7, 2010
Limitations
                •     HTTP request must be done within 30 seconds, otherwise
                      GAE throws ...
Why?
                •     More than 30 seconds can cause user sleep (and servers will
                      be blocked)

...
Is first request slow?
                                         Dinamic Server
                                         all...
Signup & Web Console




Friday, May 7, 2010
Daily free resources
                •     1,300,000 http requests
                •     In/Out Bandwidth 1 Gb
           ...
Price list
                •     Outgoing Bandwidth...........$0.12/Gb
                •     Incoming Bandwidth..............
Who use GAE?




Friday, May 7, 2010
Tools
                •     Eclipse
                •     Maven
                •     Spring
                •     Spring ...
XMPP
                      Extensible Messaging and Presence Protocol (XMPP) is an
                       open, XML-based ...
Pull VS. push
                            Pull




                            Push




Friday, May 7, 2010
Game Protocol
                      The game engine is based on a protocol comunication built
                      on xmp...
XMPP
                                               Problem

                       Every chat client (pigdin, empathy, et...
XMPP and GAE
   • Add this code at appengine-web.xml file:
   <inbound-services> <service>xmpp_message</service> </inbound...
Task Queue
            Easy to enqueue

     TaskOptions taskOptions = TaskOptions.Builder.url("/start-game");
     taskOp...
Cache
                      Using JCache (JSR-107)

                public void usingJCache() throws CacheException{
     ...
Authentication




Friday, May 7, 2010
Message Management




Friday, May 7, 2010
Clients communication
                      The clients only knows the Google Bot




Friday, May 7, 2010
Messages
                      Game messages are transported as text enveloped in the
                      body of the xm...
Messages Type
                      Type                   Format                    Sample

                       START ...
TankAttack console




Friday, May 7, 2010
Google accounts
                Anyone has a Google Account can use your application
                You must define who i...
Links
                •     SpringFramework
                      http://www.springsource.org/
                •     Maven...
iPhone




Friday, May 7, 2010
iPhone: xcode




Friday, May 7, 2010
iPhone: all togheter
                      The iPhone game client consists of 3 frameworks




Friday, May 7, 2010
iPhone: cocos 2D




                      The iPhone game client consists of 3 frameworks




Friday, May 7, 2010
iPhone: cocos 2D




                          easy project setup


Friday, May 7, 2010
iPhone: cocos 2D
                      Some key features:

                • Sprites and Sprite Sheets
                • E...
iPhone: tiles map
                      The game uses a tiled background map




                The map was realized with...
Map layers
                      The map consist of three layer

             • Plain layer
             • Mountains layer...
iPhone: layers code
                CCTMXTiledMap *map;
                map = [CCTMXTiledMap tiledMapWithTMXFile:@"amap.tm...
Get tile at coordinates
          CCSprite sprite;
          *sprite = [[CCSprite alloc] initWithFile:@"sprite.png"];
    ...
iPhone: xmppframework
                        xmppframework it's an objective-c
                         implementation fo...
iPhone: XMPPClient Delegate
    - (void)xmppClientConnecting:(XMPPClient *)sender

    - (void)xmppClientDidConnect:(XMPPC...
iPhone: links
               Links:
               • cocos 2D: http://www.cocos2d-iphone.org
               • xmppframewor...
Android




Friday, May 7, 2010
Android




             •   Architecture, frameworks and tools
             •   Code unfolded
             •   Tips and t...
Android: architecture, tools, frameworks
                      Architecture and frameworks:

                • Android pla...
Android: sdk tools
                Create your emulator through Android SDK and AVD manager
               daniele@Daniele...
Android: create project
                  Create "android" type project into Netbeans




             That has this struc...
Android: emulator & adb

                         This will run the
                         emulator and deploy




     ...
Android: Rokon
                      Rokon basics

             public class TankAttack extends RokonActivity {
          ...
Android: smack
                      Smack basics:

             ConnectionConfiguration connConfig = new
             Con...
Android: tips&tricks
                      • Rokon is under development: things must be
                       done from s...
Android: links
                      •   Android SDK:
                          http://developer.android.com/sdk/index.htm...
Types of game
                      •   Cards Game: Poker, Bridge
                      •   Classic games: checkers, chess...
Success Stories:




                      Puzzle Pirate, Bang! Howdy




Friday, May 7, 2010
Success Stories:




                      Pet society, WORD challenge, Biggest Brain, Crazy planet




Friday, May 7, 2010
Success Stories:




                      Farmville, FishVille, Treasure Island, Café World




Friday, May 7, 2010
Types of game




                      Browser game like: Travian, OGame, hattrick




Friday, May 7, 2010
GAE Based: Neptune’s pride




                      It's a real time game




Friday, May 7, 2010
What about money?
                •     Playfish: Electronic Arts acquired Playfish for approximately
                    ...
Microtransaction
                      Players can purchase items using "game currency".

                To buy "game cur...
Link and resources
                •     How Buddypoke scales on Facebook using GAE
                      http://highscala...
Special thanks
                Danc (www.lostgarden.com)

                      for his free tiles map

                  ...
Contacts
                Davide Cerbo

                      Twitter: http://twitter.com/davide_cerbo
                    ...
Q&A




Friday, May 7, 2010
Upcoming SlideShare
Loading in …5
×

Playing between the clouds - Better Software 2010

3,244 views
3,154 views

Published on

The talk will present a small game developed for heterogeneous platforms like IPhone, Android and web. The backend will be done using XMPP standand and released on Google cloud computing platform: Google App Engine (GAE)

We'll show you how this is an excellent starting point for people that has good ideas but no available investments, especially if we need "real" scalable solutions.

Published in: Technology
1 Comment
6 Likes
Statistics
Notes
  • Thanks a lot for that pointer of using XMPP event messages to communicate under the radar of regular XMPP clients!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
3,244
On SlideShare
0
From Embeds
0
Number of Embeds
52
Actions
Shares
0
Downloads
58
Comments
1
Likes
6
Embeds 0
No embeds

No notes for slide

Playing between the clouds - Better Software 2010

  1. 1. Playing between the clouds Daniele Montagni Davide Cerbo Stefano Linguerri Friday, May 7, 2010
  2. 2. Who we are http://exmachina.ch http://pronetics.it Davide Cerbo Daniele Montagni Stefano Linguerri http://www.juglugano.ch http://www.jugroma.it Friday, May 7, 2010
  3. 3. Gameplay Gameset: Tankattack is a turn-based strategy game. The setting is a fixed map and player can control 2 tanks each game: Goal of the game: win the match by destroying all against player's tanks Friday, May 7, 2010
  4. 4. Gameplay Rules: •Map • it has fixed size of 480*320 pixels. It is virtually divided in 12*8 squares • it is not scrollable •Movements • each tank can move 2 squares every turn • tanks can move freely into these squares except regions that has mountains and where are the opposite tanks. •Damages • each tank has 2000 points of stamina • every shoot from the enemy cause 500 points of damage • when a tank reach 0 points of stamina is delete from the screen • a tank can shoot when is 2 squares far from the enemy. • Who destroy both of enemy's tanks win the game Friday, May 7, 2010
  5. 5. Multiplayer / Multiplatform Friday, May 7, 2010
  6. 6. Google App Engine Friday, May 7, 2010
  7. 7. Everybody can play • A successful game must have a huge players number • Players wants to interact to each other • Players prefers human enemy rather than CPU We must scale! • How? • Horizontally? No, thanks. We do not want limits • Vertically? uhm... Friday, May 7, 2010
  8. 8. Dilemma: Cluster? Cloud? Grid? Cloud: Grid: • More requests number • Less requests number • Small tasks • Large tasks Many SERVERS work for YOU Friday, May 7, 2010
  9. 9. Searching on Google and... ...found companies that offers Cloud or Grid Computing services Friday, May 7, 2010
  10. 10. If I want to make it by my own? Friday, May 7, 2010
  11. 11. ...but we can’t do it ! Friday, May 7, 2010
  12. 12. Why Google App Engine? • System Administration for dummies • Cheap, pay as much as the application grow up • Good installed software like Memcache, Bigtable etc... • Powerful Administration console with logs, access statistics, application management and so on... • JAVA, PYTHON and JVM based languages (Scala, Groovy, etc...) compatibility • Multi-standard Java compatibility • No OS, one less problem. • Many limitations, but can be a great opportunity to write scalable and balanced applications Friday, May 7, 2010
  13. 13. How it works Friday, May 7, 2010
  14. 14. Standard? Friday, May 7, 2010
  15. 15. Limitations • HTTP request must be done within 30 seconds, otherwise GAE throws a DeadlineExceededException • No socket • No threads • Only 1000 file per application • First requests can be slowly Friday, May 7, 2010
  16. 16. Why? • More than 30 seconds can cause user sleep (and servers will be blocked) • More security is not enough • Threads are cool, but tasks are much more manageable • Sometimes you have to be happy with what you have • Next slide... Friday, May 7, 2010
  17. 17. Is first request slow? Dinamic Server allocation Every new request will be forwarded to a new server only if the existing ones can't process it. This is called “automatic load balancing” and cause slowdown But...you can have as much more server as network traffic you produce! Friday, May 7, 2010
  18. 18. Signup & Web Console Friday, May 7, 2010
  19. 19. Daily free resources • 1,300,000 http requests • In/Out Bandwidth 1 Gb • CPU Time 6.5 CPU-h • Data Storage 1 Gb • Emails 7,000 • Recipients emailed 2,000 • Admins emailed 5,000 • Url fetch 657,000 • XMPP CALLS 657,000 • Image Manipulation 864,000 • Calls to Memcache 8,600,000 • Tasks invocation 100,000 • and a lot more... Friday, May 7, 2010
  20. 20. Price list • Outgoing Bandwidth...........$0.12/Gb • Incoming Bandwidth...........$0.10/Gb • CPU Time.....................$0.10/h • Stored Data..................$0.15/Gb • Recipients Emailed.........$0.0001/rec (Excluding taxes) Choose a billable quotas to prevent the cost of the application from exceeding your budget Friday, May 7, 2010
  21. 21. Who use GAE? Friday, May 7, 2010
  22. 22. Tools • Eclipse • Maven • Spring • Spring MVC Friday, May 7, 2010
  23. 23. XMPP Extensible Messaging and Presence Protocol (XMPP) is an open, XML-based protocol originally aimed at near-real- time, extensible instant messaging (IM) and presence information, but now expanded into the broader realm of message-oriented middleware. App Engine applications can send and receive instant messages to and from users of XMPP-compatible instant message services, including Google Talk Friday, May 7, 2010
  24. 24. Pull VS. push Pull Push Friday, May 7, 2010
  25. 25. Game Protocol The game engine is based on a protocol comunication built on xmpp Friday, May 7, 2010
  26. 26. XMPP Problem Every chat client (pigdin, empathy, etc...) can interact with our game, how can avoid it? Solution • Messages will be delivered in a non-conventional format, so common clients will refuse it. • Messages will be sent using a specified resource string • Messages that has no resource assigned from server will be rejected Friday, May 7, 2010
  27. 27. XMPP and GAE • Add this code at appengine-web.xml file: <inbound-services> <service>xmpp_message</service> </inbound-services> • We create a Servlet listening on a URL: http://???.appspot.com/_ah/xmpp/message/chat/ • Now we can receive messages: XMPPService xmpp = XMPPServiceFactory.getXMPPService(); Message message = xmpp.parseMessage(req);JID fromJid = message.getFromJid();String body = message.getBody(); • And we can send messages: JID j = new JID("example@gmail.com");String msgBody = "a text"; Message m = new MessageBuilder().withRecipientJids(j).withBody(msgBody).build(); boolean messageSent = false; XMPPService xmpp = XMPPServiceFactory.getXMPPService(); if (xmpp.getPresence(j).isAvailable()) { SendResponse status = xmpp.sendMessage(m); messageSent = (status.getStatusMap().get(j) == SendResponse.Status.SUCCESS); } Friday, May 7, 2010
  28. 28. Task Queue Easy to enqueue TaskOptions taskOptions = TaskOptions.Builder.url("/start-game"); taskOptions.param("roomId", roomId); Queue queue = QueueFactory.getQueue("start-game"); queue.add(taskOptions); • Easy to define a new task queue <queue-entries>   <queue>       <name>start-game</name>       <rate>20/s</rate> <bucket-size>1</bucket-size>   </queue> </queue-entries> The work to do when the task comes executed must be defined in a servlet Friday, May 7, 2010
  29. 29. Cache Using JCache (JSR-107) public void usingJCache() throws CacheException{     CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();     Cache cache = cacheFactory.createCache(new HashMap());     cache.put("key", "value");     cache.get("key"); } Using low level API public void usingGoogleService() throws CacheException{ MemcacheService memcache = MemcacheServiceFactory.getMemcacheService(); memcache.put("key", "value"); memcache.put("key2", "value2", Expiration.byDeltaSeconds(3000), SetPolicy.ADD_ONLY_IF_NOT_PRESENT); memcache.get("key"); } Friday, May 7, 2010
  30. 30. Authentication Friday, May 7, 2010
  31. 31. Message Management Friday, May 7, 2010
  32. 32. Clients communication The clients only knows the Google Bot Friday, May 7, 2010
  33. 33. Messages Game messages are transported as text enveloped in the body of the xmpp messages. <message type="chat" id="purple3c8b244a" to="tankattack.player2@gmail.com/23125316" from="tankattack.player1@gmail.com/23517326"> <x xmlns="jabber:x:event"> <composing/> </x> <body>1:1:2:2:2</body> <html xmlns="http://jabber.org/protocol/xhtml-im"> <body xmlns="http://www.w3.org/1999/xhtml">1:1:2:2:2</body> </html> <nos:x value="disabled" xmlns:nos="google:nosave"/> <arc:record otr="false" xmlns:arc="http://jabber.org/protocol/archive"/> </message> Friday, May 7, 2010
  34. 34. Messages Type Type Format Sample START START:[ROOM_ID]:[PLAYER_NUMBER] START:234523:1 MOVE 1:[XX]:[YY]:[ROTATION] 1:3:7:4 SHOOT 2:[SENDER]:[TARGET]:[DAMAGE] 2:1:3:500 ACK TRUE TRUE:[TANK_ID] TRUE:2 END GAME END:[ROOM_ID]:[PLAYER_LOSER_NUMBER] END:234523:1 Friday, May 7, 2010
  35. 35. TankAttack console Friday, May 7, 2010
  36. 36. Google accounts Anyone has a Google Account can use your application You must define who is Administrator using web console Obviously there are a simple API //return the serviceUserService userService = UserServiceFactory.getUserService();//you can create a url to loginuserService.createLoginURL("/...returnUrl...")//or an url to logoutuserService.createLogoutURL("/...returnUrl...")//use standard to retrieve logged userPrincipal user = request.getUserPrincipal(); Friday, May 7, 2010
  37. 37. Links • SpringFramework http://www.springsource.org/ • Maven http://maven.apache.org/ • Maven GAE plugin http://code.google.com/p/maven-gae-plugin/ • GAE Documentation http://code.google.com/appengine/docs/ Friday, May 7, 2010
  38. 38. iPhone Friday, May 7, 2010
  39. 39. iPhone: xcode Friday, May 7, 2010
  40. 40. iPhone: all togheter The iPhone game client consists of 3 frameworks Friday, May 7, 2010
  41. 41. iPhone: cocos 2D The iPhone game client consists of 3 frameworks Friday, May 7, 2010
  42. 42. iPhone: cocos 2D easy project setup Friday, May 7, 2010
  43. 43. iPhone: cocos 2D Some key features: • Sprites and Sprite Sheets • Effects: Lens, Ripple, Waves, Liquid, Twirl, etc. • Actions (behaviors): • Trasformation Actions: Move, Rotate, Scale, Jump, etc. • Composable actions: Sequence, Spawn, Repeat, Reverse • Ease Actions: Exp, Sin, Cubic, etc. • Misc actions: CallFunc, OrbitCamera • Tile Map support • Touch/Accelerometer support • OpenGL ES 1.1 based Friday, May 7, 2010
  44. 44. iPhone: tiles map The game uses a tiled background map The map was realized with Tiled Friday, May 7, 2010
  45. 45. Map layers The map consist of three layer • Plain layer • Mountains layer • Collision layer The collision layer is invisible in the game. Friday, May 7, 2010
  46. 46. iPhone: layers code CCTMXTiledMap *map; map = [CCTMXTiledMap tiledMapWithTMXFile:@"amap.tmx"]; [self addChild:map z:0 tag:1]; CCTMXLayer *layer = [map layerNamed:@"collision"]; Get a tile at a coordinate: int tileGID = [layer tileGIDAt:ccp(3, 4)]; Friday, May 7, 2010
  47. 47. Get tile at coordinates CCSprite sprite; *sprite = [[CCSprite alloc] initWithFile:@"sprite.png"]; [self addChild: sprite]; Animate a Sprite: id actionRotate = [CCRotateTo actionWithDuration:1 angle:aNangle]; id actionMove= [CCMoveTo actionWithDuration:1 position:aPosition]; [sprite runAction:[CCSequence actions:actionRotate, actionMove, nil]]; Friday, May 7, 2010
  48. 48. iPhone: xmppframework xmppframework it's an objective-c implementation fo xmpp protocol Create a client to listen messages XMPPClient *xmppClient = [[XMPPClient alloc] init]; [xmppClient setDomain:@"a.chatDomain.xyz"]; [xmppClient setPort:5222]; NSString *jid; jid = [NSString stringWithFormat:@"%@/%@", @"aUserName", @"aResource"]; [xmppClient setMyJID:[XMPPJID jidWithString:jid]]; [xmppClient setPassword:@"aPassword"]; [xmppClient setAutoLogin:YES]; [xmppClient setAllowsPlaintextAuth:NO]; [xmppClient setAutoPresence:YES]; [xmppClient setAutoRoster:YES]; [xmppClient addDelegate:aClientDelegate]; [xmppClient connect]; Friday, May 7, 2010
  49. 49. iPhone: XMPPClient Delegate - (void)xmppClientConnecting:(XMPPClient *)sender - (void)xmppClientDidConnect:(XMPPClient *)sender - (void)xmppClientDidNotConnect:(XMPPClient *)sender - (void)xmppClientDidDisconnect:(XMPPClient *)sender - (void)xmppClientDidRegister:(XMPPClient *)sender - (void)xmppClient:(XMPPClient *)sender didNotRegister:(NSXMLElement *)error - (void)xmppClientDidAuthenticate:(XMPPClient *)sender - (void)xmppClient:(XMPPClient *)sender didNotAuthenticate:(NSXMLElement *)error - (void)xmppClientDidUpdateRoster:(XMPPClient *)sender - (void)xmppClient:(XMPPClient *)sender didReceiveBuddyRequest:(XMPPJID *)jid - (void)xmppClient:(XMPPClient *)sender didReceiveIQ:(XMPPIQ *)iq - (void)xmppClient:(XMPPClient *)sender didReceiveMessage:(XMPPClient *)message { Friday, May 7, 2010
  50. 50. iPhone: links Links: • cocos 2D: http://www.cocos2d-iphone.org • xmppframework: http://code.google.com/p/xmppframework • Apple Dev Center: http://developer.apple.com/iphone/index.action Friday, May 7, 2010
  51. 51. Android Friday, May 7, 2010
  52. 52. Android • Architecture, frameworks and tools • Code unfolded • Tips and tricks Friday, May 7, 2010
  53. 53. Android: architecture, tools, frameworks Architecture and frameworks: • Android platform: 1.6 • Rokon game engine: 1.1.1 • Smack libraries: 3.1.0 Tools: • Android SDK for Mac: 2.1 (r5) • Netbeans: 6.8 • Android plugin for netbeans Friday, May 7, 2010
  54. 54. Android: sdk tools Create your emulator through Android SDK and AVD manager daniele@Daniele-Montagnis-MacBook~/android-sdk-mac_86/tools$./android  Starting Android SDK and AVD Manager... Friday, May 7, 2010
  55. 55. Android: create project Create "android" type project into Netbeans That has this structure ...than click "Run" project Friday, May 7, 2010
  56. 56. Android: emulator & adb This will run the emulator and deploy logcat through Netbeans show status: Friday, May 7, 2010
  57. 57. Android: Rokon Rokon basics public class TankAttack extends RokonActivity { public void onCreate() {      createEngine(480, 320, true);  } public void onLoad() {     ...     Background background = new TileTextureBackground(atlas, tileloader.getLayers());     ...     Sprite sprite = new Sprite(80, 180, spriteTexture); } public void onLoadComplete() {     rokon.setBackground(background);     rokon.addSprite(sprite); } Friday, May 7, 2010
  58. 58. Android: smack Smack basics: ConnectionConfiguration connConfig = new ConnectionConfiguration(host, port, service); XMPPConnection connection = new XMPPConnection(connConfig); connection.login(username, password); Presence presence = new Presence(Presence.Type.available); connection.sendPacket(presence); xmppClient.setConnection(connection); Message msg = new Message(to, Message.Type.chat); msg.setBody(text); connection.sendPacket(msg); Friday, May 7, 2010
  59. 59. Android: tips&tricks • Rokon is under development: things must be done from scratch! (ex. TileTextureBackground...) • Android plugin for Netbeans is not stable (build.xml must be modified...) • For rapid development use Eclipse that is supported by Android community: http://developer.android.com/sdk/eclipse- adt.html#installing Friday, May 7, 2010
  60. 60. Android: links • Android SDK: http://developer.android.com/sdk/index.html • Rokon: http://www.rokonandroid.com • Smack xmpp: http://www.igniterealtime.org/projects/smack • Netbeans plugin: http://kenai.com/projects/nbandroid/pages/Install Friday, May 7, 2010
  61. 61. Types of game • Cards Game: Poker, Bridge • Classic games: checkers, chess • Tamagotchi like: Pets Society, FishVille Friday, May 7, 2010
  62. 62. Success Stories: Puzzle Pirate, Bang! Howdy Friday, May 7, 2010
  63. 63. Success Stories: Pet society, WORD challenge, Biggest Brain, Crazy planet Friday, May 7, 2010
  64. 64. Success Stories: Farmville, FishVille, Treasure Island, Café World Friday, May 7, 2010
  65. 65. Types of game Browser game like: Travian, OGame, hattrick Friday, May 7, 2010
  66. 66. GAE Based: Neptune’s pride It's a real time game Friday, May 7, 2010
  67. 67. What about money? • Playfish: Electronic Arts acquired Playfish for approximately 300$ milion dollars. • Zynga: last year has earned over 250$ milion dollars. • Three Rings: Puzzle Pirates takes in approximately $230,000 a month. ...all of them are based on microtransaction Friday, May 7, 2010
  68. 68. Microtransaction Players can purchase items using "game currency". To buy "game currency" player use real world money. Friday, May 7, 2010
  69. 69. Link and resources • How Buddypoke scales on Facebook using GAE http://highscalability.com/blog/2010/1/22/how-buddypoke-scales-on-facebook-using-google-app-engine.html • Games on App Engine: An interview with Jay Kyburz, developer for Neptune’s Pride http://googleappengine.blogspot.com/2010/04/games-on-app-engine-interview-with-jay.html • PlayFish http://www.playfish.com • Zynga http://www.zynga.com • ThreeRing http://www.threerings.net • Neptune’s pride http://np.ironhelmet.com • Hattrick http://www.hattrick.org • Travian http://www.travian.com • OGame http://www.ogame.org Friday, May 7, 2010
  70. 70. Special thanks Danc (www.lostgarden.com) for his free tiles map http://www.lostgarden.com/2006/07/more-free-game-graphics.html Android: Fabio Marinelli for help in Rokon development twitter: http://twitter.com/fmarinelli Friday, May 7, 2010
  71. 71. Contacts Davide Cerbo Twitter: http://twitter.com/davide_cerbo Linkedin: http://www.linkedin.com/in/davidecerbo Stefano Linguerri Twitter: http://twitter.com/eljeko Linkedin: http://www.linkedin.com/in/linguerri Daniele Montagni Twitter: http://twitter.com/dmontagni LinkedIn: http://it.linkedin.com/in/danielemontagni Friday, May 7, 2010
  72. 72. Q&A Friday, May 7, 2010

×