Since beginning my foray into the world of tabletop gaming, I have consumed a great many ‘Battle Reports’. Otherwise known as recordings, either pre-recorded or streamed, of people playing said tabletop games.
One battle report producing channel, Brutal Damage, seems to have a rather tech savvy bunch that have designed both a score-clock app and a social media generator. After learning about the app and generator, I took to poking at both and have some results to share.
Social Media Generator
The social media generator is rather neat. Just a few tweaks to change fonts and colors and a couple custom template images and you have some easy, branded media to pimp your streams.
In order to use the generator you will need:
- Python 3.7.1 – I am not going to go into a Pythin tutorial here, there are plenty of others that have tread hat ground before.
- Image Magic ( x86 or x64 ) – Just install it. The script interacts with it to manipulate the images.
- Wand Libraries – type “pip install wand” in a command line or Powershell. This is what lets Python talk to Image Magic.
- Click Libraries – type “pip install click” in a command line or Powershell. Their ReadMe doesn’t mention this, but I had to grab it as well.
The generator can be found here: https://github.com/brutaldamage/graphic-builder
Dual Attack App
The Dual Attack app that was put out by this group is awesome. It is built rather specifically for Warmachine, though. Static time ranges. Can’t change points until Turn 2B. I don’t think it would take much tweaking to make it a tad more generic and work for many games.
One reason they designed the app was to serve the game state data to a web page that can be rendered in a broadcast application and customized with CSS to fit your overlay. And it works rather well.
After learning about the Dual Attack app I began playing with it and Open Broadcast Software Studio (OBS). I then remembered how bad I am at CSS. I poked around and discovered they are providing the data to the webpage via JSON. Knowing that, I knew I should be able grab that data myself. So I dusted off my Python (read Google) skill and whipped up a little script to grab the app data and dump them into text files for OBS to pull. It works rather well, even if I do say so myself. There is a momentary blanking of some of the fields in OBS due to what I imagine are read/write collisions. A better programmer might add some protection for such things, but I am not that programmer. I prompted for the IP…isn’t that enough? Here is a haphazard demo of it in action…
My script is provided at the end of this post.
The Dual Attack app particulars that thus:
I have no idea if i will ever put any of this stuff to proper use, but was intrigued by the idea and the potential that it felt blog-worthy. So intrigued, in fact, that i cranked out a whole LargeGeek branded bat-rep setup in OBS…
And a big thanks to Brutal Damage for sharing these tools and producing solid Bat-Reps! I plan to follow up this post with another post and maybe a video as a crash course in OBS as there are a few quality of life things that are not immediately apparent that I absorbed in researching all this.
# This is a little Python script to be using in conjunction with the 'Dual Attack' score clock app. # It will generate separate files for each piece of data that the app tracks. # You can point text sources to these files in Open Broadcast Software to track game state on an overlay. # I am by no means an expert on any of this, so do what you will with it. # It seems to work well enough for me, with a momentary blanking on OBS when there are write/read collision. import urllib.request import urllib.error import json from time import sleep svrIP = input('Enter Server IP: ') # Ask user to input server IP server = "http://"+svrIP+":8080/data" # Build full address print('Currently generating Dual Attack files...\nCTRL+C to quit') # Print instructions # Main function to do all the work def file_gen(): with urllib.request.urlopen(server) as url: # Look at server and dump json into jdata jdata = json.loads(url.read().decode()) #Build individual variables for each json object jTurn = jdata["score"]["turn"] jTime1 = jdata["timer1"] jTime2 = jdata["timer2"] jCp1 = jdata["score"]["cp1"] jCp2 = jdata["score"]["cp2"] #Write data to each file wTurn = open("turn.txt","w+") wTurn.write(jTurn) wTime1 = open("time1.txt","w+") wTime1.write(jTime1) wTime2 = open("time2.txt","w+") wTime2.write(jTime2) wCp1 = open("CP1.txt","w+") wCp1.write(str(jCp1)) wCp2 = open("CP2.txt","w+") wCp2.write(str(jCp2)) #Loop to run function or exit try: while True: file_gen() sleep(0.5) # adds a delay so you are writing and polling needlessly except KeyboardInterrupt: quit()