Search code examples
actionscript-3multiplayerflash-cs3xmlsocket

XMLSocket approach for a multiplayer card/pet game


I'm fairly new to multiplayer design and am struggling to find the information i need to continue in my synchronous (you cannot disconnect and continue at a later time similar to hearthstone), turn-based, pvp "card" game. I'm using AS3 (flash) as my client side and node.js with javascript + mysql for the server-side processing.

In this game, the cards are created by the players themselves. It's importnat to note that these "cards" are assembled from individual building blocks (library swf files); The database keeps track of how these cards are made/assembled. Because there can be hundreds of uniqely made cards per player, i need a good way to load information about these on demand for pvp battles or for modding/deck building purposes.

What i've accomplished so far:

  • i have successfully sent data to server and sent data back to flash over an XMLSocket.
  • I have successfully assembled a single "card" from database information.

What questions i really need to answer:

Flash asynchronously loads content so i'm not sure how to approach making multiple data requests for (sometimes simultaneous) battle actions, card information, or card assembly (THere can potentially be a lot of cards and card building blocks so i feel it would be inefficient to load all data with a single request). I can either have the classes responsible make those calls OR create a messenger class to handle all requests/disconnects/reconnects/messages for the entire game.

  • For the global messenger class approach, I am not sure how to retrieve the fully loaded data from the messenger by upstream code (as3 continues execution even if data isnt there) or how to ensure that the request pertains to the appropriate call

    • i could use a ID request system to uniquely identify the socket request
    • i could develop a custom event driven system
    • I could spawn multiple messenger objects in each class that i need one. <- im leaning towards this option.
  • On a similar note, perhaps i should handle database requests within card objects or preload all data prior to game start (This would limit me to only query for opponent card and battle data on the fly). With the one call per card object approach I was unsure if #requests per card per player will overwhelm my server or not. Node.js scales very well but i dont have the networking knowledge to understand whether all these simultaneous request on a single port would be too much.

EDIT: I'm strongly leaning towards using a singleton XMLSocket Wrapper or an extended class of some sort to deal with all networking transactions since that seems intuitive to me. I would highly appreciate your feedback on the best approach to notify my code that the messenger received and parsed the message it asked for (since flash will proceed with execution even if data isnt there i need a good way to do this.). I read that using custom events isnt all that terrific and there are better ways... but this is how i'd approach the problem.

For example i can do (pseudocode).

m:Messenger = new Messenger(); //this is my wrapper
m.addEventListener(new CustomEvent(CustomEvent.EVENTSTRING, parseData));
m.sendData(DataObject); //calls 

function parseData(e:CustomEvent) {
    xml(e.data);
}

import flash.net.XMLSocket;
import globals.*;
public class Messenger 
{
    public static var _Socket:XMLSocket; 
    public var xml:XML;

    public function Messenger() 
    {
        _Socket = new XMLSocket(vars._Server, vars._Port);
        _Socket.addEventListener(Event.CONNECT, onConnect);  
        _Socket.addEventListener(IOErrorEvent.IO_ERROR, onError); 
    }

    private function onConnect(evt:Event):void  {  
        trace("Connected");  
        _Socket.removeEventListener(Event.CONNECT, onConnect);  
        _Socket.removeEventListener(IOErrorEvent.IO_ERROR, onError);  

        _Socket.addEventListener(DataEvent.DATA, onDataReceived);
        _Socket.addEventListener(Event.CLOSE, onSocketClose);              
    }  

    private function onDataReceived(evt:DataEvent):void  {  
        //trigger correct event here based on data identifier w/ dispatchEvent(EVENTSTRING);
    }  
}

Solution

  • About card assembly - you should store data in database that does not require recalculation, so if a card is built out of several "building blocks", you could, for example, store a sequence of blocks in a single line of database, so that when a card info for the opponent is retrieved, you just select from player_cards where player_id=... and deck_id=... and then assemble the gathered data on AS3 side. You should definitely not put this load on server.

    About asynchronous loading - make sure your PVP handshake waits for both sides to successfully process cards for both own side and enemy side. You can load data asynchronously, but you'd better do a full preload while displaying a "Loading bla-bla" screen to the player, then operate with received data. Yes, you can do multiple requests to your server, and yes, you can wait for all of them to success prior to displaying a "Battle start" screen, in the meantime AS3 will wait for the loading to complete. But, about single vs multiple request - I say go with as few as possible, because every added request at the init stage is a potential to add a thousand or so requests to a single server, and a potential to put just too much strain on your server side, resulting in a self-initiated DDoS. Make sure your database structure allows streamlining of data reading operation right into the client side, so that your server is not under a heavy load from preparing the data. But also make sure to double check whatever clients report to the server, as the primary rule of client-server games is "client always lies" assumption.

    Since you are planning to make server process player actions mid-battle, you can create either temporary tables on the server (SQL-based) or state objects (process-based) that will contain current game state with links to whatever abilities the cards have and their state (in hand, discarded, in play, destroyed, etc), so that when an action from the player comes, saying "this card changes state from A to B" you can easily verify if that card is in A state, then switch it into B state, performing any actions that result from this state change on both server and client sides. Make sure to clean up this set of data once the battle is over. This requires thorough planning of your server side, so that your game engine will not stall the server while running some action sequence due to excess SQL requests, for example.

    About messengers - probably you should use a session approach, together with login/password authentication this way you will have sessions available as keys to determine which battle does this data packet belongs. I think also make AS3 side periodically send pull requests to the server, which should be quickly parsed by the server and the submitted enemy/server actions returned as quickly as possible, this will also ensure that disconnects will be handled and reconnects allowed.

    Overall, the question is very broad, and only tips can be given, not even examples.