Compare commits
No commits in common. "d584340f010e67919de8be2fffde17ffd9f20cdb" and "81a9ec0c50468cd6cdd4192323b6303bf7dd32d6" have entirely different histories.
d584340f01
...
81a9ec0c50
18 changed files with 216 additions and 1153 deletions
1136
Cargo.lock
generated
1136
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@
|
|||
members = ["cli", "discord"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.11.0"
|
||||
version = "0.10.0"
|
||||
edition = "2024"
|
||||
repository = "https://2ndbeam.ru/git/2ndbeam/squad-quest"
|
||||
homepage = "https://2ndbeam.ru/git/2ndbeam/squad-quest"
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ license.workspace = true
|
|||
chrono = "0.4.42"
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
squad-quest = { version = "0.11.0", path = ".." }
|
||||
squad-quest = { version = "0.10.0", path = ".." }
|
||||
toml = "0.9.8"
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ pub enum MapCommands {
|
|||
Delete(MapDeleteArgs),
|
||||
/// Update room data
|
||||
Update(MapUpdateArgs),
|
||||
/// Get room implementation data
|
||||
Data(MapDataArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
@ -57,9 +55,3 @@ pub struct MapUpdateArgs {
|
|||
#[arg(short,long)]
|
||||
pub value: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct MapDataArgs {
|
||||
/// Room ID
|
||||
pub id: u16,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,8 +147,7 @@ fn main() {
|
|||
answer: args.answer.clone(),
|
||||
public: args.public,
|
||||
available_on: args.available.clone(),
|
||||
deadline: args.deadline.clone(),
|
||||
..Default::default()
|
||||
deadline: args.deadline.clone()
|
||||
};
|
||||
|
||||
do_and_log(quest.save(path), !cli.quiet, format!("Created quest #{}.", quest.id));
|
||||
|
|
@ -170,8 +169,7 @@ fn main() {
|
|||
answer: args.answer.clone().unwrap_or(quest.answer.clone()),
|
||||
public: args.public.unwrap_or(quest.public),
|
||||
available_on: args.available.clone().or(quest.available_on.clone()),
|
||||
deadline: args.deadline.clone().or(quest.deadline.clone()),
|
||||
..Default::default()
|
||||
deadline: args.deadline.clone().or(quest.deadline.clone())
|
||||
};
|
||||
|
||||
do_and_log(quest.save(path), !cli.quiet, format!("Updated quest #{}.", quest.id));
|
||||
|
|
@ -447,15 +445,6 @@ fn main() {
|
|||
let connected = if connect { "Connected" } else { "Disconnected" };
|
||||
do_and_log(map_save(map, map_path), !cli.quiet, format!("{connected} rooms #{} <-> #{}.", args.first, args.second));
|
||||
},
|
||||
MapCommands::Data(args) => {
|
||||
if let Some(room) = map.room.iter().find(|r| r.id == args.id) {
|
||||
if let Some(data) = &room.data {
|
||||
for (key, value) in data {
|
||||
println!("{key} = {value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ chrono = "0.4.42"
|
|||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
dotenvy = "0.15.7"
|
||||
poise = "0.6.1"
|
||||
rocket = { version = "0.5.1", features = ["json"] }
|
||||
serde = "1.0.228"
|
||||
serde_json = "1.0.146"
|
||||
squad-quest = { version = "0.11.0", path = ".." }
|
||||
squad-quest = { version = "0.10.0", path = ".." }
|
||||
tokio = { version = "1.48.0", features = ["rt-multi-thread"] }
|
||||
toml = "0.9.8"
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
[default]
|
||||
address = "127.0.0.1" # should be local only because frontend runs on the same machine
|
||||
port = 2526
|
||||
log_level = "critical"
|
||||
|
||||
[default.shutdown]
|
||||
ctrlc = false
|
||||
|
||||
|
|
@ -1,24 +1,12 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use poise::serenity_prelude::{User, UserId};
|
||||
use poise::serenity_prelude::UserId;
|
||||
use squad_quest::{account::Account, config::Config, map::Map};
|
||||
|
||||
pub fn fetch_or_init_account(conf: &Config, id: String, user: Option<&User>) -> Account {
|
||||
pub fn fetch_or_init_account(conf: &Config, id: String) -> Account {
|
||||
let accounts = conf.load_accounts();
|
||||
let mut data: HashMap<String, String> = HashMap::new();
|
||||
|
||||
if let Some(user) = user {
|
||||
let avatar = user.avatar_url().unwrap_or("null".to_string());
|
||||
let name = user.display_name().to_string();
|
||||
data.insert("avatar".to_string(), avatar);
|
||||
data.insert("name".to_string(), name);
|
||||
}
|
||||
|
||||
match accounts.iter().find(|a| a.id == id) {
|
||||
Some(a) => a.clone(),
|
||||
None => Account {
|
||||
id,
|
||||
data: Some(data),
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
use rocket::{Build, Response, Rocket, State, http::{Header, hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN}, response::Responder, serde::json::Json};
|
||||
use serde::Serialize;
|
||||
use squad_quest::{SquadObject, account::Account, config::Config, map::{Map, Room}};
|
||||
|
||||
struct RocketData {
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct UserData {
|
||||
pub id: String,
|
||||
pub avatar: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct RoomData {
|
||||
pub id: u16,
|
||||
pub value: u32,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub w: f32,
|
||||
pub h: f32,
|
||||
pub markers: Vec<UserData>,
|
||||
}
|
||||
|
||||
struct RoomDataResponse {
|
||||
pub data: Vec<RoomData>
|
||||
}
|
||||
|
||||
impl From<Vec<RoomData>> for RoomDataResponse {
|
||||
fn from(value: Vec<RoomData>) -> Self {
|
||||
Self {
|
||||
data: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Responder<'r, 'static> for RoomDataResponse {
|
||||
fn respond_to(self, request: &'r rocket::Request<'_>) -> rocket::response::Result<'static> {
|
||||
Response::build_from(Json(&self.data).respond_to(request)?)
|
||||
.header(Header::new(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), "http://localhost:5173"))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Room> for RoomData {
|
||||
fn from(value: &Room) -> Self {
|
||||
let data = value.data.clone().unwrap_or_default();
|
||||
let keys = [ "x", "y", "w", "h" ];
|
||||
let mut values = [ 0f32, 0f32, 0f32, 0f32 ];
|
||||
let mut counter = 0usize;
|
||||
for key in keys {
|
||||
values[counter] = data.get(key).map_or(0f32, |v| v.parse::<f32>().unwrap_or_default());
|
||||
counter += 1;
|
||||
}
|
||||
RoomData {
|
||||
id: value.id,
|
||||
value: value.value,
|
||||
name: value.name.clone(),
|
||||
description: value.description.clone().unwrap_or(String::new()),
|
||||
x: values[0],
|
||||
y: values[1],
|
||||
w: values[2],
|
||||
h: values[3],
|
||||
markers: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn acc_filt_map(account: &Account, room_id: u16) -> Option<UserData> {
|
||||
if account.location == room_id {
|
||||
let data = account.data.clone().unwrap_or_default();
|
||||
let keys = [ "avatar", "name" ];
|
||||
let empty = String::new();
|
||||
let mut values = [ &String::new(), &String::new() ];
|
||||
let mut counter = 0usize;
|
||||
for key in keys {
|
||||
values[counter] = data.get(key).unwrap_or(&empty);
|
||||
counter += 1;
|
||||
}
|
||||
Some(UserData {
|
||||
id: account.id.clone(),
|
||||
avatar: values[0].clone(),
|
||||
name: values[1].clone(),
|
||||
})
|
||||
} else { None }
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
fn index(rd: &State<RocketData>) -> RoomDataResponse {
|
||||
let map_path = rd.config.full_map_path();
|
||||
let Ok(map) = Map::load(map_path) else {
|
||||
return Vec::new().into();
|
||||
};
|
||||
let accounts = rd.config.load_accounts();
|
||||
|
||||
let rooms_vec: Vec<RoomData> = map.room.iter()
|
||||
.map(|r| {
|
||||
let mut rd = RoomData::from(r);
|
||||
let markers = accounts.iter()
|
||||
.filter_map(|a| acc_filt_map(a, r.id))
|
||||
.collect::<Vec<UserData>>();
|
||||
rd.markers = markers;
|
||||
rd
|
||||
})
|
||||
.collect();
|
||||
rooms_vec.into()
|
||||
}
|
||||
|
||||
pub fn rocket(config: Config) -> Rocket<Build> {
|
||||
rocket::build()
|
||||
.mount("/", routes![index])
|
||||
.manage(RocketData{config})
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -142,8 +142,7 @@ pub async fn give(
|
|||
let mut accounts = config.load_accounts();
|
||||
|
||||
let user_id = format!("{}", ctx.author().id.get());
|
||||
|
||||
let mut user_account = fetch_or_init_account(config, user_id, Some(ctx.author()));
|
||||
let mut user_account = fetch_or_init_account(config, user_id);
|
||||
|
||||
let who_id = format!("{}", who.id.get());
|
||||
let Some(other_account) = accounts.iter_mut().find(|a| a.id == who_id ) else {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ pub async fn answer(
|
|||
#[description_localized("ru", "Вложение к ответу на квест")]
|
||||
file3: Option<Attachment>,
|
||||
) -> Result<(), Error> {
|
||||
let mut account = fetch_or_init_account(&ctx.data().config, ctx.author().id.to_string(), Some(ctx.author()));
|
||||
let mut account = fetch_or_init_account(&ctx.data().config, ctx.author().id.to_string());
|
||||
|
||||
if let Some(_) = account.quests_completed.iter().find(|qid| **qid == quest_id) {
|
||||
return Err(Error::QuestIsCompleted(quest_id));
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub async fn unlock(
|
|||
};
|
||||
|
||||
let acc_id = format!("{}", ctx.author().id.get());
|
||||
let mut account = fetch_or_init_account(conf, acc_id, Some(ctx.author()));
|
||||
let mut account = fetch_or_init_account(conf, acc_id);
|
||||
|
||||
if account.balance < room.value {
|
||||
return Err(Error::InsufficientFunds(room.value));
|
||||
|
|
@ -68,7 +68,7 @@ pub async fn r#move(
|
|||
let conf = &ctx.data().config;
|
||||
|
||||
let acc_id = format!("{}", ctx.author().id.get());
|
||||
let mut account = fetch_or_init_account(conf, acc_id, Some(ctx.author()));
|
||||
let mut account = fetch_or_init_account(conf, acc_id);
|
||||
|
||||
if let None = account.rooms_unlocked.iter().find(|rid| **rid == id) {
|
||||
return Err(Error::CannotReach(id));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#[macro_use] extern crate rocket;
|
||||
|
||||
use std::{sync::{Arc, Mutex}};
|
||||
|
||||
use clap::Parser;
|
||||
|
|
@ -7,9 +5,8 @@ use dotenvy::dotenv;
|
|||
use poise::serenity_prelude as serenity;
|
||||
use squad_quest::config::Config;
|
||||
|
||||
use crate::{commands::{error_handler, print_error_recursively}, config::{ConfigImpl, DiscordConfig}, error::Error, strings::Strings};
|
||||
use crate::{commands::error_handler, config::{ConfigImpl, DiscordConfig}, error::Error, strings::Strings};
|
||||
|
||||
mod api;
|
||||
mod commands;
|
||||
mod cli;
|
||||
mod config;
|
||||
|
|
@ -68,14 +65,6 @@ async fn main() {
|
|||
let token = std::env::var(DISCORD_TOKEN).expect("missing DISCORD_TOKEN");
|
||||
let intents = serenity::GatewayIntents::non_privileged();
|
||||
|
||||
let conf1 = config.clone();
|
||||
tokio::spawn(async {
|
||||
if let Err(error) = api::rocket(conf1).launch().await {
|
||||
eprintln!("ERROR ON API LAUNCH");
|
||||
print_error_recursively(&error);
|
||||
}
|
||||
});
|
||||
|
||||
let framework = poise::Framework::builder()
|
||||
.options(poise::FrameworkOptions {
|
||||
on_error: |err| Box::pin(error_handler(err)),
|
||||
|
|
@ -98,8 +87,6 @@ async fn main() {
|
|||
.setup(|_ctx, _ready, _framework| {
|
||||
Box::pin(async move {
|
||||
//poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||
|
||||
|
||||
Ok(Data {
|
||||
config,
|
||||
discord: Arc::new(Mutex::new(discord)),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! User accounts
|
||||
|
||||
use std::{collections::HashMap, fs, io::Write, path::PathBuf};
|
||||
use std::{fs, io::Write, path::PathBuf};
|
||||
|
||||
use serde::{ Serialize, Deserialize };
|
||||
|
||||
|
|
@ -29,9 +29,6 @@ pub struct Account {
|
|||
|
||||
/// Vec of rooms unlocked by this user
|
||||
pub rooms_unlocked: Vec<u16>,
|
||||
|
||||
/// Additional implementation-defined data
|
||||
pub data: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl Default for Account {
|
||||
|
|
@ -42,7 +39,6 @@ impl Default for Account {
|
|||
location: u16::default(),
|
||||
quests_completed: Vec::new(),
|
||||
rooms_unlocked: Vec::new(),
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::{SquadObject, account::Account, error::Error, quest::Quest};
|
||||
|
||||
/// Struct for containing paths to other (de-)serializable things
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct Config {
|
||||
/// Path to config directory
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Map, a.k.a. a graph of rooms
|
||||
|
||||
use std::{collections::HashMap, fs, io::Write, path::PathBuf};
|
||||
use std::{fs, io::Write, path::PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ use crate::{SquadObject, account::Account, error::{Error, MapError}};
|
|||
#[serde(default)]
|
||||
pub struct Map {
|
||||
/// Rooms go here
|
||||
pub room: Vec<Room>,
|
||||
pub room: Vec<Room>
|
||||
}
|
||||
|
||||
impl Default for Map {
|
||||
|
|
@ -131,8 +131,6 @@ pub struct Room {
|
|||
pub name: String,
|
||||
/// Room description
|
||||
pub description: Option<String>,
|
||||
/// Additional implementation-based data
|
||||
pub data: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
fn default_name() -> String {
|
||||
|
|
@ -147,7 +145,6 @@ impl Default for Room {
|
|||
value: u32::default(),
|
||||
name: default_name(),
|
||||
description: None,
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Text-based quests and user solutions for them
|
||||
|
||||
use std::{collections::HashMap, fs, io::Write, path::PathBuf};
|
||||
use std::{fs, io::Write, path::PathBuf};
|
||||
|
||||
use serde::{ Serialize, Deserialize };
|
||||
use crate::{SquadObject, account::Account, error::{Error, QuestError}};
|
||||
|
|
@ -66,10 +66,7 @@ pub struct Quest {
|
|||
pub available_on: Option<Date>,
|
||||
|
||||
/// When quest expires
|
||||
pub deadline: Option<Date>,
|
||||
|
||||
/// Additional implementation-defined data
|
||||
pub data: Option<HashMap<String, String>>,
|
||||
pub deadline: Option<Date>
|
||||
}
|
||||
|
||||
impl Default for Quest {
|
||||
|
|
@ -83,8 +80,7 @@ impl Default for Quest {
|
|||
answer: default_answer(),
|
||||
public: false,
|
||||
available_on: None,
|
||||
deadline: None,
|
||||
data: None,
|
||||
deadline: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ fn quest_one() {
|
|||
answer: "Accept the answer if it has no attachments and an empty comment".to_owned(),
|
||||
public: false,
|
||||
available_on: None,
|
||||
deadline: None,
|
||||
..Default::default()
|
||||
deadline: None
|
||||
};
|
||||
|
||||
assert_eq!(*quest, expected);
|
||||
|
|
@ -74,8 +73,7 @@ fn account_test() {
|
|||
balance: 150,
|
||||
location: 0,
|
||||
quests_completed: vec![0],
|
||||
rooms_unlocked: Vec::new(),
|
||||
..Default::default()
|
||||
rooms_unlocked: Vec::new()
|
||||
};
|
||||
|
||||
let accounts = config.load_accounts();
|
||||
|
|
@ -94,7 +92,6 @@ fn load_map() {
|
|||
value: 0,
|
||||
name: "Entrance".to_string(),
|
||||
description: Some("Enter the dungeon".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let room1 = Room {
|
||||
|
|
@ -103,7 +100,6 @@ fn load_map() {
|
|||
value: 100,
|
||||
name: "Kitchen hall".to_string(),
|
||||
description: None,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let room2 = Room {
|
||||
|
|
@ -112,7 +108,6 @@ fn load_map() {
|
|||
value: 250,
|
||||
name: "Room".to_string(),
|
||||
description: Some("Simple room with no furniture".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let room3 = Room {
|
||||
|
|
@ -121,7 +116,6 @@ fn load_map() {
|
|||
value: 175,
|
||||
name: "Kitchen".to_string(),
|
||||
description: Some("Knives are stored here".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let expected = Map {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue