squashed commit of many breaking changes
Dropping Support for Python 3.7; adding support for Python 3.9
This commit is contained in:
		
							parent
							
								
									add0762114
								
							
						
					
					
						commit
						4f8c737eec
					
				|  | @ -11,7 +11,7 @@ def adjust(args): | ||||||
|     logger = logging.getLogger('Adjuster') |     logger = logging.getLogger('Adjuster') | ||||||
|     logger.info('Patching ROM.') |     logger.info('Patching ROM.') | ||||||
| 
 | 
 | ||||||
|     if os.path.splitext(args.rom)[-1].lower() == '.bmbp': |     if os.path.splitext(args.rom)[-1].lower() == '.apbp': | ||||||
|         import Patch |         import Patch | ||||||
|         meta, args.rom = Patch.create_rom_file(args.rom) |         meta, args.rom = Patch.create_rom_file(args.rom) | ||||||
|          |          | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								Gui.py
								
								
								
								
							
							
						
						
									
										2
									
								
								Gui.py
								
								
								
								
							|  | @ -655,7 +655,7 @@ def guiMain(args=None): | ||||||
|     romEntry2 = Entry(romDialogFrame2, textvariable=romVar2) |     romEntry2 = Entry(romDialogFrame2, textvariable=romVar2) | ||||||
| 
 | 
 | ||||||
|     def RomSelect2(): |     def RomSelect2(): | ||||||
|         rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc", ".bmbp")), ("All Files", "*")]) |         rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc", ".apbp")), ("All Files", "*")]) | ||||||
|         romVar2.set(rom) |         romVar2.set(rom) | ||||||
|     romSelectButton2 = Button(romDialogFrame2, text='Select Rom', command=RomSelect2) |     romSelectButton2 = Button(romDialogFrame2, text='Select Rom', command=RomSelect2) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										39
									
								
								Main.py
								
								
								
								
							
							
						
						
									
										39
									
								
								Main.py
								
								
								
								
							|  | @ -1,7 +1,6 @@ | ||||||
| from collections import OrderedDict | from collections import OrderedDict | ||||||
| import copy | import copy | ||||||
| from itertools import zip_longest | from itertools import zip_longest | ||||||
| import json |  | ||||||
| import logging | import logging | ||||||
| import os | import os | ||||||
| import random | import random | ||||||
|  | @ -170,7 +169,7 @@ def main(args, seed=None): | ||||||
| 
 | 
 | ||||||
|     logger.info('Patching ROM.') |     logger.info('Patching ROM.') | ||||||
| 
 | 
 | ||||||
|     outfilebase = 'BM_%s' % (args.outputname if args.outputname else world.seed) |     outfilebase = 'AP_%s' % (args.outputname if args.outputname else world.seed) | ||||||
| 
 | 
 | ||||||
|     rom_names = [] |     rom_names = [] | ||||||
| 
 | 
 | ||||||
|  | @ -258,7 +257,7 @@ def main(args, seed=None): | ||||||
|         rom.write_to_file(rompath, hide_enemizer=True) |         rom.write_to_file(rompath, hide_enemizer=True) | ||||||
|         if args.create_diff: |         if args.create_diff: | ||||||
|             Patch.create_patch_file(rompath) |             Patch.create_patch_file(rompath) | ||||||
|         return player, team, bytes(rom.name).decode() |         return player, team, bytes(rom.name) | ||||||
| 
 | 
 | ||||||
|     pool = concurrent.futures.ThreadPoolExecutor() |     pool = concurrent.futures.ThreadPoolExecutor() | ||||||
|     multidata_task = None |     multidata_task = None | ||||||
|  | @ -293,26 +292,26 @@ def main(args, seed=None): | ||||||
|             precollected_items[item.player - 1].append(item.code) |             precollected_items[item.player - 1].append(item.code) | ||||||
| 
 | 
 | ||||||
|         def write_multidata(roms): |         def write_multidata(roms): | ||||||
|  |             import base64 | ||||||
|  |             import pickle | ||||||
|             for future in roms: |             for future in roms: | ||||||
|                 rom_name = future.result() |                 rom_name = future.result() | ||||||
|                 rom_names.append(rom_name) |                 rom_names.append(rom_name) | ||||||
|             multidata = zlib.compress(json.dumps({"names": parsed_names, |             multidata = zlib.compress(pickle.dumps({"names": parsed_names, | ||||||
|                                                   # backwards compat for < 2.4.1 |                                                     "roms": {base64.b64encode(rom_name).decode(): (team, slot) for slot, team, rom_name in rom_names}, | ||||||
|                                                   "roms": [(slot, team, list(name.encode())) |                                                     "remote_items": {player for player in range(1, world.players + 1) if | ||||||
|                                                            for (slot, team, name) in rom_names], |                                                                      world.remote_items[player]}, | ||||||
|                                                   "rom_strings": rom_names, |                                                     "locations": { | ||||||
|                                                   "remote_items": [player for player in range(1, world.players + 1) if |                                                         (location.address, location.player) : | ||||||
|                                                                    world.remote_items[player]], |                                                             (location.item.code, location.item.player) | ||||||
|                                                   "locations": [((location.address, location.player), |                                                         for location in world.get_filled_locations() if | ||||||
|                                                                  (location.item.code, location.item.player)) |                                                         type(location.address) is int}, | ||||||
|                                                                 for location in world.get_filled_locations() if |                                                     "server_options": get_options()["server_options"], | ||||||
|                                                                 type(location.address) is int], |                                                     "er_hint_data": er_hint_data, | ||||||
|                                                   "server_options": get_options()["server_options"], |                                                     "precollected_items": precollected_items, | ||||||
|                                                   "er_hint_data": er_hint_data, |                                                     "version": _version_tuple, | ||||||
|                                                   "precollected_items": precollected_items, |                                                     "tags": ["AP"] | ||||||
|                                                   "version": _version_tuple, |                                                     }), 9) | ||||||
|                                                   "tags": ["ER"] |  | ||||||
|                                                   }).encode("utf-8"), 9) |  | ||||||
| 
 | 
 | ||||||
|             with open(output_path('%s.multidata' % outfilebase), 'wb') as f: |             with open(output_path('%s.multidata' % outfilebase), 'wb') as f: | ||||||
|                 f.write(multidata) |                 f.write(multidata) | ||||||
|  |  | ||||||
|  | @ -3,6 +3,10 @@ import sys | ||||||
| import subprocess | import subprocess | ||||||
| import importlib | import importlib | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | if sys.version_info < (3, 8, 6): | ||||||
|  |     raise RuntimeError("Incompatible Python Version. 3.8.7+ is supported.") | ||||||
|  | 
 | ||||||
| update_ran = hasattr(sys, "frozen") and getattr(sys, "frozen")  # don't run update if environment is frozen/compiled | update_ran = hasattr(sys, "frozen") and getattr(sys, "frozen")  # don't run update if environment is frozen/compiled | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										122
									
								
								MultiClient.py
								
								
								
								
							
							
						
						
									
										122
									
								
								MultiClient.py
								
								
								
								
							|  | @ -1,6 +1,5 @@ | ||||||
| import argparse | import argparse | ||||||
| import asyncio | import asyncio | ||||||
| import json |  | ||||||
| import logging | import logging | ||||||
| import urllib.parse | import urllib.parse | ||||||
| import atexit | import atexit | ||||||
|  | @ -13,6 +12,8 @@ import sys | ||||||
| import typing | import typing | ||||||
| import os | import os | ||||||
| import subprocess | import subprocess | ||||||
|  | import base64 | ||||||
|  | from json import loads, dumps | ||||||
| 
 | 
 | ||||||
