dingus ocringus

This commit is contained in:
Anna 2022-09-03 08:59:06 -04:00
parent 216a62708e
commit cef98173e3
10 changed files with 183 additions and 21 deletions

View File

@ -64,8 +64,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="elden-ring.yaml"/> <EmbeddedResource Include="../server/packs/*.yaml" LinkBase="packs"/>
<EmbeddedResource Include="elden-ring.yaml"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,7 +1,29 @@
using YamlDotNet.Serialization.NamingConventions;
namespace OrangeGuidanceTomestone; namespace OrangeGuidanceTomestone;
[Serializable] [Serializable]
public class Pack { public class Pack {
internal static Lazy<Pack[]> All { get; } = new(() => {
var des = new YamlDotNet.Serialization.DeserializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.Build();
return new[] {
"ffxiv",
"elden-ring",
"dark-souls",
}
.Select(name => {
try {
return des.Deserialize<Pack>(Resourcer.Resource.AsString($"{name}.yaml"));
} catch {
return null;
}
})
.Where(pack => pack != null)
.ToArray()!;
});
public string Name { get; init; } public string Name { get; init; }
public Guid Id { get; init; } public Guid Id { get; init; }
public string[] Templates { get; init; } public string[] Templates { get; init; }

View File

@ -1,6 +1,5 @@
using System.Text; using System.Text;
using ImGuiNET; using ImGuiNET;
using YamlDotNet.Serialization.NamingConventions;
namespace OrangeGuidanceTomestone; namespace OrangeGuidanceTomestone;
@ -33,14 +32,40 @@ public class PluginUi : IDisposable {
return; return;
} }
var packPrev = Pack.All.Value[this._pack].Name;
if (ImGui.BeginCombo("Pack", packPrev)) {
for (var i = 0; i < Pack.All.Value.Length; i++) {
var selPack = Pack.All.Value[i];
if (!ImGui.Selectable(selPack.Name)) {
continue;
}
this._pack = i;
this._part1 = -1;
this._word1 = (-1, -1);
this._conj = -1;
this._part2 = -1;
this._word2 = (-1, -1);
}
ImGui.EndCombo();
}
const string placeholder = "****";
void DrawPicker(string id, IReadOnlyList<string> items, ref int x) { void DrawPicker(string id, IReadOnlyList<string> items, ref int x) {
var preview = x == -1 ? "" : items[x].Replace("{0}", "****"); var preview = x == -1 ? "" : items[x].Replace("{0}", placeholder);
if (!ImGui.BeginCombo(id, preview)) { if (!ImGui.BeginCombo(id, preview)) {
return; return;
} }
if (ImGui.Selectable("<none>")) {
x = -1;
}
for (var i = 0; i < items.Count; i++) { for (var i = 0; i < items.Count; i++) {
var template = items[i].Replace("{0}", "****"); var template = items[i].Replace("{0}", placeholder);
if (ImGui.Selectable(template, i == x)) { if (ImGui.Selectable(template, i == x)) {
x = i; x = i;
} }
@ -73,18 +98,15 @@ public class PluginUi : IDisposable {
ImGui.EndCombo(); ImGui.EndCombo();
} }
var pack = new YamlDotNet.Serialization.DeserializerBuilder() var pack = Pack.All.Value[this._pack];
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.Build()
.Deserialize<Pack>(Resourcer.Resource.AsString("elden-ring.yaml"));
if (this._part1 == -1) { if (this._part1 == -1) {
ImGui.TextUnformatted("****"); ImGui.TextUnformatted(placeholder);
} else { } else {
var preview = new StringBuilder(); var preview = new StringBuilder();
var template1 = pack.Templates[this._part1]; var template1 = pack.Templates[this._part1];
var word1 = this._word1 == (-1, -1) ? "****" : pack.Words[this._word1.Item1].Words[this._word1.Item2]; var word1 = this._word1 == (-1, -1) ? placeholder : pack.Words[this._word1.Item1].Words[this._word1.Item2];
preview.Append(string.Format(template1, word1)); preview.Append(string.Format(template1, word1));
if (this._conj != -1) { if (this._conj != -1) {
@ -98,7 +120,7 @@ public class PluginUi : IDisposable {
if (this._part2 != -1) { if (this._part2 != -1) {
var template2 = pack.Templates[this._part2]; var template2 = pack.Templates[this._part2];
var word2 = this._word2 == (-1, -1) ? "****" : pack.Words[this._word2.Item1].Words[this._word2.Item2]; var word2 = this._word2 == (-1, -1) ? placeholder : pack.Words[this._word2.Item1].Words[this._word2.Item2];
preview.Append(string.Format(template2, word2)); preview.Append(string.Format(template2, word2));
} }
} }
@ -109,12 +131,90 @@ public class PluginUi : IDisposable {
ImGui.Separator(); ImGui.Separator();
DrawPicker("Template##part-1", pack.Templates, ref this._part1); DrawPicker("Template##part-1", pack.Templates, ref this._part1);
DrawWordPicker("Word##word-1", pack.Words, ref this._word1); if (this._part1 > -1 && pack.Templates[this._part1].Contains("{0}")) {
DrawPicker("Conjugation##conj", pack.Conjunctions, ref this._conj); DrawWordPicker("Word##word-1", pack.Words, ref this._word1);
DrawPicker("Template##part-2", pack.Templates, ref this._part2); }
DrawWordPicker("Word##word-2", pack.Words, ref this._word2);
DrawPicker("Conjunction##conj", pack.Conjunctions, ref this._conj);
if (this._conj != -1) {
DrawPicker("Template##part-2", pack.Templates, ref this._part2);
if (this._part2 > -1 && pack.Templates[this._part2].Contains("{0}")) {
DrawWordPicker("Word##word-2", pack.Words, ref this._word2);
}
}
this.ClearIfNecessary();
var valid = this.ValidSetup();
if (valid) {
ImGui.BeginDisabled();
}
if (ImGui.Button("Write") && valid) {
}
if (valid) {
ImGui.EndDisabled();
}
ImGui.End(); ImGui.End();
} }
private void ClearIfNecessary() {
if (this._pack == -1) {
this._part1 = -1;
}
var pack = Pack.All.Value[this._pack];
if (this._part1 == -1 || !pack.Templates[this._part1].Contains("{0}")) {
this._word1 = (-1, -1);
}
if (this._conj == -1) {
this._part2 = -1;
}
if (this._part2 == -1 || !pack.Templates[this._part2].Contains("{0}")) {
this._word2 = (-1, -1);
}
}
private bool ValidSetup() {
if (this._pack == -1 || this._part1 == -1) {
return false;
}
var pack = Pack.All.Value[this._pack];
var template1 = pack.Templates[this._part1];
var temp1Variable = template1.Contains("{0}");
switch (temp1Variable) {
case true when this._word1 == (-1, -1):
case false when this._word1 != (-1, -1):
return false;
}
if (this._conj == -1 && (this._part2 != -1 || this._word2 != (-1, -1))) {
return false;
}
if (this._conj != -1) {
if (this._part2 == -1) {
return false;
}
var template2 = pack.Templates[this._part2];
var temp2Variable = template2.Contains("{0}");
switch (temp2Variable) {
case true when this._word2 == (-1, -1):
case false when this._word2 != (-1, -1):
return false;
}
}
return true;
}
} }

1
server/.env Normal file
View File

@ -0,0 +1 @@
DATABASE_URL="sqlite://database.sqlite"

1
server/.gitignore vendored
View File

@ -1,2 +1,3 @@
/target /target
/database.sqlite* /database.sqlite*
/config.toml

10
server/Cargo.lock generated
View File

@ -1038,6 +1038,7 @@ dependencies = [
"sha3", "sha3",
"sqlx", "sqlx",
"tokio", "tokio",
"toml",
"uuid", "uuid",
"warp", "warp",
] ]
@ -1388,6 +1389,15 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.2" version = "0.3.2"

View File

@ -13,6 +13,7 @@ serde = { version = "1", features = ["derive"] }
serde_yaml = "0.9" serde_yaml = "0.9"
sha3 = "0.10" sha3 = "0.10"
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "sqlite", "chrono"] } sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "sqlite", "chrono"] }
toml = "0.5"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] } tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
uuid = { version = "1", features = ["serde", "v4"] } uuid = { version = "1", features = ["serde", "v4"] }
warp = "0.3" warp = "0.3"

