Compare commits

...

2 Commits

Author SHA1 Message Date
Not 57120e257a add commands for moving and checksumming records 2022-11-19 01:26:46 +01:00
Not cf74af8945 map identity by checksum 2022-11-18 01:37:11 +01:00
5 changed files with 384 additions and 195 deletions

View File

@ -12,6 +12,7 @@ local ModeSep
-- lb_common.lua -- lb_common.lua
local ZoneAct = lb_ZoneAct local ZoneAct = lb_ZoneAct
local TicsToTime = lb_TicsToTime local TicsToTime = lb_TicsToTime
local mapChecksum = lb_map_checksum
-- lb_store.lua -- lb_store.lua
local GetMapRecords = lb_get_map_records local GetMapRecords = lb_get_map_records
@ -50,7 +51,7 @@ local function updateMapIndex(n)
mapIndex = mapIndexOffset(n) mapIndex = mapIndexOffset(n)
scrollPos = 1 scrollPos = 1
MapRecords = GetMapRecords(maps[mapIndex], ModeSep) MapRecords = GetMapRecords(maps[mapIndex], mapChecksum(maps[mapIndex]), ModeSep)
updateModes() updateModes()
end end
@ -388,7 +389,7 @@ local function initBrowser(modeSep)
end end
-- initialize MapRecords -- initialize MapRecords
MapRecords = GetMapRecords(gamemap, ModeSep) MapRecords = GetMapRecords(gamemap, mapChecksum(gamemap), ModeSep)
scrollPos = 1 scrollPos = 1
updateModes() updateModes()

View File

