Jump to content

Can't update playing hours on the scoreboard


JonJo

Recommended Posts

I tried to make a scoreboard for server roleplay, and I wanted to add playing hours, but this did not work and my playing hours did not increase on the scoreboard.

this is cliend side

--[[ Configuration ]]--
local SCOREBOARD_WIDTH				= 460				-- The scoreboard window width
local SCOREBOARD_HEIGHT				= 460				-- The scoreboard window height
local SCOREBOARD_HEADER_HEIGHT		= 30				-- Height for the header in what you can see the server info
local SCOREBOARD_TOGGLE_CONTROL		= "tab"				-- Control/Key to toggle the scoreboard visibility
local SCOREBOARD_PGUP_CONTROL		= "mouse_wheel_up"	-- Control/Key to move one page up
local SCOREBOARD_PGDN_CONTROL		= "mouse_wheel_down"-- Control/Key to move one page down
local SCOREBOARD_DISABLED_CONTROLS	= { "next_weapon",	-- Controls that are disabled when the scoreboard is showing
										"previous_weapon",
										"aim_weapon",
										"radio_next",
										"radio_previous" }
local SCOREBOARD_TOGGLE_TIME		= 200				-- Time in miliseconds to make the scoreboard (dis)appear
local SCOREBOARD_POSTGUI			= true				-- Set to true if it must be drawn over the GUI
local SCOREBOARD_INFO_BACKGROUND	= { 0, 0, 0, 150 }			-- RGBA color for the info header background
local SCOREBOARD_SERVER_NAME_COLOR	= { 255, 255, 255, 255 }		-- RGBA color for the server name text
local SCOREBOARD_PLAYERCOUNT_COLOR	= { 255, 255, 255, 255 }	-- RGBA color for the server player count text
local SCOREBOARD_BACKGROUND			= { 0, 0, 0, 140 }			-- RGBA color for the background
local SCOREBOARD_BACKGROUND_IMAGE	= { 255, 255, 255, 40 }		-- RGBA color for the background image
local SCOREBOARD_HEADERS_COLOR		= { 255, 255, 255, 180 }	-- RGBA color for the headers
local SCOREBOARD_SEPARATOR_COLOR	= { 82, 82, 82, 140 }		-- RGBA color for the separator line between headers and body content
local SCOREBOARD_SCROLL_BACKGROUND	= { 0, 10, 20, 100 }		-- RGBA color for the scroll background
local SCOREBOARD_SCROLL_BACKGROUND	= { 0, 10, 20, 100 }		-- RGBA color for the scroll background
local SCOREBOARD_SCROLL_FOREGROUND	= { 255, 255, 255, 255 }		-- RGBA color for the scroll foreground
local SCOREBOARD_SCROLL_HEIGHT		= 40						-- Size for the scroll marker
local SCOREBOARD_COLUMNS_WIDTH		= { 0.08, 0.57, 0.2, 0.14, 0.02 }	-- Relative width for each column: id, player name, ping and scroll position
local SCOREBOARD_ROW_GAP			= 1							-- Gap between rows

--[[ Uncomment to test with dummies ]]--
--[[
local _getPlayerName = getPlayerName
local _getPlayerPing = getPlayerPing
local _getPlayerNametagColor = getPlayerNametagColor

function getPlayerName ( player )
	if getElementType ( player ) == "player" then return _getPlayerName ( player ) end
	return getElementData ( player, "name" )
end

function getPlayerPing ( player )
	if getElementType ( player ) == "player" then return _getPlayerPing ( player ) end
	return getElementData ( player, "ping" )
end

function getPlayerNametagColor ( player )
	if getElementType ( player ) == "player" then return _getPlayerNametagColor ( player )
	else return unpack(getElementData(player, "color")) end
end
--]]


--[[ Global variables to this context ]]--
local g_isShowing = false		-- Marks if the scoreboard is showing
local g_currentWidth = 0		-- Current window width. Used for the fade in/out effect.
local g_currentHeight = 0		-- Current window height. Used for the fade in/out effect.
local g_scoreboardDummy			-- Will contain the scoreboard dummy element to gather info from.
local g_windowSize = { guiGetScreenSize () }	-- The window size
local g_localPlayer = getLocalPlayer ()			-- The local player...
local g_currentPage = 0			-- The current scroll page
local g_players					-- We will keep a cache of the conected player list
local g_oldControlStates		-- To save the old control states before disabling them for scrolling

