feat: working admin interface

(need rework/styling)
This commit is contained in:
Laureηt 2021-11-20 19:58:48 +01:00
parent 32cd1080bf
commit 89d3ef103e
No known key found for this signature in database
GPG key ID: D88C6B294FD40994
4 changed files with 52 additions and 43 deletions

View file

@ -30,13 +30,13 @@
</div> </div>
<script> <script>
var websocket = new WebSocket("ws://127.0.0.1:6789/"); let websocket = new WebSocket("ws://127.0.0.1:6789/");
var passwordInput = document.querySelector('#password-text'); let passwordInput = document.querySelector('#password-text');
var divLogin = document.querySelector("#login"); let divLogin = document.querySelector("#login");
var divDashboard = document.querySelector("#dashboard"); let divDashboard = document.querySelector("#dashboard");
var stateList = document.querySelector("#stateList"); let stateList = document.querySelector("#stateList");
var saveButton = document.getElementById('save'); let saveButton = document.getElementById('save');
saveButton.onclick = function (event) { saveButton.onclick = function (event) {
websocket.send(JSON.stringify({ 'admin': 'save' })); websocket.send(JSON.stringify({ 'admin': 'save' }));
@ -44,11 +44,14 @@
function receiveStates(ev) { function receiveStates(ev) {
let msg = JSON.parse(ev.data); let msg = JSON.parse(ev.data);
let states = msg.state let states = msg.states
for (var i = 0; i < states.length; i++) { for (let i = 0; i < states.length; i++) {
var state = states[i]; let state = states[i];
var li = document.createElement('li'); let li = document.createElement('li');
li.appendChild(document.createTextNode(state)); let button = document.createElement('button');
button.onclick = () => websocket.send(JSON.stringify({ 'admin': "load:" + state }))
button.appendChild(document.createTextNode(state));
li.appendChild(button);
stateList.appendChild(li); stateList.appendChild(li);
} }
} }
@ -59,13 +62,12 @@
divLogin.style.display = "none"; divLogin.style.display = "none";
divDashboard.style.display = "unset"; divDashboard.style.display = "unset";
websocket.removeEventListener('message', authSuccess); websocket.removeEventListener('message', authSuccess);
websocket.send(JSON.stringify({ "state": "get" })); receiveStates(ev);
websocket.addEventListener('message', receiveStates);
} }
}; };
function sendCreds() { function sendCreds() {
var message = JSON.stringify({ "auth": passwordInput.value }); let message = JSON.stringify({ "auth": passwordInput.value });
websocket.send(message) websocket.send(message)
websocket.addEventListener('message', authSuccess); websocket.addEventListener('message', authSuccess);
}; };

View file

@ -1,5 +1,6 @@
import asyncio import asyncio
import logging import logging
import os
import threading import threading
import time import time
from subprocess import PIPE, Popen # nosec from subprocess import PIPE, Popen # nosec
@ -9,6 +10,7 @@ import mgba.image
import mgba.log import mgba.log
import redis import redis
import utils
from settings import ( from settings import (
EMULATOR_FPS, EMULATOR_FPS,
EMULATOR_HEIGHT, EMULATOR_HEIGHT,
@ -27,7 +29,6 @@ from settings import (
REDIS_PORT, REDIS_PORT,
RTMP_STREAM_URI, RTMP_STREAM_URI,
) )
from utils import States
core = mgba.core.load_path(EMULATOR_ROM_PATH) core = mgba.core.load_path(EMULATOR_ROM_PATH)
screen = mgba.image.Image(EMULATOR_WIDTH, EMULATOR_HEIGHT) screen = mgba.image.Image(EMULATOR_WIDTH, EMULATOR_HEIGHT)
@ -38,8 +39,6 @@ logging.basicConfig(level=logging.DEBUG)
mgba.log.silence() mgba.log.silence()
r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0) r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0)
states: States = States()
def next_action(): def next_action():
"""Select the next key from the redis database. """Select the next key from the redis database.
@ -97,13 +96,12 @@ def state_manager(loop):
ps.subscribe("admin") ps.subscribe("admin")
while True: while True:
for message in ps.listen(): for message in ps.listen():
logging.debug(message)
if message["type"] == "message": if message["type"] == "message":
data = message["data"].decode("utf-8") data = message["data"].decode("utf-8")
if data == "save": if data == "save":
asyncio.ensure_future(states.save(core), loop=loop) asyncio.ensure_future(utils.save(core), loop=loop)
elif data.startswith("load:"): elif data.startswith("load:"):
asyncio.ensure_future(states.load(core, data.removeprefix("load:")), loop=loop) asyncio.ensure_future(utils.load(core, data.removeprefix("load:")), loop=loop)
async def emulator(): async def emulator():
@ -127,6 +125,13 @@ async def emulator():
async def main(loop): async def main(loop):
# setup states in redis
files = os.listdir("states")
states = list(filter(lambda x: x.endswith(".state"), files))
for state in states:
r.sadd("states", state.removesuffix(".state")) # voir si oneline possible
thread = threading.Thread(target=state_manager, args=(loop,)) thread = threading.Thread(target=state_manager, args=(loop,))
thread.start() thread.start()

