12.07.2015 Views

Implementation of a Peer-to-Peer Multiplayer Game with ... - DVS

Implementation of a Peer-to-Peer Multiplayer Game with ... - DVS

Implementation of a Peer-to-Peer Multiplayer Game with ... - DVS

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Applications implement this interface and pass a reference <strong>to</strong> an instance <strong>of</strong> the implementing classwhen calling contact<strong>Peer</strong>(). The implemented class may carry any context information that is necessaryfor the application. Depending on the outcome <strong>of</strong> the contact operation, either onContact() oronContactFail() are invoked.4.6.3 Convenience Helper ClassesCUSP only provides unidirectional streams. So applications requiring a TCP-like bidirectional connectionhave <strong>to</strong> set up one stream for each direction. The typical approach is the following. The client (i.e., thenode that initialates the connection) creates an outgoing stream <strong>to</strong> the server (which is listening at acommonly known service). Additionally, the client starts listening and sends the corresponding serviceID <strong>to</strong> the server via the stream. The server receives the service ID and creates a stream <strong>to</strong> the client. Sincethis is a common pattern, the helper class BiDiConn implements the client side functionality, providingan easy-<strong>to</strong>-use bidirectional connection interface.Another common pattern is the single request-response cycle as typically appearing <strong>with</strong> an RPC call.For that purpose the two classes SingleCycleConn (client side) and SingleCycleServer (server side)were implemented. SingleCycleConn basically just provides the request functionality that takes therequest message as a string and returns the server’s response as a string. There are a few variants <strong>of</strong>that functionality for both synchronous (blocking) and asynchronous invocation. SingleCycleServerexpects an application callback that handles incoming requests and returns the response message (bothpassed as strings).Two additional helper classes are BufferedOutStream and AbortableContactHandler. The formeris similar <strong>to</strong> a normal OutStream but allows <strong>to</strong> write data in any state. It buffers all written datauntil the encapsulated OutStream is ready <strong>to</strong> write. The downside <strong>of</strong> using BufferedOutStream isthat the application does not have any information about how much <strong>of</strong> the data is already written.AbortableContactHandler performs an EndPoint::contact() call that is abortable, i.e., after callingits abort() method, the handler’s onContact() will not be invoked anymore.4.6.4 DiscussionThe mapping <strong>of</strong> the Standard ML’s API signatures/structures <strong>to</strong> C++ classes using IdBucket, handles,and C++ wrapper classes is surprisingly effective and problem-free after the basic patterns have becomeclear. Still, a lot <strong>of</strong> glue code is necessary for each new structure-<strong>to</strong>-class mapping, but always applyingthe same pattern, most <strong>of</strong> the work can be done by copying existing code.In contrast <strong>to</strong> that, the translation <strong>of</strong> the callback mechanisms has not shown <strong>to</strong> be satisfac<strong>to</strong>ry. TheJava-like way <strong>of</strong> passing callbacks suffers from the lack <strong>of</strong> anonymous classes in C++. Thus keepingtrack <strong>of</strong> the context still needs much extra code. A better alternative solution may be <strong>to</strong> use a C++signal framework like libsigc++ 10 or Boost signals 11 . These frameworks at least <strong>of</strong>fer more flexibility <strong>to</strong>the application developer, not requiring <strong>to</strong> implement a new class for each callback use. But still, thesedo not <strong>of</strong>fer the convenience <strong>of</strong> closures.The development <strong>of</strong> the CUSP networking code for Planet π4 has shown that the development forsuch a heavily callback-driven API quickly becomes tricky as it is likely <strong>to</strong> produce bugs that are hard<strong>to</strong> discover. A main reason is that callbacks that are passed <strong>to</strong> a function are usually called later inthe event loop (e.g., after data as been received), but the callback may also be called immediately from<strong>with</strong>in the function, if certain conditions apply. One example is the write() method <strong>of</strong> OutStream. Whilein normal operation its callback is invoked later when all (or most <strong>of</strong>) the data has been transmitted, the10 http://libsigc.sourceforge.net/11 http://www.boost.org/doc/libs/1_40_0/doc/html/signals.html4 <strong>Implementation</strong> 69

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!