--[[ Pre-calculate some stuff ]]--
-- Scoreboard position
local SCOREBOARD_X = math.floor ( ( g_windowSize[1] - SCOREBOARD_WIDTH ) / 2 )
local SCOREBOARD_Y = math.floor ( ( g_windowSize[2] - SCOREBOARD_HEIGHT ) / 2 )
-- Scoreboard colors
SCOREBOARD_INFO_BACKGROUND = tocolor ( unpack ( SCOREBOARD_INFO_BACKGROUND ) )
SCOREBOARD_SERVER_NAME_COLOR = tocolor ( unpack ( SCOREBOARD_SERVER_NAME_COLOR ) )
SCOREBOARD_PLAYERCOUNT_COLOR = tocolor ( unpack ( SCOREBOARD_PLAYERCOUNT_COLOR ) )
SCOREBOARD_BACKGROUND = tocolor ( unpack ( SCOREBOARD_BACKGROUND ) )
SCOREBOARD_BACKGROUND_IMAGE = tocolor ( unpack ( SCOREBOARD_BACKGROUND_IMAGE ) )
SCOREBOARD_HEADERS_COLOR = tocolor ( unpack ( SCOREBOARD_HEADERS_COLOR ) )
SCOREBOARD_SCROLL_BACKGROUND = tocolor ( unpack ( SCOREBOARD_SCROLL_BACKGROUND ) )
SCOREBOARD_SCROLL_FOREGROUND = tocolor ( unpack ( SCOREBOARD_SCROLL_FOREGROUND ) )
SCOREBOARD_SEPARATOR_COLOR = tocolor ( unpack ( SCOREBOARD_SEPARATOR_COLOR ) )
-- Columns width in absolute units
for k=1,#SCOREBOARD_COLUMNS_WIDTH do
	SCOREBOARD_COLUMNS_WIDTH[k] = math.floor ( SCOREBOARD_COLUMNS_WIDTH[k] * SCOREBOARD_WIDTH )
end
-- Pre-calculate each row horizontal bounding box.
local rowsBoundingBox = { { SCOREBOARD_X, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 } }
-- ID
rowsBoundingBox[1][2] = SCOREBOARD_X + SCOREBOARD_COLUMNS_WIDTH[1]
-- Name
rowsBoundingBox[2][1] = rowsBoundingBox[1][2]
rowsBoundingBox[2][2] = rowsBoundingBox[2][1] + SCOREBOARD_COLUMNS_WIDTH[2]
-- Hoursplayed
rowsBoundingBox[3][1] = rowsBoundingBox[2][2]
rowsBoundingBox[3][2] = rowsBoundingBox[3][1] + SCOREBOARD_COLUMNS_WIDTH[3]
-- Ping
rowsBoundingBox[4][1] = rowsBoundingBox[3][2]
rowsBoundingBox[4][2] = rowsBoundingBox[4][1] + SCOREBOARD_COLUMNS_WIDTH[4]
-- Scrollbar
rowsBoundingBox[5][1] = rowsBoundingBox[4][2]
rowsBoundingBox[5][2] = SCOREBOARD_X + SCOREBOARD_WIDTH


--[[ Pre-declare some functions ]]--
local onRender
local fadeScoreboard
local drawBackground
local drawScoreboard


--[[
* clamp
Clamps a value into a range.
--]]
local function clamp ( valueMin, current, valueMax )
	if current < valueMin then
		return valueMin
	elseif current > valueMax then
		return valueMax
	else
		return current
	end
end

