gamenetworkexample2_6_addedclientmapdisplay.zip

GameNetworkExample2_6/GameClient.py

################################################################################ ################################################################################ # Begin unmodified code from ChatClient.py example import sys from time import sleep from sys import stdin, exit from PodSixNet.Connection import connection, ConnectionListener # This example uses Python threads to manage async input from sys.stdin. # This is so that I can receive input from the console whilst running the server. # Don't ever do this - it's slow and ugly. (I'm doing it for simplicity's sake) from thread import * class Client(ConnectionListener): def __init__(self, host, port): self.Connect((host, port)) print "Chat client started" print "Ctrl-C to exit" # get a nickname from the user before starting print "Enter your nickname: ", connection.Send({"action": "nickname", "nickname": stdin.readline().rstrip("\n")}) # launch our threaded input loop t = start_new_thread(self.InputLoop, ()) def Loop(self): connection.Pump() self.Pump() def InputLoop(self): # horrid threaded input loop # continually reads from stdin and sends whatever is typed to the server while 1: msg = stdin.readline().rstrip("\n") connection.Send({"action": "message", "message": msg}) connection.Send({"action": "move", "dir" : msg}) ####################################### ### Network event/message callbacks ### ####################################### def Network_players(self, data): print "*** players: " + ", ".join([p for p in data['players']]) def Network_message(self, data): print data['who'] + ": " + data['message'] # built in stuff def Network_connected(self, data): print "You are now connected to the server" def Network_error(self, data): print 'error:', data['error'][1] connection.Close() def Network_disconnected(self, data): print 'Server disconnected' exit() # End unmodified code from ChatClient.py example ################################################################################ ################################################################################ ################################################################################ ################################################################################ # Begin Cocos2D integration from ast import literal_eval import cocos import Player import MapLayer cocos.director.director.init() network_layer = cocos.layer.ScrollingManager() map_layer = MapLayer.MapLayer() network_layer.add(map_layer) ################################################################################ ################################################################################ class GameClient(Client): """ """ ############################################################################# def __init__(self, host, port): Client.__init__(self, host, port) self.own_ID = None self.players = {} ############################################################################# def Network_update(self, data): """ """ #print data['info'] for info in data['info']: id = info['i'] if not id in self.players: map_position = literal_eval(info['mp']) player = Player.Player('', map_position, map_layer) player.direction = literal_eval(info['d']) map_layer.add(player) self.players[id] = player player = self.players[id] new_direction = literal_eval(info['d']) map_position = literal_eval(info['mp']) if map_position != player.map_position or \ new_direction != player.direction: player.map_position = map_position player._moveDirection(new_direction, map_layer, distance = 0) player.position = literal_eval(info['p']) ############################################################################# def Network_setid(self, data): """ """ if None == self.own_ID: self.own_ID = data['id'] print "self.own_ID " + self.own_ID ################################################################################ ################################################################################ host = 'localhost' port = '8001' class ClientNetworkAction(cocos.actions.Action): """ This custom Action exists to initialize the Server and frequently 'pump' the server i.e. give the server a chance to handle network messages """ def start(self): """ This method is called once per instance by Cocos. """ self.client = GameClient(host, int(port)) def step(self, dt): """ This method is called frequently by Cocos. """ self.client.Loop() if self.client.own_ID in self.client.players: focus_player = self.client.players[self.client.own_ID] focus_x, focus_y = focus_player.position network_layer.set_focus(focus_x, focus_y) ################################################################################ ################################################################################ if len(sys.argv) != 2: print "Usage:", sys.argv[0], "host:port" print "e.g.", sys.argv[0], "localhost:31425" else: host, port = sys.argv[1].split(":") network_layer.do(ClientNetworkAction()) main_scene = cocos.scene.Scene (network_layer) cocos.director.director.run (main_scene)

GameNetworkExample2_6/GameServer.py

################################################################################ ################################################################################ # Begin unmodified code from ChatServer.py example import sys from time import sleep, localtime from weakref import WeakKeyDictionary from PodSixNet.Server import Server from PodSixNet.Channel import Channel class ClientChannel(Channel): """ This is the server representation of a single connected client. """ def __init__(self, *args, **kwargs): self.nickname = "anonymous" Channel.__init__(self, *args, **kwargs) def Close(self): self._server.DelPlayer(self) ################################## ### Network specific callbacks ### ################################## def Network_message(self, data): self._server.SendToAll({"action": "message", "message": data['message'], "who": self.nickname}) def Network_nickname(self, data): self.nickname = data['nickname'] self._server.SendPlayers() ################################################################################ ################################################################################ class ChatServer(Server): channelClass = ClientChannel def __init__(self, *args, **kwargs): Server.__init__(self, *args, **kwargs) self.players = WeakKeyDictionary() print 'Server launched' def Connected(self, channel, addr): self.AddPlayer(channel) def AddPlayer(self, player): print "New Player" + str(player.addr) self.players[player] = True self.SendPlayers() print "players", [p for p in self.players] def DelPlayer(self, player): print "Deleting Player" + str(player.addr) del self.players[player] self.SendPlayers() def SendPlayers(self): self.SendToAll({"action": "players", "players": [p.nickname for p in self.players]}) def SendToAll(self, data): [p.Send(data) for p in self.players] def Launch(self): while True: self.Pump() sleep(0.0001) # End unmodified code from ChatServer.py example ################################################################################ ################################################################################ ################################################################################ ################################################################################ # Begin Cocos2D integration import cocos import Player import MapLayer cocos.director.director.init() network_layer = cocos.layer.ScrollingManager() map_layer = MapLayer.MapLayer() network_layer.add(map_layer) ################################################################################ ################################################################################ class GameClientChannel(ClientChannel): """ This class exists to override and supliment methods from the ClientChannel superclass. The methods handle network messages and call corresponding GameServer methods. """ def Network_move(self, data): self._server.Move(self, data) def Network_nickname(self, data): self.nickname = data['nickname'] self._server.SetNickname(self) ################################################################################ ################################################################################ class GameServer(ChatServer): """ This class exists to override and supliment methods from the ChatServer superclass. The methods support GameServer specific logic. """ channelClass = GameClientChannel def __init__(self, *args, **kwargs): ChatServer.__init__(self, *args, **kwargs) def Move(self, channel, data): if 'dir' in data: if data['dir'] == 'cw': self.players[channel].turnClockwise(map_layer) elif data['dir'] == 'ccw': self.players[channel].turnCounterclockwise(map_layer) else: self.players[channel].moveForeward(map_layer) def SetNickname(self, channel): if not channel in self.players: map_position = map_layer.getRandomOpenMapPosition() player = Player.Player(channel.nickname, map_position, map_layer) player_z = map_layer.zForMapPosition(map_position) map_layer.add(player, z=player_z) self.players[channel] = player print "New Player" + str(channel.addr) print "players", [p for p in self.players] self.SendPlayers() def AddPlayer(self, channel): channel.Send({"action": "setid", "id": str(channel.addr)}) def UpdateAllClients(self): self.SendToAll({"action": "update", "info": [self.players[k].getInfo(k.addr) for k in self.players]}) ################################################################################ ################################################################################ host = 'localhost' port = '8001' class NetworkAction(cocos.actions.Action): """ This custom Action exists to initialize the Server and frequently 'pump' the server i.e. give the server a chance to handle network messages """ def start(self): """ This method is called once per instance by Cocos. """ self.server = GameServer(localaddr=(host, int(port))) def step(self, dt): """ This method is called frequently by Cocos. """ self.server.Pump() self.server.UpdateAllClients() if 0 < len(self.server.players): focus_player = next(self.server.players.iteritems())[1] focus_x, focus_y = focus_player.position network_layer.set_focus(focus_x, focus_y) ################################################################################ ################################################################################ if __name__ == "__main__": if len(sys.argv) == 2: host, port = sys.argv[1].split(":") print host, port else: print "Usage:", sys.argv[0], "host:port" print "e.g.", sys.argv[0], "localhost:31425" network_layer.do(NetworkAction()) main_scene = cocos.scene.Scene (network_layer) cocos.director.director.run (main_scene) # End Cocos2D integration ################################################################################ ################################################################################

