Add client->SendToGuildHall - have instances properly cycle out IDs

This commit is contained in:
Akkadius 2020-04-10 01:43:00 -05:00
parent 1728923bbb
commit 88ff56b2f2
12 changed files with 323 additions and 174 deletions

View File

@ -159,7 +159,7 @@ public:
uint16 GetInstanceID(const char* zone, uint32 charid, int16 version); uint16 GetInstanceID(const char* zone, uint32 charid, int16 version);
uint16 GetInstanceID(uint32 zone, uint32 charid, int16 version); uint16 GetInstanceID(uint32 zone, uint32 charid, int16 version);
uint16 GetInstanceVersion(uint16 instance_id); uint16 GetInstanceVersion(uint16 instance_id);
uint32 GetTimeRemainingInstance(uint16 instance_id, bool &is_perma); uint32 GetTimeRemainingInstance(uint16 instance_id, bool is_perma = false);
uint32 VersionFromInstanceID(uint16 instance_id); uint32 VersionFromInstanceID(uint16 instance_id);
uint32 ZoneIDFromInstanceID(uint16 instance_id); uint32 ZoneIDFromInstanceID(uint16 instance_id);

View File

@ -101,14 +101,20 @@ bool Database::CheckInstanceExpired(uint16 instance_id)
int32 duration = 0; int32 duration = 0;
uint32 never_expires = 0; uint32 never_expires = 0;
std::string query = StringFormat("SELECT start_time, duration, never_expires FROM instance_list WHERE id=%u", instance_id); std::string query = StringFormat(
"SELECT start_time, duration, never_expires FROM instance_list WHERE id=%u",
instance_id
);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
if (!results.Success()) if (!results.Success()) {
return true; return true;
}
if (results.RowCount() == 0) if (results.RowCount() == 0) {
return true; return true;
}
auto row = results.begin(); auto row = results.begin();
@ -116,23 +122,28 @@ bool Database::CheckInstanceExpired(uint16 instance_id)
duration = atoi(row[1]); duration = atoi(row[1]);
never_expires = atoi(row[2]); never_expires = atoi(row[2]);
if (never_expires == 1) if (never_expires == 1) {
return false; return false;
}
timeval tv; timeval tv{};
gettimeofday(&tv, nullptr); gettimeofday(&tv, nullptr);
if ((start_time + duration) <= tv.tv_sec) return (start_time + duration) <= tv.tv_sec;
return true;
return false;
} }
bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration) bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration)
{ {
std::string query = StringFormat("INSERT INTO instance_list (id, zone, version, start_time, duration)" std::string query = StringFormat(
" values(%lu, %lu, %lu, UNIX_TIMESTAMP(), %lu)", "INSERT INTO instance_list (id, zone, version, start_time, duration)"
(unsigned long)instance_id, (unsigned long)zone_id, (unsigned long)version, (unsigned long)duration); " values (%u, %u, %u, UNIX_TIMESTAMP(), %u)",
instance_id,
zone_id,
version,
duration
);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
return results.Success(); return results.Success();
@ -140,66 +151,79 @@ bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version
bool Database::GetUnusedInstanceID(uint16 &instance_id) bool Database::GetUnusedInstanceID(uint16 &instance_id)
{ {
uint32 count = RuleI(Zone, ReservedInstances); uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances);
uint32 max = 65535; uint32 max = 32000;
std::string query = StringFormat(
"SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u",
max_reserved_instance_id,
max_reserved_instance_id
);
if (RuleB(Instances, RecycleInstanceIds)) {
query = (
SQL(
SELECT i.id + 1 AS next_available
FROM instance_list i
LEFT JOIN instance_list i2 ON i2.id = i.id + 1
WHERE i2.id IS NULL
ORDER BY i.id
LIMIT 0, 1;
)
);
}
std::string query = StringFormat("SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", count, count);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
if (!results.Success()) if (!results.Success()) {
{
instance_id = 0; instance_id = 0;
return false; return false;
} }
if (results.RowCount() == 0) if (results.RowCount() == 0) {
{ instance_id = max_reserved_instance_id;
instance_id = 0; return true;
return false;
} }
auto row = results.begin(); auto row = results.begin();
if (atoi(row[0]) <= max) if (atoi(row[0]) <= max) {
{
instance_id = atoi(row[0]); instance_id = atoi(row[0]);
return true; return true;
} }
query = StringFormat("SELECT id FROM instance_list where id > %u ORDER BY id", count); query = StringFormat("SELECT id FROM instance_list where id > %u ORDER BY id", max_reserved_instance_id);
results = QueryDatabase(query); results = QueryDatabase(query);
if (!results.Success()) if (!results.Success()) {
{
instance_id = 0; instance_id = 0;
return false; return false;
} }
if (results.RowCount() == 0) if (results.RowCount() == 0) {
{
instance_id = 0; instance_id = 0;
return false; return false;
} }
count++; max_reserved_instance_id++;
for (auto row = results.begin(); row != results.end(); ++row) for (auto row = results.begin(); row != results.end(); ++row) {
{ if (max_reserved_instance_id < atoi(row[0])) {
if (count < atoi(row[0])) instance_id = max_reserved_instance_id;
{
instance_id = count;
return true; return true;
} }
if (count > max) if (max_reserved_instance_id > max) {
{
instance_id = 0; instance_id = 0;
return false; return false;
} }
count++; max_reserved_instance_id++;
} }
instance_id = count; instance_id = max_reserved_instance_id;
return true; return true;
} }
@ -357,7 +381,7 @@ uint16 Database::GetInstanceVersion(uint16 instance_id) {
return atoi(row[0]); return atoi(row[0]);
} }
uint32 Database::GetTimeRemainingInstance(uint16 instance_id, bool &is_perma) uint32 Database::GetTimeRemainingInstance(uint16 instance_id, bool is_perma)
{ {
uint32 start_time = 0; uint32 start_time = 0;
uint32 duration = 0; uint32 duration = 0;
@ -548,17 +572,36 @@ void Database::GetCharactersInInstance(uint16 instance_id, std::list<uint32> &ch
void Database::PurgeExpiredInstances() void Database::PurgeExpiredInstances()
{ {
std::string query("SELECT id FROM instance_list where (start_time+duration) <= UNIX_TIMESTAMP() and never_expires = 0");
/**
* Delay purging by a day so that we can continue using adjacent free instance id's
* from the table without risking the chance we immediately re-allocate a zone that freshly expired but
* has not been fully de-allocated
*/
std::string query =
SQL(
SELECT
id
FROM
instance_list
where
(start_time + duration) <= (UNIX_TIMESTAMP() + 86400)
and never_expires = 0
);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
if (!results.Success()) if (!results.Success()) {
return; return;
}
if (results.RowCount() == 0) if (results.RowCount() == 0) {
return; return;
}
for (auto row = results.begin(); row != results.end(); ++row) for (auto row = results.begin(); row != results.end(); ++row) {
DeleteInstance(atoi(row[0])); DeleteInstance(atoi(row[0]));
}
} }
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)

View File

@ -270,7 +270,6 @@ RULE_INT(Zone, PEQZoneDebuff1, 4454, "First debuff casted by #peqzone Default is
RULE_INT(Zone, PEQZoneDebuff2, 2209, "Second debuff casted by #peqzone Default is Tendrils of Apathy") RULE_INT(Zone, PEQZoneDebuff2, 2209, "Second debuff casted by #peqzone Default is Tendrils of Apathy")
RULE_BOOL(Zone, UsePEQZoneDebuffs, true, "Will determine if #peqzone will debuff players or not when used") RULE_BOOL(Zone, UsePEQZoneDebuffs, true, "Will determine if #peqzone will debuff players or not when used")
RULE_REAL(Zone, HotZoneBonus, 0.75, "") RULE_REAL(Zone, HotZoneBonus, 0.75, "")
RULE_INT(Zone, ReservedInstances, 30, "Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running")
RULE_INT(Zone, EbonCrystalItemID, 40902, "") RULE_INT(Zone, EbonCrystalItemID, 40902, "")
RULE_INT(Zone, RadiantCrystalItemID, 40903, "") RULE_INT(Zone, RadiantCrystalItemID, 40903, "")
RULE_BOOL(Zone, LevelBasedEXPMods, false, "Allows you to use the level_exp_mods table in consideration to your players EXP hits") RULE_BOOL(Zone, LevelBasedEXPMods, false, "Allows you to use the level_exp_mods table in consideration to your players EXP hits")
@ -775,6 +774,12 @@ RULE_BOOL(HotReload, QuestsRepopWhenPlayersNotInCombat, true, "When a hot reload
RULE_BOOL(HotReload, QuestsResetTimersWithReload, true, "When a hot reload is triggered, quest timers will be reset") RULE_BOOL(HotReload, QuestsResetTimersWithReload, true, "When a hot reload is triggered, quest timers will be reset")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Instances)
RULE_INT(Instances, ReservedInstances, 30, "Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running")
RULE_BOOL(Instances, RecycleInstanceIds, true, "Will recycle free instance ids instead of gradually running out at 32k")
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
RULE_CATEGORY_END()
#undef RULE_CATEGORY #undef RULE_CATEGORY
#undef RULE_INT #undef RULE_INT
#undef RULE_REAL #undef RULE_REAL

View File

@ -129,13 +129,17 @@ void Timer::SetTimer(uint32 set_timer_time) {
} }
} }
uint32 Timer::GetRemainingTime() const { uint32 Timer::GetRemainingTime() const
{
if (enabled) { if (enabled) {
if (current_time - start_time > timer_time) if (current_time - start_time > timer_time) {
return 0; return 0;
else }
else {
return (start_time + timer_time) - current_time; return (start_time + timer_time) - current_time;
} else { }
}
else {
return 0xFFFFFFFF; return 0xFFFFFFFF;
} }
} }