--[[
* createPlayerCache
Generates a new player cache.
--]]
local function createPlayerCache ( ignorePlayer )
	-- Optimize the function in case of not having to ignore a player
	if ignorePlayer then
		-- Clear the gloal table
		g_players = {}
		
		-- Get the list of connected players
		local players = getElementsByType ( "player" )
	
		-- Dump them to the global table
		for k, player in ipairs(players) do
			if ignorePlayer ~= player then
				table.insert ( g_players, player )
			end
		end
	else
		g_players = getElementsByType ( "player" )
	end
	
	--[[ Uncomment to test with dummies ]]--
	--[[
	for k,v in ipairs(getElementsByType("playerDummy")) do
		table.insert(g_players, v)
	end
	--]]
	
	-- Sort the player list by their ID, giving priority to the local player
	table.sort ( g_players, function ( a, b )
		local idA = getElementData ( a, "playerid" ) or 0
		local idB = getElementData ( b, "playerid" ) or 0

		-- Perform the checks to always set the local player at the beggining
		if a == g_localPlayer then
			idA = -1
		elseif b == g_localPlayer then
			idB = -1
		end
		
		return tonumber(idA) < tonumber(idB)
	end )
end

--[[
* onClientResourceStart
Handles the resource start event to create the initial player cache
--]]
addEventHandler ( "onClientResourceStart", getResourceRootElement(getThisResource()), function ()
	createPlayerCache ()
end, false )

--[[
* onClientElementDataChange
Handles the element data changes event to update the player cache
if the playerid was changed.
--]]
addEventHandler ( "onClientElementDataChange", root, function ( dataName, dataValue )
	if dataName == "playerid" then
		createPlayerCache ()
	end
end )

--[[
* onClientPlayerQuit
Handles the player quit event to update the player cache.
--]]
addEventHandler ( "onClientPlayerQuit", root, function ()
	createPlayerCache ( source )
end )

--[[
* toggleScoreboard
Toggles the visibility of the scoreboard.
--]]
local function toggleScoreboard ( show )
	if not getControlState( 'aim_weapon' ) then
		-- Force the parameter to be a boolean
		local show = show == true
		
		-- Check if the status has changed
		if show ~= g_isShowing then
			g_isShowing = show
			
			if g_isShowing and g_currentWidth == 0 and g_currentHeight == 0 then
				-- Handle the onClientRender event to start drawing the scoreboard.
				addEventHandler ( "onClientPreRender", root, onRender, false )
			end
			
			-- Little hack to avoid switching weapons while moving through the scoreboard pages.
			if g_isShowing then
				g_oldControlStates = {}
				for k, control in ipairs ( SCOREBOARD_DISABLED_CONTROLS ) do
					g_oldControlStates[k] = isControlEnabled ( control )
					toggleControl ( control, false )
				end
			else
				for k, control in ipairs ( SCOREBOARD_DISABLED_CONTROLS ) do
					toggleControl ( control, g_oldControlStates[k] )
				end
				g_oldControlStates = nil
			end
		end
	end
end

--[[
* onToggleKey
Function to bind to the appropiate key the function to toggle the scoreboard visibility.
--]]
local function onToggleKey ( key, keyState )
	-- Check if the scoreboard element has been created
	if not g_scoreboardDummy then
		local elementTable = getElementsByType ( "scoreboard" )
		if #elementTable > 0 then
			g_scoreboardDummy = elementTable[1]
		else
			return
		end
	end
	
	-- Toggle the scoreboard, and check that it's allowed.
	toggleScoreboard ( keyState == "down" and getElementData ( g_scoreboardDummy, "allow" ) )
end
bindKey ( SCOREBOARD_TOGGLE_CONTROL, "both", onToggleKey )

--[[
* onScrollKey
Function to bind to the appropiate key the function to change the current page.
--]]
local function onScrollKey ( direction )
	if g_isShowing then
		if direction then
			g_currentPage = g_currentPage + 1
		else
			g_currentPage = g_currentPage - 1
			if g_currentPage < 0 then
				g_currentPage = 0
			end
		end
	end
end
bindKey ( SCOREBOARD_PGUP_CONTROL, "down", function () onScrollKey ( false ) end )
bindKey ( SCOREBOARD_PGDN_CONTROL, "down", function () onScrollKey ( true ) end )

--[[
* onRender
Event handler for onClientPreRender. It will forward the flow to the most appropiate
function: fading-in, fading-out or drawScoreboard.
--]]
onRender = function ( timeshift )
	-- Boolean to check if we must draw the scoreboard.
	local drawIt = false
	
	if g_isShowing then
		-- Check if the scoreboard has been disallowed
		if not getElementData ( g_scoreboardDummy, "allow" ) then
			toggleScoreboard ( false )
		-- If it's showing, check if it got fully faded in. Else, draw it normally.
		elseif g_currentWidth < SCOREBOARD_WIDTH or g_currentHeight < SCOREBOARD_HEIGHT then
			drawIt = fadeScoreboard ( timeshift, 1 )
		else
			-- Allow drawing the full scoreboard
			drawIt = true
		end
	else
		-- If it shouldn't be showing, make another step to fade it out.
		drawIt = fadeScoreboard ( timeshift, -1 )
	end
	
	-- Draw the scoreboard if allowed.
	if drawIt then
		drawScoreboard ()
	end