GameNetworkExample2_6/GameSprite.py

import cocos import pyglet ###################################################################### ###################################################################### class GameSprite(cocos.sprite.Sprite): """ This class exists to provide several features shared by almost every game object. Each instance has the following: A unique identifier A motion vector to describe how the instances should move. A radius used to detect collisions with other GameSprite instances A flag, shouldDie, used to signal when the instance should be removed from the game. Instances automatically move according to each instance's motion vector. Positions "wrap" meaning that if an instance moves off the screen, it reappears on the opposite side of the screen. """ next_unique_id = 1 live_instances = {} # map unique_id to instance with that id @staticmethod def handleCollisions(): """ """ objects = GameSprite.live_instances.values() for object in objects: for other_object in objects: if other_object.id != object.id and \ object.isHitByCircle(other_object.position,\ other_object.radius): object.onCollision(other_object) @staticmethod def getInstances(klass): """ """ result = [] for object in GameSprite.live_instances.values(): if isinstance(object, klass): result.append(object) return result def __init__(self, image, id=None, position=(0, 0), rotation=0, scale=1, opacity = 255, color=(255, 255, 255), anchor=None): """ """ super( GameSprite, self ).__init__( image, position, rotation, scale, opacity, color, anchor) if not id: self.id = GameSprite.next_unique_id else: self.id = id GameSprite.next_unique_id += 1 self.motion_vector = (0,0) # No motion by default self.radius = 3 # Small default radius self.shouldDie = False self.type = '_' GameSprite.live_instances[self.id] = self def start(self): """ """ self.do(GameSpriteAction()) def getInfo(self): """ """ x, y = self.position rot_deg = self.rotation return {'id':self.id, 'type':self.type, 'shouldDie' : self.shouldDie } def updateWithInfo(self, info): """ """ self.position = info['pos'] self.rotation = info['rot_deg'] self.shouldDie = info['shouldDie'] def getVelocityMultiplier(self): """ Return a multiplier for use when calculating motion per unit time. """ return 1 def setRandomPosition(self): width, height = cocos.director.director.get_window_size() self.position = (random.random() * width, random.random() * height) def markForDeath(self): """ """ self.shouldDie = True def isHitByCircle(self, center, radius): """ Returns True if and only if the receiver's circle calculated using the receiver's position and radius overlaps the circle calculated using the center and radius arguments to this method. """ total_radius = self.radius + radius total_radius_squared = total_radius * total_radius x, y = self.position delta_x = center[0] - x delta_y = center[1] - y distance_squared = delta_x * delta_x + delta_y * delta_y return distance_squared < total_radius_squared def processCollision(self, other_object): """ """ playLayer = self.get_ancestor(PlayLayer.PlayLayer) if playLayer: pass return True def onRespawn(self): """ Adds the receiver back into collision detection set after receiver has respawned """ GameSprite.live_instances[self.id] = self self.do(GameSpriteAction()) def onCollision(self, other_object): """ """ if self.processCollision(other_object): self.markForDeath() def step(self, dt): """ Perform any updates that should occur after dt seconds from the last update. """ if self.shouldDie: self.stop() self.kill() if self.id in GameSprite.live_instances: del GameSprite.live_instances[self.id]; else: width, height = cocos.director.director.get_window_size() dx = self.motion_vector[0] * self.getVelocityMultiplier() dy = self.motion_vector[1] * self.getVelocityMultiplier() x = self.position[0] + dx * dt y = self.position[1] + dy * dt if x < 0: x += width elif x > width: x -= width if y < 0: y += height elif y > height: y -= height self.position = (x, y) ###################################################################### ###################################################################### if __name__ == "__main__": assert False

GameNetworkExample2_6/Map.py

