def load_shot(sid): sid = str(sid) log('sakura.config.shot.load_shot: Compiling ', sid) # load geometry animations = {} # or 'animations as geometry set' # Get all animations for anim_dir in [x for x in os.listdir(sys.argv[2]+'shot/'+sid) if x.startswith('anim')]: try: anim_id = int(anim_dir[4:].strip()) cf = ConfigParser.ConfigParser() cf.read(sys.argv[2]+'shot/'+sid+'/'+anim_dir+'/hitbox.ini') sx, sy = cf.getint('synchroPoint', 'x'), cf.getint('synchroPoint', 'y') hitboxes = [] for rectangle_id in xrange(0, cf.getint('main', 'rectangles')): section = 'hitBox%s' % (rectangle_id, ) hitboxes.append(Rectangle(cf.getint(section, 'x1')-sx, cf.getint(section, 'y1')-sy, cf.getint(section, 'x2')-sx, cf.getint(section, 'y2')-sy)) animations[anim_id] = Geometry(hitboxes) except: print 'sakura.config.shot.load_shot: Skipping %s' % (anim_dir, ) # Perform upside-down animating for canims, geometry in animations.items(): animations[canims | 64] = geometry.upside_down() d = {'animations':animations} return d
def on_udp_packet(self, data, address): """On UDP packet received""" with self.sock_lock: if address in self.udp_addresses.values( ): # Logged in user pinging packtype = data[0] if packtype == '\x00': # PING request self.udp_socket.sendto('\x01PING', address) else: # Unverified (?) user pinging for unverified_socket in self.unverified_sockets: if data == unverified_socket.response2: # bingo log( 'sakura.network.sequencer.Sequencer.on_udp_packet: Verified ', unverified_socket.pid) self.unverified_sockets.remove(unverified_socket) # inform people about player connected for sock in self.tcp_sockets.itervalues(): sock.send('\x03' + pack('<H', unverified_socket.pid)) self.tcp_sockets[ unverified_socket.pid] = unverified_socket unverified_socket.on_verified() unverified_socket.send('READY') self.rcvd_commands.put( (unverified_socket.pid, MsgPlayerOnline())) self.udp_addresses[unverified_socket.pid] = address return
def run(self): log('sakura.network.select_loop.SelectLoop: Running network logic') config.registry['tcp_socket'].listen(10) while True: self.loop()
def init(): global registry log('sakura.config: Beginning config compilation') # Load baseline data registry = load_bpf() # Load game map registry['map'] = load_map(registry['map']) shots_to_load = set() # Load player characters for pid, player_profile in registry['players'].iteritems(): player_profile['character'] = load_hero(player_profile['character']) for shot in player_profile['character']['related_shots']: shots_to_load.add(shot) # Load shots registry['shots'] = {} for shot in shots_to_load: try: registry['shots'][int(shot)] = load_shot(shot) except: raise print 'sakura.config.__init__: Could not load shot %s' % (shot, ) registry['_runtime'] = {}
def init_delegates(): delegates = {} log('sakura.players.init_delegates: Initializing status delegates') for pid_or_login, player in config.registry['players'].iteritems(): if type(pid_or_login) in (int, long): delegates[pid_or_login] = StatusDelegate(pid_or_login) config.registry['_runtime']['status_delegates'] = delegates
def init_simulation(): from sakura import config log('sakura.physics.init_simulation: Initializing simulation') physics = Simulation(config.registry['map']['map_boundary'], config.registry['map']['platforms'], config.registry['map']['obstacles']) config.registry['_runtime']['simulation'] = physics
def init_sockets(): log('sakura.network.init_sockets: Initializing network sockets') config.registry['tcp_socket'] = socket.socket(socket.AF_INET, socket.SOCK_STREAM) config.registry['tcp_socket'].setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) config.registry['tcp_socket'].bind(config.registry['tcp_bind']) config.registry['udp_socket'] = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) config.registry['udp_socket'].setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) config.registry['udp_socket'].bind(config.registry['udp_bind'])
def load_map(mapname): log('sakura.config.map.load_map: Compiling ', mapname) cf = ConfigParser.ConfigParser() cf.read(sys.argv[2] + 'maps/' + mapname + '/info.ini') # Read basic info d = { 'mapname': cf.get('Main', 'MapName'), 'width': cf.getint('Main', 'Width'), 'height': cf.getint('Main', 'Height'), 'map_boundary': MapBoundary(cf.getint('Main', 'Width'), cf.getint('Main', 'Height')) } # Read platforms platforms = [] for platform_id in xrange(0, cf.getint('Main', 'Platforms')): section = 'Platform%s' % (platform_id, ) x = cf.getint(section, 'X') width = cf.getint(section, 'Width') platforms.append(Platform(x, x + width, cf.getint(section, 'Y'))) d['platforms'] = platforms # Read obstacles obstacles = [] for obstacle_id in xrange(0, cf.getint('Main', 'Obstacles')): section = 'Obstacle%s' % (obstacle_id, ) obstacles.append( Obstacle(cf.getint(section, 'X1'), cf.getint(section, 'Y1'), cf.getint(section, 'X2'), cf.getint(section, 'Y2'))) d['obstacles'] = sorted(obstacles, key=lambda x: -x.y1) # Read spawnpoints spawnpoints = [] for spawnpoint_id in xrange(0, 2): section = 'Team%s' % (spawnpoint_id, ) spawnpoints.append((cf.getint(section, 'SpawnX'), cf.getint(section, 'SpawnY'))) d['spawnpoints'] = spawnpoints return d
def load_shot(sid): sid = str(sid) log('sakura.config.shot.load_shot: Compiling ', sid) # load geometry animations = {} # or 'animations as geometry set' # Get all animations for anim_dir in [ x for x in os.listdir(sys.argv[2] + 'shot/' + sid) if x.startswith('anim') ]: try: anim_id = int(anim_dir[4:].strip()) cf = ConfigParser.ConfigParser() cf.read(sys.argv[2] + 'shot/' + sid + '/' + anim_dir + '/hitbox.ini') sx, sy = cf.getint('synchroPoint', 'x'), cf.getint('synchroPoint', 'y') hitboxes = [] for rectangle_id in xrange(0, cf.getint('main', 'rectangles')): section = 'hitBox%s' % (rectangle_id, ) hitboxes.append( Rectangle( cf.getint(section, 'x1') - sx, cf.getint(section, 'y1') - sy, cf.getint(section, 'x2') - sx, cf.getint(section, 'y2') - sy)) animations[anim_id] = Geometry(hitboxes) except: print 'sakura.config.shot.load_shot: Skipping %s' % (anim_dir, ) # Perform upside-down animating for canims, geometry in animations.items(): animations[canims | 64] = geometry.upside_down() d = {'animations': animations} return d
def load_bpf(): log('sakura.config.bpf.load_bpf: Compiling BPF') with open(sys.argv[1]) as f: data = json.load(f) d = { 'tcp_bind': (data['tcp_interface'], data['tcp_port']), 'udp_bind': (data['udp_interface'], data['udp_port']), 'map': data['map_name'], 'map_name': data['map_name'] } players = {} for pid, player in enumerate(data['players']): player['pid'] = pid players[pid] = player d['players'] = players return d
def load_bpf(): log('sakura.config.bpf.load_bpf: Compiling BPF') with open(sys.argv[1]) as f: data = json.load(f) d = {'tcp_bind': (data['tcp_interface'], data['tcp_port']), 'udp_bind': (data['udp_interface'], data['udp_port']), 'map': data['map_name'], 'map_name': data['map_name'] } players = {} for pid, player in enumerate(data['players']): player['pid'] = pid players[pid] = player d['players'] = players return d
def on_udp_packet(self, data, address): """On UDP packet received""" with self.sock_lock: if address in self.udp_addresses.values(): # Logged in user pinging packtype = data[0] if packtype == '\x00': # PING request self.udp_socket.sendto('\x01PING', address) else: # Unverified (?) user pinging for unverified_socket in self.unverified_sockets: if data == unverified_socket.response2: # bingo log('sakura.network.sequencer.Sequencer.on_udp_packet: Verified ', unverified_socket.pid) self.unverified_sockets.remove(unverified_socket) # inform people about player connected for sock in self.tcp_sockets.itervalues(): sock.send('\x03'+pack('<H', unverified_socket.pid)) self.tcp_sockets[unverified_socket.pid] = unverified_socket unverified_socket.on_verified() unverified_socket.send('READY') self.rcvd_commands.put((unverified_socket.pid, MsgPlayerOnline())) self.udp_addresses[unverified_socket.pid] = address return
def on_read(self): self.socket.on_read() log('sakura.network.wrappers.LoggingInWrapper.on_read: starting') if len(self.socket.in_packets) > 0: inbound = self.socket.in_packets.pop(0) if self.state == STATE_AWAITING_LOGIN: login = str(inbound).lower() try: for pid, player in config.registry['players'].iteritems(): if player['login'].lower() == login: self.player_profile = player self.player_profile # throws Exception except KeyError: log('sakura.network.wrappers.LoggingInWrapper.on_read: No such player as requested' ) self.close() return # Generate challenge and response, send it self.challenge1 = 'alwaysthesun' self.response1 = sha1(self.player_profile['password'] + self.challenge1).hexdigest() self.send(self.challenge1) self.state = STATE_AWAITING_RESPONSE_1 elif self.state == STATE_AWAITING_RESPONSE_1: if self.response1 != str(inbound): log('sakura.network.wrappers.LoggingInWrapper.on_read: Invalid response1' ) self.close() return challenge2 = 'somemoresun' response2 = sha1(self.player_profile['password'] + challenge2).hexdigest() self.send(config.registry['map_name'].encode('utf8') + '\xFF' + challenge2) # Prepare player info for sending pinfotab = [] for pid, player_profile in config.registry[ 'players'].iteritems(): pinfotab.append(str(player_profile['pid'])) pinfotab.append(str(player_profile['login'])) pinfotab.append(str(player_profile['team'])) pinfotab.append(str(player_profile['character']['name'])) self.send('\xFF'.join(pinfotab)) return SequencerRegistered(self.socket, self.player_profile['pid'], self.player_profile['login'], challenge2, response2)
def on_read(self): self.socket.on_read() log('sakura.network.wrappers.LoggingInWrapper.on_read: starting') if len(self.socket.in_packets) > 0: inbound = self.socket.in_packets.pop(0) if self.state == STATE_AWAITING_LOGIN: login = str(inbound).lower() try: for pid, player in config.registry['players'].iteritems(): if player['login'].lower() == login: self.player_profile = player self.player_profile # throws Exception except KeyError: log('sakura.network.wrappers.LoggingInWrapper.on_read: No such player as requested') self.close() return # Generate challenge and response, send it self.challenge1 = 'alwaysthesun' self.response1 = sha1(self.player_profile['password'] + self.challenge1).hexdigest() self.send(self.challenge1) self.state = STATE_AWAITING_RESPONSE_1 elif self.state == STATE_AWAITING_RESPONSE_1: if self.response1 != str(inbound): log('sakura.network.wrappers.LoggingInWrapper.on_read: Invalid response1') self.close() return challenge2 = 'somemoresun' response2 = sha1(self.player_profile['password'] + challenge2).hexdigest() self.send(config.registry['map_name'].encode('utf8') + '\xFF' + challenge2) # Prepare player info for sending pinfotab = [] for pid, player_profile in config.registry['players'].iteritems(): pinfotab.append(str(player_profile['pid'])) pinfotab.append(str(player_profile['login'])) pinfotab.append(str(player_profile['team'])) pinfotab.append(str(player_profile['character']['name'])) self.send('\xFF'.join(pinfotab)) return SequencerRegistered(self.socket, self.player_profile['pid'], self.player_profile['login'], challenge2, response2)
def load_hero(charname): log('sakura.config.hero.load_hero: Compiling ', charname) cf = ConfigParser.ConfigParser() cf.read(sys.argv[2] + 'heroes/' + charname + '/infoHero.ini') cf.read(sys.argv[2] + 'heroes/' + charname + '/infoSkills.ini') related_shots_list = [] for rsc in cf.get('general', 'related_shots').split(' '): try: int(rsc) except: pass else: related_shots_list.append(int(rsc)) d = { 'name': cf.get('general', 'name'), 'speed': cf.getfloat('general', 'speed'), 'jump': cf.getfloat('general', 'jump'), 'mass': cf.getfloat('general', 'mass'), 'related_shots': related_shots_list, 'hp': cf.getfloat('general', 'hp'), 'regen': cf.getfloat('general', 'regen') } def import_skill(skillname): return __import__('sakura.scripting.library.skill', globals(), locals(), [skillname]).__dict__[skillname].Skill d['skilltab'] = [None, None, None, None, None] for keyid, skillkey in [(2, 'shift'), (0, 'q'), (1, 'e'), (3, 'lpm'), (4, 'ppm')]: secname = 'vkey_' + skillkey args = [] # Fetch arguments for i in xrange(1, 1000): try: arg = cf.get(secname, 'arg%s' % (i, )) except: break else: args.append(arg) # Fetch cooldown try: cooldown = float(cf.get(secname, 'cooldown')) except: d['skilltab'][keyid] = 0, lambda invoker: import_skill('noop')( invoker) continue # Fetch name try: skillname = cf.get(secname, 'name') except: d['skilltab'][keyid] = 0, lambda invoker: import_skill('noop')( invoker) skillobject = import_skill(skillname) def closeover(skillobject, args): def skill_closure(invoker): return skillobject(invoker, *args) return skill_closure # Add the cooldown and generating closure d['skilltab'][keyid] = cooldown, closeover(skillobject, args) del cf animations = {} # or 'animations as geometry set' # Get all animations for anim_dir in [ x for x in os.listdir(sys.argv[2] + 'heroes/' + charname) if x.startswith('anim') ]: try: anim_id = int(anim_dir[4:].strip()) cf = ConfigParser.ConfigParser() cf.read(sys.argv[2] + 'heroes/' + charname + '/' + anim_dir + '/hitbox.ini') sx, sy = cf.getint('synchroPoint', 'x'), cf.getint('synchroPoint', 'y') hitboxes = [] for rectangle_id in xrange(0, cf.getint('main', 'rectangles')): section = 'hitBox%s' % (rectangle_id, ) hitboxes.append( Rectangle( cf.getint(section, 'x1') - sx, cf.getint(section, 'y1') - sy, cf.getint(section, 'x2') - sx, cf.getint(section, 'y2') - sy)) animations[anim_id] = Geometry(hitboxes) animations[anim_id + 64] = Geometry(hitboxes).upside_down() except: print 'sakura.config.hero.load_hero: Skipping %s' % (anim_dir, ) d['animations'] = animations return d
def init_sequencer(): log('sakura.network.init_sequencer: Initializing sequencer') config.registry['_runtime']['sequencer'] = Sequencer()
from sakura.instrumentation import log import json import sys import time import sakura.config import sakura.network import sakura.gameworld import sakura.players import sakura.physics if __name__ == '__main__': log('sakura: Sakura v1.0 starting') log('sakura: Compiling configuration') sakura.config.init() log('sakura: Initializing network layer') sakura.network.init_sockets() sakura.network.init_sequencer() sakura.network.init_network() log('sakura: Initializing game world objects') sakura.physics.init_simulation() sakura.players.init_delegates() log('sakura: Starting game world simulation') sakura.gameworld.init_gameworld()
def init_network(): log('sakura.network.init_sequencer: Firing up the network') config.registry['_runtime']['select_loop_thread'] = SelectLoop() config.registry['_runtime']['select_loop_thread'].start()
def loop(self): # Get all sockets try: if config.registry['game_world_processor'] == False: print 'sakura.network.select_loop.loop: Terminating' raise Exception, 'Terminating' except KeyError: pass r_socks = [self.tcp_socket, self.udp_socket] + self.client_sockets w_socks = [x for x in self.client_sockets if x.wants_to_write()] try: rs, ws, xs = select(r_socks, w_socks, (), 5) except: for socket in self.client_sockets: try: select((socket, ), (), (), 0) except: log('sakura.network.select_loop.SelectLoop.loop: Found failed socket in select') self.client_sockets.remove(socket) socket.close() return for socket in ws: try: socket.on_write() except: log('sakura.network.select_loop.SelectLoop.loop: Failure during on_write') self.client_sockets.remove(socket) socket.close() continue for socket in rs: if socket == self.tcp_socket: log('sakura.network.select_loop.SelectLoop.loop: Accepting a connection') sock, addr = socket.accept() sock = NT1TCPSocket(sock) sock = LoggingInWrapper(sock) self.client_sockets.append(sock) elif socket == self.udp_socket: data, addr = socket.recvfrom(1024) self.sequencer.on_udp_packet(data, addr) else: try: nso = socket.on_read() except: log('sakura.network.select_loop.SelectLoop.loop: Failure during on_read') self.client_sockets.remove(socket) socket.close() continue if nso != None: # re-wrapping requested self.client_sockets.remove(socket) self.client_sockets.append(nso) for socket in self.client_sockets: if socket.has_timed_out(): log('sakura.network.select_loop.SelectLoop.loop: Socket timed out') self.client_sockets.remove(socket) socket.close() return
def on_register_socket(self, socket): """On new socket appears that needs punchthrough validation""" with self.sock_lock: self.unverified_sockets.append(socket) log('sakura.network.sequencer.Sequencer.on_register_socket: Added socket for verification' )
def load_hero(charname): log('sakura.config.hero.load_hero: Compiling ', charname) cf = ConfigParser.ConfigParser() cf.read(sys.argv[2]+'heroes/'+charname+'/infoHero.ini') cf.read(sys.argv[2]+'heroes/'+charname+'/infoSkills.ini') related_shots_list = [] for rsc in cf.get('general', 'related_shots').split(' '): try: int(rsc) except: pass else: related_shots_list.append(int(rsc)) d = {'name': cf.get('general', 'name'), 'speed': cf.getfloat('general', 'speed'), 'jump': cf.getfloat('general', 'jump'), 'mass': cf.getfloat('general', 'mass'), 'related_shots': related_shots_list, 'hp': cf.getfloat('general', 'hp'), 'regen': cf.getfloat('general', 'regen')} def import_skill(skillname): return __import__('sakura.scripting.library.skill', globals(), locals(), [skillname]).__dict__[skillname].Skill d['skilltab'] = [None, None, None, None, None] for keyid, skillkey in [(2, 'shift'), (0, 'q'), (1, 'e'), (3, 'lpm'), (4, 'ppm')]: secname = 'vkey_'+skillkey args = [] # Fetch arguments for i in xrange(1, 1000): try: arg = cf.get(secname, 'arg%s' % (i, )) except: break else: args.append(arg) # Fetch cooldown try: cooldown = float(cf.get(secname, 'cooldown')) except: d['skilltab'][keyid] = 0, lambda invoker: import_skill('noop')(invoker) continue # Fetch name try: skillname = cf.get(secname, 'name') except: d['skilltab'][keyid] = 0, lambda invoker: import_skill('noop')(invoker) skillobject = import_skill(skillname) def closeover(skillobject, args): def skill_closure(invoker): return skillobject(invoker, *args) return skill_closure # Add the cooldown and generating closure d['skilltab'][keyid] = cooldown, closeover(skillobject, args) del cf animations = {} # or 'animations as geometry set' # Get all animations for anim_dir in [x for x in os.listdir(sys.argv[2]+'heroes/'+charname) if x.startswith('anim')]: try: anim_id = int(anim_dir[4:].strip()) cf = ConfigParser.ConfigParser() cf.read(sys.argv[2]+'heroes/'+charname+'/'+anim_dir+'/hitbox.ini') sx, sy = cf.getint('synchroPoint', 'x'), cf.getint('synchroPoint', 'y') hitboxes = [] for rectangle_id in xrange(0, cf.getint('main', 'rectangles')): section = 'hitBox%s' % (rectangle_id, ) hitboxes.append(Rectangle(cf.getint(section, 'x1')-sx, cf.getint(section, 'y1')-sy, cf.getint(section, 'x2')-sx, cf.getint(section, 'y2')-sy)) animations[anim_id] = Geometry(hitboxes) animations[anim_id+64] = Geometry(hitboxes).upside_down() except: print 'sakura.config.hero.load_hero: Skipping %s' % (anim_dir, ) d['animations'] = animations return d
def loop(self): # Get all sockets try: if config.registry['game_world_processor'] == False: print 'sakura.network.select_loop.loop: Terminating' raise Exception, 'Terminating' except KeyError: pass r_socks = [self.tcp_socket, self.udp_socket] + self.client_sockets w_socks = [x for x in self.client_sockets if x.wants_to_write()] try: rs, ws, xs = select(r_socks, w_socks, (), 5) except: for socket in self.client_sockets: try: select((socket, ), (), (), 0) except: log('sakura.network.select_loop.SelectLoop.loop: Found failed socket in select' ) self.client_sockets.remove(socket) socket.close() return for socket in ws: try: socket.on_write() except: log('sakura.network.select_loop.SelectLoop.loop: Failure during on_write' ) self.client_sockets.remove(socket) socket.close() continue for socket in rs: if socket == self.tcp_socket: log('sakura.network.select_loop.SelectLoop.loop: Accepting a connection' ) sock, addr = socket.accept() sock = NT1TCPSocket(sock) sock = LoggingInWrapper(sock) self.client_sockets.append(sock) elif socket == self.udp_socket: data, addr = socket.recvfrom(1024) self.sequencer.on_udp_packet(data, addr) else: try: nso = socket.on_read() except: log('sakura.network.select_loop.SelectLoop.loop: Failure during on_read' ) self.client_sockets.remove(socket) socket.close() continue if nso != None: # re-wrapping requested self.client_sockets.remove(socket) self.client_sockets.append(nso) for socket in self.client_sockets: if socket.has_timed_out(): log('sakura.network.select_loop.SelectLoop.loop: Socket timed out' ) self.client_sockets.remove(socket) socket.close() return
def on_register_socket(self, socket): """On new socket appears that needs punchthrough validation""" with self.sock_lock: self.unverified_sockets.append(socket) log('sakura.network.sequencer.Sequencer.on_register_socket: Added socket for verification')