end

--[[
* fadeScoreboard
Makes a step of the fade effect. Gets a multiplier to make it either fading in or out.
--]]
fadeScoreboard = function ( timeshift, multiplier )
	-- Get the percentage of the final size that it should grow for this step.
	local growth = ( timeshift / SCOREBOARD_TOGGLE_TIME ) * multiplier
	
	-- Apply the growth to the scoreboard size
	g_currentWidth = clamp ( 0, g_currentWidth + ( SCOREBOARD_WIDTH * growth ), SCOREBOARD_WIDTH )
	g_currentHeight = clamp ( 0, g_currentHeight + ( SCOREBOARD_HEIGHT * growth ), SCOREBOARD_HEIGHT )
	
	-- Check if the scoreboard has collapsed. If so, unregister the onClientRender event.
	if g_currentWidth == 0 or g_currentHeight == 0 then
		g_currentWidth = 0
		g_currentHeight = 0
		removeEventHandler ( "onClientPreRender", root, onRender )
		return false
	else
		return true
	end
end

--[[
* drawBackground
Draws the scoreboard background.
--]]
drawBackground = function ()

	dxDrawImage(SCOREBOARD_X, SCOREBOARD_Y, g_currentWidth, g_currentHeight, ":hud/images/hud/box.png")
	
end

--[[
* drawRowBounded
Draws a scoreboard body row with the pre-calculated row bounding boxes.
--]]
local function drawRowBounded ( id, name, hours, ping, colors, font, top )
	-- Precalculate some constants
	local bottom = clamp ( 0, top + dxGetFontHeight ( 1, font ), SCOREBOARD_Y + g_currentHeight )
	local maxWidth = SCOREBOARD_X + g_currentWidth
	
	-- If the row doesn't fit, just avoid any further calculations.
	if bottom < top then return end
	
	-- ID
	local left = rowsBoundingBox[1][1]
	local right = clamp ( 0, rowsBoundingBox[1][2], maxWidth )
	if left < right then
		dxDrawText ( id, left, top, right, bottom,
					 colors[1], 1, font, "right", "top",
					 true, false, SCOREBOARD_POSTGUI )
		
		-- Name
		left = rowsBoundingBox[2][1] + 17 -- Grant some padding to the name column
		right = clamp ( 0, rowsBoundingBox[2][2], maxWidth )
		if left < right then
			dxDrawText ( name, left, top, right, bottom,
						 colors[2], 1, font, "left", "top",
						 true, false, SCOREBOARD_POSTGUI )
			
			-- Hoursplayed
			left = rowsBoundingBox[3][1]
			right = clamp ( 0, rowsBoundingBox[3][2], maxWidth )
			--outputDebugString(left.." vs ".. right)
			if left < right then
				dxDrawText ( hours, left, top, right, bottom,
							 colors[3], 1, font, "left", "top",
							 true, false, SCOREBOARD_POSTGUI )
				-- Ping
				left = rowsBoundingBox[4][1]
				right = clamp ( 0, rowsBoundingBox[4][2], maxWidth )
				--outputDebugString(left.." vs ".. right)
				if left < right then
					dxDrawText ( ping, left, top, right, bottom,
								 colors[3], 1, font, "left", "top",
								 true, false, SCOREBOARD_POSTGUI )
				end
			end
			

		end
	end
end

--[[
* drawScrollBar
Draws the scroll bar. Position ranges from 0 to 1.
--]]
local function drawScrollBar ( top, position )
	-- Get the bounding box
	local left = rowsBoundingBox[5][1]
	local right = clamp ( 0, rowsBoundingBox[5][2], SCOREBOARD_X + g_currentWidth )
	local bottom = clamp ( 0, SCOREBOARD_Y + SCOREBOARD_HEIGHT, SCOREBOARD_Y + g_currentHeight )
	
	-- Make sure that it'd be visible.
	if left < right and top < bottom then
		-- Draw the background
		dxDrawRectangle ( left, top, right - left, bottom - top, SCOREBOARD_SCROLL_BACKGROUND, SCOREBOARD_POSTGUI )
		
		-- Get the current Y position for the scroll marker
		local top = top + position * ( SCOREBOARD_Y + SCOREBOARD_HEIGHT - SCOREBOARD_SCROLL_HEIGHT - top )
		bottom = clamp ( 0, top + SCOREBOARD_SCROLL_HEIGHT, SCOREBOARD_Y + g_currentHeight )
		
		-- Make sure that it'd be visible
		if top < bottom then
			dxDrawRectangle ( left, top, right - left, bottom - top, SCOREBOARD_SCROLL_FOREGROUND, SCOREBOARD_POSTGUI )
		end
	end
end

function makeFont()
	if not theFont then
		theFont = dxCreateFont ( ':resources/cartwheel.otf' )
	end
	return theFont
end