###################################################################### ###################################################################### map0 = [ [22, 49, 57, 15, 15, 15, 15, 15, 56, 49, 49, 49, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [48, 15, 33, 29, 21, 49, 49, 57, 52, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [52, -33, 15, 15, 15, 15, 15, 50, 48, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [ 0, -33, 15, 15, 15, 34, 51, 58, 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [48, -33, 15, 15, 15, 33, 49, 49, 32, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [16, -33, 15, 15, 31, 15, -33, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [48, -33, 15, 15, 15, 15, -33, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [48, -39, -32, -32, -32, -32, -38, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [48, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [59, 51, 51, 35, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 48, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 24, 15, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [18, 49, 49, 32, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [48, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,], ] ###################################################################### ###################################################################### if __name__ == "__main__": assert False

GameNetworkExample2_6/MapLayer.py

import cocos import pyglet import random import Map ###################################################################### ###################################################################### class MapLayer(cocos.layer.ScrollableLayer): """ """ # Don't call calls variable 'image' because it masks pyglet # Sprite class variable and accessors cave_floor_spritesheet = pyglet.resource.image('tileset_floor.png') cave_floor_grid = pyglet.image.ImageGrid(cave_floor_spritesheet, 4, 16) cave_floor_textures = pyglet.image.TextureGrid(cave_floor_grid) cave_wall_spritesheet = pyglet.resource.image('tileset_wall.png') cave_wall_grid = pyglet.image.ImageGrid(cave_wall_spritesheet, 4, 16) cave_wall_textures = pyglet.image.TextureGrid(cave_wall_grid) floor_tile_y_offset = -48 def __init__(self): """ """ super(MapLayer, self).__init__() batch_walls = cocos.batch.BatchNode() batch_floors = cocos.batch.BatchNode() map_length_tiles = len(Map.map0) for j in range(0, map_length_tiles): map_width_tiles = len(Map.map0[j]) for i in range(0, map_width_tiles): tile_index = Map.map0[j][i] if 0 > tile_index: # This is a floor tile with no wall new_floor_position = self.layerPositionForMapPosition((i,j)) new_floor_position = (new_floor_position[0], new_floor_position[1] + MapLayer.floor_tile_y_offset) new_sprite = cocos.sprite.Sprite( MapLayer.cave_floor_textures[-tile_index], position=new_floor_position) batch_floors.add(new_sprite, z = -1) else: if 15 != tile_index: # This is a floor tile with a wall new_position = self.layerPositionForMapPosition((i,j)) new_sprite = cocos.sprite.Sprite( MapLayer.cave_wall_textures[tile_index], position=new_position) self.add(new_sprite, z = self.zForMapPosition((i,j))) new_floor_position = self.layerPositionForMapPosition((i,j)) new_floor_position = (new_floor_position[0], new_floor_position[1] + MapLayer.floor_tile_y_offset) new_sprite = cocos.sprite.Sprite( MapLayer.cave_floor_textures[48], position=new_floor_position) batch_floors.add(new_sprite, z = -1) self.add(batch_floors, z = -1) self.add(batch_walls, z = 1) def layerPositionForMapPosition(self, aPos): return (512 + aPos[0] * 32 + aPos[1] * -32, 768 + aPos[0] * -16 + aPos[1] * -16) def isOpenAtMapPosition(self, aPos): if 0 < aPos[0] and 0 < aPos[1] and \ len(Map.map0) > aPos[1] and \ len(Map.map0[aPos[1]]) > aPos[0]: tile_index = Map.map0[aPos[1]][aPos[0]] return 15 == tile_index or 0 > tile_index return False def zForMapPosition(self, aPos): map_width_tiles = len(Map.map0[aPos[1]]) return (1 + aPos[0] + aPos[1] * map_width_tiles) / (64.0 * 64.0); def getRandomOpenMapPosition(self): y = random.randint(1, len(Map.map0)-1) x = random.randint(1, len(Map.map0[y])-1) map_position = (x, y) while not self.isOpenAtMapPosition(map_position): y = random.randint(1, len(Map.map0)-1) x = random.randint(1, len(Map.map0[y])-1) map_position = (x, y) return map_position ###################################################################### ###################################################################### if __name__ == "__main__": assert False

GameNetworkExample2_6/Player.py

