koragg Posted January 25, 2017 Share Posted January 25, 2017 (edited) Hi guys, I tried to edit my race_toptimes resource so that it would always show the current nickname of the player who has done the toptime. For that I save the current player's nickname as account data onPlayerLogin and then replace "getPlayerName(player)" with that account data everywhere but it still shows the nickname with which the player has done the toptime, not his current one. For example, I make a toptime with my nick "[SiK]Megas" (color coded) and then change my name to "tester45" or something. The next time the same map starts it should show "tester45" as top1, not my old nick (in this case "[SiK]Megas"). I don't know what I'm doing wrong. Any help would be appreciated. Here's the 'maptimes_server.lua' file: -- -- maptimes_server.lua -- SMaptimes = {} SMaptimes.__index = SMaptimes SMaptimes.instances = {} --------------------------------------------------------------------------- -- -- SMaptimes:create() -- -- Create a SMaptimes instance -- --------------------------------------------------------------------------- function SMaptimes:create( raceModeName, mapName, statsKey ) local id = #SMaptimes.instances + 1 SMaptimes.instances[id] = setmetatable( { id = id, raceModeName = raceModeName, mapName = mapName, statsKey = statsKey, dbTable = nil, }, self ) SMaptimes.instances[id]:postCreate() return SMaptimes.instances[id] end --------------------------------------------------------------------------- -- -- SMaptimes:destroy() -- -- Destroy a SMaptimes instance -- --------------------------------------------------------------------------- function SMaptimes:destroy() self.dbTable:destroy() SMaptimes.instances[self.id] = nil self.id = 0 end --------------------------------------------------------------------------- -- -- SMaptimes:postCreate() -- -- -- --------------------------------------------------------------------------- function SMaptimes:postCreate() local tableName = self:makeDatabaseTableName( self.raceModeName, self.mapName ) local columns = { 'playerName', 'playerSerial', 'timeMs', 'timeText', 'dateRecorded', 'extra' } local columnTypes = { 'TEXT', 'TEXT', 'REAL', 'TEXT', 'TEXT' } self.dbTable = SDatabaseTable:create( tableName, columns, columnTypes ) end --------------------------------------------------------------------------- -- -- SMaptimes:validateDbTableRow() -- -- Make sure each cell in the row contains a valid value -- --------------------------------------------------------------------------- function SMaptimes:validateDbTableRow( index ) local row = self.dbTable.rows[index] row.playerName = tostring(row.playerName) or "playerName" row.playerSerial = tostring(row.playerSerial) or "playerSerial" row.timeMs = tonumber(row.timeMs) or 0 row.timeText = tostring(row.timeText) or "00:00:000" row.dateRecorded = tostring(row.dateRecorded) or "1900-00-00 00:00:00" row.extra = tostring(row.extra) or "" end --------------------------------------------------------------------------- -- -- SMaptimes:flush() -- -- Destroy a SMaptimes instance -- --------------------------------------------------------------------------- function SMaptimes:flush() outputDebug( 'TOPTIMES', 'SMaptimes:flush()') self:save() end --------------------------------------------------------------------------- -- -- SMaptimes:makeDatabaseTableName() -- -- -- --------------------------------------------------------------------------- function SMaptimes:makeDatabaseTableName( raceModeName, mapName ) return 'race maptimes ' .. raceModeName .. ' ' .. mapName end --------------------------------------------------------------------------- -- -- SMaptimes:timeMsToTimeText() -- -- -- --------------------------------------------------------------------------- function SMaptimes:timeMsToTimeText( timeMs ) local minutes = math.floor( timeMs / 60000 ) timeMs = timeMs - minutes * 60000; local seconds = math.floor( timeMs / 1000 ) local ms = timeMs - seconds * 1000; return string.format( '%02d:%02d:%03d', minutes, seconds, ms ); end --------------------------------------------------------------------------- -- -- SMaptimes:load() -- -- -- --------------------------------------------------------------------------- function SMaptimes:load() self.dbTable:load() -- Make sure each cell in the table contains a valid value - saves lots of checks later for i,row in ipairs(self.dbTable.rows) do self:validateDbTableRow( i ) end self:sort() end --------------------------------------------------------------------------- -- -- SMaptimes:save() -- -- -- --------------------------------------------------------------------------- function SMaptimes:save() self.dbTable:save() end --------------------------------------------------------------------------- -- -- SMaptimes:sort() -- -- Not quick -- --------------------------------------------------------------------------- function SMaptimes:sort() self:checkIsSorted('Presort') table.sort(self.dbTable.rows, function(a, b) return a.timeMs < b.timeMs or ( a.timeMs == b.timeMs and a.dateRecorded < b.dateRecorded ) end ) self:checkIsSorted('Postsort') end --------------------------------------------------------------------------- -- -- SMaptimes:checkIsSorted() -- -- Debug -- --------------------------------------------------------------------------- function SMaptimes:checkIsSorted(msg) for i=2,#self.dbTable.rows do local prevTime = self.dbTable.rows[i-1].timeMs local time = self.dbTable.rows[i].timeMs if prevTime > time then outputWarning( 'Maptimes sort error: ' .. msg .. ' timeMs order error at ' .. i ) end if prevTime == time then prevDate = self.dbTable.rows[i-1].dateRecorded date = self.dbTable.rows[i].dateRecorded if prevDate > date then outputWarning( 'Maptimes sort error: ' .. msg .. ' dateRecorded order error at ' .. i ) end end end end --------------------------------------------------------------------------- -- -- SMaptimes:getToptimes() -- -- Return a table of the top 'n' toptimes -- --------------------------------------------------------------------------- function SMaptimes:getToptimes( howMany ) if _DEBUG_CHECK then self:checkIsSorted('getToptimes') end local result = {} for i=1,howMany do if i <= #self.dbTable.rows then result[i] = { timeText = self.dbTable.rows[i].timeText, playerName = self.dbTable.rows[i].playerName, dateRecorded = self.dbTable.rows[i].dateRecorded, playerCountry = self.dbTable.rows[i].extra, } else result[i] = { timeText = '', playerName = '-- Empty --', dateRecorded = '', playerCountry = '', } end end return result end --------------------------------------------------------------------------- -- -- SMaptimes:getValidEntryCount() -- -- Return a count of the number of toptimes -- --------------------------------------------------------------------------- function SMaptimes:getValidEntryCount() return #self.dbTable.rows end --------------------------------------------------------------------------- -- -- SMaptimes:addPlayer() -- -- -- --------------------------------------------------------------------------- function SMaptimes:addPlayer(player) local account = getPlayerAccount(player) if account and not isGuestAccount(account) then local currentPlayerName = getAccountData(account, "currentPlayerName") table.insert( self.dbTable.rows, { playerName = currentPlayerName, playerSerial = getPlayerSerial(player), timeMs = 0, timeText = '00:00:000', dateRecorded = '1900-00-00 00:00:00', extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "", } ) -- Make sure new row has valid values self:validateDbTableRow( #self.dbTable.rows ) end return #self.dbTable.rows end --------------------------------------------------------------------------- -- -- SMaptimes:getIndexForPlayer() -- -- Can return false if player has no entry -- --------------------------------------------------------------------------- function SMaptimes:getIndexForPlayer( player ) if isGuestAccount (getPlayerAccount(player)) then return end local account = getPlayerAccount(player) if account and not isGuestAccount(account) then local currentPlayerName = getAccountData(account, "currentPlayerName") if self.statsKey == 'serial' then -- Find player by serial local serial = getPlayerSerial(player) for i,row in ipairs(self.dbTable.rows) do if serial == row.playerSerial then return i end end else -- Find player by name local name = currentPlayerName for i,row in ipairs(self.dbTable.rows) do if name == row.playerName then return i end end end end return false end --------------------------------------------------------------------------- -- -- SMaptimes:getPositionForTime() -- -- Always succeeds -- --------------------------------------------------------------------------- function SMaptimes:getPositionForTime( time, dateRecorded ) for i,row in ipairs(self.dbTable.rows) do if time < row.timeMs then return i end if time == row.timeMs and dateRecorded < row.dateRecorded then return i end end return #self.dbTable.rows + 1 end --------------------------------------------------------------------------- -- -- SMaptimes:getTimeForPlayer() -- -- Can return false if player has no entry -- --------------------------------------------------------------------------- function SMaptimes:getTimeForPlayer( player ) local i = self:getIndexForPlayer( player ) if not i then return false end return self.dbTable.rows[i].timeMs end --------------------------------------------------------------------------- -- -- SMaptimes:setTimeForPlayer() -- -- Update the time for this player -- --------------------------------------------------------------------------- function SMaptimes:setTimeForPlayer( player, time, dateRecorded ) if isGuestAccount (getPlayerAccount(player)) then outputChatBox("You need to register and login in order to do toptimes!", player, 255,153,0) return end -- Find current entry for player local oldIndex = self:getIndexForPlayer( player ) if not oldIndex then -- No entry yet, so add it to the end oldIndex = self:addPlayer( player ) if oldIndex ~= self:getIndexForPlayer( player ) then outputError( "oldIndex ~= self:getIndexForPlayer( player )" ) end end -- Copy it out and then remove it from the table local row = self.dbTable.rows[oldIndex] table.remove( self.dbTable.rows, oldIndex ) -- Update it local account = getPlayerAccount(player) if account and not isGuestAccount(account) then local currentPlayerName = getAccountData(account, "currentPlayerName") row.playerName = currentPlayerName row.timeMs = time row.timeText = self:timeMsToTimeText(time) row.dateRecorded = dateRecorded row.extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "" -- Put it back in at the correct position to maintain sort order local newIndex = self:getPositionForTime( row.timeMs, row.dateRecorded ) table.insert( self.dbTable.rows, newIndex, row ) end if _DEBUG_CHECK then self:checkIsSorted('setTimeForPlayer') end end --------------------------------------------------------------------------- -- -- SMaptimes:deletefirst() -- -- Remove the best time from this map -- --------------------------------------------------------------------------- function SMaptimes:deletetime(place) place = tonumber(place) or 1 -- Although the list should be sorted already, make sure self:sort() -- Remove the first row if #self.dbTable.rows >= place then -- Copy it out and then remove it from the table local row = self.dbTable.rows[place] table.remove( self.dbTable.rows, place ) return row end return false end ------------------------------------------------------------------------------------------------------------------------- addEvent('onPlayerToptimeImprovement') function toptimeImprovement(newPos, newTime, oldPos, oldTime, displayTopCount, getValidEntryCount ) if not oldPos then outputChatBox ( "#96CC9EYou got a new personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9E#F3ECDC#"..newPos.." #F9CB8D("..timeMsToTimeText(newTime)..")" , source, 255, 165, 0, true) else outputChatBox ( "#96CC9EYou improved your personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9EFrom #F3ECDC#"..oldPos.." #F9CB8D("..timeMsToTimeText(oldTime)..") #96CC9Eto #F3ECDC#"..newPos.." #F9CB8D("..timeMsToTimeText(newTime)..") #F9CC8E[-"..timeMsToTimeText(oldTime - newTime).."]", source, 255, 165, 0, true) end end addEventHandler("onPlayerToptimeImprovement", root, toptimeImprovement) ------------------------------------------------------------------------------------------------------------------------- function timeMsToTimeText(timeMs) if timeMs then local minutes = math.floor( timeMs / 60000 ) timeMs = timeMs - minutes * 60000; local seconds = math.floor( timeMs / 1000 ) local ms = timeMs - seconds * 1000; return string.format( '%02d:%02d:%03d', minutes, seconds, ms ); end end ------------------------------------------------------------------------------------------------------------------------- function onPlayerLogin() local account = getPlayerAccount(source) if(account and isGuestAccount(account)==false) then local playername = getPlayerNameWithColor(source) setAccountData(account, "currentPlayerName", playername) end end addEventHandler("onPlayerLogin", root, onPlayerLogin) ------------------------------------------------------------------------------------------------------------------------- --[[addEvent("onMapStarting", true) function onMapStarting() for i, player in ipairs(getElementsByType('player')) do local account = getPlayerAccount(player) if(account and isGuestAccount(account)==false) then local playername = getPlayerNameWithColor(player) setAccountData(account, "currentPlayerName", playername) end end end addEventHandler("onMapStarting", root, onMapStarting)--]] And I want to make it work only with MTA's accounts system, no SQL or anything like that. Thanks to anyone that helps! Edited January 25, 2017 by koragg Link to comment
Bonsai Posted January 25, 2017 Share Posted January 25, 2017 The account stays the same if a player changes his name? Instead of storing the player name of the top time, you should rather store the account name. Then you can simply lookup that accounts data to find the name. Link to comment
koragg Posted January 25, 2017 Author Share Posted January 25, 2017 (edited) Not sure how to make it update the name. I did as you said and it recorded account name, but then what? Quote Then you can simply lookup that accounts data to find the name. Isn't that what I did in my first attempt? I used 'getAccountData(account, "currentPlayerName")' instead of 'getPlayerName(player)'. Edited January 25, 2017 by koragg Link to comment
Bonsai Posted January 25, 2017 Share Posted January 25, 2017 As far as I can see you are storing the player name, not the account (or account name as I don't know if accounts can be stored). 7 hours ago, koragg said: local currentPlayerName = getAccountData(account, "currentPlayerName") That should not get the name but only store the account. So when you get the toptimes again later to display, you can use getAccount() or something, then get the account data, which will be the final player name. 1 Link to comment
koragg Posted January 26, 2017 Author Share Posted January 26, 2017 (edited) So I made some tweaks today: - now account name gets recorded to the database - the player name is shown in the toptime list (not the account name) - this^ prevents double tops as each player has one account, if they change nickname they would not make another toptime with their new name But I still don't know how to make the nickname update each time the player associated with the account which is registered as a toptime changes his nickname :\ -- maptimes_server.lua SMaptimes = {} SMaptimes.__index = SMaptimes SMaptimes.instances = {} --------------------------------------------------------------------------- -- SMaptimes:create() -- -- Create a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:create( raceModeName, mapName, statsKey ) local id = #SMaptimes.instances + 1 SMaptimes.instances[id] = setmetatable( { id = id, raceModeName = raceModeName, mapName = mapName, statsKey = statsKey, dbTable = nil, }, self ) SMaptimes.instances[id]:postCreate() return SMaptimes.instances[id] end --------------------------------------------------------------------------- -- SMaptimes:destroy() -- -- Destroy a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:destroy() self.dbTable:destroy() SMaptimes.instances[self.id] = nil self.id = 0 end --------------------------------------------------------------------------- -- SMaptimes:postCreate() --------------------------------------------------------------------------- function SMaptimes:postCreate() local tableName = self:makeDatabaseTableName( self.raceModeName, self.mapName ) local columns = { 'playerName', 'playerSerial', 'timeMs', 'timeText', 'dateRecorded', 'extra', 'currentNick' } local columnTypes = { 'TEXT', 'TEXT', 'REAL', 'TEXT', 'TEXT', 'TEXT' } self.dbTable = SDatabaseTable:create( tableName, columns, columnTypes ) end --------------------------------------------------------------------------- -- SMaptimes:validateDbTableRow() -- -- Make sure each cell in the row contains a valid value --------------------------------------------------------------------------- function SMaptimes:validateDbTableRow( index ) local row = self.dbTable.rows[index] row.playerName = tostring(row.playerName) or "playerName" row.playerSerial = tostring(row.playerSerial) or "playerSerial" row.timeMs = tonumber(row.timeMs) or 0 row.timeText = tostring(row.timeText) or "00:00:000" row.dateRecorded = tostring(row.dateRecorded) or "1900-00-00 00:00:00" row.extra = tostring(row.extra) or "" row.currentNick = tostring(row.currentNick) or "currentNick" end --------------------------------------------------------------------------- -- SMaptimes:flush() -- -- Destroy a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:flush() outputDebug( 'TOPTIMES', 'SMaptimes:flush()') self:save() end --------------------------------------------------------------------------- -- SMaptimes:makeDatabaseTableName() --------------------------------------------------------------------------- function SMaptimes:makeDatabaseTableName( raceModeName, mapName ) return 'race maptimes ' .. raceModeName .. ' ' .. mapName end --------------------------------------------------------------------------- -- SMaptimes:timeMsToTimeText() --------------------------------------------------------------------------- function SMaptimes:timeMsToTimeText( timeMs ) local minutes = math.floor( timeMs / 60000 ) timeMs = timeMs - minutes * 60000; local seconds = math.floor( timeMs / 1000 ) local ms = timeMs - seconds * 1000; return string.format( '%02d:%02d:%03d', minutes, seconds, ms ); end --------------------------------------------------------------------------- -- SMaptimes:load() --------------------------------------------------------------------------- function SMaptimes:load() self.dbTable:load() -- Make sure each cell in the table contains a valid value - saves lots of checks later for i,row in ipairs(self.dbTable.rows) do self:validateDbTableRow( i ) end self:sort() end --------------------------------------------------------------------------- -- SMaptimes:save() --------------------------------------------------------------------------- function SMaptimes:save() self.dbTable:save() end --------------------------------------------------------------------------- -- SMaptimes:sort() -- -- Not quick --------------------------------------------------------------------------- function SMaptimes:sort() self:checkIsSorted('Presort') table.sort(self.dbTable.rows, function(a, b) return a.timeMs < b.timeMs or ( a.timeMs == b.timeMs and a.dateRecorded < b.dateRecorded ) end ) self:checkIsSorted('Postsort') end --------------------------------------------------------------------------- -- SMaptimes:checkIsSorted() -- -- Debug --------------------------------------------------------------------------- function SMaptimes:checkIsSorted(msg) for i=2,#self.dbTable.rows do local prevTime = self.dbTable.rows[i-1].timeMs local time = self.dbTable.rows[i].timeMs if prevTime > time then outputWarning( 'Maptimes sort error: ' .. msg .. ' timeMs order error at ' .. i ) end if prevTime == time then prevDate = self.dbTable.rows[i-1].dateRecorded date = self.dbTable.rows[i].dateRecorded if prevDate > date then outputWarning( 'Maptimes sort error: ' .. msg .. ' dateRecorded order error at ' .. i ) end end end end --------------------------------------------------------------------------- -- SMaptimes:getToptimes() -- -- Return a table of the top 'n' toptimes --------------------------------------------------------------------------- function SMaptimes:getToptimes( howMany ) if _DEBUG_CHECK then self:checkIsSorted('getToptimes') end local result = {} for i=1,howMany do if i <= #self.dbTable.rows then result[i] = { timeText = self.dbTable.rows[i].timeText, playerName = self.dbTable.rows[i].currentNick, dateRecorded = self.dbTable.rows[i].dateRecorded, playerCountry = self.dbTable.rows[i].extra, } else result[i] = { timeText = '', playerName = '-- Empty --', dateRecorded = '', playerCountry = '', } end end return result end --------------------------------------------------------------------------- -- SMaptimes:getValidEntryCount() -- -- Return a count of the number of toptimes --------------------------------------------------------------------------- function SMaptimes:getValidEntryCount() return #self.dbTable.rows end --------------------------------------------------------------------------- -- SMaptimes:addPlayer() --------------------------------------------------------------------------- function SMaptimes:addPlayer( player ) table.insert( self.dbTable.rows, { playerName = getAccountName(getPlayerAccount(player)), playerSerial = getPlayerSerial(player), timeMs = 0, timeText = '00:00:000', dateRecorded = '1900-00-00 00:00:00', extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "", currentNick = getPlayerNameWithColor(player) } ) -- Make sure new row has valid values self:validateDbTableRow( #self.dbTable.rows ) return #self.dbTable.rows end --------------------------------------------------------------------------- -- SMaptimes:getIndexForPlayer() -- -- Can return false if player has no entry --------------------------------------------------------------------------- function SMaptimes:getIndexForPlayer( player ) if isGuestAccount (getPlayerAccount(player)) then return end if self.statsKey == 'serial' then -- Find player by serial local serial = getPlayerSerial(player) for i,row in ipairs(self.dbTable.rows) do if serial == row.playerSerial then return i end end else -- Find player by name local name = getAccountName(getPlayerAccount(player)) for i,row in ipairs(self.dbTable.rows) do if name == row.playerName then return i end end end return false end --------------------------------------------------------------------------- -- SMaptimes:getPositionForTime() -- -- Always succeeds --------------------------------------------------------------------------- function SMaptimes:getPositionForTime( time, dateRecorded ) for i,row in ipairs(self.dbTable.rows) do if time < row.timeMs then return i end if time == row.timeMs and dateRecorded < row.dateRecorded then return i end end return #self.dbTable.rows + 1 end --------------------------------------------------------------------------- -- SMaptimes:getTimeForPlayer() -- -- Can return false if player has no entry --------------------------------------------------------------------------- function SMaptimes:getTimeForPlayer( player ) local i = self:getIndexForPlayer( player ) if not i then return false end return self.dbTable.rows[i].timeMs end --------------------------------------------------------------------------- -- SMaptimes:setTimeForPlayer() -- -- Update the time for this player --------------------------------------------------------------------------- function SMaptimes:setTimeForPlayer( player, time, dateRecorded ) if isGuestAccount (getPlayerAccount(player)) then outputChatBox("You need to register and login in order to do toptimes!", player, 255,153,0) return end -- Find current entry for player local oldIndex = self:getIndexForPlayer( player ) if not oldIndex then -- No entry yet, so add it to the end oldIndex = self:addPlayer( player ) if oldIndex ~= self:getIndexForPlayer( player ) then outputError( "oldIndex ~= self:getIndexForPlayer( player )" ) end end -- Copy it out and then remove it from the table local row = self.dbTable.rows[oldIndex] table.remove( self.dbTable.rows, oldIndex ) -- Update it row.playerName = getAccountName(getPlayerAccount(player)) -- Refresh the name row.timeMs = time row.timeText = self:timeMsToTimeText(time) row.dateRecorded = dateRecorded row.extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "" row.currentNick = getPlayerNameWithColor(player) -- Put it back in at the correct position to maintain sort order local newIndex = self:getPositionForTime( row.timeMs, row.dateRecorded ) table.insert( self.dbTable.rows, newIndex, row ) if _DEBUG_CHECK then self:checkIsSorted('setTimeForPlayer') end end --------------------------------------------------------------------------- -- SMaptimes:deletefirst() -- -- Remove the best time from this map --------------------------------------------------------------------------- function SMaptimes:deletetime(place) place = tonumber(place) or 1 -- Although the list should be sorted already, make sure self:sort() -- Remove the first row if #self.dbTable.rows >= place then -- Copy it out and then remove it from the table local row = self.dbTable.rows[place] table.remove( self.dbTable.rows, place ) return row end return false end --------------------------------------------------------------------------- -- toptimeImprovement() -- -- Display personal best time of player --------------------------------------------------------------------------- addEvent('onPlayerToptimeImprovement') function toptimeImprovement(newPos, newTime, oldPos, oldTime, displayTopCount, getValidEntryCount ) if not oldPos then outputChatBox ( "#96CC9EYou got a new personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9E#F3ECDC#"..newPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(newTime)..")" , source, 255, 165, 0, true) else outputChatBox ( "#96CC9EYou improved your personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9EFrom #F3ECDC#"..oldPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(oldTime)..") #96CC9Eto #F3ECDC#"..newPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(newTime)..") #F9CC8E[-"..SMaptimes:timeMsToTimeText(oldTime - newTime).."]", source, 255, 165, 0, true) end end addEventHandler("onPlayerToptimeImprovement", root, toptimeImprovement) PS: I removed the saving of nick to account as it was not needed actually. Edited January 26, 2017 by koragg Also it would be nice if flags update as well as nickname. Like if a player joins from another country, it would update the toptimes table's country flag. Link to comment
idarrr Posted January 26, 2017 Share Posted January 26, 2017 Use this event may help. OnClientPlayerChangeNick OnPlayerChangeNick Link to comment
koragg Posted January 26, 2017 Author Share Posted January 26, 2017 (edited) 23 minutes ago, idarrr said: Use this event may help. OnClientPlayerChangeNick OnPlayerChangeNick Yea I did use those but turns out it's better if I don't save the nick anywhere and just get it when it's needed. (that way it's up to date) Edited January 26, 2017 by koragg Link to comment
koragg Posted January 27, 2017 Author Share Posted January 27, 2017 Fixed it Thanks for help. I had trouble saving nick onPlayerNickChange so I just save it to account whenever a new map starts. Also I think I made country update as well (if player changed his location and joined my server, it'll display his new country in tops list even without him setting a better time). Can't really test country but I think it should work. If anyone's interested in how I did it, full file below: (I'm not using MTA's default race_toptimes though) -- maptimes_server.lua SMaptimes = {} SMaptimes.__index = SMaptimes SMaptimes.instances = {} --------------------------------------------------------------------------- -- SMaptimes:create() -- -- Create a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:create( raceModeName, mapName, statsKey ) local id = #SMaptimes.instances + 1 SMaptimes.instances[id] = setmetatable( { id = id, raceModeName = raceModeName, mapName = mapName, statsKey = statsKey, dbTable = nil, }, self ) SMaptimes.instances[id]:postCreate() return SMaptimes.instances[id] end --------------------------------------------------------------------------- -- SMaptimes:destroy() -- -- Destroy a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:destroy() self.dbTable:destroy() SMaptimes.instances[self.id] = nil self.id = 0 end --------------------------------------------------------------------------- -- SMaptimes:postCreate() --------------------------------------------------------------------------- function SMaptimes:postCreate() local tableName = self:makeDatabaseTableName( self.raceModeName, self.mapName ) local columns = { 'playerName', 'playerSerial', 'timeMs', 'timeText', 'dateRecorded', 'extra' } local columnTypes = { 'TEXT', 'TEXT', 'REAL', 'TEXT', 'TEXT' } self.dbTable = SDatabaseTable:create( tableName, columns, columnTypes ) end --------------------------------------------------------------------------- -- SMaptimes:validateDbTableRow() -- -- Make sure each cell in the row contains a valid value --------------------------------------------------------------------------- function SMaptimes:validateDbTableRow( index ) local row = self.dbTable.rows[index] row.playerName = tostring(row.playerName) or "playerName" row.playerSerial = tostring(row.playerSerial) or "playerSerial" row.timeMs = tonumber(row.timeMs) or 0 row.timeText = tostring(row.timeText) or "00:00:000" row.dateRecorded = tostring(row.dateRecorded) or "1900-00-00 00:00:00" row.extra = tostring(row.extra) or "" end --------------------------------------------------------------------------- -- SMaptimes:flush() -- -- Destroy a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:flush() outputDebug( 'TOPTIMES', 'SMaptimes:flush()') self:save() end --------------------------------------------------------------------------- -- SMaptimes:makeDatabaseTableName() --------------------------------------------------------------------------- function SMaptimes:makeDatabaseTableName( raceModeName, mapName ) return 'race maptimes ' .. raceModeName .. ' ' .. mapName end --------------------------------------------------------------------------- -- SMaptimes:timeMsToTimeText() --------------------------------------------------------------------------- function SMaptimes:timeMsToTimeText( timeMs ) local minutes = math.floor( timeMs / 60000 ) timeMs = timeMs - minutes * 60000; local seconds = math.floor( timeMs / 1000 ) local ms = timeMs - seconds * 1000; return string.format( '%02d:%02d:%03d', minutes, seconds, ms ); end --------------------------------------------------------------------------- -- SMaptimes:load() --------------------------------------------------------------------------- function SMaptimes:load() self.dbTable:load() -- Make sure each cell in the table contains a valid value - saves lots of checks later for i,row in ipairs(self.dbTable.rows) do self:validateDbTableRow( i ) end self:sort() end --------------------------------------------------------------------------- -- SMaptimes:save() --------------------------------------------------------------------------- function SMaptimes:save() self.dbTable:save() end --------------------------------------------------------------------------- -- SMaptimes:sort() -- -- Not quick --------------------------------------------------------------------------- function SMaptimes:sort() self:checkIsSorted('Presort') table.sort(self.dbTable.rows, function(a, b) return a.timeMs < b.timeMs or ( a.timeMs == b.timeMs and a.dateRecorded < b.dateRecorded ) end ) self:checkIsSorted('Postsort') end --------------------------------------------------------------------------- -- SMaptimes:checkIsSorted() -- -- Debug --------------------------------------------------------------------------- function SMaptimes:checkIsSorted(msg) for i=2,#self.dbTable.rows do local prevTime = self.dbTable.rows[i-1].timeMs local time = self.dbTable.rows[i].timeMs if prevTime > time then outputWarning( 'Maptimes sort error: ' .. msg .. ' timeMs order error at ' .. i ) end if prevTime == time then prevDate = self.dbTable.rows[i-1].dateRecorded date = self.dbTable.rows[i].dateRecorded if prevDate > date then outputWarning( 'Maptimes sort error: ' .. msg .. ' dateRecorded order error at ' .. i ) end end end end --------------------------------------------------------------------------- -- SMaptimes:getToptimes() -- -- Return a table of the top 'n' toptimes --------------------------------------------------------------------------- function SMaptimes:getToptimes( howMany ) if _DEBUG_CHECK then self:checkIsSorted('getToptimes') end local result = {} for i=1,howMany do if i <= #self.dbTable.rows then result[i] = { timeText = self.dbTable.rows[i].timeText, playerName = getAccountData(getAccount(self.dbTable.rows[i].playerName), "currentPlayerName"), dateRecorded = self.dbTable.rows[i].dateRecorded, playerCountry = getAccountData(getAccount(self.dbTable.rows[i].playerName), "country") or "", } else result[i] = { timeText = '', playerName = '-- Empty --', dateRecorded = '', playerCountry = '', } end end return result end --------------------------------------------------------------------------- -- SMaptimes:getValidEntryCount() -- -- Return a count of the number of toptimes --------------------------------------------------------------------------- function SMaptimes:getValidEntryCount() return #self.dbTable.rows end --------------------------------------------------------------------------- -- SMaptimes:addPlayer() --------------------------------------------------------------------------- function SMaptimes:addPlayer( player ) table.insert( self.dbTable.rows, { playerName = getAccountName(getPlayerAccount(player)), playerSerial = getPlayerSerial(player), timeMs = 0, timeText = '00:00:000', dateRecorded = '1900-00-00 00:00:00', extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "", } ) -- Make sure new row has valid values self:validateDbTableRow( #self.dbTable.rows ) return #self.dbTable.rows end --------------------------------------------------------------------------- -- SMaptimes:getIndexForPlayer() -- -- Can return false if player has no entry --------------------------------------------------------------------------- function SMaptimes:getIndexForPlayer( player ) if isGuestAccount (getPlayerAccount(player)) then return end if self.statsKey == 'serial' then -- Find player by serial local serial = getPlayerSerial(player) for i,row in ipairs(self.dbTable.rows) do if serial == row.playerSerial then return i end end else -- Find player by name local name = getAccountName(getPlayerAccount(player)) for i,row in ipairs(self.dbTable.rows) do if name == row.playerName then return i end end end return false end --------------------------------------------------------------------------- -- SMaptimes:getPositionForTime() -- -- Always succeeds --------------------------------------------------------------------------- function SMaptimes:getPositionForTime( time, dateRecorded ) for i,row in ipairs(self.dbTable.rows) do if time < row.timeMs then return i end if time == row.timeMs and dateRecorded < row.dateRecorded then return i end end return #self.dbTable.rows + 1 end --------------------------------------------------------------------------- -- SMaptimes:getTimeForPlayer() -- -- Can return false if player has no entry --------------------------------------------------------------------------- function SMaptimes:getTimeForPlayer( player ) local i = self:getIndexForPlayer( player ) if not i then return false end return self.dbTable.rows[i].timeMs end --------------------------------------------------------------------------- -- SMaptimes:setTimeForPlayer() -- -- Update the time for this player --------------------------------------------------------------------------- function SMaptimes:setTimeForPlayer( player, time, dateRecorded ) if isGuestAccount (getPlayerAccount(player)) then outputChatBox("You need to register and login in order to do toptimes!", player, 255,153,0) return end -- Find current entry for player local oldIndex = self:getIndexForPlayer( player ) if not oldIndex then -- No entry yet, so add it to the end oldIndex = self:addPlayer( player ) if oldIndex ~= self:getIndexForPlayer( player ) then outputError( "oldIndex ~= self:getIndexForPlayer( player )" ) end end -- Copy it out and then remove it from the table local row = self.dbTable.rows[oldIndex] table.remove( self.dbTable.rows, oldIndex ) -- Update it row.playerName = getAccountName(getPlayerAccount(player)) -- Refresh the name row.timeMs = time row.timeText = self:timeMsToTimeText(time) row.dateRecorded = dateRecorded row.extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "" -- Put it back in at the correct position to maintain sort order local newIndex = self:getPositionForTime( row.timeMs, row.dateRecorded ) table.insert( self.dbTable.rows, newIndex, row ) if _DEBUG_CHECK then self:checkIsSorted('setTimeForPlayer') end end --------------------------------------------------------------------------- -- SMaptimes:deletefirst() -- -- Remove the best time from this map --------------------------------------------------------------------------- function SMaptimes:deletetime(place) place = tonumber(place) or 1 -- Although the list should be sorted already, make sure self:sort() -- Remove the first row if #self.dbTable.rows >= place then -- Copy it out and then remove it from the table local row = self.dbTable.rows[place] table.remove( self.dbTable.rows, place ) return row end return false end --------------------------------------------------------------------------- -- toptimeImprovement() -- -- Display personal best time of player --------------------------------------------------------------------------- addEvent('onPlayerToptimeImprovement') function toptimeImprovement(newPos, newTime, oldPos, oldTime, displayTopCount, getValidEntryCount ) if not oldPos then outputChatBox ( "#96CC9EYou got a new personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9E#F3ECDC#"..newPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(newTime)..")" , source, 255, 165, 0, true) else outputChatBox ( "#96CC9EYou improved your personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9EFrom #F3ECDC#"..oldPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(oldTime)..") #96CC9Eto #F3ECDC#"..newPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(newTime)..") #F9CC8E[-"..SMaptimes:timeMsToTimeText(oldTime - newTime).."]", source, 255, 165, 0, true) end end addEventHandler("onPlayerToptimeImprovement", root, toptimeImprovement) --------------------------------------------------------------------------- -- Save the player's current nickname and current country into his account --------------------------------------------------------------------------- function onPlayerLogin() local account = getPlayerAccount(source) local countryid = exports["admin"]:getPlayerCountry(source) if(account and isGuestAccount(account)==false) then local playername = getPlayerNameWithColor(source) setAccountData(account, "currentPlayerName", playername) setAccountData(account, "country", countryid) end end addEventHandler("onPlayerLogin", root, onPlayerLogin) addEvent("onMapStarting", true) function onMapStarting() for i, player in ipairs(getElementsByType("player")) do local account = getPlayerAccount(player) if(account and isGuestAccount(account)==false) then local playername = getPlayerNameWithColor(player) setAccountData(account, "currentPlayerName", playername) end end end addEventHandler("onMapStarting", root, onMapStarting) Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now