11
server/src/config.rs Normal file
View File

@ -0,0 +1,11 @@
use std::net::SocketAddr;
use std::path::PathBuf;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct Config {
pub address: SocketAddr,
pub packs: PathBuf,
pub database: String,
}

View File

@ -1,7 +1,6 @@
#![feature(let_chains)] #![feature(let_chains)]
use std::collections::HashMap; use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@ -11,16 +10,19 @@ use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use uuid::Uuid; use uuid::Uuid;
use crate::config::Config;
use crate::pack::Pack; use crate::pack::Pack;
mod pack; mod pack;
mod message; mod message;
mod web; mod web;
mod util; mod util;
mod config;
static MIGRATOR: Migrator = sqlx::migrate!(); static MIGRATOR: Migrator = sqlx::migrate!();
pub struct State { pub struct State {
pub config: Config,
pub db: Pool<Sqlite>, pub db: Pool<Sqlite>,
pub packs: RwLock<HashMap<Uuid, Pack>>, pub packs: RwLock<HashMap<Uuid, Pack>>,
} }
@ -29,7 +31,7 @@ impl State {
pub async fn update_packs(&self) -> Result<()> { pub async fn update_packs(&self) -> Result<()> {
let mut packs = HashMap::new(); let mut packs = HashMap::new();
let mut dir = tokio::fs::read_dir("packs").await?; let mut dir = tokio::fs::read_dir(&self.config.packs).await?;
while let Ok(Some(entry)) = dir.next_entry().await { while let Ok(Some(entry)) = dir.next_entry().await {
if !entry.path().is_file() { if !entry.path().is_file() {
continue; continue;
@ -64,6 +66,18 @@ impl State {
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let args: Vec<String> = std::env::args().skip(1).collect();
if args.is_empty() {
eprintln!("usage: server [config]");
return Ok(());
}
let config_str = tokio::fs::read_to_string(&args[0])
.await
.with_context(|| format!("could not read config file at {}", args[0]))?;
let config: Config = toml::from_str(&config_str)
.context("could not parse config file")?;
let options = SqliteConnectOptions::new(); let options = SqliteConnectOptions::new();
// options.log_statements(LevelFilter::Debug); // options.log_statements(LevelFilter::Debug);
@ -76,7 +90,7 @@ async fn main() -> Result<()> {
Ok(()) Ok(())
})) }))
// .connect_with(options.filename(&config.database.path)) // .connect_with(options.filename(&config.database.path))
.connect_with(options.filename("./database.sqlite")) .connect_with(options.filename(&config.database))
.await .await
.context("could not connect to database")?; .context("could not connect to database")?;
MIGRATOR.run(&pool) MIGRATOR.run(&pool)
@ -84,12 +98,14 @@ async fn main() -> Result<()> {
.context("could not run database migrations")?; .context("could not run database migrations")?;
let state = Arc::new(State { let state = Arc::new(State {
config,
db: pool, db: pool,
packs: Default::default(), packs: Default::default(),
}); });
state.update_packs().await?; state.update_packs().await?;
warp::serve(web::routes(state)).run("127.0.0.1:8080".parse::<SocketAddr>()?).await; let address = state.config.address.clone();
warp::serve(web::routes(state)).run(address).await;
Ok(()) Ok(())
} }

View File

@ -30,10 +30,11 @@ async fn logic(state: Arc<State>, id: i64, message_id: Uuid, vote: i8) -> Result
}; };
sqlx::query!( sqlx::query!(
// language=sqlite // language=sqlite
"insert or ignore into votes (user, message, vote) values (?, ?, ?)", "insert into votes (user, message, vote) values (?, ?, ?) on conflict do update set vote = ?",
id, id,
message_id, message_id,
vote, vote,
vote,
) )
.execute(&state.db) .execute(&state.db)
.await .await