import cocos import pyglet import random import GameSprite import MapLayer ################################################################################ ################################################################################ class Player(cocos.layer.Layer): """ """ ############################################################################# N = 0 NE = 1 E = 2 SE = 3 S = 4 SW = 5 W = 6 NW = 7 ############################################################################# # Don't call calls variable 'image' because it masks pyglet # Sprite class variable and accessors walk_spritesheet = pyglet.resource.image('walk.png') grid = pyglet.image.ImageGrid(walk_spritesheet, 8, 8) textures = pyglet.image.TextureGrid(grid) animation_walkSW = pyglet.image.Animation.from_image_sequence(textures[0:8], 0.075, loop=True) animation_walkS = pyglet.image.Animation.from_image_sequence(textures[8:16], 0.075, loop=True) animation_walkSE = pyglet.image.Animation.from_image_sequence(textures[16:24], 0.075, loop=True) animation_walkE = pyglet.image.Animation.from_image_sequence(textures[24:32], 0.075, loop=True) animation_walkNE = pyglet.image.Animation.from_image_sequence(textures[32:40], 0.075, loop=True) animation_walkN = pyglet.image.Animation.from_image_sequence(textures[40:48], 0.075, loop=True) animation_walkNW = pyglet.image.Animation.from_image_sequence(textures[48:56], 0.075, loop=True) animation_walkW = pyglet.image.Animation.from_image_sequence(textures[56:64], 0.075, loop=True) ############################################################################# walk_animations = [ animation_walkN, animation_walkNE, animation_walkE, animation_walkSE, animation_walkS, animation_walkSW, animation_walkW, animation_walkNW, ] ############################################################################# direction_deltas = { N : (-1, -1), NE : (0, -1), E : (1, -1), SE : (1, 0), S : (1, 1), SW : (0, 1), W : (-1, 1), NW : (-1, 0), } ############################################################################# direction_next = {N : NE, NE : E, E : SE, SE : S, S : SW, SW : W, W : NW, NW : N,} direction_previous = {N : NW, NE : N, E : NE, SE : E, S : SE, SW : S, W : SW, NW : W,} ############################################################################# def __init__(self, nickname, map_position, map_layer): """ """ super(Player, self).__init__() self.is_moving = False self.nickname = nickname self.direction = Player.W self.map_position = map_position self.position = map_layer.layerPositionForMapPosition(self.map_position) self.sprite = GameSprite.GameSprite( image=Player.walk_animations[self.direction], position=(0,0)) self.add(self.sprite) self.label = cocos.text.Label(nickname, font_name='Times New Roman', font_size=9, anchor_x="center") self.label.position = (0, 50) # arbitrary self.add(self.label) ############################################################################# def getInfo(self, unique_ID): """ """ return {"i": str(unique_ID), "p": str(self.position), "mp": str(self.map_position), "d":str(self.direction)} ############################################################################# def _moveDidEnd(self): """ """ self.is_moving = False ############################################################################# def _moveDirection(self, aDirection, map_layer, distance = 1): """ """ if not self.is_moving: assert Player.N <= aDirection and Player.NW >= aDirection self.is_moving = True deltas = Player.direction_deltas[aDirection] new_map_position = (self.map_position[0] + deltas[0] * distance, self.map_position[1] + deltas[1] * distance) if self.direction != aDirection: self.direction = aDirection self.sprite.image = Player.walk_animations[aDirection]; if map_layer.isOpenAtMapPosition(new_map_position): self.map_position = new_map_position new_layer_position = map_layer.layerPositionForMapPosition( self.map_position) self.do(cocos.actions.MoveTo(new_layer_position, 0.075 * 2.0) + \ cocos.actions.CallFuncS(Player._moveDidEnd)) map_layer.remove(self) map_layer.add(self, z = map_layer.zForMapPosition(new_map_position)) else: self.do(cocos.actions.Delay(0.075 * 2.0) + \ cocos.actions.CallFuncS(Player._moveDidEnd)) ############################################################################# def moveForeward(self, map_layer): """ """ self._moveDirection(self.direction, map_layer) ############################################################################# def turnClockwise(self, map_layer): """ """ self._moveDirection(Player.direction_next[self.direction], map_layer) ############################################################################# def turnCounterclockwise(self, map_layer): """ """ self._moveDirection(Player.direction_previous[self.direction], map_layer) ###################################################################### ###################################################################### if __name__ == "__main__": assert False

GameNetworkExample2_6/PodSixNet/async.py

""" monkey patched version of asynchat to allow map argument on all version of Python, and the best version of the poll function. """ from sys import version import asynchat import asyncore if float(version[:3]) < 2.5: from asyncore import poll2 as poll else: from asyncore import poll # monkey patch older versions to support maps in asynchat. Yuck. if float(version[:3]) < 2.6: def asynchat_monkey_init(self, conn=None, map=None): self.ac_in_buffer = '' self.ac_out_buffer = '' self.producer_fifo = asynchat.fifo() asyncore.dispatcher.__init__ (self, sock=conn, map=map) asynchat.async_chat.__init__ = asynchat_monkey_init

GameNetworkExample2_6/PodSixNet/Channel.py

import sys, traceback from async import asynchat from rencode import loads, dumps class Channel(asynchat.async_chat): endchars = '\0---\0' def __init__(self, conn=None, addr=(), server=None, map=None): asynchat.async_chat.__init__(self, conn, map) self.addr = addr self._server = server self._ibuffer = "" self.set_terminator(self.endchars) self.sendqueue = [] def collect_incoming_data(self, data): self._ibuffer += data def found_terminator(self): data = loads(self._ibuffer) self._ibuffer = "" if type(dict()) == type(data) and data.has_key('action'): [getattr(self, n)(data) for n in ('Network', 'Network_' + data['action']) if hasattr(self, n)] else: print "OOB data (no such Network_action):", data def Pump(self): [asynchat.async_chat.push(self, d) for d in self.sendqueue] self.sendqueue = [] def Send(self, data): """Returns the number of bytes sent after enoding.""" outgoing = dumps(data) + self.endchars self.sendqueue.append(outgoing) return len(outgoing) def handle_connect(self): if hasattr(self, "Connected"): self.Connected() else: print "Unhandled Connected()" def handle_error(self): try: self.close() except: pass if hasattr(self, "Error"): self.Error(sys.exc_info()[1]) else: asynchat.async_chat.handle_error(self) def handle_expt(self): pass def handle_close(self): if hasattr(self, "Close"): self.Close() asynchat.async_chat.handle_close(self)

GameNetworkExample2_6/PodSixNet/Connection.py

""" A client's connection to the server. This module contains two components: a singleton called 'connection' and a class called 'ConnectionListener'. 'connection' is a singleton instantiation of an EndPoint which will be connected to the server at the other end. It's a singleton because each client should only need one of these in most multiplayer scenarios. (If a client needs more than one connection to the server, a more complex architecture can be built out of instantiated EndPoint()s.) The connection is based on Python's asyncore and so it should have it's polling loop run periodically, probably once per gameloop. This just means putting "from Connection import connection; connection.Pump()" somewhere in your top level gameloop. Subclass ConnectionListener in order to have an object that will receive network events. For example, you might have a GUI element which is a label saying how many players there are online. You would declare it like 'class NumPlayersLabel(ConnectionListener, ...):' Later you'd instantitate it 'n = NumPlayersLabel()' and then somewhere in your loop you'd have 'n.Pump()' which asks the connection singleton if there are any new messages from the network, and calls the 'Network_' callbacks for each bit of new data from the server. So you'd implement a method like "def Network_players(self, data):" which would be called whenever a message from the server arrived which looked like {"action": "players", "number": 5}. """ from EndPoint import EndPoint connection = EndPoint() class ConnectionListener: """ Looks at incoming data and calls "Network_" methods in self, based on what messages come in. Subclass this to have your own classes monitor incoming network messages. For example, a method called "Network_players(self, data)" will be called when a message arrives like: {"action": "players", "number": 5, ....} """ def Connect(self, *args, **kwargs): connection.DoConnect(*args, **kwargs) # check for connection errors: self.Pump() def Pump(self): for data in connection.GetQueue(): [getattr(self, n)(data) for n in ("Network_" + data['action'], "Network") if hasattr(self, n)] def Send(self, data): """ Convenience method to allow this listener to appear to send network data, whilst actually using connection. """ connection.Send(data) if __name__ == "__main__": from time import sleep from sys import exit class ConnectionTest(ConnectionListener): def Network(self, data): print "Network:", data def Network_error(self, error): print "error:", error['error'] print "Did you start a server?" exit(-1) def Network_connected(self, data): print "connection test Connected" c = ConnectionTest() c.Connect() while 1: connection.Pump() c.Pump() sleep(0.001)

