M Cargo.lock => Cargo.lock +4 -0
@@ 2463,6 2463,10 @@ dependencies = [
]
[[package]]
+name = "kabel"
+version = "0.1.0"
+
+[[package]]
name = "khronos-egl"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
M kabel/grammar.ebnf => kabel/grammar.ebnf +5 -4
@@ 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 } ] ;
A kabel/src/error.rs => kabel/src/error.rs +68 -0
@@ 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<ErrorKind> for usize {
+ fn from(value: ErrorKind) -> Self {
+ use ErrorKind::*;
+ match value {
+ UnexpectedEof => 0x00,
+ UnexpectedCharacter => 0x01,
+ UnexpectedToken => 0x02,
+ MissingDelimiter => 0x03,
+ }
+ }
+}
A kabel/src/lexer.rs => kabel/src/lexer.rs +133 -0
@@ 0,0 1,133 @@
+use std::str::from_utf8;
+
+use crate::{error::{ErrorKind, KabelError}, token};
+
+pub struct Lexer {
+ input: Vec<u8>,
+ start: usize,
+ current: usize,
+ line: usize,
+ line_start: usize,
+ column: usize,
+ c: u8,
+ pub errors: Vec<KabelError>,
+ pub output: Vec<Token>,
+}
+
+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),
+}
A kabel/src/lib.rs => kabel/src/lib.rs +18 -0
@@ 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<Token>) -> (AST, Parser) {
+ let mut parser = Parser::new(text, input);
+ (parser.program(), parser)
+}
A kabel/src/macros.rs => kabel/src/macros.rs +13 -0
@@ 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,
+ }
+ };
+}
D kabel/src/main.rs => kabel/src/main.rs +0 -3
@@ 1,3 0,0 @@
-fn main() {
- println!("Hello, world!");
-}
A kabel/src/parser.rs => kabel/src/parser.rs +226 -0
@@ 0,0 1,226 @@
+use crate::{error::{ErrorKind, KabelError}, lexer::{Token, TokenType}};
+
+pub struct Parser {
+ input: Vec<Token>,
+ text: String,
+ //start: usize,
+ current: usize,
+ token: Token,
+ pub errors: Vec<KabelError>,
+}
+
+impl Parser {
+ pub fn new(text: String, input: Vec<Token>) -> 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<AST, KabelError> {
+ let term = self.term()?;
+ Ok(term)
+ }
+
+ pub fn term(&mut self) -> Result<AST, KabelError> {
+ 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<AST, KabelError> {
+ 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<AST, KabelError> {
+ 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<AST, KabelError> {
+ 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<Token, KabelError> {
+ 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<Token, KabelError> {
+ 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<AST>),
+
+ Binary(Box<AST>, BinOp, Box<AST>),
+
+ Group(Box<AST>),
+ Ident(String),
+ Num(f32),
+ Str(String),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum BinOp {
+ Add,
+ Sub,
+ Mul,
+ Div,
+}
M server/src/main.rs => server/src/main.rs +14 -5
@@ 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,
(
M server/src/module/component.rs => server/src/module/component.rs +3 -1
@@ 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;
M server/src/module/mod.rs => server/src/module/mod.rs +102 -25
@@ 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<PlanetType>, Without<Player>),
>,
- 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<PlanetType>,
>,
) -> 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<PlanetType>, Without<Player>),
>,
- 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<PlanetType>, Without<Player>, Without<Attach>),
>,
- 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<PlanetType>,
>,
) -> 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<RapierContext>,
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<Player>,
>,
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<Player>,
>,
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<RapierContext>,
- 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<PlanetType>, Without<Player>),
>,
- 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<PlanetType>,
>,
) {
M server/src/player/client_login.rs => server/src/player/client_login.rs +96 -39
@@ 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<String>, app_keys: AppKeys, from: &SocketAddr,
- event_queue: &mut Vec<WsEvent>, server_config: StkConfig,
+ jwt: Option<String>,
+ app_keys: AppKeys,
+ from: &SocketAddr,
+ event_queue: &mut Vec<WsEvent>,
+ server_config: StkConfig,
) -> Result<(), ()> {
if let Some(token) = jwt {
let key: Hmac<Sha256> = 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::<f32>() * 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<String>, 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<String>,
+ app_keys: AppKeys,
+ attached_query: &mut Query<
+ (
+ Entity,
+ &PartType,
+ &mut Transform,
+ &mut Attach,
+ &Velocity,
+ Option<&CanAttach>,
+ Option<&LooseAttach>,
+ &mut PartFlags,
+ ),
(Without<PlanetType>, Without<Player>),
>,
- 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<PlanetType>, Without<Player>, Without<Attach>),
>,
- 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<PlanetType>,
>,
- player_comp: &mut Player, attach: &mut Attach, from: &SocketAddr,
+ player_comp: &mut Player,
+ attach: &mut Attach,
+ from: &SocketAddr,
event_queue: &mut Vec<WsEvent>,
) {
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<WsEvent>, from: &SocketAddr,
- player_query: &Query<(Entity, &mut Player, &Transform, &Velocity,
- &mut Attach, &mut PartFlags),
+ event_queue: &mut Vec<WsEvent>,
+ from: &SocketAddr,
+ player_query: &Query<
+ (
+ Entity,
+ &mut Player,
+ &Transform,
+ &Velocity,
+ &mut Attach,
+ &mut PartFlags,
+ ),
Without<PlanetType>,
>,
- 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<PlanetType>, Without<Player>, Without<Attach>),
>,
- 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<PlanetType>, Without<Player>),
>,
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 },
},
));
M server/src/player/mod.rs => server/src/player/mod.rs +71 -24
@@ 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,
+ );
}
}
}
M server/src/player/player_mouse_input.rs => server/src/player/player_mouse_input.rs +58 -24
@@ 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<PlanetType>, Without<Player>),
>,
- 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<PlanetType>,
>,
- 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<PlanetType>, Without<Player>, Without<Attach>),
>,
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<PlanetType>, Without<Player>),
>,
- 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<PlanetType>, Without<Player>, Without<Attach>),
>,
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;
}
M server/src/player/request_save.rs => server/src/player/request_save.rs +20 -11
@@ 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<PlanetType>, Without<Player>),
>,
old_save: Option<String>,
app_keys: AppKeys,
attach: Attach,
event_queue: &mut Vec<WsEvent>,
- 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);
M server/src/player/send_message.rs => server/src/player/send_message.rs +15 -3
@@ 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<PlanetType>,
>,
from: &SocketAddr,
A starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs => starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs +39 -0
@@ 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=