Skip to content

Commit

Permalink
Merge pull request #6 from ferraro1/doubles
Browse files Browse the repository at this point in the history
Doubles Update
  • Loading branch information
ferraro1 authored Mar 13, 2019
2 parents e2f42f4 + a9022df commit 3dcebcd
Show file tree
Hide file tree
Showing 18 changed files with 11,398 additions and 1,167 deletions.
4 changes: 2 additions & 2 deletions crashchecker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'''

import gevent
from pbrEngine.states import PbrStates
from pbrEngine.states import EngineStates
from pbrEngine.util import EventHook
from pbrEngine import PBREngine

Expand All @@ -28,7 +28,7 @@ def loop(self):
while self.fails <= 3:
now = self.pbr.timer.frame
if self._lastFrame == now and self.pbr.state not in\
(PbrStates.WAITING_FOR_NEW, PbrStates.WAITING_FOR_START):
(EngineStates.WAITING_FOR_NEW, EngineStates.WAITING_FOR_START):
self.fails += 1
else:
self._lastFrame = now
Expand Down
4,495 changes: 4,495 additions & 0 deletions generateAvatarOptions.py

Large diffs are not rendered by default.

22 changes: 10 additions & 12 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@
import monitor

from pbrEngine import PBREngine
from pbrEngine.states import PbrStates
from pbrEngine.states import EngineStates
from pbrEngine import Colosseums
from pbrEngine import AvatarsBlue, AvatarsRed

with open("testpkmn.yaml", encoding="utf-8") as f:
yaml_data = yaml.load_all(f)
data = [pokecat.instantiate_pokeset(pokecat.populate_pokeset(single_set)) for single_set in yaml_data]
for d in data:
d["ingamename_cmdsafe"] = d["ingamename"].encode("ascii", "replace").decode()
# reduce by shinies
#data = [d for d in data if not d["shiny"]]
# TODO remove this again, it's debugging stuff
Expand Down Expand Up @@ -59,7 +57,7 @@ def countdown(t=20):
t -= 1
if t <= 0:
t = 0
pbr.start()
pbr.matchStart()
break


Expand All @@ -70,9 +68,9 @@ def new():
pkmn = random.sample(data, 6)
colosseum = random.choice(list(Colosseums))

pbr.new(colosseum, pkmn[:3], pkmn[3:6],
random.choice(list(AvatarsBlue)),
random.choice(list(AvatarsRed)))
pbr.matchPrepare(colosseum, pkmn[:3], pkmn[3:6],
random.choice(list(AvatarsBlue)),
random.choice(list(AvatarsRed)))
# pbr.new(colosseum, [data[398]], [data[9], data[10], data[12]])
# pbr.new(random.randint(0,9),
# random.sample([data[201], data[49], data[359]],
Expand All @@ -84,13 +82,13 @@ def new():


def onState(state):
if state == PbrStates.WAITING_FOR_NEW:
if state == EngineStates.WAITING_FOR_NEW:
new()


def onAttack(side, monindex, moveindex, movename, obj):
mon = (pbr.match.pkmn_blue if side == "blue" else pbr.match.pkmn_red)[monindex]
display.addEvent("%s (%s) uses %s." % (mon["ingamename_cmdsafe"], side, movename))
display.addEvent("%s (%s) uses %s." % (mon["ingamename"], side, movename))


def onWin(winner):
Expand All @@ -102,12 +100,12 @@ def onWin(winner):

def onDeath(side, monindex):
mon = (pbr.match.pkmn_blue if side == "blue" else pbr.match.pkmn_red)[monindex]
display.addEvent("%s (%s) is down." % (mon["ingamename_cmdsafe"], side))
display.addEvent("%s (%s) is down." % (mon["ingamename"], side))


def onSwitch(side, monindex, obj):
mon = (pbr.match.pkmn_blue if side == "blue" else pbr.match.pkmn_red)[monindex]
display.addEvent("%s (%s) is sent out." % (mon["ingamename_cmdsafe"], side))
display.addEvent("%s (%s) is sent out." % (mon["ingamename"], side))


def actionCallback(side, fails, moves, switch, cause):
Expand Down Expand Up @@ -159,7 +157,7 @@ def main():
pbr.on_state += onState
pbr.on_win += onWin
pbr.on_attack += onAttack
pbr.on_death += onDeath
pbr.on_faint += onDeath
pbr.on_switch += onSwitch
pbr.connect()
pbr.on_gui += lambda gui: display.reprint()
Expand Down
8 changes: 4 additions & 4 deletions monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'''