GameNetworkExample2_6/PodSixNet/EndPoint.py

# coding=utf-8 import socket import sys from async import poll from Channel import Channel class EndPoint(Channel): """ The endpoint queues up all network events for other classes to read. """ def __init__(self, address=("127.0.0.1", 31425), map=None): self.address = address self.isConnected = False self.queue = [] if map is None: self._map = {} else: self._map = map def DoConnect(self, address=None): if address: self.address = address try: Channel.__init__(self, map=self._map) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.connect(self.address) except socket.gaierror, e: self.queue.append({"action": "error", "error": e.args}) except socket.error, e: self.queue.append({"action": "error", "error": e.args}) def GetQueue(self): return self.queue def Pump(self): Channel.Pump(self) self.queue = [] poll(map=self._map) # methods to add network data to the queue depending on network events def Close(self): self.isConnected = False self.close() self.queue.append({"action": "disconnected"}) def Connected(self): self.queue.append({"action": "socketConnect"}) def Network_connected(self, data): self.isConnected = True def Network(self, data): self.queue.append(data) def Error(self, error): self.queue.append({"action": "error", "error": error}) def ConnectionError(self): self.isConnected = False self.queue.append({"action": "error", "error": (-1, "Connection error")}) if __name__ == "__main__": import unittest from time import sleep, time class FailEndPointTestCase(unittest.TestCase): def setUp(self): print print "Trying failed endpoint" print "----------------------" class FailEndPoint(EndPoint): def __init__(self): EndPoint.__init__(self, ("localhost", 31429)) self.result = "" def Error(self, error): print "Received error message:", error self.result = error def Test(self): self.DoConnect() start = time() while not self.result and time() - start < 10: self.Pump() sleep(0.001) self.endpoint_bad = FailEndPoint() def runTest(self): self.endpoint_bad.Test() want = (61, 'Connection refused') self.assertEqual(list(self.endpoint_bad.result), list(want), "Socket got %s instead of %s" % (str(self.endpoint_bad.result), str(want))) print def tearDown(self): del self.endpoint_bad print "FailEndPointTestCase complete" from Server import Server class EndPointTestCase(unittest.TestCase): def setUp(self): self.outgoing = [ {"action": "hello", "data": {"a": 321, "b": [2, 3, 4], "c": ["afw", "wafF", "aa", "weEEW", "w234r"], "d": ["x"] * 256}}, {"action": "hello", "data": [454, 35, 43, 543, "aabv"]}, {"action": "hello", "data": [10] * 512}, {"action": "hello", "data": [10] * 512, "otherstuff": "hello\0---\0goodbye", "x": [0, "---", 0], "y": "zäö"}, ] self.count = len(self.outgoing) self.lengths = [len(data['data']) for data in self.outgoing] print print "Trying successful endpoint" print "--------------------------" class ServerChannel(Channel): def Network_hello(self, data): print "*Server* received:", data self._server.received.append(data) self._server.count += 1 self.Send({"action": "gotit", "data": "Yeah, we got it: " + str(len(data['data'])) + " elements"}) class TestEndPoint(EndPoint): received = [] connected = False count = 0 def Network_connected(self, data): self.connected = True def Network_gotit(self, data): self.received.append(data) self.count += 1 print "gotit:", data class TestServer(Server): connected = False received = [] count = 0 def Connected(self, channel, addr): self.connected = True self.server = TestServer(channelClass=ServerChannel) self.endpoint = TestEndPoint(("localhost", 31425)) def runTest(self): self.endpoint.DoConnect() for o in self.outgoing: self.endpoint.Send(o) print "polling for half a second" for x in range(50): self.server.Pump() self.endpoint.Pump() # see if what we receive from the server is what we expect for r in self.server.received: self.failUnless(r == self.outgoing.pop(0)) self.server.received = [] # see if what we receive from the client is what we expect for r in self.endpoint.received: self.failUnless(r['data'] == "Yeah, we got it: %d elements" % self.lengths.pop(0)) self.endpoint.received = [] sleep(0.001) self.assertTrue(self.server.connected, "Server is not connected") self.assertTrue(self.endpoint.connected, "Endpoint is not connected") self.failUnless(self.server.count == self.count, "Didn't receive the right number of messages") self.failUnless(self.endpoint.count == self.count, "Didn't receive the right number of messages") self.endpoint.Close() print self.endpoint.GetQueue() print def tearDown(self): del self.server del self.endpoint print "EndPointTestCase complete" unittest.main()

GameNetworkExample2_6/PodSixNet/rencode.py

