Skip to content

Commit 5560fbd

Browse files
committed
fix: updated SEA times for guild hunt and dance (moved forward an hour) hopefully fixing issue finally?
refactor: converted short-lived thread spawn on hp reports to a sender channel report_hp call separate from main thread to prevent multiple threads per call refactor: updated reqwest function names due to breaking changes in 0.13 chore: update dependencies and bump version(s)
1 parent 6034f1b commit 5560fbd

12 files changed

Lines changed: 377 additions & 169 deletions

File tree

apps/desktop/Cargo.lock

Lines changed: 199 additions & 46 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/desktop/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bptimer-desktop"
3-
version = "0.1.8"
3+
version = "0.1.9"
44
edition = "2024"
55

66
[dependencies]
@@ -28,7 +28,7 @@ netdev = "0.40.0"
2828
open = "5.3"
2929
pcap = { version = "2.4.0", features = ["capture-stream"] }
3030
prost = "0.14.1"
31-
reqwest = { version = "0.12.28", features = ["json", "stream", "rustls-tls", "blocking"], default-features = false }
31+
reqwest = { version = "0.13.1", features = ["json", "stream", "rustls", "blocking"], default-features = false }
3232
self_update = "0.42.0"
3333
serde = { version = "1.0", features = ["derive"] }
3434
serde_json = "1.0"

apps/desktop/src/api/bptimer.rs

Lines changed: 137 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::utils::constants;
22
use std::collections::{HashMap, HashSet};
3-
use std::sync::{Arc, Mutex};
3+
use std::sync::mpsc::{self, Sender};
4+
use std::sync::{Arc, Mutex, OnceLock};
45
use std::time::{SystemTime, UNIX_EPOCH};
56

