Adding version with the byte websocket bug to report to mojo.

This commit is contained in:
Sergiotarxz 2023-03-14 04:34:37 +01:00
parent 876f3506e6
commit 6bcde9fa56
9 changed files with 316 additions and 32 deletions

View File

@ -10,6 +10,8 @@ my $build = Module::Build->new(
dist_abstract => 'The emulator webpage.',
requires => {
'Mojolicious' => 0,
'Moo' => 0,
'Types::Standard' => 0,
},
);
$build->create_build_script;

View File

@ -0,0 +1,21 @@
package MSGBA::Web;
use Mojo::Base 'Mojolicious', -signatures;
# This method will run once at server start
sub startup ($self) {
# Load configuration from config file
my $config = $self->plugin('NotYAMLConfig' => { file => './msgba-web.yml' });
# Configure the application
$self->secrets($config->{secrets});
# Router
my $r = $self->routes;
# Normal route to controller
$r->get('/')->to('Static#index');
$r->websocket('/ws')->to('WS#proxy');
}
1;

View File

@ -0,0 +1,10 @@
package MSGBA::Web::Controller::Static;
use v5.34.1;
use strict;
use warnings;
use Mojo::Base 'Mojolicious::Controller', -signatures;
1;

View File

@ -0,0 +1,110 @@
package MSGBA::Web::Controller::WS;
use v5.34.1;
use strict;
use warnings;
use Mojo::Base 'Mojolicious::Controller', -signatures;
sub proxy {
my $self = shift;
my $conn = MSGBA::Web::Controller::WS::Connection->new(ws => $self, config => $self->config);
return $conn->handle();
}
package MSGBA::Web::Controller::WS::Connection {
use v5.34.1;
use strict;
use warnings;
use Moo;
use IO::Socket::UNIX;
has config => (
is => 'ro',
required => 1,
);
has ws => (
is => 'ro',
required => 1,
);
has msgba_connection => (
is => 'rw',
required => 0,
);
sub handle {
my $self = shift;
my $ws = $self->ws;
$self->_build_msgba_connection;
$ws->on('message', sub {
my ($ws, $bytes) = @_;
if (!$bytes) {
warn "Received empty message";
return;
}
say "Received message";
say unpack 'H*', $bytes;
open my $fh, '<', \$bytes;
read $fh, my $id, 8;
read $fh, my $size, 8;
$size = unpack 'Q>', $size;
close $fh;
$self->msgba_connection->print($bytes);
});
return;
#while (my $packet = $self->read_packet) {
# $self->handle_packet($packet);
#}
#close $self->msgba_connection;
#$ws->closed;
}
sub handle_packet {
my $self = shift;
my $packet = shift;
my $ws = $self->ws;
my ($id, $size, $raw_data) = $packet->@{qw/id size raw_data/};
$ws->send({binary => "${id}${size}${raw_data}"});
}
sub read_packet {
my $self = shift;
my $fh = $self->msgba_connection;
my $ws = $self->ws;
my ($id, $size, $raw_data);
if ((read $fh, $id, 8) != 8) {
return;
}
if ((read $fh, $size, 8) != 8) {
return;
}
my $size_num = unpack 'Q>', $size;
if ((read $fh, $raw_data, $size_num) != $size_num) {
return;
}
return {
id => $id,
size => $size,
raw_data => $raw_data,
};
}
sub _build_msgba_connection {
my $self = shift;
my $config = $self->config;
my $msgba_connection = IO::Socket::UNIX->new(
Type => SOCK_STREAM(),
Peer => $config->{domain_socket},
) or die "@{[$config->{domain_socket}]}: $!";
$self->msgba_connection($msgba_connection);
}
};
1;

View File

@ -1,20 +0,0 @@
package msgba-web;
use Mojo::Base 'Mojolicious', -signatures;
# This method will run once at server start
sub startup ($self) {
# Load configuration from config file
my $config = $self->plugin('NotYAMLConfig');
# Configure the application
$self->secrets($config->{secrets});
# Router
my $r = $self->routes;
# Normal route to controller
$r->get('/')->to('Example#welcome');
}
1;

View File

@ -1,11 +0,0 @@
package msgba-web::Controller::Example;
use Mojo::Base 'Mojolicious::Controller', -signatures;
# This action will render a template
sub welcome ($self) {
# Render template "example/welcome.html.ep" with message
$self->render(msg => 'Welcome to the Mojolicious real-time web framework!');
}
1;

2
msgba-web.yml Normal file
View File

@ -0,0 +1,2 @@
---
domain_socket: "/home/sergio/msgba/msgba.sock"

View File

@ -8,4 +8,4 @@ use lib curfile->dirname->sibling('lib')->to_string;
use Mojolicious::Commands;
# Start command line interface for application
Mojolicious::Commands->start_app('msgba-web');
Mojolicious::Commands->start_app('MSGBA::Web');

View File

