feat: use ward info

This commit is contained in:
Anna 2023-01-29 04:26:58 -05:00
parent dc4fc02e71
commit db2cfcfb94
17 changed files with 534 additions and 337 deletions

View File

@ -7,6 +7,7 @@ namespace OrangeGuidanceTomestone;
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class MessageRequest {
public uint Territory { get; set; }
public uint? Ward { get; set; }
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }

View File

@ -153,6 +153,8 @@ internal class Messages : IDisposable {
return;
}
var ward = this.Plugin.Common.Functions.Housing.Location?.Ward;
if (this.Plugin.Config.DisableTrials && this.Trials.Contains(territory)) {
return;
}
@ -173,18 +175,23 @@ internal class Messages : IDisposable {
Task.Run(async () => {
try {
await this.DownloadMessages(territory);
await this.DownloadMessages(territory, ward);
} catch (Exception ex) {
PluginLog.LogError(ex, $"Failed to get messages for territory {territory}");
}
});
}
private async Task DownloadMessages(ushort territory) {
private async Task DownloadMessages(ushort territory, ushort? ward) {
var route = $"/messages/{territory}";
if (ward != null) {
route += $"?ward={ward}";
}
var resp = await ServerHelper.SendRequest(
this.Plugin.Config.ApiKey,
HttpMethod.Get,
$"/messages/{territory}"
route
);
var json = await resp.Content.ReadAsStringAsync();
var messages = JsonConvert.DeserializeObject<Message[]>(json)!;

View File

@ -61,6 +61,7 @@
<PackageReference Include="DalamudPackager" Version="2.1.10"/>
<PackageReference Include="Fody" Version="6.6.4" PrivateAssets="all"/>
<PackageReference Include="Resourcer.Fody" Version="1.8.0" PrivateAssets="all"/>
<PackageReference Include="XivCommon" Version="7.0.0-alpha.1" PrivateAssets="all"/>
</ItemGroup>
<ItemGroup>

View File

@ -7,6 +7,7 @@ using Dalamud.Game.Gui;
using Dalamud.IoC;
using Dalamud.Plugin;
using OrangeGuidanceTomestone.MiniPenumbra;
using XivCommon;
namespace OrangeGuidanceTomestone;
@ -38,6 +39,7 @@ public class Plugin : IDalamudPlugin {
internal GameGui GameGui { get; init; }
internal Configuration Config { get; }
internal XivCommonBase Common { get; }
internal Vfx Vfx { get; }
internal PluginUi Ui { get; }
internal Messages Messages { get; }
@ -51,6 +53,7 @@ public class Plugin : IDalamudPlugin {
this.AvfxFilePath = this.CopyAvfxFile();
this.Config = this.Interface!.GetPluginConfig() as Configuration ?? new Configuration();
this.Common = new XivCommonBase();
this.Vfx = new Vfx();
this.Messages = new Messages(this);
this.Ui = new PluginUi(this);
@ -70,6 +73,7 @@ public class Plugin : IDalamudPlugin {
this.Ui.Dispose();
this.Messages.Dispose();
this.Vfx.Dispose();
this.Common.Dispose();
}
internal void SaveConfig() {

View File

@ -231,6 +231,7 @@ internal class Write : ITab {
if (ImGui.Button("Write") && valid && !inAir && this.Plugin.ClientState.LocalPlayer is { } player) {
var req = new MessageRequest {
Territory = this.Plugin.ClientState.TerritoryType,
Ward = this.Plugin.Common.Functions.Housing.Location?.Ward,
X = player.Position.X,
Y = player.Position.Y,
Z = player.Position.Z,

6
client/packages.lock.json Executable file → Normal file
View File

@ -24,6 +24,12 @@
"NETStandard.Library": "1.6.1"
}
},
"XivCommon": {
"type": "Direct",
"requested": "[7.0.0-alpha.1, )",
"resolved": "7.0.0-alpha.1",
"contentHash": "4Yy4Wg3bnI/g9BSEYAqOZALmVMJZS0wcP847VlUIThBqIS/47O6tw2BI84he4KuPSTfIsYOzrcz3vAia8+Pn3g=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "1.1.0",

703
server/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ edition = "2021"
[dependencies]
anyhow = "1"
base64 = "0.13"
base64 = "0.21"
bytes = "1"
chrono = "0.4"
if_chain = "1"
@ -16,7 +16,7 @@ serde = { version = "1", features = ["derive"] }
serde_yaml = "0.9"
sha3 = "0.10"
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "sqlite", "chrono"] }
toml = "0.5"
toml = "0.7"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
tokio-stream = { version = "0.1", default-features = false, features = ["net"] }
uuid = { version = "1", features = ["serde", "v4"] }

View File

@ -0,0 +1,3 @@
alter table messages
add column ward int;
create index messages_ward_idx on messages (ward);

View File

@ -1,9 +1,7 @@
#![feature(drain_filter)]
use std::collections::HashMap;
use std::fs::Permissions;
use std::net::SocketAddr;
use std::os::unix::fs::PermissionsExt;
use std::str::FromStr;
use std::sync::Arc;
@ -11,7 +9,6 @@ use anyhow::{Context, Result};
use sqlx::{Executor, Pool, Sqlite};
use sqlx::migrate::Migrator;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use tokio::fs::File;
use tokio::net::{TcpListener, UnixListener};
use tokio::runtime::Handle;
use tokio::sync::RwLock;
@ -54,7 +51,7 @@ impl State {
let text = match tokio::fs::read_to_string(entry.path()).await {
Ok(t) => t,
Err(e) => {
eprintln!("error reading pack: {:#?}", e);
eprintln!("error reading pack: {e:#?}");
continue;
}
};
@ -119,10 +116,10 @@ async fn main() -> Result<()> {
let address = state.config.address.clone();
let server = warp::serve(web::routes(state));
println!("listening at {}", address);
println!("listening at {address}");
if address.starts_with("unix:") {
let listener = UnixListener::bind(&address[5..])?;
if let Some(path) = address.strip_prefix("unix:") {
let listener = UnixListener::bind(path)?;
let stream = UnixListenerStream::new(listener);
server.run_incoming(stream).await;
} else {
@ -141,16 +138,13 @@ fn spawn_command_reader(state: Arc<State>, handle: Handle) {
while let Ok(size) = std::io::stdin().read_line(&mut line) {
let read = line[..size].trim();
match read {
"reload packs" => {
let state = Arc::clone(&state);
handle.spawn(async move {
if let Err(e) = state.update_packs().await {
eprintln!("failed to update packs: {:#?}", e);
}
});
}
_ => {}
if read == "reload packs" {
let state = Arc::clone(&state);
handle.spawn(async move {
if let Err(e) = state.update_packs().await {
eprintln!("failed to update packs: {e:#?}");
}
});
}
line.clear();

View File

@ -22,6 +22,9 @@ pub struct Message {
#[serde(default = "glyph_default")]
pub glyph: i8,
#[serde(default)]
pub ward: Option<u16>,
}
fn glyph_default() -> i8 {
@ -52,6 +55,7 @@ pub struct RetrievedMessage {
pub struct RetrievedMessageTerritory {
pub id: String,
pub territory: i64,
pub ward: Option<i64>,
pub x: f64,
pub y: f64,
pub z: f64,
@ -69,6 +73,7 @@ pub struct RetrievedMessageTerritory {
pub struct OwnMessage {
pub id: String,
pub territory: i64,
pub ward: Option<i64>,
pub x: f64,
pub y: f64,
pub z: f64,

View File

@ -1,8 +1,54 @@
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use sha3::{Digest, Sha3_384};
// TerritoryIntendedUse = 13 or 14
pub const HOUSING_ZONES: &[u32] = &[
282,
283,
284,
339,
340,
341,
342,
343,
344,
345,
346,
347,
384,
385,
386,
423,
424,
425,
573,
574,
575,
608,
609,
610,
641,
649,
650,
651,
652,
653,
654,
655,
979,
980,
981,
982,
983,
984,
985,
999,
];
pub fn hash(input: &str) -> String {
let mut hasher = Sha3_384::default();
hasher.update(input.as_bytes());
let result = hasher.finalize();
base64::encode(result)
BASE64_STANDARD.encode(result)
}

View File

@ -73,6 +73,8 @@ pub enum WebError {
TooManyMessages,
NoSuchMessage,
InvalidExtraCode,
MissingWard,
UnnecessaryWard,
}
impl Reject for WebError {}
@ -92,22 +94,24 @@ async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
WebError::TooManyMessages => (StatusCode::BAD_REQUEST, "too_many_messages", "you have run out of messages - delete one and try again".into()),
WebError::NoSuchMessage => (StatusCode::NOT_FOUND, "no_such_message", "no message with that id was found".into()),
WebError::InvalidExtraCode => (StatusCode::BAD_REQUEST, "invalid_extra_code", "that extra code was not found".into()),
WebError::MissingWard => (StatusCode::BAD_REQUEST, "missing_ward", "a ward was not provided - try updating the plugin".into()),
WebError::UnnecessaryWard => (StatusCode::BAD_REQUEST, "unnecessar_ward", "a ward was provided but not necessary - try updating the plugin".into()),
}
} else if err.is_not_found() {
(StatusCode::NOT_FOUND, "not_found", "route was unknown to the server".into())
} else if let Some(e) = err.find::<BodyDeserializeError>() {
(StatusCode::BAD_REQUEST, "invalid_body", format!("invalid body: {}", e))
} else if let Some(_) = err.find::<MethodNotAllowed>() {
(StatusCode::BAD_REQUEST, "invalid_body", format!("invalid body: {e}"))
} else if err.find::<MethodNotAllowed>().is_some() {
(StatusCode::METHOD_NOT_ALLOWED, "method_not_allowed", "that http method is not allowed on that route".into())
} else if let Some(AnyhowRejection(e)) = err.find::<AnyhowRejection>() {
eprintln!("{:#?}", e);
eprintln!("{e:#?}");
(
StatusCode::INTERNAL_SERVER_ERROR,
"internal_error",
"an internal logic error occured".into(),
)
} else {
eprintln!("{:#?}", err);
eprintln!("{err:#?}");
(
StatusCode::INTERNAL_SERVER_ERROR,
"internal_error",

View File

@ -4,12 +4,14 @@ use anyhow::Context;
use chrono::{Duration, Utc};
use rand::Rng;
use rand::seq::SliceRandom;
use serde::Deserialize;
use warp::{Filter, Rejection, Reply};
use warp::filters::BoxedFilter;
use crate::message::RetrievedMessage;
use crate::State;
use crate::web::AnyhowRejection;
use crate::util::HOUSING_ZONES;
use crate::web::{AnyhowRejection, WebError};
pub fn get_location(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
warp::get()
@ -17,11 +19,27 @@ pub fn get_location(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
.and(warp::path::param())
.and(warp::path::end())
.and(super::get_id(Arc::clone(&state)))
.and_then(move |location: u32, (id, _)| logic(Arc::clone(&state), id, location))
.and(warp::query::<GetLocationQuery>())
.and_then(move |location: u32, (id, _), query| logic(Arc::clone(&state), id, location, query))
.boxed()
}
async fn logic(state: Arc<State>, id: i64, location: u32) -> Result<impl Reply, Rejection> {
#[derive(Deserialize)]
pub struct GetLocationQuery {
#[serde(default)]
ward: Option<u32>,
}
async fn logic(state: Arc<State>, id: i64, location: u32, query: GetLocationQuery) -> Result<impl Reply, Rejection> {
let housing = HOUSING_ZONES.contains(&location);
if housing && query.ward.is_none() {
return Err(warp::reject::custom(WebError::MissingWard));
}
if !housing && query.ward.is_some() {
return Err(warp::reject::custom(WebError::UnnecessaryWard));
}
let location = location as i64;
let mut messages = sqlx::query_as!(
RetrievedMessage,
@ -44,10 +62,11 @@ async fn logic(state: Arc<State>, id: i64, location: u32) -> Result<impl Reply,
left join votes v on m.id = v.message
left join votes v2 on m.id = v2.message and v2.user = ?
inner join users u on m.user = u.id
where m.territory = ?
where m.territory = ? and m.ward = ?
group by m.id"#,
id,
location,
query.ward,
)
.fetch_all(&state.db)
.await

View File

@ -27,6 +27,7 @@ async fn logic(state: Arc<State>, id: i64, message_id: Uuid) -> Result<impl Repl
r#"
select m.id,
m.territory,
m.ward,
m.x,
m.y,
m.z,

View File

@ -31,6 +31,7 @@ async fn logic(state: Arc<State>, id: i64, extra: i64, mut query: HashMap<String
r#"
select m.id,
m.territory,
m.ward,
m.x,
m.y,
m.z,

View File

@ -7,6 +7,7 @@ use warp::filters::BoxedFilter;
use crate::message::Message;
use crate::State;
use crate::util::HOUSING_ZONES;
use crate::web::{AnyhowRejection, WebError};
pub fn write(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
@ -21,6 +22,15 @@ pub fn write(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
}
async fn logic(state: Arc<State>, id: i64, extra: i64, message: Message) -> Result<impl Reply, Rejection> {
let housing = HOUSING_ZONES.contains(&message.territory);
if housing && message.ward.is_none() {
return Err(warp::reject::custom(WebError::MissingWard));
}
if !housing && message.ward.is_some() {
return Err(warp::reject::custom(WebError::UnnecessaryWard));
}
let text = {
let packs = state.packs.read().await;
let pack = packs.get(&message.pack_id)
@ -57,10 +67,11 @@ async fn logic(state: Arc<State>, id: i64, extra: i64, message: Message) -> Resu
sqlx::query!(
// language=sqlite
"insert into messages (id, user, territory, x, y, z, yaw, message, glyph) values (?, ?, ?, ?, ?, ?, ?, ?, ?)",
"insert into messages (id, user, territory, ward, x, y, z, yaw, message, glyph) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
message_id,
id,
territory,
message.ward,
message.x,
message.y,
message.z,