@ -1,3 +1,17 @@
rawset(_G, "lb_score_t", function(map, name, skin, color, time, splits, flags, stat, checksum)
return {
["map"] = map,
["name"] = name,
["skin"] = skin,
["color"] = color,
["time"] = time,
["splits"] = splits,
["flags"] = flags,
["stat"] = stat,
["checksum"] = checksum
}
end)
rawset(_G, "lb_TicsToTime", function(tics, pure) rawset(_G, "lb_TicsToTime", function(tics, pure)
if tics == 0 and pure then if tics == 0 and pure then
return "-:--:--" return "-:--:--"
@ -44,3 +58,52 @@ rawset(_G, "lb_comp", function(a, b)
local s = (a.flags & (F_SPBEXP | F_SPBBIG)) - (b.flags & (F_SPBEXP | F_SPBBIG)) local s = (a.flags & (F_SPBEXP | F_SPBBIG)) - (b.flags & (F_SPBEXP | F_SPBBIG))
return s > 0 or not(s < 0 or a.time >= b.time) return s > 0 or not(s < 0 or a.time >= b.time)
end) end)
local function djb2(message)
local digest = 5381
for c in message:gmatch(".") do
digest = (($ << 5) + $) + string.byte(c)
end
return digest
end
-- Produce a checksum by using the maps title, subtitle and zone
rawset(_G, "lb_map_checksum", function(mapnum)
local mh = mapheaderinfo[mapnum]
if not mh then
return nil
end
local digest = string.format("%04x", djb2(mh.lvlttl..mh.subttl..mh.zonttl))
return string.sub(digest, #digest - 3)
end)
rawset(_G, "lb_mapnum_from_extended", function(map)
local p, q = map:upper():match("MAP(%w)(%w)$", 1)
if not (p and q) then
return nil
end
local mapnum = 0
local A = string.byte("A")
if tonumber(p) != nil then
-- Non extended map numbers
if tonumber(q) == nil then
return nil
end
mapnum = tonumber(p) * 10 + tonumber(q)
else
--Extended map numbers
p = string.byte(p) - A
local qn = tonumber(q)
if qn == nil then
qn = string.byte(q) - A + 10
end
mapnum = 36 * p + qn + 100
end
return mapnum
end)

View File

@ -5,10 +5,14 @@
-- lb_common.lua -- lb_common.lua
local stat_t = lb_stat_t local stat_t = lb_stat_t
local lbComp = lb_comp local lbComp = lb_comp
local score_t = lb_score_t
local mapChecksum = lb_map_checksum
local mapnumFromExtended = lb_mapnum_from_extended
---------------------------- ----------------------------
local LEADERBOARD_FILE = "leaderboard.txt" local LEADERBOARD_FILE = "leaderboard.txt"
local COLDSTORE_FILE = "leaderboard.coldstore.txt"
-- ColdStore are records loaded from lua addons -- ColdStore are records loaded from lua addons
-- this table should never be modified outside of the AddColdStore function -- this table should never be modified outside of the AddColdStore function
@ -20,22 +24,91 @@ local LiveStore = {}
-- parse score function -- parse score function
local parseScore local parseScore
local MSK_SPEED = 0xF0
local MSK_WEIGHT = 0xF
local function stat_str(stat)
if stat then
return string.format("%d%d", (stat & MSK_SPEED) >> 4, stat & MSK_WEIGHT)
end
return "0"
end
local function isSameRecord(a, b, modeSep)
return a.name == b.name and (a.flags & modeSep) == (b.flags & modeSep)
end
-- insert or replace the score in dest
local function insertOrReplace(dest, score, modeSep)
for i, record in ipairs(dest) do
if isSameRecord(record, score, modeSep) then
if lbComp(score, record) then
dest[i] = score
end
return
end
end
table.insert(dest, score)
end
local function dumpStoreToFile(filename, store)
local f = assert(
io.open(filename, "w"),
"Failed to open file for writing: "..filename
)
f:setvbuf("line")
for mapid, checksums in pairs(store) do
for checksum, records in pairs(checksums) do
for _, record in ipairs(records) do
if not record.checksum or record.checksum == "" then
record.checksum = mapChecksum(record.map) or ""
end
f:write(
mapid, "\t",
record.name, "\t",
record.skin, "\t",
record.color, "\t",
record.time, "\t",
table.concat(record.splits, " "), "\t",
record.flags, "\t",
stat_str(record.stat), "\t",
record.checksum, "\n"
)
end
end
end
f:close()
end
-- GLOBAL -- GLOBAL
-- Returns a list of all maps with records -- Returns a list of all maps with records
local function MapList() local function MapList()
local maps = {} local maps = {}
for map in pairs(ColdStore) do for mapid, checksums in pairs(ColdStore) do
maps[map] = true maps[mapid] = $ or {}
for checksum in pairs(checksums) do
maps[mapid][checksum] = true
end
end end
for map in pairs(LiveStore) do for mapid, checksums in pairs(LiveStore) do
maps[map] = true maps[mapid] = $ or {}
for checksum in pairs(checksums) do
maps[mapid][checksum] = true
end
end end
local maplist = {} local maplist = {}
for map in pairs(maps) do for mapid, checksums in pairs(maps) do
table.insert(maplist, map) for checksum in pairs(checksums) do
table.insert(maplist, {["id"] = mapid, ["checksum"] = checksum})
end
end end
table.sort(maplist) table.sort(maplist, function(a, b) return a.id < b.id end)
return maplist return maplist
end end
@ -45,7 +118,9 @@ rawset(_G, "lb_map_list", MapList)
-- Function for adding a single record from lua -- Function for adding a single record from lua
local function AddColdStore(record) local function AddColdStore(record)
ColdStore[record.map] = $ or {} ColdStore[record.map] = $ or {}
table.insert(ColdStore[record.map], record) ColdStore[record.map][record.checksum] = $ or {}
table.insert(ColdStore[record.map][record.checksum], record)
end end
rawset(_G, "lb_add_coldstore_record", AddColdStore) rawset(_G, "lb_add_coldstore_record", AddColdStore)
@ -57,11 +132,12 @@ end
rawset(_G, "lb_add_coldstore_record_string", AddColdStoreString) rawset(_G, "lb_add_coldstore_record_string", AddColdStoreString)
-- Insert mode separated records from the flat sourceTable into dest -- Insert mode separated records from the flat sourceTable into dest
local function insertRecords(dest, sourceTable, modeSep) local function insertRecords(dest, sourceTable, checksum, modeSep)
if not sourceTable then return end if not sourceTable then return end
if not sourceTable[checksum] then return end
local mode = nil local mode = nil
for _, record in ipairs(sourceTable) do for _, record in ipairs(sourceTable[checksum]) do
mode = record.flags & modeSep mode = record.flags & modeSep
dest[mode] = $ or {} dest[mode] = $ or {}
table.insert(dest[mode], record) table.insert(dest[mode], record)
@ -71,14 +147,14 @@ end
-- GLOBAL -- GLOBAL
-- Construct the leaderboard table of the supplied mapid -- Construct the leaderboard table of the supplied mapid
-- combines the ColdStore and LiveStore records -- combines the ColdStore and LiveStore records
local function GetMapRecords(map, modeSep) local function GetMapRecords(map, checksum, modeSep)
local mapRecords = {} local mapRecords = {}
-- Insert ColdStore records -- Insert ColdStore records
insertRecords(mapRecords, ColdStore[map], modeSep) insertRecords(mapRecords, ColdStore[map], checksum, modeSep)
-- Insert LiveStore records -- Insert LiveStore records
insertRecords(mapRecords, LiveStore[map], modeSep) insertRecords(mapRecords, LiveStore[map], checksum, modeSep)
-- Sort records -- Sort records
for _, records in pairs(mapRecords) do for _, records in pairs(mapRecords) do
@ -103,92 +179,19 @@ local function GetMapRecords(map, modeSep)
end end
rawset(_G, "lb_get_map_records", GetMapRecords) rawset(_G, "lb_get_map_records", GetMapRecords)
local function insertOrReplaceRecord(map, score, modeSep)
LiveStore[map] = $ or {}
for i, record in ipairs(LiveStore[map]) do
-- Replace the record
if record.name == score.name
and (record.flags & modeSep) == (score.flags & modeSep) then
LiveStore[map][i] = score
return
end
end
table.insert(LiveStore[map], score)
-- TODO: remove excess records
end
local MSK_SPEED = 0xF0
local MSK_WEIGHT = 0xF
local function stat_str(stat)
if stat then
return string.format("%d%d", (stat & MSK_SPEED) >> 4, stat & MSK_WEIGHT)
end
return "0"
end
local function djb2(message)
local digest = 5381
for c in message:gmatch(".") do
digest = (($ << 5) + $) + string.byte(c)
end
return digest
end
-- Produce a checksum by using the maps title, subtitle and zone
local function mapChecksum(mapnum)
local mh = mapheaderinfo[mapnum]
if not mh then
return nil
end
local digest = string.format("%04x", djb2(mh.lvlttl..mh.subttl..mh.zonttl))
return string.sub(digest, #digest - 3)
end
-- GLOBAL -- GLOBAL
-- Save a record to the LiveStore and write to disk -- Save a record to the LiveStore and write to disk
-- SaveRecord will replace the record holders previous record but it will not compare any record times -- SaveRecord will replace the record holders previous record
local function SaveRecord(score, map, modeSep) local function SaveRecord(score, map, modeSep)
insertOrReplaceRecord(map, score, modeSep) local checksum = mapChecksum(map)
LiveStore[map] = $ or {}
LiveStore[map][checksum] = $ or {}
insertOrReplace(LiveStore[map][checksum], score, modeSep)
print("Saving score") print("Saving score")
if isserver then
if not isserver then return end dumpStoreToFile(LEADERBOARD_FILE, LiveStore)
local f = assert(
io.open(LEADERBOARD_FILE, "w"),
"Failed to open file for writing: "..LEADERBOARD_FILE
)
f:setvbuf("line")
for mapid, records in pairs(LiveStore) do
for _, record in ipairs(records) do
-- Insert checksum if missing
if (not record.checksum) or record.checksum == "" then
record.checksum = mapChecksum(mapid)
end
f:write(
mapid, "\t",
record.name, "\t",
record.skin, "\t",
record.color, "\t",
record.time, "\t",
table.concat(record.splits, " "), "\t",
record.flags, "\t",
stat_str(record.stat), "\t",
record.checksum or "", "\n"
)
end
end end
f:close()
end end
rawset(_G, "lb_save_record", SaveRecord) rawset(_G, "lb_save_record", SaveRecord)
@ -198,20 +201,6 @@ end
addHook("NetVars", netvars) addHook("NetVars", netvars)
local function score_t(map, name, skin, color, time, splits, flags, stat, checksum)
return {
["map"] = map,
["name"] = name,
["skin"] = skin,
["color"] = color,
["time"] = time,
["splits"] = splits,
["flags"] = flags,
["stat"] = stat,
["checksum"] = checksum
}
end
function parseScore(str) function parseScore(str)
-- Leaderboard is stored in the following tab separated format -- Leaderboard is stored in the following tab separated format
-- mapnum, name, skin, color, time, splits, flags, stat -- mapnum, name, skin, color, time, splits, flags, stat
@ -241,7 +230,7 @@ function parseScore(str)
end end
end end
local checksum = t[9] local checksum = t[9] or ""
return score_t( return score_t(
tonumber(t[1]), -- Map tonumber(t[1]), -- Map
@ -257,18 +246,145 @@ function parseScore(str)
end end
rawset(_G, "lb_parse_score", parseScore) rawset(_G, "lb_parse_score", parseScore)
-- Load the livestore -- Read and parse a store file
if isserver then local function loadStoreFile(filename)
local f = assert( local f = assert(
io.open(LEADERBOARD_FILE, "r"), io.open(filename, "r"),
"Failed to open file: "..LEADERBOARD_FILE "Failed to open file for reading: "..filename
) )
local store = {}
for l in f:lines() do for l in f:lines() do
local score = parseScore(l) local score = parseScore(l)
LiveStore[score.map] = $ or {} store[score.map] = $ or {}
table.insert(LiveStore[score.map], score) store[score.map][score.checksum] = $ or {}
table.insert(store[score.map][score.checksum], score)
end end
f:close() f:close()
return store
end
-- GLOBAL
-- Command for moving records from one map to another
local function moveRecords(from, to, modeSep)
local function moveRecordsInStore(store)
if not (store[from.id] and store[from.id][from.checksum]) then
return 0
end
local moveCount = #store[from.id][from.checksum]
store[to.id] = $ or {}
store[to.id][to.checksum] = $ or {}
for i, score in ipairs(store[from.id][from.checksum]) do
score.map = to.id
score.checksum = to.checksum
insertOrReplace(store[to.id][to.checksum], score, modeSep)
end
-- Destroy the original table
store[from.id][from.checksum] = nil
return moveCount
end
-- move livestore records and write to disk
local moveCount = moveRecordsInStore(LiveStore)
dumpStoreToFile(LEADERBOARD_FILE, LiveStore)
-- move coldstore records
if isserver then
local ok, coldstore = pcall(loadStoreFile, COLDSTORE_FILE)
if ok and coldstore then
moveRecordsInStore(coldstore)
dumpStoreToFile(COLDSTORE_FILE, coldstore)
end
end
return moveCount
end
rawset(_G, "lb_move_records", moveRecords)
-- Helper function for those upgrading from 1.2 to 1.3
COM_AddCommand("lb_write_checksums", function(player)
local count = 0
local moved = {}
-- Gather movable records (no checksum, map loaded)
for map, checksums in pairs(LiveStore) do
for checksum, records in pairs(checksums) do
if checksum == "" then
local sum = mapChecksum(map)
if not sum then continue end
moved[map] = {}
moved[map][sum] = {}
for i, record in ipairs(records) do
record.checksum = sum
table.insert(moved[map][sum], record)
end
end
end
end
-- Write moved to livestore
for map, checksums in pairs(moved) do
LiveStore[map] = $ or {}
for checksum, records in pairs(checksums) do
LiveStore[map][checksum] = $ or {}
for i, score in ipairs(records) do
table.insert(LiveStore[map][checksum], score)
end
count = $ + #records
end
LiveStore[map][""] = nil
end
if isserver then
dumpStoreToFile(LEADERBOARD_FILE, LiveStore)
end
CONS_Printf(player, string.format("Successful operation on %d records", count))
end, COM_ADMIN)
COM_AddCommand("lb_known_maps", function(player, map)
if not map then
CONS_Printf(player, "Usage: <map>")
CONS_Printf(player, "Print all known checksums under <map>")
return
end
local mapnum = mapnumFromExtended(map)
if not mapnum then
CONS_Printf(player, string.format("invalid map '%s'", map))
return
end
local known = {}
if LiveStore[mapnum] then
for checksum, records in pairs(LiveStore[mapnum]) do
known[checksum] = #records
end
end
if ColdStore[mapnum] then
for checksum, records in pairs(ColdStore[mapnum]) do
known[checksum] = $ or 0 + #records
end
end
CONS_Printf(player, "Map Chck Records")
for checksum, count in pairs(known) do
CONS_Printf(player, string.format("%s %s %d", map, checksum, count))
end
end)
-- Load the livestore
if isserver then
LiveStore = loadStoreFile(LEADERBOARD_FILE)
end end

View File

@ -1,6 +1,28 @@
-- Leaderboards written by Not -- Leaderboards written by Not
-- Reusable -- Reusable
---------- Imported functions -------------
-- lb_common.lua
local ticsToTime = lb_TicsToTime
local zoneAct = lb_ZoneAct
local stat_t = lb_stat_t
local lbComp = lb_comp
local mapChecksum = lb_map_checksum
local score_t = lb_score_t
local mapnumFromExtended = lb_mapnum_from_extended
-- browser.lua
local InitBrowser = InitBrowser
local DrawBrowser = DrawBrowser
local BrowserController = BrowserController
-- lb_store.lua
local GetMapRecords = lb_get_map_records
local SaveRecord = lb_save_record
local MapList = lb_map_list
local MoveRecords = lb_move_records
--------------------------------------------
-- Holds the current maps records table including all modes -- Holds the current maps records table including all modes
local MapRecords = {} local MapRecords = {}
@ -85,24 +107,6 @@ local scroll_to
local allowJoin local allowJoin
-- Imported functions --
-- lb_common.lua
local ticsToTime = lb_TicsToTime
local zoneAct = lb_ZoneAct
local stat_t = lb_stat_t
local lbComp = lb_comp
-- browser.lua
local InitBrowser = InitBrowser
local DrawBrowser = DrawBrowser
local BrowserController = BrowserController
-- lb_store.lua
local GetMapRecords = lb_get_map_records
local SaveRecord = lb_save_record
local MapList = lb_map_list
---------------
-- cvars -- cvars
local cv_teamchange local cv_teamchange
@ -181,19 +185,6 @@ local cv_spb_separate = CV_RegisterVar({
end end
}) })
local function score_t(map, name, skin, color, time, splits, flags, stat)
return {
["map"] = map,
["name"] = name,
["skin"] = skin,
["color"] = color,
["time"] = time,
["splits"] = splits,
["flags"] = flags,
["stat"] = stat
}
end
local MSK_SPEED = 0xF0 local MSK_SPEED = 0xF0
local MSK_WEIGHT = 0xF local MSK_WEIGHT = 0xF
@ -342,35 +333,6 @@ local function findMap(player, ...)
end end
COM_AddCommand("findmap", findMap) COM_AddCommand("findmap", findMap)
local function mapnumFromExtended(map)
local p, q = map:upper():match("MAP(%w)(%w)$", 1)
if not (p and q) then
return nil
end
local mapnum = 0
local A = string.byte("A")
if tonumber(p) != nil then
-- Non extended map numbers
if tonumber(q) == nil then
return nil
end
mapnum = tonumber(p) * 10 + tonumber(q)
else
--Extended map numbers
p = string.byte(p) - A
local qn = tonumber(q)
if qn == nil then
qn = string.byte(q) - A + 10
end
mapnum = 36 * p + qn + 100
end
return mapnum
end
local SPBModeSym = { local SPBModeSym = {
[F_SPBEXP] = "X", [F_SPBEXP] = "X",
[F_SPBBIG] = "B", [F_SPBBIG] = "B",
@ -403,7 +365,7 @@ local function records(player, ...)
return return
end end
mapRecords = GetMapRecords(mapnum, ST_SEP) mapRecords = GetMapRecords(mapnum, mapChecksum(mapnum), ST_SEP)
end end
local map = mapheaderinfo[mapnum] local map = mapheaderinfo[mapnum]
@ -562,14 +524,14 @@ local function findRival(player, ...)
local totalDiff = 0 local totalDiff = 0
CONS_Printf(player, string.format("\x89%s's times:", rival)) CONS_Printf(player, string.format("\x89%s's times:", rival))
CONS_Printf(player, "MAP Time Diff Mode") CONS_Printf(player, "MAP CHCK Time Diff Mode")
local maplist = MapList() local maplist = MapList()
local mapRecords local mapRecords
local rivalScore local rivalScore
local yourScore local yourScore
for i = 1, #maplist do for i = 1, #maplist do
mapRecords = GetMapRecords(maplist[i], ST_SEP) mapRecords = GetMapRecords(maplist[i].id, maplist[i].checksum, ST_SEP)
for mode, records in pairs(mapRecords) do for mode, records in pairs(mapRecords) do
scores[mode] = $ or {} scores[mode] = $ or {}
@ -636,9 +598,10 @@ local function findRival(player, ...)
CONS_Printf( CONS_Printf(
player, player,
string.format( string.format(
"%s %8s %s%9s \x80%s", "%s %4s %8s %s%9s \x80%s",
G_BuildMapName(score["rival"]["map"]), G_BuildMapName(score.rival.map),
ticsToTime(score["rival"]["time"]), score.rival.checksum,
ticsToTime(score.rival.time),
color, color,
sym[diff<0] + ticsToTime(abs(diff)), sym[diff<0] + ticsToTime(abs(diff)),
modestr modestr
@ -648,9 +611,10 @@ local function findRival(player, ...)
CONS_Printf( CONS_Printf(
player, player,
string.format( string.format(
"%s %8s %9s %s", "%s %4s %8s %9s %s",
G_BuildMapName(score["rival"]["map"]), G_BuildMapName(score.rival.map),
ticsToTime(score["rival"]["time"]), score.rival.checksum,
ticsToTime(score.rival.time),
ticsToTime(0, true), ticsToTime(0, true),
modestr modestr
) )
@ -680,6 +644,50 @@ local function findRival(player, ...)
end end
COM_AddCommand("rival", findRival) COM_AddCommand("rival", findRival)
local function moveRecords(player, from_map, from_checksum, to_map, to_checksum)
if not(from_map and from_checksum and to_map) then
CONS_Printf(player, "Usage: lb_move_records <from_map> <from_checksum> <to_map> [<to_checksum>]")
CONS_Printf(
player,
string.format(
"Summary: Move records from one map to another.\n"..
"If no <to_checksum> is supplied then the checksum of the current loaded map %s is used.\n"..
"Hint: Use lb_known_maps to find checksums",
to_map or "<to_map>"
)
)
return
end
local from = {
["id"] = mapnumFromExtended(from_map),
["checksum"] = from_checksum
}
local to = {
["id"] = mapnumFromExtended(to_map),
}
to.checksum = to_checksum or mapChecksum(to.id)
if not to.checksum then
CONS_Printf(player, string.format("error: '%s' is missing; provide to_checksum to continue", to.id))
return
end
CONS_Printf(
player,
string.format(
"%d records have been moved from %s %s to %s %s",
MoveRecords(from, to, ST_SEP),
from_map, from.checksum,
to_map, to.checksum
)
)
CONS_Printf(player, "Please repack coldstore and restart the server for changes to take effect.")
end
COM_AddCommand("lb_move_records", moveRecords)
--DEBUGGING --DEBUGGING
--local function printTable(tb) --local function printTable(tb)
-- for mode, tbl in pairs(tb) do -- for mode, tbl in pairs(tb) do
@ -714,7 +722,7 @@ addHook("MapLoad", function()
allowJoin(true) allowJoin(true)
--printTable(lb) --printTable(lb)
MapRecords = GetMapRecords(gamemap, ST_SEP) MapRecords = GetMapRecords(gamemap, mapChecksum(gamemap), ST_SEP)
end end
) )
@ -1149,7 +1157,8 @@ local function saveTime(player)
TimeFinished, TimeFinished,
splits, splits,
Flags, Flags,
stat_t(player.HMRs or pskin.kartspeed, player.HMRw or pskin.kartweight) stat_t(player.HMRs or pskin.kartspeed, player.HMRw or pskin.kartweight),
mapChecksum(gamemap)
) )
-- Check if you beat your previous best -- Check if you beat your previous best
@ -1178,7 +1187,7 @@ local function saveTime(player)
FlashVFlags = YellowFlash FlashVFlags = YellowFlash
-- Reload the MapRecords -- Reload the MapRecords
MapRecords = GetMapRecords(gamemap, ST_SEP) MapRecords = GetMapRecords(gamemap, mapChecksum(gamemap), ST_SEP)
-- Set the updated ScoreTable -- Set the updated ScoreTable
ScoreTable = MapRecords[Flags] ScoreTable = MapRecords[Flags]
@ -1188,12 +1197,12 @@ local function saveTime(player)
end end
-- DEBUGGING -- DEBUGGING
--local function saveLeaderboard(player, ...) local function saveLeaderboard(player, ...)
-- TimeFinished = tonumber(... or player.realtime) TimeFinished = tonumber(... or player.realtime)
-- splits = {1000, 2000, 3000} splits = {1000, 2000, 3000}
-- saveTime(player) saveTime(player)
--end end
--COM_AddCommand("save", saveLeaderboard) COM_AddCommand("save", saveLeaderboard)
local function regLap(player) local function regLap(player)
if player.laps > prevLap and TimeFinished == 0 then if player.laps > prevLap and TimeFinished == 0 then

View File

@ -36,7 +36,7 @@ def CompareScore(a, b):
F_SEP = 0xF F_SEP = 0xF
def SameScore(a, b): def SameScore(a, b):
return a["name"] == b["name"] and (a["flags"] & F_SEP) == (b["flags"] & F_SEP) return a["name"] == b["name"] and a["checksum"] == b["checksum"] and (a["flags"] & F_SEP) == (b["flags"] & F_SEP)
def LoadRecordsFromFile(path): def LoadRecordsFromFile(path):
records = [] records = []