Jump to content

Update toptimes name whenever player changes his nick


koragg

Recommended Posts

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 by koragg
Link to comment

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 by koragg
Link to comment

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.

 

  • Like 1
Link to comment

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 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

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

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...