Update of /cvsroot/bzflag/pybzflag/BZFlag
In directory sc8-pr-cvs1:/tmp/cvs-serv25479/BZFlag
Modified Files:
Client.py Network.py
Added Files:
Server.py
Log Message:
Refactored out all the client code that will also be useful for a server
--- NEW FILE: Server.py ---
""" BZFlag.Client
Provides the BaseServer class, which implements basic communication
with clients and provides hooks for adding more functionality
in subclasses.
"""
#
# Python BZFlag Protocol Package
# Copyright (C) 2003 Micah Dowty <micahjd@...>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
import BZFlag
from BZFlag import Network, Protocol, Errors, Player, Game, World, Event, Flag
from BZFlag.Protocol import FromServer, ToServer, Common
from StringIO import StringIO
class BaseServer:
"""Implements a very simple but extensible BZFlag server.
This client can accept connections from clients, and it has a system
for asynchronously processing messages. This class does not provide
any message handler implementations.
The methods of this class and its subclasses use the following
naming conventions:
- Low-level socket handlers should be of the form handleFoo()
- Event handlers for messages should be of the form onMsgFoo()
- Event handlers for other events should be of the form onFoo()
All onFoo() and onMsgFoo() events are observable and traceable,
See the Event class for more information about this feature.
"""
def __init__(self, **options):
"""Any options passed to the constructor will be sent to setOptions"""
self.tcp = None
self.udp = None
self.clients = []
self.options = {
'interface': None,
}
Event.attach(self, 'onConnect', 'onAnyMessage', 'onUnhandledMessage',
'onDisconnect')
# Add events for all messages, with onUnhandledMessage as an
# unhandled event handler.
for message in Common.getMessageDict(ToServer).values():
eventName = self.getMsgHandlerName(message)
if hasattr(self, eventName):
event = Event.Event(getattr(self, eventName))
else:
event = Event.Event()
event.unhandledCallback = self.onUnhandledMessage
setattr(self, eventName, event)
self.init()
self.setOptions(**options)
def getMsgHandlerName(self, messageClass):
return "on%s" % messageClass.__name__
def init(self):
"""A hook for subclasses to add initialization in the proper sequence"""
pass
def setOptions(self, **options):
self.options.update(options)
if 'eventLoop' in options.keys():
self.eventLoop = options['eventLoop']
else:
self.eventLoop = Event.EventLoop()
if 'server' in options.keys():
self.connect(options['server'])
def getSupportedOptions(self):
return self.options.keys()
def connect(self, server):
"""This does the bare minimum necessary to connect to the
BZFlag server. It does not negotiate flags, obtain the
world database, or join the game. After this function
returns, the client is connected to the server and can
receive and transmit messages.
"""
# Establish the TCP socket
if self.tcp:
self.disconnect()
self.tcp = Network.Socket()
self.tcp.connect(server, Common.defaultPort)
self.tcp.setBlocking(0)
self.eventLoop.add(self.tcp)
# Until we establish a UDP connection, we'll need to send
# normally-multicasted messages over TCP
self.multicast = self.tcp
# Now we have to wait for the server's Hello packet,
# with the server version and client ID.
self.tcp.handler = self.handleHelloPacket
### The End ###
Index: Client.py
===================================================================
RCS file: /cvsroot/bzflag/pybzflag/BZFlag/Client.py,v
retrieving revision 1.20
retrieving revision 1.21
diff -w -u -r1.20 -r1.21
--- Client.py 8 Jul 2003 20:31:25 -0000 1.20
+++ Client.py 8 Jul 2003 21:36:45 -0000 1.21
@@ -3,6 +3,9 @@
Provides the BaseClient class, which implements basic communication
with the server and provides hooks for adding more functionality
in subclasses.
+
+Provides subclasses providing logic to update the game state,
+and providing player functionality.
"""
#
# Python BZFlag Protocol Package
@@ -28,70 +31,26 @@
from StringIO import StringIO
-class BaseClient:
- """Implements a very simple but extensible BZFlag client.
- This client can connect and disconnect, and it has a system
- for asynchronously processing messages. This class only processes
- messages related to upkeep on the server-client link, such as
- lag ping, disconnection, and UDP-related messages.
-
- The methods of this class and its subclasses use the following
- naming conventions:
-
- - Low-level socket handlers should be of the form handleFoo()
- - Event handlers for messages should be of the form onMsgFoo()
- - Event handlers for other events should be of the form onFoo()
-
- All onFoo() and onMsgFoo() events are observable and traceable,
- See the Event class for more information about this feature.
+class BaseClient(Network.Endpoint):
+ """On top of the basic message processing provided by Network.Endpoint,
+ this class implements connecting to a server, and processing the
+ basic messages necessary to keep a client-server connection going.
"""
- def __init__(self, **options):
- """Any options passed to the constructor will be sent to setOptions"""
- self.tcp = None
- self.udp = None
- self.connected = 0
- self.options = {
- 'server': None,
- }
-
- Event.attach(self, 'onConnect', 'onAnyMessage', 'onUnhandledMessage',
- 'onDisconnect')
-
- # Add events for all messages, with onUnhandledMessage as an
- # unhandled event handler.
- for message in Common.getMessageDict(FromServer).values():
- eventName = self.getMsgHandlerName(message)
- if hasattr(self, eventName):
- event = Event.Event(getattr(self, eventName))
- else:
- event = Event.Event()
- event.unhandledCallback = self.onUnhandledMessage
- setattr(self, eventName, event)
-
- self.init()
- self.setOptions(**options)
-
- def getMsgHandlerName(self, messageClass):
- return "on%s" % messageClass.__name__
+ outgoing = ToServer
+ incoming = FromServer
def init(self):
- """A hook for subclasses to add initialization in the proper sequence"""
- pass
-
- def setOptions(self, **options):
- self.options.update(options)
-
- if 'eventLoop' in options.keys():
- self.eventLoop = options['eventLoop']
- else:
- self.eventLoop = Event.EventLoop()
+ self.connected = 0
+ self.options.update({
+ 'server': None,
+ })
+ Event.attach(self, 'onConnect', 'onDisconnect')
+ def setOptions(**options):
if 'server' in options.keys():
self.connect(options['server'])
-
- def getSupportedOptions(self):
- return self.options.keys()
+ self.onSetOptions.observe(setOptions)
def connect(self, server):
"""This does the bare minimum necessary to connect to the
@@ -129,9 +88,6 @@
self.connected = 0
self.onDisconnect()
- def run(self):
- self.eventLoop.run()
-
def onDisconnect(self):
self.eventLoop.stop()
@@ -153,25 +109,6 @@
self.connected = 1
socket.handler = self.handleMessage
self.onConnect()
-
- def handleMessage(self, socket, eventLoop):
- """This is a callback used to handle incoming socket
- data when we're expecting a message.
- """
- # This can return None if part of the mesasge but not the whole
- # thing is available. The rest of the message will be rebuffered,
- # so we'll read the whole thing next time this is called.
- msg = socket.readMessage(FromServer)
- if msg:
- msg.socket = socket
- msg.eventLoop = eventLoop
- handler = getattr(self, self.getMsgHandlerName(msg.__class__))
- if self.onAnyMessage(msg):
- return
- handler(msg)
-
- def onUnhandledMessage(self, msg):
- raise Errors.ProtocolWarning("Unhandled message %s" % msg.__class__.__name__)
def onMsgSuperKill(self, msg):
"""The server wants us to die immediately"""
Index: Network.py
===================================================================
RCS file: /cvsroot/bzflag/pybzflag/BZFlag/Network.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -w -u -r1.8 -r1.9
--- Network.py 8 Jul 2003 19:47:29 -0000 1.8
+++ Network.py 8 Jul 2003 21:36:45 -0000 1.9
@@ -34,6 +34,7 @@
"""
def __init__(self, protocol='TCP'):
self.readBuffer = ''
+ if protocol:
self.socket = getattr(self, "new%sSocket" % protocol)()
def newTCPSocket(self):
@@ -121,6 +122,23 @@
port = int(port)
self.socket.connect((host, port))
+ def bind(self, port, interface=None):
+ """Bind this socket to the given port and optional interface,
+ and start listening for connections.
+ """
+ if interface is None:
+ interface = socket.gethostname()
+ self.socket.bind(interface, port)
+ self.socket.listen(5)
+
+ def accept(self):
+ """Accept an incoming connection on a socket that has been bind()'ed,
+ returning a Socket class for the new connection.
+ """
+ s = Socket(None)
+ (s.socket, s.address) = self.socket.accept()
+ return s
+
def poll(self, eventLoop):
"""This is designed for event driven programming with
one or more sockets. The event loop should call this
@@ -164,5 +182,95 @@
except KeyError:
raise Errors.ProtocolWarning("Received unknown message type 0x%04X" % header.id)
return msgClass(str(header) + body)
+
+
+class Endpoint:
+ """Abstract base class for an endpoint in the BZFlag client-server connection.
+ This is a base class for both BaseClient and BaseServer.
+
+ This class provides a system for asynchronously processing messages
+ and dispatching them to event handler functions.
+
+ The methods of this class and its subclasses use the following
+ naming conventions:
+
+ - Low-level socket handlers should be of the form handleFoo()
+ - Event handlers for messages should be of the form onMsgFoo()
+ - Event handlers for other events should be of the form onFoo()
+
+ All onFoo() and onMsgFoo() events are observable and traceable,
+ See the Event class for more information about this feature.
+ """
+
+ # Protocol modules for the messages sent in and out of this endpoint
+ outgoing = None
+ incoming = None
+
+ def __init__(self, **options):
+ """Any options passed to the constructor will be sent to setOptions"""
+ self.tcp = None
+ self.udp = None
+ self.options = {}
+
+ from BZFlag import Event
+ Event.attach(self, 'onAnyMessage', 'onUnhandledMessage', 'onSetOptions')
+
+ # Add events for all messages, with onUnhandledMessage as an
+ # unhandled event handler.
+ for message in Common.getMessageDict(self.incoming).values():
+ eventName = self.getMsgHandlerName(message)
+ if hasattr(self, eventName):
+ event = Event.Event(getattr(self, eventName))
+ else:
+ event = Event.Event()
+ event.unhandledCallback = self.onUnhandledMessage
+ setattr(self, eventName, event)
+
+ self.init()
+ self.onSetOptions(**options)
+
+ def setOptions(self, **options):
+ self.onSetOptions(**options)
+
+ def init(self):
+ """A hook for subclasses to add initialization in the proper sequence"""
+ pass
+
+ def getMsgHandlerName(self, messageClass):
+ return "on%s" % messageClass.__name__
+
+ def onSetOptions(self, **options):
+ self.options.update(options)
+
+ if 'eventLoop' in options.keys():
+ self.eventLoop = options['eventLoop']
+ else:
+ from BZFlag import Event
+ self.eventLoop = Event.EventLoop()
+
+ def getSupportedOptions(self):
+ return self.options.keys()
+
+ def run(self):
+ self.eventLoop.run()
+
+ def handleMessage(self, socket, eventLoop):
+ """This is a callback used to handle incoming socket
+ data when we're expecting a message.
+ """
+ # This can return None if part of the mesasge but not the whole
+ # thing is available. The rest of the message will be rebuffered,
+ # so we'll read the whole thing next time this is called.
+ msg = socket.readMessage(self.incoming)
+ if msg:
+ msg.socket = socket
+ msg.eventLoop = eventLoop
+ handler = getattr(self, self.getMsgHandlerName(msg.__class__))
+ if self.onAnyMessage(msg):
+ return
+ handler(msg)
+
+ def onUnhandledMessage(self, msg):
+ raise Errors.ProtocolWarning("Unhandled message %s" % msg.__class__.__name__)
### The End ###
|