--[[
* drawScoreboard
Draws the scoreboard contents.
--]]
drawScoreboard = function ()
	-- Check that we got the list of players
	if not g_players then return end

	-- First draw the background
	drawBackground ()
	
	-- Get the server information
	local serverName = getElementData ( g_scoreboardDummy, "serverName" ) or "MTA server"
	local maxPlayers = getElementData ( g_scoreboardDummy, "maxPlayers" ) or 0
	serverName = tostring ( serverName )
	maxPlayers = tonumber ( maxPlayers )
	
	-- Render the header
	-- Calculate the bounding box for the header texts
	local left, top, right, bottom = SCOREBOARD_X + 2, SCOREBOARD_Y + 2, SCOREBOARD_X + g_currentWidth - 2, SCOREBOARD_Y + SCOREBOARD_HEADER_HEIGHT - 2
	local strPlayerCount = "Players: " .. tostring(#g_players) .. "/250"
	-- Render the server name
	dxDrawText ( serverName .. " - " .. strPlayerCount, left, top, right, bottom,
				 SCOREBOARD_SERVER_NAME_COLOR, 1, "default-bold", "center", "center",
				 true, false, SCOREBOARD_POSTGUI )

	
	-- We need to recalculate the left position, to make it not move when fading.
	local offset = SCOREBOARD_WIDTH - dxGetTextWidth ( strPlayerCount, 1, makeFont() or "default" ) - 4
	left = left + offset
	-- Make sure of that it needs to be rendered now
	
	-- Draw the body.
	-- Update the bounding box.
	left, top, bottom = SCOREBOARD_X, SCOREBOARD_Y + SCOREBOARD_HEADER_HEIGHT + 2, SCOREBOARD_Y + g_currentHeight - 2

	-- Pre-calculate how much height will each row have.
	local rowHeight = dxGetFontHeight ( 1, "default-bold" )
	
	-- Draw the headers
	drawRowBounded ( "ID", "Player Name", "Hours Played", "Ping",
					 { SCOREBOARD_HEADERS_COLOR, SCOREBOARD_HEADERS_COLOR, SCOREBOARD_HEADERS_COLOR },
					 "default-bold", top )
	
				 
	-- Add the offset for a new row
	top = top + rowHeight + 3
	
	-- Draw the separator
	right = clamp ( 0, rowsBoundingBox[4][2] - 5, SCOREBOARD_X + g_currentWidth )
	if top < SCOREBOARD_Y + g_currentHeight then
		dxDrawLine ( SCOREBOARD_X + 5, top, right, top, SCOREBOARD_SEPARATOR_COLOR, 1, SCOREBOARD_POSTGUI )
	end
	top = top + 3
	
	-- Create a function to render a player entry
	local renderEntry = function ( player, seeUsername )
		-- Get the player data
		local playerID = getElementData ( player, "playerid" ) or 0
		playerID = tostring ( playerID )
		local playerUsername = ""
		if seeUsername then
			local uname = getElementData( player, 'account:username' )
			playerUsername = uname and (" ("..uname..")") or ""
		end
		local playerName = exports.global:getPlayerName( player )--..playerUsername
		local playerHours = jammain (player, 'hoursplayed') or 0 --getElementData( player, 'hoursplayed' ) or 0
		local playerPing = getPlayerPing ( player )
		playerHours = tostring( playerHours )
		playerPing = tostring ( playerPing )
		local r, g, b = getPlayerNametagColor ( player )
		local playerColor = tocolor ( r, g, b, 255 )
		
		-- Create the table of colors
		local colors = { playerColor, playerColor, playerColor, playerColor }
		
		-- Render it!
		drawRowBounded ( playerID, playerName, playerHours, playerPing, colors, "default-bold", top )
	end
	
	-- Calculate how much players can fit in the body window.
	local playersPerPage = math.floor ( ( SCOREBOARD_Y + SCOREBOARD_HEIGHT - top ) / ( rowHeight + SCOREBOARD_ROW_GAP ) )
	
	-- Get the amount of shifted players per page
	local playerShift = math.floor ( playersPerPage / 2 )
	
	-- Get the number of players to skip
	local playersToSkip = playerShift * g_currentPage
	if (#g_players - playersToSkip) < playersPerPage then
		-- Check that they didn't go to an invalid page
		if (#g_players - playersToSkip) < playerShift then
			g_currentPage = g_currentPage - 1
			if g_currentPage < 0 then g_currentPage = 0 end
		end

		-- Try to always fill pages
		playersToSkip = #g_players - playersPerPage + 1
	end
	
	-- Check for when there are too few players to fill one page.
	if playersToSkip < 0 then
		playersToSkip = 0
	end

	local isStaffOnDuty = exports.global:isStaffOnDuty( localPlayer )

	-- For every player in the cache, render a new entry.
	for k=playersToSkip + 1, #g_players do
		local player = g_players [ k ]
		local hasPerk, perkValue = exports.donators:hasPlayerPerk(player, 12)
		if not (hasPerk and tonumber(perkValue) == 1) or isStaffOnDuty then
			-- Check if it's gonna fit. If it doesn't stop rendering.
			if top < bottom - rowHeight - SCOREBOARD_ROW_GAP then
				renderEntry ( player, isStaffOnDuty )
				-- Update the height for the next entry
				top = top + rowHeight + SCOREBOARD_ROW_GAP
			else break end
		end
	end
	
	-- Draw the scrollbar. The maximum players to skip is #g_players - playersPerPage + 1, so when
	-- the scoreboard is fully scrolled it will become 1, while when it's not scrolled it will be
	-- 0 due to playersToSkip being 0.
	drawScrollBar ( SCOREBOARD_Y + SCOREBOARD_HEADER_HEIGHT + rowHeight + 10, playersToSkip / ( #g_players - playersPerPage + 1 ) )
end

function jammain(pemain)
	v = getElementData( pemain, "hoursplayed")
	return v
end
setTimer(jammain,5000,0)

--[[
* isVisible
Returns wherever or not the scoreboard is visible
--]]
function isVisible ( )
	return g_isShowing
end

and this is the server side

local scoreboardDummy

--[[
* onResourceStart
Handles the resource start event to create a dummy entity with information about the server.
--]]
addEventHandler ( "onResourceStart", getResourceRootElement(getThisResource()), function ()
	scoreboardDummy = createElement ( "scoreboard" )
	setElementData ( scoreboardDummy, "serverName", " Neo RP MTA Roleplay")
	setElementData ( scoreboardDummy, "maxPlayers", getMaxPlayers () )
	setElementData ( scoreboardDummy, "allow", true )
	
	--[[ Uncomment to test with dummies ]]--
	--[[
	for k=70,270 do
		local dummy = createElement ( "playerDummy" )
		setElementData ( dummy, "playerid", k )
		setElementData ( dummy, "name", "dummy" .. tostring(k), false )
		setElementData ( dummy, "ping", math.random ( 1, 300 ) )
		setElementData ( dummy, "color", { math.random(0,255), math.random(0,255), math.random(0,255) } )
	end
	--]]
end, false )

--[[
* onResourceStop
Delete the dummy created on resource start.
--]]
addEventHandler ( "onResourceStop", getResourceRootElement(getThisResource()), function ()
	if scoreboardDummy then
		destroyElement ( scoreboardDummy )
	end
end, false )

 

Edited by JonJo
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...