| from random import randrange | from random import randrange | ||||||
| 
 | 
 | ||||||
|  | @ -113,7 +114,7 @@ class Context(): | ||||||
|     async def send_msgs(self, msgs): |     async def send_msgs(self, msgs): | ||||||
|         if not self.server or not self.server.socket.open or self.server.socket.closed: |         if not self.server or not self.server.socket.open or self.server.socket.closed: | ||||||
|             return |             return | ||||||
|         await self.server.socket.send(json.dumps(msgs)) |         await self.server.socket.send(dumps(msgs)) | ||||||
| 
 | 
 | ||||||
| color_codes = {'reset': 0, 'bold': 1, 'underline': 4, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, | color_codes = {'reset': 0, 'bold': 1, 'underline': 4, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, | ||||||
|                'magenta': 35, 'cyan': 36, 'white': 37, 'black_bg': 40, 'red_bg': 41, 'green_bg': 42, 'yellow_bg': 43, |                'magenta': 35, 'cyan': 36, 'white': 37, 'black_bg': 40, 'red_bg': 41, 'green_bg': 42, 'yellow_bg': 43, | ||||||
|  | @ -427,17 +428,17 @@ async def get_snes_devices(ctx: Context): | ||||||
|         "Opcode": "DeviceList", |         "Opcode": "DeviceList", | ||||||
|         "Space": "SNES" |         "Space": "SNES" | ||||||
|     } |     } | ||||||
|     await socket.send(json.dumps(DeviceList_Request)) |     await socket.send(dumps(DeviceList_Request)) | ||||||
| 
 | 
 | ||||||
|     reply = json.loads(await socket.recv()) |     reply = loads(await socket.recv()) | ||||||
|     devices = reply['Results'] if 'Results' in reply and len(reply['Results']) > 0 else None |     devices = reply['Results'] if 'Results' in reply and len(reply['Results']) > 0 else None | ||||||
| 
 | 
 | ||||||
|     if not devices: |     if not devices: | ||||||
|         ctx.ui_node.log_info('No SNES device found. Ensure QUsb2Snes is running and connect it to the multibridge.') |         ctx.ui_node.log_info('No SNES device found. Ensure QUsb2Snes is running and connect it to the multibridge.') | ||||||
|         while not devices: |         while not devices: | ||||||
|             await asyncio.sleep(1) |             await asyncio.sleep(1) | ||||||
|             await socket.send(json.dumps(DeviceList_Request)) |             await socket.send(dumps(DeviceList_Request)) | ||||||
|             reply = json.loads(await socket.recv()) |             reply = loads(await socket.recv()) | ||||||
|             devices = reply['Results'] if 'Results' in reply and len(reply['Results']) > 0 else None |             devices = reply['Results'] if 'Results' in reply and len(reply['Results']) > 0 else None | ||||||
| 
 | 
 | ||||||
|     ctx.ui_node.send_device_list(devices) |     ctx.ui_node.send_device_list(devices) | ||||||
|  | @ -481,7 +482,7 @@ async def snes_connect(ctx: Context, address): | ||||||
|             "Space": "SNES", |             "Space": "SNES", | ||||||
|             "Operands": [device] |             "Operands": [device] | ||||||
|         } |         } | ||||||
|         await ctx.snes_socket.send(json.dumps(Attach_Request)) |         await ctx.snes_socket.send(dumps(Attach_Request)) | ||||||
|         ctx.snes_state = SNES_ATTACHED |         ctx.snes_state = SNES_ATTACHED | ||||||
|         ctx.snes_attached_device = (devices.index(device), device) |         ctx.snes_attached_device = (devices.index(device), device) | ||||||
|         ctx.ui_node.send_connection_status(ctx) |         ctx.ui_node.send_connection_status(ctx) | ||||||
|  | @ -489,8 +490,8 @@ async def snes_connect(ctx: Context, address): | ||||||
|         if 'sd2snes' in device.lower() or (len(device) == 4 and device[:3] == 'COM'): |         if 'sd2snes' in device.lower() or (len(device) == 4 and device[:3] == 'COM'): | ||||||
|             ctx.ui_node.log_info("SD2SNES Detected") |             ctx.ui_node.log_info("SD2SNES Detected") | ||||||
|             ctx.is_sd2snes = True |             ctx.is_sd2snes = True | ||||||
|             await ctx.snes_socket.send(json.dumps({"Opcode" : "Info", "Space" : "SNES"})) |             await ctx.snes_socket.send(dumps({"Opcode" : "Info", "Space" : "SNES"})) | ||||||
|             reply = json.loads(await ctx.snes_socket.recv()) |             reply = loads(await ctx.snes_socket.recv()) | ||||||
|             if reply and 'Results' in reply: |             if reply and 'Results' in reply: | ||||||
|                 ctx.ui_node.log_info(reply['Results']) |                 ctx.ui_node.log_info(reply['Results']) | ||||||
|         else: |         else: | ||||||
|  | @ -501,7 +502,6 @@ async def snes_connect(ctx: Context, address): | ||||||
|         SNES_RECONNECT_DELAY = START_RECONNECT_DELAY |         SNES_RECONNECT_DELAY = START_RECONNECT_DELAY | ||||||
| 
 | 
 | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
| 
 |  | ||||||
|         if recv_task is not None: |         if recv_task is not None: | ||||||
|             if not ctx.snes_socket.closed: |             if not ctx.snes_socket.closed: | ||||||
|                 await ctx.snes_socket.close() |                 await ctx.snes_socket.close() | ||||||
|  | @ -576,7 +576,7 @@ async def snes_read(ctx : Context, address, size): | ||||||
|             "Operands" : [hex(address)[2:], hex(size)[2:]] |             "Operands" : [hex(address)[2:], hex(size)[2:]] | ||||||
|         } |         } | ||||||
|         try: |         try: | ||||||
|             await ctx.snes_socket.send(json.dumps(GetAddress_Request)) |             await ctx.snes_socket.send(dumps(GetAddress_Request)) | ||||||
|         except websockets.ConnectionClosed: |         except websockets.ConnectionClosed: | ||||||
|             return None |             return None | ||||||
| 
 | 
 | ||||||
