Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,12 @@ dmypy.json
*.iml
out
gen
build.sh
obs-plugin/build.sh

# AppImage Build Artifacts
*.AppImage
AppDir/
build_artifacts/
linuxdeploy-*.AppImage
appimagetool-*.AppImage
*.so
templates/generated_reset_*.jpg
*.tar.gz
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ This will generate `AutoSplit64plus-x86_64.AppImage` in the root directory.
### Using the AppImage
1. Make it executable: `chmod +x AutoSplit64plus-x86_64.AppImage`
2. Run it: `./AutoSplit64plus-x86_64.AppImage`

## Known issues:
Having a filter on the same source as the OBS plugin that has errors (i.e. doesn't have the relevant plugin installed) will cause shared memory to not initialize properly on the plugin side.
48 changes: 25 additions & 23 deletions as64core/capture_shmem.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class SharedMemoryCapture(object):
def __init__(self):
self.shmem_handle = None
self.shmem = None
self.shmem_fd = None
self.width = 0
self.height = 0
self.linesize = 0
Expand All @@ -38,13 +39,13 @@ def open_shmem(self):
# Linux implementation
shm_path = f"/dev/shm/{SHMEM_NAME}"
if not os.path.exists(shm_path):
debug_info = f"Path checked: {shm_path}\n"
if os.path.exists("/dev/shm"):
debug_info += f"Contents of /dev/shm: {os.listdir('/dev/shm')}"
else:
debug_info += "/dev/shm directory does not exist!"
raise Exception(f"Could not find OBS Grabber Plugin!\n\n{debug_info}\n\nPlease make sure the plugin is enabled and OBS is running.")
debug_info = f"Path checked: {shm_path}\n"
if os.path.exists("/dev/shm"):
debug_info += f"Contents of /dev/shm: {os.listdir('/dev/shm')}"
else:
debug_info += "/dev/shm directory does not exist!"

raise Exception(f"Could not find OBS Grabber Plugin!\n\n{debug_info}\n\nPlease make sure the plugin is enabled and OBS is running.")

# We need to open the file to get a file descriptor
self.shmem_fd = open(shm_path, "r+b")
Expand Down Expand Up @@ -91,23 +92,24 @@ def _update_dimensions(self):
header = np.frombuffer(shmem_header.read(16), dtype=np.uint32)
shmem_header.close()
else:
if not hasattr(self, 'shmem_fd') or not self.shmem_fd:
# Try to open if we have the file
if os.path.exists(f"/dev/shm/{SHMEM_NAME}"):
self.open_shmem()
else:
return # Can't update dimensions if not open/exists
if not hasattr(self, 'shmem_fd') or not self.shmem_fd:
print(f"hasattr: {hasattr(self, 'shmem_fd')} shmem_fd: {self.shmem_fd}")
# Try to open if we have the file
if os.path.exists(f"/dev/shm/{SHMEM_NAME}"):
self.open_shmem()
else:
return # Can't update dimensions if not open/exists

# Map header using the file descriptor
# We use the existing FD.
try:
shmem_header = mmap.mmap(self.shmem_fd.fileno(), 16, access=mmap.ACCESS_READ)
shmem_header.seek(0)
header = np.frombuffer(shmem_header.read(16), dtype=np.uint32)
shmem_header.close()
except Exception as e:
print(f"Error reading shmem header: {e}")
return
# Map header using the file descriptor
# We use the existing FD.
try:
shmem_header = mmap.mmap(self.shmem_fd.fileno(), 16, access=mmap.ACCESS_READ)
shmem_header.seek(0)
header = np.frombuffer(shmem_header.read(16), dtype=np.uint32)
shmem_header.close()
except Exception as e:
print(f"Error reading shmem header: {e}")
return

new_width = int(header[0])
new_height = int(header[1])
Expand Down
70 changes: 56 additions & 14 deletions as64core/livesplit.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,32 @@
except ImportError:
win32file = None
win32pipe = None
CONNECTIONS = set()

import time
import websockets
import asyncio
import threading

from . import config


async def register(websocket):
CONNECTIONS.add(websocket)
try:
await websocket.wait_closed()
finally:
CONNECTIONS.remove(websocket)

def websocket_func():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
ws_server = websockets.serve(register, 'localhost', 5678)

loop.run_until_complete(ws_server)
loop.run_forever()
loop.close()

# Connect to LiveSplit and return socket
def connect() -> object:
# Get connection type from config
Expand Down Expand Up @@ -39,7 +61,13 @@ def connect() -> object:
ls_socket.connect((config.get("connection", "ls_host"), config.get("connection", "ls_port")))
return ls_socket
except:
return False
try:
# Initialise websocket
server = threading.Thread(target=websocket_func, daemon=True)
server.start()
finally:
return "websocket"

else:
return False

Expand All @@ -48,18 +76,27 @@ def disconnect(ls_socket) -> None:
# Check if connection even exists
if ls_socket is False:
return
ls_socket.close()
try:
ls_socket.close()
except:
# for websocket
return


def check_connection(ls_socket) -> bool:
# Check if connection has been established
if (ls_socket == False):
return False
# Check if communication is possible and response is received
if split_index(ls_socket) is False:
try:
if ls_socket == "websocket":
return True
# Check if connection has been established
if (ls_socket == False):
return False
# Check if communication is possible and response is received
if split_index(ls_socket) is False:
return False
else:
return True
except:
return False
else:
return True


def send(ls_socket, command) -> None:
Expand All @@ -70,6 +107,8 @@ def send(ls_socket, command) -> None:
ls_socket.send(command.encode('utf-8'))
# If it is a pipe:
else:
if ls_socket == "websocket":
websockets.broadcast(CONNECTIONS, '{"command": "' + command + '"}')
if win32file is None:
return

Expand All @@ -80,21 +119,24 @@ def send(ls_socket, command) -> None:
raise Exception("LiveSplit connection lost")

def split(ls_socket) -> None:
send(ls_socket, "startorsplit\r\n")
send(ls_socket, "splitOrStart" if ls_socket == "websocket" else "startorsplit\r\n")
# send(ls_socket, "startorsplit\r\n")


def reset(ls_socket) -> None:
send(ls_socket, "reset\r\n")
send(ls_socket, "reset" if ls_socket == "websocket" else "reset\r\n")

def restart(ls_socket) -> None:
send(ls_socket, "reset\r\nstarttimer\r\n")
send(ls_socket, "reset" if ls_socket == "websocket" else "reset\r\nstarttimer\r\n")
if ls_socket == "websocket":
send(ls_socket, "splitOrStart")

def skip(ls_socket) -> None:
send(ls_socket, "skipsplit\r\n")
send(ls_socket, "skipSplit" if ls_socket == "websocket" else "skipsplit\r\n")


def undo(ls_socket) -> None:
send(ls_socket, "unsplit\r\n")
send(ls_socket, "undoSplit" if ls_socket == "websocket" else "unsplit\r\n")


def split_index(ls_socket):
Expand Down
4 changes: 2 additions & 2 deletions build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ call .venv\Scripts\activate.bat
pyinstaller ^
--noconfirm ^
--name "AutoSplit64plus" ^
--splash "resources\gui\icons\as64plus.png" ^
--icon "resources\gui\icons\as64plus.ico" ^
--splash "resources\gui\icons\icon.png" ^
--icon "resources\gui\icons\icon.ico" ^
--noconsole ^
--clean ^
--noupx ^
Expand Down
20 changes: 20 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
source .venv/bin/activate

pyinstaller \
--noconfirm \
--name "AutoSplit64plus" \
--splash "resources/gui/icons/icon.png" \
--icon "resources/gui/icons/icon.ico" \
--noconsole \
--clean \
--noupx \
--contents-directory "libraries" \
AutoSplit64.py

cp -r logic dist/AutoSplit64plus/logic
cp -r resources dist/AutoSplit64plus/resources
cp -r routes dist/AutoSplit64plus/routes
cp -r templates dist/AutoSplit64plus/templates
cp defaults.ini dist/AutoSplit64plus/

tar -czvf Autosplit64.tar.gz ./dist/AutoSplit64plus
2 changes: 1 addition & 1 deletion build_appimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ BUILD_ROOT="/tmp/as64_build_$(date +%s)"
APP_DIR="$BUILD_ROOT/AppDir"
BUILD_DIR="$BUILD_ROOT/build_artifacts"
# Path to host Python 3.11 installation to bundle
PYTHON_HOST_PATH="/home/poke/.pyenv/versions/3.11.9"
PYTHON_HOST_PATH="/home/$USER/.pyenv/versions/3.11.9"

echo "Build started at $(date)"
echo "Build root: $BUILD_ROOT"
Expand Down
Binary file removed obs-plugin/autosplit64plus-framegrabber.so
Binary file not shown.
Loading