View File

@ -9292,3 +9292,41 @@ void Client::SetBotOption(BotOwnerOption boo, bool flag) {
} }
#endif #endif
void Client::SendToGuildHall()
{
std::string zone_short_name = "guildhall";
uint32 zone_id = database.GetZoneID(zone_short_name.c_str());
if (zone_id == 0) {
return;
}
uint32 expiration_time = (RuleI(Instances, GuildHallExpirationDays) * 86400);
uint16 instance_id = 0;
std::string guild_hall_instance_key = fmt::format("guild-hall-instance-{}", GuildID());
std::string instance_data = DataBucket::GetData(guild_hall_instance_key);
if (!instance_data.empty() && std::stoi(instance_data) > 0) {
instance_id = std::stoi(instance_data);
}
if (instance_id <= 0) {
if (!database.GetUnusedInstanceID(instance_id)) {
Message(Chat::Red, "Server was unable to find a free instance id.");
return;
}
if (!database.CreateInstance(instance_id, zone_id, 0, expiration_time)) {
Message(Chat::Red, "Server was unable to create a new instance.");
return;
}
DataBucket::SetData(
guild_hall_instance_key,
std::to_string(instance_id),
std::to_string(expiration_time)
);
}
AssignToInstance(instance_id);
MovePC(345, instance_id, -1.00, -1.00, 3.34, 0, 1);
}

