Show personal map records on join and replay
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/* tws_t5_leaderboard.gsc - private map records, round reminders and .leaders replay. */
|
||||
/* tws_t5_leaderboard.gsc - private map/personal records, round reminders and .leaders replay. */
|
||||
|
||||
#include maps\_utility;
|
||||
#include common_scripts\utility;
|
||||
@@ -25,7 +25,7 @@ init()
|
||||
level.tws_lb_cache = undefined;
|
||||
level.tws_lb_attempt = 0;
|
||||
level.tws_lb_chase_finished = undefined;
|
||||
scripts\sp\tws_t5_common::register_help_command(".leaders", "Show the map record again");
|
||||
scripts\sp\tws_t5_common::register_help_command(".leaders", "Show map and personal records");
|
||||
scripts\sp\tws_t5_common::register_connect_handler(::lb_welcome);
|
||||
level thread lb_chat_listener();
|
||||
level thread lb_worker();
|
||||
@@ -212,14 +212,113 @@ lb_request(manual)
|
||||
// This also applies when the cache is unavailable.
|
||||
if (manual)
|
||||
self notify("tws_lb_requested");
|
||||
self endon("tws_lb_requested");
|
||||
self.tws_lb_last_request = getTime();
|
||||
snapshot = lb_cached_record();
|
||||
if (!isDefined(snapshot) && !manual)
|
||||
return;
|
||||
|
||||
// Common owns pagination, help priority and the single service window.
|
||||
personal = lb_personal_record(self, snapshot);
|
||||
self.tws_lb_last_request = getTime();
|
||||
// Common owns display lifetime, help priority and the single service window.
|
||||
// Undefined snapshots produce a private unavailable page for manual replay.
|
||||
self scripts\sp\tws_t5_common::show_record(snapshot);
|
||||
self scripts\sp\tws_t5_common::show_record(snapshot, personal);
|
||||
}
|
||||
|
||||
// Cache per connection; a failed lookup is unavailable, never an empty history.
|
||||
lb_personal_record(player, snapshot)
|
||||
{
|
||||
if (!isDefined(snapshot))
|
||||
return "Your Best | Unavailable";
|
||||
if (snapshot.round == 0)
|
||||
return "Your Best | None yet";
|
||||
guid = lb_player_guid(player);
|
||||
if (!isDefined(guid))
|
||||
return "Your Best | Unavailable";
|
||||
cached = player.tws_lb_personal;
|
||||
if (isDefined(cached) && cached.key == snapshot.key && cached.guid == guid &&
|
||||
getTime() - cached.saved_at < cached.ttl)
|
||||
return cached.text;
|
||||
|
||||
round = lb_personal_fetch(snapshot.key, guid);
|
||||
cached = spawnStruct();
|
||||
cached.key = snapshot.key;
|
||||
cached.guid = guid;
|
||||
cached.saved_at = getTime();
|
||||
cached.ttl = 30000;
|
||||
cached.text = "Your Best | Unavailable";
|
||||
if (isDefined(round))
|
||||
{
|
||||
cached.ttl = 300000;
|
||||
cached.text = "Your Best | None yet";
|
||||
if (round > 0)
|
||||
cached.text = "Your Best | Round " + round;
|
||||
}
|
||||
player.tws_lb_personal = cached;
|
||||
return cached.text;
|
||||
}
|
||||
|
||||
lb_player_guid(player)
|
||||
{
|
||||
guid = player getGuid();
|
||||
if (!isDefined(guid) || (typeof(guid) != "string" && typeof(guid) != "int"))
|
||||
return undefined;
|
||||
guid = "" + guid;
|
||||
if (guid.size == 0 || guid.size > 64)
|
||||
return undefined;
|
||||
return guid;
|
||||
}
|
||||
|
||||
// Search also matches names and GUID substrings. Only exact string identity
|
||||
// with matching game/map evidence can supply this player's personal best.
|
||||
lb_personal_fetch(key, guid)
|
||||
{
|
||||
offset = 0;
|
||||
total = -1;
|
||||
highest = -1;
|
||||
started = getTime();
|
||||
for (;;)
|
||||
{
|
||||
// Bound broad substring searches; incomplete searches are unavailable.
|
||||
if (getTime() - started >= 15000)
|
||||
return undefined;
|
||||
page = lb_get("&map=" + lb_url_encode(key) + "&q=" + lb_url_encode(guid) + "&limit=3&offset=" + offset);
|
||||
if (!isDefined(page) || !lb_page_valid(page, offset, 3))
|
||||
return undefined;
|
||||
if (offset == 0)
|
||||
{
|
||||
total = page["total"];
|
||||
highest = page["summary"]["highestRound"];
|
||||
}
|
||||
if (page["total"] != total || page["summary"]["highestRound"] != highest ||
|
||||
offset + page["entries"].size > total)
|
||||
return undefined;
|
||||
for (i = 0; i < page["entries"].size; i++)
|
||||
{
|
||||
entry = page["entries"][i];
|
||||
if (typeof(entry) != "array" || typeof(entry["guid"]) != "string")
|
||||
return undefined;
|
||||
if (entry["guid"] != guid)
|
||||
continue;
|
||||
if (!lb_uint(entry["highestRound"]) || entry["highestRound"] < 2 ||
|
||||
entry["highestRound"] > highest || typeof(entry["records"]) != "array")
|
||||
return undefined;
|
||||
evidence = entry["records"]["highestRound"];
|
||||
if (typeof(evidence) != "array" || typeof(evidence["game"]) != "string" ||
|
||||
typeof(evidence["mapKey"]) != "string" || !lb_uint(evidence["value"]))
|
||||
return undefined;
|
||||
if (evidence["game"] != level.tws_lb_game || evidence["mapKey"] != key ||
|
||||
evidence["value"] != entry["highestRound"])
|
||||
return undefined;
|
||||
return entry["highestRound"];
|
||||
}
|
||||
offset += page["entries"].size;
|
||||
if (offset == total)
|
||||
return 0;
|
||||
if (page["entries"].size == 0 || offset > 1000000)
|
||||
return undefined;
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
lb_refresh()
|
||||
@@ -239,6 +338,7 @@ lb_refresh()
|
||||
return undefined;
|
||||
|
||||
snapshot = spawnStruct();
|
||||
snapshot.key = selection.key;
|
||||
snapshot.name = lb_display_text(selection.name, 64);
|
||||
snapshot.round = 0;
|
||||
snapshot.names = [];
|
||||
@@ -288,9 +388,18 @@ lb_selection()
|
||||
return selection;
|
||||
}
|
||||
|
||||
// All HTTP and parsing work belongs to the level worker, never the join thread.
|
||||
// Map refreshes and personal lookups share one native request slot.
|
||||
lb_get(query)
|
||||
{
|
||||
// A disconnect/replay can abandon a caller, but the collector still drains
|
||||
// its native request. Bound queue wait and never overlap that request.
|
||||
queued_at = getTime();
|
||||
while (isDefined(level.tws_lb_pending))
|
||||
{
|
||||
if (getTime() - queued_at >= 15000)
|
||||
return undefined;
|
||||
wait 0.1;
|
||||
}
|
||||
options = [];
|
||||
options["method"] = "GET";
|
||||
headers = [];
|
||||
@@ -461,28 +570,9 @@ lb_uint(value)
|
||||
lb_holder_lines(names)
|
||||
{
|
||||
lines = [];
|
||||
line = "";
|
||||
columns = 0;
|
||||
// Each tied player gets a separate row in the full record announcement.
|
||||
for (i = 0; i < names.size; i++)
|
||||
{
|
||||
name = lb_holder_name(names[i]);
|
||||
width = lb_holder_width(name);
|
||||
if (line != "" && columns + width + 2 > 16)
|
||||
{
|
||||
lines[lines.size] = line;
|
||||
line = "";
|
||||
columns = 0;
|
||||
}
|
||||
if (line != "")
|
||||
{
|
||||
line += ", ";
|
||||
columns += 2;
|
||||
}
|
||||
line += name;
|
||||
columns += width;
|
||||
}
|
||||
if (line != "")
|
||||
lines[lines.size] = line;
|
||||
lines[lines.size] = lb_holder_name(names[i]);
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user