From 46218c06a96bcafb08cd18349edc8a5b7e451604 Mon Sep 17 00:00:00 2001 From: ghostlyzsh Date: Thu, 1 Aug 2024 01:10:46 -0500 Subject: [PATCH] calculator parser kabel --- Cargo.lock | 4 + kabel/grammar.ebnf | 9 +- kabel/src/error.rs | 68 ++++++ kabel/src/lexer.rs | 133 +++++++++++ kabel/src/lib.rs | 18 ++ kabel/src/macros.rs | 13 + kabel/src/main.rs | 3 - kabel/src/parser.rs | 226 ++++++++++++++++++ server/src/main.rs | 19 +- server/src/module/component.rs | 4 +- server/src/module/mod.rs | 127 ++++++++-- server/src/player/client_login.rs | 135 ++++++++--- server/src/player/mod.rs | 95 ++++++-- server/src/player/player_mouse_input.rs | 82 +++++-- server/src/player/request_save.rs | 31 ++- server/src/player/send_message.rs | 18 +- ....timestamp-1722468930578-9a55b81119f46.mjs | 39 +++ 17 files changed, 885 insertions(+), 139 deletions(-) create mode 100644 kabel/src/error.rs create mode 100644 kabel/src/lexer.rs create mode 100644 kabel/src/lib.rs create mode 100644 kabel/src/macros.rs delete mode 100644 kabel/src/main.rs create mode 100644 kabel/src/parser.rs create mode 100644 starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs diff --git a/Cargo.lock b/Cargo.lock index 9f00dda3343d3fd748c13dab2ed00f0069495922..0a2b89d18f408bc5134606d8d098f2d2ddd456ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2462,6 +2462,10 @@ dependencies = [ "sha2", ] +[[package]] +name = "kabel" +version = "0.1.0" + [[package]] name = "khronos-egl" version = "6.0.0" diff --git a/kabel/grammar.ebnf b/kabel/grammar.ebnf index 4da81ebb155aacd0544c5b952bb644baeb87061b..e45e9faf665a51b38da0e4d1dc8504e892bd6b8c 100644 --- a/kabel/grammar.ebnf +++ b/kabel/grammar.ebnf @@ -12,16 +12,17 @@ expression = assignment | declaration ; declaration = "var" , identifier , "=" , expression ; -assignment = { identifier , "=" , } logical_and ; +assignment = { identifier , "=" , } logical_or ; -logical_and = logical_or { , "||" , logical_or } ; +logical_or = logical_and { , "||" , logical_and } ; -logical_or = equality { , "&&" , equality } ; +logical_and = equality { , "&&" , equality } ; equality = comparison { , ( "==" | "!=" ) , comparison } ; comparison = term { , ( ">" | "<" | ">=" | "<=" ) , term } ; +(* implemented *) term = factor { , ( "+" | "-" ) , factor } ; factor = primary { , ( "*" | "/" ) , primary } ; @@ -31,5 +32,5 @@ primary = identifier | number | string | group ; group = "(" , expression , ")" ; identifier = alphabetic , { alphabetic | digit } ; -string = ( '"' | "'" ) , { character - ( '"' | "'" ) } , ( '"' | "'" ) ; +string = '"' , { character - '"' } , '"' ; number = digit , { digit } , [ "." , digit , { digit } ] ; diff --git a/kabel/src/error.rs b/kabel/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e8d99327483b5fe55eedee8ed125a9f86f3e3b5 --- /dev/null +++ b/kabel/src/error.rs @@ -0,0 +1,68 @@ +#[derive(Debug, Clone)] +pub struct KabelError { + pub kind: ErrorKind, + pub message: String, + pub line: usize, + pub column: usize, + pub code: String, +} + +impl KabelError { + pub fn new(kind: ErrorKind, message: String, line: usize, + column: usize, code: String) -> Self { + Self { + kind, + message, + line, + column, + code, + } + } +} + +impl std::fmt::Display for KabelError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let caret_space: String = vec![' '; self.column-1].iter().collect(); + f.write_str(& + format!("Error {:0>4}: {1} at line {2}, column {3}\n\ + {4}\n\ + {5}^", + self.kind.clone() as usize, self.message, self.line+1, self.column, + self.code, caret_space)) + } +} + +impl std::error::Error for KabelError {} + + +#[derive(Debug, Clone)] +pub enum ErrorKind { + UnexpectedEof, + UnexpectedCharacter, + UnexpectedToken, + MissingDelimiter, +} + +impl std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use ErrorKind::*; + match self { + UnexpectedEof => f.write_str("Unexpected End of File"), + UnexpectedCharacter => f.write_str("Unrecognized Charcter"), + UnexpectedToken => f.write_str("Unrecognized Token"), + MissingDelimiter => f.write_str("Missing delimiter"), + } + } +} + +impl From for usize { + fn from(value: ErrorKind) -> Self { + use ErrorKind::*; + match value { + UnexpectedEof => 0x00, + UnexpectedCharacter => 0x01, + UnexpectedToken => 0x02, + MissingDelimiter => 0x03, + } + } +} diff --git a/kabel/src/lexer.rs b/kabel/src/lexer.rs new file mode 100644 index 0000000000000000000000000000000000000000..73663d0fe81da8bff4cf38399b404698366201ca --- /dev/null +++ b/kabel/src/lexer.rs @@ -0,0 +1,133 @@ +use std::str::from_utf8; + +use crate::{error::{ErrorKind, KabelError}, token}; + +pub struct Lexer { + input: Vec, + start: usize, + current: usize, + line: usize, + line_start: usize, + column: usize, + c: u8, + pub errors: Vec, + pub output: Vec, +} + +impl Lexer { + pub fn new(input: String) -> Self { + Self { + input: input.as_bytes().to_vec(), + start: 0, + current: 0, + line: 0, + line_start: 0, + column: 0, + c: 0x00, + errors: Vec::new(), + output: Vec::new(), + } + } + + pub fn next_token(&mut self) -> bool { + self.read_char(); + match self.c { + b'+' => { self.output.push(token!(self, TokenType::Plus)); self.start = self.current; }, + b'-' => { self.output.push(token!(self, TokenType::Minus)); self.start = self.current; }, + b'*' => { self.output.push(token!(self, TokenType::Star)); self.start = self.current; }, + b'/' => { self.output.push(token!(self, TokenType::Slash)); self.start = self.current; }, + b'(' => { self.output.push(token!(self, TokenType::LeftParen)); self.start = self.current; }, + b')' => { self.output.push(token!(self, TokenType::RightParen)); self.start = self.current; }, + b'"' => { + let mut contents = String::new(); + while self.read_char() != b'"' { + if self.c == 0x05 { + self.errors.push(KabelError::new(ErrorKind::UnexpectedEof, + "File ended before closing quote".to_string(), + self.line, self.column, from_utf8(&self.input[self.start..self.current]).unwrap().to_string())); + return false; + } + contents.push(self.c as char); + } + self.start = self.current; + self.output.push(token!(self, TokenType::Str(contents))); + } + b'\n' => { + self.line += 1; + self.line_start = self.current; + self.column = 0; + } + b' ' | b'\r' | b'\t' => { self.start = self.current; } + 0x05 => return false, + c => { + if c.is_ascii_alphabetic() { + let mut content = (c as char).to_string(); + while self.peek().is_ascii_alphanumeric() || self.c == b'_' { + content.push(self.c as char); + self.read_char(); + } + self.output.push(token!(self, TokenType::Ident(content))); + } else if c.is_ascii_digit() { + let mut number = (c as char).to_string(); + while self.peek().is_ascii_digit() { + number.push(self.c as char); + self.read_char(); + } + if self.c == b'.' { + number.push('.'); + while self.read_char().is_ascii_digit() { + number.push(self.c as char); + } + } + /*self.current -= 1; + self.column -= 1;*/ + // panic = error in this code + self.output.push(token!(self, TokenType::Num(number.parse().unwrap()))); + self.start = self.current; + } else { + self.errors.push(KabelError::new(ErrorKind::UnexpectedToken, + format!("Stray \"{0}\"", c as char), + self.line, self.column, + from_utf8(&self.input[self.line_start..self.current]).unwrap().to_string())); + } + } + } + true + } + + pub fn read_char(&mut self) -> u8 { + if self.current >= self.input.len() { + self.c = 0x05; // EOF + return self.c; + } + self.c = self.input[self.current]; + self.current += 1; + self.column += 1; + return self.c; + } + pub fn peek(&mut self) -> u8 { + if self.current >= self.input.len() { + self.c = 0x05; // EOF + return self.c; + } + self.c = self.input[self.current]; + return self.c; + } +} + +#[derive(Debug, Clone)] +pub struct Token { + pub token_type: TokenType, + pub column: usize, + pub line: usize, + pub line_start: usize, + pub start: usize, + pub end: usize, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum TokenType { + Star, Slash, Plus, Minus, LeftParen, RightParen, + + Ident(String), Str(String), Num(f32), +} diff --git a/kabel/src/lib.rs b/kabel/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..7cd58e51f46a28f457c8cbb8abf02208aa424526 --- /dev/null +++ b/kabel/src/lib.rs @@ -0,0 +1,18 @@ +use lexer::{Lexer, Token}; +use parser::{Parser, AST}; + +pub mod lexer; +pub mod parser; +pub mod macros; +pub mod error; + +pub fn run_lexer(input: String) -> Lexer { + let mut lexer = Lexer::new(input); + while lexer.next_token() {} + lexer +} + +pub fn run_parser(text:String, input: Vec) -> (AST, Parser) { + let mut parser = Parser::new(text, input); + (parser.program(), parser) +} diff --git a/kabel/src/macros.rs b/kabel/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..85a715ab8f99d00705c7241284311a8a056fc4fb --- /dev/null +++ b/kabel/src/macros.rs @@ -0,0 +1,13 @@ +#[macro_export] +macro_rules! token { + ($self:expr, $token:expr) => { + crate::lexer::Token { + line: $self.line, + line_start: $self.line_start, + column: $self.column, + start: $self.start, + end: $self.current, + token_type: $token, + } + }; +} diff --git a/kabel/src/main.rs b/kabel/src/main.rs deleted file mode 100644 index e7a11a969c037e00a796aafeff6258501ec15e9a..0000000000000000000000000000000000000000 --- a/kabel/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/kabel/src/parser.rs b/kabel/src/parser.rs new file mode 100644 index 0000000000000000000000000000000000000000..3749db901b6dc804a7aa75688af0137cee150263 --- /dev/null +++ b/kabel/src/parser.rs @@ -0,0 +1,226 @@ +use crate::{error::{ErrorKind, KabelError}, lexer::{Token, TokenType}}; + +pub struct Parser { + input: Vec, + text: String, + //start: usize, + current: usize, + token: Token, + pub errors: Vec, +} + +impl Parser { + pub fn new(text: String, input: Vec) -> Self { + Self { + input: input.clone(), + text, + //start: 0, + current: 0, + token: input[0].clone(), + errors: Vec::new(), + } + } + + pub fn program(&mut self) -> AST { + let mut program = Vec::new(); + loop { + if self.current >= self.input.len() { + break; + } + match self.expression() { + Ok(ast) => program.push(ast), + Err(e) => self.errors.push(e), + } + break; + } + AST { + ast_type: ASTType::Program(program), + start: 0, + end: 0, + line: 0, + column: 0, + } + } + + pub fn expression(&mut self) -> Result { + let term = self.term()?; + Ok(term) + } + + pub fn term(&mut self) -> Result { + let mut left = self.factor()?; + + while self.current < self.input.len() && + (self.peek()?.token_type == TokenType::Plus || self.peek()?.token_type == TokenType::Minus) { + let binop = self.read_token()?; + let right = self.factor()?; + + if binop.token_type == TokenType::Plus { + left = AST { + ast_type: ASTType::Binary(Box::new(left.clone()), BinOp::Add, Box::new(right.clone())), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } else { + left = AST { + ast_type: ASTType::Binary(Box::new(left.clone()), BinOp::Sub, Box::new(right.clone())), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } + } + Ok(left) + } + pub fn factor(&mut self) -> Result { + let mut left = self.primary()?; + + while self.current < self.input.len() && + (self.peek()?.token_type == TokenType::Star || self.peek()?.token_type == TokenType::Slash) { + let binop = self.read_token()?; + let right = self.primary()?; + + if binop.token_type == TokenType::Star { + left = AST { + ast_type: ASTType::Binary(Box::new(left.clone()), BinOp::Mul, Box::new(right.clone())), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } else { + left = AST { + ast_type: ASTType::Binary(Box::new(left.clone()), BinOp::Div, Box::new(right.clone())), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } + } + Ok(left) + } + pub fn primary(&mut self) -> Result { + let token = self.read_token()?; + + match token.token_type { + TokenType::Ident(ident) => { + return Ok(AST { + ast_type: ASTType::Ident(ident), + start: token.start, + end: token.end, + line: token.line, + column: token.column, + }); + } + TokenType::Num(num) => { + return Ok(AST { + ast_type: ASTType::Num(num), + start: token.start, + end: token.end, + line: token.line, + column: token.column, + }); + } + TokenType::Str(string) => { + return Ok(AST { + ast_type: ASTType::Str(string), + start: token.start, + end: token.end, + line: token.line, + column: token.column, + }); + } + TokenType::LeftParen => { + return Ok(self.group(token)?); + } + _ => { + return Err(KabelError::new(ErrorKind::UnexpectedToken, + format!("Unexpected token {}", self.text[token.start..token.end].to_string()), + token.line, token.column, + self.text[token.line_start..token.end].to_string())); + }, + } + } + pub fn group(&mut self, left_paren: Token) -> Result { + let expr = self.expression()?; + let right_paren = self.peek(); + if let Ok(right_paren) = right_paren { + if right_paren.token_type != TokenType::RightParen { + return Err(KabelError::new(ErrorKind::MissingDelimiter, + "Missing right parenthesis".to_string(), + right_paren.line, right_paren.column, + self.text[left_paren.start..right_paren.end].to_string())); + } + self.read_token()?; + return Ok(AST { + ast_type: ASTType::Group(Box::new(expr.clone())), + start: left_paren.start, + end: right_paren.end, + line: left_paren.line, + column: left_paren.column, + }); + } + if let Err(e) = right_paren { + return Err(KabelError::new(ErrorKind::MissingDelimiter, + "Missing right parenthesis".to_string(), + e.line, e.column, + self.text[left_paren.line_start..expr.end].to_string())); + } + unreachable!(); + } + + + pub fn read_token(&mut self) -> Result { + if self.current >= self.input.len() { + let last_token = self.input[self.input.len()-1].clone(); + return Err(KabelError::new(ErrorKind::UnexpectedEof, + "Unexpected end of file".to_string(), last_token.line, last_token.column, + self.text[last_token.line_start..last_token.end].to_string())); + } + self.token = self.input[self.current].clone(); + self.current += 1; + return Ok(self.token.clone()); + } + pub fn peek(&mut self) -> Result { + if self.current >= self.input.len() { + let last_token = self.input[self.input.len()-1].clone(); + return Err(KabelError::new(ErrorKind::UnexpectedEof, + "Unexpected end of file".to_string(), last_token.line, last_token.column, + self.text[last_token.line_start..last_token.end].to_string())); + } + return Ok(self.input[self.current].clone()); + } +} + +#[derive(Debug, Clone)] +pub struct AST { + pub ast_type: ASTType, + pub start: usize, + pub end: usize, + pub line: usize, + pub column: usize, +} + +#[derive(Debug, Clone)] +pub enum ASTType { + Program(Vec), + + Binary(Box, BinOp, Box), + + Group(Box), + Ident(String), + Num(f32), + Str(String), +} + +#[derive(Debug, Clone, Copy)] +pub enum BinOp { + Add, + Sub, + Mul, + Div, +} diff --git a/server/src/main.rs b/server/src/main.rs index 4f9480a5a810a3908fe6e0a9a7e10468927f4900..83537eb959ffe21fd22f49400bd7fbb410129dd5 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -150,12 +150,21 @@ fn main() { server_config.world.pixels_per_meter, )) .add_plugins(StkTungsteniteServerPlugin) - .add_systems(Startup, (setup_integration_parameters, planet::spawn_planets)) + .add_systems( + Startup, + (setup_integration_parameters, planet::spawn_planets), + ) .add_systems(Update, (player::on_message, player::packet::on_close)) - .add_systems(FixedUpdate, - (module::module_spawn, player::packet::send_player_energy, - player::packet::on_position_change, module::save::save_eligibility, - module::convert_modules)) + .add_systems( + FixedUpdate, + ( + module::module_spawn, + player::packet::send_player_energy, + player::packet::on_position_change, + module::save::save_eligibility, + module::convert_modules, + ), + ) .add_systems( FixedUpdate, ( diff --git a/server/src/module/component.rs b/server/src/module/component.rs index ab9f20a2482d9b8a116d683e48e9d327455c9ab5..eb96cf26164294a073e513c18fa3b86b3075b0a2 100644 --- a/server/src/module/component.rs +++ b/server/src/module/component.rs @@ -1,5 +1,7 @@ use bevy::prelude::*; -use bevy_rapier2d::prelude::{ExternalForce, ExternalImpulse, ReadMassProperties, RigidBody, Velocity}; +use bevy_rapier2d::prelude::{ + ExternalForce, ExternalImpulse, ReadMassProperties, RigidBody, Velocity, +}; use serde::{Deserialize, Serialize}; use starkingdoms_common::PartType as c_PartType; diff --git a/server/src/module/mod.rs b/server/src/module/mod.rs index 87c94201bfae8efe33cfea0d27c6ffef28a64955..abfe2b529abd85d786729e266f5cabac594f38a4 100644 --- a/server/src/module/mod.rs +++ b/server/src/module/mod.rs @@ -71,12 +71,28 @@ pub fn module_spawn( pub fn detach_recursive( commands: &mut Commands, this: Entity, - attached_query: &mut Query<(Entity, &PartType, &mut Transform, &mut Attach, - &Velocity, Option<&CanAttach>, Option<&LooseAttach>, &mut PartFlags), + attached_query: &mut Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without), >, - player_query: &mut Query<(Entity, &mut Player, &Transform, &Velocity, - &mut Attach, &mut PartFlags), + player_query: &mut Query< + ( + Entity, + &mut Player, + &Transform, + &Velocity, + &mut Attach, + &mut PartFlags, + ), Without, >, ) -> u32 { @@ -163,22 +179,45 @@ pub fn despawn_module_tree( } pub fn attach_on_module_tree( - x: f32, y: f32, + x: f32, + y: f32, commands: &mut Commands, this: Entity, select: Entity, player_id: Entity, - attached_query: &mut Query<(Entity, &PartType, &mut Transform, &mut Attach, - &Velocity, Option<&CanAttach>, Option<&LooseAttach>, - &mut PartFlags), + attached_query: &mut Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without), >, - part_query: &mut Query<(Entity, &PartType, &mut Transform, &mut Velocity, - Option<&LooseAttach>, &mut PartFlags), + part_query: &mut Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Velocity, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without, Without), >, - player_query: &mut Query<(Entity, &mut Player, &Transform, &Velocity, - &mut Attach, &mut PartFlags), + player_query: &mut Query< + ( + Entity, + &mut Player, + &Transform, + &Velocity, + &mut Attach, + &mut PartFlags, + ), Without, >, ) -> bool { @@ -194,8 +233,15 @@ pub fn attach_on_module_tree( let part_type = *module.1; ret |= if part_type != PartType::LandingThrusterSuspension { attach_on_module_tree( - x, y, commands, *this, select, player_id, attached_query, - part_query, player_query, + x, + y, + commands, + *this, + select, + player_id, + attached_query, + part_query, + player_query, ) } else { false @@ -324,9 +370,16 @@ pub fn convert_modules( rapier_context: Res, planet_query: Query<(Entity, &PlanetType, &Children)>, mut player_query: Query<(&Attach, &mut Player)>, - mut attached_query: Query<(Entity, &mut PartType, &mut Attach, - &mut AdditionalMassProperties, &Children, &Transform, - &PartFlags), + mut attached_query: Query< + ( + Entity, + &mut PartType, + &mut Attach, + &mut AdditionalMassProperties, + &Children, + &Transform, + &PartFlags, + ), Without, >, mut collider_query: Query< @@ -389,8 +442,16 @@ fn convert_modules_recursive( commands: &mut Commands, planet_type: PlanetType, attach: Attach, - attached_query: &mut Query<(Entity, &mut PartType, &mut Attach, - &mut AdditionalMassProperties, &Children, &Transform, &PartFlags), + attached_query: &mut Query< + ( + Entity, + &mut PartType, + &mut Attach, + &mut AdditionalMassProperties, + &Children, + &Transform, + &PartFlags, + ), Without, >, collider_query: &mut Query< @@ -486,8 +547,8 @@ fn convert_modules_recursive( .insert(ImpulseJoint::new(module_entity, joint)) .insert(AdditionalMassProperties::MassProperties(MassProperties { local_center_of_mass: vec2(0.0, 0.0), - mass: 0.00000000000001, - principal_inertia: 0.00000000000001, + mass: 0.00000000000001, + principal_inertia: 0.00000000000001, })) .insert(Attach { associated_player: attach.associated_player, @@ -550,12 +611,28 @@ fn convert_modules_recursive( pub fn break_modules( mut commands: Commands, rapier_context: Res, - mut attached_query: Query<(Entity, &PartType, &mut Transform, &mut Attach, - &Velocity, Option<&CanAttach>, Option<&LooseAttach>, &mut PartFlags), + mut attached_query: Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without), >, - mut player_query: Query<(Entity, &mut Player, &Transform, &Velocity, - &mut Attach, &mut PartFlags), + mut player_query: Query< + ( + Entity, + &mut Player, + &Transform, + &Velocity, + &mut Attach, + &mut PartFlags, + ), Without, >, ) { diff --git a/server/src/player/client_login.rs b/server/src/player/client_login.rs index 54a0dfe38ddf8502a272136546f58b67b6e80e22..496be1164c75125e87149f15ad618c59f4cd93e1 100644 --- a/server/src/player/client_login.rs +++ b/server/src/player/client_login.rs @@ -8,13 +8,26 @@ use rand::Rng; use sha2::Sha256; use starkingdoms_common::unpack_savefile; -use crate::{config::StkConfig, module::{component::{Attach, CanAttach, LooseAttach, PartBundle, PartFlags, PartType}, save::load_savefile}, planet::PlanetType, proto_part_flags, proto_transform, ws::WsEvent, AppKeys, MessageType, Packet, Part, Planet, ProtoPartFlags, UserToken, CLIENT_SCALE}; +use crate::{ + config::StkConfig, + module::{ + component::{Attach, CanAttach, LooseAttach, PartBundle, PartFlags, PartType}, + save::load_savefile, + }, + planet::PlanetType, + proto_part_flags, proto_transform, + ws::WsEvent, + AppKeys, MessageType, Packet, Part, Planet, ProtoPartFlags, UserToken, CLIENT_SCALE, +}; use super::component::{Input, Player}; pub fn join_auth( - jwt: Option, app_keys: AppKeys, from: &SocketAddr, - event_queue: &mut Vec, server_config: StkConfig, + jwt: Option, + app_keys: AppKeys, + from: &SocketAddr, + event_queue: &mut Vec, + server_config: StkConfig, ) -> Result<(), ()> { if let Some(token) = jwt { let key: Hmac = Hmac::new_from_slice(&app_keys.app_key).unwrap(); @@ -30,9 +43,7 @@ pub fn join_auth( } }; - if claims.permission_level - < server_config.security.required_permission_level - { + if claims.permission_level < server_config.security.required_permission_level { event_queue.push(WsEvent::Send { to: *from, message: Packet::Message { message_type: MessageType::Error, actor: "SERVER".to_string(), content: format!("Permission level {} is too low, {} is required. If your permissions were just changed, you need to log out and log back in for the change to take effect. If you believe this is a mistake, contact StarKingdoms staff.", claims.permission_level, server_config.security.required_permission_level) }.into(), @@ -57,7 +68,9 @@ pub fn join_auth( } pub fn spawn_player( - commands: &mut Commands, from: &SocketAddr, username: String, + commands: &mut Commands, + from: &SocketAddr, + username: String, ) -> (Entity, Transform, Player) { // generate random angle let angle: f32 = { @@ -65,8 +78,7 @@ pub fn spawn_player( rng.gen::() * std::f32::consts::PI * 2. }; // convert to cartesian with 30.0 meter radius - let mut transform = - Transform::from_xyz(angle.cos() * 30.0, angle.sin() * 30.0, 0.0); + let mut transform = Transform::from_xyz(angle.cos() * 30.0, angle.sin() * 30.0, 0.0); transform.rotate_z(angle); let player_comp = Player { addr: *from, @@ -94,21 +106,49 @@ pub fn spawn_player( } pub fn load_save( - commands: &mut Commands, transform: Transform, id: Entity, - save: Option, app_keys: AppKeys, - attached_query: &mut Query<(Entity, &PartType, &mut Transform, &mut Attach, - &Velocity, Option<&CanAttach>, Option<&LooseAttach>, &mut PartFlags), + commands: &mut Commands, + transform: Transform, + id: Entity, + save: Option, + app_keys: AppKeys, + attached_query: &mut Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without), >, - part_query: &mut Query<(Entity, &PartType, &mut Transform, &mut Velocity, - Option<&LooseAttach>, &mut PartFlags), + part_query: &mut Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Velocity, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without, Without), >, - player_query: &mut Query<(Entity, &mut Player, &Transform, &Velocity, - &mut Attach, &mut PartFlags), + player_query: &mut Query< + ( + Entity, + &mut Player, + &Transform, + &Velocity, + &mut Attach, + &mut PartFlags, + ), Without, >, - player_comp: &mut Player, attach: &mut Attach, from: &SocketAddr, + player_comp: &mut Player, + attach: &mut Attach, + from: &SocketAddr, event_queue: &mut Vec, ) { if let Some(save) = save { @@ -149,18 +189,43 @@ pub fn load_save( pub fn packet_stream( planet_query: &Query<(Entity, &PlanetType, &Transform)>, - event_queue: &mut Vec, from: &SocketAddr, - player_query: &Query<(Entity, &mut Player, &Transform, &Velocity, - &mut Attach, &mut PartFlags), + event_queue: &mut Vec, + from: &SocketAddr, + player_query: &Query< + ( + Entity, + &mut Player, + &Transform, + &Velocity, + &mut Attach, + &mut PartFlags, + ), Without, >, - index: u32, username: String, - part_query: &Query<(Entity, &PartType, &mut Transform, &mut Velocity, - Option<&LooseAttach>, &mut PartFlags), + index: u32, + username: String, + part_query: &Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Velocity, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without, Without), >, - attached_query: &Query<(Entity, &PartType, &mut Transform, &mut Attach, - &Velocity, Option<&CanAttach>, Option<&LooseAttach>, &mut PartFlags), + attached_query: &Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without), >, transform: Transform, @@ -177,15 +242,9 @@ pub fn packet_stream( translation * CLIENT_SCALE )), radius: match *planet_type { - PlanetType::Earth => { - planet!(PlanetType::Earth).size * CLIENT_SCALE - } - PlanetType::Moon => { - planet!(PlanetType::Moon).size * CLIENT_SCALE - } - PlanetType::Mars => { - planet!(PlanetType::Mars).size * CLIENT_SCALE - } + PlanetType::Earth => planet!(PlanetType::Earth).size * CLIENT_SCALE, + PlanetType::Moon => planet!(PlanetType::Moon).size * CLIENT_SCALE, + PlanetType::Mars => planet!(PlanetType::Mars).size * CLIENT_SCALE, }, }, )); @@ -254,10 +313,8 @@ pub fn packet_stream( index, Part { part_type: PartType::Hearty, - transform: proto_transform!(Transform::from_translation( - transform.translation - ) - .with_rotation(transform.rotation)), + transform: proto_transform!(Transform::from_translation(transform.translation) + .with_rotation(transform.rotation)), flags: ProtoPartFlags { attached: false }, }, )); diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 245ef16072f0021c7edbe17c4cdce158f4caaf10..83974e3a118b1fcb4cf4cde3b478057fb15ceade 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -1,8 +1,4 @@ -use bevy::{ - ecs::event::ManualEventReader, - math::vec2, - prelude::*, -}; +use bevy::{ecs::event::ManualEventReader, math::vec2, prelude::*}; use bevy_rapier2d::prelude::*; use client_login::packet_stream; use component::Player; @@ -11,18 +7,25 @@ use request_save::request_save; use send_message::send_message; use crate::{ - config::StkConfig, err_or_cont, mathutil::rot2d, module::{ + config::StkConfig, + err_or_cont, + mathutil::rot2d, + module::{ component::{Attach, CanAttach, LooseAttach, PartFlags, PartType}, PART_HALF_SIZE, - }, part, planet::PlanetType, ws::WsEvent, AppKeys, Packet, CLIENT_SCALE + }, + part, + planet::PlanetType, + ws::WsEvent, + AppKeys, Packet, CLIENT_SCALE, }; +pub mod client_login; pub mod component; pub mod packet; -pub mod client_login; -pub mod send_message; pub mod player_mouse_input; pub mod request_save; +pub mod send_message; pub fn on_message( mut commands: Commands, @@ -79,9 +82,14 @@ pub fn on_message( jwt, } => { // auth - err_or_cont!(client_login::join_auth(jwt, app_keys.clone(), - from, &mut event_queue, server_config.clone())); - + err_or_cont!(client_login::join_auth( + jwt, + app_keys.clone(), + from, + &mut event_queue, + server_config.clone() + )); + // create player in world let (id, transform, mut player_comp) = client_login::spawn_player(&mut commands, from, username.clone()); @@ -94,19 +102,37 @@ pub fn on_message( children: [None, None, None, None], }; // create ship from potential save - client_login::load_save(&mut commands, transform, id, save, - app_keys.clone(), &mut attached_query, &mut part_query, - &mut player_query, &mut player_comp, &mut attach, - from, &mut event_queue); + client_login::load_save( + &mut commands, + transform, + id, + save, + app_keys.clone(), + &mut attached_query, + &mut part_query, + &mut player_query, + &mut player_comp, + &mut attach, + from, + &mut event_queue, + ); // finish player entity let mut entity_id = commands.entity(id); entity_id.insert(player_comp); entity_id.insert(attach); // send packets that tell player initial world state - packet_stream(&planet_query, &mut event_queue, from, - &player_query, index, username, &part_query, - &attached_query, transform); + packet_stream( + &planet_query, + &mut event_queue, + from, + &player_query, + index, + username, + &part_query, + &attached_query, + transform, + ); } Packet::SendMessage { target, content } => { // a player sent a message @@ -147,12 +173,27 @@ pub fn on_message( }; q_player.selected = None; // process if module was attach or detached - attach_or_detach(select, &mut attached_query, - &mut player_query, &mut part_query, &mut commands, x, y, entity); + attach_or_detach( + select, + &mut attached_query, + &mut player_query, + &mut part_query, + &mut commands, + x, + y, + entity, + ); break; } // check if mouse touched a module - mouse_picking(&attached_query, &part_query, &mut q_player, x, y, entity); + mouse_picking( + &attached_query, + &part_query, + &mut q_player, + x, + y, + entity, + ); } } } @@ -164,8 +205,14 @@ pub fn on_message( // mhm yeah done // client asked to save, generate save data and send it back - request_save(&attached_query, old_save.clone(), app_keys.clone(), - attach.clone(), &mut event_queue, from); + request_save( + &attached_query, + old_save.clone(), + app_keys.clone(), + attach.clone(), + &mut event_queue, + from, + ); } } } diff --git a/server/src/player/player_mouse_input.rs b/server/src/player/player_mouse_input.rs index 1e192c9737486d73e49a8c50619973c3dde7e73b..cac49b99bf80cf39154b61aeb0a60c1cecff475b 100644 --- a/server/src/player/player_mouse_input.rs +++ b/server/src/player/player_mouse_input.rs @@ -1,22 +1,48 @@ use bevy::{math::vec3, prelude::*}; use bevy_rapier2d::prelude::*; -use crate::{module::component::{Attach, CanAttach, LooseAttach, PartFlags, PartType}, planet::PlanetType}; +use crate::{ + module::component::{Attach, CanAttach, LooseAttach, PartFlags, PartType}, + planet::PlanetType, +}; use super::component::Player; pub fn attach_or_detach( select: Entity, - attached_query: &mut Query<(Entity, &PartType, &mut Transform, &mut Attach, - &Velocity, Option<&CanAttach>, Option<&LooseAttach>, &mut PartFlags), + attached_query: &mut Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without), >, - player_query: &mut Query<(Entity, &mut Player, &Transform, &Velocity, - &mut Attach, &mut PartFlags), + player_query: &mut Query< + ( + Entity, + &mut Player, + &Transform, + &Velocity, + &mut Attach, + &mut PartFlags, + ), Without, >, - part_query: &mut Query<(Entity, &PartType, &mut Transform, &mut Velocity, - Option<&LooseAttach>, &mut PartFlags), + part_query: &mut Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Velocity, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without, Without), >, commands: &mut Commands, @@ -27,24 +53,18 @@ pub fn attach_or_detach( if attached_query.contains(select) { let module = attached_query.get(select).unwrap(); let attach = module.3.clone(); - let lost_energy_capacity = crate::module::detach_recursive( - commands, - module.0, - attached_query, - player_query, - ); + let lost_energy_capacity = + crate::module::detach_recursive(commands, module.0, attached_query, player_query); let mut module = attached_query.get_mut(select).unwrap(); module.2.translation = vec3(x, y, 0.); if *module.1 == PartType::LandingThruster { let sub_entity = attach.children[2].unwrap(); - let mut suspension = - attached_query.get_mut(sub_entity).unwrap(); + let mut suspension = attached_query.get_mut(sub_entity).unwrap(); suspension.2.translation = vec3(x, y, 0.); } let mut player = player_query.get_mut(entity).unwrap().1; player.energy_capacity -= lost_energy_capacity; - player.energy = - std::cmp::min(player.energy, player.energy_capacity); + player.energy = std::cmp::min(player.energy, player.energy_capacity); return; } if crate::module::attach_on_module_tree( @@ -75,12 +95,28 @@ pub fn attach_or_detach( } pub fn mouse_picking( - attached_query: &Query<(Entity, &PartType, &mut Transform, &mut Attach, - &Velocity, Option<&CanAttach>, Option<&LooseAttach>, &mut PartFlags), + attached_query: &Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without), >, - part_query: &Query<(Entity, &PartType, &mut Transform, &mut Velocity, - Option<&LooseAttach>, &mut PartFlags), + part_query: &Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Velocity, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without, Without), >, q_player: &mut Player, @@ -88,9 +124,7 @@ pub fn mouse_picking( y: f32, entity: Entity, ) { - for (m_entity, part_type, transform, m_attach, _velocity, _, _, _) in - attached_query.iter() - { + for (m_entity, part_type, transform, m_attach, _velocity, _, _, _) in attached_query.iter() { if *part_type == PartType::LandingThrusterSuspension { continue; } diff --git a/server/src/player/request_save.rs b/server/src/player/request_save.rs index 140e7042fb646a2e562b74b4100ba6ef53f262d1..313c14f98129a6be364da9d38cc577bdd7988ce3 100644 --- a/server/src/player/request_save.rs +++ b/server/src/player/request_save.rs @@ -4,25 +4,37 @@ use bevy::prelude::*; use bevy_rapier2d::prelude::Velocity; use starkingdoms_common::{pack_savefile, unpack_savefile, SaveData}; -use crate::{module::component::{Attach, CanAttach, LooseAttach, PartFlags, PartType}, planet::PlanetType, ws::WsEvent, AppKeys, Packet}; +use crate::{ + module::component::{Attach, CanAttach, LooseAttach, PartFlags, PartType}, + planet::PlanetType, + ws::WsEvent, + AppKeys, Packet, +}; use super::component::Player; pub fn request_save( - attached_query: &Query<(Entity, &PartType, &mut Transform, &mut Attach, - &Velocity, Option<&CanAttach>, Option<&LooseAttach>, &mut PartFlags), + attached_query: &Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), (Without, Without), >, old_save: Option, app_keys: AppKeys, attach: Attach, event_queue: &mut Vec, - from: &SocketAddr + from: &SocketAddr, ) { let unused_modules = if let Some(ref old_save) = old_save { - if let Ok(old_savedata) = - unpack_savefile(&app_keys.app_key, old_save.to_string()) - { + if let Ok(old_savedata) = unpack_savefile(&app_keys.app_key, old_save.to_string()) { old_savedata.unused_modules } else { Vec::new() @@ -31,10 +43,7 @@ pub fn request_save( Vec::new() }; let save = SaveData { - children: crate::module::save::construct_save_data( - attach.clone(), - attached_query, - ), + children: crate::module::save::construct_save_data(attach.clone(), attached_query), unused_modules, }; let save_string = pack_savefile(&app_keys.app_key, save); diff --git a/server/src/player/send_message.rs b/server/src/player/send_message.rs index fef8ec14a7913ea29b011e3bf6e20b3ff089af7d..798957c5af62a82f2fa8c4e63429be9ddc2fb998 100644 --- a/server/src/player/send_message.rs +++ b/server/src/player/send_message.rs @@ -3,13 +3,25 @@ use std::net::SocketAddr; use bevy::prelude::*; use bevy_rapier2d::prelude::Velocity; -use crate::{module::component::{Attach, PartFlags}, planet::PlanetType, ws::WsEvent, Packet}; +use crate::{ + module::component::{Attach, PartFlags}, + planet::PlanetType, + ws::WsEvent, + Packet, +}; use super::component::Player; pub fn send_message( - player_query: &Query<(Entity, &mut Player, &Transform, &Velocity, - &mut Attach, &mut PartFlags), + player_query: &Query< + ( + Entity, + &mut Player, + &Transform, + &Velocity, + &mut Attach, + &mut PartFlags, + ), Without, >, from: &SocketAddr, diff --git a/starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs b/starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs new file mode 100644 index 0000000000000000000000000000000000000000..4e1007b478aa25042b825615d80068891527efb8 --- /dev/null +++ b/starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs @@ -0,0 +1,39 @@ +// vite.config.ts +import { defineConfig } from "file:///home/ghostlyzsh/dev/starkingdoms.tk/starkingdoms-client/node_modules/vite/dist/node/index.js"; +import { resolve } from "path"; +import * as child from "child_process"; +import autoprefixer from "file:///home/ghostlyzsh/dev/starkingdoms.tk/starkingdoms-client/node_modules/autoprefixer/lib/autoprefixer.js"; +import { svelte } from "file:///home/ghostlyzsh/dev/starkingdoms.tk/starkingdoms-client/node_modules/@sveltejs/vite-plugin-svelte/src/index.js"; +var __vite_injected_original_dirname = "/home/ghostlyzsh/dev/starkingdoms.tk/starkingdoms-client"; +var commitHash = child.execSync("git describe --no-match --always --abbrev=8 --dirty").toString().trim(); +var vite_config_default = defineConfig({ + plugins: [svelte()], + define: { + APP_VERSION: JSON.stringify(process.env.npm_package_version), + COMMIT_HASH: JSON.stringify(commitHash) + }, + build: { + target: ["chrome89", "edge89", "firefox89", "safari15"], + cssCodeSplit: false, + rollupOptions: { + input: { + main: resolve(__vite_injected_original_dirname, "index.html"), + play: resolve(__vite_injected_original_dirname, "play/index.html"), + signup: resolve(__vite_injected_original_dirname, "signup/index.html"), + login: resolve(__vite_injected_original_dirname, "login/index.html"), + shipeditor: resolve(__vite_injected_original_dirname, "shipeditor/index.html"), + uikit: resolve(__vite_injected_original_dirname, "uikit/index.html") + } + } + }, + appType: "mpa", + css: { + postcss: { + plugins: [autoprefixer({})] + } + } +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvaG9tZS9naG9zdGx5enNoL2Rldi9zdGFya2luZ2RvbXMudGsvc3Rhcmtpbmdkb21zLWNsaWVudFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL2hvbWUvZ2hvc3RseXpzaC9kZXYvc3Rhcmtpbmdkb21zLnRrL3N0YXJraW5nZG9tcy1jbGllbnQvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL2hvbWUvZ2hvc3RseXpzaC9kZXYvc3Rhcmtpbmdkb21zLnRrL3N0YXJraW5nZG9tcy1jbGllbnQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tIFwidml0ZVwiO1xuaW1wb3J0IHsgcmVzb2x2ZSB9IGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgKiBhcyBjaGlsZCBmcm9tIFwiY2hpbGRfcHJvY2Vzc1wiO1xuLy9AdHMtaWdub3JlXG5pbXBvcnQgYXV0b3ByZWZpeGVyIGZyb20gXCJhdXRvcHJlZml4ZXJcIjtcbmltcG9ydCB7IHN2ZWx0ZSB9IGZyb20gXCJAc3ZlbHRlanMvdml0ZS1wbHVnaW4tc3ZlbHRlXCI7XG5cbmNvbnN0IGNvbW1pdEhhc2ggPSBjaGlsZFxuICAuZXhlY1N5bmMoXCJnaXQgZGVzY3JpYmUgLS1uby1tYXRjaCAtLWFsd2F5cyAtLWFiYnJldj04IC0tZGlydHlcIilcbiAgLnRvU3RyaW5nKClcbiAgLnRyaW0oKTtcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW3N2ZWx0ZSgpXSxcbiAgZGVmaW5lOiB7XG4gICAgQVBQX1ZFUlNJT046IEpTT04uc3RyaW5naWZ5KHByb2Nlc3MuZW52Lm5wbV9wYWNrYWdlX3ZlcnNpb24pLFxuICAgIENPTU1JVF9IQVNIOiBKU09OLnN0cmluZ2lmeShjb21taXRIYXNoKSxcbiAgfSxcbiAgYnVpbGQ6IHtcbiAgICB0YXJnZXQ6IFtcImNocm9tZTg5XCIsIFwiZWRnZTg5XCIsIFwiZmlyZWZveDg5XCIsIFwic2FmYXJpMTVcIl0sXG4gICAgY3NzQ29kZVNwbGl0OiBmYWxzZSxcbiAgICByb2xsdXBPcHRpb25zOiB7XG4gICAgICBpbnB1dDoge1xuICAgICAgICBtYWluOiByZXNvbHZlKF9fZGlybmFtZSwgXCJpbmRleC5odG1sXCIpLFxuICAgICAgICBwbGF5OiByZXNvbHZlKF9fZGlybmFtZSwgXCJwbGF5L2luZGV4Lmh0bWxcIiksXG4gICAgICAgIHNpZ251cDogcmVzb2x2ZShfX2Rpcm5hbWUsIFwic2lnbnVwL2luZGV4Lmh0bWxcIiksXG4gICAgICAgIGxvZ2luOiByZXNvbHZlKF9fZGlybmFtZSwgXCJsb2dpbi9pbmRleC5odG1sXCIpLFxuICAgICAgICBzaGlwZWRpdG9yOiByZXNvbHZlKF9fZGlybmFtZSwgXCJzaGlwZWRpdG9yL2luZGV4Lmh0bWxcIiksXG4gICAgICAgIHVpa2l0OiByZXNvbHZlKF9fZGlybmFtZSwgXCJ1aWtpdC9pbmRleC5odG1sXCIpLFxuICAgICAgfSxcbiAgICB9LFxuICB9LFxuICBhcHBUeXBlOiBcIm1wYVwiLFxuICBjc3M6IHtcbiAgICBwb3N0Y3NzOiB7XG4gICAgICBwbHVnaW5zOiBbYXV0b3ByZWZpeGVyKHt9KV0sXG4gICAgfSxcbiAgfSxcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUEwVixTQUFTLG9CQUFvQjtBQUN2WCxTQUFTLGVBQWU7QUFDeEIsWUFBWSxXQUFXO0FBRXZCLE9BQU8sa0JBQWtCO0FBQ3pCLFNBQVMsY0FBYztBQUx2QixJQUFNLG1DQUFtQztBQU96QyxJQUFNLGFBQ0gsZUFBUyxxREFBcUQsRUFDOUQsU0FBUyxFQUNULEtBQUs7QUFFUixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTLENBQUMsT0FBTyxDQUFDO0FBQUEsRUFDbEIsUUFBUTtBQUFBLElBQ04sYUFBYSxLQUFLLFVBQVUsUUFBUSxJQUFJLG1CQUFtQjtBQUFBLElBQzNELGFBQWEsS0FBSyxVQUFVLFVBQVU7QUFBQSxFQUN4QztBQUFBLEVBQ0EsT0FBTztBQUFBLElBQ0wsUUFBUSxDQUFDLFlBQVksVUFBVSxhQUFhLFVBQVU7QUFBQSxJQUN0RCxjQUFjO0FBQUEsSUFDZCxlQUFlO0FBQUEsTUFDYixPQUFPO0FBQUEsUUFDTCxNQUFNLFFBQVEsa0NBQVcsWUFBWTtBQUFBLFFBQ3JDLE1BQU0sUUFBUSxrQ0FBVyxpQkFBaUI7QUFBQSxRQUMxQyxRQUFRLFFBQVEsa0NBQVcsbUJBQW1CO0FBQUEsUUFDOUMsT0FBTyxRQUFRLGtDQUFXLGtCQUFrQjtBQUFBLFFBQzVDLFlBQVksUUFBUSxrQ0FBVyx1QkFBdUI7QUFBQSxRQUN0RCxPQUFPLFFBQVEsa0NBQVcsa0JBQWtCO0FBQUEsTUFDOUM7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUFBLEVBQ0EsU0FBUztBQUFBLEVBQ1QsS0FBSztBQUFBLElBQ0gsU0FBUztBQUFBLE1BQ1AsU0FBUyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7QUFBQSxJQUM1QjtBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=