View File

@ -633,6 +633,7 @@ public:
void MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); void MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
void MovePC(float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); void MovePC(float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
void MovePC(uint32 zoneID, uint32 instanceID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); void MovePC(uint32 zoneID, uint32 instanceID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
void SendToGuildHall();
void AssignToInstance(uint16 instance_id); void AssignToInstance(uint16 instance_id);
void RemoveFromInstance(uint16 instance_id); void RemoveFromInstance(uint16 instance_id);
void WhoAll(); void WhoAll();

View File

@ -818,35 +818,46 @@ void Client::CompleteConnect()
database.QueryDatabase( database.QueryDatabase(
StringFormat( StringFormat(
"UPDATE `character_data` SET `last_login` = UNIX_TIMESTAMP() WHERE id = %u", "UPDATE `character_data` SET `last_login` = UNIX_TIMESTAMP() WHERE id = %u",
this->CharacterID() CharacterID()
) )
); );
} }
if (zone) { if (zone && zone->GetInstanceID() > 0) {
if (zone->GetInstanceTimer()) {
uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); uint32 remaining_time_seconds = zone->GetInstanceTimeRemaining();
uint32 day = (ttime / 86400000); uint32 day = (remaining_time_seconds / 86400);
uint32 hour = (ttime / 3600000) % 24; uint32 hour = (remaining_time_seconds / 3600) % 24;
uint32 minute = (ttime / 60000) % 60; uint32 minute = (remaining_time_seconds / 60) % 60;
uint32 second = (ttime / 1000) % 60; uint32 second = (remaining_time_seconds / 1) % 60;
LogInfo("Remaining time seconds [{}]", remaining_time_seconds);
if (day) { if (day) {
Message(Chat::Yellow, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", Message(
zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); Chat::Yellow, "%s (%u) will expire in %u days, %u hours, %u minutes, and %u seconds.",
zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second
);
} }
else if (hour) { else if (hour) {
Message(Chat::Yellow, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", Message(
zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); Chat::Yellow, "%s (%u) will expire in %u hours, %u minutes, and %u seconds.",
zone->GetLongName(), zone->GetInstanceID(), hour, minute, second
);
} }
else if (minute) { else if (minute) {
Message(Chat::Yellow, "%s(%u) will expire in %u minutes, and %u seconds.", Message(
zone->GetLongName(), zone->GetInstanceID(), minute, second); Chat::Yellow, "%s (%u) will expire in %u minutes, and %u seconds.",
zone->GetLongName(), zone->GetInstanceID(), minute, second
);
} }
else { else {
Message(Chat::Yellow, "%s(%u) will expire in in %u seconds.", Message(
zone->GetLongName(), zone->GetInstanceID(), second); Chat::Yellow, "%s (%u) will expire in in %u seconds.",
} zone->GetLongName(), zone->GetInstanceID(), second
);
} }
} }
SendRewards(); SendRewards();