""" rencode -- Web safe object pickling/unpickling. The rencode module is a modified version of bencode from the BitTorrent project. For complex, heterogeneous data structures with many small elements, r-encodings take up significantly less space than b-encodings: >>> len(rencode.dumps({'a':0, 'b':[1,2], 'c':99})) 13 >>> len(bencode.bencode({'a':0, 'b':[1,2], 'c':99})) 26 The rencode format is not standardized, and may change with different rencode module versions, so you should check that you are using the same rencode version throughout your project. """ __version__ = '1.0.0-ntk' __all__ = ['dumps', 'loads', 'serializable'] from base64 import b64encode, b64decode ##TODO # - Why the hell it encodes both tuples and lists to tuple? # Try loads(dumps([1,(2,3)])) # Grr # # - extend it! Support other basic types, f.e. Set() # ## # Original bencode module by Petru Paler, et al. # # Modifications by Daniele Tricoli: # # - Added support for instances # Only registered instances can be serialized. An instance to be serialized # must provide a '_pack` method. # E.g. # # class X(object): # # def __init__(self, x): # self.x = x # # def _pack(self) # return (self.x,) # a tuple # # - Lists are decoded again as list # # Modifications by Connelly Barnes: # # - Added support for floats (sent as 32-bit or 64-bit in network # order), bools, None. # - Allowed dict keys to be of any serializable type. # - Lists/tuples are always decoded as tuples (thus, tuples can be # used as dict keys). # - Embedded extra information in the 'typecodes' to save some space. # - Added a restriction on integer length, so that malicious hosts # cannot pass us large integers which take a long time to decode. # # Licensed by Bram Cohen under the "MIT license": # # "Copyright (C) 2001-2002 Bram Cohen # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # The Software is provided "AS IS", without warranty of any kind, # express or implied, including but not limited to the warranties of # merchantability, fitness for a particular purpose and # noninfringement. In no event shall the authors or copyright holders # be liable for any claim, damages or other liability, whether in an # action of contract, tort or otherwise, arising from, out of or in # connection with the Software or the use or other dealings in the # Software." # # (The rencode module is licensed under the above license as well). # import inspect import struct from threading import Lock from types import (StringType, UnicodeType, IntType, LongType, DictType, ListType, TupleType, FloatType, NoneType) class AlreadyRegistered(Exception): pass class NotRegistered(Exception): def __init__(self, class_): self.class_ = class_ def __str__(self): return 'Class %s is not registered' % self.class_ class NotSerializable(Exception): pass def add_class_name(func): if inspect.ismethod(func): def decorate(*args, **kargs): result = func(*args, **kargs) result = (str(func.im_class.__name__),) + result return result return decorate class _SerializableRegistry(object): def __init__(self): self._registry = {} def __contains__(self, item): return item in self._registry def __getitem__(self, key): return self._registry[key] def register(self, cls): ''' ''' if inspect.isclass(cls): if cls.__name__ in self._registry: msg = 'Class %s is already registered' % cls.__name__ raise AlreadyRegistered(msg) try: if inspect.ismethod(cls._pack): cls._pack = add_class_name(cls._pack) self._registry[cls.__name__] = cls except AttributeError, err: raise NotSerializable(err) def unregister(self, cls): ''' ''' if inspect.isclass(cls): if cls in self._registry: del self._registry[cls.__name__] else: raise NotRegistered(cls.__name__) serializable = _SerializableRegistry() # Number of bits for serialized floats, either 32 or 64. FLOAT_BITS = 32 # Maximum length of integer when written as base 10 string. MAX_INT_LENGTH = 64 # The bencode 'typecodes' such as i, d, etc have been extended and # relocated on the base-256 character set. # Can't be used chr(48) to chr(57) because they are manually set CHR_INSTANCE = chr(47) CHR_TUPLE = chr(58) CHR_LIST = chr(59) CHR_DICT = chr(60) CHR_INT = chr(61) CHR_INT1 = chr(62) CHR_INT2 = chr(63) CHR_INT4 = chr(64) CHR_INT8 = chr(65) CHR_FLOAT = chr(66) CHR_TRUE = chr(67) CHR_FALSE = chr(68) CHR_NONE = chr(69) CHR_TERM = chr(127) # Positive integers with value embedded in typecode. INT_POS_FIXED_START = 0 INT_POS_FIXED_COUNT = 32 # Dictionaries with length embedded in typecode. DICT_FIXED_START = 102 DICT_FIXED_COUNT = 25 # Negative integers with value embedded in typecode. INT_NEG_FIXED_START = 70 INT_NEG_FIXED_COUNT = 32 # Strings with length embedded in typecode. STR_FIXED_START = 128 STR_FIXED_COUNT = 64 # Lists with length embedded in typecode. LIST_FIXED_START = STR_FIXED_START + STR_FIXED_COUNT LIST_FIXED_COUNT = 32 # Tuples with length embedded in typecode. TUPLE_FIXED_START = LIST_FIXED_START + LIST_FIXED_COUNT TUPLE_FIXED_COUNT = 32 def decode_int(x, f): f += 1 newf = x.index(CHR_TERM, f) if newf - f >= MAX_INT_LENGTH: raise ValueError('overflow') try: n = int(x[f:newf]) except (OverflowError, ValueError): n = long(x[f:newf]) if x[f] == '-': if x[f + 1] == '0': raise ValueError elif x[f] == '0' and newf != f+1: raise ValueError return (n, newf+1) def decode_intb(x, f): f += 1 return (struct.unpack('!b', x[f:f+1])[0], f+1) def decode_inth(x, f): f += 1 return (struct.unpack('!h', x[f:f+2])[0], f+2) def decode_intl(x, f): f += 1 return (struct.unpack('!l', x[f:f+4])[0], f+4) def decode_intq(x, f): f += 1 return (struct.unpack('!q', x[f:f+8])[0], f+8) def decode_float(x, f): f += 1 if FLOAT_BITS == 32: n = struct.unpack('!f', x[f:f+4])[0] return (n, f+4) elif FLOAT_BITS == 64: n = struct.unpack('!d', x[f:f+8])[0] return (n, f+8) else: raise ValueError def decode_string(x, f): colon = x.index(':', f) try: n = int(x[f:colon]) except (OverflowError, ValueError): n = long(x[f:colon]) if x[f] == '0' and colon != f+1: raise ValueError colon += 1 return (b64decode(x[colon:colon+n]), colon+n) def decode_list(x, f): r, f = [], f+1 while x[f] != CHR_TERM: v, f = decode_func[x[f]](x, f) r.append(v) return (r, f + 1) def decode_tuple(x, f): r, f = [], f+1 while x[f] != CHR_TERM: v, f = decode_func[x[f]](x, f) r.append(v) return (tuple(r), f + 1) def decode_dict(x, f): r, f = {}, f+1 while x[f] != CHR_TERM: k, f = decode_func[x[f]](x, f) r[k], f = decode_func[x[f]](x, f) return (r, f + 1) def decode_true(x, f): return (True, f+1) def decode_false(x, f): return (False, f+1) def decode_none(x, f): return (None, f+1) def decode_instance(x, f): f += 1 while x[f] != CHR_TERM: v, f = decode_func[x[f]](x, f) if v[0] in serializable: r = serializable[v[0]](*v[1:]) else: raise NotRegistered(v[0]) return (r, f+1) decode_func = {} decode_func['0'] = decode_string decode_func['1'] = decode_string decode_func['2'] = decode_string decode_func['3'] = decode_string decode_func['4'] = decode_string decode_func['5'] = decode_string decode_func['6'] = decode_string decode_func['7'] = decode_string decode_func['8'] = decode_string decode_func['9'] = decode_string decode_func[CHR_LIST ] = decode_list decode_func[CHR_TUPLE] = decode_tuple decode_func[CHR_DICT ] = decode_dict decode_func[CHR_INT ] = decode_int decode_func[CHR_INT1 ] = decode_intb decode_func[CHR_INT2 ] = decode_inth decode_func[CHR_INT4 ] = decode_intl decode_func[CHR_INT8 ] = decode_intq decode_func[CHR_FLOAT] = decode_float decode_func[CHR_TRUE ] = decode_true decode_func[CHR_FALSE] = decode_false decode_func[CHR_NONE ] = decode_none decode_func[CHR_INSTANCE] = decode_instance def make_fixed_length_string_decoders(): def make_decoder(slen): def f_fixed_string(x, f): return (b64decode(x[f+1:f+1+slen]), f+1+slen) return f_fixed_string for i in range(STR_FIXED_COUNT): decode_func[chr(STR_FIXED_START+i)] = make_decoder(i) make_fixed_length_string_decoders() def make_fixed_length_list_decoders(): def make_decoder(slen): def f_fixed_list(x, f): r, f = [], f+1 for i in range(slen): v, f = decode_func[x[f]](x, f) r.append(v) return (r, f) return f_fixed_list for i in range(LIST_FIXED_COUNT): decode_func[chr(LIST_FIXED_START+i)] = make_decoder(i) make_fixed_length_list_decoders() def make_fixed_length_tuple_decoders(): def make_decoder(slen): def f_fixed_tuple(x, f): r, f = [], f+1 for i in range(slen): v, f = decode_func[x[f]](x, f) r.append(v) return (tuple(r), f) return f_fixed_tuple for i in range(TUPLE_FIXED_COUNT): decode_func[chr(TUPLE_FIXED_START+i)] = make_decoder(i) make_fixed_length_tuple_decoders() def make_fixed_length_int_decoders(): def make_decoder(j): def f(x, f): return (j, f+1) return f for i in range(INT_POS_FIXED_COUNT): decode_func[chr(INT_POS_FIXED_START+i)] = make_decoder(i) for i in range(INT_NEG_FIXED_COUNT): decode_func[chr(INT_NEG_FIXED_START+i)] = make_decoder(-1-i) make_fixed_length_int_decoders() def make_fixed_length_dict_decoders(): def make_decoder(slen): def f(x, f): r, f = {}, f+1 for j in range(slen): k, f = decode_func[x[f]](x, f) r[k], f = decode_func[x[f]](x, f) return (r, f) return f for i in range(DICT_FIXED_COUNT): decode_func[chr(DICT_FIXED_START+i)] = make_decoder(i) make_fixed_length_dict_decoders() def loads(x): try: r, l = decode_func[x[0]](x, 0) except (IndexError, KeyError): raise if l != len(x): raise ValueError return r def encode_int(x, r): if 0 <= x < INT_POS_FIXED_COUNT: r.append(chr(INT_POS_FIXED_START+x)) elif -INT_NEG_FIXED_COUNT <= x < 0: r.append(chr(INT_NEG_FIXED_START-1-x)) elif -128 <= x < 128: r.extend((CHR_INT1, struct.pack('!b', x))) elif -32768 <= x < 32768: r.extend((CHR_INT2, struct.pack('!h', x))) elif -2147483648 <= x < 2147483648: r.extend((CHR_INT4, struct.pack('!l', x))) elif -9223372036854775808 <= x < 9223372036854775808: r.extend((CHR_INT8, struct.pack('!q', x))) else: s = str(x) if len(s) >= MAX_INT_LENGTH: raise ValueError('overflow') r.extend((CHR_INT, s, CHR_TERM)) def encode_float(x, r): if FLOAT_BITS == 32: r.extend((CHR_FLOAT, struct.pack('!f', x))) elif FLOAT_BITS == 64: r.extend((CHR_FLOAT, struct.pack('!d', x))) else: raise ValueError def encode_bool(x, r): r.extend({False: CHR_FALSE, True: CHR_TRUE}[bool(x)]) def encode_none(x, r): r.extend(CHR_NONE) def encode_string(x, r): x = b64encode(x) if len(x) < STR_FIXED_COUNT: r.extend((chr(STR_FIXED_START + len(x)), x)) else: r.extend((str(len(x)), ':', x)) def encode_list(x, r): if len(x) < LIST_FIXED_COUNT: r.append(chr(LIST_FIXED_START + len(x))) for i in x: encode_func.get(type(i), encode_instance)(i, r) else: r.append(CHR_LIST) for i in x: encode_func.get(type(i), encode_instance)(i, r) r.append(CHR_TERM) def encode_tuple(x, r): if len(x) < TUPLE_FIXED_COUNT: r.append(chr(TUPLE_FIXED_START + len(x))) for i in x: encode_func.get(type(i), encode_instance)(i, r) else: r.append(CHR_TUPLE) for i in x: encode_func.get(type(i), encode_instance)(i, r) r.append(CHR_TERM) def encode_dict(x,r): if len(x) < DICT_FIXED_COUNT: r.append(chr(DICT_FIXED_START + len(x))) for k, v in x.items(): encode_func[type(k)](k, r) encode_func[type(v)](v, r) else: r.append(CHR_DICT) for k, v in x.items(): encode_func[type(k)](k, r) encode_func[type(v)](v, r) r.append(CHR_TERM) encode_func = {} encode_func[IntType] = encode_int encode_func[LongType] = encode_int encode_func[FloatType] = encode_float encode_func[StringType] = encode_string encode_func[UnicodeType] = encode_string encode_func[ListType] = encode_list encode_func[TupleType] = encode_tuple encode_func[DictType] = encode_dict encode_func[NoneType] = encode_none try: from types import BooleanType encode_func[BooleanType] = encode_bool except ImportError: pass def encode_instance(x, r): if hasattr(x, '_pack'): if x.__class__.__name__ in serializable: # Calling the class of instance `x' passing it to the # unbound method result = serializable[x.__class__.__name__]._pack(x) r.append(CHR_INSTANCE) encode_func[type(result)](result, r) r.append(CHR_TERM) else: raise NotRegistered(x.__class__.__name__) lock = Lock() def dumps(x): lock.acquire() r = [] encode_func.get(type(x), encode_instance)(x, r) lock.release() return ''.join(r) def test(): f1 = struct.unpack('!f', struct.pack('!f', 25.5))[0] f2 = struct.unpack('!f', struct.pack('!f', 29.3))[0] f3 = struct.unpack('!f', struct.pack('!f', -0.6))[0] L = (({'a':15, 'bb':f1, 'ccc':f2, '':(f3,(),False,True,'')},('a',10**20),tuple(range(-100000,100000)),'b'*31,'b'*62,'b'*64,2**30,2**33,2**62,2**64,2**30,2**33,2**62,2**64,False,False, True, -1, 2, 0),) assert loads(dumps(L)) == L d = dict(zip(range(-100000,100000),range(-100000,100000))) d.update({'a':20, 20:40, 40:41, f1:f2, f2:f3, f3:False, False:True, True:False}) L = (d, {}, {5:6}, {7:7,True:8}, {9:10, 22:39, 49:50, 44: ''}) assert loads(dumps(L)) == L L = ('', 'a'*10, 'a'*100, 'a'*1000, 'a'*10000, 'a'*100000, 'a'*1000000, 'a'*10000000) assert loads(dumps(L)) == L L = tuple([dict(zip(range(n),range(n))) for n in range(100)]) + ('b',) assert loads(dumps(L)) == L L = tuple([dict(zip(range(n),range(-n,0))) for n in range(100)]) + ('b',) assert loads(dumps(L)) == L L = tuple([tuple(range(n)) for n in range(100)]) + ('b',) assert loads(dumps(L)) == L L = tuple(['a'*n for n in range(100)]) + ('b',) assert loads(dumps(L)) == L L = tuple(['a'*n for n in range(100)]) + (None,True,None) assert loads(dumps(L)) == L L = list(['a'*n for n in range(100)]) + [None,True,None] assert loads(dumps(L)) == L assert loads(dumps(None)) == None assert loads(dumps({None:None})) == {None:None} class A(object): def __init__(self, a, b, c): self.a = a self.b = b self.c = c def _pack(self): return (self.a, self.b, self.c) serializable.register(A) instance = [A(1,2,3), 1, A(1,3,4), 'sss'] print loads(dumps(instance)) if __name__ == '__main__': test()

