Forums  /  Broforce  /  Auto Splitter
  lachszungelachszunge

Hey guys,
my friend Gammler33 and I have been working on an Autosplitter for LiveSplit. So far the Autosplitter can only Split after every level in Arcade mode except the final level.
Works for single and multiplayer.

Note: the Pointer used to track the "level" variable is not yet optimised. This could lead to the Autosplitter not working on your system.
There also is a lot of unused code in there (all the lines starting with //). These lines will hopefully be used someday but are not relevant to the script as of now.
If you want the Auto Splitter to also work for Campaign, you can just ask me, it is not hard to make it work there also.

How to install:
1. create a .txt file and paste the code from below into it
2. rename the .txt file to name.asl
3. open LiveSplit and go to Edit Layout
4. Add -> Contol -> Scriptable Auto Splitter
5. set the Script path and check the "Split" checkbox

Code:

state("Broforce_beta")
{
//bool isLoading : "system.xml"
byte level : "mono.dll", 0x1F6740, 0xA4, 0x7C, 0xC, 0xC8, 0x18;
//bool satanExplosion
//float countdown
}
init
{
vars.split = 0;
//vars.overworldSection = 1;
//refreshRate = 30;
}

//start
//{
// if (current.level == 0 && current.countdown == 0)
// {
// return true;
// }
//}
split
{
if (current.level == (vars.split + 1))
{
vars.split++;
if(vars.split >= 63)
{
vars.split = 0;
}
return true;
//level 1 - 63
}
if (current.level != vars.split)
{
vars.split = 0;
}
//if (current.level == 63 && current.satanExplosion == 1)
//{
// return true;
// //satan kill
//}
}

 
  argusdustyargusdusty

Hi lachszunge! This is excellent work and a very useful tool! Unfortunately I wasn't able to get the script working for me. Might vary from system to system. I put in a print statement to output current.level, and it appeared to be stuck on a single number, apparently random every time I started the program (124, 4, 68).

I'd love to work on this, but I'm afraid I don't have much experience with dll files. What was your process for tracking down the location of the level byte?

 
  lachszungelachszunge

Hi argusdusty,
We used Cheat Engine to track down the variable we wanted, in this case "level".
This gives us the current location of this variable in the RAM. The problem is that this location changes every time you restart the game, that is why we need to find the base address and the Pointers pointing towards the Variable.

Cheat Engine has a feature called Pointer Scans which allows you to search for all base addresses and the pointers which point towards the temporary address with your variable.
The only Problem with that is that it will not only give you the correct address, but also all other addresses which Point towards your variable by chance. You can then rescan your list of Pointers in order to filter those "random hits"
After our first scan we ended up with about 1.5 billion results, after about 20 more scans we narrowed it down to about 27k possible base addresses. Since any more scans on our system wouldn't reduce the ammount of addresses signifficantly anymore, we simply picked one at random.

I uploaded the Pointer Scan file if you want to take a look at it yourself and maybe try to filter out some more addresses.
Note that these Pointer Scans must be opened with Cheat Engine 6.4 (or possibly older, haven't checked that) or else you will receive an error.

If you or anyone else wants a tutorial on how to scan for pointers and how to re scan your pointer scans, simply ask and I will try to put together a step by step tutorial. I have gotten most of my information on autosplitters from this blogpost:
https://github.com/LiveSplit/LiveSplit/blob/master/Documentation/Auto-Splitters.md
and I learned how to use Cheat Engine from youtube.

You can Download the already filtered Pointer Scan here:
https://www.mediafire.com/file/siahj1f9t88ydhd/Pointer_Scan.rar

 
  argusdustyargusdusty

Hi lachszunge. Thanks for that guide. I used Cheat Engine 6.6, unfortunately, so I wasn't able to compare with your pointer scan, but following your method and repeating across restarts of the program, I narrowed the list to 56 pointer paths that appear to be consistent.

I picked one of the 56 paths at random - `"mono.dll", 0x1F64D4, 0x30C, 0x10, 0x31C, 0x568;` works on my system. Hopefully it also works for anyone else trying to use this. It splits the instant the "Area Liberated" screen begins to appear, which is earlier than I usually split, but it does the job.

 
  Gammler33Gammler33

Hey argusdusty, nice to hear that it now works for you too! It would be really cool, if you could somehow send us the new pointer scan files so that we can try to further improve them. Maybe we will be able to make it work for everybody someday.

 
  argusdustyargusdusty

Hi Gammler33,

That path I posted above appears to stop working for me when I play around in the level editor, so I restarted my search with expanded parameters and messing about with the level editor, and have gotten it down to 634 paths. I've uploaded the PTR files here: http://www.mediafire.com/file/va1tsc79xmil98c/Pointer_Scan.zip

Those are from CheatEngine version 6.6, however.

I chose the new path to be closest to the one you originally had: `"mono.dll", 0x1F6740, 0xA4, 0x7C, 0xC, 0xC0, 0x18;`, changing 0xC8 to 0xC0. Hopefully that remains consistent for me.

 
  argusdustyargusdusty

Hi all,

I extended lachszunge and Gammler33's auto splitter to be more fully featured. It will auto-start a run at the start, it will split at the end of the run, it will end the run when you return to the main menu, and it supports campaign mode (which you can enable by flipping some switches in a settings menu). You install it the same way as lachszunge explained in the first comment on this thread.

You can get the script here: https://gist.github.com/argusdusty/7fea0526d09c74184f71c4d6c0911480

Hopefully it works for everyone. I was hoping to narrow down the variable paths a bit before posting but it's been working consistently for me for awhile now.

lachszungelachszunge likes this. 
  lachszungelachszunge

Hey argusdusty,

I just tested your script and after changing the pointer for "level" it worked for me.
The Timer automatically starts, splits and resets when you enter the menu. Great Job!
Splitting on Satan does not always work for me, I guess the problem is the "boss_health" Pointer.
I will post again when I find a Pointer that works consistently for me.
Gammler33 and I rescanned your pointer scans for "level" and managed to bring them down to 26.
The pointer I am currently using is:
byte level : "mono.dll", 0x20A14C, 0x0, 0x364, 0x10, 0x18;
Here is a link to the file with all 26 Pointers:
http://www.mediafire.com/file/w3a82kw1k933vkw/Pointer_Scan%28CheatEngine%206.6%29.zip

 
  argusdustyargusdusty

Hey lachszunge,

I tested with your pointerlist and there are 2 which appear to work consistently for me:
byte level : "mono.dll", 0x20A14C, 0x0, 0x364, 0x10, 0x18;
byte level : "mono.dll", 0x20A14C, 0x0, 0x48C, 0x18, 0x18;

I'll update my script to use the first since it's the pointer you're already using.

I have my own pointerlist which doesn't overlap with yours at all, with 8 pointers: http://www.mediafire.com/file/qtbrrkrnql3m22p/broforce_level.zip

I also have a pointerlist for the boss health value, unfortunately over 8000 pointers: http://www.mediafire.com/file/6292s9k2d8k5c9p/broforce_health.zip
The other value that could be causing issues with the split on Satan is game_speed, and I don't have a pointerlist for that. If you need to search for it, it's a float that goes from 1.0 to a smaller value when the game slows for some cutscenes/timebro. Should be -> 0.1 for Satan death and 0.35 for timebro's ability if I have the numbers right.

 
  lachszungelachszunge

Hey argusdusty,
I finally found some time to check all the Pointers:
-your game_speed Pointer works very well for me, I scanned for it myself and came to the same result.
-your boss_health Pointer does not work for me, I use:
"mono.dll", 0x1F46AC, 0x8, 0x470, 0x18, 0x98, 0x1E8;
but even after I ensured that the Pointer was correct it sometimes wouldn't split on Satans death.
So I looked at Satans hp value during the fight and noticed that on the final hit his hp did not drop below 0 but simply froze. I fixed that by changing the final if statement:
if (current.boss_health <= 50 && current.game_speed < 0.30)
This is not a perfect solution but it works for me.

I scanned for the is_loading variable. It is really hard to scan for this since it is either 1 or 0 (which a lot of variables are), but I found a pointer which works for me. This Pointer does most likely not work on everybody's system (I had to choose from about 1 million possible pointers), but if you want to try it here it is:
byte is_loading : "Broforce_beta.exe", 0x1095F8C, 0x54, 0x8, 0x0, 0x4, 0x6FC;

Add these lines to your Autosplitter to implement the load removal:
isLoading
{
if(current.is_loading == 1){
return true;
}else{
return false;
}
}
Note: if the Pointer works for you then this should automatically Pause your "Game Time" Timer in Livesplit while the game is Loading(except on the first time you load any level after you start the game). This means that your Timer must be set to "Game Time" in your Overlay.

 
  argusdustyargusdusty

Hey lachszunge,
My boss_health pointer apparently doesn't work for me either. I'm guessing that LiveSplit was treating an invalid pointer as 0 and thus current.boss_health <= 0 was always true. To the best of my knowledge the only way to get below 30% game_speed on the final level is by triggering the final cutscene/ending, so (current.game_speed < 0.30) on the final level should be sufficient without needing the boss_health variable at all.

However, I did rescan for the boss_health parameter and found that the pointer path varied for each boss. Scanning just for the final boss, I ended up with a single pointer path: "mono.dll", 0x1F46AC, 0x8, 0x438, 0x18, 0x98, 0x1E8;
I noticed the freezing above 0 as well - if you want to use that variable you'll probably have to set the threshold to the maximum possible damage you can do (idk, maybe 100?). Though for now I'll leave it disabled in my script because not having it seems to work fine.

I tried your is_loading parameter and had no success. I also tried scanning for a 1 or 0 that indicated loading and had no success. I did, however, discover an interesting memory region that may be useful. I haven't been able to get a pointer path to it yet, but you hopefully can find it by searching for a 4-byte value, usually in the 15XXXXXX or 16XXXXXX range, with the properties:
1) When playing normally: has the value 2000810396 (or is a pointer to the value 3277710731)
2) When loading: is a pointer to the value 1945225193
3) When in or after a boss cutscene (level 16 is a good test): has the value 0 (a nil pointer)
4) At a constant address on each run of Broforce.exe
Those 4 bytes and the surrounding memory region appear to be useful for getting an is_loading boolean. Hopefully you'll have better luck deciphering it than I did.

 
  argusdustyargusdusty

Update on that boss_health value: as it turns out, you can get game_speed < 0.3 outside of the final cutscene, using Bro Lee's special (0.01). I instead found another value near the health that can be used instead:
float final_death_countdown : "mono.dll", 0x1F46AC, 0x8, 0x438, 0x18, 0x98, 0x1C4;
Should be 0x24 offset from the health value (1C4 instead of 1E8). It decreases from 5 to 0 after the final hit on the boss, hitting 3 when the boss explodes. I updated my autosplitter to use that instead.

lachszungelachszunge likes this. 
  argusdustyargusdusty

Hi all,

I've updated my autosplitter with my own isLoading method utilizing the in-game timers. The game keeps 2 timers representing the time taken on each level. The first is nil (which livesplit treats as '0') during the 'level complete' screen and '0' during loading. The second is computed at the 'level complete' screen and is '0' otherwise. Checking when both are '0' gives you only the loading screen, which works pretty well for me (I've done a complete run with an in-game timer - loading accounted for about 3m).

I'm trying to get the autosplitter into a more complete state now. I rescanned all the 'mono.dll' paths with a longer path length, which apparently they require. It's prohibitively expensive, computationally, to scan paths that long, so I determined the final offsets (using cheatengine's mono tools) for each variable and included them as comments in the script:

https://gist.github.com/argusdusty/7fea0526d09c74184f71c4d6c0911480

Hopefully this is working for everyone. If not, let me know what parts are acting up so I can further narrow down the pointer paths.

 
  MaodenMaoden

hello I try to put the auto splitter except that my live split does not recognize it while I did everything that is in the forum

 
  lachszungelachszunge

Hi Maxi,

unfortunately almost all Pointers stopped working after the last Broforce Update (I guess thats why the Autosplitter is not beeing recognised).

https://github.com/lachszunge/Broforce-Autosplitter/tree/master
This is the Version I'm currently using. There still are some things missing like campaign% support.
And some of these Pointers may not work for you.
You can use the Cheat Table in the github (Broforce_beta.CT) to check which Pointers work.

If you want your Autosplitter to automatically remove load times you will need to Scan for the "is_loading" variable.
Use the option "Pause the game while scanning" to make it easier.
You are looking for a Byte which is 0 when not loading and any number from 1 to 255 when loading.
look for a base address (the green ones in the address list).
Good thing is that you don't need to Pointer Scan for it since base addresses usually don't change.

If you are sure that your pointers are correct and there are no syntax errors in the code of the Autosplitter try removing variables , pointer paths and lines of code which you are not using.

I hope this was helpful. If you need any additional help feel free to ask.

MaodenMaoden likes this. 
  UlooraUloora

Thank You for autosplitter and remove loader. It's very useful.
when I will beating my PB in RTA, I will submitting a time.

 
  lachszungelachszunge

Hey Uloora,
I'm glad the Autosplitter works for you.
I want to note that this wouldn't have been possible without the help of Gammler33 who helped find some of these original Pointers and argusdusty who found the "GO" countdown, a method to reliably split in the last level and added campaign% support (I haven't implemented it yet, because I'm lazy).

Also good luck with your PB!

 
  ShadowDraftShadowDraft

Hey the two most recent runs had an issue with the autosplitter. The timer stopped when True Satan had his introduction cutscene and did not continue afterwards.

 
  argusdustyargusdusty

I believe my version of the autosplitter at https://gist.github.com/argusdusty/7fea0526d09c74184f71c4d6c0911480, which I try to keep updated, should be working for the final split.

The latest update really messed with the code, I had to remove to IGT timer sections as I couldn't get that working anymore. Not sure how the newest runs are counting that - I assume they are manually adding up the time from all the individual levels?

 
  BrennfeuBrennfeu

I think it would be pretty unfair to add the missing time when I lost way more time because of the lag and the not completely removed loading times.
I don't know for others but I personnaly loose way more time because of that than the 41min speedrun does with loading times...
(I'm kinda new to the speedrun community, and I don't plan to speedrun games other than Broforce, so sorry if out of place or anything)

Also kinda not the subject here but there's a guide on steam on a glitch with the brode that's really useful.