Raised This Month: $12 Target: $400
 3% 

Solved [CS:GO] Help with keyvalue-based ctban plugin


Post New Thread Reply   
 
Thread Tools Display Modes
potatoz
AlliedModders Donor
Join Date: Nov 2016
Old 08-18-2018 , 10:20   Re: [CS:GO] Help with keyvalue-based ctban plugin
Reply With Quote #11

Quote:
Originally Posted by Crasher_3637 View Post
Show your updated code.
I've got some other small issues that i noticed now afterwards but anyways, this is it;

@mug1wara I'll def try yours out too, major creds for taking the time

PHP Code:
#include <sourcemod>
#include <sdktools>
#include <cstrike>
#pragma semicolon 1 
 
int ctban[MAXPLAYERS+1];
Handle ctbantimer[MAXPLAYERS+1];
bool chatprinted[MAXPLAYERS+1];
 
KeyValues kv

public 
Plugin myinfo =
{
    
name "Simple CT-Ban",
    
author "Potatoz",
    
description "Simple CT-Ban system for jailbreak servers",
    
version "1.0",
    
url "http://www.sourcemod.net/"
};


public 
void OnPluginStart()
{
    
LoadTranslations("common.phrases");
    
RegAdminCmd("sm_ctban"Command_CTBanADMFLAG_BAN);
    
RegAdminCmd("sm_ctunban"Command_CTUnBanADMFLAG_BAN);
    
RegAdminCmd("sm_isbanned"Command_IsBannedADMFLAG_BAN);
    
    
HookEvent("player_team"Event_PlayerTeam);
    
AddCommandListener(Event_OnJoinTeam"jointeam");
    
    
char buffer[PLATFORM_MAX_PATH];
    
BuildPath(Path_SMbuffersizeof(buffer), "configs/ctban_database.cfg");
    
kv = new KeyValues("ctban_database");
    
kv.ImportFromFile(buffer);
}

public 
Action Event_PlayerTeam(Handle event, const char[] namebool dontBroadcast)
{
    new 
client  GetClientOfUserId(GetEventInt(event"userid"));
    if(
IsValidClient(client))
        
CreateTimer(0.2CheckTeamclientTIMER_FLAG_NO_MAPCHANGE);
}

public 
Action CheckTeam(Handle timerint client)
{
    if(
IsValidClient(client) && GetClientTeam(client) == && ctban[client] > 0)
    {
        
int minutes ctban[client] / 60;
        
minutes++;
    
        
PrintToChat(client" \x07* You are banned from the CT team for %.1d more minute(s)"minutes);
        
ChangeClientTeam(client2);
    }
    
    return 
Plugin_Handled;
}