View file

@ -2,6 +2,7 @@ import asyncio
import json import json
import logging import logging
import time import time
from typing import Union
import redis import redis
import websockets import websockets
@ -38,14 +39,20 @@ async def parse_message(user: User, message: dict[str, str]) -> None:
if USERS.admin is None and data == PASSWORD_ADMIN: if USERS.admin is None and data == PASSWORD_ADMIN:
USERS.admin = user USERS.admin = user
logging.debug(f"admin authenticated: {user}") logging.debug(f"admin authenticated: {user}")
await user.send('{"auth":"success"}')
response: dict[str, Union[str, list[str]]] = dict()
response["auth"] = "success"
states = r.smembers("states")
stringlist = [x.decode("utf-8") for x in states]
response["states"] = sorted(stringlist)
await user.send(json.dumps(response))
if "admin" in message: if "admin" in message:
if user == USERS.admin: if user == USERS.admin:
data = message["admin"] data = message["admin"]
if data == "save": if data == "save":
r.publish("admin", "save") r.publish("admin", "save")
elif data == data.startswith("load:"): elif data.startswith("load:"):
r.publish("admin", data) r.publish("admin", data)
else: else:
logging.error(f"unsupported admin action: {data}") logging.error(f"unsupported admin action: {data}")
@ -82,6 +89,8 @@ async def handler(websocket, path: str):
await parse_message(user, message) await parse_message(user, message)
finally: finally:
# Unregister user # Unregister user
if user == USERS.admin:
USERS.admin = None
USERS.unregister(user) USERS.unregister(user)

View file

@ -1,5 +1,4 @@
import logging import logging
import os
import time import time
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Optional from typing import Any, Optional
@ -64,25 +63,19 @@ class Users(set):
logging.debug(f"user unregistered: {self}") logging.debug(f"user unregistered: {self}")
class States(set): async def save(core):
"""Save and load states from files."""
def __init__(self) -> None:
"""Construct a `States` object."""
files = os.listdir("states")
states = list(filter(lambda x: x.endswith(".state"), files))
self.update(states)
async def save(self, core):
state = core.save_raw_state() state = core.save_raw_state()
with open(f"states/{time.strftime('%Y-%m-%dT%H:%M:%S')}.state", "wb") as state_file: current_time = time.strftime("%Y-%m-%dT%H:%M:%S")
with open(f"states/{current_time}.state", "wb") as state_file:
for byte in state: for byte in state:
state_file.write(byte.to_bytes(4, byteorder="big", signed=False)) state_file.write(byte.to_bytes(4, byteorder="big", signed=False))
self.add(state) logging.debug(f"state saved : {current_time}.state")
async def load(self, core, filename):
async def load(core, filename):
state = ffi.new("unsigned char[397312]") # pulled 397312 from my ass state = ffi.new("unsigned char[397312]") # pulled 397312 from my ass
with open(f"states/{filename}.state", "rb") as state_file: with open(f"states/{filename}.state", "rb") as state_file:
for i in range(len(state)): for i in range(len(state)):
state[i] = int.from_bytes(state_file.read(4), byteorder="big", signed=False) state[i] = int.from_bytes(state_file.read(4), byteorder="big", signed=False)
core.load_raw_state(state) core.load_raw_state(state)
logging.debug(f"state loaded : {filename}")