Remove wasm; add js client.
This commit is contained in:
parent
26fe6c86e7
commit
7ea088737d
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
|
/data
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
*.pem
|
*.pem
|
||||||
|
@ -3,6 +3,5 @@ resolver = "2"
|
|||||||
|
|
||||||
members = [
|
members = [
|
||||||
"game",
|
"game",
|
||||||
"client-web",
|
|
||||||
"server",
|
"server",
|
||||||
]
|
]
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "client-web"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
wasm-bindgen = "0.2.92"
|
|
||||||
|
|
||||||
game = { path = "../game" }
|
|
||||||
|
|
||||||
[dependencies.web-sys]
|
|
||||||
version = "0.3.69"
|
|
||||||
features = [
|
|
||||||
'Document',
|
|
||||||
'Element',
|
|
||||||
'HtmlElement',
|
|
||||||
'Node',
|
|
||||||
'Window',
|
|
||||||
|
|
||||||
'Event',
|
|
||||||
]
|
|
@ -1,67 +0,0 @@
|
|||||||
use crate::prelude::Scene;
|
|
||||||
|
|
||||||
pub mod session; use session::Session;
|
|
||||||
pub mod scene;
|
|
||||||
|
|
||||||
pub struct App {
|
|
||||||
pub session:Session,
|
|
||||||
|
|
||||||
pub document:web_sys::Document,
|
|
||||||
pub menu:web_sys::Element,
|
|
||||||
pub main:web_sys::Element,
|
|
||||||
|
|
||||||
unload:Option<fn()>,
|
|
||||||
}
|
|
||||||
impl App {
|
|
||||||
pub fn init() -> Result<Self,()>
|
|
||||||
{
|
|
||||||
match web_sys::window() {
|
|
||||||
Some(window) => match window.document() {
|
|
||||||
Some(document) => {
|
|
||||||
let menu = document.create_element("nav");
|
|
||||||
let container = document.create_element("main");
|
|
||||||
|
|
||||||
if menu.is_ok() && container.is_ok() {
|
|
||||||
Ok(Self {
|
|
||||||
session:Session::new(),
|
|
||||||
|
|
||||||
document:document,
|
|
||||||
menu:menu.unwrap(),
|
|
||||||
main:container.unwrap(),
|
|
||||||
|
|
||||||
unload:None,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(())
|
|
||||||
}
|
|
||||||
None => Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load<S:Scene>(&mut self)
|
|
||||||
{
|
|
||||||
match self.unload {
|
|
||||||
Some(proc) => { proc(); }
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
self.document_clear();
|
|
||||||
S::load();
|
|
||||||
self.unload = Some(S::unload);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn document_clear(&self)
|
|
||||||
{
|
|
||||||
match self.document.body() {
|
|
||||||
Some(body) => {
|
|
||||||
while let Some(child) = body.last_child() {
|
|
||||||
body.remove_child(&child).ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
scene::{ui, util},
|
|
||||||
session::SessionState,
|
|
||||||
APP,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* List Online
|
|
||||||
**
|
|
||||||
** Displays a table of recent, ongoing contests.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub struct SceneListOnline { }
|
|
||||||
impl crate::prelude::Scene for SceneListOnline {
|
|
||||||
fn load()
|
|
||||||
{
|
|
||||||
let mut app = unsafe {APP.borrow_mut()};
|
|
||||||
match &mut *app {
|
|
||||||
Some(app) => {
|
|
||||||
util::load_mainmenu();
|
|
||||||
|
|
||||||
// Create header
|
|
||||||
match app.document.create_element("header") {
|
|
||||||
Ok(header) => {
|
|
||||||
|
|
||||||
// Left-side header
|
|
||||||
match app.document.create_element("section") {
|
|
||||||
Ok(section_left) => {
|
|
||||||
|
|
||||||
// Button: Start
|
|
||||||
if app.session.state == SessionState::User {
|
|
||||||
match ui::button(&app.document, "Start", |_e|{ }) {
|
|
||||||
Ok(button) => { section_left.append_child(&button).ok(); }
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header.append_child(§ion_left).ok();
|
|
||||||
}
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right-side header
|
|
||||||
match app.document.create_element("section") {
|
|
||||||
Ok(section_right) => {
|
|
||||||
|
|
||||||
// Button: Previous Page
|
|
||||||
match ui::button(&app.document, "◀", |_e|{ }) {
|
|
||||||
Ok(button) => { section_right.append_child(&button).ok(); }
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text: Page Number
|
|
||||||
match ui::button(&app.document, "", |_e|{ }) {
|
|
||||||
Ok(elem) => {
|
|
||||||
elem.set_id("pn");
|
|
||||||
section_right.append_child(&elem).ok();
|
|
||||||
}
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Button: Next Page
|
|
||||||
match ui::button(&app.document, "▶", |_e|{ }) {
|
|
||||||
Ok(button) => { section_right.append_child(&button).ok(); }
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
app.main.append_child(&header).ok();
|
|
||||||
}
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
pub mod util;
|
|
||||||
pub mod ui;
|
|
||||||
mod list_online; pub use list_online::*;
|
|
@ -1,27 +0,0 @@
|
|||||||
use wasm_bindgen::{prelude::Closure, JsCast};
|
|
||||||
|
|
||||||
pub fn button(document:&web_sys::Document, text:&str, callback:fn(web_sys::Event)) -> Result<web_sys::Element,()>
|
|
||||||
{
|
|
||||||
match document.create_element("button") {
|
|
||||||
Ok(element) => {
|
|
||||||
element.set_text_content(Some(text));
|
|
||||||
|
|
||||||
let cl = Closure::wrap(Box::new(callback) as Box<dyn FnMut(_)>);
|
|
||||||
element.add_event_listener_with_callback("click", cl.as_ref().unchecked_ref()).ok();
|
|
||||||
cl.forget();
|
|
||||||
Ok(element)
|
|
||||||
}
|
|
||||||
Err(_) => Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn text_block(document:&web_sys::Document, text:&str) -> Result<web_sys::Element,()>
|
|
||||||
{
|
|
||||||
match document.create_element("div") {
|
|
||||||
Ok(element) => {
|
|
||||||
element.set_text_content(Some(text));
|
|
||||||
Ok(element)
|
|
||||||
}
|
|
||||||
Err(_) => Err(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
scene::{self, ui},
|
|
||||||
session::SessionState,
|
|
||||||
APP
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn load_mainmenu()
|
|
||||||
{
|
|
||||||
let mut app = unsafe {APP.borrow_mut()};
|
|
||||||
match &mut *app {
|
|
||||||
Some(app) => {
|
|
||||||
|
|
||||||
if app.session.state == SessionState::User {
|
|
||||||
// Button: Online
|
|
||||||
match ui::button(&app.document, "Online", |_e|{
|
|
||||||
let mut app = unsafe {APP.borrow_mut()};
|
|
||||||
match &mut *app {
|
|
||||||
Some(app) => { app.load::<scene::SceneListOnline>(); }
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Ok(button) => { app.menu.append_child(&button).ok(); }
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Button: Continue
|
|
||||||
match ui::button(&app.document, "Continue", |_e|{
|
|
||||||
let mut app = unsafe {APP.borrow_mut()};
|
|
||||||
match &mut *app {
|
|
||||||
Some(app) => { app.load::<scene::SceneListOnline>(); }
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Ok(button) => { app.menu.append_child(&button).ok(); }
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Button: Join
|
|
||||||
match ui::button(&app.document, "Continue", |_e|{
|
|
||||||
let mut app = unsafe {APP.borrow_mut()};
|
|
||||||
match &mut *app {
|
|
||||||
Some(app) => { app.load::<scene::SceneListOnline>(); }
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Ok(button) => { app.menu.append_child(&button).ok(); }
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} None => { }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum SessionState {
|
|
||||||
Anonymous,
|
|
||||||
User,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Session {
|
|
||||||
pub state:SessionState,
|
|
||||||
pub handle:String,
|
|
||||||
pub token:[u8; 8],
|
|
||||||
pub secret:[u8; 16],
|
|
||||||
}
|
|
||||||
impl Session {
|
|
||||||
pub fn new() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
state:SessionState::Anonymous,
|
|
||||||
handle:String::new(),
|
|
||||||
token:[0; 8],
|
|
||||||
secret:[0; 16],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" { }
|
|
@ -1,32 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
mod ext;
|
|
||||||
mod prelude;
|
|
||||||
mod app; use app::*;
|
|
||||||
|
|
||||||
static mut APP :RefCell<Option<App>> = RefCell::new(None);
|
|
||||||
|
|
||||||
#[wasm_bindgen(start)]
|
|
||||||
fn main() -> Result<(),JsValue>
|
|
||||||
{
|
|
||||||
match App::init() {
|
|
||||||
Ok(app) => {
|
|
||||||
unsafe {
|
|
||||||
APP = RefCell::new(Some(app));
|
|
||||||
|
|
||||||
let mut app = APP.borrow_mut();
|
|
||||||
match &mut *app {
|
|
||||||
Some(app) => {
|
|
||||||
app.load::<scene::SceneListOnline>();
|
|
||||||
} None => { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => { }
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
pub trait Scene {
|
|
||||||
fn load();
|
|
||||||
fn unload() { }
|
|
||||||
|
|
||||||
fn hover() { }
|
|
||||||
fn click() { }
|
|
||||||
fn keydown() { }
|
|
||||||
}
|
|
@ -15,6 +15,7 @@ webpki-roots = "0.26"
|
|||||||
opaque-ke = "2.0.0"
|
opaque-ke = "2.0.0"
|
||||||
hyper = { version = "1.4.1", features = ["full"] }
|
hyper = { version = "1.4.1", features = ["full"] }
|
||||||
hyper-util = { version = "0.1.7", features = ["tokio"] }
|
hyper-util = { version = "0.1.7", features = ["tokio"] }
|
||||||
|
http-body-util = "0.1.2"
|
||||||
|
|
||||||
game = { path = "../game" }
|
game = { path = "../game" }
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ mod system;
|
|||||||
mod protocol;
|
mod protocol;
|
||||||
|
|
||||||
use app::App;
|
use app::App;
|
||||||
|
use hyper::body::Bytes;
|
||||||
use system::{cache::WebCache, net::Stream};
|
use system::{cache::WebCache, net::Stream};
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
use tokio_tungstenite::WebSocketStream;
|
use tokio_tungstenite::WebSocketStream;
|
||||||
@ -60,6 +61,8 @@ async fn handle_ws(mut ws_stream:WebSocketStream<TlsStream<TcpStream>>, args:Htt
|
|||||||
packet::*,
|
packet::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("ws ready");
|
||||||
|
|
||||||
let bus_ds = args.bus.mailbox(1).unwrap_or(1);
|
let bus_ds = args.bus.mailbox(1).unwrap_or(1);
|
||||||
|
|
||||||
while match ws_stream.try_next().await {
|
while match ws_stream.try_next().await {
|
||||||
@ -102,41 +105,62 @@ async fn handle_ws(mut ws_stream:WebSocketStream<TlsStream<TcpStream>>, args:Htt
|
|||||||
ws_stream.close(None).await.ok();
|
ws_stream.close(None).await.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn service_http(request:hyper::Request<hyper::body::Incoming>, args:HttpServiceArgs) -> Result<hyper::Response<String>, std::convert::Infallible>
|
async fn service_http(request:hyper::Request<hyper::body::Incoming>, args:HttpServiceArgs) -> Result<hyper::Response<http_body_util::Full<Bytes>>, std::convert::Infallible>
|
||||||
{
|
{
|
||||||
use hyper::Response;
|
use hyper::{Response, body::Bytes, header::{CONTENT_TYPE, CACHE_CONTROL, UPGRADE}};
|
||||||
|
use http_body_util::Full;
|
||||||
use tokio_tungstenite::accept_async;
|
use tokio_tungstenite::accept_async;
|
||||||
|
use tokio_tungstenite::tungstenite::handshake::server::create_response_with_body;
|
||||||
|
|
||||||
|
println!("Serving: {}", request.uri().path());
|
||||||
|
|
||||||
match request.uri().path() {
|
match request.uri().path() {
|
||||||
"" => Ok(Response::new(args.cache.html())),
|
"/.css" => Ok(Response::builder()
|
||||||
".css" => Ok(Response::new(args.cache.css())),
|
.header(CONTENT_TYPE, "text/css")
|
||||||
".js" => Ok(Response::new(args.cache.js())),
|
.header(CACHE_CONTROL, "no-cache")
|
||||||
".wasm" => Ok(Response::new(args.cache.wasm())),
|
.body(Full::new(Bytes::from(args.cache.css()))).unwrap()),
|
||||||
//"favicon.png" => Ok(Response::new(String::new())),
|
|
||||||
"ws" => {
|
"/.js" => Ok(Response::builder()
|
||||||
if request.headers().get(hyper::header::UPGRADE).map(|h| h == "websocket").unwrap_or(false) {
|
.header(CONTENT_TYPE, "text/javascript")
|
||||||
|
.header(CACHE_CONTROL, "no-cache")
|
||||||
|
.body(Full::new(Bytes::from(args.cache.js()))).unwrap()),
|
||||||
|
|
||||||
|
"/favicon.png" => Ok(Response::builder()
|
||||||
|
.header(CONTENT_TYPE, "image/png")
|
||||||
|
.body(Full::new(Bytes::from(args.cache.favicon()))).unwrap()),
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
if request.headers().get(UPGRADE).map(|h| h == "websocket").unwrap_or(false) {
|
||||||
|
let response = create_response_with_body(&request, || Full::new(Bytes::new())).unwrap();
|
||||||
|
|
||||||
|
tokio::task::spawn(async move {
|
||||||
match hyper::upgrade::on(request).await {
|
match hyper::upgrade::on(request).await {
|
||||||
Ok(upgraded) => {
|
Ok(upgraded) => {
|
||||||
match upgraded.downcast::<TokioIo<TlsStream<TcpStream>>>() {
|
match upgraded.downcast::<TokioIo<TlsStream<TcpStream>>>() {
|
||||||
Ok(parts) => {
|
Ok(parts) => {
|
||||||
match accept_async(parts.io.into_inner()).await {
|
match accept_async(parts.io.into_inner()).await {
|
||||||
Ok(ws_stream) => { handle_ws(ws_stream, args).await }
|
Ok(ws_stream) => {
|
||||||
Err(_) => { }
|
println!("here");
|
||||||
|
handle_ws(ws_stream, args).await
|
||||||
|
}
|
||||||
|
Err(e) => { println!("ws not accepted: {}", e.to_string()); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => { }
|
Err(_) => { println!("transfer error"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => { }
|
Err(e) => { println!("upgrade error: {}", e.to_string()); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
} else {
|
||||||
Ok(Response::builder()
|
Ok(Response::builder()
|
||||||
.status(101)
|
.header(CONTENT_TYPE, "text/html")
|
||||||
.header(hyper::header::UPGRADE, "websocket")
|
.header(CACHE_CONTROL, "no-cache")
|
||||||
.body(String::new())
|
.body(Full::new(Bytes::from(args.cache.html()))).unwrap())
|
||||||
.unwrap())
|
}
|
||||||
}
|
}
|
||||||
_ => Ok(Response::new(String::new())),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,11 +173,12 @@ async fn handle_http(stream:system::net::tls::TlsStream, addr:SocketAddr, args:H
|
|||||||
|
|
||||||
let io = TokioIo::new(stream.to_stream());
|
let io = TokioIo::new(stream.to_stream());
|
||||||
|
|
||||||
http1::Builder::new()
|
let conn = http1::Builder::new()
|
||||||
.serve_connection(io, service_fn(move |req| {
|
.serve_connection(io, service_fn(move |req| {
|
||||||
service_http(req, args.clone())
|
service_http(req, args.clone())
|
||||||
}))
|
}));
|
||||||
.await.ok();
|
|
||||||
|
conn.with_upgrades().await.ok();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,9 +220,12 @@ async fn main()
|
|||||||
let cache = WebCache::init();
|
let cache = WebCache::init();
|
||||||
|
|
||||||
let mut server = TlsServer::new();
|
let mut server = TlsServer::new();
|
||||||
server.add_cert("omen.kirisame.com", "cert/fullchain.pem", "cert/privkey.pem").await.ok();
|
if server.add_cert("omen.kirisame.com", "cert/fullchain.pem", "cert/privkey.pem").await.is_ok() {
|
||||||
|
println!("Loaded cert file.");
|
||||||
|
}
|
||||||
match server.bind("0.0.0.0:38612").await {
|
match server.bind("0.0.0.0:38612").await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
println!("Listener bind successful.");
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while server.accept(handle_http, HttpServiceArgs {
|
while server.accept(handle_http, HttpServiceArgs {
|
||||||
bus:bus.connect().unwrap(),
|
bus:bus.connect().unwrap(),
|
||||||
|
21
server/src/system/cache/mod.rs
vendored
21
server/src/system/cache/mod.rs
vendored
@ -6,7 +6,7 @@ struct WebCacheData {
|
|||||||
html:String,
|
html:String,
|
||||||
css:String,
|
css:String,
|
||||||
js:String,
|
js:String,
|
||||||
wasm:String,
|
favicon:Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -21,7 +21,7 @@ impl WebCache {
|
|||||||
let mut html = String::new();
|
let mut html = String::new();
|
||||||
let mut css = String::new();
|
let mut css = String::new();
|
||||||
let mut js = String::new();
|
let mut js = String::new();
|
||||||
let mut wasm = String::new();
|
let mut favicon = Vec::<u8>::new();
|
||||||
|
|
||||||
// Cache html file
|
// Cache html file
|
||||||
match File::open("www/.html") {
|
match File::open("www/.html") {
|
||||||
@ -45,23 +45,22 @@ impl WebCache {
|
|||||||
match File::open("www/.js") {
|
match File::open("www/.js") {
|
||||||
Ok(mut file) => {
|
Ok(mut file) => {
|
||||||
file.read_to_string(&mut js).ok();
|
file.read_to_string(&mut js).ok();
|
||||||
js = minimize_whitespace(&js);
|
//js = minimize_whitespace(&js);
|
||||||
}
|
}
|
||||||
Err(_) => { }
|
Err(_) => { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache wasm file
|
// Cache favicon file
|
||||||
match File::open("www/.wasm") {
|
match File::open("www/favicon.png") {
|
||||||
Ok(mut file) => {
|
Ok(mut file) => {
|
||||||
file.read_to_string(&mut wasm).ok();
|
file.read_to_end(&mut favicon).ok();
|
||||||
wasm = minimize_whitespace(&wasm);
|
|
||||||
}
|
}
|
||||||
Err(_) => { }
|
Err(_) => { }
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
data:Arc::new(RwLock::new(WebCacheData {
|
data:Arc::new(RwLock::new(WebCacheData {
|
||||||
html, css, js, wasm
|
html, css, js, favicon,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,11 +89,11 @@ impl WebCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wasm(&self) -> String
|
pub fn favicon(&self) -> Vec<u8>
|
||||||
{
|
{
|
||||||
match self.data.read() {
|
match self.data.read() {
|
||||||
Ok(reader) => reader.wasm.to_string(),
|
Ok(reader) => reader.favicon.clone(),
|
||||||
Err(_) => String::new(),
|
Err(_) => Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,8 +63,7 @@ impl CertificateStore {
|
|||||||
key:key.unwrap(),
|
key:key.unwrap(),
|
||||||
}));
|
}));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,8 +72,7 @@ impl CertificateStore {
|
|||||||
{
|
{
|
||||||
if self.certs.unset(domain) {
|
if self.certs.unset(domain) {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ impl Server for TlsServer {
|
|||||||
else { Err(()) }
|
else { Err(()) }
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("[conn failure] {:}", e.to_string());
|
println!("[connection failure] {:}", e.to_string());
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
www/.css
14
www/.css
@ -91,6 +91,7 @@ main>nav {
|
|||||||
height:3rem;
|
height:3rem;
|
||||||
|
|
||||||
background-color:#282828;
|
background-color:#282828;
|
||||||
|
border-bottom:1px solid #404040;
|
||||||
}
|
}
|
||||||
|
|
||||||
main>nav>section:first-child{
|
main>nav>section:first-child{
|
||||||
@ -125,7 +126,8 @@ main>nav>section>button {
|
|||||||
background-color:#282828;
|
background-color:#282828;
|
||||||
border:0;
|
border:0;
|
||||||
color:#c0c0c0;
|
color:#c0c0c0;
|
||||||
} main>nav>section>button:hover {
|
}
|
||||||
|
main>nav>section>button:hover{
|
||||||
background-color:#383838;
|
background-color:#383838;
|
||||||
color:#e0e0e0;
|
color:#e0e0e0;
|
||||||
}
|
}
|
||||||
@ -198,7 +200,8 @@ main table td:last-child>button {
|
|||||||
background-color:#303030;
|
background-color:#303030;
|
||||||
border:0;
|
border:0;
|
||||||
color:#e0e0e0;
|
color:#e0e0e0;
|
||||||
} main table td:last-child>button:hover {
|
}
|
||||||
|
main table td:last-child>button:hover{
|
||||||
background-color:#343434;
|
background-color:#343434;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +216,6 @@ main>canvas {
|
|||||||
background-color:#202020;
|
background-color:#202020;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scene:game */
|
|
||||||
main.game>div.sidemenu{
|
main.game>div.sidemenu{
|
||||||
display:flex;
|
display:flex;
|
||||||
position:absolute;
|
position:absolute;
|
||||||
@ -245,9 +247,11 @@ main.game>div.sidemenu>button {
|
|||||||
font-size:1rem;
|
font-size:1rem;
|
||||||
|
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
} main.game>div.sidemenu>button:hover {
|
}
|
||||||
|
main.game>div.sidemenu>button:hover{
|
||||||
background-color:#404040;
|
background-color:#404040;
|
||||||
} main.game>div.sidemenu>button.warn:hover {
|
}
|
||||||
|
main.game>div.sidemenu>button.warn:hover{
|
||||||
background-color:#602020;
|
background-color:#602020;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="application-name" content="Omen">
|
<meta name="application-name" content="Omen">
|
||||||
<meta name="description" content="Abstract strategy game mixing chess and shogi on a hexagon grid.">
|
<meta name="description" content="Abstract strategy game mixing chess and shogi on a hexagon grid.">
|
||||||
|
<link rel="icon" href="/favicon.png">
|
||||||
<link rel="stylesheet" href=".css">
|
<link rel="stylesheet" href=".css">
|
||||||
<script src=".js" async></script>
|
<script src=".js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>This application requires JavaScript to function.</noscript>
|
<noscript>This application requires JavaScript to function.</noscript>
|
||||||
|
300
www/.js
300
www/.js
@ -1 +1,299 @@
|
|||||||
WebAssembly.instantiateStreaming(fetch(".wasm"),ap);
|
let MAIN = null;
|
||||||
|
let MENU = null;
|
||||||
|
let SCENE = null;
|
||||||
|
|
||||||
|
let CONNECTED = false;
|
||||||
|
let SOCKET = null;
|
||||||
|
let USER = null;
|
||||||
|
let CONTEXT = null;
|
||||||
|
|
||||||
|
let UI = {
|
||||||
|
text:function(value) {
|
||||||
|
return document.createTextNode(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
button:function(text, callback) {
|
||||||
|
let b = document.createElement("button");
|
||||||
|
b.innerText = text;
|
||||||
|
if(callback !== null) { b.addEventListener("click", callback); }
|
||||||
|
return b;
|
||||||
|
},
|
||||||
|
|
||||||
|
div:function(children) {
|
||||||
|
let div = document.createElement("div");
|
||||||
|
for(child of children) { div.appendChild(child); }
|
||||||
|
return div;
|
||||||
|
},
|
||||||
|
|
||||||
|
table:function(header, rows) {
|
||||||
|
let table = document.createElement("table");
|
||||||
|
let tbody = document.createElement("tbody");
|
||||||
|
|
||||||
|
if(header !== null) {
|
||||||
|
let row = document.createElement("tr");
|
||||||
|
for(head of header) {
|
||||||
|
let cell = document.createElement("th");
|
||||||
|
cell.innerText = head;
|
||||||
|
row.appendChild(cell);
|
||||||
|
}
|
||||||
|
tbody.appendChild(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(row of rows) {
|
||||||
|
let tr = document.createElement("tr");
|
||||||
|
for(node of row) {
|
||||||
|
let cell = document.createElement("td");
|
||||||
|
if(Array.isArray(node)) { for(item of node) { cell.appendChild(item); } }
|
||||||
|
else { cell.appendChild(node); }
|
||||||
|
tr.appendChild(cell);
|
||||||
|
}
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.appendChild(tbody);
|
||||||
|
return table;
|
||||||
|
},
|
||||||
|
|
||||||
|
mainnav:function(left_children, right_children) {
|
||||||
|
let header = document.createElement("nav");
|
||||||
|
let left = document.createElement("section");
|
||||||
|
for(child of left_children) { left.appendChild(child); }
|
||||||
|
|
||||||
|
let right = document.createElement("section");
|
||||||
|
for(child of right_children) { right.appendChild(child); }
|
||||||
|
|
||||||
|
header.appendChild(left);
|
||||||
|
header.appendChild(right);
|
||||||
|
return header;
|
||||||
|
},
|
||||||
|
|
||||||
|
mainmenu:function() {
|
||||||
|
if(SOCKET !== null) {
|
||||||
|
MENU.appendChild(UI.button("Online", function() { LOAD(SCENE.Online); }));
|
||||||
|
MENU.appendChild(UI.button("Continue", function() { LOAD(SCENE.Continue); }));
|
||||||
|
MENU.appendChild(UI.button("Join", function() { LOAD(SCENE.Continue); }));
|
||||||
|
MENU.appendChild(UI.button("Live", function() { LOAD(SCENE.Continue); }));
|
||||||
|
MENU.appendChild(UI.button("History", function() { LOAD(SCENE.Continue); }));
|
||||||
|
MENU.appendChild(UI.button("Guide", function() { LOAD(SCENE.Continue); }));
|
||||||
|
MENU.appendChild(UI.button("About", function() { LOAD(SCENE.Continue); }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function RECONNECT() {
|
||||||
|
if(SOCKET === null) {
|
||||||
|
console.log("Connecting..");
|
||||||
|
SOCKET = new WebSocket("wss://omen.kirisame.com:38612");
|
||||||
|
SOCKET.binaryType = 'blob';
|
||||||
|
SOCKET.addEventListener("error", function(e) {
|
||||||
|
console.log("Failed");
|
||||||
|
SOCKET = null;
|
||||||
|
});
|
||||||
|
SOCKET.addEventListener("open", function(e) {
|
||||||
|
if(SOCKET.readyState === WebSocket.OPEN) {
|
||||||
|
console.log("Connected.");
|
||||||
|
|
||||||
|
SOCKET.addEventListener("message", function(e) {
|
||||||
|
MESSAGE(e.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
SOCKET.addEventListener("close", function(e) {
|
||||||
|
console.log("Closed.");
|
||||||
|
SOCKET = null;
|
||||||
|
RECONNECT();
|
||||||
|
});
|
||||||
|
|
||||||
|
RESUME();
|
||||||
|
SOCKET.send(new Uint8Array([ 0 ]));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function RESUME() {
|
||||||
|
LOAD(SCENES.Online);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SCENES = {
|
||||||
|
Init:{
|
||||||
|
load:function() {
|
||||||
|
LOAD(SCENES.Offline);
|
||||||
|
RECONNECT();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Offline:{
|
||||||
|
load:function() {
|
||||||
|
MENU.appendChild(UI.button("Reconnect", function() { RECONNECT(); }))
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Online:{
|
||||||
|
load:function() {
|
||||||
|
UI.mainmenu();
|
||||||
|
MAIN.appendChild(UI.mainnav([
|
||||||
|
UI.button("Start", null),
|
||||||
|
], [
|
||||||
|
UI.div([UI.text("0 - 0 of 0")]),
|
||||||
|
UI.button("◀", null),
|
||||||
|
UI.button("▶", null),
|
||||||
|
]));
|
||||||
|
|
||||||
|
let table = document.createElement("table");
|
||||||
|
table.setAttribute("id", "content");
|
||||||
|
MAIN.appendChild(table);
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
refresh:function() {
|
||||||
|
let request = new Uint8Array();
|
||||||
|
|
||||||
|
//SERVER.send()
|
||||||
|
|
||||||
|
let cb = function() {
|
||||||
|
let table = document.getElementById("content");
|
||||||
|
MAIN.removeChild(table);
|
||||||
|
|
||||||
|
let data = [
|
||||||
|
[ UI.text("Player1"), UI.text("Player2"), UI.text("0"), UI.text("Ha1-D ◈ Ba1-M"), UI.text("0"), [ UI.button("Join", null), UI.button("Spectate", null) ] ],
|
||||||
|
];
|
||||||
|
|
||||||
|
MAIN.appendChild(UI.table(
|
||||||
|
[ "Dawn", "Dusk", "Turn", "Move", "Spectators", "" ],
|
||||||
|
data,
|
||||||
|
));
|
||||||
|
};
|
||||||
|
cb();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Continue:{
|
||||||
|
load:function() {
|
||||||
|
if(USER === null) return false;
|
||||||
|
UI.mainmenu();
|
||||||
|
MAIN.appendChild(UI.mainnav([
|
||||||
|
UI.button("Start", null),
|
||||||
|
], [
|
||||||
|
UI.button("◀", null),
|
||||||
|
UI.button("▶", null),
|
||||||
|
]));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
refresh:function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Join:{
|
||||||
|
load:function() {
|
||||||
|
if(USER === null) return false;
|
||||||
|
UI.mainmenu();
|
||||||
|
MAIN.appendChild(UI.mainnav([
|
||||||
|
UI.button("Start", null),
|
||||||
|
], [
|
||||||
|
UI.button("◀", null),
|
||||||
|
UI.button("▶", null),
|
||||||
|
]));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
refresh:function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Live:{
|
||||||
|
load:function() {
|
||||||
|
UI.mainmenu();
|
||||||
|
MAIN.appendChild(UI.mainnav([
|
||||||
|
UI.button("Start", null),
|
||||||
|
], [
|
||||||
|
UI.button("◀", null),
|
||||||
|
UI.button("▶", null),
|
||||||
|
]));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
refresh:function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
History:{
|
||||||
|
load:function() {
|
||||||
|
UI.mainmenu();
|
||||||
|
MAIN.appendChild(UI.mainnav([ ], [
|
||||||
|
UI.button("◀", null),
|
||||||
|
UI.button("▶", null),
|
||||||
|
]));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
refresh:function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Guide:{
|
||||||
|
load:function() {
|
||||||
|
UI.mainmenu();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
refresh:function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
About:{
|
||||||
|
load:function() {
|
||||||
|
UI.mainmenu();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
refresh:function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Game:{
|
||||||
|
load:function() {
|
||||||
|
MENU.appendChild(UI.button("Back", function() { LOAD(SCENES.Online) }));
|
||||||
|
MENU.appendChild(UI.button("Retire", function() { }));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
unload:function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
refresh:function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function REBUILD() {
|
||||||
|
MENU = document.createElement("nav");
|
||||||
|
let title = document.createElement("header");
|
||||||
|
title.innerText = "Omen";
|
||||||
|
MENU.appendChild(title);
|
||||||
|
|
||||||
|
MAIN = document.createElement("main");
|
||||||
|
|
||||||
|
document.body.appendChild(MENU);
|
||||||
|
document.body.appendChild(MAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MESSAGE() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function LOAD(scene) {
|
||||||
|
if(SCENE.unload !== undefined) { SCENE.unload(); }
|
||||||
|
while(document.body.lastChild !== null) { document.body.removeChild(document.body.lastChild); }
|
||||||
|
REBUILD();
|
||||||
|
SCENE = scene;
|
||||||
|
if(!SCENE.load()) { LOAD(SCENES.Online); }
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
SCENE = SCENES.Offline;
|
||||||
|
LOAD(SCENES.Init);
|
||||||
|
});
|
||||||
|
BIN
www/favicon.png
Normal file
BIN
www/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
Loading…
x
Reference in New Issue
Block a user