import os
from pbrEngine.states import PbrStates, PbrGuis
from pbrEngine.states import EngineStates, PbrGuis
from pbrEngine import Colosseums
import gevent

Expand Down Expand Up @@ -38,7 +38,7 @@ def reprint(self):
print(" +-------------------------------------------------+")
if self.pbr.colosseum:
print(" | Colosseum: %36s |" % Colosseums(self.pbr.colosseum).name)
print(" | State: %36s |" % PbrStates(self.pbr.state).name)
print(" | State: %36s |" % EngineStates(self.pbr.state).name)
print(" | Gui: %36s |" % PbrGuis(self.pbr.gui).name)
print(" +------------------------+------------------------+")
lenBlue = len(self.pbr.match.pkmn_blue)
Expand All @@ -50,8 +50,8 @@ def reprint(self):
("X" if not self.pbr.match.alive_blue[i]
else (">" if i == self.pbr.match.current_blue
else " ")) if blue else " ",
blue["ingamename_cmdsafe"] if blue else " ",
red["ingamename_cmdsafe"] if red else " ",
blue["ingamename"] if blue else " ",
red["ingamename"] if red else " ",
("X" if not self.pbr.match.alive_red[i]
else ("<" if i == self.pbr.match.current_red
else " ")) if red else " ",
Expand Down
3 changes: 1 addition & 2 deletions pbrEngine/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

from .engine import PBREngine, ActionCause
from .memorymap.values import Colosseums, FieldEffects
from .avatars import AvatarsBlue, AvatarsRed
from .states import EngineStates
144 changes: 144 additions & 0 deletions pbrEngine/abstractions/dolphinIO.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import logging
import gevent
from gevent.event import AsyncResult

from ..memorymap.addresses import LocPath, isValidLoc

logger = logging.getLogger("pbrEngine")


class DolphinIO:
def __init__(self, dolphin, crash):
self._dolphin = dolphin
self._crash = crash

def read8(self, addr, **kwargs):
return self.read(8, addr, **kwargs)

def read16(self, addr, **kwargs):
return self.read(16, addr, **kwargs)

def read32(self, addr, **kwargs):
return self.read(32, addr, **kwargs)

def read(self, mode, addr, **kwargs):
return self.readMulti([(mode, addr)], **kwargs)[0]

def readMulti(self, readTuplesList, numAttempts=5):
''' Return a list of memory values corresponding to a list of (mode, addr) tuples
Memory is batch read <num_reads> times, with a 10ms wait between batches. Exists
because the 0x9xxxxxxx addresses occasionally read as 0 instead of the correct
value. This occurs approximately once every 1,000 reads. With a second read after
10ms, this reduces to once every 250,000 reads.
'''
results = [0] * len(readTuplesList)
if numAttempts <= 0:
raise ValueError("numAttempts must be > 0")
for i in range(numAttempts):
temp_results = []
for mode, addr in readTuplesList:
if mode not in (8, 16, 32):
raise ValueError("Mode must be 8, 16, or 32, got {}".format(mode))
ar = AsyncResult()
temp_results.append(ar)
self._dolphin.read(mode, addr, ar.set)
for i, ar in enumerate(temp_results):
val = ar.get()
if val != 0:
results[i] = val
if i < numAttempts - 1:
gevent.sleep(0.01) # Sleep a bit between read batches
return results

def write8(self, addr, val, **kwargs):
self.write(8, addr, val, **kwargs)