GameNetworkExample2_6/PodSixNet/Server.py

import socket import sys from async import poll, asyncore from Channel import Channel class Server(asyncore.dispatcher): channelClass = Channel def __init__(self, channelClass=None, localaddr=("127.0.0.1", 31425), listeners=5): if channelClass: self.channelClass = channelClass self._map = {} self.channels = [] asyncore.dispatcher.__init__(self, map=self._map) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.set_reuse_addr() self.bind(localaddr) self.listen(listeners) def handle_accept(self): try: conn, addr = self.accept() except socket.error: print 'warning: server accept() threw an exception' return except TypeError: print 'warning: server accept() threw EWOULDBLOCK' return self.channels.append(self.channelClass(conn, addr, self, self._map)) self.channels[-1].Send({"action": "connected"}) if hasattr(self, "Connected"): self.Connected(self.channels[-1], addr) def Pump(self): [c.Pump() for c in self.channels] poll(map=self._map) ######################### # Test stub # ######################### if __name__ == "__main__": import unittest class ServerTestCase(unittest.TestCase): testdata = {"action": "hello", "data": {"a": 321, "b": [2, 3, 4], "c": ["afw", "wafF", "aa", "weEEW", "w234r"], "d": ["x"] * 256}} def setUp(self): print "ServerTestCase" print "--------------" class ServerChannel(Channel): def Network_hello(self, data): print "*Server* ran test method for 'hello' action" print "*Server* received:", data self._server.received = data class EndPointChannel(Channel): connected = False def Connected(self): print "*EndPoint* Connected()" def Network_connected(self, data): self.connected = True print "*EndPoint* Network_connected(", data, ")" print "*EndPoint* initiating send" self.Send(ServerTestCase.testdata) class TestServer(Server): connected = False received = None def Connected(self, channel, addr): self.connected = True print "*Server* Connected() ", channel, "connected on", addr self.server = TestServer(channelClass=ServerChannel) sender = asyncore.dispatcher(map=self.server._map) sender.create_socket(socket.AF_INET, socket.SOCK_STREAM) sender.connect(("localhost", 31425)) self.outgoing = EndPointChannel(sender, map=self.server._map) def runTest(self): from time import sleep print "*** polling for half a second" for x in range(250): self.server.Pump() self.outgoing.Pump() if self.server.received: self.failUnless(self.server.received == self.testdata) self.server.received = None sleep(0.001) self.failUnless(self.server.connected == True, "Server is not connected") self.failUnless(self.outgoing.connected == True, "Outgoing socket is not connected") def tearDown(self): pass del self.server del self.outgoing unittest.main()

GameNetworkExample2_6/PodSixNet/__init__.py

GameNetworkExample2_6/tileset_floor.png

GameNetworkExample2_6/tileset_wall.png

GameNetworkExample2_6/walk.png