Add persistent storage for salt and handle.
This commit is contained in:
parent
d5c0d74016
commit
ee12d74c67
@ -19,6 +19,7 @@ http-body-util = "0.1.2"
|
|||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
rust-argon2 = "2.1.0"
|
rust-argon2 = "2.1.0"
|
||||||
ring = "0.17.8"
|
ring = "0.17.8"
|
||||||
|
const_format = "0.2.32"
|
||||||
|
|
||||||
game = { path = "../game" }
|
game = { path = "../game" }
|
||||||
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
# Table
|
|
||||||
|
|
||||||
## User Authentication
|
|
||||||
|
|
||||||
- Register—create a new account.
|
|
||||||
- Handle
|
|
||||||
- Secret
|
|
||||||
- Invite_Code
|
|
||||||
- Authenticate—log into an account and create a new client session.
|
|
||||||
- Handle
|
|
||||||
- Secret
|
|
||||||
- Deauthenticate—revoke the current client session.
|
|
||||||
|
|
||||||
## Sessions
|
|
||||||
|
|
||||||
- List_Sessions—
|
|
||||||
- Page
|
|
||||||
- Ongoing or Complete
|
|
||||||
- Joinable
|
|
||||||
- Live
|
|
||||||
- Update_Sessions—
|
|
||||||
|
|
||||||
## Game
|
|
||||||
|
|
||||||
- Join—
|
|
||||||
- Session_Id
|
|
||||||
- Spectate—
|
|
||||||
- Session_Id
|
|
||||||
- Leave—
|
|
||||||
- Retire—
|
|
||||||
- Play—
|
|
||||||
- Move_Source [ Board, Pool ]
|
|
||||||
- Move_From
|
|
||||||
- Piece_From
|
|
||||||
- Move_To
|
|
||||||
- Piece_To
|
|
143
server/docs/protocol/requests.szun
Normal file
143
server/docs/protocol/requests.szun
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
### Status Codes ##
|
||||||
|
|
||||||
|
# General
|
||||||
|
|
||||||
|
0000 OK
|
||||||
|
0001 ERROR
|
||||||
|
0002 NOAUTH
|
||||||
|
00FE SERVER_ERROR
|
||||||
|
00FF NOT_IMPLEMENTED
|
||||||
|
|
||||||
|
# Context Specific
|
||||||
|
|
||||||
|
0010 BAD_HANDLE
|
||||||
|
0011 BAD_SECRET
|
||||||
|
0012 BAD_CODE
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
## Authentication ##
|
||||||
|
|
||||||
|
# 0010 REGISTER
|
||||||
|
Req_Register {
|
||||||
|
handle :sequence # username
|
||||||
|
secret :sequence # password
|
||||||
|
code :sequence # invitation code
|
||||||
|
}
|
||||||
|
Res_Register {
|
||||||
|
status :block<2>
|
||||||
|
# OK - registration accepted
|
||||||
|
# BAD_HANDLE - handle empty or not vacant
|
||||||
|
# BAD_SECRET - secret is empty
|
||||||
|
# BAD_CODE - code not accepted
|
||||||
|
token :block<8> # auth key
|
||||||
|
secret :block<16> # resume code
|
||||||
|
}
|
||||||
|
|
||||||
|
# 0011 AUTH
|
||||||
|
Req_Auth {
|
||||||
|
handle :sequence # username
|
||||||
|
secret :sequence # password
|
||||||
|
}
|
||||||
|
Res_Auth {
|
||||||
|
status :block<2>
|
||||||
|
# STATUS_OK - authentication successful
|
||||||
|
# ERROR - authentication failed
|
||||||
|
token :block<8> # auth key
|
||||||
|
secret :block<16> # resume code
|
||||||
|
}
|
||||||
|
|
||||||
|
# 0012 AUTH_RESUME
|
||||||
|
Req_AuthResume {
|
||||||
|
token :block<8> # auth key
|
||||||
|
secret :block<16> # resume code
|
||||||
|
}
|
||||||
|
Res_AuthResume {
|
||||||
|
status :block<2>
|
||||||
|
# OK - resume accepted
|
||||||
|
# ERROR - auth/code pair not accepted
|
||||||
|
}
|
||||||
|
|
||||||
|
# 0013 AUTH_REVOKE
|
||||||
|
Req_AuthRevoke { }
|
||||||
|
Res_AuthRevoke { }
|
||||||
|
|
||||||
|
|
||||||
|
## Session ##
|
||||||
|
|
||||||
|
# 0020 SESSION_LIST
|
||||||
|
Req_SessionList {
|
||||||
|
flags :block<2>
|
||||||
|
# 0[2] Game State
|
||||||
|
# 2[1] Is Player
|
||||||
|
# 3[1] Is Live
|
||||||
|
page :block<2>
|
||||||
|
}
|
||||||
|
Res_SessionList {
|
||||||
|
records :list<{
|
||||||
|
token :block<8> # session key
|
||||||
|
handle_dawn :sequence # username of dawn player
|
||||||
|
handle_dusk :sequence # username of dusk player
|
||||||
|
turn :block<2> # turn number
|
||||||
|
last_move :block<3> # most recent move code
|
||||||
|
viewers :block<4> # number of viewers
|
||||||
|
player :bool # user is player
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
# 0021 SESSION_CREATE
|
||||||
|
Req_SessionCreate { }
|
||||||
|
Res_SessionCreate {
|
||||||
|
status :block<2>
|
||||||
|
token :block<8>
|
||||||
|
mode :block<1>
|
||||||
|
# 0: Player Dawn
|
||||||
|
# 1: Player Dusk
|
||||||
|
# 2: Spectator
|
||||||
|
}
|
||||||
|
|
||||||
|
# 0022 SESSION_JOIN
|
||||||
|
Res_SessionJoin {
|
||||||
|
token :block<8> # session key
|
||||||
|
}
|
||||||
|
Req_SessionJoin {
|
||||||
|
status :block<2>
|
||||||
|
# OK
|
||||||
|
# NOAUTH
|
||||||
|
token :block<8> # session key
|
||||||
|
mode :block<1>
|
||||||
|
# 0: Player Dawn
|
||||||
|
# 1: Player Dusk
|
||||||
|
# 2: Spectator
|
||||||
|
}
|
||||||
|
|
||||||
|
# 002E SESSION_LEAVE
|
||||||
|
Req_SessionLeave { }
|
||||||
|
Res_SessionLeave { }
|
||||||
|
|
||||||
|
# 002F SESSION_RETIRE
|
||||||
|
Req_SessionRetire {
|
||||||
|
token :block<8> # session key
|
||||||
|
}
|
||||||
|
Res_SessionRetire {
|
||||||
|
status:block<2>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## Game ##
|
||||||
|
|
||||||
|
# 0030 GAME_STATE
|
||||||
|
Req_GameState {
|
||||||
|
token :block<8> # session token
|
||||||
|
}
|
||||||
|
Res_GameState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# 0031 GAME_PLAY
|
||||||
|
Req_GamePlay {
|
||||||
|
token :block<8> # session token
|
||||||
|
}
|
||||||
|
Res_GamePlay {
|
||||||
|
|
||||||
|
}
|
@ -1,15 +1,16 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use futures::stream::SplitSink;
|
||||||
|
use hyper::upgrade::Upgraded;
|
||||||
|
use hyper_util::rt::TokioIo;
|
||||||
|
use tokio_tungstenite::{tungstenite::Message, WebSocketStream};
|
||||||
|
|
||||||
use crate::app::authentication::AuthToken;
|
use crate::app::authentication::AuthToken;
|
||||||
|
|
||||||
|
type StreamType = Arc<RwLock<SplitSink<WebSocketStream<TokioIo<Upgraded>>, Message>>>;
|
||||||
|
|
||||||
pub struct Connection {
|
pub struct Connection {
|
||||||
pub bus:u32,
|
pub bus:u32,
|
||||||
|
pub stream:StreamType,
|
||||||
pub auth:Option<AuthToken>,
|
pub auth:Option<AuthToken>,
|
||||||
}
|
}
|
||||||
impl Connection {
|
|
||||||
pub fn new() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
bus:0,
|
|
||||||
auth:None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
use sparse::Sparse;
|
use sparse::Sparse;
|
||||||
use pool::Pool;
|
use pool::Pool;
|
||||||
use trie::Trie;
|
use trie::Trie;
|
||||||
use crate::util::{
|
use crate::{
|
||||||
Chain,
|
system::filesystem::FileSystem,
|
||||||
pack::pack_u32,
|
util::Chain,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod connection; use connection::Connection;
|
pub mod connection; use connection::Connection;
|
||||||
@ -15,13 +15,15 @@ pub mod session; use session::{Session, SessionToken};
|
|||||||
pub mod context;
|
pub mod context;
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
|
pub filesystem:FileSystem,
|
||||||
|
|
||||||
pub connections:Pool<Connection>,
|
pub connections:Pool<Connection>,
|
||||||
|
|
||||||
pub users:Pool<User>,
|
pub users:Pool<User>,
|
||||||
pub user_next:u32,
|
pub user_next:u32,
|
||||||
pub user_id:Sparse<usize>,
|
pub user_id:Sparse<usize>,
|
||||||
pub user_handle:Trie<u32>,
|
pub user_handle:Trie<u32>,
|
||||||
pub salts:Vec<[u8; 16]>,
|
pub salts:Sparse<[u8; 16]>,
|
||||||
|
|
||||||
pub auths:Trie<Authentication>,
|
pub auths:Trie<Authentication>,
|
||||||
pub sessions:Trie<Session>,
|
pub sessions:Trie<Session>,
|
||||||
@ -29,39 +31,45 @@ pub struct App {
|
|||||||
pub session_time:Chain<SessionToken>,
|
pub session_time:Chain<SessionToken>,
|
||||||
}
|
}
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new() -> Self
|
pub fn init() -> Result<Self, ()>
|
||||||
{
|
{
|
||||||
Self {
|
if let Ok(mut filesystem) = FileSystem::init() {
|
||||||
connections:Pool::new(),
|
|
||||||
|
|
||||||
users:Pool::new(),
|
// Load salts
|
||||||
user_next:0,
|
let mut salts = Sparse::new();
|
||||||
user_id:Sparse::new(),
|
let salt_count = filesystem.salt_count()?;
|
||||||
user_handle:Trie::new(),
|
for id in 0..salt_count {
|
||||||
salts:Vec::new(),
|
let salt = filesystem.salt_fetch(id as u32).unwrap();
|
||||||
|
salts.set(id as isize, salt);
|
||||||
|
}
|
||||||
|
|
||||||
auths:Trie::new(),
|
// Load handles
|
||||||
sessions:Trie::new(),
|
let mut user_handle = Trie::new();
|
||||||
|
let handle_count = filesystem.handle_count()?;
|
||||||
|
for id in 0..handle_count {
|
||||||
|
let (handle, user_id) = filesystem.handle_fetch(id as u32).unwrap();
|
||||||
|
println!("got: {} = {}", handle, user_id);
|
||||||
|
user_handle.set(handle.as_bytes(), user_id);
|
||||||
|
}
|
||||||
|
|
||||||
session_time:Chain::new(),
|
Ok(Self {
|
||||||
|
filesystem:filesystem,
|
||||||
|
|
||||||
|
connections:Pool::new(),
|
||||||
|
|
||||||
|
users:Pool::new(),
|
||||||
|
user_next:0,
|
||||||
|
user_id:Sparse::new(),
|
||||||
|
user_handle:Trie::new(),
|
||||||
|
salts,
|
||||||
|
|
||||||
|
auths:Trie::new(),
|
||||||
|
sessions:Trie::new(),
|
||||||
|
|
||||||
|
session_time:Chain::new(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&mut self) -> Result<(),std::io::Error>
|
|
||||||
{ use std::{path::Path, fs};
|
|
||||||
|
|
||||||
let path_data = Path::new("data");
|
|
||||||
if !path_data.exists() {
|
|
||||||
fs::create_dir(path_data)?;
|
|
||||||
fs::create_dir(path_data.join("c"))?;
|
|
||||||
fs::create_dir(path_data.join("s"))?;
|
|
||||||
fs::create_dir(path_data.join("u"))?;
|
|
||||||
|
|
||||||
fs::write(path_data.join("c/.i"), pack_u32(0)).ok();
|
|
||||||
fs::write(path_data.join("s/.i"), pack_u32(0)).ok();
|
|
||||||
fs::write(path_data.join("u/.i"), pack_u32(0)).ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,5 @@ pub struct User {
|
|||||||
pub id:u32,
|
pub id:u32,
|
||||||
pub handle:String,
|
pub handle:String,
|
||||||
pub secret:Vec<u8>,
|
pub secret:Vec<u8>,
|
||||||
pub na_key:usize,
|
pub na_key:u32,
|
||||||
}
|
}
|
||||||
|
@ -100,13 +100,14 @@ async fn main()
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Initialize application data.
|
// Initialize application data.
|
||||||
let mut app = App::new();
|
let app;
|
||||||
if app.init().is_err() {
|
if let Ok(result) = App::init() {
|
||||||
|
app = result;
|
||||||
|
} else {
|
||||||
println!("fatal: failed to initialize server.");
|
println!("fatal: failed to initialize server.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize central bus and data serivce.
|
// Initialize central bus and data serivce.
|
||||||
let b_main :Bus<protocol::QRPacket> = Bus::new_as(bus::Mode::Transmitter);
|
let b_main :Bus<protocol::QRPacket> = Bus::new_as(bus::Mode::Transmitter);
|
||||||
match b_main.connect() {
|
match b_main.connect() {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
|
use futures::SinkExt;
|
||||||
|
|
||||||
use bus::Bus;
|
use bus::Bus;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
@ -7,20 +10,28 @@ use crate::{
|
|||||||
connection::Connection,
|
connection::Connection,
|
||||||
},
|
},
|
||||||
protocol,
|
protocol,
|
||||||
|
util::pack::pack_u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn encode_response(code:u16, data:Vec<u8>) -> Vec<u8>
|
||||||
|
{
|
||||||
|
[
|
||||||
|
pack_u16(code),
|
||||||
|
data,
|
||||||
|
].concat()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
||||||
{
|
{
|
||||||
use protocol::*;
|
use protocol::*;
|
||||||
use protocol::QRPacketData::*;
|
|
||||||
use ring::rand::{SecureRandom, SystemRandom};
|
use ring::rand::{SecureRandom, SystemRandom};
|
||||||
|
|
||||||
let rng = SystemRandom::new();
|
let rng = SystemRandom::new();
|
||||||
let argon_config = argon2::Config::default();
|
let argon_config = argon2::Config::default();
|
||||||
|
|
||||||
while match bus.receive_wait() {
|
while let Some(response) = match bus.receive_wait() {
|
||||||
Some(packet) => {
|
Some(packet) => {
|
||||||
let qr = &packet.data;
|
let qr = packet.data;
|
||||||
|
|
||||||
let mut user_id = None;
|
let mut user_id = None;
|
||||||
if let Some(conn) = app.connections.get(qr.id as usize) {
|
if let Some(conn) = app.connections.get(qr.id as usize) {
|
||||||
@ -31,28 +42,41 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match &qr.data {
|
match qr.data {
|
||||||
QConn(request) => {
|
QRPacketData::QConn(request) => {
|
||||||
let id = app.connections.add(Connection {
|
let id = app.connections.add(Connection {
|
||||||
bus: request.bus_id,
|
bus: request.bus_id,
|
||||||
|
stream: request.stream,
|
||||||
auth: None,
|
auth: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("Connect: {}", id);
|
println!("Connect: {}", id);
|
||||||
|
|
||||||
bus.send(packet.from, QRPacket::new(id as u32, RConn)).is_ok()
|
bus.send(
|
||||||
|
packet.from,
|
||||||
|
QRPacket::new(id as u32, QRPacketData::RConn)
|
||||||
|
).ok();
|
||||||
|
Some(QRPacket::new(0, QRPacketData::None))
|
||||||
}
|
}
|
||||||
|
|
||||||
QDisconn => {
|
QRPacketData::QDisconn => {
|
||||||
app.connections.remove(qr.id as usize).ok();
|
// Close socket and remove connection if valid
|
||||||
|
//
|
||||||
|
if if let Some(conn) = app.connections.get_mut(qr.id as usize) {
|
||||||
|
let mut socket = conn.stream.write().await;
|
||||||
|
socket.close().await.ok();
|
||||||
|
true
|
||||||
|
} else { false } {
|
||||||
|
app.connections.remove(qr.id as usize).ok();
|
||||||
|
|
||||||
println!("Disconnect: {}", qr.id);
|
println!("Disconnect: {}", qr.id);
|
||||||
true
|
}
|
||||||
|
Some(QRPacket::new(0, QRPacketData::None))
|
||||||
}
|
}
|
||||||
|
|
||||||
QRegister(request) => {
|
QRPacketData::QRegister(request) => {
|
||||||
let mut response = PacketRegisterResponse::new();
|
let mut response = PacketRegisterResponse::new();
|
||||||
response.status = STATUS_ERROR;
|
response.status = STATUS_SERVER_ERROR;
|
||||||
|
|
||||||
println!("Request: Register");
|
println!("Request: Register");
|
||||||
|
|
||||||
@ -67,8 +91,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
let mut salt = [0u8; 16];
|
let mut salt = [0u8; 16];
|
||||||
match rng.fill(&mut salt) {
|
match rng.fill(&mut salt) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let salt_id = app.salts.len();
|
let salt_id = app.filesystem.salt_store(salt).unwrap();
|
||||||
app.salts.push(salt);
|
app.salts.set(salt_id as isize, salt);
|
||||||
|
|
||||||
if let Ok(hash) = argon2::hash_raw(&request.secret, &salt, &argon_config) {
|
if let Ok(hash) = argon2::hash_raw(&request.secret, &salt, &argon_config) {
|
||||||
let user_id = app.user_next;
|
let user_id = app.user_next;
|
||||||
@ -83,6 +107,7 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Register user pool id and handle
|
// Register user pool id and handle
|
||||||
|
app.filesystem.handle_store(&request.handle, user_id).ok();
|
||||||
app.user_id.set(user_id as isize, user_pos);
|
app.user_id.set(user_id as isize, user_pos);
|
||||||
app.user_handle.set(request.handle.as_bytes(), user_id);
|
app.user_handle.set(request.handle.as_bytes(), user_id);
|
||||||
|
|
||||||
@ -120,10 +145,10 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.send(packet.from, QRPacket::new(qr.id, RRegister(response))).is_ok()
|
Some(QRPacket::new(qr.id, QRPacketData::RRegister(response)))
|
||||||
}
|
}
|
||||||
|
|
||||||
QAuth(request) => {
|
QRPacketData::QAuth(request) => {
|
||||||
let mut response = PacketAuthResponse::new();
|
let mut response = PacketAuthResponse::new();
|
||||||
response.status = STATUS_ERROR;
|
response.status = STATUS_ERROR;
|
||||||
|
|
||||||
@ -141,7 +166,7 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
if let Some(tuid) = app.user_id.get(*uid as isize) {
|
if let Some(tuid) = app.user_id.get(*uid as isize) {
|
||||||
if let Some(user) = app.users.get(*tuid) {
|
if let Some(user) = app.users.get(*tuid) {
|
||||||
// Get user salt
|
// Get user salt
|
||||||
if let Some(salt) = app.salts.get(user.na_key) {
|
if let Some(salt) = app.salts.get(user.na_key as isize) {
|
||||||
|
|
||||||
// Verify salted secret against user data
|
// Verify salted secret against user data
|
||||||
if argon2::verify_raw(&request.secret.as_bytes(), salt, &user.secret, &argon_config).unwrap_or(false) {
|
if argon2::verify_raw(&request.secret.as_bytes(), salt, &user.secret, &argon_config).unwrap_or(false) {
|
||||||
@ -184,10 +209,10 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.send(packet.from, QRPacket::new(qr.id, RAuth(response))).is_ok()
|
Some(QRPacket::new(qr.id, QRPacketData::RAuth(response)))
|
||||||
}
|
}
|
||||||
|
|
||||||
QAuthResume(request) => {
|
QRPacketData::QAuthResume(request) => {
|
||||||
let mut response = PacketAuthResumeResponse::new();
|
let mut response = PacketAuthResumeResponse::new();
|
||||||
response.status = STATUS_ERROR;
|
response.status = STATUS_ERROR;
|
||||||
|
|
||||||
@ -209,10 +234,10 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.send(packet.from, QRPacket::new(qr.id, RAuthResume(response))).is_ok()
|
Some(QRPacket::new(qr.id, QRPacketData::RAuthResume(response)))
|
||||||
}
|
}
|
||||||
|
|
||||||
QDeauth => {
|
QRPacketData::QAuthRevoke => {
|
||||||
if let Some(conn) = app.connections.get_mut(qr.id as usize) {
|
if let Some(conn) = app.connections.get_mut(qr.id as usize) {
|
||||||
match conn.auth {
|
match conn.auth {
|
||||||
Some(auth) => {
|
Some(auth) => {
|
||||||
@ -223,10 +248,10 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
}
|
}
|
||||||
conn.auth = None;
|
conn.auth = None;
|
||||||
}
|
}
|
||||||
true
|
Some(QRPacket::new(0, QRPacketData::None))
|
||||||
}
|
}
|
||||||
|
|
||||||
QSessionList(request) => {
|
QRPacketData::QSessionList(request) => {
|
||||||
use game::game::GameState;
|
use game::game::GameState;
|
||||||
|
|
||||||
println!("Request: Session List");
|
println!("Request: Session List");
|
||||||
@ -277,7 +302,7 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
dusk_handle,
|
dusk_handle,
|
||||||
],
|
],
|
||||||
turn:0,
|
turn:0,
|
||||||
last_move:[0; 5],
|
last_move:[0; 3],
|
||||||
viewers:0,
|
viewers:0,
|
||||||
player:is_player,
|
player:is_player,
|
||||||
});
|
});
|
||||||
@ -290,10 +315,10 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
next_id = app.session_time.next(id);
|
next_id = app.session_time.next(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.send(packet.from, QRPacket::new(qr.id, RSessionList(response))).is_ok()
|
Some(QRPacket::new(qr.id, QRPacketData::RSessionList(response)))
|
||||||
}
|
}
|
||||||
|
|
||||||
QSessionCreate(_request) => {
|
QRPacketData::QSessionCreate(_request) => {
|
||||||
use crate::app::session::*;
|
use crate::app::session::*;
|
||||||
|
|
||||||
println!("Request: Session Create");
|
println!("Request: Session Create");
|
||||||
@ -336,10 +361,10 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
response.token = token;
|
response.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.send(packet.from, QRPacket::new(qr.id, RSessionCreate(response))).is_ok()
|
Some(QRPacket::new(qr.id, QRPacketData::RSessionCreate(response)))
|
||||||
}
|
}
|
||||||
|
|
||||||
QSessionJoin(request) => {
|
QRPacketData::QSessionJoin(request) => {
|
||||||
println!("Request: Session Join");
|
println!("Request: Session Join");
|
||||||
|
|
||||||
let mut response = PacketSessionJoinResponse::new();
|
let mut response = PacketSessionJoinResponse::new();
|
||||||
@ -390,12 +415,58 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.send(packet.from, QRPacket::new(qr.id, RSessionJoin(response))).is_ok()
|
Some(QRPacket::new(qr.id, QRPacketData::RSessionJoin(response)))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => { true }
|
_ => { Some(QRPacket::new(0, QRPacketData::None)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => false,
|
None => None,
|
||||||
} { }
|
} {
|
||||||
|
if match response.data { QRPacketData::None => false, _ => true } {
|
||||||
|
if let Some(conn) = app.connections.get_mut(response.id as usize) {
|
||||||
|
let mut socket = conn.stream.write().await;
|
||||||
|
|
||||||
|
match response.data {
|
||||||
|
QRPacketData::RRegister(response) => {
|
||||||
|
socket.send(Message::Binary(
|
||||||
|
encode_response(CODE_REGISTER, response.encode())
|
||||||
|
)).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRPacketData::RAuth(response) => {
|
||||||
|
socket.send(Message::Binary(
|
||||||
|
encode_response(CODE_AUTH, response.encode())
|
||||||
|
)).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRPacketData::RAuthResume(response) => {
|
||||||
|
socket.send(Message::Binary(
|
||||||
|
encode_response(CODE_AUTH_RESUME, response.encode())
|
||||||
|
)).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRPacketData::RSessionList(response) => {
|
||||||
|
socket.send(Message::Binary(
|
||||||
|
encode_response(CODE_SESSION_LIST, response.encode())
|
||||||
|
)).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRPacketData::RSessionCreate(response) => {
|
||||||
|
socket.send(Message::Binary(
|
||||||
|
encode_response(CODE_SESSION_CREATE, response.encode())
|
||||||
|
)).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRPacketData::RSessionJoin(response) => {
|
||||||
|
socket.send(Message::Binary(
|
||||||
|
encode_response(CODE_SESSION_JOIN, response.encode())
|
||||||
|
)).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,42 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
use hyper::upgrade::Upgraded;
|
use hyper::upgrade::Upgraded;
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use tokio_tungstenite::WebSocketStream;
|
use tokio_tungstenite::WebSocketStream;
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::StreamExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
protocol,
|
protocol,
|
||||||
util::pack::{pack_u16, unpack_u16},
|
util::pack::unpack_u16,
|
||||||
HttpServiceArgs,
|
HttpServiceArgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn encode_response(code:u16, data:Vec<u8>) -> Vec<u8>
|
pub async fn handle_ws(ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceArgs) -> Result<(),()>
|
||||||
{
|
|
||||||
[
|
|
||||||
pack_u16(code),
|
|
||||||
data,
|
|
||||||
].concat()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn handle_ws(mut ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceArgs) -> Result<(),()>
|
|
||||||
// Handle websocket connection.
|
// Handle websocket connection.
|
||||||
//
|
//
|
||||||
{
|
{
|
||||||
use tokio_tungstenite::tungstenite::protocol::Message;
|
use tokio_tungstenite::tungstenite::protocol::Message;
|
||||||
use protocol::{QRPacket, QRPacketData::*, code::*, packet::*};
|
use protocol::{QRPacket, QRPacketData, code::*, packet::*};
|
||||||
|
|
||||||
let conn_id :u32;
|
let conn_id :u32;
|
||||||
|
|
||||||
|
let (sink, mut stream) = ws.split();
|
||||||
|
|
||||||
let bus_ds = args.bus.mailbox(1).unwrap_or(1);
|
let bus_ds = args.bus.mailbox(1).unwrap_or(1);
|
||||||
|
|
||||||
// Perform connection handshake with data system.
|
// Perform connection handshake with data system.
|
||||||
// - Provide system with connection/bus pairing.
|
// - Provide system with connection/bus pairing.
|
||||||
// - Acquire connection id.
|
// - Acquire connection id.
|
||||||
//
|
//
|
||||||
args.bus.send(bus_ds, QRPacket::new(0, QConn(LocalPacketConnect {
|
args.bus.send(bus_ds, QRPacket::new(0, QRPacketData::QConn(LocalPacketConnect {
|
||||||
bus_id:args.bus.id(),
|
bus_id:args.bus.id(),
|
||||||
|
stream:Arc::new(RwLock::new(sink)),
|
||||||
})))?;
|
})))?;
|
||||||
match args.bus.receive_wait() {
|
match args.bus.receive_wait() {
|
||||||
Some(resp) => {
|
Some(resp) => {
|
||||||
let qr = &resp.data;
|
let qr = &resp.data;
|
||||||
match qr.data {
|
match qr.data {
|
||||||
RConn => { conn_id = qr.id; }
|
QRPacketData::RConn => { conn_id = qr.id; }
|
||||||
_ => { return Err(()); }
|
_ => { return Err(()); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,7 +46,7 @@ pub async fn handle_ws(mut ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServi
|
|||||||
// Decode client requests from websocket,
|
// Decode client requests from websocket,
|
||||||
// pass requests to data system,
|
// pass requests to data system,
|
||||||
// and return responses to client.
|
// and return responses to client.
|
||||||
while match ws.next().await {
|
while match stream.next().await {
|
||||||
Some(msg) => match msg {
|
Some(msg) => match msg {
|
||||||
Ok(msg) => match msg {
|
Ok(msg) => match msg {
|
||||||
Message::Binary(data) => {
|
Message::Binary(data) => {
|
||||||
@ -62,142 +59,67 @@ pub async fn handle_ws(mut ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServi
|
|||||||
|
|
||||||
CODE_REGISTER => match PacketRegister::decode(&data, &mut index) {
|
CODE_REGISTER => match PacketRegister::decode(&data, &mut index) {
|
||||||
Ok(packet) => {
|
Ok(packet) => {
|
||||||
if args.bus.send(bus_ds, QRPacket::new(conn_id, QRegister(packet))).is_ok() {
|
args.bus.send(
|
||||||
while match args.bus.receive_wait() {
|
bus_ds,
|
||||||
Some(resp) => {
|
QRPacket::new(conn_id, QRPacketData::QRegister(packet))
|
||||||
let qr = &resp.data;
|
).ok();
|
||||||
match &qr.data {
|
|
||||||
RRegister(resp) => {
|
|
||||||
ws.send(Message::Binary(
|
|
||||||
encode_response(code, resp.encode())
|
|
||||||
)).await.ok();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => true,
|
|
||||||
} { }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => { }
|
Err(_) => { }
|
||||||
}
|
}
|
||||||
|
|
||||||
CODE_AUTH => match PacketAuth::decode(&data, &mut index) {
|
CODE_AUTH => match PacketAuth::decode(&data, &mut index) {
|
||||||
Ok(packet) => {
|
Ok(packet) => {
|
||||||
if args.bus.send(bus_ds, QRPacket::new(conn_id, QAuth(packet))).is_ok() {
|
args.bus.send(
|
||||||
while match args.bus.receive_wait() {
|
bus_ds,
|
||||||
Some(resp) => {
|
QRPacket::new(conn_id, QRPacketData::QAuth(packet))
|
||||||
let qr = &resp.data;
|
).ok();
|
||||||
match &qr.data {
|
|
||||||
RAuth(resp) => {
|
|
||||||
ws.send(Message::Binary(
|
|
||||||
encode_response(code, resp.encode())
|
|
||||||
)).await.ok();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => true,
|
|
||||||
} { }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => { }
|
Err(_) => { }
|
||||||
}
|
}
|
||||||
|
|
||||||
CODE_AUTH_RESUME => match PacketAuthResume::decode(&data, &mut index) {
|
CODE_AUTH_RESUME => match PacketAuthResume::decode(&data, &mut index) {
|
||||||
Ok(packet) => {
|
Ok(packet) => {
|
||||||
if args.bus.send(bus_ds, QRPacket::new(conn_id, QAuthResume(packet))).is_ok() {
|
args.bus.send(
|
||||||
while match args.bus.receive_wait() {
|
bus_ds,
|
||||||
Some(resp) => {
|
QRPacket::new(conn_id, QRPacketData::QAuthResume(packet))
|
||||||
let qr = &resp.data;
|
).ok();
|
||||||
match &qr.data {
|
|
||||||
RAuth(resp) => {
|
|
||||||
ws.send(Message::Binary(
|
|
||||||
encode_response(code, resp.encode())
|
|
||||||
)).await.ok();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => true,
|
|
||||||
} { }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => { }
|
Err(_) => { }
|
||||||
}
|
}
|
||||||
|
|
||||||
CODE_DEAUTH => {
|
CODE_AUTH_REVOKE => {
|
||||||
args.bus.send(bus_ds, QRPacket::new(conn_id, QDeauth)).ok();
|
args.bus.send(
|
||||||
|
bus_ds, QRPacket::new(conn_id,
|
||||||
|
QRPacketData::QAuthRevoke)
|
||||||
|
).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
CODE_SESSION_LIST => match PacketSessionList::decode(&data, &mut index) {
|
CODE_SESSION_LIST => match PacketSessionList::decode(&data, &mut index) {
|
||||||
Ok(packet) => {
|
Ok(packet) => {
|
||||||
if args.bus.send(bus_ds, QRPacket::new(conn_id, QSessionList(packet))).is_ok() {
|
args.bus.send(
|
||||||
while match args.bus.receive_wait() {
|
bus_ds,
|
||||||
Some(resp) => {
|
QRPacket::new(conn_id, QRPacketData::QSessionList(packet))
|
||||||
let qr = &resp.data;
|
).ok();
|
||||||
match &qr.data {
|
|
||||||
RSessionList(resp) => {
|
|
||||||
ws.send(Message::Binary(
|
|
||||||
encode_response(code, resp.encode())
|
|
||||||
)).await.ok();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => true,
|
|
||||||
} { }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => { println!("error: packet decode failed."); }
|
Err(_) => { println!("error: packet decode failed."); }
|
||||||
}
|
}
|
||||||
|
|
||||||
CODE_SESSION_CREATE => match PacketSessionCreate::decode(&data, &mut index) {
|
CODE_SESSION_CREATE => match PacketSessionCreate::decode(&data, &mut index) {
|
||||||
Ok(packet) => {
|
Ok(packet) => {
|
||||||
if args.bus.send(bus_ds, QRPacket::new(conn_id, QSessionCreate(packet))).is_ok() {
|
args.bus.send(
|
||||||
while match args.bus.receive_wait() {
|
bus_ds,
|
||||||
Some(resp) => {
|
QRPacket::new(conn_id, QRPacketData::QSessionCreate(packet))
|
||||||
let qr = &resp.data;
|
).ok();
|
||||||
match &qr.data {
|
|
||||||
RSessionCreate(resp) => {
|
|
||||||
ws.send(Message::Binary(
|
|
||||||
encode_response(code, resp.encode())
|
|
||||||
)).await.ok();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => true,
|
|
||||||
} { }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => { println!("error: packet decode failed."); }
|
Err(_) => { println!("error: packet decode failed."); }
|
||||||
}
|
}
|
||||||
|
|
||||||
CODE_SESSION_JOIN => match PacketSessionJoin::decode(&data, &mut index) {
|
CODE_SESSION_JOIN => match PacketSessionJoin::decode(&data, &mut index) {
|
||||||
Ok(packet) => {
|
Ok(packet) => {
|
||||||
if args.bus.send(bus_ds, QRPacket::new(conn_id, QSessionJoin(packet))).is_ok() {
|
args.bus.send(
|
||||||
while match args.bus.receive_wait() {
|
bus_ds,
|
||||||
Some(resp) => {
|
QRPacket::new(conn_id, QRPacketData::QSessionJoin(packet))
|
||||||
let qr = &resp.data;
|
).ok();
|
||||||
match &qr.data {
|
|
||||||
RSessionJoin(resp) => {
|
|
||||||
ws.send(Message::Binary(
|
|
||||||
encode_response(code, resp.encode())
|
|
||||||
)).await.ok();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => true,
|
|
||||||
} { }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => { println!("error: packet decode failed."); }
|
Err(_) => { println!("error: packet decode failed."); }
|
||||||
}
|
}
|
||||||
@ -216,7 +138,6 @@ pub async fn handle_ws(mut ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServi
|
|||||||
None => false,
|
None => false,
|
||||||
} { }
|
} { }
|
||||||
|
|
||||||
args.bus.send(bus_ds, QRPacket::new(conn_id, QDisconn)).ok();
|
args.bus.send(bus_ds, QRPacket::new(conn_id, QRPacketData::QDisconn)).ok();
|
||||||
ws.close(None).await.ok();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ pub const STATUS_NOT_IMPL :u16 = 0x00FF;
|
|||||||
|
|
||||||
pub const CODE_REGISTER :u16 = 0x0010;
|
pub const CODE_REGISTER :u16 = 0x0010;
|
||||||
pub const CODE_AUTH :u16 = 0x0011;
|
pub const CODE_AUTH :u16 = 0x0011;
|
||||||
pub const CODE_DEAUTH :u16 = 0x0013;
|
|
||||||
pub const CODE_AUTH_RESUME :u16 = 0x0012;
|
pub const CODE_AUTH_RESUME :u16 = 0x0012;
|
||||||
|
pub const CODE_AUTH_REVOKE :u16 = 0x0013;
|
||||||
|
|
||||||
pub const CODE_SESSION_LIST :u16 = 0x0020;
|
pub const CODE_SESSION_LIST :u16 = 0x0020;
|
||||||
pub const CODE_SESSION_CREATE :u16 = 0x0021;
|
pub const CODE_SESSION_CREATE :u16 = 0x0021;
|
||||||
@ -29,4 +29,5 @@ pub const CODE_SESSION_JOIN :u16 = 0x0022;
|
|||||||
pub const CODE_SESSION_RETIRE :u16 = 0x002E;
|
pub const CODE_SESSION_RETIRE :u16 = 0x002E;
|
||||||
pub const CODE_SESSION_LEAVE :u16 = 0x002F;
|
pub const CODE_SESSION_LEAVE :u16 = 0x002F;
|
||||||
|
|
||||||
pub const CODE_GAME_PLAY :u16 = 0x0030;
|
pub const CODE_GAME_STATE :u16 = 0x0030;
|
||||||
|
pub const CODE_GAME_PLAY :u16 = 0x0031;
|
||||||
|
@ -5,7 +5,7 @@ pub mod packet; pub use packet::*;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum QRPacketData {
|
pub enum QRPacketData {
|
||||||
ROk,
|
None,
|
||||||
|
|
||||||
QConn(LocalPacketConnect),
|
QConn(LocalPacketConnect),
|
||||||
RConn,
|
RConn,
|
||||||
@ -21,7 +21,7 @@ pub enum QRPacketData {
|
|||||||
QAuthResume(PacketAuthResume),
|
QAuthResume(PacketAuthResume),
|
||||||
RAuthResume(PacketAuthResumeResponse),
|
RAuthResume(PacketAuthResumeResponse),
|
||||||
|
|
||||||
QDeauth,
|
QAuthRevoke,
|
||||||
|
|
||||||
QSessionList(PacketSessionList),
|
QSessionList(PacketSessionList),
|
||||||
RSessionList(PacketSessionListResponse),
|
RSessionList(PacketSessionListResponse),
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
#[derive(Clone, Copy)]
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use futures::stream::SplitSink;
|
||||||
|
use hyper::upgrade::Upgraded;
|
||||||
|
use hyper_util::rt::TokioIo;
|
||||||
|
use tokio_tungstenite::{tungstenite::Message, WebSocketStream};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct LocalPacketConnect {
|
pub struct LocalPacketConnect {
|
||||||
pub bus_id:u32,
|
pub bus_id:u32,
|
||||||
|
pub stream:Arc<RwLock<SplitSink<WebSocketStream<TokioIo<Upgraded>>, Message>>>,
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ pub struct PacketSessionListResponseRecord {
|
|||||||
pub token:SessionToken,
|
pub token:SessionToken,
|
||||||
pub handles:[String; 2],
|
pub handles:[String; 2],
|
||||||
pub turn:u16,
|
pub turn:u16,
|
||||||
pub last_move:[u8; 5],
|
pub last_move:[u8; 3],
|
||||||
pub viewers:u32,
|
pub viewers:u32,
|
||||||
pub player:bool,
|
pub player:bool,
|
||||||
}
|
}
|
||||||
|
244
server/src/system/filesystem/mod.rs
Normal file
244
server/src/system/filesystem/mod.rs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
use std::{
|
||||||
|
fs::{self, File}, io::{Read, Seek, SeekFrom, Write}, path::Path
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::session::Session,
|
||||||
|
app::user::User,
|
||||||
|
util::pack::{pack_u32, unpack_u32}
|
||||||
|
};
|
||||||
|
|
||||||
|
const HANDLE_BUCKET_MASK :u32 = 0xFF;
|
||||||
|
const HANDLE_BUCKET_SIZE :u32 = HANDLE_BUCKET_MASK + 1;
|
||||||
|
|
||||||
|
const DIR_DATA :&str = "data";
|
||||||
|
const DIR_HANDLE :&str = const_format::formatcp!("{}/h", DIR_DATA);
|
||||||
|
const DIR_SESSION :&str = const_format::formatcp!("{}/s", DIR_DATA);
|
||||||
|
const DIR_USER :&str = const_format::formatcp!("{}/u", DIR_DATA);
|
||||||
|
|
||||||
|
const INDEX_HANDLE :&str = const_format::formatcp!("{}/i.bin", DIR_HANDLE);
|
||||||
|
const INDEX_SESSION :&str = const_format::formatcp!("{}/i.bin", DIR_SESSION);
|
||||||
|
const INDEX_USER :&str = const_format::formatcp!("{}/i.bin", DIR_USER);
|
||||||
|
|
||||||
|
pub const FILE_SALT :&str = const_format::formatcp!("{}/x.bin", DIR_DATA);
|
||||||
|
|
||||||
|
pub struct FileSystem {
|
||||||
|
index_handle:File,
|
||||||
|
index_session:File,
|
||||||
|
index_user:File,
|
||||||
|
|
||||||
|
table_salt:File,
|
||||||
|
}
|
||||||
|
impl FileSystem {
|
||||||
|
pub fn init() -> Result<Self, std::io::Error>
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// TEMPORARY: REMOVE AFTER TESTING
|
||||||
|
//
|
||||||
|
//fs::remove_dir_all(DIR_DATA).ok();
|
||||||
|
|
||||||
|
// Initialize filesystem if does not exist.
|
||||||
|
//
|
||||||
|
// Note: does not currently check for corruption.
|
||||||
|
//
|
||||||
|
if !Path::new(DIR_DATA).exists() {
|
||||||
|
fs::create_dir(DIR_DATA)?;
|
||||||
|
fs::create_dir(DIR_HANDLE)?;
|
||||||
|
fs::create_dir(DIR_SESSION)?;
|
||||||
|
fs::create_dir(DIR_USER)?;
|
||||||
|
|
||||||
|
fs::write(INDEX_HANDLE, pack_u32(0))?;
|
||||||
|
fs::write(INDEX_SESSION, pack_u32(0))?;
|
||||||
|
fs::write(INDEX_USER, pack_u32(0))?;
|
||||||
|
|
||||||
|
fs::write(FILE_SALT, pack_u32(0))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let index_handle = File::options()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(INDEX_HANDLE)?;
|
||||||
|
let index_session = File::options()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(INDEX_SESSION)?;
|
||||||
|
let index_user = File::options()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(INDEX_USER)?;
|
||||||
|
|
||||||
|
let table_salt = File::options()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(FILE_SALT)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
index_handle,
|
||||||
|
index_session,
|
||||||
|
index_user,
|
||||||
|
|
||||||
|
table_salt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn session_store(&mut self, _session:&Session) -> Result<(),()>
|
||||||
|
{
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn user_store(&mut self, _user:&User) -> Result<(),()>
|
||||||
|
{
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn handle_store(&mut self, handle:&String, user_id:u32) -> Result<u32,()>
|
||||||
|
// Add a salt to store.
|
||||||
|
//
|
||||||
|
{
|
||||||
|
let size = self.handle_count()? as u32;
|
||||||
|
let data = handle.as_bytes();
|
||||||
|
let length = data.len();
|
||||||
|
|
||||||
|
let bucket_index = size & !HANDLE_BUCKET_MASK;
|
||||||
|
let record_index = size & HANDLE_BUCKET_MASK;
|
||||||
|
let record_offset = 12 * record_index as u64;
|
||||||
|
|
||||||
|
// Update size record
|
||||||
|
self.index_handle.seek(SeekFrom::Start(0)).map_err(|_| ())?;
|
||||||
|
self.index_handle.write(&pack_u32(size + 1)).map_err(|_| ())?;
|
||||||
|
|
||||||
|
// Create bucket file if not exists
|
||||||
|
let bucket_path = Path::new(DIR_HANDLE).join(format!("{:x}.bin", bucket_index));
|
||||||
|
if !bucket_path.exists() {
|
||||||
|
fs::write(bucket_path.clone(), vec![0u8; 12 * HANDLE_BUCKET_SIZE as usize]).map_err(|_| ())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open bucket file for record
|
||||||
|
if let Ok(mut file) = File::options().read(true).write(true).open(bucket_path) {
|
||||||
|
let offset = file.seek(SeekFrom::End(0)).map_err(|_| ())?;
|
||||||
|
|
||||||
|
// Write record header to table
|
||||||
|
let buffer_header = [
|
||||||
|
pack_u32(offset as u32),
|
||||||
|
pack_u32(length as u32),
|
||||||
|
pack_u32(user_id),
|
||||||
|
].concat();
|
||||||
|
|
||||||
|
file.seek(SeekFrom::Start(record_offset)).map_err(|_| ())?;
|
||||||
|
file.write(&buffer_header).map_err(|_| ())?;
|
||||||
|
|
||||||
|
|
||||||
|
// Write handle data to end of file
|
||||||
|
file.seek(SeekFrom::End(0)).map_err(|_| ())?;
|
||||||
|
file.write(data).map_err(|_| ())?;
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
} else { Err(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_fetch(&mut self, id:u32) -> Result<(String, u32),()>
|
||||||
|
// Retrieve a salt from store.
|
||||||
|
//
|
||||||
|
{
|
||||||
|
let size = self.salt_count()? as u32;
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
|
||||||
|
// Get location of handle by index
|
||||||
|
if id < size {
|
||||||
|
let bucket_index = id & !HANDLE_BUCKET_MASK;
|
||||||
|
let record_index = id & HANDLE_BUCKET_MASK;
|
||||||
|
let record_offset = 12 * record_index as u64;
|
||||||
|
|
||||||
|
let path = Path::new(DIR_HANDLE)
|
||||||
|
.join(format!("{:x}.bin", bucket_index));
|
||||||
|
|
||||||
|
// Read bucket file for record
|
||||||
|
if let Ok(mut file) = File::open(path) {
|
||||||
|
|
||||||
|
// Get record offset, length, and user association
|
||||||
|
let mut buffer_header = [0u8; 12];
|
||||||
|
file.seek(SeekFrom::Start(record_offset)).map_err(|_| ())?;
|
||||||
|
if file.read_exact(&mut buffer_header).is_ok() {
|
||||||
|
let offset = unpack_u32(&buffer_header, &mut 0);
|
||||||
|
let length = unpack_u32(&buffer_header, &mut 4);
|
||||||
|
let user_id = unpack_u32(&buffer_header, &mut 8);
|
||||||
|
|
||||||
|
if offset != 0 {
|
||||||
|
buffer.resize(length as usize, 0);
|
||||||
|
|
||||||
|
// Read handle data from offset
|
||||||
|
file.seek(SeekFrom::Start(offset as u64)).map_err(|_| ())?;
|
||||||
|
match file.read_exact(&mut buffer) {
|
||||||
|
Ok(_) => {
|
||||||
|
Ok((String::from_utf8(buffer).map_err(|_| ())?, user_id))
|
||||||
|
}
|
||||||
|
Err(e) => { println!("e: {}", e.to_string()); Err(()) }
|
||||||
|
}
|
||||||
|
} else { Err(()) }
|
||||||
|
} else { Err(()) }
|
||||||
|
} else { Err(()) }
|
||||||
|
} else { Err(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_count(&mut self) -> Result<usize, ()>
|
||||||
|
// Get number of salts in store.
|
||||||
|
//
|
||||||
|
{
|
||||||
|
Self::get_header_size(&mut self.index_handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn salt_store(&mut self, salt:[u8; 16]) -> Result<u32,()>
|
||||||
|
// Add a salt to store.
|
||||||
|
//
|
||||||
|
{
|
||||||
|
let size = self.salt_count()? as u32;
|
||||||
|
|
||||||
|
// Update size record
|
||||||
|
self.table_salt.seek(SeekFrom::Start(0)).map_err(|_| ())?;
|
||||||
|
self.table_salt.write(&pack_u32(size + 1)).map_err(|_| ())?;
|
||||||
|
|
||||||
|
// Write salt to store
|
||||||
|
self.table_salt.seek(SeekFrom::End(0)).map_err(|_| ())?;
|
||||||
|
self.table_salt.write(&salt).map_err(|_| ())?;
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn salt_fetch(&mut self, id:u32) -> Result<[u8; 16],()>
|
||||||
|
// Retrieve a salt from store.
|
||||||
|
//
|
||||||
|
{
|
||||||
|
let size = self.salt_count()? as u32;
|
||||||
|
let mut buffer = [0u8; 16];
|
||||||
|
|
||||||
|
if id < size {
|
||||||
|
let offset = 4 + (16 * id as u64);
|
||||||
|
|
||||||
|
self.table_salt.seek(SeekFrom::Start(offset)).map_err(|_| ())?;
|
||||||
|
if self.table_salt.read_exact(&mut buffer).is_ok() {
|
||||||
|
Ok(buffer)
|
||||||
|
} else { Err(()) }
|
||||||
|
} else { Err(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn salt_count(&mut self) -> Result<usize, ()>
|
||||||
|
// Get number of salts in store.
|
||||||
|
//
|
||||||
|
{
|
||||||
|
Self::get_header_size(&mut self.table_salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_header_size(file:&mut File) -> Result<usize, ()>
|
||||||
|
{
|
||||||
|
let mut size_buffer = [0u8; 4];
|
||||||
|
|
||||||
|
file.seek(SeekFrom::Start(0)).map_err(|_| ())?;
|
||||||
|
if file.read_exact(&mut size_buffer).is_ok() {
|
||||||
|
Ok(unpack_u32(&size_buffer, &mut 0) as usize)
|
||||||
|
} else { Err(()) }
|
||||||
|
}
|
||||||
|
}
|
@ -2,3 +2,4 @@
|
|||||||
|
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
|
pub mod filesystem;
|
||||||
|
@ -3,7 +3,7 @@ pub fn pack_u8(value:u8) -> Vec<u8>
|
|||||||
vec![value]
|
vec![value]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unpack_u8(data:&Vec<u8>, index:&mut usize) -> u8
|
pub fn unpack_u8(data:&[u8], index:&mut usize) -> u8
|
||||||
{
|
{
|
||||||
let mut result :u8 = 0;
|
let mut result :u8 = 0;
|
||||||
if *index < data.len() {
|
if *index < data.len() {
|
||||||
@ -18,7 +18,7 @@ pub fn pack_u16(value:u16) -> Vec<u8>
|
|||||||
vec![(value >> 8) as u8, (value & 0xFF) as u8]
|
vec![(value >> 8) as u8, (value & 0xFF) as u8]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unpack_u16(data:&Vec<u8>, index:&mut usize) -> u16
|
pub fn unpack_u16(data:&[u8], index:&mut usize) -> u16
|
||||||
{
|
{
|
||||||
let mut result :u16 = 0;
|
let mut result :u16 = 0;
|
||||||
if *index < data.len() {
|
if *index < data.len() {
|
||||||
@ -42,7 +42,7 @@ pub fn pack_u32(value:u32) -> Vec<u8>
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unpack_u32(data:&Vec<u8>, index:&mut usize) -> u32
|
pub fn unpack_u32(data:&[u8], index:&mut usize) -> u32
|
||||||
{
|
{
|
||||||
let mut result :u32 = 0;
|
let mut result :u32 = 0;
|
||||||
if *index < data.len() {
|
if *index < data.len() {
|
||||||
|
@ -11,13 +11,13 @@ let CONTEXT = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Status = {
|
const Status = {
|
||||||
Ok: 0,
|
Ok: 0x0000,
|
||||||
Error: 1,
|
Error: 0x0001,
|
||||||
NotImplement: 2,
|
NotImplemented: 0x0002,
|
||||||
|
|
||||||
BadHandle: 1,
|
BadHandle: 0x0010,
|
||||||
BadSecret: 2,
|
BadSecret: 0x0011,
|
||||||
BadCode: 3,
|
BadCode: 0x0012,
|
||||||
};
|
};
|
||||||
|
|
||||||
const OpCode = {
|
const OpCode = {
|
||||||
@ -29,8 +29,11 @@ const OpCode = {
|
|||||||
SessionList :0x0020,
|
SessionList :0x0020,
|
||||||
SessionCreate :0x0021,
|
SessionCreate :0x0021,
|
||||||
SessionJoin :0x0022,
|
SessionJoin :0x0022,
|
||||||
|
SessionRetire :0x002E,
|
||||||
|
SessionLeave :0x002F,
|
||||||
|
|
||||||
GameState :0x0030,
|
GameState :0x0030,
|
||||||
|
GamePlay :0x0031,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GameState = {
|
const GameState = {
|
||||||
|
351
www/js/game.js
351
www/js/game.js
@ -1,157 +1,212 @@
|
|||||||
class GamePieceMove {
|
const GAME_CONST = {
|
||||||
|
PLAYER_DAWN: 0,
|
||||||
|
PLAYER_DUSK: 1,
|
||||||
|
|
||||||
}
|
SOURCE_BOARD: 0,
|
||||||
|
SOURCE_POOL: 1,
|
||||||
class GamePiece {
|
|
||||||
constructor(name, assets, moves, promote_moves) {
|
|
||||||
this.name = name;
|
|
||||||
this.assets = assets;
|
|
||||||
this.moves = moves;
|
|
||||||
this.pmoves = promote_moves;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let GAME_DATA = {
|
|
||||||
board: {
|
|
||||||
tiles: [ ],
|
|
||||||
},
|
|
||||||
|
|
||||||
pieces: [
|
|
||||||
new PieceDef("Militia「兵」", "♟︎", // ♟︎士
|
|
||||||
["asset/militia_dusk.svg", "asset/militia_dawn.svg"],
|
|
||||||
new Move()
|
|
||||||
.add(0)
|
|
||||||
.add(1)
|
|
||||||
.add(5),
|
|
||||||
|
|
||||||
new Move()
|
|
||||||
.add(0)
|
|
||||||
.add(1)
|
|
||||||
.add(2)
|
|
||||||
.add(4)
|
|
||||||
.add(5)
|
|
||||||
),
|
|
||||||
new PieceDef("Knight「騎」", "♞", // ♞馬
|
|
||||||
["asset/knight_dusk.svg", "asset/knight_dawn.svg"],
|
|
||||||
new Move()
|
|
||||||
.add(3)
|
|
||||||
.add(6)
|
|
||||||
.add(11)
|
|
||||||
.add(13)
|
|
||||||
.add(17),
|
|
||||||
|
|
||||||
new Move()
|
|
||||||
.add(3)
|
|
||||||
.add(6)
|
|
||||||
.add(7)
|
|
||||||
.add(10)
|
|
||||||
.add(11)
|
|
||||||
.add(13)
|
|
||||||
.add(14)
|
|
||||||
.add(16)
|
|
||||||
.add(17)
|
|
||||||
),
|
|
||||||
new PieceDef("Lance「槍」", "♛", // ♛槍
|
|
||||||
["asset/lance_dusk.svg", "asset/lance_dawn.svg"],
|
|
||||||
new Move()
|
|
||||||
.add(0, true)
|
|
||||||
.add(1)
|
|
||||||
.add(5),
|
|
||||||
|
|
||||||
new Move()
|
|
||||||
.add(0, true)
|
|
||||||
.add(1, true)
|
|
||||||
.add(2, true)
|
|
||||||
.add(3, true)
|
|
||||||
.add(4, true)
|
|
||||||
.add(5, true)
|
|
||||||
),
|
|
||||||
new PieceDef("Tower「楼」", "♖", // ♖高
|
|
||||||
["asset/tower_dusk.svg", "asset/tower_dawn.svg"],
|
|
||||||
new Move()
|
|
||||||
.add(0)
|
|
||||||
.add(1)
|
|
||||||
.add(3)
|
|
||||||
.add(5)
|
|
||||||
.add(6)
|
|
||||||
.add(11),
|
|
||||||
|
|
||||||
new Move()
|
|
||||||
.add(0)
|
|
||||||
.add(1)
|
|
||||||
.add(2)
|
|
||||||
.add(3)
|
|
||||||
.add(4)
|
|
||||||
.add(5)
|
|
||||||
.add(6)
|
|
||||||
.add(8)
|
|
||||||
.add(9)
|
|
||||||
.add(11)
|
|
||||||
),
|
|
||||||
new PieceDef("Castle「城」", "♜", // ♜城
|
|
||||||
["asset/castle_dusk.svg", "asset/castle_dawn.svg"],
|
|
||||||
new Move()
|
|
||||||
.add(0)
|
|
||||||
.add(1)
|
|
||||||
.add(2)
|
|
||||||
.add(4)
|
|
||||||
.add(5)
|
|
||||||
.add(7)
|
|
||||||
.add(10),
|
|
||||||
|
|
||||||
new Move()
|
|
||||||
.add(0)
|
|
||||||
.add(1)
|
|
||||||
.add(2)
|
|
||||||
.add(3)
|
|
||||||
.add(4)
|
|
||||||
.add(5)
|
|
||||||
.add(7, true)
|
|
||||||
.add(10, true)
|
|
||||||
),
|
|
||||||
new PieceDef("Dragon「竜」", "♝", // ♝竜
|
|
||||||
["asset/dragon_dusk.svg", "asset/dragon_dawn.svg"],
|
|
||||||
new Move()
|
|
||||||
.add(6, true)
|
|
||||||
.add(7, true)
|
|
||||||
.add(8, true)
|
|
||||||
.add(9, true)
|
|
||||||
.add(10, true)
|
|
||||||
.add(11, true),
|
|
||||||
|
|
||||||
new Move()
|
|
||||||
.add(0, true)
|
|
||||||
.add(1, true)
|
|
||||||
.add(2, true)
|
|
||||||
.add(3, true)
|
|
||||||
.add(4, true)
|
|
||||||
.add(5, true)
|
|
||||||
.add(6, true)
|
|
||||||
.add(7, true)
|
|
||||||
.add(8, true)
|
|
||||||
.add(9, true)
|
|
||||||
.add(10, true)
|
|
||||||
.add(11, true)
|
|
||||||
),
|
|
||||||
new PieceDef("King「王」", "♚", // ♚王
|
|
||||||
["asset/king_dusk.svg", "asset/king_dawn.svg"],
|
|
||||||
new Move()
|
|
||||||
.add(0)
|
|
||||||
.add(1)
|
|
||||||
.add(2)
|
|
||||||
.add(3)
|
|
||||||
.add(4)
|
|
||||||
.add(5)
|
|
||||||
.add(7)
|
|
||||||
.add(10)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const GAME_CLASS = {
|
||||||
|
Board: class {
|
||||||
|
constructor() {
|
||||||
|
this.tiles = [ ]; for(let i = 0; i < 61; ++i) { this.tiles.push(new GAME_CLASS.Tile()); }
|
||||||
|
this.dawn = new GAME_CLASS.Player();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Player: class {
|
||||||
|
constructor() {
|
||||||
|
this.handle = "";
|
||||||
|
this.pool = new GAME_CLASS.Pool();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Pool: class {
|
||||||
|
constructor() {
|
||||||
|
this.pieces = [ ]; for(let i = 0; i < 6; ++i) { this.pieces.push(0); }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Tile: class {
|
||||||
|
constructor() {
|
||||||
|
this.piece = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Move: class {
|
||||||
|
constructor(source, from, to) {
|
||||||
|
this.source = source;
|
||||||
|
this.from = from;
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
GamePiece: class {
|
||||||
|
constructor(name, assets, moves, promote_moves) {
|
||||||
|
this.name = name;
|
||||||
|
this.assets = assets;
|
||||||
|
this.moves = moves;
|
||||||
|
this.pmoves = promote_moves;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Piece: class {
|
||||||
|
constructor(piece, player) {
|
||||||
|
this.piece = piece;
|
||||||
|
this.player = player;
|
||||||
|
this.promoted = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Game: class {
|
||||||
|
constructor() {
|
||||||
|
this.board = new GAME_CLASS.Board();
|
||||||
|
this.pieces = [
|
||||||
|
new GAME_CLASS.GamePiece("Militia",
|
||||||
|
["asset/militia_dusk.svg", "asset/militia_dawn.svg"],
|
||||||
|
new Move()
|
||||||
|
.add(0)
|
||||||
|
.add(1)
|
||||||
|
.add(5),
|
||||||
|
|
||||||
|
new Move()
|
||||||
|
.add(0)
|
||||||
|
.add(1)
|
||||||
|
.add(2)
|
||||||
|
.add(4)
|
||||||
|
.add(5)
|
||||||
|
),
|
||||||
|
new GAME_CLASS.GamePiece("Knight",
|
||||||
|
["asset/knight_dusk.svg", "asset/knight_dawn.svg"],
|
||||||
|
new Move()
|
||||||
|
.add(3)
|
||||||
|
.add(6)
|
||||||
|
.add(11)
|
||||||
|
.add(13)
|
||||||
|
.add(17),
|
||||||
|
|
||||||
|
new Move()
|
||||||
|
.add(3)
|
||||||
|
.add(6)
|
||||||
|
.add(7)
|
||||||
|
.add(10)
|
||||||
|
.add(11)
|
||||||
|
.add(13)
|
||||||
|
.add(14)
|
||||||
|
.add(16)
|
||||||
|
.add(17)
|
||||||
|
),
|
||||||
|
new GAME_CLASS.GamePiece("Lance",
|
||||||
|
["asset/lance_dusk.svg", "asset/lance_dawn.svg"],
|
||||||
|
new Move()
|
||||||
|
.add(0, true)
|
||||||
|
.add(1)
|
||||||
|
.add(5),
|
||||||
|
|
||||||
|
new Move()
|
||||||
|
.add(0, true)
|
||||||
|
.add(1, true)
|
||||||
|
.add(2, true)
|
||||||
|
.add(3, true)
|
||||||
|
.add(4, true)
|
||||||
|
.add(5, true)
|
||||||
|
),
|
||||||
|
new GAME_CLASS.GamePiece("Tower",
|
||||||
|
["asset/tower_dusk.svg", "asset/tower_dawn.svg"],
|
||||||
|
new Move()
|
||||||
|
.add(0)
|
||||||
|
.add(1)
|
||||||
|
.add(3)
|
||||||
|
.add(5)
|
||||||
|
.add(6)
|
||||||
|
.add(11),
|
||||||
|
|
||||||
|
new Move()
|
||||||
|
.add(0)
|
||||||
|
.add(1)
|
||||||
|
.add(2)
|
||||||
|
.add(3)
|
||||||
|
.add(4)
|
||||||
|
.add(5)
|
||||||
|
.add(6)
|
||||||
|
.add(8)
|
||||||
|
.add(9)
|
||||||
|
.add(11)
|
||||||
|
),
|
||||||
|
new GAME_CLASS.GamePiece("Castle",
|
||||||
|
["asset/castle_dusk.svg", "asset/castle_dawn.svg"],
|
||||||
|
new Move()
|
||||||
|
.add(0)
|
||||||
|
.add(1)
|
||||||
|
.add(2)
|
||||||
|
.add(4)
|
||||||
|
.add(5)
|
||||||
|
.add(7)
|
||||||
|
.add(10),
|
||||||
|
|
||||||
|
new Move()
|
||||||
|
.add(0)
|
||||||
|
.add(1)
|
||||||
|
.add(2)
|
||||||
|
.add(3)
|
||||||
|
.add(4)
|
||||||
|
.add(5)
|
||||||
|
.add(7, true)
|
||||||
|
.add(10, true)
|
||||||
|
),
|
||||||
|
new GAME_CLASS.GamePiece("Dragon",
|
||||||
|
["asset/dragon_dusk.svg", "asset/dragon_dawn.svg"],
|
||||||
|
new Move()
|
||||||
|
.add(6, true)
|
||||||
|
.add(7, true)
|
||||||
|
.add(8, true)
|
||||||
|
.add(9, true)
|
||||||
|
.add(10, true)
|
||||||
|
.add(11, true),
|
||||||
|
|
||||||
|
new Move()
|
||||||
|
.add(0, true)
|
||||||
|
.add(1, true)
|
||||||
|
.add(2, true)
|
||||||
|
.add(3, true)
|
||||||
|
.add(4, true)
|
||||||
|
.add(5, true)
|
||||||
|
.add(6, true)
|
||||||
|
.add(7, true)
|
||||||
|
.add(8, true)
|
||||||
|
.add(9, true)
|
||||||
|
.add(10, true)
|
||||||
|
.add(11, true)
|
||||||
|
),
|
||||||
|
new GAME_CLASS.GamePiece("Omen",
|
||||||
|
["asset/king_dusk.svg", "asset/king_dawn.svg"],
|
||||||
|
new Move()
|
||||||
|
.add(0)
|
||||||
|
.add(1)
|
||||||
|
.add(2)
|
||||||
|
.add(3)
|
||||||
|
.add(4)
|
||||||
|
.add(5)
|
||||||
|
.add(7)
|
||||||
|
.add(10)
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let GAME_DATA = null;
|
||||||
|
|
||||||
const GAME = {
|
const GAME = {
|
||||||
init() {
|
init() {
|
||||||
GAME_DATA.board.tiles
|
GAME_DATA = new GAME_CLASS.Game();
|
||||||
|
},
|
||||||
|
|
||||||
|
process(move) {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
validate(move) {
|
||||||
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -53,7 +53,11 @@ const INTERFACE = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
GAME.init();
|
||||||
|
|
||||||
INTERFACE_DATA.canvas = document.getElementById("game");
|
INTERFACE_DATA.canvas = document.getElementById("game");
|
||||||
|
let canvas = INTERFACE_DATA.canvas;
|
||||||
|
|
||||||
if(canvas !== undefined) {
|
if(canvas !== undefined) {
|
||||||
INTERFACE_DATA.context = canvas.getContext("2d");
|
INTERFACE_DATA.context = canvas.getContext("2d");
|
||||||
|
|
||||||
@ -68,4 +72,8 @@ const INTERFACE = {
|
|||||||
INTERFACE_DATA.canvas = null;
|
INTERFACE_DATA.canvas = null;
|
||||||
INTERFACE_DATA.context = null;
|
INTERFACE_DATA.context = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
message(data) {
|
||||||
|
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
SCENE = SCENES.Offline;
|
SCENE = SCENES.Offline;
|
||||||
LOAD(SCENES.Init);
|
LOAD(SCENES.Init);
|
||||||
|
|
||||||
|
document.addEventListener("beforeunload", UNLOAD);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const SCENES = {
|
const SCENES = {
|
||||||
Init:{
|
Init:{
|
||||||
load() {
|
load() {
|
||||||
LOAD_OFFLINE();
|
LOAD_STACK(SCENES.Offline);
|
||||||
CONTEXT.Scene = SCENES.Online;
|
CONTEXT.Scene = SCENES.Online;
|
||||||
RECONNECT();
|
RECONNECT();
|
||||||
return true;
|
return true;
|
||||||
@ -210,11 +210,13 @@ const SCENES = {
|
|||||||
MESSAGE_SESSION_LIST(0, 0, false, false);
|
MESSAGE_SESSION_LIST(0, 0, false, false);
|
||||||
},
|
},
|
||||||
message(code, data) {
|
message(code, data) {
|
||||||
let table = document.getElementById("content");
|
if(code == OpCode.SessionList) {
|
||||||
UI.clear(table);
|
let table = document.getElementById("content");
|
||||||
|
UI.clear(table);
|
||||||
|
|
||||||
if(data !== null) {
|
if(data !== null) {
|
||||||
table.appendChild(UI.session_table(data.records));
|
table.appendChild(UI.session_table(data.records));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -250,6 +252,16 @@ const SCENES = {
|
|||||||
refresh() {
|
refresh() {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
message(code, data) {
|
||||||
|
if(code == OpCode.SessionList) {
|
||||||
|
let table = document.getElementById("content");
|
||||||
|
UI.clear(table);
|
||||||
|
|
||||||
|
if(data !== null) {
|
||||||
|
table.appendChild(UI.session_table(data.records));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Join:{
|
Join:{
|
||||||
@ -283,6 +295,16 @@ const SCENES = {
|
|||||||
refresh() {
|
refresh() {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
message(code, data) {
|
||||||
|
if(code == OpCode.SessionList) {
|
||||||
|
let table = document.getElementById("content");
|
||||||
|
UI.clear(table);
|
||||||
|
|
||||||
|
if(data !== null) {
|
||||||
|
table.appendChild(UI.session_table(data.records));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Live:{
|
Live:{
|
||||||
@ -315,6 +337,16 @@ const SCENES = {
|
|||||||
refresh() {
|
refresh() {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
message(code, data) {
|
||||||
|
if(code == OpCode.SessionList) {
|
||||||
|
let table = document.getElementById("content");
|
||||||
|
UI.clear(table);
|
||||||
|
|
||||||
|
if(data !== null) {
|
||||||
|
table.appendChild(UI.session_table(data.records));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
History:{
|
History:{
|
||||||
@ -342,12 +374,25 @@ const SCENES = {
|
|||||||
refresh() {
|
refresh() {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
message(code, data) {
|
||||||
|
if(code == OpCode.SessionList) {
|
||||||
|
let table = document.getElementById("content");
|
||||||
|
UI.clear(table);
|
||||||
|
|
||||||
|
if(data !== null) {
|
||||||
|
table.appendChild(UI.session_table(data.records));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Guide:{
|
Guide:{
|
||||||
load() {
|
load() {
|
||||||
UI.mainmenu();
|
UI.mainmenu();
|
||||||
UI.mainnav([], []);
|
UI.mainnav([], []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
refresh() {
|
refresh() {
|
||||||
@ -359,6 +404,9 @@ const SCENES = {
|
|||||||
load() {
|
load() {
|
||||||
UI.mainmenu();
|
UI.mainmenu();
|
||||||
UI.mainnav([], []);
|
UI.mainnav([], []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
refresh() {
|
refresh() {
|
||||||
@ -377,7 +425,6 @@ const SCENES = {
|
|||||||
|
|
||||||
let canvas = document.createElement("canvas");
|
let canvas = document.createElement("canvas");
|
||||||
canvas.setAttribute("id", "game");
|
canvas.setAttribute("id", "game");
|
||||||
|
|
||||||
MAIN.appendChild(canvas);
|
MAIN.appendChild(canvas);
|
||||||
|
|
||||||
INTERFACE.init();
|
INTERFACE.init();
|
||||||
@ -385,29 +432,33 @@ const SCENES = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
unload() {
|
unload() {
|
||||||
|
MESSAGE_COMPOSE([
|
||||||
|
PACK.u16(OpCode.SessionLeave),
|
||||||
|
]);
|
||||||
},
|
},
|
||||||
refresh() {
|
message(code, data) {
|
||||||
|
if(code == OpCode.GameState || code == OpCode.GamePlay) {
|
||||||
},
|
INTERFACE.message(code, data);
|
||||||
message() {
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function LOAD(scene) {
|
function LOAD(scene) {
|
||||||
if(SCENE.unload !== undefined) { SCENE.unload(); }
|
UNLOAD();
|
||||||
UI.rebuild();
|
UI.rebuild();
|
||||||
SCENE = scene;
|
SCENE = scene;
|
||||||
CONTEXT.Scene = SCENE;
|
CONTEXT.Scene = SCENE;
|
||||||
if(!SCENE.load()) { LOAD(SCENES.Online); }
|
if(!SCENE.load()) { LOAD(SCENES.Online); }
|
||||||
}
|
}
|
||||||
|
|
||||||
function LOAD_OFFLINE() {
|
function UNLOAD() {
|
||||||
if(SCENE.unload !== undefined) { SCENE.unload(); }
|
if(SCENE !== null && SCENE.unload !== undefined) { SCENE.unload(); }
|
||||||
while(document.body.lastChild !== null) { document.body.removeChild(document.body.lastChild); }
|
}
|
||||||
|
|
||||||
|
function LOAD_STACK(scene) {
|
||||||
|
UNLOAD();
|
||||||
UI.rebuild();
|
UI.rebuild();
|
||||||
SCENE = SCENES.Offline;
|
SCENE = scene;
|
||||||
if(!SCENE.load()) { LOAD(SCENES.Online); }
|
if(!SCENE.load()) { LOAD(SCENES.Online); }
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ function RECONNECT() {
|
|||||||
SOCKET.binaryType = "arraybuffer";
|
SOCKET.binaryType = "arraybuffer";
|
||||||
SOCKET.addEventListener("error", (event) => {
|
SOCKET.addEventListener("error", (event) => {
|
||||||
SOCKET = null;
|
SOCKET = null;
|
||||||
LOAD_OFFLINE()
|
LOAD_STACK(SCENES.Offline);
|
||||||
});
|
});
|
||||||
SOCKET.addEventListener("open", (event) => {
|
SOCKET.addEventListener("open", (event) => {
|
||||||
if(SOCKET.readyState === WebSocket.OPEN) {
|
if(SOCKET.readyState === WebSocket.OPEN) {
|
||||||
|
@ -84,8 +84,8 @@ const UI = {
|
|||||||
let left = document.createElement("section");
|
let left = document.createElement("section");
|
||||||
|
|
||||||
if(CONTEXT.Auth === null) {
|
if(CONTEXT.Auth === null) {
|
||||||
left.appendChild(UI.button("Register", () => { LOAD(SCENES.Register) }));
|
left.appendChild(UI.button("Register", () => { LOAD_STACK(SCENES.Register) }));
|
||||||
left.appendChild(UI.button("Log In", () => { LOAD(SCENES.Authenticate) }));
|
left.appendChild(UI.button("Log In", () => { LOAD_STACK(SCENES.Authenticate) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(child of left_children) { left.appendChild(child); }
|
for(child of left_children) { left.appendChild(child); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user