View File

@ -90,6 +90,11 @@ void Lua_Client::SetPVP(bool v) {
self->SetPVP(v); self->SetPVP(v);
} }
void Lua_Client::SendToGuildHall() {
Lua_Safe_Call_Void();
self->SendToGuildHall();
}
bool Lua_Client::GetPVP() { bool Lua_Client::GetPVP() {
Lua_Safe_Call_Bool(); Lua_Safe_Call_Bool();
return self->GetPVP(); return self->GetPVP();
@ -1584,6 +1589,7 @@ luabind::scope lua_register_client() {
.def("Disconnect", (void(Lua_Client::*)(void))&Lua_Client::Disconnect) .def("Disconnect", (void(Lua_Client::*)(void))&Lua_Client::Disconnect)
.def("IsLD", (bool(Lua_Client::*)(void))&Lua_Client::IsLD) .def("IsLD", (bool(Lua_Client::*)(void))&Lua_Client::IsLD)
.def("WorldKick", (void(Lua_Client::*)(void))&Lua_Client::WorldKick) .def("WorldKick", (void(Lua_Client::*)(void))&Lua_Client::WorldKick)
.def("SendToGuildHall", (void(Lua_Client::*)(void))&Lua_Client::SendToGuildHall)
.def("GetAnon", (bool(Lua_Client::*)(void))&Lua_Client::GetAnon) .def("GetAnon", (bool(Lua_Client::*)(void))&Lua_Client::GetAnon)
.def("Duck", (void(Lua_Client::*)(void))&Lua_Client::Duck) .def("Duck", (void(Lua_Client::*)(void))&Lua_Client::Duck)
.def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand) .def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand)

View File

@ -39,6 +39,7 @@ public:
void Disconnect(); void Disconnect();
bool IsLD(); bool IsLD();
void WorldKick(); void WorldKick();
void SendToGuildHall();
bool GetAnon(); bool GetAnon();
void Duck(); void Duck();
void Stand(); void Stand();

View File

@ -245,6 +245,27 @@ XS(XS_Client_WorldKick) {
XSRETURN_EMPTY; XSRETURN_EMPTY;
} }
XS(XS_Client_SendToGuildHall); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_SendToGuildHall) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Client::SendToGuildHall(THIS)");
{
Client *THIS;
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
THIS = INT2PTR(Client *, tmp);
} else
Perl_croak(aTHX_ "THIS is not of type Client");
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
THIS->SendToGuildHall();
}
XSRETURN_EMPTY;
}
XS(XS_Client_GetAnon); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_GetAnon); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_GetAnon) { XS(XS_Client_GetAnon) {
dXSARGS; dXSARGS;
@ -6564,6 +6585,7 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "SendSound"), XS_Client_SendSound, file, "$"); newXSproto(strcpy(buf, "SendSound"), XS_Client_SendSound, file, "$");
newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$"); newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$");
newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$");
newXSproto(strcpy(buf, "SendToGuildHall"), XS_Client_SendToGuildHall, file, "$");
newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$"); newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$");
newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$"); newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$");
newXSproto(strcpy(buf, "SetAAPoints"), XS_Client_SetAAPoints, file, "$$"); newXSproto(strcpy(buf, "SetAAPoints"), XS_Client_SetAAPoints, file, "$$");

