diff --git a/browser.lua b/browser.lua index c2fd4da..d7c7bb0 100644 --- a/browser.lua +++ b/browser.lua @@ -12,6 +12,7 @@ local ModeSep -- lb_common.lua local ZoneAct = lb_ZoneAct local TicsToTime = lb_TicsToTime +local mapChecksum = lb_map_checksum -- lb_store.lua local GetMapRecords = lb_get_map_records @@ -50,7 +51,7 @@ local function updateMapIndex(n) mapIndex = mapIndexOffset(n) scrollPos = 1 - MapRecords = GetMapRecords(maps[mapIndex], ModeSep) + MapRecords = GetMapRecords(maps[mapIndex], mapChecksum(maps[mapIndex]), ModeSep) updateModes() end @@ -388,7 +389,7 @@ local function initBrowser(modeSep) end -- initialize MapRecords - MapRecords = GetMapRecords(gamemap, ModeSep) + MapRecords = GetMapRecords(gamemap, mapChecksum(gamemap), ModeSep) scrollPos = 1 updateModes() diff --git a/lb_common.lua b/lb_common.lua index 0557e81..5ca198e 100644 --- a/lb_common.lua +++ b/lb_common.lua @@ -44,3 +44,23 @@ rawset(_G, "lb_comp", function(a, b) 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) 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) diff --git a/lb_store.lua b/lb_store.lua index d22e4ec..dfe867f 100644 --- a/lb_store.lua +++ b/lb_store.lua @@ -5,6 +5,7 @@ -- lb_common.lua local stat_t = lb_stat_t local lbComp = lb_comp +local mapChecksum = lb_map_checksum ---------------------------- @@ -24,18 +25,26 @@ local parseScore -- Returns a list of all maps with records local function MapList() local maps = {} - for map in pairs(ColdStore) do - maps[map] = true + for mapid, checksums in pairs(ColdStore) do + maps[mapid] = $ or {} + for checksum in pairs(checksums) do + maps[mapid][checksum] = true + end end - for map in pairs(LiveStore) do - maps[map] = true + for mapid, checksums in pairs(LiveStore) do + maps[mapid] = $ or {} + for checksum in pairs(checksums) do + maps[mapid][checksum] = true + end end local maplist = {} - for map in pairs(maps) do - table.insert(maplist, map) + for mapid, checksums in pairs(maps) do + for checksum in pairs(checksums) do + table.insert(maplist, {["id"] = mapid, ["checksum"] = checksum}) + end end - table.sort(maplist) + table.sort(maplist, function(a, b) return a.id < b.id end) return maplist end @@ -45,7 +54,9 @@ rawset(_G, "lb_map_list", MapList) -- Function for adding a single record from lua local function AddColdStore(record) 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 rawset(_G, "lb_add_coldstore_record", AddColdStore) @@ -57,11 +68,12 @@ end rawset(_G, "lb_add_coldstore_record_string", AddColdStoreString) -- 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[checksum] then return end local mode = nil - for _, record in ipairs(sourceTable) do + for _, record in ipairs(sourceTable[checksum]) do mode = record.flags & modeSep dest[mode] = $ or {} table.insert(dest[mode], record) @@ -71,14 +83,14 @@ end -- GLOBAL -- Construct the leaderboard table of the supplied mapid -- combines the ColdStore and LiveStore records -local function GetMapRecords(map, modeSep) +local function GetMapRecords(map, checksum, modeSep) local mapRecords = {} -- Insert ColdStore records - insertRecords(mapRecords, ColdStore[map], modeSep) + insertRecords(mapRecords, ColdStore[map], checksum, modeSep) -- Insert LiveStore records - insertRecords(mapRecords, LiveStore[map], modeSep) + insertRecords(mapRecords, LiveStore[map], checksum, modeSep) -- Sort records for _, records in pairs(mapRecords) do @@ -104,18 +116,20 @@ end rawset(_G, "lb_get_map_records", GetMapRecords) local function insertOrReplaceRecord(map, score, modeSep) + local checksum = mapChecksum(map) LiveStore[map] = $ or {} + LiveStore[map][checksum] = $ or {} - for i, record in ipairs(LiveStore[map]) do + for i, record in ipairs(LiveStore[map][checksum]) do -- Replace the record if record.name == score.name and (record.flags & modeSep) == (score.flags & modeSep) then - LiveStore[map][i] = score + LiveStore[map][checksum][i] = score return end end - table.insert(LiveStore[map], score) + table.insert(LiveStore[map][checksum], score) -- TODO: remove excess records end @@ -130,26 +144,6 @@ local function stat_str(stat) 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 -- 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 @@ -167,24 +161,26 @@ local function SaveRecord(score, map, modeSep) 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 + for mapid, checksums in pairs(LiveStore) do + for checksum, records in pairs(checksums) 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" - ) + 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 @@ -267,7 +263,8 @@ if isserver then for l in f:lines() do local score = parseScore(l) LiveStore[score.map] = $ or {} - table.insert(LiveStore[score.map], score) + LiveStore[score.map][score.checksum] = $ or {} + table.insert(LiveStore[score.map][score.checksum], score) end f:close() diff --git a/leaderboard.lua b/leaderboard.lua index 04aea58..e08cb70 100644 --- a/leaderboard.lua +++ b/leaderboard.lua @@ -92,6 +92,7 @@ local ticsToTime = lb_TicsToTime local zoneAct = lb_ZoneAct local stat_t = lb_stat_t local lbComp = lb_comp +local mapChecksum = lb_map_checksum -- browser.lua local InitBrowser = InitBrowser @@ -403,7 +404,7 @@ local function records(player, ...) return end - mapRecords = GetMapRecords(mapnum, ST_SEP) + mapRecords = GetMapRecords(mapnum, mapChecksum(mapnum), ST_SEP) end local map = mapheaderinfo[mapnum] @@ -562,14 +563,14 @@ local function findRival(player, ...) local totalDiff = 0 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 mapRecords local rivalScore local yourScore 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 scores[mode] = $ or {} @@ -636,9 +637,10 @@ local function findRival(player, ...) CONS_Printf( player, string.format( - "%s %8s %s%9s \x80%s", - G_BuildMapName(score["rival"]["map"]), - ticsToTime(score["rival"]["time"]), + "%s %4s %8s %s%9s \x80%s", + G_BuildMapName(score.rival.map), + score.rival.checksum, + ticsToTime(score.rival.time), color, sym[diff<0] + ticsToTime(abs(diff)), modestr @@ -648,9 +650,10 @@ local function findRival(player, ...) CONS_Printf( player, string.format( - "%s %8s %9s %s", - G_BuildMapName(score["rival"]["map"]), - ticsToTime(score["rival"]["time"]), + "%s %4s %8s %9s %s", + G_BuildMapName(score.rival.map), + score.rival.checksum, + ticsToTime(score.rival.time), ticsToTime(0, true), modestr ) @@ -714,7 +717,7 @@ addHook("MapLoad", function() allowJoin(true) --printTable(lb) - MapRecords = GetMapRecords(gamemap, ST_SEP) + MapRecords = GetMapRecords(gamemap, mapChecksum(gamemap), ST_SEP) end ) @@ -1149,7 +1152,8 @@ local function saveTime(player) TimeFinished, splits, 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 @@ -1178,7 +1182,7 @@ local function saveTime(player) FlashVFlags = YellowFlash -- Reload the MapRecords - MapRecords = GetMapRecords(gamemap, ST_SEP) + MapRecords = GetMapRecords(gamemap, mapChecksum(gamemap), ST_SEP) -- Set the updated ScoreTable ScoreTable = MapRecords[Flags] diff --git a/tools/coldstore.py b/tools/coldstore.py index c5e94fd..fb1f57e 100755 --- a/tools/coldstore.py +++ b/tools/coldstore.py @@ -36,7 +36,7 @@ def CompareScore(a, b): F_SEP = 0xF 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): records = []