an http based roguelike rpg about jelqing
Commit e76ab808e7ad
tarball4 files changed+25-17
@@ -261,25 +261,29 @@ impl Run {
261261
cause.problem().with("turn", self.turn)262262
}263263
264
+ /// Ends the refractory period once it has run out, whatever the request.265
+ pub fn recover(&mut self, now: Instant) {266
+ if self.refractory_until.is_some_and(|until| now >= until) {267
+ self.refractory_until = None;268
+ self.stamina = self.max_stamina();269
+ self.eq = STARTING_EQ;270
+ self.warmed_up = false;271
+ self.touch();272
+ }273
+ }274
+264275
fn check_refractory(&mut self, now: Instant) -> Result<(), Problem> {276
+ self.recover(now);265277
let Some(until) = self.refractory_until else {266278
return Ok(());267279
};268
- if now < until {269
- let seconds = (until - now).as_secs() + 1;270
- return Err(Problem::new(271
- StatusCode::TOO_MANY_REQUESTS,272
- "overtrained",273
- "Stamina is exhausted. The refractory period is in effect.",274
- )275
- .header(header::RETRY_AFTER, seconds.to_string()));276
- }277
- self.refractory_until = None;278
- self.stamina = self.max_stamina();279
- self.eq = STARTING_EQ;280
- self.warmed_up = false;281
- self.touch();282
- Ok(())280
+ let seconds = until.saturating_duration_since(now).as_secs() + 1;281
+ Err(Problem::new(282
+ StatusCode::TOO_MANY_REQUESTS,283
+ "overtrained",284
+ "Stamina is exhausted. The refractory period is in effect.",285
+ )286
+ .header(header::RETRY_AFTER, seconds.to_string()))283287
}284288
285289
fn exhaust(&mut self, now: Instant) {@@ -246,14 +246,12 @@ impl Run {
246246
if blocked {247247
return Vec::new();248248
}249
- let rested = self.refractory_until.is_some();250249
Technique::ALL251250
.into_iter()252251
.filter(|&technique| match technique {253252
Technique::Utility(Utility::Warmup) => true,254253
Technique::Utility(Utility::Ice) => self.items.ice_packs > 0,255254
Technique::Utility(Utility::Supplement) => self.items.supplements > 0,256
- _ if rested => false,257255
_ => self.access(technique).is_ok() && self.check_storage().is_ok(),258256
})259257
.collect()@@ -57,6 +57,7 @@ pub fn serve(state: &AppState, mut ctx: Ctx, target: &RunResource) -> Response {
5757
.into_response();5858
}5959
run.last_seen = ctx.now;60
+ run.recover(ctx.now);6061
run.observed = None;6162
let response =6263
gate(run, &ctx, target.scope).unwrap_or_else(|| target.resource.dispatch(run, &ctx));@@ -209,6 +209,8 @@ function renderAlerts(list) {
209209
alerts.push(titled(text) + ".");210210
}211211
}212
+ const stamina = (list || []).find(([name]) => name === "x-stamina");213
+ if (stamina && stamina[1].startsWith("0/")) alerts.push("Exhausted. Wait out the refractory period, then GET the run.");212214
for (const alert of alerts) box.append(el("h2", { textContent: alert }));213215
}214216
@@ -327,7 +329,10 @@ function describe(r) {
327329
for (const key of ["type", "title", "status", "detail"]) delete rest[key];328330
if (Object.keys(rest).length) lines.push(short(JSON.stringify(rest, null, 2)).replace(/^/gm, " "));329331
} else if (r.body) {332
+ const range = (r.headers.find(h => h[0] === "content-range") || [])[1] || "";333
+ const partial = r.status === 206 && range.match(/^bytes (\d+)-(\d+)\/(\d+)$/);330334
lines.push(" " + r.body);335
+ if (partial) lines.push(" [bytes " + partial[1] + "-" + partial[2] + " of " + partial[3] + ". A 206 carries only part of the representation, as RFC 9110 requires.]");331336
}332337
return lines.join("\n");333338
}