View File

@ -1018,6 +1018,10 @@ bool Zone::Init(bool iStaticZone) {
petition_list.ClearPetitions(); petition_list.ClearPetitions();
petition_list.ReadDatabase(); petition_list.ReadDatabase();
if (zone->GetInstanceID() > 0) {
zone->SetInstanceTimeRemaining(database.GetTimeRemainingInstance(zone->GetInstanceID()));
}
LogInfo("Loading timezone data"); LogInfo("Loading timezone data");
zone->zone_time.setEQTimeZone(database.GetZoneTZ(zoneid, GetInstanceVersion())); zone->zone_time.setEQTimeZone(database.GetZoneTZ(zoneid, GetInstanceVersion()));
@ -2455,3 +2459,13 @@ void Zone::SetQuestHotReloadQueued(bool in_quest_hot_reload_queued)
{ {
quest_hot_reload_queued = in_quest_hot_reload_queued; quest_hot_reload_queued = in_quest_hot_reload_queued;
} }
uint32 Zone::GetInstanceTimeRemaining() const
{
return instance_time_remaining;
}
void Zone::SetInstanceTimeRemaining(uint32 instance_time_remaining)
{
Zone::instance_time_remaining = instance_time_remaining;
}

View File

@ -278,6 +278,9 @@ public:
ZonePoint *GetClosestZonePoint(const glm::vec3 &location, uint32 to, Client *client, float max_distance = 40000.0f); ZonePoint *GetClosestZonePoint(const glm::vec3 &location, uint32 to, Client *client, float max_distance = 40000.0f);
ZonePoint *GetClosestZonePointWithoutZone(float x, float y, float z, Client *client, float max_distance = 40000.0f); ZonePoint *GetClosestZonePointWithoutZone(float x, float y, float z, Client *client, float max_distance = 40000.0f);
uint32 GetInstanceTimeRemaining() const;
void SetInstanceTimeRemaining(uint32 instance_time_remaining);
/** /**
* GMSay Callback for LogSys * GMSay Callback for LogSys
* *
@ -361,6 +364,7 @@ private:
uint8 zone_type; uint8 zone_type;
uint16 instanceversion; uint16 instanceversion;
uint32 instanceid; uint32 instanceid;
uint32 instance_time_remaining;
uint32 pgraveyard_id, pgraveyard_zoneid; uint32 pgraveyard_id, pgraveyard_zoneid;
uint32 pMaxClients; uint32 pMaxClients;
uint32 zoneid; uint32 zoneid;