67
const CACHE_EXPIRY_MS: u64 = 5 * 60 * 1000;
@@ -34,6 +35,110 @@ pub struct BPTimerClient {
3435
unsafe impl Send for BPTimerClient {}
3536
unsafe impl Sync for BPTimerClient {}
3637

38+
struct HpReportTask {
39+
api_url: String,
40+
api_key: String,
41+
payload: serde_json::Value,
42+
cache: Arc<Mutex<HashMap<String, CacheEntry>>>,
43+
cache_key: String,
44+
rounded_hp_pct: f32,
45+
monster_id: u32,
46+
line: i32,
47+
rounded_pos_x: Option<f32>,
48+
rounded_pos_y: Option<f32>,
49+
rounded_pos_z: Option<f32>,
50+
}
51+
52+
static HP_REPORT_SENDER: OnceLock<Sender<HpReportTask>> = OnceLock::new();
53+
54+
fn get_hp_report_sender() -> &'static Sender<HpReportTask> {
55+
HP_REPORT_SENDER.get_or_init(|| {
56+
let (tx, rx) = mpsc::channel::<HpReportTask>();
57+
58+
std::thread::spawn(move || {
59+
let client = reqwest::blocking::Client::builder()
60+
.user_agent(&crate::utils::constants::user_agent())
61+
.tls_backend_rustls()
62+
.build()
63+
.unwrap_or_else(|_| reqwest::blocking::Client::new());
64+
65+
while let Ok(task) = rx.recv() {
66+
match client
67+
.post(&task.api_url)
68+
.header("X-API-Key", &task.api_key)
69+
.header("Content-Type", "application/json")
70+
.json(&task.payload)
71+
.send()
72+
{
73+
Ok(resp) => {
74+
if resp.status().is_success() {
75+
let mob_name =
76+
constants::get_mob_name(task.monster_id).unwrap_or_else(|| {
77+
format!("Unknown Monster ({})", task.monster_id)
78+
});
79+
let pos_info = match (
80+
task.rounded_pos_x,
81+
task.rounded_pos_y,
82+
task.rounded_pos_z,
83+
) {
84+
(Some(x), Some(y), Some(z)) => {
85+
format!(" X: {:.2}, Y: {:.2}, Z: {:.2}", x, y, z)
86+
}
87+
_ => String::new(),
88+
};
89+
log::info!(
90+
"[BPTimer] Reported {}% HP for {} ({}) on Line {}{}",
91+
task.rounded_hp_pct,
92+
mob_name,
93+
task.monster_id,
94+
task.line,
95+
pos_info
96+
);
97+
} else {
98+
let status = resp.status();
99+
if status.as_u16() == 409 {
100+
// 409 Conflict is expected when multiple clients are on the same line
101+
log::info!(
102+
"[BPTimer] HP Report skipped: Already reported by another user."
103+
);
104+
} else {
105+
let message = resp.text().ok().and_then(|body| {
106+
serde_json::from_str::<serde_json::Value>(&body)
107+
.ok()
108+
.and_then(|json| {
109+
json.get("message")
110+
.or_else(|| json.get("error"))
111+
.and_then(|v| v.as_str())
112+
.map(|s| s.to_string())
113+
})
114+
});
115+
116+
let error_msg = message
117+
.map(|m| format!("{} - {}", status, m))
118+
.unwrap_or_else(|| status.to_string());
119+
log::warn!("[BPTimer] Failed to report HP: {}", error_msg);
120+
}
121+
}
122+
}
123+
Err(e) => {
124+
log::warn!("[BPTimer] Failed to report HP: {}", e);
125+
}
126+
};
127+
128+
// Update cache on both success and error to prevent spam retries
129+
if let Ok(mut cache_guard) = task.cache.lock() {
130+
if let Some(entry) = cache_guard.get_mut(&task.cache_key) {
131+
entry.is_pending = false;
132+
entry.last_reported_hp = Some(task.rounded_hp_pct);
133+
}
134+
}
135+
}
136+
});
137+
138+
tx
139+
})
140+
}
141+
37142
impl BPTimerClient {
38143
pub fn new(api_url: String, api_key: String) -> Self {
39144
Self {
@@ -47,7 +152,7 @@ impl BPTimerClient {
47152
fn create_http_client() -> reqwest::blocking::Client {
48153
reqwest::blocking::Client::builder()
49154
.user_agent(&crate::utils::constants::user_agent())
50-
.use_rustls_tls()
155+
.tls_backend_rustls()
51156
.build()
52157
.unwrap_or_else(|_| reqwest::blocking::Client::new())
53158
}
@@ -138,93 +243,41 @@ impl BPTimerClient {
138243
let rounded_pos_z = pos_z
139244
.map(|z| (z * POSITION_ROUNDING_MULTIPLIER).round() / POSITION_ROUNDING_MULTIPLIER);
140245

141-
// Clone values for thread
142-
let api_url = self.api_url.clone();
143-
let api_key = self.api_key.clone();
144-
let cache_key_clone = cache_key.clone();
145-
let cache = self.cache.clone();
146-
147-
// Spawn thread for blocking HTTP call
148-
std::thread::spawn(move || {
149-
let client = Self::create_http_client();
246+
let payload = serde_json::json!({
247+
"monster_id": monster_id as i32,
248+
"hp_pct": rounded_hp_pct as i32,
249+
"line": line,
250+
"pos_x": rounded_pos_x,
251+
"pos_y": rounded_pos_y,
252+
"pos_z": rounded_pos_z,
253+
"account_id": account_id,
254+
"uid": uid,
255+
});
150256

151-
let url = format!("{}/api/create-hp-report", api_url);
152-
153-
let payload = serde_json::json!({
154-
"monster_id": monster_id as i32,
155-
"hp_pct": rounded_hp_pct as i32,
156-
"line": line,
157-
"pos_x": rounded_pos_x,
158-
"pos_y": rounded_pos_y,
159-
"pos_z": rounded_pos_z,
160-
"account_id": account_id,
161-
"uid": uid,
162-
});
163-
164-
match client
165-
.post(&url)
166-
.header("X-API-Key", &api_key)
167-
.header("Content-Type", "application/json")
168-
.json(&payload)
169-
.send()
170-
{
171-
Ok(resp) => {
172-
if resp.status().is_success() {
173-
let mob_name = constants::get_mob_name(monster_id)
174-
.unwrap_or_else(|| format!("Unknown Monster ({monster_id})"));
175-
let pos_info = match (rounded_pos_x, rounded_pos_y, rounded_pos_z) {
176-
(Some(x), Some(y), Some(z)) => {
177-
format!(" X: {:.2}, Y: {:.2}, Z: {:.2}", x, y, z)
178-
}
179-
_ => String::new(),
180-
};
181-
log::info!(
182-
"[BPTimer] Reported {}% HP for {} ({}) on Line {}{}",
183-
rounded_hp_pct,
184-
mob_name,
185-
monster_id,
186-
line,
187-
pos_info
188-
);
189-
} else {
190-
let status = resp.status();
191-
if status.as_u16() == 409 {
192-
// 409 Conflict is expected when multiple clients are on the same line
193-
log::info!(
194-
"[BPTimer] HP Report skipped: Already reported by another user."
195-
);
196-
} else {
197-
let message = resp.text().ok().and_then(|body| {
198-
serde_json::from_str::<serde_json::Value>(&body)
199-
.ok()
200-
.and_then(|json| {
201-
json.get("message")
202-
.or_else(|| json.get("error"))
203-
.and_then(|v| v.as_str())
204-
.map(|s| s.to_string())
205-
})
206-
});
207-
208-
let error_msg = message
209-
.map(|m| format!("{} - {}", status, m))
210-
.unwrap_or_else(|| status.to_string());
211-
log::warn!("[BPTimer] Failed to report HP: {}", error_msg);
212-
}
213-
}
214-
}
215-
Err(e) => {
216-
log::warn!("[BPTimer] Failed to report HP: {}", e);
217-
}
218-
};
257+
let task = HpReportTask {
258+
api_url: format!("{}/api/create-hp-report", self.api_url),
259+
api_key: self.api_key.clone(),
260+
payload,
261+
cache: self.cache.clone(),
262+
cache_key: cache_key.clone(),
263+
rounded_hp_pct,
264+
monster_id,
265+
line,
266+
rounded_pos_x,
267+
rounded_pos_y,
268+
rounded_pos_z,
269+
};
219270

220-
// Update cache on both success and error to prevent spam retries
221-
if let Ok(mut cache_guard) = cache.lock() {
222-
if let Some(entry) = cache_guard.get_mut(&cache_key_clone) {
223-
entry.is_pending = false;
271+
if let Err(e) = get_hp_report_sender().send(task) {
272+
log::error!("[BPTimer] Failed to queue HP report: {}", e);
273+
// Worker thread died - reset cache to prevent blocking future reports
274+
if let Ok(mut cache_guard) = self.cache.lock() {
275+
if let Some(entry) = cache_guard.get_mut(&cache_key) {
224276
entry.last_reported_hp = Some(rounded_hp_pct);
277+
entry.is_pending = false;
225278
}
226279
}
227-
});
280+
}
228281
}
229282

230283
/// Test API connection

apps/desktop/src/api/pocketbase.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl PocketBaseClient {
6666
let client = Client::builder()
6767
.user_agent(&crate::utils::constants::user_agent())
6868
.timeout(Duration::from_secs(600))
69-
.use_rustls_tls()
69+
.tls_backend_rustls()
7070
.build()
7171
.unwrap_or_else(|_| Client::new());
7272
Self {

apps/pocketbase/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ require (
5050
golang.org/x/sys v0.39.0 // indirect
5151
golang.org/x/text v0.32.0 // indirect
5252
google.golang.org/protobuf v1.36.11 // indirect
53-
modernc.org/libc v1.67.2 // indirect
53+
modernc.org/libc v1.67.4 // indirect
5454
modernc.org/mathutil v1.7.1 // indirect
5555
modernc.org/memory v1.11.0 // indirect
56-
modernc.org/sqlite v1.41.0 // indirect
56+
modernc.org/sqlite v1.42.2 // indirect
5757
)

apps/pocketbase/go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
157157
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
158158
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
159159
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
160-
modernc.org/libc v1.67.2 h1:ZbNmly1rcbjhot5jlOZG0q4p5VwFfjwWqZ5rY2xxOXo=
161-
modernc.org/libc v1.67.2/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA=
160+
modernc.org/libc v1.67.4 h1:zZGmCMUVPORtKv95c2ReQN5VDjvkoRm9GWPTEPuvlWg=
161+
modernc.org/libc v1.67.4/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA=
162162
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
163163
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
164164
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
@@ -167,8 +167,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
167167
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
168168
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
169169
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
170-
modernc.org/sqlite v1.41.0 h1:bJXddp4ZpsqMsNN1vS0jWo4IJTZzb8nWpcgvyCFG9Ck=
171-
modernc.org/sqlite v1.41.0/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
170+
modernc.org/sqlite v1.42.2 h1:7hkZUNJvJFN2PgfUdjni9Kbvd4ef4mNLOu0B9FGxM74=
171+
modernc.org/sqlite v1.42.2/go.mod h1:+VkC6v3pLOAE0A0uVucQEcbVW0I5nHCeDaBf+DpsQT8=
172172
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
173173
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
174174
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

apps/web/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
"mode-watcher": "^1.1.0",
2222
"pako": "^2.1.0",
2323
"pocketbase": "^0.26.5",
24-
"simple-icons": "^16.2.0",
25-
"zod": "^4.2.1"
24+
"simple-icons": "^16.3.0",
25+
"zod": "^4.3.4"
2626
},
2727
"devDependencies": {
2828
"@internationalized/date": "^3.10.1",

apps/web/src/lib/components/navigation/sidebar.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
rel="noopener noreferrer"
101101
class="hover:underline"
102102
>
103-
Buy me a coffee ☕ | v1.4.5
103+
Buy me a coffee ☕ | v1.4.6
104104
</a>
105105
</p>
106106
{/if}

apps/web/src/lib/utils/event-timer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export const EVENT_CONFIGS: EventConfig[] = [
8686
},
8787
SEA: {
8888
days: [5, 6, 0],
89-
hour: 2, // 10:00 - 24:00 UTC+8
89+
hour: 3, // 11:00 - 01:00 UTC+8
9090
minute: 0,
9191
durationHours: 14
9292
}
@@ -105,7 +105,7 @@ export const EVENT_CONFIGS: EventConfig[] = [
105105
},
106106
SEA: {
107107
days: [5],
108-
hour: 11, // 19:30 - 19:55 UTC+8
108+
hour: 12, // 20:30 - 20:55 UTC+8
109109
minute: 30,
110110
durationHours: 0,
111111
durationMinutes: 25

0 commit comments

Comments
 (0)