|  | @ -633,7 +633,7 @@ async def snes_write(ctx : Context, write_list): | ||||||
|             PutAddress_Request['Operands'] = ["2C00", hex(len(cmd)-1)[2:], "2C00", "1"] |             PutAddress_Request['Operands'] = ["2C00", hex(len(cmd)-1)[2:], "2C00", "1"] | ||||||
|             try: |             try: | ||||||
|                 if ctx.snes_socket is not None: |                 if ctx.snes_socket is not None: | ||||||
|                     await ctx.snes_socket.send(json.dumps(PutAddress_Request)) |                     await ctx.snes_socket.send(dumps(PutAddress_Request)) | ||||||
|                 if ctx.snes_socket is not None: |                 if ctx.snes_socket is not None: | ||||||
|                     await ctx.snes_socket.send(cmd) |                     await ctx.snes_socket.send(cmd) | ||||||
|             except websockets.ConnectionClosed: |             except websockets.ConnectionClosed: | ||||||
|  | @ -645,7 +645,7 @@ async def snes_write(ctx : Context, write_list): | ||||||
|                 for address, data in write_list: |                 for address, data in write_list: | ||||||
|                     PutAddress_Request['Operands'] = [hex(address)[2:], hex(len(data))[2:]] |                     PutAddress_Request['Operands'] = [hex(address)[2:], hex(len(data))[2:]] | ||||||
|                     if ctx.snes_socket is not None: |                     if ctx.snes_socket is not None: | ||||||
|                         await ctx.snes_socket.send(json.dumps(PutAddress_Request)) |                         await ctx.snes_socket.send(dumps(PutAddress_Request)) | ||||||
|                     if ctx.snes_socket is not None: |                     if ctx.snes_socket is not None: | ||||||
|                         await ctx.snes_socket.send(data) |                         await ctx.snes_socket.send(data) | ||||||
|             except websockets.ConnectionClosed: |             except websockets.ConnectionClosed: | ||||||
|  | @ -674,7 +674,7 @@ async def snes_flush_writes(ctx : Context): | ||||||
| async def send_msgs(websocket, msgs): | async def send_msgs(websocket, msgs): | ||||||
|     if not websocket or not websocket.open or websocket.closed: |     if not websocket or not websocket.open or websocket.closed: | ||||||
|         return |         return | ||||||
|     await websocket.send(json.dumps(msgs)) |     await websocket.send(dumps(msgs)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| async def server_loop(ctx: Context, address=None): | async def server_loop(ctx: Context, address=None): | ||||||
|  | @ -685,16 +685,16 @@ async def server_loop(ctx: Context, address=None): | ||||||
|         ctx.ui_node.log_error('Already connected') |         ctx.ui_node.log_error('Already connected') | ||||||
|         return |         return | ||||||
| 
 | 
 | ||||||
|     if address is None:  # set through CLI or BMBP |     if address is None:  # set through CLI or APBP | ||||||
|         address = ctx.server_address |         address = ctx.server_address | ||||||
|     if address is None:  # see if this is an old connection |     if address is None:  # see if this is an old connection | ||||||
|         await asyncio.sleep(0.5)  # wait for snes connection to succeed if possible. |         await asyncio.sleep(0.5)  # wait for snes connection to succeed if possible. | ||||||
|         rom = ctx.rom if ctx.rom else None |         rom = ctx.rom if ctx.rom else None | ||||||
|         try: | 
 | ||||||
|             servers = cached_address = Utils.persistent_load()["servers"] |         servers = Utils.persistent_load()["servers"] | ||||||
|             address = servers[rom] if rom and rom in servers else servers["default"] |         if rom in servers: | ||||||
|         except Exception as e: |             address = servers[rom] | ||||||
|             logging.debug(f"Could not find cached server address. {e}") |             cached_address = True | ||||||
| 
 | 
 | ||||||
|     # Wait for the user to provide a multiworld server address |     # Wait for the user to provide a multiworld server address | ||||||
|     if not address: |     if not address: | ||||||
|  | @ -714,7 +714,7 @@ async def server_loop(ctx: Context, address=None): | ||||||
|         ctx.ui_node.send_connection_status(ctx) |         ctx.ui_node.send_connection_status(ctx) | ||||||
|         SERVER_RECONNECT_DELAY = START_RECONNECT_DELAY |         SERVER_RECONNECT_DELAY = START_RECONNECT_DELAY | ||||||
|         async for data in ctx.server.socket: |         async for data in ctx.server.socket: | ||||||
|             for msg in json.loads(data): |             for msg in loads(data): | ||||||
|                 cmd, args = (msg[0], msg[1]) if len(msg) > 1 else (msg, None) |                 cmd, args = (msg[0], msg[1]) if len(msg) > 1 else (msg, None) | ||||||
|                 await process_server_cmd(ctx, cmd, args) |                 await process_server_cmd(ctx, cmd, args) | ||||||
|         ctx.ui_node.log_warning('Disconnected from multiworld server, type /connect to reconnect') |         ctx.ui_node.log_warning('Disconnected from multiworld server, type /connect to reconnect') | ||||||
|  | @ -806,7 +806,6 @@ async def process_server_cmd(ctx: Context, cmd, args): | ||||||
|             raise Exception( |             raise Exception( | ||||||
|                 'Invalid ROM detected, please verify that you have loaded the correct rom and reconnect your snes (/snes)') |                 'Invalid ROM detected, please verify that you have loaded the correct rom and reconnect your snes (/snes)') | ||||||
|         if 'SlotAlreadyTaken' in args: |         if 'SlotAlreadyTaken' in args: | ||||||
|             Utils.persistent_store("servers", "default", ctx.server_address) |  | ||||||
|             Utils.persistent_store("servers", ctx.rom, ctx.server_address) |             Utils.persistent_store("servers", ctx.rom, ctx.server_address) | ||||||
|             raise Exception('Player slot already in use for that team') |             raise Exception('Player slot already in use for that team') | ||||||
|         if 'IncompatibleVersion' in args: |         if 'IncompatibleVersion' in args: | ||||||
|  | @ -814,7 +813,6 @@ async def process_server_cmd(ctx: Context, cmd, args): | ||||||
|         raise Exception('Connection refused by the multiworld host') |         raise Exception('Connection refused by the multiworld host') | ||||||
| 
 | 
 | ||||||
|     elif cmd == 'Connected': |     elif cmd == 'Connected': | ||||||
|         Utils.persistent_store("servers", "default", ctx.server_address) |  | ||||||
|         Utils.persistent_store("servers", ctx.rom, ctx.server_address) |         Utils.persistent_store("servers", ctx.rom, ctx.server_address) | ||||||
|         ctx.team, ctx.slot = args[0] |         ctx.team, ctx.slot = args[0] | ||||||
|         ctx.player_names = {p: n for p, n in args[1]} |         ctx.player_names = {p: n for p, n in args[1]} | ||||||
|  | @ -873,12 +871,6 @@ async def process_server_cmd(ctx: Context, cmd, args): | ||||||
|         logging.info('%s found %s (%s)' % (player_sent, item, color(get_location_name_from_address(found.location), |         logging.info('%s found %s (%s)' % (player_sent, item, color(get_location_name_from_address(found.location), | ||||||
|                                                                     'blue_bg', 'white'))) |                                                                     'blue_bg', 'white'))) | ||||||
| 
 | 
 | ||||||
|     elif cmd == 'Missing': |  | ||||||
|         if 'locations' in args: |  | ||||||
|             locations = json.loads(args['locations']) |  | ||||||
|             for location in locations: |  | ||||||
|                 ctx.ui_node.log_info(f'Missing: {location}') |  | ||||||
|             ctx.ui_node.log_info(f'Found {len(locations)} missing location checks') |  | ||||||
| 
 | 
 | ||||||
|     elif cmd == 'Hint': |     elif cmd == 'Hint': | ||||||
|         hints = [Utils.Hint(*hint) for hint in args] |         hints = [Utils.Hint(*hint) for hint in args] | ||||||
|  | @ -931,7 +923,7 @@ async def server_auth(ctx: Context, password_requested): | ||||||
|         return |         return | ||||||
|     ctx.awaiting_rom = False |     ctx.awaiting_rom = False | ||||||
|     ctx.auth = ctx.rom |     ctx.auth = ctx.rom | ||||||
|     auth = ctx.rom if ctx.server_version > (2, 4, 0) else list(ctx.rom.encode()) |     auth = base64.b64encode(ctx.rom).decode() | ||||||
|     await ctx.send_msgs([['Connect', { |     await ctx.send_msgs([['Connect', { | ||||||
|         'password': ctx.password, 'rom': auth, 'version': Utils._version_tuple, 'tags': get_tags(ctx), |         'password': ctx.password, 'rom': auth, 'version': Utils._version_tuple, 'tags': get_tags(ctx), | ||||||
|         'uuid': Utils.get_unique_identifier() |         'uuid': Utils.get_unique_identifier() | ||||||
|  | @ -1156,7 +1148,7 @@ async def game_watcher(ctx : Context): | ||||||
|             if rom is None or rom == bytes([0] * ROMNAME_SIZE): |             if rom is None or rom == bytes([0] * ROMNAME_SIZE): | ||||||
|                 continue |                 continue | ||||||
| 
 | 
 | ||||||
|             ctx.rom = rom.decode() |             ctx.rom = rom | ||||||
|             if not ctx.prev_rom or ctx.prev_rom != ctx.rom: |             if not ctx.prev_rom or ctx.prev_rom != ctx.rom: | ||||||
|                 ctx.locations_checked = set() |                 ctx.locations_checked = set() | ||||||
|                 ctx.locations_scouted = set() |                 ctx.locations_scouted = set() | ||||||
|  | @ -1251,45 +1243,43 @@ async def websocket_server(websocket: websockets.WebSocketServerProtocol, path, | ||||||
|     process_command = ClientCommandProcessor(ctx) |     process_command = ClientCommandProcessor(ctx) | ||||||
|     try: |     try: | ||||||
|         async for incoming_data in websocket: |         async for incoming_data in websocket: | ||||||
|             try: |             data = loads(incoming_data) | ||||||
|                 data = json.loads(incoming_data) |             logging.debug(f"WebUIData:{data}") | ||||||
|                 logging.debug(f"WebUIData:{data}") |             if ('type' not in data) or ('content' not in data): | ||||||
|                 if ('type' not in data) or ('content' not in data): |                 raise Exception('Invalid data received in websocket') | ||||||
|                     raise Exception('Invalid data received in websocket') |  | ||||||
| 
 | 
 | ||||||
|                 elif data['type'] == 'webStatus': |             elif data['type'] == 'webStatus': | ||||||
|                     if data['content'] == 'connections': |                 if data['content'] == 'connections': | ||||||
|                         ctx.ui_node.send_connection_status(ctx) |                     ctx.ui_node.send_connection_status(ctx) | ||||||
|                     elif data['content'] == 'devices': |                 elif data['content'] == 'devices': | ||||||
|                         await get_snes_devices(ctx) |                     await get_snes_devices(ctx) | ||||||
|                     elif data['content'] == 'gameInfo': |                 elif data['content'] == 'gameInfo': | ||||||
|                         ctx.ui_node.send_game_info(ctx) |                     ctx.ui_node.send_game_info(ctx) | ||||||
|                     elif data['content'] == 'checkData': |                 elif data['content'] == 'checkData': | ||||||
|                         ctx.ui_node.send_location_check(ctx, 'Waiting for check...') |                     ctx.ui_node.send_location_check(ctx, 'Waiting for check...') | ||||||
| 
 | 
 | ||||||
|                 elif data['type'] == 'webConfig': |             elif data['type'] == 'webConfig': | ||||||
|                     if 'serverAddress' in data['content']: |                 if 'serverAddress' in data['content']: | ||||||
|                         ctx.server_address = data['content']['serverAddress'] |                     ctx.server_address = data['content']['serverAddress'] | ||||||
|                         await connect(ctx, data['content']['serverAddress']) |                     await connect(ctx, data['content']['serverAddress']) | ||||||
|                     elif 'deviceId' in data['content']: |                 elif 'deviceId' in data['content']: | ||||||
|                         # Allow a SNES disconnect via UI sending -1 as new device |                     # Allow a SNES disconnect via UI sending -1 as new device | ||||||
|                         if data['content']['deviceId'] == "-1": |                     if data['content']['deviceId'] == "-1": | ||||||
|                             ctx.ui_node.manual_snes = None |                         ctx.ui_node.manual_snes = None | ||||||
|                             ctx.snes_reconnect_address = None |                         ctx.snes_reconnect_address = None | ||||||
|                             await snes_disconnect(ctx) |                         await snes_disconnect(ctx) | ||||||
|                         else: |                     else: | ||||||
|                             await snes_disconnect(ctx) |                         await snes_disconnect(ctx) | ||||||
|                             ctx.ui_node.manual_snes = data['content']['deviceId'] |                         ctx.ui_node.manual_snes = data['content']['deviceId'] | ||||||
|                             await snes_connect(ctx, ctx.snes_address) |                         await snes_connect(ctx, ctx.snes_address) | ||||||
| 
 | 
 | ||||||
|                 elif data['type'] == 'webControl': |             elif data['type'] == 'webControl': | ||||||
|                     if 'disconnect' in data['content']: |                 if 'disconnect' in data['content']: | ||||||
|                         await ctx.disconnect() |                     await ctx.disconnect() | ||||||
|  | 
 | ||||||
|  |             elif data['type'] == 'webCommand': | ||||||
|  |                 process_command(data['content']) | ||||||
| 
 | 
 | ||||||
|                 elif data['type'] == 'webCommand': |  | ||||||
|                     process_command(data['content']) |  | ||||||
|             except json.JSONDecodeError: |  | ||||||
|                 pass |  | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         if not isinstance(e, websockets.WebSocketException): |         if not isinstance(e, websockets.WebSocketException): | ||||||
|             logging.exception(e) |             logging.exception(e) | ||||||
|  |  | ||||||
|  | @ -121,8 +121,8 @@ if __name__ == "__main__": | ||||||
|                 seedname = segment |                 seedname = segment | ||||||
|                 break |                 break | ||||||
| 
 | 
 | ||||||
|         multidataname = f"BM_{seedname}.multidata" |         multidataname = f"AP_{seedname}.multidata" | ||||||
|         spoilername = f"BM_{seedname}_Spoiler.txt" |         spoilername = f"AP_{seedname}_Spoiler.txt" | ||||||
|         romfilename = "" |         romfilename = "" | ||||||
| 
 | 
 | ||||||
|         if player_name: |         if player_name: | ||||||
|  | @ -158,7 +158,7 @@ if __name__ == "__main__": | ||||||
|                 print(f"Removed {file} which is now present in the zipfile") |                 print(f"Removed {file} which is now present in the zipfile") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|             zipname = os.path.join(output_path, f"BM_{seedname}.{typical_zip_ending}") |             zipname = os.path.join(output_path, f"AP_{seedname}.{typical_zip_ending}") | ||||||
| 
 | 
 | ||||||
|             print(f"Creating zipfile {zipname}") |             print(f"Creating zipfile {zipname}") | ||||||
|             ipv4 = (host if host else get_public_ipv4()) + ":" + str(port) |             ipv4 = (host if host else get_public_ipv4()) + ":" + str(port) | ||||||
|  | @ -185,7 +185,7 @@ if __name__ == "__main__": | ||||||
|                         if seedname in file: |                         if seedname in file: | ||||||
|                             if file.endswith(".sfc"): |                             if file.endswith(".sfc"): | ||||||
|                                 futures.append(pool.submit(_handle_sfc_file, file)) |                                 futures.append(pool.submit(_handle_sfc_file, file)) | ||||||
|                             elif file.endswith(".bmbp"): |                             elif file.endswith(".apbp"): | ||||||
|                                 futures.append(pool.submit(_handle_diff_file, file)) |                                 futures.append(pool.submit(_handle_diff_file, file)) | ||||||
| 
 | 
 | ||||||
|                     if zip_multidata and os.path.exists(os.path.join(output_path, multidataname)): |                     if zip_multidata and os.path.exists(os.path.join(output_path, multidataname)): | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ from __future__ import annotations | ||||||
| import argparse | import argparse | ||||||
| import asyncio | import asyncio | ||||||
| import functools | import functools | ||||||
| import json |  | ||||||
| import logging | import logging | ||||||
| import zlib | import zlib | ||||||
| import collections | import collections | ||||||
|  | @ -13,6 +12,8 @@ import weakref | ||||||
| import datetime | import datetime | ||||||
| import threading | import threading | ||||||
| import random | import random | ||||||
|  | import pickle | ||||||
|  | from json import loads, dumps | ||||||
| 
 | 
 | ||||||
| import ModuleUpdate | import ModuleUpdate | ||||||
| 
 | 
 | ||||||
|  | @ -26,7 +27,8 @@ from fuzzywuzzy import process as fuzzy_process | ||||||
| import Items | import Items | ||||||
| import Regions | import Regions | ||||||
| import Utils | import Utils | ||||||
| from Utils import get_item_name_from_id, get_location_name_from_address, ReceivedItem, _version_tuple | from Utils import get_item_name_from_id, get_location_name_from_address, \ | ||||||
|  |     ReceivedItem, _version_tuple, restricted_loads | ||||||
| from NetUtils import Node, Endpoint | from NetUtils import Node, Endpoint | ||||||
| 
 | 
 | ||||||
| console_names = frozenset(set(Items.item_table) | set(Regions.location_table) | set(Items.item_name_groups)) | console_names = frozenset(set(Items.item_table) | set(Regions.location_table) | set(Items.item_name_groups)) | ||||||
|  | @ -106,28 +108,23 @@ class Context(Node): | ||||||
| 
 | 
 | ||||||
|     def load(self, multidatapath: str, use_embedded_server_options: bool = False): |     def load(self, multidatapath: str, use_embedded_server_options: bool = False): | ||||||
|         with open(multidatapath, 'rb') as f: |         with open(multidatapath, 'rb') as f: | ||||||
|             self._load(json.loads(zlib.decompress(f.read()).decode("utf-8-sig")), |             self._load(restricted_loads(zlib.decompress(f.read())), | ||||||
|                        use_embedded_server_options) |                        use_embedded_server_options) | ||||||
| 
 | 
 | ||||||
|         self.data_filename = multidatapath |         self.data_filename = multidatapath | ||||||
| 
 | 
 | ||||||
|     def _load(self, jsonobj: dict, use_embedded_server_options: bool): |     def _load(self, decoded_obj: dict, use_embedded_server_options: bool): | ||||||
|         for team, names in enumerate(jsonobj['names']): |         for team, names in enumerate(decoded_obj['names']): | ||||||
|             for player, name in enumerate(names, 1): |             for player, name in enumerate(names, 1): | ||||||
|                 self.player_names[(team, player)] = name |                 self.player_names[(team, player)] = name | ||||||
| 
 | 
 | ||||||
|         if "rom_strings" in jsonobj: |         self.rom_names = decoded_obj['roms'] | ||||||
|             self.rom_names = {rom: (team, slot) for slot, team, rom in jsonobj['rom_strings']} |         self.remote_items = decoded_obj['remote_items'] | ||||||
|         else: |         self.locations = decoded_obj['locations'] | ||||||
|             self.rom_names = {bytes(letter for letter in rom).decode(): (team, slot) for slot, team, rom in |         self.er_hint_data = {int(player): {int(address): name for address, name in loc_data.items()} | ||||||
|                               jsonobj['roms']} |                              for player, loc_data in decoded_obj["er_hint_data"].items()} | ||||||
|         self.remote_items = set(jsonobj['remote_items']) |  | ||||||
|         self.locations = {tuple(k): tuple(v) for k, v in jsonobj['locations']} |  | ||||||
|         if "er_hint_data" in jsonobj: |  | ||||||
|             self.er_hint_data = {int(player): {int(address): name for address, name in loc_data.items()} |  | ||||||
|                                  for player, loc_data in jsonobj["er_hint_data"].items()} |  | ||||||
|         if use_embedded_server_options: |         if use_embedded_server_options: | ||||||
|             server_options = jsonobj.get("server_options", {}) |             server_options = decoded_obj.get("server_options", {}) | ||||||
|             self._set_options(server_options) |             self._set_options(server_options) | ||||||
| 
 | 
 | ||||||
|     def _set_options(self, server_options: dict): |     def _set_options(self, server_options: dict): | ||||||
|  | @ -154,9 +151,9 @@ class Context(Node): | ||||||
| 
 | 
 | ||||||
|     def _save(self, exit_save:bool=False) -> bool: |     def _save(self, exit_save:bool=False) -> bool: | ||||||
|         try: |         try: | ||||||
|             jsonstr = json.dumps(self.get_save()) |             encoded_save = pickle.dumps(self.get_save()) | ||||||
|             with open(self.save_filename, "wb") as f: |             with open(self.save_filename, "wb") as f: | ||||||
|                 f.write(zlib.compress(jsonstr.encode("utf-8"))) |                 f.write(zlib.compress(encoded_save)) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             logging.exception(e) |             logging.exception(e) | ||||||
|             return False |             return False | ||||||
|  | @ -171,8 +168,8 @@ class Context(Node): | ||||||
|                         self.data_filename + '_')) + 'multisave' |                         self.data_filename + '_')) + 'multisave' | ||||||
|             try: |             try: | ||||||
|                 with open(self.save_filename, 'rb') as f: |                 with open(self.save_filename, 'rb') as f: | ||||||
|                     jsonobj = json.loads(zlib.decompress(f.read()).decode("utf-8")) |                     save_data = restricted_loads(zlib.decompress(f.read())) | ||||||
|                     self.set_save(jsonobj) |                     self.set_save(save_data) | ||||||
|             except FileNotFoundError: |             except FileNotFoundError: | ||||||
|                 logging.error('No save data found, starting a new game') |                 logging.error('No save data found, starting a new game') | ||||||
|             except Exception as e: |             except Exception as e: | ||||||
|  | @ -280,16 +277,21 @@ class Context(Node): | ||||||
|         logging.info("Notice (Player %s in team %d): %s" % (client.name, client.team + 1, text)) |         logging.info("Notice (Player %s in team %d): %s" % (client.name, client.team + 1, text)) | ||||||
|         asyncio.create_task(self.send_msgs(client, [['Print', text]])) |         asyncio.create_task(self.send_msgs(client, [['Print', text]])) | ||||||
| 
 | 
 | ||||||
|  |     def notify_client_multiple(self, client: Client, texts: typing.List[str]): | ||||||
|  |         if not client.auth: | ||||||
|  |             return | ||||||
|  |         asyncio.create_task(self.send_msgs(client, [['Print', text] for text in texts])) | ||||||
|  | 
 | ||||||
|     def broadcast_team(self, team, msgs): |     def broadcast_team(self, team, msgs): | ||||||
|         for client in self.endpoints: |         for client in self.endpoints: | ||||||
|             if client.auth and client.team == team: |             if client.auth and client.team == team: | ||||||
|                 asyncio.create_task(self.send_msgs(client, msgs)) |                 asyncio.create_task(self.send_msgs(client, msgs)) | ||||||
| 
 | 
 | ||||||
|     def broadcast_all(self, msgs): |     def broadcast_all(self, msgs): | ||||||
|         msgs = json.dumps(msgs) |         msgs = dumps(msgs) | ||||||
|         for endpoint in self.endpoints: |         for endpoint in self.endpoints: | ||||||
|             if endpoint.auth: |             if endpoint.auth: | ||||||
|                 asyncio.create_task(self.send_json_msgs(endpoint, msgs)) |                 asyncio.create_task(self.send_encoded_msgs(endpoint, msgs)) | ||||||
| 
 | 
 | ||||||
|     async def disconnect(self, endpoint): |     async def disconnect(self, endpoint): | ||||||
|         await super(Context, self).disconnect(endpoint) |         await super(Context, self).disconnect(endpoint) | ||||||
|  | @ -298,26 +300,25 @@ class Context(Node): | ||||||
| 
 | 
 | ||||||
| # separated out, due to compatibilty between clients | # separated out, due to compatibilty between clients | ||||||
| def notify_hints(ctx: Context, team: int, hints: typing.List[Utils.Hint]): | def notify_hints(ctx: Context, team: int, hints: typing.List[Utils.Hint]): | ||||||
|     cmd = json.dumps([["Hint", hints]])  # make sure it is a list, as it can be set internally |     cmd = dumps([["Hint", hints]])  # make sure it is a list, as it can be set internally | ||||||
|     texts = [['Print', format_hint(ctx, team, hint)] for hint in hints] |     texts = [['Print', format_hint(ctx, team, hint)] for hint in hints] | ||||||
|     for _, text in texts: |     for _, text in texts: | ||||||
|         logging.info("Notice (Team #%d): %s" % (team + 1, text)) |         logging.info("Notice (Team #%d): %s" % (team + 1, text)) | ||||||
| 
 |  | ||||||
|     for client in ctx.endpoints: |     for client in ctx.endpoints: | ||||||
|         if client.auth and client.team == team: |         if client.auth and client.team == team: | ||||||
|             asyncio.create_task(ctx.send_json_msgs(client, cmd)) |             asyncio.create_task(ctx.send_encoded_msgs(client, cmd)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def update_aliases(ctx: Context, team: int, client: typing.Optional[Client] = None): | def update_aliases(ctx: Context, team: int, client: typing.Optional[Client] = None): | ||||||
|     cmd = json.dumps([["AliasUpdate", |     cmd = dumps([["AliasUpdate", | ||||||
|                        [(key[1], ctx.get_aliased_name(*key)) for key, value in ctx.player_names.items() if |                        [(key[1], ctx.get_aliased_name(*key)) for key, value in ctx.player_names.items() if | ||||||
|                         key[0] == team]]]) |                         key[0] == team]]]) | ||||||
|     if client is None: |     if client is None: | ||||||
|         for client in ctx.endpoints: |         for client in ctx.endpoints: | ||||||
|             if client.team == team and client.auth and client.version > [2, 0, 3]: |             if client.team == team and client.auth: | ||||||
|                 asyncio.create_task(ctx.send_json_msgs(client, cmd)) |                 asyncio.create_task(ctx.send_encoded_msgs(client, cmd)) | ||||||
|     else: |     else: | ||||||
|         asyncio.create_task(ctx.send_json_msgs(client, cmd)) |         asyncio.create_task(ctx.send_encoded_msgs(client, cmd)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| async def server(websocket, path, ctx: Context): | async def server(websocket, path, ctx: Context): | ||||||
|  | @ -327,7 +328,7 @@ async def server(websocket, path, ctx: Context): | ||||||
|     try: |     try: | ||||||
|         await on_client_connected(ctx, client) |         await on_client_connected(ctx, client) | ||||||
|         async for data in websocket: |         async for data in websocket: | ||||||
|             for msg in json.loads(data): |             for msg in loads(data): | ||||||
|                 if len(msg) == 1: |                 if len(msg) == 1: | ||||||
|                     cmd = msg |                     cmd = msg | ||||||
|                     args = None |                     args = None | ||||||
|  | @ -392,7 +393,7 @@ async def countdown(ctx: Context, timer): | ||||||
| 
 | 
 | ||||||
| async def missing(ctx: Context, client: Client, locations: list): | async def missing(ctx: Context, client: Client, locations: list): | ||||||
|     await ctx.send_msgs(client, [['Missing', { |     await ctx.send_msgs(client, [['Missing', { | ||||||
|         'locations': json.dumps(locations) |         'locations': dumps(locations) | ||||||
|     }]]) |     }]]) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -762,6 +763,9 @@ class ClientMessageProcessor(CommonCommandProcessor): | ||||||
|                 self.output( |                 self.output( | ||||||
|                     "Sorry, client forfeiting requires you to have beaten the game on this server." |                     "Sorry, client forfeiting requires you to have beaten the game on this server." | ||||||
|                     " You can ask the server admin for a /forfeit") |                     " You can ask the server admin for a /forfeit") | ||||||
|  |                 if self.client.version < [2, 1, 0]: | ||||||
|  |                     self.output( | ||||||
|  |                         "Your client is too old to send game beaten information. Please update, load you savegame and reconnect.") | ||||||
|                 return False |                 return False | ||||||
| 
 | 
 | ||||||
|     def _cmd_remaining(self) -> bool: |     def _cmd_remaining(self) -> bool: | ||||||
|  | @ -805,13 +809,9 @@ class ClientMessageProcessor(CommonCommandProcessor): | ||||||
|                 locations.append(location_name) |                 locations.append(location_name) | ||||||
| 
 | 
 | ||||||
|         if len(locations) > 0: |         if len(locations) > 0: | ||||||
|             if self.client.version < [2, 3, 0]: |             texts = [f'Missing: {location}\n' for location in locations] | ||||||
|                 buffer = "" |             texts.append(f"Found {len(locations)} missing location checks") | ||||||
|                 for location in locations: |             self.ctx.notify_client_multiple(self.client, texts) | ||||||
|                     buffer += f'Missing: {location}\n' |  | ||||||
|                 self.output(buffer + f"Found {len(locations)} missing location checks") |  | ||||||
|             else: |  | ||||||
|                 asyncio.create_task(missing(self.ctx, self.client, locations)) |  | ||||||
|         else: |         else: | ||||||
|             self.output("No missing location checks found.") |             self.output("No missing location checks found.") | ||||||
|         return True |         return True | ||||||
|  | @ -954,6 +954,7 @@ async def process_client_cmd(ctx: Context, client: Client, cmd, args): | ||||||
|         if type(args["rom"]) == list: |         if type(args["rom"]) == list: | ||||||
|             args["rom"] = bytes(letter for letter in args["rom"]).decode() |             args["rom"] = bytes(letter for letter in args["rom"]).decode() | ||||||
|         if args['rom'] not in ctx.rom_names: |         if args['rom'] not in ctx.rom_names: | ||||||
|  |             logging.info((args["rom"], ctx.rom_names)) | ||||||
|             errors.add('InvalidRom') |             errors.add('InvalidRom') | ||||||
|         else: |         else: | ||||||
|             team, slot = ctx.rom_names[args['rom']] |             team, slot = ctx.rom_names[args['rom']] | ||||||
|  | @ -972,7 +973,7 @@ async def process_client_cmd(ctx: Context, client: Client, cmd, args): | ||||||
|                 client.name = ctx.player_names[(team, slot)] |                 client.name = ctx.player_names[(team, slot)] | ||||||
|                 client.team = team |                 client.team = team | ||||||
|                 client.slot = slot |                 client.slot = slot | ||||||
|         if "AP" not in args.get('tags', Client.tags): |         if ctx.compatibility == 1 and "AP" not in args.get('tags', Client.tags): | ||||||
|             errors.add('IncompatibleVersion') |             errors.add('IncompatibleVersion') | ||||||
|         elif ctx.compatibility == 0 and args.get('version', Client.version) != list(_version_tuple): |         elif ctx.compatibility == 0 and args.get('version', Client.version) != list(_version_tuple): | ||||||
|             errors.add('IncompatibleVersion') |             errors.add('IncompatibleVersion') | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								NetUtils.py
								
								
								
								
							
							
						
						
									
										13
									
								
								NetUtils.py
								
								
								
								
							|  | @ -1,33 +1,34 @@ | ||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
| import asyncio | import asyncio | ||||||
| import json |  | ||||||
| import logging | import logging | ||||||
| import typing | import typing | ||||||
|  | from json import loads, dumps | ||||||
| 
 | 
 | ||||||
| import websockets | import websockets | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| class Node: | class Node: | ||||||
|     endpoints: typing.List |     endpoints: typing.List | ||||||
|  |     dumper = staticmethod(dumps) | ||||||
|  |     loader = staticmethod(loads) | ||||||
| 
 | 
 | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self.endpoints = [] |         self.endpoints = [] | ||||||
| 
 | 
 | ||||||
|     def broadcast_all(self, msgs): |     def broadcast_all(self, msgs): | ||||||
|         msgs = json.dumps(msgs) |         msgs = self.dumper(msgs) | ||||||
|         for endpoint in self.endpoints: |         for endpoint in self.endpoints: | ||||||
|             asyncio.create_task(self.send_json_msgs(endpoint, msgs)) |             asyncio.create_task(self.send_encoded_msgs(endpoint, msgs)) | ||||||
| 
 | 
 | ||||||
|     async def send_msgs(self, endpoint: Endpoint, msgs): |     async def send_msgs(self, endpoint: Endpoint, msgs): | ||||||
|         if not endpoint.socket or not endpoint.socket.open or endpoint.socket.closed: |         if not endpoint.socket or not endpoint.socket.open or endpoint.socket.closed: | ||||||
|             return |             return | ||||||
|         try: |         try: | ||||||
|             await endpoint.socket.send(json.dumps(msgs)) |             await endpoint.socket.send(self.dumper(msgs)) | ||||||
|         except websockets.ConnectionClosed: |         except websockets.ConnectionClosed: | ||||||
|             logging.exception("Exception during send_msgs") |             logging.exception("Exception during send_msgs") | ||||||
|             await self.disconnect(endpoint) |             await self.disconnect(endpoint) | ||||||
| 
 | 
 | ||||||
|     async def send_json_msgs(self, endpoint: Endpoint, msg: str): |     async def send_encoded_msgs(self, endpoint: Endpoint, msg: str): | ||||||
|         if not endpoint.socket or not endpoint.socket.open or endpoint.socket.closed: |         if not endpoint.socket or not endpoint.socket.open or endpoint.socket.closed: | ||||||
|             return |             return | ||||||
|         try: |         try: | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								Patch.py
								
								
								
								
							
							
						
						
									
										6
									
								
								Patch.py
								
								
								
								
							|  | @ -57,7 +57,7 @@ def create_patch_file(rom_file_to_patch: str, server: str = "", destination: str | ||||||
|     bytes = generate_patch(load_bytes(rom_file_to_patch), |     bytes = generate_patch(load_bytes(rom_file_to_patch), | ||||||
|                            { |                            { | ||||||
|                                "server": server})  # allow immediate connection to server in multiworld. Empty string otherwise |                                "server": server})  # allow immediate connection to server in multiworld. Empty string otherwise | ||||||
|     target = destination if destination else os.path.splitext(rom_file_to_patch)[0] + ".bmbp" |     target = destination if destination else os.path.splitext(rom_file_to_patch)[0] + ".apbp" | ||||||
|     write_lzma(bytes, target) |     write_lzma(bytes, target) | ||||||
|     return target |     return target | ||||||
| 
 | 
 | ||||||
|  | @ -110,7 +110,7 @@ if __name__ == "__main__": | ||||||
|                     result = pool.submit(create_patch_file, rom, address) |                     result = pool.submit(create_patch_file, rom, address) | ||||||
|                     result.add_done_callback(lambda task: print(f"Created patch {task.result()}")) |                     result.add_done_callback(lambda task: print(f"Created patch {task.result()}")) | ||||||
| 
 | 
 | ||||||
|                 elif rom.endswith(".bmbp"): |                 elif rom.endswith(".apbp"): | ||||||
|                     print(f"Applying patch {rom}") |                     print(f"Applying patch {rom}") | ||||||
|                     data, target = create_rom_file(rom) |                     data, target = create_rom_file(rom) | ||||||
|                     romfile, adjusted = Utils.get_adjuster_settings(target) |                     romfile, adjusted = Utils.get_adjuster_settings(target) | ||||||
|  | @ -147,7 +147,7 @@ if __name__ == "__main__": | ||||||
| 
 | 
 | ||||||
|                     def _handle_zip_file_entry(zfinfo : zipfile.ZipInfo, server: str): |                     def _handle_zip_file_entry(zfinfo : zipfile.ZipInfo, server: str): | ||||||
|                         data = zfr.read(zfinfo) |                         data = zfr.read(zfinfo) | ||||||
|                         if zfinfo.filename.endswith(".bmbp"): |                         if zfinfo.filename.endswith(".apbp"): | ||||||
|                             data = update_patch_data(data, server) |                             data = update_patch_data(data, server) | ||||||
|                         with ziplock: |                         with ziplock: | ||||||
|                             zfw.writestr(zfinfo, data) |                             zfw.writestr(zfinfo, data) | ||||||
|  |  | ||||||
							
								
								
									
										7
									
								
								Rom.py
								
								
								
								
							
							
						
						
									
										7
									
								
								Rom.py
								
								
								
								
							|  | @ -79,12 +79,12 @@ class LocalRom(object): | ||||||
| 
 | 
 | ||||||
|             if self.verify(buffer): |             if self.verify(buffer): | ||||||
|                 self.buffer = buffer |                 self.buffer = buffer | ||||||
|                 if not os.path.exists(local_path('data', 'basepatch.bmbp')): |                 if not os.path.exists(local_path('data', 'basepatch.apbp')): | ||||||
|                     Patch.create_patch_file(local_path('basepatch.sfc')) |                     Patch.create_patch_file(local_path('basepatch.sfc')) | ||||||
|                 return |                 return | ||||||
| 
 | 
 | ||||||
|         if os.path.isfile(local_path('data', 'basepatch.bmbp')): |         if os.path.isfile(local_path('data', 'basepatch.apbp')): | ||||||
|             _, target, buffer = Patch.create_rom_bytes(local_path('data', 'basepatch.bmbp')) |             _, target, buffer = Patch.create_rom_bytes(local_path('data', 'basepatch.apbp')) | ||||||
|             if self.verify(buffer): |             if self.verify(buffer): | ||||||
|                 self.buffer = bytearray(buffer) |                 self.buffer = bytearray(buffer) | ||||||
|                 with open(local_path('basepatch.sfc'), 'wb') as stream: |                 with open(local_path('basepatch.sfc'), 'wb') as stream: | ||||||
|  | @ -1398,6 +1398,7 @@ def patch_rom(world, rom, player, team, enemized): | ||||||
|     # set rom name |     # set rom name | ||||||
|     # 21 bytes |     # 21 bytes | ||||||
|     from Main import __version__ |     from Main import __version__ | ||||||
|  |     # TODO: Adjust Enemizer to accept AP and AD | ||||||
|     rom.name = bytearray(f'BM{__version__.replace(".", "")[0:3]}_{team + 1}_{player}_{world.seed:09}\0', 'utf8')[:21] |     rom.name = bytearray(f'BM{__version__.replace(".", "")[0:3]}_{team + 1}_{player}_{world.seed:09}\0', 'utf8')[:21] | ||||||
|     rom.name.extend([0] * (21 - len(rom.name))) |     rom.name.extend([0] * (21 - len(rom.name))) | ||||||
|     rom.write_bytes(0x7FC0, rom.name) |     rom.write_bytes(0x7FC0, rom.name) | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ def download_patch(room_id, patch_id): | ||||||
|         patch_data = update_patch_data(patch.data, server="berserkermulti.world:" + str(last_port)) |         patch_data = update_patch_data(patch.data, server="berserkermulti.world:" + str(last_port)) | ||||||
|         patch_data = io.BytesIO(patch_data) |         patch_data = io.BytesIO(patch_data) | ||||||
| 
 | 
 | ||||||
|         fname = f"P{patch.player}_{pname}_{app.jinja_env.filters['suuid'](room_id)}.bmbp" |         fname = f"P{patch.player}_{pname}_{app.jinja_env.filters['suuid'](room_id)}.apbp" | ||||||
|         return send_file(patch_data, as_attachment=True, attachment_filename=fname) |         return send_file(patch_data, as_attachment=True, attachment_filename=fname) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -43,5 +43,5 @@ def download_raw_patch(seed_id, player_id): | ||||||
|         patch_data = update_patch_data(patch.data, server="") |         patch_data = update_patch_data(patch.data, server="") | ||||||
|         patch_data = io.BytesIO(patch_data) |         patch_data = io.BytesIO(patch_data) | ||||||
| 
 | 
 | ||||||
|         fname = f"P{patch.player}_{pname}_{app.jinja_env.filters['suuid'](seed_id)}.bmbp" |         fname = f"P{patch.player}_{pname}_{app.jinja_env.filters['suuid'](seed_id)}.apbp" | ||||||
|         return send_file(patch_data, as_attachment=True, attachment_filename=fname) |         return send_file(patch_data, as_attachment=True, attachment_filename=fname) | ||||||
|  |  | ||||||
|  | @ -123,7 +123,7 @@ def upload_to_db(folder, owner, sid): | ||||||
|     multidata = None |     multidata = None | ||||||
|     for file in os.listdir(folder): |     for file in os.listdir(folder): | ||||||
|         file = os.path.join(folder, file) |         file = os.path.join(folder, file) | ||||||
|         if file.endswith(".bmbp"): |         if file.endswith(".apbp"): | ||||||
|             player = int(file.split("P")[1].split(".")[0].split("_")[0]) |             player = int(file.split("P")[1].split(".")[0].split("_")[0]) | ||||||
|             patches.add(Patch(data=open(file, "rb").read(), player=player)) |             patches.add(Patch(data=open(file, "rb").read(), player=player)) | ||||||
|         elif file.endswith(".txt"): |         elif file.endswith(".txt"): | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ from pony.orm import commit, select | ||||||
| 
 | 
 | ||||||
| from WebHostLib import app, Seed, Room, Patch | from WebHostLib import app, Seed, Room, Patch | ||||||
| 
 | 
 | ||||||
| accepted_zip_contents = {"patches": ".bmbp", | accepted_zip_contents = {"patches": ".apbp", | ||||||
|                          "spoiler": ".txt", |                          "spoiler": ".txt", | ||||||
|                          "multidata": "multidata"} |                          "multidata": "multidata"} | ||||||
| 
 | 
 | ||||||
|  | @ -38,7 +38,7 @@ def uploads(): | ||||||
|                         for file in infolist: |                         for file in infolist: | ||||||
|                             if file.filename.endswith(banned_zip_contents): |                             if file.filename.endswith(banned_zip_contents): | ||||||
|                                 return "Uploaded data contained a rom file, which is likely to contain copyrighted material. Your file was deleted." |                                 return "Uploaded data contained a rom file, which is likely to contain copyrighted material. Your file was deleted." | ||||||
|                             elif file.filename.endswith(".bmbp"): |                             elif file.filename.endswith(".apbp"): | ||||||
|                                 player = int(file.filename.split("P")[1].split(".")[0].split("_")[0]) |                                 player = int(file.filename.split("P")[1].split(".")[0].split("_")[0]) | ||||||
|                                 patches.add(Patch(data=zfile.open(file, "r").read(), player=player)) |                                 patches.add(Patch(data=zfile.open(file, "r").read(), player=player)) | ||||||
|                             elif file.filename.endswith(".txt"): |                             elif file.filename.endswith(".txt"): | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								WebUI.py
								
								
								
								
							
							
						
						
									
										5
									
								
								WebUI.py
								
								
								
								
							|  | @ -1,6 +1,6 @@ | ||||||
| import http.server | import http.server | ||||||
| import logging | import logging | ||||||
| import os | import json | ||||||
| import socket | import socket | ||||||
| import socketserver | import socketserver | ||||||
| import threading | import threading | ||||||
|  | @ -16,6 +16,9 @@ logger = logging.getLogger("WebUIRelay") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class WebUiClient(Node): | class WebUiClient(Node): | ||||||
|  |     loader = staticmethod(json.loads) | ||||||
|  |     dumper = staticmethod(json.dumps) | ||||||
|  | 
 | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         super().__init__() |         super().__init__() | ||||||
|         self.manual_snes = None |         self.manual_snes = None | ||||||
|  |  | ||||||
|  | @ -57,7 +57,7 @@ Type: dirifempty; Name: "{app}" | ||||||
| 
 | 
 | ||||||
| [Registry] | [Registry] | ||||||
| 
 | 
 | ||||||
| Root: HKCR; Subkey: ".bmbp";                                 ValueData: "{#MyAppName}patch";        Flags: uninsdeletevalue; ValueType: string;  ValueName: "" | Root: HKCR; Subkey: ".apbp";                                 ValueData: "{#MyAppName}patch";        Flags: uninsdeletevalue; ValueType: string;  ValueName: "" | ||||||
| Root: HKCR; Subkey: "{#MyAppName}patch";                     ValueData: "Berserker's Multiworld Binary Patch";       Flags: uninsdeletekey;   ValueType: string;  ValueName: "" | Root: HKCR; Subkey: "{#MyAppName}patch";                     ValueData: "Berserker's Multiworld Binary Patch";       Flags: uninsdeletekey;   ValueType: string;  ValueName: "" | ||||||
| Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon";         ValueData: "{app}\{#MyAppExeName},0";                           ValueType: string;  ValueName: "" | Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon";         ValueData: "{app}\{#MyAppExeName},0";                           ValueType: string;  ValueName: "" | ||||||
| Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command";  ValueData: """{app}\{#MyAppExeName}"" ""%1""";                  ValueType: string;  ValueName: "" | Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command";  ValueData: """{app}\{#MyAppExeName}"" ""%1""";                  ValueType: string;  ValueName: "" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue