Skip to content

Commit

Permalink
Alpha build
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesbrq committed Oct 6, 2023
1 parent f235319 commit 89dcc99
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 153 deletions.
243 changes: 143 additions & 100 deletions Client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from typing import TYPE_CHECKING, Optional, Dict, Set
import struct
from NetUtils import ClientStatus
from .Locations import roomCount, nonBlock, beanstones
from .Locations import roomCount, nonBlock, beanstones, roomException, shop, badge, pants
from .Items import items_by_id, ItemData
import sys
import logging
import math
Expand Down Expand Up @@ -36,110 +37,152 @@


class MLSSClient(BizHawkClient):
game = "Mario & Luigi Superstar Saga"
system = "GBA"
local_checked_locations: Set[int]
goal_flag: int
rom_slot_name: Optional[str]
player_name: Optional[str]

def __init__(self) -> None:
super().__init__()
self.local_checked_locations = set()
self.local_set_events = {}
self.local_found_key_items = {}
self.rom_slot_name = None

async def validate_rom(self, ctx: BizHawkClientContext) -> bool:
from CommonClient import logger

try:
# Check ROM name/patch version
rom_name_bytes = ((await bizhawk.read(ctx.bizhawk_ctx, [(0xA0, 14, "ROM")]))[0])
rom_name = bytes([byte for byte in rom_name_bytes if byte != 0]).decode("UTF-8")
if not rom_name.startswith("MARIO&LUIGIU"):
return False
if rom_name == "MARIO&LUIGIUA8":
logger.info("ERROR: You appear to be running an unpatched version of Mario & Luigi Superstar Saga. "
"You need to generate a patch file and use it to create a patched ROM.")
return False
if rom_name != "MARIO&LUIGIUAP":
logger.info(rom_name)
logger.info("ERROR: The patch file used to create this ROM is not compatible with "
"this client. Double check your client version against the version being "
"used by the generator.")
return False
except UnicodeDecodeError:
game = "Mario & Luigi Superstar Saga"
system = "GBA"
local_checked_locations: Set[int]
goal_flag: int
rom_slot_name: Optional[str]
player_name: Optional[str]

def __init__(self) -> None:
super().__init__()
self.local_checked_locations = set()
self.local_set_events = {}
self.local_found_key_items = {}
self.rom_slot_name = None

async def validate_rom(self, ctx: BizHawkClientContext) -> bool:
from CommonClient import logger

try:
# Check ROM name/patch version
rom_name_bytes = ((await bizhawk.read(ctx.bizhawk_ctx, [(0xA0, 14, "ROM")]))[0])
rom_name = bytes([byte for byte in rom_name_bytes if byte != 0]).decode("UTF-8")
if not rom_name.startswith("MARIO&LUIGIU"):
return False
except bizhawk.RequestFailedError:
return False # Should verify on the next pass

ctx.game = self.game
ctx.items_handling = 0b001
ctx.want_slot_data = True
ctx.watcher_timeout = 0.125
self.rom_slot_name = rom_name
name_bytes = ((await bizhawk.read(ctx.bizhawk_ctx, [(0xB0, 16, "ROM")]))[0])
name = bytes([byte for byte in name_bytes if byte != 0]).decode("UTF-8")
self.player_name = name

return True

async def set_auth(self, ctx: BizHawkClientContext) -> None:
ctx.auth = self.player_name

async def game_watcher(self, ctx: BizHawkClientContext) -> None:
from CommonClient import logger
try:
read_state = await bizhawk.read(ctx.bizhawk_ctx, [(0x4564, 59, "EWRAM")])
flags = read_state[0]


# TODO Confirm a save has been made

locs_to_send = set()

# Check for set location flags.
for byte_i, byte in enumerate(bytearray(flags)):
for i in range(8):
and_value = 1 << i
if byte & and_value != 0:
flag_id = byte_i * 8 + (i + 1)
room, item = find_key(roomCount, flag_id)
pointer_arr = await bizhawk.read(ctx.bizhawk_ctx, [(ROOM_ARRAY_POINTER + ((room - 1) * 4), 4, "ROM")])
pointer = (struct.unpack('<I', pointer_arr[0])[0])
pointer = pointer & 0xFFFFFF
offset = await bizhawk.read(ctx.bizhawk_ctx, [(pointer, 1, "ROM")])
offset = offset[0][0]
if offset != 0:
offset = 2
pointer += (item * 8) + 1 + offset
if pointer in beanstones:
pointer = beanstones[pointer]
if pointer in ctx.server_locations:
locs_to_send.add(pointer)

for item in nonBlock:
address, mask, location = item
flag_byte = await bizhawk.read(ctx.bizhawk_ctx, [(address, 1, "ROM")])
flag_byte = flag_byte[0][0]
if flag_byte & mask != 0:
if rom_name == "MARIO&LUIGIUA8":
logger.info("ERROR: You appear to be running an unpatched version of Mario & Luigi Superstar Saga. "
"You need to generate a patch file and use it to create a patched ROM.")
return False
if rom_name != "MARIO&LUIGIUAP":
logger.info(rom_name)
logger.info("ERROR: The patch file used to create this ROM is not compatible with "
"this client. Double check your client version against the version being "
"used by the generator.")
return False
except UnicodeDecodeError:
return False
except bizhawk.RequestFailedError:
return False # Should verify on the next pass

ctx.game = self.game
ctx.items_handling = 0b001
ctx.want_slot_data = True
ctx.watcher_timeout = 0.125
self.rom_slot_name = rom_name
name_bytes = ((await bizhawk.read(ctx.bizhawk_ctx, [(0xB0, 16, "ROM")]))[0])
name = bytes([byte for byte in name_bytes if byte != 0]).decode("UTF-8")
self.player_name = name

return True

async def set_auth(self, ctx: BizHawkClientContext) -> None:
ctx.auth = self.player_name

async def game_watcher(self, ctx: BizHawkClientContext) -> None:
from CommonClient import logger
try:
read_state = await bizhawk.read(ctx.bizhawk_ctx, [(0x4564, 59, "EWRAM"),
(0x2330, 2, "IWRAM"), (0x3FE0, 1, "IWRAM"), (0x3FE4, 1, "IWRAM"), (0x304B, 1, "EWRAM"), (0x304C, 4, "EWRAM"), (0x3058, 6, "EWRAM")])
flags = read_state[0]
room = (read_state[1][1] << 8) + read_state[1][0]
shop_init = read_state[2][0]
shop_scroll = read_state[3][0]
is_buy = (read_state[4][0] != 0)
shop_address = (struct.unpack('<I', read_state[5])[0]) & 0xFFFFFF
logo = bytes([byte for byte in read_state[6] if byte != 0]).decode("UTF-8")

if logo != "MLSSAP":
return
# TODO Confirm a save has been made

locs_to_send = set()
location = 0

for item in ctx.items_received:
item_data = items_by_id[item.item]
b = await bizhawk.read(ctx.bizhawk_ctx, [(0x3057, 1, "EWRAM")])
if b[0][0] == 0:
await bizhawk.write(ctx.bizhawk_ctx, [(0x3057, [item_data.itemID], "EWRAM")])
else:
break


if is_buy:
is_buy = False
if shop_address != 0x3c0618 and shop_address != 0x3c0684:
location = shop[shop_address][shop_scroll]
else:
if shop_init & 0x1 != 0:
location = badge[shop_address][shop_scroll & 0x1F]
else:
location = pants[shop_address][shop_scroll & 0x1F]
await bizhawk.write(ctx.bizhawk_ctx, [(0x304B, [0x0], "EWRAM")])

if location in ctx.server_locations:
locs_to_send.add(location)

# Check for set location flags.
for byte_i, byte in enumerate(bytearray(flags)):
for i in range(8):
and_value = 1 << i
if byte & and_value != 0:
flag_id = byte_i * 8 + (i + 1)
room, item = find_key(roomCount, flag_id)
pointer_arr = await bizhawk.read(ctx.bizhawk_ctx,
[(ROOM_ARRAY_POINTER + ((room - 1) * 4), 4, "ROM")])
pointer = (struct.unpack('<I', pointer_arr[0])[0])
pointer = pointer & 0xFFFFFF
offset = await bizhawk.read(ctx.bizhawk_ctx, [(pointer, 1, "ROM")])
offset = offset[0][0]
if offset != 0:
offset = 2
pointer += (item * 8) + 1 + offset
for key, value in beanstones.items():
if pointer == value:
pointer = key
break
if pointer in ctx.server_locations:
locs_to_send.add(pointer)

for item in nonBlock:
address, mask, location = item
flag_bytes = await bizhawk.read(ctx.bizhawk_ctx, [(address, 1, "EWRAM")])
flag_byte = flag_bytes[0][0]
if flag_byte & mask != 0:
if location in roomException:
if roomException[location] == room:
exception = True
else:
exception = False
else:
exception = True
if location in ctx.server_locations and exception:
locs_to_send.add(location)

# Send locations if there are any to send.
if locs_to_send != self.local_checked_locations:
self.local_checked_locations = locs_to_send

# Send locations if there are any to send.
if locs_to_send != self.local_checked_locations:
self.local_checked_locations = locs_to_send

if locs_to_send is not None:
await ctx.send_msgs([{
"cmd": "LocationChecks",
"locations": list(locs_to_send)
}])
if locs_to_send is not None:
await ctx.send_msgs([{
"cmd": "LocationChecks",
"locations": list(locs_to_send)
}])

except bizhawk.RequestFailedError:
# Exit handler and return to main loop to reconnect.
pass
except bizhawk.RequestFailedError:
# Exit handler and return to main loop to reconnect.
pass


def find_key(dictionary, target):
Expand Down
14 changes: 7 additions & 7 deletions Items.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ class MLSSItem(Item):
ItemData(0x2A, "Fake Beanstar", ItemClassification.progression, 0x43),
ItemData(0x2B, "Red Pearl Bean", ItemClassification.progression, 0x45),
ItemData(0x2C, "Green Pearl Bean", ItemClassification.progression, 0x46),
ItemData(0x2D, "Bean Fruit 1", ItemClassification.progression, 0x47),
ItemData(0x2E, "Bean Fruit 2", ItemClassification.progression, 0x50),
ItemData(0x2F, "Bean Fruit 3", ItemClassification.progression, 0x51),
ItemData(0x30, "Bean Fruit 4", ItemClassification.progression, 0x52),
ItemData(0x31, "Bean Fruit 5", ItemClassification.progression, 0x53),
ItemData(0x32, "Bean Fruit 6", ItemClassification.progression, 0x54),
ItemData(0x33, "Bean Fruit 7", ItemClassification.progression, 0x55),
ItemData(0x2D, "Bean Fruit 1", ItemClassification.progression_skip_balancing, 0x47),
ItemData(0x2E, "Bean Fruit 2", ItemClassification.progression_skip_balancing, 0x50),
ItemData(0x2F, "Bean Fruit 3", ItemClassification.progression_skip_balancing, 0x51),
ItemData(0x30, "Bean Fruit 4", ItemClassification.progression_skip_balancing, 0x52),
ItemData(0x31, "Bean Fruit 5", ItemClassification.progression_skip_balancing, 0x53),
ItemData(0x32, "Bean Fruit 6", ItemClassification.progression_skip_balancing, 0x54),
ItemData(0x33, "Bean Fruit 7", ItemClassification.progression_skip_balancing, 0x55),
ItemData(0x34, "Blue Neon Egg", ItemClassification.progression, 0x56),
ItemData(0x35, "Red Neon Egg", ItemClassification.progression, 0x57),
ItemData(0x36, "Green Neon Egg", ItemClassification.progression, 0x60),
Expand Down
Loading

0 comments on commit 89dcc99

Please sign in to comment.