Multidata/save: moved away from pickle and store a compressed json instead
This commit is contained in:
parent
1be0d62d4f
commit
a3657c02aa
18
Main.py
18
Main.py
|
@ -4,9 +4,9 @@ from itertools import zip_longest
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pickle
|
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
import zlib
|
||||||
|
|
||||||
from BaseClasses import World, CollectionState, Item, Region, Location, Shop
|
from BaseClasses import World, CollectionState, Item, Region, Location, Shop
|
||||||
from Regions import create_regions, mark_light_world_regions
|
from Regions import create_regions, mark_light_world_regions
|
||||||
|
@ -140,12 +140,9 @@ def main(args, seed=None):
|
||||||
player_names = parse_names_string(args.names)
|
player_names = parse_names_string(args.names)
|
||||||
outfilebase = 'ER_%s' % (args.outputname if args.outputname else world.seed)
|
outfilebase = 'ER_%s' % (args.outputname if args.outputname else world.seed)
|
||||||
|
|
||||||
|
rom_names = []
|
||||||
jsonout = {}
|
jsonout = {}
|
||||||
if not args.suppress_rom:
|
if not args.suppress_rom:
|
||||||
from MultiServer import MultiWorld
|
|
||||||
multidata = MultiWorld()
|
|
||||||
multidata.players = world.players
|
|
||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none'
|
use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none'
|
||||||
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
|
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
|
||||||
|
@ -161,16 +158,12 @@ def main(args, seed=None):
|
||||||
else:
|
else:
|
||||||
rom = LocalRom(args.rom)
|
rom = LocalRom(args.rom)
|
||||||
patch_rom(world, player, rom, use_enemizer)
|
patch_rom(world, player, rom, use_enemizer)
|
||||||
|
rom_names.append((player, list(rom.name)))
|
||||||
|
|
||||||
enemizer_patch = []
|
enemizer_patch = []
|
||||||
if use_enemizer and (args.enemizercli or not args.jsonout):
|
if use_enemizer and (args.enemizercli or not args.jsonout):
|
||||||
enemizer_patch = get_enemizer_patch(world, player, rom, args.rom, args.enemizercli, args.shufflepalette[player], args.shufflepots[player])
|
enemizer_patch = get_enemizer_patch(world, player, rom, args.rom, args.enemizercli, args.shufflepalette[player], args.shufflepots[player])
|
||||||
|
|
||||||
multidata.rom_names[player] = list(rom.name)
|
|
||||||
for location in world.get_filled_locations(player):
|
|
||||||
if type(location.address) is int:
|
|
||||||
multidata.locations[(location.address, player)] = (location.item.code, location.item.player)
|
|
||||||
|
|
||||||
if args.jsonout:
|
if args.jsonout:
|
||||||
jsonout[f'patch{player}'] = rom.patches
|
jsonout[f'patch{player}'] = rom.patches
|
||||||
if use_enemizer:
|
if use_enemizer:
|
||||||
|
@ -209,7 +202,10 @@ def main(args, seed=None):
|
||||||
rom.write_to_file(output_path(f'{outfilebase}{playername}{outfilesuffix}.sfc'))
|
rom.write_to_file(output_path(f'{outfilebase}{playername}{outfilesuffix}.sfc'))
|
||||||
|
|
||||||
with open(output_path('%s_multidata' % outfilebase), 'wb') as f:
|
with open(output_path('%s_multidata' % outfilebase), 'wb') as f:
|
||||||
pickle.dump(multidata, f, pickle.HIGHEST_PROTOCOL)
|
jsonstr = json.dumps((world.players,
|
||||||
|
rom_names,
|
||||||
|
[((location.address, location.player), (location.item.code, location.item.player)) for location in world.get_filled_locations() if type(location.address) is int]))
|
||||||
|
f.write(zlib.compress(jsonstr.encode("utf-8")))
|
||||||
|
|
||||||
if args.create_spoiler and not args.jsonout:
|
if args.create_spoiler and not args.jsonout:
|
||||||
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
||||||
|
|
|
@ -4,10 +4,10 @@ import asyncio
|
||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import pickle
|
|
||||||
import re
|
import re
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import websockets
|
import websockets
|
||||||
|
import zlib
|
||||||
|
|
||||||
import Items
|
import Items
|
||||||
import Regions
|
import Regions
|
||||||
|
@ -22,18 +22,14 @@ class Client:
|
||||||
self.slot = None
|
self.slot = None
|
||||||
self.send_index = 0
|
self.send_index = 0
|
||||||
|
|
||||||
class MultiWorld:
|
|
||||||
def __init__(self):
|
|
||||||
self.players = None
|
|
||||||
self.rom_names = {}
|
|
||||||
self.locations = {}
|
|
||||||
|
|
||||||
class Context:
|
class Context:
|
||||||
def __init__(self, host, port, password):
|
def __init__(self, host, port, password):
|
||||||
self.data_filename = None
|
self.data_filename = None
|
||||||
self.save_filename = None
|
self.save_filename = None
|
||||||
self.disable_save = False
|
self.disable_save = False
|
||||||
self.world = MultiWorld()
|
self.players = 0
|
||||||
|
self.rom_names = {}
|
||||||
|
self.locations = {}
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
self.password = password
|
self.password = password
|
||||||
|
@ -44,7 +40,7 @@ class Context:
|
||||||
def get_room_info(ctx : Context):
|
def get_room_info(ctx : Context):
|
||||||
return {
|
return {
|
||||||
'password': ctx.password is not None,
|
'password': ctx.password is not None,
|
||||||
'slots': ctx.world.players,
|
'slots': ctx.players,
|
||||||
'players': [(client.name, client.team, client.slot) for client in ctx.clients if client.auth]
|
'players': [(client.name, client.team, client.slot) for client in ctx.clients if client.auth]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,8 +171,8 @@ def forfeit_player(ctx : Context, team, slot, name):
|
||||||
def register_location_checks(ctx : Context, name, team, slot, locations):
|
def register_location_checks(ctx : Context, name, team, slot, locations):
|
||||||
found_items = False
|
found_items = False
|
||||||
for location in locations:
|
for location in locations:
|
||||||
if (location, slot) in ctx.world.locations:
|
if (location, slot) in ctx.locations:
|
||||||
target_item, target_player = ctx.world.locations[(location, slot)]
|
target_item, target_player = ctx.locations[(location, slot)]
|
||||||
if target_player != slot:
|
if target_player != slot:
|
||||||
found = False
|
found = False
|
||||||
recvd_items = get_received_items(ctx, team, target_player)
|
recvd_items = get_received_items(ctx, team, target_player)
|
||||||
|
@ -196,7 +192,10 @@ def register_location_checks(ctx : Context, name, team, slot, locations):
|
||||||
if found_items and not ctx.disable_save:
|
if found_items and not ctx.disable_save:
|
||||||
try:
|
try:
|
||||||
with open(ctx.save_filename, "wb") as f:
|
with open(ctx.save_filename, "wb") as f:
|
||||||
pickle.dump((ctx.world.players, ctx.world.rom_names, ctx.received_items), f, pickle.HIGHEST_PROTOCOL)
|
jsonstr = json.dumps((ctx.players,
|
||||||
|
[(k, v) for k, v in ctx.rom_names.items()],
|
||||||
|
[(k, [i.__dict__ for i in v]) for k, v in ctx.received_items.items()]))
|
||||||
|
f.write(zlib.compress(jsonstr.encode("utf-8")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
|
|
||||||
|
@ -233,13 +232,13 @@ async def process_client_cmd(ctx : Context, client : Client, cmd, args):
|
||||||
if 'slot' in args and any([c.slot == args['slot'] for c in ctx.clients if c.auth and same_team(c.team, client.team)]):
|
if 'slot' in args and any([c.slot == args['slot'] for c in ctx.clients if c.auth and same_team(c.team, client.team)]):
|
||||||
errors.add('SlotAlreadyTaken')
|
errors.add('SlotAlreadyTaken')
|
||||||
elif 'slot' not in args or not args['slot']:
|
elif 'slot' not in args or not args['slot']:
|
||||||
for slot in range(1, ctx.world.players + 1):
|
for slot in range(1, ctx.players + 1):
|
||||||
if slot not in [c.slot for c in ctx.clients if c.auth and same_team(c.team, client.team)]:
|
if slot not in [c.slot for c in ctx.clients if c.auth and same_team(c.team, client.team)]:
|
||||||
client.slot = slot
|
client.slot = slot
|
||||||
break
|
break
|
||||||
elif slot == ctx.world.players:
|
elif slot == ctx.players:
|
||||||
errors.add('SlotAlreadyTaken')
|
errors.add('SlotAlreadyTaken')
|
||||||
elif args['slot'] not in range(1, ctx.world.players + 1):
|
elif args['slot'] not in range(1, ctx.players + 1):
|
||||||
errors.add('InvalidSlot')
|
errors.add('InvalidSlot')
|
||||||
else:
|
else:
|
||||||
client.slot = args['slot']
|
client.slot = args['slot']
|
||||||
|
@ -251,7 +250,7 @@ async def process_client_cmd(ctx : Context, client : Client, cmd, args):
|
||||||
await send_msgs(client.socket, [['ConnectionRefused', list(errors)]])
|
await send_msgs(client.socket, [['ConnectionRefused', list(errors)]])
|
||||||
else:
|
else:
|
||||||
client.auth = True
|
client.auth = True
|
||||||
reply = [['Connected', ctx.world.rom_names[client.slot]]]
|
reply = [['Connected', ctx.rom_names[client.slot]]]
|
||||||
items = get_received_items(ctx, client.team, client.slot)
|
items = get_received_items(ctx, client.team, client.slot)
|
||||||
if items:
|
if items:
|
||||||
reply.append(['ReceivedItems', (0, tuplize_received_items(items))])
|
reply.append(['ReceivedItems', (0, tuplize_received_items(items))])
|
||||||
|
@ -358,13 +357,16 @@ async def main():
|
||||||
ctx.data_filename = tkinter.filedialog.askopenfilename(filetypes=(("Multiworld data","*multidata"),))
|
ctx.data_filename = tkinter.filedialog.askopenfilename(filetypes=(("Multiworld data","*multidata"),))
|
||||||
|
|
||||||
with open(ctx.data_filename, 'rb') as f:
|
with open(ctx.data_filename, 'rb') as f:
|
||||||
ctx.world = pickle.load(f)
|
jsonobj = json.loads(zlib.decompress(f.read()).decode("utf-8"))
|
||||||
|
ctx.players = jsonobj[0]
|
||||||
|
ctx.rom_names = {k: v for k, v in jsonobj[1]}
|
||||||
|
ctx.locations = {tuple(k): tuple(v) for k, v in jsonobj[2]}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Failed to read multiworld data (%s)' % e)
|
print('Failed to read multiworld data (%s)' % e)
|
||||||
return
|
return
|
||||||
|
|
||||||
ip = urllib.request.urlopen('https://v4.ident.me').read().decode('utf8') if not ctx.host else ctx.host
|
ip = urllib.request.urlopen('https://v4.ident.me').read().decode('utf8') if not ctx.host else ctx.host
|
||||||
print('Hosting game of %d players (%s) at %s:%d' % (ctx.world.players, 'No password' if not ctx.password else 'Password: %s' % ctx.password, ip, ctx.port))
|
print('Hosting game of %d players (%s) at %s:%d' % (ctx.players, 'No password' if not ctx.password else 'Password: %s' % ctx.password, ip, ctx.port))
|
||||||
|
|
||||||
ctx.disable_save = args.disable_save
|
ctx.disable_save = args.disable_save
|
||||||
if not ctx.disable_save:
|
if not ctx.disable_save:
|
||||||
|
@ -372,8 +374,11 @@ async def main():
|
||||||
ctx.save_filename = (ctx.data_filename[:-9] if ctx.data_filename[-9:] == 'multidata' else (ctx.data_filename + '_')) + 'multisave'
|
ctx.save_filename = (ctx.data_filename[:-9] if ctx.data_filename[-9:] == 'multidata' else (ctx.data_filename + '_')) + 'multisave'
|
||||||
try:
|
try:
|
||||||
with open(ctx.save_filename, 'rb') as f:
|
with open(ctx.save_filename, 'rb') as f:
|
||||||
players, rom_names, received_items = pickle.load(f)
|
jsonobj = json.loads(zlib.decompress(f.read()).decode("utf-8"))
|
||||||
if players != ctx.world.players or rom_names != ctx.world.rom_names:
|
players = jsonobj[0]
|
||||||
|
rom_names = {k: v for k, v in jsonobj[1]}
|
||||||
|
received_items = {tuple(k): [ReceivedItem(**i) for i in v] for k, v in jsonobj[2]}
|
||||||
|
if players != ctx.players or rom_names != ctx.rom_names:
|
||||||
raise Exception('Save file mismatch, will start a new game')
|
raise Exception('Save file mismatch, will start a new game')
|
||||||
ctx.received_items = received_items
|
ctx.received_items = received_items
|
||||||
print('Loaded save file with %d received items for %d players' % (sum([len(p) for p in received_items.values()]), len(received_items)))
|
print('Loaded save file with %d received items for %d players' % (sum([len(p) for p in received_items.values()]), len(received_items)))
|
||||||
|
|
Loading…
Reference in New Issue