public 
Action Event_OnJoinTeam(int client, const char[] commandint args
{
    if (!
IsValidClient(client) || IsFakeClient(client))
        return 
Plugin_Continue;

    
char teamString[2];
    
GetCmdArg(1teamStringsizeof(teamString));
    
int team StringToInt(teamString);

    
int minutes ctban[client] / 60;
    
minutes++;
    
    if (
team == 0
    {
        
PrintToChat(client" \x07* You can't auto-assign to select team");
        return 
Plugin_Stop;
    }
    
    if (
team == && ctban[client] > 0
    {
        
PrintToChat(client" \x07* You are banned from the CT team for %.1d more minute(s)"minutes);
        return 
Plugin_Stop;
    }
    
    return 
Plugin_Continue;
}

public 
OnClientPostAdminCheck(int client
{
    
chatprinted[client] = false;
    
GetCTBanData(client);
    
    if(
ctban[client] > 0)
        
ctbantimer[client] = CreateTimer(1.0Timer_CTBanclientTIMER_REPEAT TIMER_FLAG_NO_MAPCHANGE);
}

public 
Action Command_CTBan(int clientint args)
{
    if(
args 3
    {
        
ReplyToCommand(client"[SM] Usage: sm_ctban <name|#userid> [minutes] [reason]");
        return 
Plugin_Handled;
    }
    
    
char arg1[32], arg2[32], arg3[40];

    
GetCmdArg(1arg1sizeof(arg1));
    
GetCmdArg(2arg2sizeof(arg2));
    
GetCmdArg(3arg3sizeof(arg3));
    
    
int minutes StringToInt(arg2);
    
int target FindTarget(clientarg1);
    if (
target == -1) return Plugin_Handled;
    
    if(
ctban[target] > 0)
    {
        
PrintToChat(client" \x07* %N is already CT-Banned"target);
        return 
Plugin_Handled;
    }
    
    
chatprinted[target] = false;
    
ctban[target] = minutes 60;
    
ctbantimer[target] = CreateTimer(1.0Timer_CTBantargetTIMER_REPEAT TIMER_FLAG_NO_MAPCHANGE);
    
    
UpdateCTBanData(target);
    
    if(
GetClientTeam(target) == 3)
    {
        
ChangeClientTeam(target2);
        
ForcePlayerSuicide(target);
    }
    
    
PrintToChatAll(" \x07*\x01 ADMIN: \x07%N\x01 CT-Banned \x07%N \x01for %s minute(s) with reason: \x07%s"clienttargetarg2arg3);
    
    return 
Plugin_Handled;
}

public 
Action Command_CTUnBan(int clientint args)
{
    if(
args 1
    {
        
ReplyToCommand(client"[SM] Usage: sm_ctunban <name|#userid>");
        return 
Plugin_Handled;
    }
    
    
char arg1[32];

    
GetCmdArg(1arg1sizeof(arg1));
    
int target FindTarget(clientarg1);
    if (
target == -1) return Plugin_Handled;
    
    if(
ctban[target] < 1)
        
PrintToChat(client" \x07* %N is not CT-Banned"target);
    
    
chatprinted[target] = true;
    
ctbantimer[target] = null;
    
KillTimer(ctbantimer[target]);
    
    
ctban[target] = 0;
    
PrintToChatAll(" \x06*\x01 ADMIN: \x06%N\x01 has removed CT-Ban from \x06%N"clienttarget);
    
UpdateCTBanData(target);
    
    return 
Plugin_Handled;
}

public 
Action Timer_CTBan(Handle timerint client)
{
    if(
ctban[client] < 1)
    {
        
ctban[client] = 0;
        
        
ctbantimer[client] = null;
        
KillTimer(ctbantimer[client]);
        
        return 
Plugin_Stop;
    }
    
    
ctban[client]--;
    
UpdateCTBanData(client);
    
    if(
ctban[client] < 1)
    {
        if(!
chatprinted[client])
        {
            
PrintToChat(client" \x06*\x01 Your CT-Ban has expired");
            
chatprinted[client] = true;
        }
        
        
ctban[client] = 0;
        
        
ctbantimer[client] = null;
        
KillTimer(ctbantimer[client]);
        
        return 
Plugin_Stop;
    }
    
    return 
Plugin_Continue;
}

public 
void OnClientDisconnect(int client)
{
    if (
ctbantimer[client] != null)
    {
        
KillTimer(ctbantimer[client]);
        
ctbantimer[client] = null;
        
chatprinted[client] = false;
    }
}

public 
Action Command_IsBanned(int clientint args)
{
    if(
args 1
    {
        
ReplyToCommand(client"[SM] Usage: sm_isbanned <name|#userid>");
        return 
Plugin_Handled;
    }
    
    
char arg1[32];
    
GetCmdArg(1arg1sizeof(arg1));
    
int target FindTarget(clientarg1);
    if (
target == -1) return Plugin_Handled;
    
    
int minutes ctban[client] / 60;
    
minutes++;
    
    if(
ctban[target] < 1)
        
PrintToChat(client" \x07* %N is not CT-Banned"target);
    else 
PrintToChat(client" \x06* %N \x01is CT-Banned for %.1d more minute(s)"targetminutes);
    
    return 
Plugin_Handled;
}

UpdateCTBanData(int client)
{
    
char buffer[PLATFORM_MAX_PATH], steamauth[50];
    
GetClientAuthId(clientAuthId_Steam2steamauthsizeof(steamauth));
    
Format(buffersizeof(buffer), "%d"ctban[client]);
    
kv.JumpToKey(steamauthtrue);
    
kv.SetString("ctban_time"buffer);
    
    
int length StringToInt(buffer);
    if (
length == 0)
        
kv.DeleteThis();
    
    
kv.Rewind();
    
BuildPath(Path_SMbuffersizeof(buffer), "configs/ctban_database.cfg");
    
kv.ExportToFile(buffer);
}

GetCTBanData(int client)
{
    
char sConfig[PLATFORM_MAX_PATH], sUserID[50];
    
GetClientAuthId(clientAuthId_Steam2sUserIDsizeof(sUserID));
    
BuildPath(Path_SMsConfigsizeof(sConfig), "configs/ctban_database.cfg");

    
KeyValues hConfig = new KeyValues("ctban_database");
    
    if (
FileToKeyValues(hConfigsConfig))
    {
        if (
KvJumpToKey(hConfigsUserIDfalse))
        {
            
char sTimeLeft[20];
            
KvGetString(hConfig"ctban_time"sTimeLeftsizeof(sTimeLeft));
                
            if (
sTimeLeft[0] != '\0')
                
ctban[client] = StringToInt(sTimeLeft);
            else
                
ctban[client] = 0;
                
            if(
ctban[client] > 0)
            {
                if(
ctbantimer[client] == null)
                    
ctbantimer[client] = CreateTimer(1.0Timer_CTBanclientTIMER_REPEAT TIMER_FLAG_NO_MAPCHANGE);
            }
            else
            {
                
ctban[client] = 0;
                
KillTimer(ctbantimer[client]);
                
ctbantimer[client] = null;
            }
        }
        else
        {
            
ctban[client] = 0;
            
KillTimer(ctbantimer[client]);
            
ctbantimer[client] = null;
        }
    }
    
    
CloseHandle(hConfig);
}

bool IsValidClient(int client
{
    if (!( 
<= client <= MaxClients ) || !IsClientInGame(client)) 
        return 
false
     
    return 
true

__________________
potatoz is offline
potatoz
AlliedModders Donor
Join Date: Nov 2016
Old 08-18-2018 , 10:55   Re: [CS:GO] Help with keyvalue-based ctban plugin
Reply With Quote #12

Considering @mug1wara made more of a full remake and didnt include all features, i used his code as a base and built in the same functions of the original in to his which is using MySQL. Unsure if it works as of now but i've sent it to the server manager; still open for help on the code in both this and previous post, we really need a working CT-Ban system so it's all really appreciated.

This is my rendition of the code from @mug1wara, feel free to correct any of my mistakes in implementing it

PHP Code:
#include <sourcemod> 
#include <sdktools> 
#include <cstrike> 
#pragma semicolon 1 

ConVar ctban_database_name;
Database ctban_database;
char steamauth[MAXPLAYERS 1][32];
int ctban[MAXPLAYERS 1];

public 
Plugin myinfo 

    
name "CT-Ban"
    
author "potatoz & mug1wara"
    
description "SQL CT-Ban system for jailbreak servers"
    
version "1.1b"
    
url "" 
}; 

public 
void OnPluginStart()
{
    
ctban_database_name CreateConVar("sm_ctbans_database_name""ctbans");
    
    
LoadTranslations("common.phrases"); 
    
RegAdminCmd("sm_ctban"Command_CTBanADMFLAG_BAN); 
    
RegAdminCmd("sm_ctunban"Command_CTUnBanADMFLAG_BAN); 
    
RegAdminCmd("sm_isbanned"Command_IsBannedADMFLAG_BAN); 
    
    
HookEvent("player_team"Event_Team); 
    
AddCommandListener(Event_OnJoinTeam"jointeam"); 
}

public 
void OnConfigsExecuted()
{
    
char s_databasename[256];
    
GetConVarString(ctban_database_names_databasenamesizeof(s_databasename));
    
    
Database.Connect(SQL_Connections_databasename);
}

public 
void SQL_Connection(Database hDatabase, const char[] sErrorint iData)
{
    if (
hDatabase == null)
        
ThrowError(sError);
    else
    {
        
ctban_database hDatabase;
        
ctban_database.Query(SQL_Error"CREATE TABLE IF NOT EXISTS ctbans (steam_id VARCHAR(32) NOT NULL, time INT(11) NOT NULL)");
    }
}

public 
void SQL_Error(Database hDatabaseDBResultSet hResults, const char[] sErrorint iData)
{
    if (
hResults == null)
        
ThrowError(sError);
}

public 
void OnClientPostAdminCheck(int client)
{
    if (
IsValidClient(client))
    {
        
GetClientAuthId(clientAuthId_Steam2steamauth[client], sizeof(steamauth[]));
        
        
char query[256];
        
Format(querysizeof(query), "SELECT * FROM ctbans WHERE steam_id = \"%s\""steamauth[client]);
        
        
ctban_database.Query(SQL_AuthorizationqueryGetClientUserId(client));
    }
}

public 
void SQL_Authorization(Database hDatabaseDBResultSet hResults, const char[] sErrorint iData)
{
    if (
hResults == null)
        
ThrowError(sError);
    
    
int client GetClientOfUserId(client);
    
    if (
IsValidClient(client))
    {
        if (
hResults.RowCount != 0)
        {
            
hResults.FetchRow();
            
            
ctban[client] = hResults.FetchInt(1);
            if (
ctban[client] != 0)
                
CreateTimer(1.0Timer_CTBanTimerGetClientUserId(client), TIMER_REPEAT TIMER_FLAG_NO_MAPCHANGE);
        }
        else
        {
            
char query[256];
            
Format(querysizeof(query), "INSERT INTO ctbans (steam_id, time) VALUES (\"%s\", '%d')"steamauth[client], ctban[client]);
            
            
ctban_database.Query(SQL_Errorquery);
        }
    }
}

public 
Action Event_OnJoinTeam(int client, const char[] commandint args)  

    if (!
IsValidClient(client) || IsFakeClient(client)) 
        return 
Plugin_Continue

    
char teamString[2]; 
    
GetCmdArg(1teamStringsizeof(teamString)); 
    
int team StringToInt(teamString); 

    
int minutes ctban[client] / 60
    
minutes++;
     
    if (
team == 0)  
    { 
        
PrintToChat(client" \x07* You can't auto-assign to select team"); 
        return 
Plugin_Stop
    } 
     
    if (
team == && ctban[client] > 0)  
    { 
        
PrintToChat(client" \x07* You are banned from the CT team for %.1d more minute(s)"minutes); 
        return 
Plugin_Stop
    } 
     
    return 
Plugin_Continue


public 
Action Event_Team(Handle event, const char[] namebool dontBroadcast

    new 
client  GetClientOfUserId(GetEventInt(event"userid")); 
    if(
IsValidClient(client)) 
        
CreateTimer(0.2CheckTeamclientTIMER_FLAG_NO_MAPCHANGE); 


public 
Action CheckTeam(Handle timerint client

    if(
IsValidClient(client) && GetClientTeam(client) == && ctban[client] > 0
    { 
        
int minutes ctban[client] / 60
        
minutes++; 
     
        
PrintToChat(client" \x07* You are banned from the CT team for %.1d more minute(s)"minutes); 
        
ChangeClientTeam(client2); 
    } 
     
    return 
Plugin_Handled


public 
Action Timer_CTBanTimer(Handle timerint client)
{
    if (
IsValidClient(client))
    {
        if (
ctban[client] < 1)
        {
            
PrintToChat(client" \x06*\x01 Your CT-Ban has expired");
            return 
Plugin_Stop;
        }
        
        
ctban[client]--;
        
        
char query[256];
        
Format(querysizeof(query), "UPDATE ctbans SET time = '%d' WHERE steam_id = \"%s\""ctban[client], steamauth[client]);
        
ctban_database.Query(SQL_Errorquery);
    }
    
    return 
Plugin_Continue;
}

public 
Action Command_CTBan(int clientint args)
{
    if (
IsValidClient(client))
    {
        if(
args 3)  
        { 
            
ReplyToCommand(client"[SM] Usage: sm_ctban <name|#userid> [minutes] [reason]"); 
            return 
Plugin_Handled
        } 
        
        
char target[MAX_NAME_LENGTH], time[32], reason[40];
        
        
GetCmdArg(1targetsizeof(target));
        
GetCmdArg(2timesizeof(time));
        
GetCmdArg(3reasonsizeof(reason)); 
        
        
int target2 FindTarget(clienttarget);
        
ctban[target2] = StringToInt(time) * 60;
        
        
char query[256];
        
Format(querysizeof(query), "UPDATE ctbans SET time = '%d' WHERE steam_id = \"%s\""ctban[target2], steamauth[target2]);
        
ctban_database.Query(SQL_Errorquery);
        
        if(
GetClientTeam(target2) == 3
        { 
            
ChangeClientTeam(target22); 
            
ForcePlayerSuicide(target2); 
        } 
     
        
PrintToChatAll(" \x07*\x01 ADMIN: \x07%N\x01 CT-Banned \x07%N \x01for %s minute(s) with reason: \x07%s"clienttarget2timereason);
    }
    
    return 
Plugin_Continue;
}

public 
Action Command_CTUnBan(int clientint args

    if(
args 1)  
    { 
        
ReplyToCommand(client"[SM] Usage: sm_ctunban <name|#userid>"); 
        return 
Plugin_Handled
    } 
     
    
char arg1[32]; 

    
GetCmdArg(1arg1sizeof(arg1)); 
    
int target FindTarget(clientarg1); 
    if (
target == -1) return Plugin_Handled
     
    if(
ctban[target] < 1
        
PrintToChat(client" \x07* %N is not CT-Banned"target);  
     
    
ctban[target] = 0;
    
    
char query[256];
    
Format(querysizeof(query), "UPDATE ctbans SET time = '%d' WHERE steam_id = \"%s\""ctban[target], steamauth[target]);
    
ctban_database.Query(SQL_Errorquery);
    
    
PrintToChatAll(" \x06*\x01 ADMIN: \x06%N\x01 has removed CT-Ban from \x06%N"clienttarget); 
     
    return 
Plugin_Handled
}

public 
Action Command_IsBanned(int clientint args

    if(
args 1)  
    { 
        
ReplyToCommand(client"[SM] Usage: sm_isbanned <name|#userid>"); 
        return 
Plugin_Handled
    } 
     
    
char arg1[32]; 
    
GetCmdArg(1arg1sizeof(arg1)); 
    
int target FindTarget(clientarg1); 
    if (
target == -1) return Plugin_Handled
     
    
int minutes ctban[client] / 60
    
minutes++; 
     
    if(
ctban[target] < 1
        
PrintToChat(client" \x07* %N is not CT-Banned"target); 
    else 
PrintToChat(client" \x06* %N \x01is CT-Banned for %.1d more minute(s)"targetminutes); 
     
    return 
Plugin_Handled
}

public 
void OnClientDisconnect(int client)
{
    if (
IsValidClient(client))
    {
        
char query[256];
        
Format(querysizeof(query), "UPDATE ctbans SET time = '%d' WHERE steam_id = \"%s\""ctban[client], steamauth[client]);
        
ctban_database.Query(SQL_Errorquery);
    }
}

public 
bool IsValidClient(int client)
{
    if (!(
client <= MaxClients) || !IsClientInGame(client) || IsFakeClient(client))
        return 
false;
    
    return 
true;

__________________
potatoz is offline
mug1wara
AlliedModders Donor
Join Date: Jun 2018
Old 08-18-2018 , 12:45   Re: [CS:GO] Help with keyvalue-based ctban plugin
Reply With Quote #13

I just threw my eye on it for a couple of seconds and noticed that you frequently update the database.

It's not necessary, because all the information is stored within the variables so when the client leaves, we update the database in order to make the code more efficient.

I also see that the time converting was quite wrong, made it easy for you:
PHP Code:
void TimeConverter(int iLengthchar[] sFormat)
{    
    
int iDaysiHoursiMinutesiTime;
    
    
char sDays[5], sHours[5], sMinutes[5];
    
    
iTime RoundToFloor(float(iLength));
    
    if (
iTime >= 86400)
    {
        
iDays RoundToFloor(float(iTime 86400));
        
iTime %= 86400;
    }
    
    if (
iTime >= 3600)
    {
        
iHours RoundToFloor(float(iTime 3600));
        
iTime %= 3600;
    }
    
    if (
iTime >= 60)
    {
        
iMinutes RoundToFloor(float(iTime 60));
        
iTime %= 60;
    }
    
    if (
iDays 10)
    {
        if (
iDays == 0)
            
Format(sDayssizeof(sDays), "");    
        
        else
            
Format(sDayssizeof(sDays), "0%i:"iDays);    
    }
    
    else
        
Format(sDayssizeof(sDays), "%i:"iDays);
    
    if (
iHours 10)
    {
        if (
iHours == 0)
            
Format(sHourssizeof(sHours), "");
        
        else
            
Format(sHourssizeof(sHours), "0%i:"iHours);    
    }
    
    else
        
Format(sHourssizeof(sHours), "%i:"iHours);
        
    if (
iMinutes 10)
    {
        if (
iMinutes == 0)
            
Format(sMinutessizeof(sMinutes), "");
        
        else
            
Format(sMinutessizeof(sMinutes), "0%i:"iMinutes);    
    }
    
    else
        
Format(sMinutessizeof(sMinutes), "%i:"iMinutes);
    
    
Format(sFormatsizeof(sFormat), "%s%s%s%s%i"sDayssHourssMinutes, (iTime 10) ? "0" ""iTime);

Example usage:
PHP Code:
char sTime[32];

TimeConverter(g_iTime[iClient], sTime)

PrintToChat(iClient"%s"sTime); 
mug1wara is offline
potatoz
AlliedModders Donor
Join Date: Nov 2016
Old 08-18-2018 , 15:38   Re: [CS:GO] Help with keyvalue-based ctban plugin
Reply With Quote #14

I've removed most of the unnecessary database updates but when it comes to time converting i prefer to keep it the way it is where all seen times are in minutes but is stored in seconds; the simplistic sourcebans way
__________________
potatoz is offline
mug1wara
AlliedModders Donor
Join Date: Jun 2018
Old 08-18-2018 , 16:22   Re: [CS:GO] Help with keyvalue-based ctban plugin
Reply With Quote #15

That's fine, good job keeping up! Cheers
mug1wara is offline
Psyk0tik
Veteran Member
Join Date: May 2012
Location: Homeless
Old 08-18-2018 , 16:49   Re: [CS:GO] Help with keyvalue-based ctban plugin
Reply With Quote #16

One thing that I've noticed is that you keep passing the client as the data through your timers. Maybe that's why you're having clients who get other clients' bans.

You need to pass GetClientUserId(client) instead since User IDs are unique to each client. If you're only passing "client" then you could have instances where 1 player leaves and is immediately replaced by another, thus getting the ban duration of the one that just left.

Example:

PHP Code:
CreateTimer(1.0tTimerCallbackGetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);

public 
Action tTimerCallback(Handle timerany userid)
{
     
int client GetClientOfUserId(userid);
     
// Start adding your code here...

__________________
Psyk0tik is offline
potatoz
AlliedModders Donor
Join Date: Nov 2016
Old 08-18-2018 , 20:03   Re: [CS:GO] Help with keyvalue-based ctban plugin
Reply With Quote #17

Quote:
Originally Posted by Crasher_3637 View Post
One thing that I've noticed is that you keep passing the client as the data through your timers. Maybe that's why you're having clients who get other clients' bans.

You need to pass GetClientUserId(client) instead since User IDs are unique to each client. If you're only passing "client" then you could have instances where 1 player leaves and is immediately replaced by another, thus getting the ban duration of the one that just left.

Example:

PHP Code:
CreateTimer(1.0tTimerCallbackGetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);

public 
Action tTimerCallback(Handle timerany userid)
{
     
int client GetClientOfUserId(userid);
     
// Start adding your code here...

Aside from the SQL plugin i will def make a update to the keyvalue one and just drop both of the .sp-files here for anyone in future need. I'll take your post in to consideration when updating the keyvalue-based plugin for sure, first thing i'll do in the morning!
__________________
potatoz is offline
potatoz
AlliedModders Donor
Join Date: Nov 2016
Old 08-19-2018 , 06:05   Re: [CS:GO] Help with keyvalue-based ctban plugin
Reply With Quote #18

Here are the final and hopefully working plugins, both using the newer MySQL with help from mug1wara and the keyvalue one, will change the thread to solved but feel free to continue making it better or correcting any issues; it's always appreciated
Attached Files
File Type: sp Get Plugin or Get Source (ctban_sql.sp - 167 views - 7.9 KB)
File Type: sp Get Plugin or Get Source (ctban_keyvalue.sp - 181 views - 6.9 KB)
__________________
potatoz is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 16:25.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode