PDA

View Full Version : [SOLVED] Command Timer



Visa
09-13-2016, 11:41 PM
I'm trying to make a custom kick command (.vkick) that vip players can use every 2 hours.



uint32 Cooldown = 10;
uint32 RemainingTime = Cooldown-time(NULL);

std::string RemainingTimeStr = secsToTimeString(uint32(RemainingTime), true);
std::string CooldownTotalStr = secsToTimeString(Cooldown, true);

if (RemainingTime < 0)
{
RemainingTime = 0;
target->GetSession()->KickPlayer();
}

if (Cooldown - RemainingTime != 0)
{
handler->PSendSysMessage("Kick Command Remaining Cooldown: %s ", RemainingTimeStr.c_str());
}


Is what I have and is confusing the hell out of me with all these time factors.

Tommy
09-14-2016, 05:29 AM
Well the way you have it now it won't work because it will allow the player to keep using it over and over again no matter what. The reason for that is because you need to save the last time they used the command in a map, single player class variable or related, instead of setting all the values inside the command function.

Visa
09-14-2016, 04:17 PM
I'm not sure how to add it to a map :x, you said related and hopefully one of those ways is adding a table in the DB.
I added the column "KickCooldown" to account_access that holds the x integer cooldown remaining.

> New VIPs that use this will get their own new row when used for the first time.
> When used, and has no cooldown, it will kick the target and add a cooldown.
> If cooldown is present, it returns false and gives remaining cooldown timer.


>>> Or at least thats how I'd like it to be set up xx




QueryResult result = LoginDatabase.PQuery("SELECT `KickCooldown` FROM `auth`.`account_access` where `id`=%u", handler->GetSession()->GetAccountId());

Field* field = result->Fetch();
uint32 timer = field[0].GetUInt32();

if (!result) // When command is executed from New VIPs.
LoginDatabase.PExecute("INSERT INTO `account_access` (`id`, `gmlevel`, `KickCooldown`) VALUES (%u, 1, 0);", target->GetSession()->GetAccountId());

if (timer == 0) // VIP has no Cooldown
{
/* Kick the Player */
target->GetSession()->KickPlayer();

/* ADD a cooldown for this member */
LoginDatabase.PExecute("UPDATE account_access SET KickCooldown = 10000 WHERE id = %u;",handler->GetSession()->GetAccountId());

/* Display Server Message (if any)*/
if (sWorld->getBoolConfig(CONFIG_SHOW_KICK_IN_WORLD))
sWorld->SendWorldText(LANG_COMMAND_KICKMESSAGE_WORLD, (handler->GetSession() ? handler->GetSession()->GetPlayerName().c_str() : "Server"), playerName.c_str(), kickReasonStr.c_str());
else
handler->PSendSysMessage(LANG_COMMAND_KICKMESSAGE, playerName.c_str());
return true;
}
else if (timer >= 1) // VIP has a cooldown remaining.
{
uint32 Cooldown = timer - time(NULL);
std::string curRespawnDelayStr = secsToTimeString(uint32(Cooldown), true);

handler->PSendSysMessage("Remaining Cooldown:%s", curRespawnDelayStr.c_str());
return false;
}



I'm think though, that the time will always sit at "10000" and never actually "countdown". I tried to reference as much information from .npc info (Respawn) as I could.

slp13at420
09-14-2016, 08:57 PM
Check out our Community Premium system --> http://emudevs.com/showthread.php/5708-CPP-EmuDevs-Community-Premium-System

I wrote it so the Premium rank can have an expiration timer.

When I added the command `.prem npc info` I included a spam timer for it. (LINE:1745)

when I added the Premium chat system I included a spam timer for it also. (LINE:877)

slp13at420
09-14-2016, 09:04 PM
for best results I would store the current time to the sql DB and a map/table when the command is executed then every time an attempt was made to use the command again I would just compare the current time to the time stored in the (map/table)+delay. if the current time is (greater than/equal too) the stored time + delay then allow the use of the command and update the time stored in the sql DB AND update the map/table.

slp13at420
09-14-2016, 09:10 PM
during login I would query the value from the sql. if it exists then store it to the map/db else create a unique entry for the char/toon (GUIDLow is good for a key) then store `0` to the map/table so you can check if stored time is 0 then do stuff since its never been used.

Upon logout you can erase.(map/table[id]) so it free's up ram.

Visa
09-14-2016, 09:32 PM
I think I might be getting closer? I understand part of this but when it comes to determining how the time is stored for the API of the game and how to correlate that with the time that I'm using it becomes more difficult.



QueryResult result = LoginDatabase.PQuery("SELECT `KickCooldown` FROM `auth`.`account_access` where `id`=%u", handler->GetSession()->GetAccountId());

Field* field = result->Fetch();
uint32 current_cooldown = field[0].GetUInt32(); // Value in the DB (Currently what they have)
uint32 full_cooldown = 10; // The full cooldown that is applied.

uint64 current_time = sWorld->GetGameTime();
uint32 account_id = handler->GetSession()->GetAccountId();

if (current_time < full_cooldown - current_cooldown)
{
// This result means he has no cooldown and is free to use ??
}

if (current_cooldown >= 1)
{
// This result means he has a cooldown and cannot use ??
}

slp13at420
09-15-2016, 12:48 AM
Ok , cooking something up.

here is an example of a command function . this does work but I would integrate it into your VIP system. So I tried to mark off blocks that should belong some where better.




// This block belongs at the start of the script.
// Frome HERE //
struct VIPTimerElements
{
uint64 test_time;
};

std::unordered_map<uint32, VIPTimerElements>VipTimers;
// To HERE //

static bool HandleVIPTestCommand(ChatHandler* handler, const char* args)
{
Player* player = handler->GetSession()->GetPlayer();
uint32 account_id = handler->GetSession()->GetAccountId();

uint64 current_test_time = sWorld->GetGameTime(); // seconds
uint64 VIPTest_Cooldown = 30; // seconds // make this just a local variable for the function or make it a global variable that can be called from any script.

// this next block I will have marked off should be in an OnLogin event of a player function
// From HERE //
QueryResult timer_result = LoginDatabase.PQuery("SELECT `KickCooldown` FROM `auth`.`account_access` where `id`=%d;", account_id);

if (timer_result)
{
Field* field = timer_result->Fetch();
uint32 test_time = field[0].GetUInt64(); // Value in the DB (Currently what they have)

VIPTimerElements& timers_data = VipTimers[account_id];
// Save the DB values to the MyData object
timers_data.test_time = test_time;
}
// To HERE //

// this block can be a stand alone function you can call from other functions `uint64 CheckTimer(ChatHandler* handler){};` or use it as part of the command function
// From HERE //
handler->PSendSysMessage("Times:%d, %d, %d.", current_test_time, VipTimers[account_id].test_time, VIPTest_Cooldown);

if ((current_test_time < (VipTimers[account_id].test_time + VIPTest_Cooldown))) {
// This result means he has a cooldown and cannot use

handler->PSendSysMessage("Timer active.");
handler->PSendSysMessage("Time left:%d, %d.", current_test_time, (VipTimers[account_id].test_time + VIPTest_Cooldown));
}
else
{
// This result means he has no cooldown and is free to use
// so do stuff

handler->PSendSysMessage("Timer expired.");

VipTimers[account_id].test_time = current_test_time;

LoginDatabase.PExecute("UPDATE `account_access` SET `KickCooldown` = '%d' WHERE `id` = '%d';", VipTimers[account_id].test_time, account_id);
}
// To HERE //
return true;
}



I wrote and tested it inside another script but renamed it to be more understandable for your VIP System.

https://github.com/BlackWolfsDen/misc/blob/master/TimerCheckerDemo.cpp

Visa
09-15-2016, 04:32 AM
I spent countless hours fiddling with the code you gave me, sometimes it partially worked, sometimes it crashed, sometimes it gave errors.

But in the end, doesn't this work too?
http://image.prntscr.com/image/bf4d9a00bc26415cb63afcb5d3ef3c23.png

The only thing is, I can't make it display the correct time variables.
Also the PExecute Variable of "LoginDatabase.PExecute("UPDATE `account_access` SET `KickCooldown` = '%d' WHERE `id` = '%d';", cooldown, account_id);"
I could be wrong with using "cooldown" instead, maybe it should be: "KickTimer[account_id].time" or "KickTimer[account_id].time - cooldown" ??

There is so many combinations with these integers it drives my mind up a solid black wall.

In-game result (helpful ?? ): http://image.prntscr.com/image/dd83e8aac89449c69050a53e7eeaedf9.png
*Screenshot was Approximately 2 minutes after using the command*

Visa
09-15-2016, 10:46 AM
http://pastebin.com/AqNd5PfR -- Full Script. --(Updated)--

Current In-game Results:

When KickCooldown column is 0: "Successful Kick :: Cooldown initated."
When KickCooldown is 3600 or any number that is not 0: "Remaining Cooldown: 0s"

Problem: The cooldown doesn't count down.. It stays the same number in the database.
Problem: After you use the command, and manually set KickCooldown column back to 0 and try to use it again, it says "Remaining Cooldown: 0s" still.

slp13at420
09-16-2016, 03:28 AM
Don't delete that pastebin. I will fix it Friday evening.

slp13at420
09-17-2016, 11:02 PM
https://github.com/BlackWolfsDen/misc/blob/master/Timed_Command_Demo.cpp

This is setup so it loads the stored time from the sql db upon player login to an unordered_map.
Then upon using the command it checks if that timer expired.
then executes the rest depending on if the timer has expired.
if the timer has expired then it will kick the target player and update the unordered_map and forwards data to a function to handle updating the sql db.
when the player logs out it will erase that players unordered_map to free up ram.

I also updated it with the `npc info` command you asked about.
`.vip npc info` will display an npc's respawn time total and remaining.

Visa
09-19-2016, 04:40 AM
Everything is working perfectly. Thank you so so much

Kaev
09-19-2016, 01:29 PM
Just my opinion about such a function: Players can and will abuse such a function. Nearly losing in arena? Kick that asshat! He's ganking me while i'm leveling? Bye! I know he's in the middle of a hard raid boss? Would be a shame if he would get kicked..
Even if you tell them that they will get banned for abusing this - They will abuse it sooner or later and fuck up someones day - which will end in one player less on your server each time. Never implement something like that, trust me.

slp13at420
09-20-2016, 12:31 AM
Just my opinion about such a function: Players can and will abuse such a function. Nearly losing in arena? Kick that asshat! He's ganking me while i'm leveling? Bye! I know he's in the middle of a hard raid boss? Would be a shame if he would get kicked..
Even if you tell them that they will get banned for abusing this - They will abuse it sooner or later and fuck up someones day - which will end in one player less on your server each time. Never implement something like that, trust me.


Granted this was just an example of how-to-add a spam timer to something like a command.
But yea Kaev does bring up a point that it doesn't currently check if player is InArena/InBg or whatever like dead/dying lol.


But I did write an example of how-to-add a multi-check to any Eluna Script:
Its for Eluna but its pretty easy to convert it over to CPP .
it uses a bitmask key to allow only specific checks when its called so it doent check for ALL condition just only the conditions you choose to use.

I would use the target player to cross-check for any conditions met, then that would protect the target. the map/area/zone can be used to check if in-leveling-area.

https://github.com/BlackWolfsDen/misc/blob/master/Condition_Checker/Eluna/Global_Criteria_Check.lua

[CPP] --> https://github.com/BlackWolfsDen/misc/blob/master/Condition_Checker/cpp/Global_Condition_Checker.cpp

Visa
09-20-2016, 10:00 AM
Yeah I've already added quite a few checks.



uint32 notSpeakTime = uint32(atoi(delayStr));

if (uint32(atoi(delayStr) > 30))
{
handler->SendSysMessage("You cannot mute players in excess of 30 minutes.");
handler->SetSentErrorMessage(true);
return true;
}

if (target->GetSession()->GetSecurity() >= SEC_MODERATOR)
{
handler->SendSysMessage("You cannot kick other VIP members or higher rank.");
handler->SetSentErrorMessage(true);
return false;
}

if (target->InBattleground() || target->InArena())
{
handler->SendSysMessage("You cannot kick players while they are in a Battleground or Arena Match.");
handler->SetSentErrorMessage(true);
return true;
}


I actually have two vip ranks, the higher rank is one that is earned from the first. Defining earned would present you with a long list. but the second vip rank is the the one with both commands. if they are will to purchase the first rank, wait some time just to earn the second rank; after all that they are willing to abuse it then its a world we live in lol and its all recorded in a custom db table. theres set rules too, must be a reason. if theres no reason in the db. obv he will be questioned asap or if it gets too much to repeating myself it'll just be immediately revoked.

slp13at420
09-22-2016, 02:57 AM
Everything is working perfectly. Thank you so so much

No Prob :D
Enjoy

I will go ahead and mark this solved.