Move remote_items and _start_inventory from world to client (#227)
This commit is contained in:
parent
219bcb3521
commit
f0cfe30a36
|
@ -118,6 +118,7 @@ class CommonContext():
|
|||
game = None
|
||||
ui = None
|
||||
keep_alive_task = None
|
||||
items_handling: typing.Optional[int] = None
|
||||
|
||||
def __init__(self, server_address, password):
|
||||
# server state
|
||||
|
@ -247,9 +248,9 @@ class CommonContext():
|
|||
|
||||
async def send_connect(self, **kwargs):
|
||||
payload = {
|
||||
"cmd": 'Connect',
|
||||
'cmd': 'Connect',
|
||||
'password': self.password, 'name': self.auth, 'version': Utils.version_tuple,
|
||||
'tags': self.tags,
|
||||
'tags': self.tags, 'items_handling': self.items_handling,
|
||||
'uuid': Utils.get_unique_identifier(), 'game': self.game
|
||||
}
|
||||
if kwargs:
|
||||
|
@ -469,6 +470,8 @@ async def process_server_cmd(ctx: CommonContext, args: dict):
|
|||
logger.error('Invalid password')
|
||||
ctx.password = None
|
||||
await ctx.server_auth(True)
|
||||
elif 'InvalidItemsHandling' in errors:
|
||||
raise Exception('The item handling flags requested by the client are not supported')
|
||||
elif errors:
|
||||
raise Exception("Unknown connection errors: " + str(errors))
|
||||
else:
|
||||
|
@ -589,6 +592,7 @@ if __name__ == '__main__':
|
|||
class TextContext(CommonContext):
|
||||
tags = {"AP", "IgnoreGame", "TextOnly"}
|
||||
game = "Archipelago"
|
||||
items_handling = 0 # don't receive any NetworkItems
|
||||
|
||||
async def server_auth(self, password_requested: bool = False):
|
||||
if password_requested and not self.password:
|
||||
|
|
|
@ -30,6 +30,9 @@ class FF1CommandProcessor(ClientCommandProcessor):
|
|||
|
||||
|
||||
class FF1Context(CommonContext):
|
||||
command_processor = FF1CommandProcessor
|
||||
items_handling = 0b111 # full remote
|
||||
|
||||
def __init__(self, server_address, password):
|
||||
super().__init__(server_address, password)
|
||||
self.nes_streams: (StreamReader, StreamWriter) = None
|
||||
|
@ -40,8 +43,6 @@ class FF1Context(CommonContext):
|
|||
self.game = 'Final Fantasy'
|
||||
self.awaiting_rom = False
|
||||
|
||||
command_processor = FF1CommandProcessor
|
||||
|
||||
async def server_auth(self, password_requested: bool = False):
|
||||
if password_requested and not self.password:
|
||||
await super(FF1Context, self).server_auth(password_requested)
|
||||
|
|
|
@ -49,6 +49,7 @@ class FactorioCommandProcessor(ClientCommandProcessor):
|
|||
class FactorioContext(CommonContext):
|
||||
command_processor = FactorioCommandProcessor
|
||||
game = "Factorio"
|
||||
items_handling = 0b111 # full remote
|
||||
|
||||
# updated by spinup server
|
||||
mod_version: Utils.Version = Utils.Version(0, 0, 0)
|
||||
|
|
|
@ -56,11 +56,19 @@ class Client(Endpoint):
|
|||
self.messageprocessor = client_message_processor(ctx, self)
|
||||
self.ctx = weakref.ref(ctx)
|
||||
|
||||
def parse_tags(self, ctx: Context):
|
||||
self.remote_items = 'Tracker' in self.tags or self.slot in ctx.remote_items
|
||||
self.remote_start_inventory = 'Tracker' in self.tags or self.slot in ctx.remote_start_inventory
|
||||
self.no_items = 'TextOnly' in self.tags
|
||||
self.no_locations = 'TextOnly' in self.tags or 'Tracker' in self.tags
|
||||
@property
|
||||
def items_handling(self):
|
||||
if self.no_items:
|
||||
return 0
|
||||
return 1 + (self.remote_items << 1) + (self.remote_start_inventory << 2)
|
||||
|
||||
@items_handling.setter
|
||||
def items_handling(self, value: int):
|
||||
if not (value & 0b001) and (value & 0b110):
|
||||
raise ValueError("Invalid flag combination")
|
||||
self.no_items = not (value & 0b001)
|
||||
self.remote_items = bool(value & 0b010)
|
||||
self.remote_start_inventory = bool(value & 0b100)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
|
@ -1307,6 +1315,16 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
|||
minver = ctx.minimum_client_versions[slot]
|
||||
if minver > args['version']:
|
||||
errors.add('IncompatibleVersion')
|
||||
if args.get('items_handling', None) is None:
|
||||
# fall back to load from multidata
|
||||
client.no_items = False
|
||||
client.remote_items = slot in ctx.remote_items
|
||||
client.remote_start_inventory = slot in ctx.remote_start_inventory
|
||||
else:
|
||||
try:
|
||||
client.items_handling = args['items_handling']
|
||||
except (ValueError, TypeError):
|
||||
errors.add('InvalidItemsHandling')
|
||||
|
||||
# only exact version match allowed
|
||||
if ctx.compatibility == 0 and args['version'] != version_tuple:
|
||||
|
@ -1327,7 +1345,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
|||
ctx.clients[team][slot].append(client)
|
||||
client.version = args['version']
|
||||
client.tags = args['tags']
|
||||
client.parse_tags(ctx)
|
||||
client.no_locations = 'TextOnly' in client.tags or 'Tracker' in client.tags
|
||||
reply = [{
|
||||
"cmd": "Connected",
|
||||
"team": client.team, "slot": client.slot,
|
||||
|
@ -1338,7 +1356,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
|||
}]
|
||||
start_inventory = get_start_inventory(ctx, team, slot, client.remote_start_inventory)
|
||||
items = get_received_items(ctx, client.team, client.slot, client.remote_items)
|
||||
if start_inventory or items:
|
||||
if (start_inventory or items) and not client.no_items:
|
||||
reply.append({"cmd": 'ReceivedItems', "index": 0, "items": start_inventory + items})
|
||||
client.send_index = len(start_inventory) + len(items)
|
||||
if not client.auth: # if this was a Re-Connect, don't print to console
|
||||
|
@ -1368,21 +1386,28 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
|||
"original_cmd": cmd}])
|
||||
return
|
||||
|
||||
if "tags" in args:
|
||||
old_tags = client.tags
|
||||
client.tags = args["tags"]
|
||||
client.parse_tags(ctx)
|
||||
if "Tracker" in old_tags != "Tracker" in client.tags \
|
||||
or "TextOnly" in old_tags != "TextOnly" in client.tags:
|
||||
if args.get('items_handling', None) is not None and client.items_handling != args['items_handling']:
|
||||
try:
|
||||
client.items_handling = args['items_handling']
|
||||
start_inventory = get_start_inventory(ctx, client.team, client.slot, client.remote_start_inventory)
|
||||
items = get_received_items(ctx, client.team, client.slot, client.remote_items)
|
||||
if start_inventory or items:
|
||||
if (items or start_inventory) and not client.no_items:
|
||||
client.send_index = len(start_inventory) + len(items)
|
||||
await ctx.send_msgs(client, [{"cmd": "ReceivedItems", "index": 0,
|
||||
"items": start_inventory + items}])
|
||||
else:
|
||||
client.send_index = 0
|
||||
except (ValueError, TypeError) as err:
|
||||
await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', 'type': 'arguments',
|
||||
'text': f'Invalid items_handling: {err}',
|
||||
'original_cmd': cmd}])
|
||||
return
|
||||
|
||||
if "tags" in args:
|
||||
old_tags = client.tags
|
||||
client.tags = args["tags"]
|
||||
if set(old_tags) != set(client.tags):
|
||||
client.no_locations = 'TextOnly' in client.tags or 'Tracker' in client.tags
|
||||
ctx.notify_all(
|
||||
f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) has changed tags "
|
||||
f"from {old_tags} to {client.tags}.")
|
||||
|
@ -1390,7 +1415,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
|||
elif cmd == 'Sync':
|
||||
start_inventory = get_start_inventory(ctx, client.team, client.slot, client.remote_start_inventory)
|
||||
items = get_received_items(ctx, client.team, client.slot, client.remote_items)
|
||||
if start_inventory or items:
|
||||
if (start_inventory or items) and not client.no_items:
|
||||
client.send_index = len(start_inventory) + len(items)
|
||||
await ctx.send_msgs(client, [{"cmd": "ReceivedItems", "index": 0,
|
||||
"items": start_inventory + items}])
|
||||
|
|
|
@ -102,6 +102,7 @@ class LttPCommandProcessor(ClientCommandProcessor):
|
|||
class Context(CommonContext):
|
||||
command_processor = LttPCommandProcessor
|
||||
game = "A Link to the Past"
|
||||
items_handling = None # set in game_watcher
|
||||
|
||||
def __init__(self, snes_address, server_address, password):
|
||||
super(Context, self).__init__(server_address, password)
|
||||
|
@ -901,8 +902,10 @@ async def game_watcher(ctx: Context):
|
|||
continue
|
||||
elif game_name == b"SM":
|
||||
ctx.game = GAME_SM
|
||||
ctx.items_handling = 0b001 # full local
|
||||
else:
|
||||
ctx.game = GAME_ALTTP
|
||||
ctx.items_handling = 0b001 # full local
|
||||
|
||||
rom = await snes_read(ctx, SM_ROMNAME_START if ctx.game == GAME_SM else ROMNAME_START, ROMNAME_SIZE)
|
||||
if rom is None or rom == bytes([0] * ROMNAME_SIZE):
|
||||
|
|
|
@ -99,13 +99,14 @@ Sent to clients when the server refuses connection. This is sent during the init
|
|||
#### Arguments
|
||||
| Name | Type | Notes |
|
||||
| ---- | ---- | ----- |
|
||||
| errors | list\[str\] | Optional. When provided, should contain any one of: `InvalidSlot`, `InvalidGame`, `SlotAlreadyTaken`, `IncompatibleVersion`, or `InvalidPassword`. |
|
||||
| errors | list\[str\] | Optional. When provided, should contain any one of: `InvalidSlot`, `InvalidGame`, `SlotAlreadyTaken`, `IncompatibleVersion`, `InvalidPassword`, or `InvalidItemsHandling`. |
|
||||
|
||||
InvalidSlot indicates that the sent 'name' field did not match any auth entry on the server.
|
||||
InvalidGame indicates that a correctly named slot was found, but the game for it mismatched.
|
||||
SlotAlreadyTaken indicates a connection with a different uuid is already established.
|
||||
IncompatibleVersion indicates a version mismatch.
|
||||
InvalidPassword indicates the wrong, or no password when it was required, was sent.
|
||||
InvalidItemsHandling indicates a wrong value type or flag combination was sent.
|
||||
|
||||
### Connected
|
||||
Sent to clients when the connection handshake is successfully completed.
|
||||
|
@ -214,17 +215,28 @@ Sent by the client to initiate a connection to an Archipelago game session.
|
|||
| name | str | The player name for this client. |
|
||||
| uuid | str | Unique identifier for player client. |
|
||||
| version | [NetworkVersion](#NetworkVersion) | An object representing the Archipelago version this client supports. |
|
||||
| items_handling | int | Flags configuring which items should be sent by the server. Read below for individual flags.
|
||||
| tags | list\[str\] | Denotes special features or capabilities that the sender is capable of. [Tags](#Tags) |
|
||||
|
||||
#### items_handling flags
|
||||
| Value | Meaning |
|
||||
| ----- | ------- |
|
||||
| 0b000 | No ReceivedItems is sent to you, ever. |
|
||||
| 0b001 | Indicates you get items sent from other worlds. |
|
||||
| 0b010 | Indicates you get items sent from your own world. Requires 0b001 to be set. |
|
||||
| 0b100 | Indicates you get your starting inventory sent. Requires 0b001 to be set. |
|
||||
| null | Null or undefined loads settings from world definition for backwards compatibility. This is deprecated. |
|
||||
|
||||
#### Authentication
|
||||
Many, if not all, other packets require a successfully authenticated client. This is described in more detail in [Archipelago Connection Handshake](#Archipelago-Connection-Handshake).
|
||||
|
||||
### ConnectUpdate
|
||||
Update arguments from the Connect package, currently only updating tags is supported.
|
||||
Update arguments from the Connect package, currently only updating tags and items_handling is supported.
|
||||
|
||||
#### Arguments
|
||||
| Name | Type | Notes |
|
||||
| ---- | ---- | ----- |
|
||||
| items_handling | int | Flags configuring which items should be sent by the server.
|
||||
| tags | list\[str\] | Denotes special features or capabilities that the sender is capable of. [Tags](#Tags) |
|
||||
|
||||
### Sync
|
||||
|
|
Loading…
Reference in New Issue