-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preparations for RPG_RT - Player regression testing #2297
Comments
Further idea via IRC: The savegame could be a concatenated file. Maybe add a |
I looked into the states thing, unfortunately it's a bit tricky. RPG_RT actually resizes the states array the first time an actor is added to your party, regardless of whether they have states or not. Ultimately, I think this and other related things depends on EasyRPG/liblcf#371 That is we should get rid of these setup and fixup functions. Liblcf should just read and present whatever in raw form, and then we do any fixing and adjusting in player. It's on my todo list. But want to get the string/array optimization stuff in first to avoid rebase problems. |
Ultimately my objective for this one is part of #1954 That is if we create a new The problem of course is this has a dependency on #2208, but we want to have this now so we can test #2208 ! Therefore as a temporary solution I can just rip the saving functionality out of
We're going to need some kind of "black list" config file which contains the set of chunks to ignore. This would either be used to omit chunks from the saves (since they not intended to be loaded, this is fine), or later in the pipeline as part of the diffing tool. I would do it wherever it is easiest and most convenient as a first step.
For a long replay this could be slow. Maybe we can binary diff the save files first, and only if they don't match, generate xml and diff again to show it in a human readable fashion? Anyway, I would put such optimization concerns off until after the first version. |
Okay but please also provide a Filesystem_Inputstream version for handling the "append mode".
Diffing tool is easier for now. Performance doesn't matter yet for the prototype.
Yeah for match the hash is fine but we are not there yet. |
|
The DynRPG plugin is finished https://github.com/EasyRPG/regression-tests/tree/master/tools/regtest-plugin Usage:
|
Here a python script for extracting all saves from a concatenated save. Unfortunately there is no size field after the header so I have to process all top chunks (advantage: Better detection of corrupted files) This was the difficult part of the script imo. Missing is only: Read two of these files, then hash compare LSD by LSD and when they mismatch extract them, convert them with lcf2xml and output a diff. import sys
chunks = list(range(0x64, 0x72+1))
save_data = []
cur_save_data = bytearray(b"")
def read(stream, count):
b = stream.read(count)
cur_save_data.extend(b)
return b
def read_ber(stream):
value = 0;
temp = 0x80;
while temp & 0x80:
value <<= 7
temp = int.from_bytes(read(stream, 1), byteorder='little')
value |= (temp & 0x7F)
return value
def read_save(stream):
saves = len(save_data) + 1
if read(f, 11).decode("ascii") != "LcfSaveData":
raise ValueError(f"Save {saves} corrupted: Expected LcfSaveData")
while (True):
chunk = read_ber(f)
if chunk not in chunks:
if chunk == 0xB:
# Start of new save
del cur_save_data[-1]
return True
elif chunk == 200:
# Likely not what you want for a compare
print(f"Warning: Save {saves} contains EasyRPG chunk")
elif chunk == 0:
if not stream.read(1):
# EOF
return False
raise ValueError(f"Save {saves} corrupted: Bad chunk {chunk}")
chunk_size = read_ber(f)
read(f, chunk_size)
with open(sys.argv[1], "rb") as f:
if read(f, 1) != b"\x0b":
raise ValueError(f"Not a save file")
while (read_save(f)):
save_data.append(cur_save_data)
cur_save_data = bytearray(b"\x0b")
save_data.append(cur_save_data)
print(f"Found {len(save_data)} savegames")
# Example: Extract them
for i, save in enumerate(save_data):
with open(f"Save{i+1}.lsd", "wb") as f:
f.write(save) |
A problem with the savegame hashing: The hash varies but lcf2xml says "they are identical" (so I can still work with them, but is suspicious and not suitable for mass-testing -> slow). Here are the two files: Worth investigating: Another logic difference but not really testable because RNG related: |
There appears to be an input lag of one frame or my hook is bad. RPG_RT:
So when hooking GetAsyncKeyState the frame counter is one behind. Maybe inc the frame counter to increase accuracy? |
This was observed in #2208 ? If so it's an incompatibility that should be fixed. Looking at RPG_RT, it will always do the encounter rate calculations based on the map encounter rate, even if there are no monsters. encounter steps logic is only skipped if the encounter rate is 0. Then when It looks like RPG_RT will only return and skip the rest of the player input logic if a battle started, and continue it if the battle is not started, which is different than what I do in #2208 I need to do some tests later on to verify this behavior. |
yes I usually do these tests with #2208 I had a map with rate 25 and no enemies. The flag was true the entire (?) time. |
More problems: Message boxes: @fmatthew5876 Doesn't appear to be used for branching by RPG_RT. What is even the purpose of it. o_O |
Are you absolutely sure about this? Every single frame? I just tested RPG_RT with a debugger, while indeed I had a small deviation in #2208 which is now fixed, I am not at all seeing This was a 20x15 map, no enemies, 25 encounter rate. Even with encounter rate of 1, I still only trigger a battle once per movement, not every frame. The RPG_RT code does The
The function is at 004ABE80 The is basically:
I'm not sure if this function is unique to According to this, pascal ReallocMem resizes the buffer but does not initialize the new data if it grows. https://www.freepascal.org/docs-html/rtl/system/reallocmem.html
I looked at this a bit here: #1849 I haven't been able to figure this one out. As far as I can tell it's some half implemented feature that gets written to in some cases but has no real effect on anything. But I could be wrong |
Have to recheck this with my better hook. Maybe it was just true during the on frame callback and reset later... |
Even with that, Unless something else is playing with these flags somehow. Btw, I checked the subcommand path stuff, I'm pretty certain that function is only used by the interpreter, so it should be possible to hook it and initialize the enlarged portion of the array with 0xFF. |
Another comment here that actually belongs in the PR but the PR history is hard to follow because Github collapses everything due to too many commits... I didn't notice that there are multiple IncFrame calls. In Player I put it after I don't think that I catch |
With my last commit, you should be fine to follow the 2 frame increment hook points. However it might be the case that input capture is done at different times. I think we do it before in Player and RPG_RT does it inside the scene update calls?
|
ResizeSubcommandPath was simple enough, so I reimplemented it instead of using a trampoline:
|
Open problem (maybe is Wine specific, needs testing): Sometimes inputs are lost, making the replay desync. |
For the ultimate testing environment this needs a DLL that is injected into any RPG_RT.exe. But lets focus on DynRPG for now because it is convenient. Required hook points. I wonder if they look similar in all RPG_RT. This would allow "byte scanning" for autopatching of any Runtime:
The following addresses are required (with "onNewGame" 10 hooks/addresses are needed):
|
If all we need are a set of addresses, it should not be hard to create a single file database of binary hash -> hook addresses. Then your test dll could be made to work generally with any supported RPG_RT binary version. |
With PRs such as #2208 where normal code reviews fail we finally need an automatic regression tester.
@fmatthew5876 I could need your help here. I ask you to provide two things. It would help me when you can contribute them (best would be as a seperate PR, so I can use it in both master and your character PR)
My idea is the following:
TODO
Known issues
[1] My suggestion: 6531fd8 The important lines are the "F" lines, the format is
game_system_frame,keys....
. A frame can occur multiple times because Game_System frames are reset in some cases, but this is no problem.[2]
Notes about savegame incompatibility
title.timestamp
- this varies (obviously)system.scene
is 0 on the mapeasyrpg.version
- only we write thisStrange observations
system.save_count
when using dynrpg save this is always 0system.save_slot
when using dynrpg save this is always 1Incompatible save storage
<saveactor><status>0 0 0 0 0 0 0 0 0 0</status>
, but RPG_RT stores<status></status>
@fmatthew5876 can you fix this? I guess RPG_RT only writes numbers until it reaches all zero.Lower priority
The text was updated successfully, but these errors were encountered: