Raised This Month: $169 Target: $400
 42% 

[INC] Client MethodMap


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
ThatKidWhoGames
Senior Member
Join Date: Jun 2013
Location: IsValidClient()
Old 04-28-2018 , 18:49   [INC] Client MethodMap
Reply With Quote #1

I got bored and wanted to learn how to make method maps. Huge thanks to this thread for helping me better understand how method maps work: https://forums.alliedmods.net/showthread.php?t=260680

Anyways, let's begin!

BTW, I want to give credit where credit is due to VoiDeD for a very similar client methodmap. I had seen it before, but didn't remember that it was a thing. However since it does look similar, I feel as though it is right to give him the majority of the credit. I will probably end up combining some of his code with mine for future updates.

Grabbing information from a client:

Before
PHP Code:
void GetClientInformation(int client)
{
    
char name[32], ip[32], steamid[32];
    
GetClientName(clientnamesizeof(name));
    
GetClientIP(clientipsizeof(ip));
    
GetClientAuthId(clientAuthId_Steam2steamidsizeof(steamid));

After
PHP Code:
void GetClientInformation(Player player)
{
    
char name[32], ip[32], steamid[32];
    
player.GetName(namesizeof(name));
    
player.GetIP(ipsizeof(ip));
    
player.GetAuthId(AuthId_Steam2steamidsizeof(steamid));

Kicking a client:

Before
PHP Code:
void KickThatHecker(int client)
{
    
KickClient(client"Leave the server you hecker and don't come back");

After
PHP Code:
void KickThatHecker(Player player)
{
    
player.Kick("Leave the server you hecker and don't come back");

Getting client's stats:

Before
PHP Code:
void GetClientStats(int client)
{
    
int health     GetClientHealth(client);
    
int frags      GetClientFrags(client);
    
int deaths     GetClientDeaths(client);

After
PHP Code:
void GetClientStats(Player player)
{
    
int health     player.Health;
    
int frags      player.Frags;
    
int deaths     player.Deaths;

To declare a Player variable in your code, do (something like) this:
PHP Code:
public void OnClientPostAdminCheck(int client)
{
    
Player player Player(client);

PHP Code:
methodmap Player
{
    public 
Player(int player) {
        return 
view_as<Player>(player);
    }

    
//=====================//
    // P R O P E R T I E S //
    //=====================//

    /**
     * Returns the client's index.
     *
     * @return                Client's index.
     */
    
property int index {
        public 
get() {
            return 
view_as<int>(this);
        }
    }

    
/**
     * Retrieves the client's user id, which is an index incremented for every client
     * that joins the server.
     *
     * @return                User id of the client.
     * @error                 If the client is not connected or the index is invalid.
     */
    
property int UserId {
        public 
get() {
            return 
GetClientUserId(this.index);
        }
    }

    
/**
     * Returns the client's unique serial identifier.
     *
     * @return    Serial number.
     * @error                Invalid client index, or client not connected.
     */
    
property int Serial {
        public 
get() {
            return 
GetClientSerial(this.index);
        }
    }

    
/**
     * Returns if a player is valid or not.
     *
     * @return                True if player is valid, false otherwise.
     */
    
property bool IsValid {
        public 
get() {
            return 
IsValidClient(this.index);
        }
    }

    
/**
     * Returns if a player is connected.
     *
     * @return                True if player is connected to the server, false otherwise.
     */
    
property bool IsConnected {
        public 
get() {
            return 
IsClientConnected(this.index);
        }
    }

    
/**
     * Returns if a player has entered the game.
     *
     * @return                True if player has entered the game, false otherwise.
     * @error                Invalid client index.
     */
    
property bool InGame {
        public 
get() {
            return 
IsClientInGame(this.index);
        }
    }

    
/**
     * Returns if a client is timing out
     *
     * @return                True if client is timing out, false otherwise.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
property bool IsTimingOut {
        public 
get() {
            return 
IsClientTimingOut(this.index);
        }
    }

    
/**
     * Returns if a player is in the "kick queue" (i.e. the client will be kicked 
     * shortly and thus they should not appear as valid).
     *
     * @return                True if in the kick queue, false otherwise.
     * @error                Invalid client index.
     */
    
property bool InKickQueue {
        public 
get() {
            return 
IsClientInKickQueue(this.index);
        }
    }

    
/**
     * Returns if a player has been authenticated.
     *
     * @return                True if player has been authenticated, false otherwise.
     */
    
property bool IsAuthorized {
        public 
get() {
            return 
IsClientAuthorized(this.index);
        }
    }

    
/**
     * Returns if a player is a fake client.
     *
     * @return                True if player is a fake client, false otherwise.
     */
    
property bool IsFake {
        public 
get() {
            return 
IsFakeClient(this.index);
        }
    }

    
/**
     * Returns if a certain player is the SourceTV bot.
     *
     * @return                True if player is the SourceTV bot, false otherwise.
     */
    
property bool IsSourceTV {
        public 
get() {
            return 
IsClientSourceTV(this.index);
        }
    }

    
/**
     * Returns if a certain player is the Replay bot.
     *
     * @return                True if player is the Replay bot, false otherwise.
     */
    
property bool IsReplay {
        public 
get() {
            return 
IsClientReplay(this.index);
        }
    }

    
/**
     * Returns if a certain player is an observer/spectator.
     *
     * @return                True if player is an observer, false otherwise.
     */
    
property bool IsObserver {
        public 
get() {
            return 
IsClientObserver(this.index);
        }
    }

    
/**
     * Returns if the client is alive or dead.
     *
     * Note: This function was originally in SDKTools and was moved to core.
     *
     * @return                True if the client is alive, false otherwise.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
property bool IsAlive {
        public 
get() {
            return 
IsPlayerAlive(this.index);
        }
    }

    
/**
     * Returns client access flags.  If the client is not an admin,
     * the result is always 0.
     * 
     * @return                Flags
     * @error                Invalid client index, or client not connected.
     */
    
property int FlagBits {
        public 
get() {
            return 
GetUserFlagBits(this.index);
        }
    }

    
/**
     * Retrieves a client's team index (and can also change the client's team).
     *
     * @return                Team index the client is on (mod specific).
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
property int Team {
        public 
get() {
            return 
GetClientTeam(this.index);
        }
        public 
set(int team) {
            
ChangeClientTeam(this.indexteam);
        }
    }

    
/**
     * Returns the client's health.
     *
     * @return                Health value.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
property int Health {
        public 
get() {
            return 
GetClientHealth(this.index);
        }
    }

    
/**
     * Returns the client's death count.
     *
     * @return                Death count.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
property int Deaths {
        public 
get() {
            return 
GetClientDeaths(this.index);
        }
    }

    
/**
     * Returns the client's frag count.
     *
     * @return                Frag count.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
property int Frags {
        public 
get() {
            return 
GetClientFrags(this.index);
        }
    }

    
/**
     * Returns the client's armor.
     *
     * @return                Armor value.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
property int Armor {
        public 
get() {
            return 
GetClientArmor(this.index);
        }
    }

    
/**
     * Returns the client's send data rate in bytes/sec.
     *
     * @return                Data rate.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
property int DataRate {
        public 
get() {
            return 
GetClientDataRate(this.index);
        }
    }

    
/**
     * Returns the client's connection time in seconds.
     *
     * @return                Connection time.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
property float ConnectionTime {
        public 
get() {
            return 
GetClientTime(this.index);
        }
    }

    
//=====================//
    //  F U N C T I O N S  //
    //=====================//

    /**
     * Returns the client's name.
     *
     * @param name            Buffer to store the client's name.
     * @param maxlen        Maximum length of string buffer (includes NULL terminator).
     * @return                True on success, false otherwise.
     * @error                If the client is not connected an error will be thrown.
     */
    
public bool GetName(char[] nameint maxlen) {
        return 
GetClientName(this.indexnamemaxlen);
    }

    
/**
     * Retrieves the client's IP address.
     *
     * @param ip            Buffer to store the client's ip address.
     * @param maxlen        Maximum length of string buffer (includes NULL terminator).
     * @param remport        Remove client's port from the ip string (true by default).
     * @return                True on success, false otherwise.
     * @error                If the client is not connected or the index is invalid.
     */
    
public bool GetIP(char[] ipint maxlenbool remport=true) {
        return 
GetClientIP(this.indexipmaxlenremport);
    }

    
/**
     * Retrieves the client's authentication string (SteamID).
     *
     * @param authType        Auth id type and format to use.
     * @param auth            Buffer to store the client's auth id.
     * @param maxlen        Maximum length of string buffer (includes NULL terminator).
     * @param validate        Check backend validation status.
     *                 DO NOT PASS FALSE UNLESS YOU UNDERSTAND THE CONSEQUENCES,
     *                    You WILL KNOW if you need to use this, MOST WILL NOT.
     * @return                True on success, false otherwise.
     * @error                If the client is not connected or the index is invalid.
     */
    
public bool GetAuthId(AuthIdType authTypechar[] authint maxlenbool validate=true) {
        return 
GetClientAuthId(this.indexauthTypeauthmaxlenvalidate);
    }

    
/**
     * Returns the client's Steam account ID.
     *
     * @param validate        Check backend validation status.
     *                 DO NOT PASS FALSE UNLESS YOU UNDERSTAND THE CONSEQUENCES,
     *                    You WILL KNOW if you need to use this, MOST WILL NOT.
     * @return                Steam account ID or 0 if not available.
     * @error                If the client is not connected or the index is invalid.
     */
    
public int GrabSteamAccountID(bool validate=true) {
        return 
GetSteamAccountID(this.indexvalidate);
    }

    
/**
     * Retrieves values from client replicated keys.
     *
     * @param key            Key string.
     * @param value            Buffer to store value.
     * @param maxlen        Maximum length of valve (UTF-8 safe).
     * @return                 True on success, false otherwise.
     * @error                Invalid client index, or client not connected.
     */
    
public bool GetInfo(const char[] keychar[] valueint maxlen) {
        return 
GetClientInfo(this.indexkeyvaluemaxlen);
    }

    
/** 
     * Sets access flags on a client using bits instead of flags.  If the
     * client is not an admin, and flags not 0, a temporary, anonymous AdminId is given.
     *
     * @param flags            Bitstring of flags to set on client.
     */
    
public void SetFlagBits(int flags) {
        return 
SetUserFlagBits(this.indexflags);
    }

    
/**
     * Returns whether a user can target another user.
     * This is a helper function for CanAdminTarget.
     *
     * @param target        Target player's index.
     * @return                True if target is targettable by the player, false otherwise.
     * @error                Invalid or unconnected player indexers.
     */
    
public bool CanTarget(int target) {
        return 
CanUserTarget(this.indextarget);
    }

    
/**
     * Runs through the Core-defined admin authorization checks on a player.
     * Has no effect if the player is already an admin.
     *
     * Note: This function is based on the internal cache only.
     *
     * @return                True if access was changed, false if it did not.
     * @error                Invalid client index or client not in-game AND authorized.
     */
    
public bool AdminCacheCheck() {
        return 
RunAdminCacheChecks(this.index);
    }

    
/**
     * Signals that a player has completed post-connection admin checks.
     * Has no effect if the player has already had this event signalled.
     *
     * Note: This must be sent even if no admin id was assigned.
     *
     * @error                Invalid client index or client not in-game AND authorized.
     */
    
public void PostAdminCheck() {
        return 
NotifyPostAdminCheck(this.index);
    }

    
/**
     * Returns the client's model name.
     *
     * @param model            Buffer to store the client's model name.
     * @param maxlen        Maximum length of string buffer (includes NULL terminator).
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
public void GetModel(char[] modelint maxlen) {
        return 
GetClientModel(this.indexmodelmaxlen);
    }

    
/**
     * Returns the client's weapon name.
     *
     * @param weapon        Buffer to store the client's weapon name.
     * @param maxlen        Maximum length of string buffer (includes NULL terminator).
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
public void GetWeapon(char[] weaponint maxlen) {
        return 
GetClientWeapon(this.indexweaponmaxlen);
    }

    
/**
     * Returns the client's max size vector.
     *
     * @param vec            Destination vector to store the client's max size.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
public void GetMaxs(float vec[3]) {
        return 
GetClientMaxs(this.indexvec);
    }

    
/**
     * Returns the client's min size vector.
     *
     * @param vec            Destination vector to store the client's min size.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
public void GetMins(float vec[3]) {
        return 
GetClientMins(this.indexvec);
    }

    
/**
     * Returns the client's position angle.
     *
     * @param ang            Destination vector to store the client's position angle.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
public void GetAbsAngles(float ang[3]) {
        return 
GetClientAbsAngles(this.indexang);
    }

    
/**
     * Returns the client's origin vector.
     *
     * @param vec            Destination vector to store the client's origin vector.
     * @error                Invalid client index, client not in game, or no mod support.
     */
    
public void GetAbsOrigin(float vec[3]) {
        return 
GetClientAbsOrigin(this.indexvec);
    }

    
/**
     * Returns the client's current latency (RTT), more accurate than GetAvgLatency but jittering.
     *
     * @param flow            Traffic flowing direction.
     * @return                Latency, or -1 if network info is not available.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
public float GetLatency(NetFlow flow) {
        return 
GetClientLatency(this.indexflow);
    }

    
/**
     * Returns the client's average packet latency in seconds.
     *
     * @param flow            Traffic flowing direction.
     * @return                Latency, or -1 if network info is not available.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
public float GetAvgLatency(NetFlow flow) {
        return 
GetClientAvgLatency(this.indexflow);
    }

    
/**
     * Returns the client's average packet loss, values go from 0 to 1 (for percentages).
     *
     * @param flow            Traffic flowing direction.
     * @return                Average packet loss, or -1 if network info is not available.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
public float GetAvgLoss(NetFlow flow) {
        return 
GetClientAvgLoss(this.indexflow);
    }

    
/**
     * Returns the client's average packet choke, values go from 0 to 1 (for percentages).
     *
     * @param flow            Traffic flowing direction.
     * @return                Average packet loss, or -1 if network info is not available.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
public float GetAvgChoke(NetFlow flow) {
        return 
GetClientAvgChoke(this.indexflow);
    }

    
/**
     * Returns the client's data flow in bytes/sec.
     *
     * @param flow            Traffic flowing direction.
     * @return                Data flow.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
public float GetAvgData(NetFlow flow) {
        return 
GetClientAvgData(this.indexflow);
    }

    
/**
     * Returns the client's average packet frequency in packets/sec.
     *
     * @param flow            Traffic flowing direction.
     * @return                Packet frequency.
     * @error                Invalid client index, client not connected, or fake client.
     */
    
public float GetAvgPackets(NetFlow flow) {
        return 
GetClientAvgPackets(this.indexflow);
    }

    
/**
     * Changes a client's team through the mod's generic team changing function.
     * On CS:S, this will kill the player.
     *
     * @param team            Mod-specific team index.
     * @error                Invalid client index, client not connected, or lack of 
     *                        mod support.
     */
    
public void ChangeTeam(int team) {
        return 
ChangeClientTeam(this.indexteam);
    }

    
/**
     * Disconnects a client from the server as soon as the next frame starts.
     *
     * Note: Originally, KickClient() was immediate.  The delay was introduced 
     * because despite warnings, plugins were using it in ways that would crash. 
     * The new safe version can break cases that rely on immediate disconnects, 
     * but ensures that plugins do not accidentally cause crashes.
     *
     * If you need immediate disconnects, use KickClientEx().
     *
     * Note: IsClientInKickQueue() will return true before the kick occurs.
     *
     * @param format        Optional formatting rules for disconnect reason.
     *                      Note that a period is automatically appended to the string by the engine.
     * @param ...            Variable number of format parameters.
     * @error                Invalid client index, or client not connected.
     */
    
public void Kick(const char[] format=""any ...) {
        if (
format[0] == '\0')
            return 
KickClient(this.index"No reason given");
        else {
            
char message[256];
            
VFormat(messagesizeof(message), format3);
            return 
KickClient(this.indexmessage);
        }
    }

    
/**
     * Immediately disconnects a client from the server.
     *
     * Kicking clients from certain events or callbacks may cause crashes.  If in 
     * doubt, create a short (0.1 second) timer to kick the client in the next 
     * available frame.
     *
     * @param format        Optional formatting rules for disconnect reason.
     *                      Note that a period is automatically appended to the string by the engine.
     * @param ...            Variable number of format parameters.
     * @error                Invalid client index, or client not connected.
     */
    
public void KickEx(const char[] format=""any ...) {
        if (
format[0] == '\0')
            return 
KickClientEx(this.index"No reason given");
        else {
            
char message[256];
            
VFormat(messagesizeof(message), format3);
            return 
KickClientEx(this.indexmessage);
        }
    }

Enjoy! Also, feel free to leave suggestions/feedback.
Attached Files
File Type: inc clients_methodmap.inc (17.2 KB, 54 views)
__________________


I take custom plugin requests, add me on Steam for details.

Last edited by ThatKidWhoGames; 05-04-2018 at 10:13.
ThatKidWhoGames is offline
Crasher_3637
AlliedModders Donor
Join Date: May 2012
Old 04-29-2018 , 01:27   Re: [INC] Client MethodMap
Reply With Quote #2

I'm gonna start using this. Thanks!

Side note:

I noticed that in your .inc file, there is an IsValidClient stock and that you have both IsClientConnected and IsClientInGame. You only really need to use IsClientInGame since it already implies that IsClientConnected is true. Using both is redundant.

Change:
PHP Code:
stock bool IsValidClient(int client)
{
    return 
client && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client);

To this:
PHP Code:
stock bool IsValidClient(int client)
{
    return 
client && client <= MaxClients && IsClientInGame(client);

__________________
AFK like JFK
Crasher_3637 is offline
ThatKidWhoGames
Senior Member
Join Date: Jun 2013
Location: IsValidClient()
Old 04-29-2018 , 09:49   Re: [INC] Client MethodMap
Reply With Quote #3

Quote:
Originally Posted by Crasher_3637 View Post
I'm gonna start using this. Thanks!

Side note:

I noticed that in your .inc file, there is an IsValidClient stock and that you have both IsClientConnected and IsClientInGame. You only really need to use IsClientInGame since it already implies that IsClientConnected is true. Using both is redundant.

Change:
PHP Code:
stock bool IsValidClient(int client)
{
    return 
client && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client);

To this:
PHP Code:
stock bool IsValidClient(int client)
{
    return 
client && client <= MaxClients && IsClientInGame(client);

Thanks for the suggestion!
__________________


I take custom plugin requests, add me on Steam for details.
ThatKidWhoGames is offline
Ragenewb
Member
Join Date: May 2017
Location: RemoveEntity(0);
Old 04-29-2018 , 22:20   Re: [INC] Client MethodMap
Reply With Quote #4

Very nice!
Don't forget you can have a setter for your 'Team' property to change teams as well, like so:
PHP Code:
    property int Team 
        public 
get() { 
            return 
GetClientTeam(this.index); 
        } 
        public 
set(int team) {
            
ChangeClientTeam(this.indexteam);
        }
    } 
__________________
Will give spaghetti code for food.
Ragenewb is offline
ThatKidWhoGames
Senior Member
Join Date: Jun 2013
Location: IsValidClient()
Old 04-30-2018 , 07:26   Re: [INC] Client MethodMap
Reply With Quote #5

Quote:
Originally Posted by Ragenewb View Post
Very nice!
Don't forget you can have a setter for your 'Team' property to change teams as well, like so:
PHP Code:
    property int Team 
        public 
get() { 
            return 
GetClientTeam(this.index); 
        } 
        public 
set(int team) {
            
ChangeClientTeam(this.indexteam);
        }
    } 
I was actually considering doing that, but I guess it was an oversight. I'll update the include once I can. I'll probably end up doing this for client's health, armor, etc. (along with adding (potentially) a few other useful stocks for managing clients). Thanks for the feedback btw!

EDIT: File updated!
__________________


I take custom plugin requests, add me on Steam for details.

Last edited by ThatKidWhoGames; 04-30-2018 at 07:46.
ThatKidWhoGames is offline
TheXeon
Member
Join Date: May 2016
Location: GetClientAbsOrigin(Zeron
Old 05-04-2018 , 01:14   Re: [INC] Client MethodMap
Reply With Quote #6

I thought I remembered something for this, this is cool to mix and match with this:

https://github.com/VoiDeD/sourcemod-...ers/player.inc
__________________


EVERYTHING BREAKS ALWAYS!
TheXeon is offline
404UNF
( ͡ ͜ʖ ͡)
Join Date: Dec 2011
Location: 🍁
Old 05-04-2018 , 03:21   Re: [INC] Client MethodMap
Reply With Quote #7

Quote:
Originally Posted by TheXeon View Post
I thought I remembered something for this, this is cool to mix and match with this:

https://github.com/VoiDeD/sourcemod-...ers/player.inc
Was about to post that. That, and I've also been working on my own TFPlayer methodmap include.
__________________
I'm not a SourcePawn expert, I just play one on TV.
Want to join the SourceMod Discord server? Click here!
<- Links fixed!
404UNF is offline
ThatKidWhoGames
Senior Member
Join Date: Jun 2013
Location: IsValidClient()
Old 05-04-2018 , 09:56   Re: [INC] Client MethodMap
Reply With Quote #8

Quote:
Originally Posted by TheXeon View Post
I thought I remembered something for this, this is cool to mix and match with this:

https://github.com/VoiDeD/sourcemod-...ers/player.inc
Quote:
Originally Posted by 404UNF View Post
Was about to post that. That, and I've also been working on my own TFPlayer methodmap include.
Holy shit, I think I remember seeing that before. I'll add a credits section for VoiDeD because my code looks very similar.

Also 404UNF, I actually ended up in my free time coding your (from what I understand) exact idea a few days ago! I took the tf2_stocks.inc and tf2.inc and made one big methodmap for them. Check it out if you want, attached it to this message!
Attached Files
File Type: inc tf2_methodmap.inc (2.2 KB, 25 views)
__________________


I take custom plugin requests, add me on Steam for details.

Last edited by ThatKidWhoGames; 05-04-2018 at 10:15.
ThatKidWhoGames is offline
404UNF
( ͡ ͜ʖ ͡)
Join Date: Dec 2011
Location: 🍁
Old 05-05-2018 , 02:04   Re: [INC] Client MethodMap
Reply With Quote #9

Quote:
Originally Posted by ThatKidWhoGames View Post
Also 404UNF, I actually ended up in my free time coding your (from what I understand) exact idea a few days ago! I took the tf2_stocks.inc and tf2.inc and made one big methodmap for them. Check it out if you want, attached it to this message!
Oh nice! This is as far as I had gotten with mine:
Spoiler
__________________
I'm not a SourcePawn expert, I just play one on TV.
Want to join the SourceMod Discord server? Click here!
<- Links fixed!
404UNF is offline
ThatKidWhoGames
Senior Member
Join Date: Jun 2013
Location: IsValidClient()
Old 05-05-2018 , 11:02   Re: [INC] Client MethodMap
Reply With Quote #10

Quote:
Originally Posted by 404UNF View Post
Oh nice! This is as far as I had gotten with mine:
Spoiler
Awesome! I've learned a valuable lesson, and that is method maps can make plugin's code a lot neater.
__________________


I take custom plugin requests, add me on Steam for details.
ThatKidWhoGames 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 23:09.


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