@ -0,0 +1,170 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
min-height: 100vh;
}
.center-content {
display: flex;
justify-content: center;
}
form label, form input {
display: block;
}
</style>
</head>
<body>
<div class="center-content">
<h2>msGBA Emulator Online for GBA.</h2>
</div>
<div class="center-content">
<canvas id="emulator" width="240" height="160">
</canvas>
</div>
<div class="center-content" id="form-select-files">
<form>
<label for="rom">
Rom file
<input type="file" id="rom" name="rom"/>
</label
<label for="savestate">
Savestate (A ss file from mgba...)
<input type="file" id="savestate" name="savestate"/>
</label>
<input type="button" value="Start emulation" id="start-emulation-button">
</form>
</div>
<script>
"use strict";
const MIN_WIDTH = 240;
const MIN_HEIGHT = 160;
const canvas = document.querySelector('#emulator');
const ctx = canvas.getContext('2d');
const emulator_dimensions = { width: MIN_WIDTH, height: MIN_HEIGHT };
const start_emulation_button = document.querySelector('#start-emulation-button');
const rom_file_selector = document.querySelector('#rom');
const savestate_file_selector = document.querySelector('#savestate');
const PACKET_ID_HELLO = 0n;
// Send is sent from server, so it is get here but whatever, it is the bad naming of the protocol.
const PACKET_ID_SEND_FRAME = 1n;
let websocket = null;
let is_little_endian = true;
(()=>{
let buf = new ArrayBuffer(4);
let buf8 = new Uint8ClampedArray(buf);
let data = new Uint32Array(buf);
data[0] = 0xdeadbeef;
if(buf8[0] === 0xde){
is_little_endian = false;
}
})();
console.log((is_little_endian ? 'Little endian' : 'Big endian') + " Detected");
function u64ToByteArrayBigEndian(input_number) {
const buffer = new ArrayBuffer(8);
const buffer8 = new Uint8Array(buffer);
const buffer64 = new BigUint64Array(buffer);
buffer64[0] = input_number;
if (is_little_endian) {
buffer8.reverse();
}
return buffer8;
}
function concatU8Array(array1, array2) {
const final_array = new Uint8Array(array1.length + array2.length);
final_array.set(array1);
final_array.set(array2, array1.length);
return final_array;
}
start_emulation_button.addEventListener('click', () => {
if (rom_file_selector.files.length == 0) {
alert('There is no rom still');
return;
}
if (savestate_file_selector.files.length == 0) {
alert('There is no savestate still');
return;
}
const rom_file = rom_file_selector.files[0];
const savestate_file = savestate_file_selector.files[0];
rom_file.arrayBuffer().then((rom_buffer) => {
savestate_file.arrayBuffer().then((savestate_buffer) => {
const rom_array = new Uint8Array(rom_buffer);
const savestate_array = new Uint8Array(savestate_buffer);
websocket = new WebSocket(`ws://${location.host}/ws`);
websocket.addEventListener('open', () => {
console.log('Opened websocket.');
sendHello(websocket, rom_array, savestate_array);
});
});
});
});
function sendHello(websocket, rom_array, savestate_array) {
console.log('Sending hello.');
const length_rom = BigInt(rom_array.length);
const length_savestate = BigInt(savestate_array.length);
const raw_data =
concatU8Array(
concatU8Array(
concatU8Array(u64ToByteArrayBigEndian(length_rom), rom_array),
u64ToByteArrayBigEndian(length_savestate)
),
savestate_array
);
sendPacket(websocket, PACKET_ID_HELLO, raw_data);
}
function sendPacket(websocket, id, raw_data) {
const packet_u8 = concatU8Array(
concatU8Array(u64ToByteArrayBigEndian(BigInt(id)), u64ToByteArrayBigEndian(BigInt(raw_data.length))),
raw_data
);
const packet_blob = packet_u8.buffer;
console.log('Sending packet');
websocket.send(packet_blob);
}
function resizeEmulator(canvas) {
const width = document.body.clientWidth;
const height = document.body.clientHeight;
if (width < MIN_WIDTH || height < MIN_HEIGHT) {
alert('Size too small.');
return;
}
const ratio_width = Math.floor(width / MIN_WIDTH);
const ratio_height = Math.floor(height / MIN_HEIGHT);
if (ratio_width < ratio_height) {
emulator_dimensions.width = MIN_WIDTH * ratio_width;
emulator_dimensions.height = MIN_HEIGHT * ratio_width;
} else {
emulator_dimensions.height = MIN_HEIGHT * ratio_height;
emulator_dimensions.width = MIN_WIDTH * ratio_height;
}
canvas.width = emulator_dimensions.width;
canvas.height = emulator_dimensions.height;
fillBlack(ctx);
}
function fillBlack(ctx) {
ctx.beginPath();
ctx.rect(0, 0, emulator_dimensions.width, emulator_dimensions.height);
ctx.fillStyle = 'black';
ctx.fill();
}
function connectWebsocket(rom, savestate) {
}
window.addEventListener('resize', () => {
resizeEmulator(canvas);
});
resizeEmulator(canvas);
</script>
</body>
</html>