Compare commits

...

7 Commits

Author SHA1 Message Date
black-sliver
53ef2aa786 setup.py: downgrade cx_freeze (#865)
Latest cx_freeze breaks tkinter/tcl on linux
2022-09-13 08:36:09 +02:00
TheBigSalarius
156e9e0e43 FF1: Throw exception for Noverworld 2022-09-12 03:48:07 +02:00
Fabian Dill
ef46979bd8 SC2: fix client freezing on exit while SC2 is running (#1011) 2022-09-12 02:08:33 +02:00
Fabian Dill
b2aa251c47 SC2: fix bitflag overflow when multiple instances of an Item are acquired (#1008) 2022-09-12 01:51:25 +02:00
Fabian Dill
e204a0fce6 Subnautica: fix missed item and correct other item pool counts to fit it 2022-09-12 01:44:10 +02:00
TheBigSalarius
bb386d3bd7 FF1: fix FF1Client messaging and scoped lua messaging with printjson
Corrects the issue causing the client and lua messaging not displaying properly after the printjson changes
2022-09-12 01:19:51 +02:00
Fabian Dill
88a225764a FF1: fix printjson 2022-09-12 01:19:51 +02:00
10 changed files with 102 additions and 86 deletions

View File

@@ -5,6 +5,7 @@ import urllib.parse
import sys import sys
import typing import typing
import time import time
import functools
import ModuleUpdate import ModuleUpdate
ModuleUpdate.update() ModuleUpdate.update()
@@ -17,7 +18,8 @@ if __name__ == "__main__":
Utils.init_logging("TextClient", exception_logger="Client") Utils.init_logging("TextClient", exception_logger="Client")
from MultiServer import CommandProcessor from MultiServer import CommandProcessor
from NetUtils import Endpoint, decode, NetworkItem, encode, JSONtoTextParser, ClientStatus, Permission, NetworkSlot from NetUtils import Endpoint, decode, NetworkItem, encode, JSONtoTextParser, \
ClientStatus, Permission, NetworkSlot, RawJSONtoTextParser
from Utils import Version, stream_input from Utils import Version, stream_input
from worlds import network_data_package, AutoWorldRegister from worlds import network_data_package, AutoWorldRegister
import os import os
@@ -204,6 +206,10 @@ class CommonContext:
# execution # execution
self.keep_alive_task = asyncio.create_task(keep_alive(self), name="Bouncy") self.keep_alive_task = asyncio.create_task(keep_alive(self), name="Bouncy")
@functools.cached_property
def raw_text_parser(self) -> RawJSONtoTextParser:
return RawJSONtoTextParser(self)
@property @property
def total_locations(self) -> typing.Optional[int]: def total_locations(self) -> typing.Optional[int]:
"""Will return None until connected.""" """Will return None until connected."""

View File

@@ -1,4 +1,5 @@
import asyncio import asyncio
import copy
import json import json
import time import time
from asyncio import StreamReader, StreamWriter from asyncio import StreamReader, StreamWriter
@@ -6,7 +7,7 @@ from typing import List
import Utils import Utils
from CommonClient import CommonContext, server_loop, gui_enabled, console_loop, ClientCommandProcessor, logger, \ from CommonClient import CommonContext, server_loop, gui_enabled, ClientCommandProcessor, logger, \
get_base_parser get_base_parser
SYSTEM_MESSAGE_ID = 0 SYSTEM_MESSAGE_ID = 0
@@ -64,7 +65,7 @@ class FF1Context(CommonContext):
def _set_message(self, msg: str, msg_id: int): def _set_message(self, msg: str, msg_id: int):
if DISPLAY_MSGS: if DISPLAY_MSGS:
self.messages[(time.time(), msg_id)] = msg self.messages[time.time(), msg_id] = msg
def on_package(self, cmd: str, args: dict): def on_package(self, cmd: str, args: dict):
if cmd == 'Connected': if cmd == 'Connected':
@@ -73,32 +74,28 @@ class FF1Context(CommonContext):
msg = args['text'] msg = args['text']
if ': !' not in msg: if ': !' not in msg:
self._set_message(msg, SYSTEM_MESSAGE_ID) self._set_message(msg, SYSTEM_MESSAGE_ID)
elif cmd == "ReceivedItems":
msg = f"Received {', '.join([self.item_names[item.item] for item in args['items']])}" def on_print_json(self, args: dict):
self._set_message(msg, SYSTEM_MESSAGE_ID) if self.ui:
elif cmd == 'PrintJSON': self.ui.print_json(copy.deepcopy(args["data"]))
print_type = args['type'] else:
item = args['item'] text = self.jsontotextparser(copy.deepcopy(args["data"]))
receiving_player_id = args['receiving'] logger.info(text)
receiving_player_name = self.player_names[receiving_player_id] relevant = args.get("type", None) in {"Hint", "ItemSend"}
sending_player_id = item.player if relevant:
sending_player_name = self.player_names[item.player] item = args["item"]
if print_type == 'Hint': # goes to this world
msg = f"Hint: Your {self.item_names[item.item]} is at" \ if self.slot_concerns_self(args["receiving"]):
f" {self.player_names[item.player]}'s {self.location_names[item.location]}" relevant = True
self._set_message(msg, item.item) # found in this world
elif print_type == 'ItemSend' and receiving_player_id != self.slot: elif self.slot_concerns_self(item.player):
if sending_player_id == self.slot: relevant = True
if receiving_player_id == self.slot: # not related
msg = f"You found your own {self.item_names[item.item]}" else:
else: relevant = False
msg = f"You sent {self.item_names[item.item]} to {receiving_player_name}" if relevant:
else: item = args["item"]
if receiving_player_id == sending_player_id: msg = self.raw_text_parser(copy.deepcopy(args["data"]))
msg = f"{sending_player_name} found their {self.item_names[item.item]}"
else:
msg = f"{sending_player_name} sent {self.item_names[item.item]} to " \
f"{receiving_player_name}"
self._set_message(msg, item.item) self._set_message(msg, item.item)
def run_gui(self): def run_gui(self):

View File

@@ -743,6 +743,7 @@ async def countdown(ctx: Context, timer: int):
broadcast_countdown(ctx, 0, f"[Server]: GO") broadcast_countdown(ctx, 0, f"[Server]: GO")
ctx.countdown_timer = 0 ctx.countdown_timer = 0
def broadcast_text_all(ctx: Context, text: str, additional_arguments: dict = {}): def broadcast_text_all(ctx: Context, text: str, additional_arguments: dict = {}):
old_clients, new_clients = [], [] old_clients, new_clients = [], []
@@ -755,8 +756,10 @@ def broadcast_text_all(ctx: Context, text: str, additional_arguments: dict = {})
ctx.broadcast(old_clients, [{"cmd": "Print", "text": text }]) ctx.broadcast(old_clients, [{"cmd": "Print", "text": text }])
ctx.broadcast(new_clients, [{**{"cmd": "PrintJSON", "data": [{ "text": text }]}, **additional_arguments}]) ctx.broadcast(new_clients, [{**{"cmd": "PrintJSON", "data": [{ "text": text }]}, **additional_arguments}])
def broadcast_countdown(ctx: Context, timer: int, message: str): def broadcast_countdown(ctx: Context, timer: int, message: str):
broadcast_text_all(ctx, message, { "type": "Countdown", "countdown": timer }) broadcast_text_all(ctx, message, {"type": "Countdown", "countdown": timer})
def get_players_string(ctx: Context): def get_players_string(ctx: Context):
auth_clients = {(c.team, c.slot) for c in ctx.endpoints if c.auth} auth_clients = {(c.team, c.slot) for c in ctx.endpoints if c.auth}

View File

@@ -15,9 +15,6 @@ import typing
from json import loads, dumps from json import loads, dumps
import ModuleUpdate
ModuleUpdate.update()
from Utils import init_logging, messagebox from Utils import init_logging, messagebox
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -19,10 +19,11 @@ from sc2.data import Race
from sc2.main import run_game from sc2.main import run_game
from sc2.player import Bot from sc2.player import Bot
import NetUtils
from MultiServer import mark_raw from MultiServer import mark_raw
from Utils import init_logging, is_windows from Utils import init_logging, is_windows
from worlds.sc2wol import SC2WoLWorld from worlds.sc2wol import SC2WoLWorld
from worlds.sc2wol.Items import lookup_id_to_name, item_table from worlds.sc2wol.Items import lookup_id_to_name, item_table, ItemData, type_flaggroups
from worlds.sc2wol.Locations import SC2WOL_LOC_ID_OFFSET from worlds.sc2wol.Locations import SC2WOL_LOC_ID_OFFSET
from worlds.sc2wol.MissionTables import lookup_id_to_mission from worlds.sc2wol.MissionTables import lookup_id_to_mission
from worlds.sc2wol.Regions import MissionInfo from worlds.sc2wol.Regions import MissionInfo
@@ -135,7 +136,7 @@ class SC2Context(CommonContext):
last_loc_list = None last_loc_list = None
difficulty_override = -1 difficulty_override = -1
mission_id_to_location_ids: typing.Dict[int, typing.List[int]] = {} mission_id_to_location_ids: typing.Dict[int, typing.List[int]] = {}
raw_text_parser: RawJSONtoTextParser last_bot: typing.Optional[ArchipelagoBot] = None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(SC2Context, self).__init__(*args, **kwargs) super(SC2Context, self).__init__(*args, **kwargs)
@@ -164,10 +165,13 @@ class SC2Context(CommonContext):
check_mod_install() check_mod_install()
def on_print_json(self, args: dict): def on_print_json(self, args: dict):
# goes to this world
if "receiving" in args and self.slot_concerns_self(args["receiving"]): if "receiving" in args and self.slot_concerns_self(args["receiving"]):
relevant = True relevant = True
# found in this world
elif "item" in args and self.slot_concerns_self(args["item"].player): elif "item" in args and self.slot_concerns_self(args["item"].player):
relevant = True relevant = True
# not related
else: else:
relevant = False relevant = False
@@ -355,6 +359,8 @@ class SC2Context(CommonContext):
async def shutdown(self): async def shutdown(self):
await super(SC2Context, self).shutdown() await super(SC2Context, self).shutdown()
if self.last_bot:
self.last_bot.want_close = True
if self.sc2_run_task: if self.sc2_run_task:
self.sc2_run_task.cancel() self.sc2_run_task.cancel()
@@ -431,47 +437,27 @@ wol_default_categories = [
] ]
def calculate_items(items): def calculate_items(items: typing.List[NetUtils.NetworkItem]) -> typing.List[int]:
unit_unlocks = 0 network_item: NetUtils.NetworkItem
armory1_unlocks = 0 accumulators: typing.List[int] = [0 for _ in type_flaggroups]
armory2_unlocks = 0
upgrade_unlocks = 0
building_unlocks = 0
merc_unlocks = 0
lab_unlocks = 0
protoss_unlock = 0
minerals = 0
vespene = 0
supply = 0
for item in items: for network_item in items:
data = lookup_id_to_name[item.item] name: str = lookup_id_to_name[network_item.item]
item_data: ItemData = item_table[name]
if item_table[data].type == "Unit": # exists exactly once
unit_unlocks += (1 << item_table[data].number) if item_data.quantity == 1:
elif item_table[data].type == "Upgrade": accumulators[type_flaggroups[item_data.type]] |= 1 << item_data.number
upgrade_unlocks += (1 << item_table[data].number)
elif item_table[data].type == "Armory 1":
armory1_unlocks += (1 << item_table[data].number)
elif item_table[data].type == "Armory 2":
armory2_unlocks += (1 << item_table[data].number)
elif item_table[data].type == "Building":
building_unlocks += (1 << item_table[data].number)
elif item_table[data].type == "Mercenary":
merc_unlocks += (1 << item_table[data].number)
elif item_table[data].type == "Laboratory":
lab_unlocks += (1 << item_table[data].number)
elif item_table[data].type == "Protoss":
protoss_unlock += (1 << item_table[data].number)
elif item_table[data].type == "Minerals":
minerals += item_table[data].number
elif item_table[data].type == "Vespene":
vespene += item_table[data].number
elif item_table[data].type == "Supply":
supply += item_table[data].number
return [unit_unlocks, upgrade_unlocks, armory1_unlocks, armory2_unlocks, building_unlocks, merc_unlocks, # exists multiple times
lab_unlocks, protoss_unlock, minerals, vespene, supply] elif item_data.type == "Upgrade":
accumulators[type_flaggroups[item_data.type]] += 1 << item_data.number
# sum
else:
accumulators[type_flaggroups[item_data.type]] += item_data.number
return accumulators
def calc_difficulty(difficulty): def calc_difficulty(difficulty):
@@ -502,7 +488,7 @@ class ArchipelagoBot(sc2.bot_ai.BotAI):
setup_done: bool setup_done: bool
ctx: SC2Context ctx: SC2Context
mission_id: int mission_id: int
want_close: bool = False
can_read_game = False can_read_game = False
last_received_update: int = 0 last_received_update: int = 0
@@ -510,12 +496,17 @@ class ArchipelagoBot(sc2.bot_ai.BotAI):
def __init__(self, ctx: SC2Context, mission_id): def __init__(self, ctx: SC2Context, mission_id):
self.setup_done = False self.setup_done = False
self.ctx = ctx self.ctx = ctx
self.ctx.last_bot = self
self.mission_id = mission_id self.mission_id = mission_id
self.boni = [False for _ in range(max_bonus)] self.boni = [False for _ in range(max_bonus)]
super(ArchipelagoBot, self).__init__() super(ArchipelagoBot, self).__init__()
async def on_step(self, iteration: int): async def on_step(self, iteration: int):
if self.want_close:
self.want_close = False
await self._client.leave()
return
game_state = 0 game_state = 0
if not self.setup_done: if not self.setup_done:
self.setup_done = True self.setup_done = True

View File

@@ -17,7 +17,7 @@ from Launcher import components, icon_paths
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it # This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
import subprocess import subprocess
import pkg_resources import pkg_resources
requirement = 'cx-Freeze>=6.11' requirement = 'cx-Freeze==6.10'
try: try:
pkg_resources.require(requirement) pkg_resources.require(requirement)
import cx_Freeze import cx_Freeze
@@ -70,7 +70,7 @@ def _threaded_hash(filepath):
# cx_Freeze's build command runs other commands. Override to accept --yes and store that. # cx_Freeze's build command runs other commands. Override to accept --yes and store that.
class BuildCommand(cx_Freeze.command.build.Build): class BuildCommand(cx_Freeze.dist.build):
user_options = [ user_options = [
('yes', 'y', 'Answer "yes" to all questions.'), ('yes', 'y', 'Answer "yes" to all questions.'),
] ]
@@ -87,8 +87,8 @@ class BuildCommand(cx_Freeze.command.build.Build):
# Override cx_Freeze's build_exe command for pre and post build steps # Override cx_Freeze's build_exe command for pre and post build steps
class BuildExeCommand(cx_Freeze.command.build_exe.BuildEXE): class BuildExeCommand(cx_Freeze.dist.build_exe):
user_options = cx_Freeze.command.build_exe.BuildEXE.user_options + [ user_options = cx_Freeze.dist.build_exe.user_options + [
('yes', 'y', 'Answer "yes" to all questions.'), ('yes', 'y', 'Answer "yes" to all questions.'),
('extra-data=', None, 'Additional files to add.'), ('extra-data=', None, 'Additional files to add.'),
] ]

View File

@@ -66,7 +66,10 @@ class FF1World(World):
def goal_rule_and_shards(state): def goal_rule_and_shards(state):
return goal_rule(state) and state.has("Shard", self.player, 32) return goal_rule(state) and state.has("Shard", self.player, 32)
terminated_event.access_rule = goal_rule_and_shards terminated_event.access_rule = goal_rule_and_shards
if "MARK" in items.keys():
# Fail generation for Noverworld and provide link to old FFR website
raise Exception("FFR Noverworld seeds must be generated on an older version of FFR. Please ensure you generated the settings using "
"4-4-0.finalfantasyrandomizer.com")
menu_region.locations.append(terminated_event) menu_region.locations.append(terminated_event)
self.world.regions += [menu_region] self.world.regions += [menu_region]

View File

@@ -163,3 +163,17 @@ filler_items: typing.Tuple[str, ...] = (
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in get_full_item_list().items() if lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in get_full_item_list().items() if
data.code} data.code}
# Map type to expected int
type_flaggroups: typing.Dict[str, int] = {
"Unit": 0,
"Upgrade": 1,
"Armory 1": 2,
"Armory 2": 3,
"Building": 4,
"Mercenary": 5,
"Laboratory": 6,
"Protoss": 7,
"Minerals": 8,
"Vespene": 9,
"Supply": 10,
}

View File

@@ -139,7 +139,7 @@ item_table: Dict[int, ItemDict] = {
'name': 'Power Transmitter Fragment', 'name': 'Power Transmitter Fragment',
'tech_type': 'PowerTransmitterFragment'}, 'tech_type': 'PowerTransmitterFragment'},
35032: {'classification': ItemClassification.progression, 35032: {'classification': ItemClassification.progression,
'count': 4, 'count': 5,
'name': 'Prawn Suit Fragment', 'name': 'Prawn Suit Fragment',
'tech_type': 'ExosuitFragment'}, 'tech_type': 'ExosuitFragment'},
35033: {'classification': ItemClassification.useful, 35033: {'classification': ItemClassification.useful,
@@ -163,7 +163,7 @@ item_table: Dict[int, ItemDict] = {
'name': 'Scanner Room Fragment', 'name': 'Scanner Room Fragment',
'tech_type': 'BaseMapRoomFragment'}, 'tech_type': 'BaseMapRoomFragment'},
35038: {'classification': ItemClassification.progression, 35038: {'classification': ItemClassification.progression,
'count': 5, 'count': 4,
'name': 'Seamoth Fragment', 'name': 'Seamoth Fragment',
'tech_type': 'SeamothFragment'}, 'tech_type': 'SeamothFragment'},
35039: {'classification': ItemClassification.progression, 35039: {'classification': ItemClassification.progression,
@@ -203,9 +203,9 @@ item_table: Dict[int, ItemDict] = {
'name': 'Picture Frame', 'name': 'Picture Frame',
'tech_type': 'PictureFrameFragment'}, 'tech_type': 'PictureFrameFragment'},
35048: {'classification': ItemClassification.filler, 35048: {'classification': ItemClassification.filler,
'count': 2, 'count': 1,
'name': 'Bench Fragment', 'name': 'Bench',
'tech_type': 'BenchFragment'}, 'tech_type': 'Bench'},
35049: {'classification': ItemClassification.filler, 35049: {'classification': ItemClassification.filler,
'count': 1, 'count': 1,
'name': 'Basic Plant Pot', 'name': 'Basic Plant Pot',
@@ -333,7 +333,12 @@ item_table: Dict[int, ItemDict] = {
35080: {'classification': ItemClassification.filler, 35080: {'classification': ItemClassification.filler,
'count': 1, 'count': 1,
'name': 'Water Filtration Machine', 'name': 'Water Filtration Machine',
'tech_type': 'BaseFiltrationMachine'}} 'tech_type': 'BaseFiltrationMachine'},
35081: {'classification': ItemClassification.progression,
'count': 1,
'name': 'Ultra High Capacity Tank',
'tech_type': 'HighCapacityTank'},
}
advancement_item_names: Set[str] = set() advancement_item_names: Set[str] = set()
non_advancement_item_names: Set[str] = set() non_advancement_item_names: Set[str] = set()

View File

@@ -41,7 +41,7 @@ class SubnauticaWorld(World):
location_name_to_id = all_locations location_name_to_id = all_locations
option_definitions = Options.options option_definitions = Options.options
data_version = 6 data_version = 7
required_client_version = (0, 3, 5) required_client_version = (0, 3, 5)
prefill_items: List[Item] prefill_items: List[Item]