353 lines
12 KiB
Rust
353 lines
12 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use sparse::Sparse;
|
|
use pool::Pool;
|
|
use trie::Trie;
|
|
use crate::{
|
|
protocol::QRPacket,
|
|
system::{
|
|
filesystem::FileSystem,
|
|
log::Log,
|
|
},
|
|
util::Chain,
|
|
};
|
|
|
|
pub mod connection; use connection::Connection;
|
|
pub mod user; use user::User;
|
|
pub mod authentication; use authentication::Authentication;
|
|
pub mod session; use session::{Session, SessionToken};
|
|
pub mod invitation; pub use invitation::{Invitation, InviteToken};
|
|
pub mod context;
|
|
|
|
pub struct App {
|
|
pub filesystem:FileSystem,
|
|
pub log:Log,
|
|
|
|
pub connections:Pool<Connection>,
|
|
|
|
pub users:Pool<User>,
|
|
pub user_id:Sparse<usize>,
|
|
pub user_handle:Trie<u32>,
|
|
pub salts:Sparse<[u8; 16]>,
|
|
|
|
pub auths:Trie<Authentication>,
|
|
pub sessions:Trie<Session>,
|
|
|
|
pub invite_tokens:Trie<u32>,
|
|
pub invites:Pool<Invitation>,
|
|
|
|
pub contests:Vec<u32>,
|
|
|
|
pub session_time:Chain<SessionToken>,
|
|
}
|
|
impl App {
|
|
pub fn init() -> Result<Self, ()>
|
|
{
|
|
if let Ok(mut filesystem) = FileSystem::init() {
|
|
|
|
let mut contests = Vec::new();
|
|
|
|
// Load salts
|
|
println!("Loading salts..");
|
|
let mut salts = Sparse::new();
|
|
let salt_count = filesystem.salt_count()?;
|
|
for id in 0..salt_count {
|
|
let salt = filesystem.salt_fetch(id as u32).unwrap();
|
|
salts.set(id as isize, salt);
|
|
}
|
|
|
|
// Load handles
|
|
println!("Loading handles..");
|
|
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();
|
|
user_handle.set(handle.as_bytes(), user_id);
|
|
}
|
|
|
|
// Load users
|
|
println!("Loading users..");
|
|
let mut users = Pool::new();
|
|
let mut user_id = Sparse::new();
|
|
let user_count = filesystem.user_count()?;
|
|
for id in 0..user_count {
|
|
let user = filesystem.user_fetch(id as u32).unwrap();
|
|
|
|
// Add user to contests if flag is set.
|
|
if (user.flags & user::FC_ENABLE) != 0 {
|
|
match contests.binary_search(&user.id) {
|
|
Ok(_) => { }
|
|
Err(pos) => { contests.insert(pos, user.id); }
|
|
}
|
|
}
|
|
|
|
let user_local_id = users.add(user);
|
|
user_id.set(user_local_id as isize, id);
|
|
}
|
|
|
|
// Load sessions
|
|
println!("Loading sessions..");
|
|
let mut sessions = Trie::new();
|
|
let mut times = Vec::<(u64, SessionToken)>::new();
|
|
let session_count = filesystem.session_count()?;
|
|
for id in 0..session_count {
|
|
let mut session = filesystem.session_fetch(id as u32).unwrap();
|
|
|
|
times.push((session.time, session.token.clone()));
|
|
|
|
// Load session history
|
|
if let Ok(history) = filesystem.session_history_fetch(id as u32) {
|
|
session.game.apply_history(&history).ok();
|
|
}
|
|
|
|
sessions.set(&session.token.clone(), session);
|
|
}
|
|
|
|
// Organize sessions by most recent
|
|
let mut session_time = Chain::new();
|
|
times.sort_by(|(a, _), (b, _)| {
|
|
if a > b { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Less }
|
|
});
|
|
for (_, token) in times {
|
|
let id = session_time.add(token);
|
|
sessions.get_mut(&token).unwrap().chain_id = id;
|
|
}
|
|
|
|
println!("App data ready.");
|
|
Ok(Self {
|
|
filesystem:filesystem,
|
|
log:Log::new(),
|
|
|
|
connections:Pool::new(),
|
|
|
|
users,
|
|
user_id,
|
|
user_handle,
|
|
salts,
|
|
|
|
auths:Trie::new(),
|
|
sessions,
|
|
|
|
invite_tokens:Trie::new(),
|
|
invites:Pool::new(),
|
|
|
|
contests,
|
|
|
|
session_time,
|
|
})
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
|
|
pub fn get_user_by_id(&self, id:u32) -> Option<&User>
|
|
{
|
|
if let Some(uid) = self.user_id.get(id as isize) {
|
|
self.users.get(*uid)
|
|
} else { None }
|
|
}
|
|
|
|
pub fn get_user_by_id_mut(&mut self, id:u32) -> Option<&mut User>
|
|
{
|
|
if let Some(uid) = self.user_id.get(id as isize) {
|
|
self.users.get_mut(*uid)
|
|
} else { None }
|
|
}
|
|
|
|
pub async fn send_response(&mut self, response:QRPacket)
|
|
{
|
|
use crate::protocol::*;
|
|
use tokio_tungstenite::tungstenite::Message;
|
|
use futures::SinkExt;
|
|
|
|
match response.data {
|
|
QRPacketData::None => { }
|
|
_ => {
|
|
if let Some(conn) = self.connections.get(response.id as usize) {
|
|
let mut socket = conn.stream.write().await;
|
|
|
|
match response.data {
|
|
QRPacketData::RHello(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_HELLO, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
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::RSessionView(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_SESSION_VIEW, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::RGameState(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_GAME_STATE, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::GameMessage(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_GAME_MESSAGE, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::RChallengeAnswer(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_CHALLENGE_ANSWER, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::RChallengeList(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_CHALLENGE_LIST, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::RUserList(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_USER_LIST, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::RUserProfile(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_USER_PROFILE, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::RInviteList(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_INVITE_LIST, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::RInviteAcquire(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_INVITE_ACQUIRE, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
QRPacketData::TestResult(response) => {
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_TEST_RESULT, response.encode())
|
|
)).await.ok();
|
|
}
|
|
|
|
_ => { }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn send_session_spectators(&mut self, token:SessionToken)
|
|
{
|
|
use crate::protocol::*;
|
|
|
|
let mut packets = Vec::new();
|
|
|
|
if let Some(session) = self.sessions.get_mut(&token) {
|
|
for (cid, _) in session.get_connections() {
|
|
packets.push(QRPacket::new(
|
|
cid,
|
|
QRPacketData::GameMessage(PacketGameMessage {
|
|
data: GameMessageData::Online(
|
|
session.p_dawn.connections.len() > 0,
|
|
session.p_dusk.connections.len() > 0,
|
|
session.connections.len() as u32,
|
|
),
|
|
test: false,
|
|
expected: false,
|
|
}),
|
|
));
|
|
}
|
|
}
|
|
|
|
for packet in packets {
|
|
self.send_response(packet).await;
|
|
}
|
|
}
|
|
|
|
pub async fn send_status(&mut self, user_id:u32)
|
|
{
|
|
use crate::protocol::*;
|
|
use tokio_tungstenite::tungstenite::Message;
|
|
use futures::SinkExt;
|
|
|
|
let mut response = PacketSummaryResponse::new();
|
|
|
|
if let Some(user) = self.get_user_by_id(user_id).cloned() {
|
|
|
|
// Get challenges
|
|
response.challenge = if user.challenges.len() < 10 { user.challenges.len() as u16 } else { 10 };
|
|
|
|
// Get awaiting sessions
|
|
let mut next_id = self.session_time.begin();
|
|
while let Some(id) = next_id {
|
|
let token = self.session_time.get(id).unwrap();
|
|
if let Some(session) = self.sessions.get(token) {
|
|
|
|
// Add to resume if session has user and is user turn.
|
|
let is_turn_player = (session.p_dawn.user == user_id && (session.game.turn & 1) == 0) || (session.p_dusk.user == user_id && (session.game.turn & 1) == 1);
|
|
if !session.game.is_complete() && is_turn_player {
|
|
response.resume += 1;
|
|
}
|
|
|
|
if response.resume >= 10 { break; }
|
|
next_id = self.session_time.next(id);
|
|
}
|
|
}
|
|
|
|
// Send packet to connections
|
|
if let Some(end_conn_id) = user.connection {
|
|
let mut conn_id = end_conn_id;
|
|
|
|
loop {
|
|
if let Some(conn) = self.connections.get_mut(conn_id as usize) {
|
|
let mut socket = conn.stream.write().await;
|
|
|
|
socket.send(Message::Binary(
|
|
encode_response(CODE_SUMMARY, response.encode())
|
|
)).await.ok();
|
|
|
|
conn_id = conn.next;
|
|
if conn_id == end_conn_id { break; }
|
|
} else { break; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn encode_response(code:u16, data:Vec<u8>) -> Vec<u8>
|
|
{
|
|
[
|
|
crate::util::pack::pack_u16(code),
|
|
data,
|
|
].concat()
|
|
}
|