def write16(self, addr, val, **kwargs):
self.write(16, addr, val, **kwargs)

def write32(self, addr, val, **kwargs):
self.write(32, addr, val, **kwargs)

def write(self, mode, addr, val, **kwargs):
self.writeMulti([(mode, addr, val)], **kwargs)

def writeMulti(self, writeTuplesList, maxAttempts=5, writesPerAttempt=5,
readsPerAttempt=2):
'''Write multiple values to memory, given a list of (mode, addr, val) tuples
On each attempt, memory is batch written <writesPerAttempt> times, with a 10ms
wait between batches. Exists because the 0x9xxxxxxx addresses occasionally fail
to write the correct value. This occurs approximately once every 1,000 writes.
If <readsPerAttempt> is nonzero, each attempt is verified by reading back the
memory. If a discrepancy exists, up to <maxAttempts> will be performed. Note
that discrepancies can arise due to faulty reads as well as faulty writes.
'''
writes_needed = writeTuplesList
assert maxAttempts > 0, "maxAttempts must be > 0"
assert writesPerAttempt > 0, "writesPerAttempt must be > 0"
assert readsPerAttempt >= 0, "readsPerAttempt must be >= 0"
assert maxAttempts == 1 if readsPerAttempt == 0 else True, "maxAttempts must be 1 when not verifying reads (readsPerAttempt == 0)"
for i in range(maxAttempts):
# Write all values for which writing is needed
for write_i in range(writesPerAttempt):
for mode, addr, val in writes_needed:
if mode not in (8, 16, 32):
raise ValueError("Mode must be 8, 16, or 32, got {}".format(mode))
self._dolphin.write(mode, addr, val)
if write_i < writesPerAttempt - 1:
gevent.sleep(0.01) # Sleep a bit between write batches
# Perform verification on values written
if readsPerAttempt > 0:
# Read all values that were written
read_values = self.readMulti([(m, a) for m, a, v in writes_needed],
numAttempts=readsPerAttempt)
# Filter out writes for which the read value matched the written value
writes_needed = [(m, a, v) for i, (m, a, v) in enumerate(writes_needed)
if read_values[i] != v]
if writes_needed:
logger.error("%d/%d writes failed. %d/%d attempts remaining",
len(writes_needed), len(writeTuplesList),
maxAttempts - i - 1, maxAttempts)
logger.debug("All writes: %s\nThis iteration's reads: %s\n"
"Writes that failed: %s", writeTuplesList, read_values,
writes_needed)
else:
# self._crash_callback("Memory write failure")
return # All values to write were successfully verified
if i < maxAttempts - 1:
gevent.sleep(0.01 * (i + 1)) # Sleep a bit between attempts
if readsPerAttempt > 0 and writes_needed:
self._crash("Memory write failure")

def readNestedAddr(self, nestedLocation, maxAttempts=5, readsPerAttempt=3):
'''Get final address of a nested pointer
Performs up to <max_attempts> of <reads_per_attempt> to reduce
chance of faulty reads.
Returns the address, or None if final address is not a valid memory location.
'''
nestedLoc = nestedLocation.value
loc = nestedLoc.startingAddr
path = LocPath()
path.append(loc)
for offset in nestedLoc.offsets:
val = 0
for i in range(maxAttempts):
val = self.read32(loc, numAttempts=readsPerAttempt)
if isValidLoc(val):
break
faultyPath = LocPath(path)
faultyPath.append(val + offset)
logger.error("Location detection for {} failed attempt {}/{}. Path: {}"
.format(nestedLocation.name, i, maxAttempts, faultyPath))
gevent.sleep(0.1 * (i + 1)) # Sleep a bit, this helps a lot with bad reads
loc = val + offset
path.append(loc)
if not isValidLoc(loc):
logger.error("Invalid pointer location for {}. Path: {}"
.format(nestedLocation.name, path))
self._crash(reason="Failed to read pointer")
return loc
Loading

0 comments on commit 3dcebcd

Please sign in to comment.