2020-04-22 03:09:46 +00:00
|
|
|
from __future__ import annotations
|
2021-01-02 11:49:43 +00:00
|
|
|
|
2020-06-21 13:32:31 +00:00
|
|
|
import typing
|
|
|
|
|
|
|
|
|
|
|
|
def tuplize_version(version: str) -> typing.Tuple[int, ...]:
|
2020-12-29 18:23:14 +00:00
|
|
|
return Version(*(int(piece, 10) for piece in version.split(".")))
|
|
|
|
|
2020-06-21 13:32:31 +00:00
|
|
|
|
2020-12-29 18:23:14 +00:00
|
|
|
class Version(typing.NamedTuple):
|
|
|
|
major: int
|
|
|
|
minor: int
|
2021-02-21 22:46:05 +00:00
|
|
|
build: int
|
2020-04-22 03:09:46 +00:00
|
|
|
|
2021-05-11 21:08:50 +00:00
|
|
|
__version__ = "0.1.1"
|
2020-06-21 13:32:31 +00:00
|
|
|
_version_tuple = tuplize_version(__version__)
|
2020-04-20 12:50:49 +00:00
|
|
|
|
2021-01-03 13:41:21 +00:00
|
|
|
import builtins
|
2017-11-28 14:36:32 +00:00
|
|
|
import os
|
2017-12-17 05:25:46 +00:00
|
|
|
import subprocess
|
2017-11-28 14:36:32 +00:00
|
|
|
import sys
|
2020-09-08 23:41:37 +00:00
|
|
|
import pickle
|
2020-02-16 14:32:40 +00:00
|
|
|
import functools
|
2021-01-03 13:32:32 +00:00
|
|
|
import io
|
2020-02-16 14:32:40 +00:00
|
|
|
|
2020-07-05 00:06:00 +00:00
|
|
|
from yaml import load, dump, safe_load
|
2020-02-16 14:32:40 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
from yaml import CLoader as Loader
|
|
|
|
except ImportError:
|
|
|
|
from yaml import Loader
|
|
|
|
|
2017-11-28 14:36:32 +00:00
|
|
|
|
2018-02-17 23:38:54 +00:00
|
|
|
def int16_as_bytes(value):
|
|
|
|
value = value & 0xFFFF
|
|
|
|
return [value & 0xFF, (value >> 8) & 0xFF]
|
|
|
|
|
2020-02-16 14:32:40 +00:00
|
|
|
|
2018-02-17 23:38:54 +00:00
|
|
|
def int32_as_bytes(value):
|
|
|
|
value = value & 0xFFFFFFFF
|
|
|
|
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]
|
|
|
|
|
2020-02-16 14:32:40 +00:00
|
|
|
|
2018-09-23 02:51:54 +00:00
|
|
|
def pc_to_snes(value):
|
2021-01-02 11:49:43 +00:00
|
|
|
return ((value << 1) & 0x7F0000) | (value & 0x7FFF) | 0x8000
|
2018-09-23 02:51:54 +00:00
|
|
|
|
2020-07-21 21:15:19 +00:00
|
|
|
|
2018-09-23 02:51:54 +00:00
|
|
|
def snes_to_pc(value):
|
2021-01-02 11:49:43 +00:00
|
|
|
return ((value & 0x7F0000) >> 1) | (value & 0x7FFF)
|
2018-09-23 02:51:54 +00:00
|
|
|
|
2020-07-21 21:15:19 +00:00
|
|
|
|
2020-01-14 09:42:27 +00:00
|
|
|
def parse_player_names(names, players, teams):
|
2020-03-02 22:27:16 +00:00
|
|
|
names = tuple(n for n in (n.strip() for n in names.split(",")) if n)
|
2020-08-20 02:03:49 +00:00
|
|
|
if len(names) != len(set(names)):
|
2021-03-07 19:11:36 +00:00
|
|
|
import collections
|
|
|
|
name_counter = collections.Counter(names)
|
|
|
|
raise ValueError(f"Duplicate Player names is not supported, "
|
|
|
|
f'found multiple "{name_counter.most_common(1)[0][0]}".')
|
2020-01-14 09:42:27 +00:00
|
|
|
ret = []
|
|
|
|
while names or len(ret) < teams:
|
|
|
|
team = [n[:16] for n in names[:players]]
|
2020-08-20 02:03:49 +00:00
|
|
|
# 16 bytes in rom per player, which will map to more in unicode, but those characters later get filtered
|
2020-01-14 09:42:27 +00:00
|
|
|
while len(team) != players:
|
2020-04-18 19:46:57 +00:00
|
|
|
team.append(f"Player{len(team) + 1}")
|
2020-01-14 09:42:27 +00:00
|
|
|
ret.append(team)
|
|
|
|
|
|
|
|
names = names[players:]
|
|
|
|
return ret
|
|
|
|
|
2020-07-21 21:15:19 +00:00
|
|
|
|
|
|
|
def is_bundled() -> bool:
|
2017-11-28 14:36:32 +00:00
|
|
|
return getattr(sys, 'frozen', False)
|
|
|
|
|
2020-07-21 21:15:19 +00:00
|
|
|
|
2020-08-25 11:22:47 +00:00
|
|
|
def local_path(*path):
|
2020-03-15 18:32:00 +00:00
|
|
|
if local_path.cached_path:
|
2020-08-25 11:22:47 +00:00
|
|
|
return os.path.join(local_path.cached_path, *path)
|
2017-11-28 14:36:32 +00:00
|
|
|
|
2020-03-23 06:45:40 +00:00
|
|
|
elif is_bundled():
|
|
|
|
if hasattr(sys, "_MEIPASS"):
|
|
|
|
# we are running in a PyInstaller bundle
|
|
|
|
local_path.cached_path = sys._MEIPASS # pylint: disable=protected-access,no-member
|
|
|
|
else:
|
|
|
|
# cx_Freeze
|
|
|
|
local_path.cached_path = os.path.dirname(os.path.abspath(sys.argv[0]))
|
2017-11-28 14:36:32 +00:00
|
|
|
else:
|
2020-03-23 06:45:40 +00:00
|
|
|
import __main__
|
2021-04-04 01:18:19 +00:00
|
|
|
if hasattr(__main__, "__file__"):
|
|
|
|
# we are running in a normal Python environment
|
|
|
|
local_path.cached_path = os.path.dirname(os.path.abspath(__main__.__file__))
|
|
|
|
else:
|
|
|
|
# pray
|
|
|
|
local_path.cached_path = os.path.abspath(".")
|
2020-03-15 18:32:00 +00:00
|
|
|
|
2020-08-25 11:22:47 +00:00
|
|
|
return os.path.join(local_path.cached_path, *path)
|
2017-11-28 14:36:32 +00:00
|
|
|
|
2021-01-02 11:49:43 +00:00
|
|
|
|
2017-11-28 14:36:32 +00:00
|
|
|
local_path.cached_path = None
|
|
|
|
|
2020-08-25 11:22:47 +00:00
|
|
|
|
|
|
|
def output_path(*path):
|
2020-03-15 18:32:00 +00:00
|
|
|
if output_path.cached_path:
|
2020-08-25 11:22:47 +00:00
|
|
|
return os.path.join(output_path.cached_path, *path)
|
2020-08-20 13:43:22 +00:00
|
|
|
output_path.cached_path = local_path(get_options()["general_options"]["output_path"])
|
2020-08-25 11:22:47 +00:00
|
|
|
path = os.path.join(output_path.cached_path, *path)
|
2020-08-01 14:52:11 +00:00
|
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
|
|
return path
|
2017-11-28 14:36:32 +00:00
|
|
|
|
2021-01-02 11:49:43 +00:00
|
|
|
|
2017-11-28 14:36:32 +00:00
|
|
|
output_path.cached_path = None
|
|
|
|
|
2021-01-02 11:49:43 +00:00
|
|
|
|
2017-11-28 14:36:32 +00:00
|
|
|
def open_file(filename):
|
|
|
|
if sys.platform == 'win32':
|
|
|
|
os.startfile(filename)
|
|
|
|
else:
|
2017-12-17 05:25:46 +00:00
|
|
|
open_command = 'open' if sys.platform == 'darwin' else 'xdg-open'
|
2017-11-28 14:36:32 +00:00
|
|
|
subprocess.call([open_command, filename])
|
2017-12-02 14:21:04 +00:00
|
|
|
|
2021-01-02 11:49:43 +00:00
|
|
|
|
2017-12-02 14:21:04 +00:00
|
|
|
def close_console():
|
|
|
|
if sys.platform == 'win32':
|
2021-01-02 11:49:43 +00:00
|
|
|
# windows
|
2017-12-02 14:21:04 +00:00
|
|
|
import ctypes.wintypes
|
|
|
|
try:
|
|
|
|
ctypes.windll.kernel32.FreeConsole()
|
2017-12-17 05:25:46 +00:00
|
|
|
except Exception:
|
2017-12-02 14:21:04 +00:00
|
|
|
pass
|
2018-01-01 19:42:23 +00:00
|
|
|
|
2020-02-09 04:28:48 +00:00
|
|
|
|
2020-07-05 00:06:00 +00:00
|
|
|
parse_yaml = safe_load
|
|
|
|
unsafe_parse_yaml = functools.partial(load, Loader=Loader)
|
2020-02-16 14:32:40 +00:00
|
|
|
|
2020-02-09 04:28:48 +00:00
|
|
|
|
2020-03-05 23:48:23 +00:00
|
|
|
def get_public_ipv4() -> str:
|
|
|
|
import socket
|
|
|
|
import urllib.request
|
|
|
|
import logging
|
|
|
|
ip = socket.gethostbyname(socket.gethostname())
|
|
|
|
try:
|
|
|
|
ip = urllib.request.urlopen('https://checkip.amazonaws.com/').read().decode('utf8').strip()
|
|
|
|
except Exception as e:
|
|
|
|
try:
|
|
|
|
ip = urllib.request.urlopen('https://v4.ident.me').read().decode('utf8').strip()
|
|
|
|
except:
|
|
|
|
logging.exception(e)
|
|
|
|
pass # we could be offline, in a local game, so no point in erroring out
|
|
|
|
return ip
|
2020-03-15 18:32:00 +00:00
|
|
|
|
2021-01-02 11:49:43 +00:00
|
|
|
|
2020-06-14 07:06:37 +00:00
|
|
|
def get_public_ipv6() -> str:
|
|
|
|
import socket
|
|
|
|
import urllib.request
|
|
|
|
import logging
|
|
|
|
ip = socket.gethostbyname(socket.gethostname())
|
|
|
|
try:
|
|
|
|
ip = urllib.request.urlopen('https://v6.ident.me').read().decode('utf8').strip()
|
|
|
|
except Exception as e:
|
|
|
|
logging.exception(e)
|
2020-06-21 14:13:42 +00:00
|
|
|
pass # we could be offline, in a local game, or ipv6 may not be available
|
2020-06-14 07:06:37 +00:00
|
|
|
return ip
|
2020-03-15 18:32:00 +00:00
|
|
|
|
2020-11-28 19:34:29 +00:00
|
|
|
|
|
|
|
def get_default_options() -> dict:
|
|
|
|
if not hasattr(get_default_options, "options"):
|
|
|
|
# Refer to host.yaml for comments as to what all these options mean.
|
2021-01-02 11:49:43 +00:00
|
|
|
options = {
|
|
|
|
"general_options": {
|
2021-04-01 09:40:58 +00:00
|
|
|
"output_path": "output",
|
|
|
|
},
|
|
|
|
"factorio_options": {
|
|
|
|
"executable": "factorio\\bin\\x64\\factorio",
|
|
|
|
},
|
|
|
|
"lttp_options": {
|
2021-01-02 11:49:43 +00:00
|
|
|
"rom_file": "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc",
|
|
|
|
"qusb2snes": "QUsb2Snes\\QUsb2Snes.exe",
|
|
|
|
"rom_start": True,
|
2021-04-01 09:40:58 +00:00
|
|
|
|
2021-01-02 11:49:43 +00:00
|
|
|
},
|
|
|
|
"server_options": {
|
|
|
|
"host": None,
|
|
|
|
"port": 38281,
|
|
|
|
"password": None,
|
|
|
|
"multidata": None,
|
|
|
|
"savefile": None,
|
|
|
|
"disable_save": False,
|
|
|
|
"loglevel": "info",
|
|
|
|
"server_password": None,
|
|
|
|
"disable_item_cheat": False,
|
|
|
|
"location_check_points": 1,
|
|
|
|
"hint_cost": 1000,
|
|
|
|
"forfeit_mode": "goal",
|
|
|
|
"remaining_mode": "goal",
|
|
|
|
"auto_shutdown": 0,
|
|
|
|
"compatibility": 2,
|
2021-04-07 00:37:21 +00:00
|
|
|
"log_network": 0
|
2021-01-02 11:49:43 +00:00
|
|
|
},
|
|
|
|
"multi_mystery_options": {
|
|
|
|
"teams": 1,
|
|
|
|
"enemizer_path": "EnemizerCLI/EnemizerCLI.Core.exe",
|
|
|
|
"player_files_path": "Players",
|
|
|
|
"players": 0,
|
|
|
|
"weights_file_path": "weights.yaml",
|
|
|
|
"meta_file_path": "meta.yaml",
|
2021-03-03 10:20:37 +00:00
|
|
|
"pre_roll": False,
|
2021-01-02 11:49:43 +00:00
|
|
|
"player_name": "",
|
|
|
|
"create_spoiler": 1,
|
|
|
|
"zip_roms": 0,
|
|
|
|
"zip_diffs": 2,
|
|
|
|
"zip_spoiler": 0,
|
|
|
|
"zip_multidata": 1,
|
|
|
|
"zip_format": 1,
|
2021-03-22 20:14:19 +00:00
|
|
|
"glitch_triforce_room": 1,
|
2021-01-02 11:49:43 +00:00
|
|
|
"race": 0,
|
|
|
|
"cpu_threads": 0,
|
|
|
|
"max_attempts": 0,
|
|
|
|
"take_first_working": False,
|
|
|
|
"keep_all_seeds": False,
|
|
|
|
"log_output_path": "Output Logs",
|
|
|
|
"log_level": None,
|
|
|
|
"plando_options": "bosses",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 19:34:29 +00:00
|
|
|
get_default_options.options = options
|
|
|
|
return get_default_options.options
|
|
|
|
|
2020-11-30 15:43:13 +00:00
|
|
|
|
|
|
|
blacklisted_options = {"multi_mystery_options.cpu_threads",
|
|
|
|
"multi_mystery_options.max_attempts",
|
|
|
|
"multi_mystery_options.take_first_working",
|
|
|
|
"multi_mystery_options.keep_all_seeds",
|
|
|
|
"multi_mystery_options.log_output_path",
|
|
|
|
"multi_mystery_options.log_level"}
|
|
|
|
|
|
|
|
|
2020-11-28 22:51:13 +00:00
|
|
|
def update_options(src: dict, dest: dict, filename: str, keys: list) -> dict:
|
|
|
|
import logging
|
|
|
|
for key, value in src.items():
|
|
|
|
new_keys = keys.copy()
|
|
|
|
new_keys.append(key)
|
2020-11-30 15:43:13 +00:00
|
|
|
option_name = '.'.join(new_keys)
|
2020-11-28 22:51:13 +00:00
|
|
|
if key not in dest:
|
|
|
|
dest[key] = value
|
2020-11-30 15:43:13 +00:00
|
|
|
if filename.endswith("options.yaml") and option_name not in blacklisted_options:
|
|
|
|
logging.info(f"Warning: {filename} is missing {option_name}")
|
2020-11-28 22:51:13 +00:00
|
|
|
elif isinstance(value, dict):
|
|
|
|
if not isinstance(dest.get(key, None), dict):
|
2020-11-30 15:43:13 +00:00
|
|
|
if filename.endswith("options.yaml") and option_name not in blacklisted_options:
|
|
|
|
logging.info(f"Warning: {filename} has {option_name}, but it is not a dictionary. overwriting.")
|
2020-11-28 22:51:13 +00:00
|
|
|
dest[key] = value
|
|
|
|
else:
|
|
|
|
dest[key] = update_options(value, dest[key], filename, new_keys)
|
|
|
|
return dest
|
2020-11-28 19:34:29 +00:00
|
|
|
|
2021-01-02 11:49:43 +00:00
|
|
|
|
2020-03-15 18:32:00 +00:00
|
|
|
def get_options() -> dict:
|
2020-03-23 06:59:55 +00:00
|
|
|
if not hasattr(get_options, "options"):
|
2020-03-15 18:32:00 +00:00
|
|
|
locations = ("options.yaml", "host.yaml",
|
|
|
|
local_path("options.yaml"), local_path("host.yaml"))
|
|
|
|
|
|
|
|
for location in locations:
|
|
|
|
if os.path.exists(location):
|
|
|
|
with open(location) as f:
|
2020-11-28 19:34:29 +00:00
|
|
|
options = parse_yaml(f.read())
|
|
|
|
|
2020-11-28 22:51:13 +00:00
|
|
|
get_options.options = update_options(get_default_options(), options, location, list())
|
2020-03-23 06:59:55 +00:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise FileNotFoundError(f"Could not find {locations[1]} to load options.")
|
|
|
|
return get_options.options
|
2020-04-14 18:22:42 +00:00
|
|
|
|
|
|
|
|
2021-04-28 13:48:11 +00:00
|
|
|
def get_item_name_from_id(code: int) -> str:
|
2021-02-21 19:17:24 +00:00
|
|
|
from worlds import lookup_any_item_id_to_name
|
|
|
|
return lookup_any_item_id_to_name.get(code, f'Unknown item (ID:{code})')
|
2020-04-14 18:22:42 +00:00
|
|
|
|
|
|
|
|
2021-04-28 13:48:11 +00:00
|
|
|
def get_location_name_from_id(code: int) -> str:
|
2021-02-21 19:17:24 +00:00
|
|
|
from worlds import lookup_any_location_id_to_name
|
2021-04-28 13:48:11 +00:00
|
|
|
return lookup_any_location_id_to_name.get(code, f'Unknown location (ID:{code})')
|
2020-04-14 18:22:42 +00:00
|
|
|
|
|
|
|
|
2021-02-25 01:07:28 +00:00
|
|
|
def persistent_store(category: str, key: typing.Any, value: typing.Any):
|
2020-04-24 03:29:02 +00:00
|
|
|
path = local_path("_persistent_storage.yaml")
|
|
|
|
storage: dict = persistent_load()
|
|
|
|
category = storage.setdefault(category, {})
|
|
|
|
category[key] = value
|
|
|
|
with open(path, "wt") as f:
|
|
|
|
f.write(dump(storage))
|
|
|
|
|
|
|
|
|
2020-04-26 13:14:30 +00:00
|
|
|
def persistent_load() -> typing.Dict[dict]:
|
2020-06-04 19:27:29 +00:00
|
|
|
storage = getattr(persistent_load, "storage", None)
|
|
|
|
if storage:
|
|
|
|
return storage
|
2020-04-24 03:29:02 +00:00
|
|
|
path = local_path("_persistent_storage.yaml")
|
|
|
|
storage: dict = {}
|
|
|
|
if os.path.exists(path):
|
|
|
|
try:
|
|
|
|
with open(path, "r") as f:
|
2020-07-05 00:06:00 +00:00
|
|
|
storage = unsafe_parse_yaml(f.read())
|
2020-04-24 03:29:02 +00:00
|
|
|
except Exception as e:
|
|
|
|
import logging
|
|
|
|
logging.debug(f"Could not read store: {e}")
|
2020-04-30 05:42:26 +00:00
|
|
|
if storage is None:
|
|
|
|
storage = {}
|
2020-06-04 19:27:29 +00:00
|
|
|
persistent_load.storage = storage
|
2020-04-24 03:29:02 +00:00
|
|
|
return storage
|
|
|
|
|
|
|
|
|
2020-06-09 16:02:15 +00:00
|
|
|
def get_adjuster_settings(romfile: str) -> typing.Tuple[str, bool]:
|
2020-06-07 19:04:33 +00:00
|
|
|
if hasattr(get_adjuster_settings, "adjuster_settings"):
|
|
|
|
adjuster_settings = getattr(get_adjuster_settings, "adjuster_settings")
|
|
|
|
else:
|
2020-11-11 12:15:35 +00:00
|
|
|
adjuster_settings = persistent_load().get("adjuster", {}).get("last_settings_3", {})
|
|
|
|
|
2020-06-07 19:04:33 +00:00
|
|
|
if adjuster_settings:
|
|
|
|
import pprint
|
|
|
|
import Patch
|
|
|
|
adjuster_settings.rom = romfile
|
|
|
|
adjuster_settings.baserom = Patch.get_base_rom_path()
|
2021-03-27 14:12:08 +00:00
|
|
|
adjuster_settings.world = None
|
2020-06-07 19:04:33 +00:00
|
|
|
whitelist = {"disablemusic", "fastmenu", "heartbeep", "heartcolor", "ow_palettes", "quickswap",
|
2020-11-11 12:15:35 +00:00
|
|
|
"uw_palettes", "sprite"}
|
2020-06-07 19:04:33 +00:00
|
|
|
printed_options = {name: value for name, value in vars(adjuster_settings).items() if name in whitelist}
|
2021-03-30 16:28:28 +00:00
|
|
|
if hasattr(adjuster_settings, "sprite_pool"):
|
|
|
|
sprite_pool = {}
|
|
|
|
for sprite in getattr(adjuster_settings, "sprite_pool"):
|
|
|
|
if sprite in sprite_pool:
|
|
|
|
sprite_pool[sprite] += 1
|
|
|
|
else:
|
|
|
|
sprite_pool[sprite] = 1
|
|
|
|
if sprite_pool:
|
|
|
|
printed_options["sprite_pool"] = sprite_pool
|
|
|
|
|
2020-11-11 12:15:35 +00:00
|
|
|
|
2020-06-07 19:04:33 +00:00
|
|
|
if hasattr(get_adjuster_settings, "adjust_wanted"):
|
|
|
|
adjust_wanted = getattr(get_adjuster_settings, "adjust_wanted")
|
2021-01-02 11:49:43 +00:00
|
|
|
elif persistent_load().get("adjuster", {}).get("never_adjust", False): # never adjust, per user request
|
2020-11-11 12:15:35 +00:00
|
|
|
return romfile, False
|
2020-06-07 19:04:33 +00:00
|
|
|
else:
|
|
|
|
adjust_wanted = input(f"Last used adjuster settings were found. Would you like to apply these? \n"
|
|
|
|
f"{pprint.pformat(printed_options)}\n"
|
2020-11-11 12:15:35 +00:00
|
|
|
f"Enter yes, no or never: ")
|
2020-06-07 19:04:33 +00:00
|
|
|
if adjust_wanted and adjust_wanted.startswith("y"):
|
2021-03-27 14:12:08 +00:00
|
|
|
if hasattr(adjuster_settings, "sprite_pool"):
|
|
|
|
from Adjuster import AdjusterWorld
|
|
|
|
adjuster_settings.world = AdjusterWorld(getattr(adjuster_settings, "sprite_pool"))
|
|
|
|
|
2020-06-07 19:04:33 +00:00
|
|
|
adjusted = True
|
2021-02-19 18:08:11 +00:00
|
|
|
import Adjuster
|
|
|
|
_, romfile = Adjuster.adjust(adjuster_settings)
|
2021-03-27 14:12:08 +00:00
|
|
|
|
|
|
|
if hasattr(adjuster_settings, "world"):
|
|
|
|
delattr(adjuster_settings, "world")
|
2020-11-11 12:15:35 +00:00
|
|
|
elif adjust_wanted and "never" in adjust_wanted:
|
|
|
|
persistent_store("adjuster", "never_adjust", True)
|
|
|
|
return romfile, False
|
2020-06-07 19:04:33 +00:00
|
|
|
else:
|
|
|
|
adjusted = False
|
|
|
|
import logging
|
|
|
|
if not hasattr(get_adjuster_settings, "adjust_wanted"):
|
|
|
|
logging.info(f"Skipping post-patch adjustment")
|
|
|
|
get_adjuster_settings.adjuster_settings = adjuster_settings
|
|
|
|
get_adjuster_settings.adjust_wanted = adjust_wanted
|
|
|
|
return romfile, adjusted
|
2020-06-09 16:02:15 +00:00
|
|
|
return romfile, False
|
2020-06-07 19:04:33 +00:00
|
|
|
|
|
|
|
|
2020-06-04 19:27:29 +00:00
|
|
|
def get_unique_identifier():
|
|
|
|
uuid = persistent_load().get("client", {}).get("uuid", None)
|
|
|
|
if uuid:
|
|
|
|
return uuid
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
uuid = uuid.getnode()
|
|
|
|
persistent_store("client", "uuid", uuid)
|
|
|
|
return uuid
|
2020-09-08 23:41:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
safe_builtins = {
|
|
|
|
'set',
|
|
|
|
'frozenset',
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class RestrictedUnpickler(pickle.Unpickler):
|
|
|
|
def find_class(self, module, name):
|
|
|
|
if module == "builtins" and name in safe_builtins:
|
|
|
|
return getattr(builtins, name)
|
2021-04-11 22:06:27 +00:00
|
|
|
if module == "NetUtils" and name in {"NetworkItem", "ClientStatus", "Hint"}:
|
2021-02-21 19:17:24 +00:00
|
|
|
import NetUtils
|
|
|
|
return getattr(NetUtils, name)
|
2020-09-08 23:41:37 +00:00
|
|
|
# Forbid everything else.
|
|
|
|
raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
|
|
|
|
(module, name))
|
|
|
|
|
|
|
|
|
|
|
|
def restricted_loads(s):
|
|
|
|
"""Helper function analogous to pickle.loads()."""
|
2021-01-03 13:32:32 +00:00
|
|
|
return RestrictedUnpickler(io.BytesIO(s)).load()
|