📄 mmpdev-article.xhtml
字号:
that Twisted can provide such a diverse array of services internally withoutsuffering from an entanglement of dependencies.</p><p>The chief concept governing Twisted's design in this regard is<em>separability</em>. The easier it is to separately instantiate twodifferent classes and have them perform their function, the less likely it isthat changes in one will destabilize the other. Also, the less a new userneeds to know in order to use one of the classes.</p><p>Since separate instances are considered a good thing in general, Twistedavoids inheritance in most cases, using it only where two classes must be verytightly coupled, or the base class is very simple and provides only utilityfunctionality.</p><p>Again, this approach has good security properties. When some objects arenetwork accessible, it is important to carefully separate out sensitiveoperations and make sure they are far from a potential source of access. Ifmost functions are factored into separate classes, that separation guardsagainst mistakes in access control. </p><p>This is an old idea. In fact, it was in some ways the origin ofobject-oriented programming, stemming from the Actor model of simulation. TheActor model is still mainly of interest for the reasons mentioned above;networking and security. This is also a new idea. Most of the substance ofthe craze over various "component architectures" can be boiled down to onesimple feature. Such architectures force objects to separate theirfunctionality into discrete, separately usable packages. </p><p>Twisted has such a boiled down component architecture in<code>twisted.python.components</code>. This is based heavily on the Zope3component architecture. While it does offer some advanced features, it is ingeneral a very simple system. It is almost entirely based around one feature,which is the Adapter.</p><p>This listing shows the basic use of an Adapter. Strictly speaking, anAdapter is simply a class that takes one argument to its constructor. Adaptersare registered to represent another class in contexts where a certain interfaceis required, so Adapters will always implement at least one interface.</p><div class="py-listing"><pre class="python"><span class="py-src-keyword">from</span> twisted.python.components <span class="py-src-keyword">import</span> getAdapter, registerAdapter, Interface<span class="py-src-keyword">class</span><a name="IA"><span class="py-src-identifier"> IA</span></a>(Interface): <span class="py-src-comment"># Define an interface that implements </span> <span class="py-src-keyword">def</span><a name="a"><span class="py-src-identifier"> a</span></a>(self): <span class="py-src-comment"># a sample method method, "a".</span> <span class="py-src-string">"A sample method."</span><span class="py-src-keyword">class</span><a name="A"><span class="py-src-identifier"> A</span></a>: <span class="py-src-comment"># define an adapter class</span> __implements__ = IA <span class="py-src-comment"># that implements our IA interface</span> <span class="py-src-keyword">def</span><a name="__init__"><span class="py-src-identifier"> __init__</span></a>(self, original): <span class="py-src-comment"># keep track of the object being wrapped</span> self.original = original <span class="py-src-keyword">def</span><a name="a"><span class="py-src-identifier"> a</span></a>(self): <span class="py-src-comment"># define the method required by our</span> <span class="py-src-keyword">print</span> <span class="py-src-string">'a'</span>, <span class="py-src-comment"># interface, and have it print 'a'</span> self.original.b() <span class="py-src-comment"># then call back to the object we're adapting</span><span class="py-src-keyword">class</span><a name="B"><span class="py-src-identifier"> B</span></a>: <span class="py-src-comment"># the hapless B class doesn't know anything</span> <span class="py-src-keyword">def</span><a name="b"><span class="py-src-identifier"> b</span></a>(self): <span class="py-src-comment"># about its adapter, and it defines one</span> <span class="py-src-keyword">print</span> <span class="py-src-string">'b'</span> <span class="py-src-comment"># method which displays 'b'</span>registerAdapter(A, B, IA) <span class="py-src-comment"># register A to adapt B for interface IA</span>adapter = getAdapter(B(), IA, None) <span class="py-src-comment"># adapt a new B instance with an A instance</span>adapter.a() <span class="py-src-comment"># call the method defined by interface IA</span></pre><div class="py-caption">twisted.python.components example - <span class="py-filename">component-example.py</span></div></div><p>The component system in Twisted has been presented here as a way tointegrate external game services. Much of the "behind the scenes" workinvolved in the examples, especially the WebMVC examples, was being done byAdapters. The same properties that make it useful to develop a Web-publishingsystem agnostic to the objects being published make it useful for other sortsof separation. For example, a Magic system agnostic to world geometryrepresentation.</p><h2>Low Level Integration: Protocols and Networking</h2><p>The lowest level of the Twisted infrastructure is<code>twisted.internet</code>. This portion of Twisted is the bedrock uponwhich the rest of it is built. Mostly, <code>twisted.internet</code> is anetworking core implemented in the style of the Reactor event-responsepattern.</p><p>Part of Twisted's design is that it is pluggable at every level, from thehighest-level message abstraction to the bits and bytes being sent on the wire.This follows directly from Twisted's original MMP focus: this allows yourTwisted application to satisfy diverse requirements for efficiency andflexibility depending on your exact needs. </p><p>The previous examples have already used the twisted.internet APIs in orderto connect Web and PB servers to the internet. Here we will show how to writeyour own kind of server. The simplest example of how to extend Twisted at thislevel is an echo server.</p><div class="py-listing"><pre class="python"><span class="py-src-keyword">from</span> twisted.internet.protocol <span class="py-src-keyword">import</span> Protocol, Factory<span class="py-src-keyword">class</span><a name="Echo"><span class="py-src-identifier"> Echo</span></a>(Protocol): <span class="py-src-comment"># Class designed to manage a connection.</span> <span class="py-src-keyword">def</span><a name="dataReceived"><span class="py-src-identifier"> dataReceived</span></a>(self, data): <span class="py-src-comment"># Method called when data is received.</span> self.transport.write(data) <span class="py-src-comment"># When we receive data, write it back out.</span><span class="py-src-keyword">class</span><a name="EchoFactory"><span class="py-src-identifier"> EchoFactory</span></a>(Factory): <span class="py-src-keyword">def</span><a name="buildProtocol"><span class="py-src-identifier"> buildProtocol</span></a>(self, address): <span class="py-src-keyword">return</span> Echo() <span class="py-src-comment"># Build Protocols on incoming connections</span><span class="py-src-keyword">from</span> twisted.internet <span class="py-src-keyword">import</span> reactorreactor.listenTCP(1234, EchoFactory()) <span class="py-src-comment"># Bind TCP Port 1234 to an EchoFactory</span>reactor.run() <span class="py-src-comment"># run the main loop until the user interrupts it</span></pre><div class="py-caption"> Echo Server - <span class="py-filename">echoserver.py</span></div></div><p>This small Python script is a fully functional, asynchronous, multiplexingserver using Twisted as a library. This would be a good basic example to beginworking with in order to build a server-side processing system that needs tointeract with a non-standard legacy protocol in an existing multiplayerinfrastructure.</p><p>It is often desirable to implement alternative clients for such servers toautomate frequently-performed tasks, or to perform load-testing. Twisted alsoprovides complete client-side support, intentionally designed to mirror theserver-side support as closely as possible.</p><div class="py-listing"><pre class="python"><span class="py-src-keyword">from</span> twisted.internet.protocol <span class="py-src-keyword">import</span> Protocol, ClientFactory<span class="py-src-keyword">class</span><a name="Shout"><span class="py-src-identifier"> Shout</span></a>(Protocol): <span class="py-src-comment"># Class designed to manage a connection.</span> stringToShout = <span class="py-src-string">"Twisted is great!"</span> <span class="py-src-comment"># a string to send the echo server</span> <span class="py-src-keyword">def</span><a name="connectionMade"><span class="py-src-identifier"> connectionMade</span></a>(self): <span class="py-src-comment"># Method called when connection established.</span> self.buf = <span class="py-src-string">""</span> <span class="py-src-comment"># create an empty buffer</span> <span class="py-src-keyword">print</span> <span class="py-src-string">"Shouting:"</span>, repr(self.stringToShout) self.transport.write(self.stringToShout) <span class="py-src-keyword">def</span><a name="dataReceived"><span class="py-src-identifier"> dataReceived</span></a>(self, data): <span class="py-src-comment"># Method called when data is received.</span> self.buf += data <span class="py-src-comment"># buffer any received data</span> <span class="py-src-keyword">if</span> self.buf == self.stringToShout: <span class="py-src-comment"># if we've received the full message</span> self.transport.loseConnection() <span class="py-src-comment"># then close the connection.</span> <span class="py-src-keyword">print</span> <span class="py-src-string">"Echoed:"</span>, repr(self.stringToShout) reactor.stop()<span class="py-src-keyword">class</span><a name="ShoutClientFactory"><span class="py-src-identifier"> ShoutClientFactory</span></a>(ClientFactory): <span class="py-src-keyword">def</span><a name="buildProtocol"><span class="py-src-identifier"> buildProtocol</span></a>(self, address): <span class="py-src-keyword">return</span> Shout() <span class="py-src-comment"># Build Protocols on incoming connections</span><span class="py-src-keyword">from</span> twisted.internet <span class="py-src-keyword">import</span> reactor<span class="py-src-comment"># connect to local port 1234 and expect an echo.</span>reactor.connectTCP(<span class="py-src-string">"localhost"</span>, 1234, ShoutClientFactory())reactor.run() <span class="py-src-comment"># run the main loop until the user interrupts it</span></pre><div class="py-caption"> Shout Client - <span class="py-filename">shoutclient.py</span></div></div><p>Notice that both the client and server examples end with<code>reactor.run()</code>. The same Reactor object is used on both client andserver. In fact, any number of clients and/or servers can be run within thesame process. </p><h2>Development Community</h2><p>Twisted provides many other protocols which may be of use. Currently itsupports more than 10 RFCs, including internet mail protocols, asynchronousdatabase access, domain name servers and clients, and internet relay chat.</p><p>More interesting than the additional tools which already exist, though, isthe potential for future development. A developer community with sustainedinterest and investment in your platform of choice is important to assure thatsupport does not disappear overnight. Also, the ability to collaborate withand get support from an existing community can potentially vastly reduce theamount of time it takes to develop a feature that does not yet exist. </p><p>A large and lively support community like Twisted's can arise from projectswhich are not open-source, such as Java. Such a community, however, isindispensable to the reliability of a framework.</p><p>As an added bonus, open-source development communities tend to be interestedin obscure and difficult problems which are tricky to get right, but not reallya competitive advantage for anyone. People who are working on tools likeTwisted for their own use are very concerned about stability, and thecollective focus of many diverse organizations on one project amplifies itsrobustness considerably </p><h2>Summary</h2><p>When you are developing an MMP game, it's not just a game. It's an entiresmall universe of infrastructure. You need tools for continuing development,marketing, and support. The problems required to provide those tools arecomplex, deep, and not much fun to solve if you are concerned with games.</p><p>Using an existing solution is a good idea, if the solution is suitable.It's difficult to find suitable solutions, though, and many of them are missingsome useful, if not critical, qualifications.</p><p>Twisted can be a useful solution for many games. It provides many differentnetwork access mechanisms, including a flexible, general client-server protocoland web publishing. Spanning these protocols are useful abstractions likeauthentication.</p><p>Twisted was designed with security in mind. It is important to thedevelopment team to keep it secure and stable, since it is used to run thetwistedmatrix.com domain.</p><p>Twisted is extensible at many levels. If the functionality you need doesn'texist, the hook to build it probably does. A large and diverse developmentcommunity ensures that this will continue to be the case. </p><p>Very few people will need all of what Twisted has to offer, but it's stillnot hard to apply only some of it, by providing only one or two ancillary gameservices from a Twisted server. Evaluating an infrastructure decision is veryimportant.</p><div style="padding:1em;background-color:#eeeeee;border:thin #cccccc solid"><h2>References</h2><ol> <li><u>Manager Design Pattern</u> Event Helix, Inc. <a href="http://www.eventhelix.com/RealtimeMantra/ManagerDesignPattern.htm">http://www.eventhelix.com/RealtimeMantra/ManagerDesignPattern.htm</a></li><li><u>Writing Servers in Twisted</u>, Moshe Zadka. <a href="http://twistedmatrix.com/documents/howto/servers">http://twistedmatrix.com/documents/howto/servers</a></li><li><u>E in a Walnut</u>, Marc Steigler. <a href="http://www.skyhunter.com/marcs/ewalnut.html">http://www.skyhunter.com/marcs/ewalnut.html</a></li><li><u>And Then Came Zope...</u>, Larry O'Brien. <a href="http://www.sdtimes.com/cols/webwatch_023.htm">http://www.sdtimes.com/cols/webwatch_023.htm</a></li><li><u>Proto Patterns</u>, Ward Cunningham Et. Al. <a href="http://www.c2.com/cgi/wiki?ProtoPatterns">http://www.c2.com/cgi/wiki?ProtoPatterns</a></li><li><u>Reactor -- An Object Behavioral Pattern for Event Demultiplexing andEvent Handler Dispatching</u>, Douglas C.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -