mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
Add DynamicZone class for expedition instancing
Add DynamicZone sql table schema Add DynamicZones logging category Modify CreateExpedition to take DynamicZone and ExpeditionRequest objects Implement DynamicZone compass, safereturn, and zone-in coordinates. Implement live-like DynamicZone instance kick timer for removed members Implement updating multiple client compasses (supports existing quest compass) fix: Send client compass update after entering zones to clear existing compass Implement Client::MovePCDynamicZone to invoke DynamicZoneSwitchListWnd when entering a zone where client has multiple dynamic zones assigned Implement OP_DzChooseZoneReply handling Add Lua api methods for expedition's associated dynamic zone Add #dz list gm command to list current DynamicZone instances from database
This commit is contained in:
parent
f74605d339
commit
8eef2ae089
@ -119,6 +119,7 @@ namespace Logs {
|
||||
ZonePoints,
|
||||
Loot,
|
||||
Expeditions,
|
||||
DynamicZones,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@ -197,6 +198,7 @@ namespace Logs {
|
||||
"ZonePoints",
|
||||
"Loot",
|
||||
"Expeditions",
|
||||
"DynamicZones",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -616,6 +616,16 @@
|
||||
OutF(LogSys, Logs::Detail, Logs::Expeditions, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogDynamicZones(message, ...) do {\
|
||||
if (LogSys.log_settings[Logs::DynamicZones].is_category_enabled == 1)\
|
||||
OutF(LogSys, Logs::General, Logs::DynamicZones, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogDynamicZonesDetail(message, ...) do {\
|
||||
if (LogSys.log_settings[Logs::DynamicZones].is_category_enabled == 1)\
|
||||
OutF(LogSys, Logs::Detail, Logs::DynamicZones, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
@ -976,6 +986,12 @@
|
||||
#define LogExpeditionsDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogDynamicZones(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogDynamicZonesDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
|
||||
@ -788,6 +788,12 @@ RULE_CATEGORY_END()
|
||||
RULE_CATEGORY(Expedition)
|
||||
RULE_INT(Expedition, MinStatusToBypassPlayerCountRequirements, 80, "Minimum GM status to bypass minimum player requirements for Expedition creation")
|
||||
RULE_BOOL(Expedition, UseDatabaseToVerifyLeaderCommands, false, "Use database instead of zone cache to verify Expedition leader for commands")
|
||||
RULE_BOOL(Expedition, EmptyDzShutdownEnabled, true, "Enable early instance shutdown after last member of expedition removed")
|
||||
RULE_INT(Expedition, EmptyDzShutdownDelaySeconds, 900, "Seconds to set dynamic zone instance expiration if early shutdown enabled")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(DynamicZone)
|
||||
RULE_INT(DynamicZone, ClientRemovalDelayMS, 60000, "Delay (ms) until a client is teleported out of dynamic zone after being removed as member")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
#undef RULE_CATEGORY
|
||||
|
||||
@ -151,6 +151,11 @@
|
||||
#define ServerOP_ExpeditionGetOnlineMembers 0x0407
|
||||
#define ServerOP_ExpeditionDzAddPlayer 0x0408
|
||||
#define ServerOP_ExpeditionDzMakeLeader 0x0409
|
||||
#define ServerOP_ExpeditionDzCompass 0x040a
|
||||
#define ServerOP_ExpeditionDzSafeReturn 0x040b
|
||||
#define ServerOP_ExpeditionDzZoneIn 0x040c
|
||||
|
||||
#define ServerOP_DzCharacterChange 0x0450
|
||||
|
||||
#define ServerOP_LSInfo 0x1000
|
||||
#define ServerOP_LSStatus 0x1001
|
||||
@ -2053,6 +2058,25 @@ struct ServerDzCommand_Struct {
|
||||
char remove_name[64]; // used for swap command
|
||||
};
|
||||
|
||||
struct ServerDzLocation_Struct {
|
||||
uint32 owner_id; // system associated with the dz (expedition, shared task, etc)
|
||||
uint16 dz_zone_id;
|
||||
uint16 dz_instance_id;
|
||||
uint32 sender_zone_id;
|
||||
uint16 sender_instance_id;
|
||||
uint32 zone_id; // compass or safereturn zone id
|
||||
float y;
|
||||
float x;
|
||||
float z;
|
||||
float heading;
|
||||
};
|
||||
|
||||
struct ServerDzCharacter_Struct {
|
||||
uint16 instance_id;
|
||||
uint8 remove; // 0: added 1: removed
|
||||
uint32 character_id;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif
|
||||
|
||||
25
utils/sql/git/required/wip_dynamiczones.sql
Normal file
25
utils/sql/git/required/wip_dynamiczones.sql
Normal file
@ -0,0 +1,25 @@
|
||||
CREATE TABLE `dynamic_zones` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`instance_id` INT(10) NOT NULL DEFAULT 0,
|
||||
`type` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`compass_zone_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`compass_x` FLOAT NOT NULL DEFAULT 0,
|
||||
`compass_y` FLOAT NOT NULL DEFAULT 0,
|
||||
`compass_z` FLOAT NOT NULL DEFAULT 0,
|
||||
`safe_return_zone_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`safe_return_x` FLOAT NOT NULL DEFAULT 0,
|
||||
`safe_return_y` FLOAT NOT NULL DEFAULT 0,
|
||||
`safe_return_z` FLOAT NOT NULL DEFAULT 0,
|
||||
`safe_return_heading` FLOAT NOT NULL DEFAULT 0,
|
||||
`zone_in_x` FLOAT NOT NULL DEFAULT 0,
|
||||
`zone_in_y` FLOAT NOT NULL DEFAULT 0,
|
||||
`zone_in_z` FLOAT NOT NULL DEFAULT 0,
|
||||
`zone_in_heading` FLOAT NOT NULL DEFAULT 0,
|
||||
`has_zone_in` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `instance_id` (`instance_id`),
|
||||
CONSTRAINT `FK_dynamic_zones_instance_list` FOREIGN KEY (`instance_id`) REFERENCES `instance_list` (`id`) ON DELETE CASCADE
|
||||
)
|
||||
COLLATE='utf8mb4_general_ci'
|
||||
ENGINE=InnoDB
|
||||
;
|
||||
@ -30,23 +30,27 @@
|
||||
extern ClientList client_list;
|
||||
extern ZSList zoneserver_list;
|
||||
|
||||
void Expedition::PurgeEmptyExpeditions()
|
||||
void Expedition::PurgeExpiredExpeditions()
|
||||
{
|
||||
std::string query = SQL(
|
||||
DELETE expedition FROM expedition_details expedition
|
||||
LEFT JOIN instance_list ON expedition.instance_id = instance_list.id
|
||||
LEFT JOIN (
|
||||
SELECT expedition_id, COUNT(IF(is_current_member = TRUE, 1, NULL)) member_count
|
||||
FROM expedition_members
|
||||
GROUP BY expedition_id
|
||||
) AS expedition_members
|
||||
ON expedition_members.expedition_id = expedition.id
|
||||
WHERE expedition_members.expedition_id IS NULL OR expedition_members.member_count <= 0
|
||||
WHERE
|
||||
expedition.instance_id IS NULL
|
||||
OR expedition_members.member_count = 0
|
||||
OR (instance_list.start_time + instance_list.duration) <= UNIX_TIMESTAMP();
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
{
|
||||
LogExpeditions("Failed to purge empty expeditions");
|
||||
LogExpeditions("Failed to purge expired and empty expeditions");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ class ServerPacket;
|
||||
|
||||
namespace Expedition
|
||||
{
|
||||
void PurgeEmptyExpeditions();
|
||||
void PurgeExpiredExpeditions();
|
||||
void PurgeExpiredCharacterLockouts();
|
||||
void AddPlayer(ServerPacket* pack);
|
||||
void MakeLeader(ServerPacket* pack);
|
||||
|
||||
@ -431,7 +431,7 @@ int main(int argc, char** argv) {
|
||||
PurgeInstanceTimer.Start(450000);
|
||||
|
||||
LogInfo("Purging expired expeditions");
|
||||
Expedition::PurgeEmptyExpeditions(); //database.PurgeExpiredExpeditions();
|
||||
Expedition::PurgeExpiredExpeditions();
|
||||
Expedition::PurgeExpiredCharacterLockouts();
|
||||
|
||||
LogInfo("Loading char create info");
|
||||
@ -604,7 +604,7 @@ int main(int argc, char** argv) {
|
||||
if (PurgeInstanceTimer.Check()) {
|
||||
database.PurgeExpiredInstances();
|
||||
database.PurgeAllDeletedDataBuckets();
|
||||
Expedition::PurgeEmptyExpeditions();
|
||||
Expedition::PurgeExpiredExpeditions();
|
||||
Expedition::PurgeExpiredCharacterLockouts();
|
||||
}
|
||||
|
||||
|
||||
@ -1375,6 +1375,9 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
case ServerOP_ExpeditionMemberChange:
|
||||
case ServerOP_ExpeditionMemberSwap:
|
||||
case ServerOP_ExpeditionMemberStatus:
|
||||
case ServerOP_ExpeditionDzCompass:
|
||||
case ServerOP_ExpeditionDzSafeReturn:
|
||||
case ServerOP_ExpeditionDzZoneIn:
|
||||
{
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
@ -1394,6 +1397,16 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
Expedition::MakeLeader(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_DzCharacterChange:
|
||||
{
|
||||
auto buf = reinterpret_cast<ServerDzCharacter_Struct*>(pack->pBuffer);
|
||||
ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(buf->instance_id);
|
||||
if (instance_zs)
|
||||
{
|
||||
instance_zs->SendPacket(pack);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
|
||||
|
||||
@ -22,6 +22,7 @@ SET(zone_sources
|
||||
corpse.cpp
|
||||
data_bucket.cpp
|
||||
doors.cpp
|
||||
dynamiczone.cpp
|
||||
effects.cpp
|
||||
embparser.cpp
|
||||
embparser_api.cpp
|
||||
@ -170,6 +171,7 @@ SET(zone_headers
|
||||
corpse.h
|
||||
data_bucket.h
|
||||
doors.h
|
||||
dynamiczone.h
|
||||
embparser.h
|
||||
embperl.h
|
||||
embxs.h
|
||||
|
||||
263
zone/client.cpp
263
zone/client.cpp
@ -43,6 +43,7 @@ extern volatile bool RunLoops;
|
||||
#include "expedition.h"
|
||||
#include "expedition_database.h"
|
||||
#include "expedition_lockout_timer.h"
|
||||
#include "expedition_request.h"
|
||||
#include "position.h"
|
||||
#include "worldserver.h"
|
||||
#include "zonedb.h"
|
||||
@ -267,6 +268,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
InitializeMercInfo();
|
||||
SetMerc(0);
|
||||
if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true, false);
|
||||
dynamiczone_removal_timer.Disable();
|
||||
|
||||
//for good measure:
|
||||
memset(&m_pp, 0, sizeof(m_pp));
|
||||
@ -6155,24 +6157,19 @@ void Client::CheckEmoteHail(Mob *target, const char* message)
|
||||
|
||||
void Client::MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 count)
|
||||
{
|
||||
uint32 entry_size = sizeof(DynamicZoneCompassEntry_Struct) * count;
|
||||
auto outapp = new EQApplicationPacket(OP_DzCompass, sizeof(DynamicZoneCompass_Struct) + entry_size);
|
||||
auto outbuf = reinterpret_cast<DynamicZoneCompass_Struct*>(outapp->pBuffer);
|
||||
|
||||
outbuf->client_id = 0;
|
||||
outbuf->count = count;
|
||||
|
||||
if (count) {
|
||||
outbuf->entries[0].dz_zone_id = 0;
|
||||
outbuf->entries[0].dz_instance_id = 0;
|
||||
outbuf->entries[0].dz_type = 0;
|
||||
outbuf->entries[0].x = in_x;
|
||||
outbuf->entries[0].y = in_y;
|
||||
outbuf->entries[0].z = in_z;
|
||||
if (count == 0)
|
||||
{
|
||||
m_quest_compass.zone_id = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_quest_compass.zone_id = zone ? zone->GetZoneID() : 0;
|
||||
m_quest_compass.x = in_x;
|
||||
m_quest_compass.y = in_y;
|
||||
m_quest_compass.z = in_z;
|
||||
}
|
||||
|
||||
FastQueuePacket(&outapp);
|
||||
safe_delete(outapp);
|
||||
SendDzCompassUpdate();
|
||||
}
|
||||
|
||||
void Client::SendZonePoints()
|
||||
@ -9564,6 +9561,8 @@ void Client::UpdateExpeditionInfoAndLockouts()
|
||||
{
|
||||
// this is processed by client after entering a zone
|
||||
// todo: live re-invites if client zoned with a pending invite window open
|
||||
SendDzCompassUpdate();
|
||||
|
||||
auto expedition = GetExpedition();
|
||||
if (expedition)
|
||||
{
|
||||
@ -9571,7 +9570,7 @@ void Client::UpdateExpeditionInfoAndLockouts()
|
||||
|
||||
// live only adds lockouts obtained during the active expedition to new
|
||||
// members once they zone into the expedition's dynamic zone instance
|
||||
if (zone && /*zone->GetInstanceID() && zone->GetInstanceID()*/zone->GetZoneID() == expedition->GetInstanceID())
|
||||
if (expedition->GetDynamicZone().IsCurrentZoneDzInstance())
|
||||
{
|
||||
ExpeditionDatabase::AssignPendingLockouts(CharacterID(), expedition->GetName());
|
||||
expedition->SetMemberStatus(this, ExpeditionMemberStatus::InDynamicZone);
|
||||
@ -9581,13 +9580,17 @@ void Client::UpdateExpeditionInfoAndLockouts()
|
||||
expedition->SetMemberStatus(this, ExpeditionMemberStatus::Online);
|
||||
}
|
||||
}
|
||||
|
||||
Expedition::LoadAllClientLockouts(this);
|
||||
}
|
||||
|
||||
Expedition* Client::CreateExpedition(
|
||||
std::string name, uint32 min_players, uint32 max_players, bool has_replay_timer)
|
||||
std::string zone_name, uint32 version, uint32 duration,
|
||||
std::string expedition_name, uint32 min_players, uint32 max_players, bool has_replay_timer)
|
||||
{
|
||||
return Expedition::TryCreate(this, name, min_players, max_players, has_replay_timer);
|
||||
DynamicZone dz_instance{ zone_name, version, duration, DynamicZoneType::Expedition };
|
||||
ExpeditionRequest request{ expedition_name, min_players, max_players, has_replay_timer };
|
||||
return Expedition::TryCreate(this, dz_instance, request);
|
||||
}
|
||||
|
||||
Expedition* Client::GetExpedition() const
|
||||
@ -9616,30 +9619,6 @@ std::vector<ExpeditionLockoutTimer> Client::GetExpeditionLockouts(const std::str
|
||||
return lockouts;
|
||||
}
|
||||
|
||||
void Client::DzListTimers()
|
||||
{
|
||||
// only lists player's current replay timer lockouts, not all event lockouts
|
||||
bool found = false;
|
||||
for (const auto& lockout : m_expedition_lockouts)
|
||||
{
|
||||
if (lockout.IsReplayTimer())
|
||||
{
|
||||
found = true;
|
||||
auto time_remaining = lockout.GetDaysHoursMinutesRemaining();
|
||||
MessageString(
|
||||
Chat::Yellow, DZLIST_REPLAY_TIMER,
|
||||
time_remaining.days.c_str(), time_remaining.hours.c_str(), time_remaining.mins.c_str(),
|
||||
lockout.GetExpeditionName().c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
MessageString(Chat::Yellow, EXPEDITION_NO_TIMERS);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::AddExpeditionLockout(const ExpeditionLockoutTimer& lockout, bool update_db)
|
||||
{
|
||||
// todo: support for account based lockouts like live AoC expeditions
|
||||
@ -9744,3 +9723,201 @@ void Client::SendExpeditionLockoutTimers()
|
||||
}
|
||||
QueuePacket(outapp.get());
|
||||
}
|
||||
|
||||
void Client::DzListTimers()
|
||||
{
|
||||
// only lists player's current replay timer lockouts, not all event lockouts
|
||||
bool found = false;
|
||||
for (const auto& lockout : m_expedition_lockouts)
|
||||
{
|
||||
if (lockout.IsReplayTimer())
|
||||
{
|
||||
found = true;
|
||||
auto time_remaining = lockout.GetDaysHoursMinutesRemaining();
|
||||
MessageString(
|
||||
Chat::Yellow, DZLIST_REPLAY_TIMER,
|
||||
time_remaining.days.c_str(), time_remaining.hours.c_str(), time_remaining.mins.c_str(),
|
||||
lockout.GetExpeditionName().c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
MessageString(Chat::Yellow, EXPEDITION_NO_TIMERS);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetDzRemovalTimer(bool enable_timer)
|
||||
{
|
||||
uint32_t timer_ms = RuleI(DynamicZone, ClientRemovalDelayMS);
|
||||
|
||||
LogDynamicZones(
|
||||
"Character [{}] instance [{}] removal timer enabled: [{}] delay (ms): [{}]",
|
||||
CharacterID(), zone ? zone->GetInstanceID() : 0, enable_timer, timer_ms
|
||||
);
|
||||
|
||||
if (enable_timer)
|
||||
{
|
||||
dynamiczone_removal_timer.Start(timer_ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
dynamiczone_removal_timer.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SendDzCompassUpdate()
|
||||
{
|
||||
// a client may be associated with multiple dynamic zones with compasses
|
||||
// in the same zone. any systems that use dynamic zones need checked here
|
||||
std::vector<DynamicZoneCompassEntry_Struct> compass_entries;
|
||||
|
||||
Expedition* expedition = GetExpedition();
|
||||
if (expedition)
|
||||
{
|
||||
auto compass = expedition->GetDynamicZone().GetCompassLocation();
|
||||
if (zone && zone->GetZoneID() == compass.zone_id)
|
||||
{
|
||||
DynamicZoneCompassEntry_Struct entry;
|
||||
entry.dz_zone_id = static_cast<uint16_t>(expedition->GetDynamicZone().GetZoneID());
|
||||
entry.dz_instance_id = static_cast<uint16_t>(expedition->GetDynamicZone().GetInstanceID());
|
||||
entry.dz_type = static_cast<uint8_t>(expedition->GetDynamicZone().GetType());
|
||||
entry.x = compass.x;
|
||||
entry.y = compass.y;
|
||||
entry.z = compass.z;
|
||||
|
||||
compass_entries.emplace_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// todo: shared tasks, missions, and quests with an associated dz
|
||||
|
||||
// compass set via MarkSingleCompassLocation()
|
||||
if (zone && zone->GetZoneID() == m_quest_compass.zone_id)
|
||||
{
|
||||
DynamicZoneCompassEntry_Struct entry;
|
||||
entry.dz_zone_id = 0;
|
||||
entry.dz_instance_id = 0;
|
||||
entry.dz_type = 0;
|
||||
entry.x = m_quest_compass.x;
|
||||
entry.y = m_quest_compass.y;
|
||||
entry.z = m_quest_compass.z;
|
||||
|
||||
compass_entries.emplace_back(entry);
|
||||
}
|
||||
|
||||
uint32 count = static_cast<uint32_t>(compass_entries.size());
|
||||
uint32 entries_size = sizeof(DynamicZoneCompassEntry_Struct) * count;
|
||||
uint32 outsize = sizeof(DynamicZoneCompass_Struct) + entries_size;
|
||||
auto outapp = std::unique_ptr<EQApplicationPacket>(new EQApplicationPacket(OP_DzCompass, outsize));
|
||||
auto outbuf = reinterpret_cast<DynamicZoneCompass_Struct*>(outapp->pBuffer);
|
||||
outbuf->client_id = 0;
|
||||
outbuf->count = count;
|
||||
memcpy(outbuf->entries, compass_entries.data(), entries_size);
|
||||
|
||||
QueuePacket(outapp.get());
|
||||
}
|
||||
|
||||
void Client::GoToDzSafeReturnOrBind(const DynamicZoneLocation& safereturn)
|
||||
{
|
||||
LogDynamicZonesDetail(
|
||||
"Sending character [{}] in zone [{}]:[{}] to safereturn [{}] at ([{}], [{}], [{}], [{}]) or bind",
|
||||
CharacterID(),
|
||||
zone ? zone->GetZoneID() : 0,
|
||||
zone ? zone->GetInstanceID() : 0,
|
||||
safereturn.zone_id,
|
||||
safereturn.x,
|
||||
safereturn.y,
|
||||
safereturn.z,
|
||||
safereturn.heading
|
||||
);
|
||||
|
||||
if (safereturn.zone_id == 0)
|
||||
{
|
||||
GoToBind();
|
||||
}
|
||||
else
|
||||
{
|
||||
MovePC(safereturn.zone_id, 0, safereturn.x, safereturn.y, safereturn.z, safereturn.heading);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::MovePCDynamicZone(uint32 zone_id)
|
||||
{
|
||||
if (zone_id == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// check client systems for any associated dynamic zones to the requested zone id
|
||||
std::vector<DynamicZoneChooseZoneEntry_Struct> client_dzs;
|
||||
DynamicZone single_dz;
|
||||
|
||||
Expedition* expedition = GetExpedition();
|
||||
if (expedition && expedition->GetDynamicZone().GetZoneID() == zone_id)
|
||||
{
|
||||
single_dz = expedition->GetDynamicZone();
|
||||
|
||||
DynamicZoneChooseZoneEntry_Struct dz;
|
||||
dz.dz_zone_id = expedition->GetDynamicZone().GetZoneID();
|
||||
dz.dz_instance_id = expedition->GetDynamicZone().GetInstanceID();
|
||||
dz.dz_type = static_cast<uint8_t>(expedition->GetDynamicZone().GetType());
|
||||
//dz.unknown_id2 = expedition->GetDynamicZone().GetRealID();
|
||||
strn0cpy(dz.description, expedition->GetName().c_str(), sizeof(dz.description));
|
||||
strn0cpy(dz.leader_name, expedition->GetLeaderName().c_str(), sizeof(dz.leader_name));
|
||||
|
||||
client_dzs.emplace_back(dz);
|
||||
}
|
||||
|
||||
// todo: check for Missions (Shared Tasks), Quests, or Tasks that have associated dzs to zone_id
|
||||
|
||||
if (client_dzs.empty())
|
||||
{
|
||||
MessageString(Chat::Red, DYNAMICZONE_WAY_IS_BLOCKED); // unconfirmed message
|
||||
}
|
||||
else if (client_dzs.size() == 1)
|
||||
{
|
||||
if (single_dz.GetInstanceID() == 0)
|
||||
{
|
||||
LogDynamicZones("Character [{}] has dz for zone [{}] with no instance id", CharacterID(), zone_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicZoneLocation zonein = single_dz.GetZoneInLocation();
|
||||
ZoneMode zone_mode = ZoneMode::ZoneToSafeCoords;
|
||||
if (single_dz.HasZoneInLocation())
|
||||
{
|
||||
zone_mode = ZoneMode::ZoneSolicited;
|
||||
}
|
||||
MovePC(zone_id, single_dz.GetInstanceID(), zonein.x, zonein.y, zonein.z, zonein.heading, 0, zone_mode);
|
||||
}
|
||||
}
|
||||
else if (client_dzs.size() > 1)
|
||||
{
|
||||
LogDynamicZonesDetail(
|
||||
"Sending DzSwitchListWnd to character [{}] associated with [{}] dynamic zone(s)",
|
||||
CharacterID(), client_dzs.size()
|
||||
);
|
||||
|
||||
// more than one dynamic zone to this zone, send out the switchlist window
|
||||
// note that this will most likely crash clients if they've reloaded the ui
|
||||
// this occurs on live as well so it may just be a long lasting client bug
|
||||
uint32 count = static_cast<uint32_t>(client_dzs.size());
|
||||
uint32 entries_size = sizeof(DynamicZoneChooseZoneEntry_Struct) * count;
|
||||
uint32 outsize = sizeof(DynamicZoneChooseZone_Struct) + entries_size;
|
||||
auto outapp = std::unique_ptr<EQApplicationPacket>(new EQApplicationPacket(OP_DzChooseZone, outsize));
|
||||
auto outbuf = reinterpret_cast<DynamicZoneChooseZone_Struct*>(outapp->pBuffer);
|
||||
outbuf->client_id = 0;
|
||||
outbuf->count = count;
|
||||
memcpy(outbuf->choices, client_dzs.data(), entries_size);
|
||||
|
||||
QueuePacket(outapp.get());
|
||||
}
|
||||
}
|
||||
|
||||
void Client::MovePCDynamicZone(const std::string& zone_name)
|
||||
{
|
||||
auto zone_id = ZoneID(zone_name.c_str());
|
||||
MovePCDynamicZone(zone_id);
|
||||
}
|
||||
|
||||
@ -54,6 +54,7 @@ namespace EQ
|
||||
#include "aggromanager.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "dynamiczone.h"
|
||||
#include "merc.h"
|
||||
#include "mob.h"
|
||||
#include "qglobals.h"
|
||||
@ -1116,10 +1117,13 @@ public:
|
||||
|
||||
void AddExpeditionLockout(const ExpeditionLockoutTimer& lockout, bool update_db = false);
|
||||
void AddNewExpeditionLockout(const std::string& expedition_name, const std::string& event_name, uint32_t duration);
|
||||
Expedition* CreateExpedition(std::string name, uint32 min_players, uint32 max_players, bool has_replay_timer = false);
|
||||
Expedition* CreateExpedition(
|
||||
std::string zone_name, uint32 version, uint32 duration, std::string expedition_name,
|
||||
uint32 min_players, uint32 max_players, bool has_replay_timer = false);
|
||||
Expedition* GetExpedition() const;
|
||||
uint32 GetExpeditionID() const { return m_expedition_id; }
|
||||
const ExpeditionLockoutTimer* GetExpeditionLockout(const std::string& expedition_name, const std::string& event_name, bool include_expired = false) const;
|
||||
const ExpeditionLockoutTimer* GetExpeditionLockout(
|
||||
const std::string& expedition_name, const std::string& event_name, bool include_expired = false) const;
|
||||
const std::vector<ExpeditionLockoutTimer>& GetExpeditionLockouts() const { return m_expedition_lockouts; };
|
||||
std::vector<ExpeditionLockoutTimer> GetExpeditionLockouts(const std::string& expedition_name);
|
||||
uint32 GetPendingExpeditionInviteID() const { return m_pending_expedition_invite_id; }
|
||||
@ -1131,6 +1135,11 @@ public:
|
||||
void SetExpeditionID(uint32 expedition_id) { m_expedition_id = expedition_id; };
|
||||
void UpdateExpeditionInfoAndLockouts();
|
||||
void DzListTimers();
|
||||
void SetDzRemovalTimer(bool enable_timer);
|
||||
void SendDzCompassUpdate();
|
||||
void GoToDzSafeReturnOrBind(const DynamicZoneLocation& safereturn);
|
||||
void MovePCDynamicZone(uint32 zone_id);
|
||||
void MovePCDynamicZone(const std::string& zone_name);
|
||||
|
||||
void CalcItemScale();
|
||||
bool CalcItemScale(uint32 slot_x, uint32 slot_y); // behavior change: 'slot_y' is now [RANGE]_END and not [RANGE]_END + 1
|
||||
@ -1585,6 +1594,7 @@ private:
|
||||
Timer hp_other_update_throttle_timer; /* This is to keep clients from DOSing the server with macros that change client targets constantly */
|
||||
Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */
|
||||
Timer consent_throttle_timer;
|
||||
Timer dynamiczone_removal_timer;
|
||||
|
||||
glm::vec3 m_Proximity;
|
||||
glm::vec4 last_position_before_bulk_update;
|
||||
@ -1688,8 +1698,8 @@ private:
|
||||
|
||||
uint32 m_expedition_id = 0;
|
||||
uint32 m_pending_expedition_invite_id = 0;
|
||||
Expedition* m_expedition = nullptr;
|
||||
std::vector<ExpeditionLockoutTimer> m_expedition_lockouts;
|
||||
DynamicZoneLocation m_quest_compass;
|
||||
|
||||
#ifdef BOTS
|
||||
|
||||
|
||||
@ -5637,9 +5637,30 @@ void Client::Handle_OP_DzAddPlayer(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_DzChooseZoneReply(const EQApplicationPacket *app)
|
||||
{
|
||||
// todo: implement
|
||||
LogExpeditionsModerate("Handle_OP_DzChooseZoneReply");
|
||||
auto dzmsg = reinterpret_cast<DynamicZoneChooseZoneReply_Struct*>(app->pBuffer);
|
||||
LogDynamicZones(
|
||||
"Character [{}] chose DynamicZone [{}]:[{}] type: [{}] with system id: [{}]",
|
||||
CharacterID(), dzmsg->dz_zone_id, dzmsg->dz_instance_id, dzmsg->dz_type, dzmsg->unknown_id2
|
||||
);
|
||||
|
||||
if (!dzmsg->dz_instance_id || !database.VerifyInstanceAlive(dzmsg->dz_instance_id, CharacterID()))
|
||||
{
|
||||
// live just no-ops this without a message
|
||||
LogDynamicZones(
|
||||
"Character [{}] chose invalid DynamicZone [{}]:[{}] or is no longer a member",
|
||||
CharacterID(), dzmsg->dz_zone_id, dzmsg->dz_instance_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicZone dz = DynamicZone::LoadDzFromDatabase(dzmsg->dz_instance_id);
|
||||
DynamicZoneLocation loc = dz.GetZoneInLocation();
|
||||
ZoneMode zone_mode = ZoneMode::ZoneToSafeCoords;
|
||||
if (dz.HasZoneInLocation())
|
||||
{
|
||||
zone_mode = ZoneMode::ZoneSolicited;
|
||||
}
|
||||
MovePC(dzmsg->dz_zone_id, dzmsg->dz_instance_id, loc.x, loc.y, loc.z, loc.heading, 0, zone_mode);
|
||||
}
|
||||
|
||||
void Client::Handle_OP_DzExpeditionInviteResponse(const EQApplicationPacket *app)
|
||||
|
||||
@ -161,6 +161,16 @@ bool Client::Process() {
|
||||
if (TaskPeriodic_Timer.Check() && taskstate)
|
||||
taskstate->TaskPeriodicChecks(this);
|
||||
|
||||
if (dynamiczone_removal_timer.Check())
|
||||
{
|
||||
dynamiczone_removal_timer.Disable();
|
||||
if (zone && zone->GetInstanceID() != 0)
|
||||
{
|
||||
DynamicZone dz = DynamicZone::LoadDzFromDatabase(zone->GetInstanceID());
|
||||
GoToDzSafeReturnOrBind(dz.GetSafeReturnLocation());
|
||||
}
|
||||
}
|
||||
|
||||
if (linkdead_timer.Check()) {
|
||||
LeaveGroup();
|
||||
Save();
|
||||
|
||||
@ -6837,22 +6837,24 @@ void command_dz(Client* c, const Seperator* sep)
|
||||
{
|
||||
if (strcasecmp(sep->arg[2], "list") == 0)
|
||||
{
|
||||
c->Message(Chat::White, "Total Active Expeditions: [%u]", static_cast<uint32>(zone->expedition_cache.size()));
|
||||
c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", zone->expedition_cache.size()).c_str());
|
||||
for (const auto& expedition : zone->expedition_cache)
|
||||
{
|
||||
c->Message(
|
||||
Chat::White, "Expedition id: [%u]: leader: [%s] instance id: [%u] members: [%u]",
|
||||
c->Message(Chat::White, fmt::format(
|
||||
"Expedition id: [{}]: leader: [{}] instance id: [{}] members: [{}]",
|
||||
expedition.second->GetID(),
|
||||
expedition.second->GetLeaderName().c_str(),
|
||||
expedition.second->GetLeaderName(),
|
||||
expedition.second->GetInstanceID(),
|
||||
expedition.second->GetMemberCount()
|
||||
);
|
||||
).c_str());
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(sep->arg[2], "reload") == 0)
|
||||
{
|
||||
Expedition::CacheAllFromDatabase();
|
||||
c->Message(Chat::White, "Reloaded [%u] expeditions to cache from database.", static_cast<uint32>(zone->expedition_cache.size()));
|
||||
c->Message(Chat::White, fmt::format(
|
||||
"Reloaded [{}] expeditions to cache from database.", zone->expedition_cache.size()
|
||||
).c_str());
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(sep->arg[1], "destroy") == 0)
|
||||
@ -6870,12 +6872,53 @@ void command_dz(Client* c, const Seperator* sep)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(sep->arg[1], "list") == 0)
|
||||
{
|
||||
std::string query = SQL(
|
||||
SELECT
|
||||
dynamic_zones.type,
|
||||
instance_list.id,
|
||||
instance_list.zone,
|
||||
instance_list.version,
|
||||
instance_list.start_time,
|
||||
instance_list.duration,
|
||||
COUNT(instance_list.id) member_count
|
||||
FROM dynamic_zones
|
||||
INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id
|
||||
LEFT JOIN instance_list_player ON instance_list.id = instance_list_player.id
|
||||
GROUP BY instance_list.id;
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success())
|
||||
{
|
||||
c->Message(Chat::White, fmt::format("Total Dynamic Zones: [{}]", results.RowCount()).c_str());
|
||||
for (auto row = results.begin(); row != results.end(); ++row)
|
||||
{
|
||||
auto start_time = strtoul(row[4], nullptr, 10);
|
||||
auto duration = strtoul(row[5], nullptr, 10);
|
||||
auto expire_time = std::chrono::system_clock::from_time_t(start_time + duration);
|
||||
bool is_expired = std::chrono::system_clock::now() > expire_time;
|
||||
|
||||
c->Message(Chat::White, fmt::format(
|
||||
"type: [{}] instance: [{}] zone: [{}] version: [{}] members: [{}] expired: [{}]",
|
||||
strtoul(row[0], nullptr, 10),
|
||||
strtoul(row[1], nullptr, 10),
|
||||
strtoul(row[2], nullptr, 10),
|
||||
strtoul(row[3], nullptr, 10),
|
||||
strtoul(row[6], nullptr, 10),
|
||||
is_expired
|
||||
).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
c->Message(Chat::White, "#dz usage:");
|
||||
c->Message(Chat::White, "#dz cache list - list expeditions in current zone cache");
|
||||
c->Message(Chat::White, "#dz cache reload - reload zone cache from database");
|
||||
c->Message(Chat::White, "#dz destroy <expedition_id> - destroy expedition globally (must be in cache)");
|
||||
c->Message(Chat::White, "#dz list - list all dynamic zones with corresponding instance ids from database");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -207,6 +207,8 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
}
|
||||
}
|
||||
|
||||
// todo: if IsDzDoor() call Client::MovePCDynamicZone(target_zone_id) (for systems that use dzs)
|
||||
|
||||
uint32 required_key_item = GetKeyItem();
|
||||
uint8 disable_add_to_key_ring = GetNoKeyring();
|
||||
uint32 player_has_key = 0;
|
||||
|
||||
507
zone/dynamiczone.cpp
Normal file
507
zone/dynamiczone.cpp
Normal file
@ -0,0 +1,507 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
* are required to give you total support for your newly bought product;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dynamiczone.h"
|
||||
#include "client.h"
|
||||
#include "worldserver.h"
|
||||
#include "zonedb.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include <chrono>
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
DynamicZone::DynamicZone(
|
||||
uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type
|
||||
) :
|
||||
m_zone_id(zone_id),
|
||||
m_version(version),
|
||||
m_duration(duration),
|
||||
m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
DynamicZone::DynamicZone(
|
||||
std::string zone_shortname, uint32_t version, uint32_t duration, DynamicZoneType type
|
||||
) :
|
||||
m_version(version),
|
||||
m_duration(duration),
|
||||
m_type(type)
|
||||
{
|
||||
m_zone_id = ZoneID(zone_shortname.c_str());
|
||||
|
||||
if (!m_zone_id)
|
||||
{
|
||||
LogDynamicZones("Failed to get zone id for zone [{}]", zone_shortname);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicZone DynamicZone::LoadDzFromDatabase(uint32_t instance_id)
|
||||
{
|
||||
DynamicZone dynamic_zone;
|
||||
if (instance_id != 0)
|
||||
{
|
||||
dynamic_zone.LoadFromDatabase(instance_id);
|
||||
}
|
||||
return dynamic_zone;
|
||||
}
|
||||
|
||||
uint32_t DynamicZone::CreateInstance()
|
||||
{
|
||||
if (m_instance_id)
|
||||
{
|
||||
LogDynamicZones("CreateInstance failed, instance id [{}] already created", m_instance_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_zone_id)
|
||||
{
|
||||
LogDynamicZones("CreateInstance failed, invalid zone id [{}]", m_zone_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t instance_id = 0;
|
||||
if (!database.GetUnusedInstanceID(instance_id)) // todo: doesn't this race with insert?
|
||||
{
|
||||
LogDynamicZones("Failed to find unused instance id");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto start_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
|
||||
std::string query = fmt::format(SQL(
|
||||
INSERT INTO instance_list
|
||||
(id, zone, version, start_time, duration)
|
||||
VALUES
|
||||
({}, {}, {}, {}, {})
|
||||
), instance_id, m_zone_id, m_version, start_time, m_duration);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
{
|
||||
LogDynamicZones("Failed to create instance [{}] for Dynamic Zone [{}]", instance_id, m_zone_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_instance_id = instance_id;
|
||||
m_start_time = static_cast<uint32_t>(start_time);
|
||||
m_never_expires = false;
|
||||
m_expire_time = std::chrono::system_clock::from_time_t(m_start_time + m_duration);
|
||||
|
||||
return m_instance_id;
|
||||
}
|
||||
|
||||
void DynamicZone::LoadFromDatabase(uint32_t instance_id)
|
||||
{
|
||||
if (instance_id == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_instance_id)
|
||||
{
|
||||
LogDynamicZones(
|
||||
"Loading instance data for [{}] failed, instance id [{}] data already loaded",
|
||||
instance_id, m_instance_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
LogDynamicZonesDetail("Loading dz instance [{}] from database", instance_id);
|
||||
|
||||
std::string query = fmt::format(SQL(
|
||||
SELECT
|
||||
instance_list.zone,
|
||||
instance_list.version,
|
||||
instance_list.start_time,
|
||||
instance_list.duration,
|
||||
instance_list.never_expires,
|
||||
dynamic_zones.type,
|
||||
dynamic_zones.compass_zone_id,
|
||||
dynamic_zones.compass_x,
|
||||
dynamic_zones.compass_y,
|
||||
dynamic_zones.compass_z,
|
||||
dynamic_zones.safe_return_zone_id,
|
||||
dynamic_zones.safe_return_x,
|
||||
dynamic_zones.safe_return_y,
|
||||
dynamic_zones.safe_return_z,
|
||||
dynamic_zones.safe_return_heading,
|
||||
dynamic_zones.zone_in_x,
|
||||
dynamic_zones.zone_in_y,
|
||||
dynamic_zones.zone_in_z,
|
||||
dynamic_zones.zone_in_heading,
|
||||
dynamic_zones.has_zone_in
|
||||
FROM dynamic_zones
|
||||
INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id
|
||||
WHERE dynamic_zones.instance_id = {};
|
||||
), instance_id);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success() && results.RowCount() > 0)
|
||||
{
|
||||
auto row = results.begin();
|
||||
|
||||
m_instance_id = instance_id;
|
||||
m_zone_id = strtoul(row[0], nullptr, 10);
|
||||
m_version = strtoul(row[1], nullptr, 10);
|
||||
m_start_time = strtoul(row[2], nullptr, 10);
|
||||
m_duration = strtoul(row[3], nullptr, 10);
|
||||
m_never_expires = (strtoul(row[4], nullptr, 10) != 0);
|
||||
m_type = static_cast<DynamicZoneType>(strtoul(row[5], nullptr, 10));
|
||||
m_expire_time = std::chrono::system_clock::from_time_t(m_start_time + m_duration);
|
||||
m_compass.zone_id = strtoul(row[6], nullptr, 10);
|
||||
m_compass.x = strtof(row[7], nullptr);
|
||||
m_compass.y = strtof(row[8], nullptr);
|
||||
m_compass.z = strtof(row[9], nullptr);
|
||||
m_safereturn.zone_id = strtoul(row[10], nullptr, 10);
|
||||
m_safereturn.x = strtof(row[11], nullptr);
|
||||
m_safereturn.y = strtof(row[12], nullptr);
|
||||
m_safereturn.z = strtof(row[13], nullptr);
|
||||
m_safereturn.heading = strtof(row[14], nullptr);
|
||||
m_zonein.x = strtof(row[15], nullptr);
|
||||
m_zonein.y = strtof(row[16], nullptr);
|
||||
m_zonein.z = strtof(row[17], nullptr);
|
||||
m_zonein.heading = strtof(row[18], nullptr);
|
||||
m_has_zonein = (strtoul(row[19], nullptr, 10) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DynamicZone::SaveToDatabase()
|
||||
{
|
||||
LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id);
|
||||
|
||||
if (m_instance_id != 0)
|
||||
{
|
||||
std::string query = fmt::format(SQL(
|
||||
INSERT INTO dynamic_zones
|
||||
(
|
||||
instance_id,
|
||||
type,
|
||||
compass_zone_id,
|
||||
compass_x,
|
||||
compass_y,
|
||||
compass_z,
|
||||
safe_return_zone_id,
|
||||
safe_return_x,
|
||||
safe_return_y,
|
||||
safe_return_z,
|
||||
safe_return_heading,
|
||||
zone_in_x,
|
||||
zone_in_y,
|
||||
zone_in_z,
|
||||
zone_in_heading,
|
||||
has_zone_in
|
||||
)
|
||||
VALUES
|
||||
({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {});
|
||||
),
|
||||
m_instance_id,
|
||||
static_cast<uint8_t>(m_type),
|
||||
m_compass.zone_id,
|
||||
m_compass.x,
|
||||
m_compass.y,
|
||||
m_compass.z,
|
||||
m_safereturn.zone_id,
|
||||
m_safereturn.x,
|
||||
m_safereturn.y,
|
||||
m_safereturn.z,
|
||||
m_safereturn.heading,
|
||||
m_zonein.x,
|
||||
m_zonein.y,
|
||||
m_zonein.z,
|
||||
m_zonein.heading,
|
||||
m_has_zonein
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success())
|
||||
{
|
||||
return results.LastInsertedID();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DynamicZone::SaveCompassToDatabase()
|
||||
{
|
||||
LogDynamicZonesDetail(
|
||||
"Instance [{}] saving compass zone: [{}] xyz: ([{}], [{}], [{}])",
|
||||
m_instance_id, m_compass.zone_id, m_compass.x, m_compass.y, m_compass.z
|
||||
);
|
||||
|
||||
if (m_instance_id != 0)
|
||||
{
|
||||
std::string query = fmt::format(SQL(
|
||||
UPDATE dynamic_zones SET
|
||||
compass_zone_id = {},
|
||||
compass_x = {},
|
||||
compass_y = {},
|
||||
compass_z = {}
|
||||
WHERE instance_id = {};
|
||||
),
|
||||
m_compass.zone_id,
|
||||
m_compass.x,
|
||||
m_compass.y,
|
||||
m_compass.z,
|
||||
m_instance_id
|
||||
);
|
||||
|
||||
database.QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::SaveSafeReturnToDatabase()
|
||||
{
|
||||
LogDynamicZonesDetail(
|
||||
"Instance [{}] saving safereturn zone: [{}] xyzh: ([{}], [{}], [{}], [{}])",
|
||||
m_instance_id, m_safereturn.zone_id, m_safereturn.x, m_safereturn.y, m_safereturn.z, m_safereturn.heading
|
||||
);
|
||||
|
||||
if (m_instance_id != 0)
|
||||
{
|
||||
std::string query = fmt::format(SQL(
|
||||
UPDATE dynamic_zones SET
|
||||
safe_return_zone_id = {},
|
||||
safe_return_x = {},
|
||||
safe_return_y = {},
|
||||
safe_return_z = {},
|
||||
safe_return_heading = {}
|
||||
WHERE instance_id = {};
|
||||
),
|
||||
m_safereturn.zone_id,
|
||||
m_safereturn.x,
|
||||
m_safereturn.y,
|
||||
m_safereturn.z,
|
||||
m_safereturn.heading,
|
||||
m_instance_id
|
||||
);
|
||||
|
||||
database.QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::SaveZoneInLocationToDatabase()
|
||||
{
|
||||
LogDynamicZonesDetail(
|
||||
"Instance [{}] saving zonein zone: [{}] xyzh: ([{}], [{}], [{}], [{}]) has: [{}]",
|
||||
m_instance_id, m_zone_id, m_zonein.x, m_zonein.y, m_zonein.z, m_zonein.heading, m_has_zonein
|
||||
);
|
||||
|
||||
if (m_instance_id != 0)
|
||||
{
|
||||
std::string query = fmt::format(SQL(
|
||||
UPDATE dynamic_zones SET
|
||||
zone_in_x = {},
|
||||
zone_in_y = {},
|
||||
zone_in_z = {},
|
||||
zone_in_heading = {},
|
||||
has_zone_in = {}
|
||||
WHERE instance_id = {};
|
||||
),
|
||||
m_zonein.x,
|
||||
m_zonein.y,
|
||||
m_zonein.z,
|
||||
m_zonein.heading,
|
||||
m_has_zonein,
|
||||
m_instance_id
|
||||
);
|
||||
|
||||
database.QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DynamicZone::DeleteFromDatabase()
|
||||
{
|
||||
LogDynamicZonesDetail("Deleting dz instance [{}] from database", m_instance_id);
|
||||
|
||||
if (m_instance_id != 0)
|
||||
{
|
||||
std::string query = fmt::format(SQL(
|
||||
DELETE FROM dynamic_zones WHERE instance_id = {};
|
||||
), m_instance_id);
|
||||
|
||||
database.QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::AddCharacter(uint32_t character_id)
|
||||
{
|
||||
database.AddClientToInstance(m_instance_id, character_id);
|
||||
SendInstanceCharacterChange(character_id, false); // stops client kick timer
|
||||
}
|
||||
|
||||
void DynamicZone::RemoveCharacter(uint32_t character_id)
|
||||
{
|
||||
database.RemoveClientFromInstance(m_instance_id, character_id);
|
||||
SendInstanceCharacterChange(character_id, true); // start client kick timer
|
||||
}
|
||||
|
||||
void DynamicZone::RemoveAllCharacters()
|
||||
{
|
||||
// caller has to notify clients of instance change since we don't hold members here
|
||||
if (m_instance_id != 0)
|
||||
{
|
||||
database.RemoveClientsFromInstance(m_instance_id);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::SaveInstanceMembersToDatabase(const std::unordered_set<uint32_t> character_ids)
|
||||
{
|
||||
std::string insert_values;
|
||||
for (const auto& character_id : character_ids)
|
||||
{
|
||||
fmt::format_to(std::back_inserter(insert_values), "({}, {}),", m_instance_id, character_id);
|
||||
}
|
||||
|
||||
if (!insert_values.empty())
|
||||
{
|
||||
insert_values.pop_back(); // trailing comma
|
||||
|
||||
std::string query = fmt::format(SQL(
|
||||
REPLACE INTO instance_list_player (id, charid) VALUES {};
|
||||
), insert_values);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
{
|
||||
LogDynamicZones("Failed to save instance members to database");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::SendInstanceCharacterChange(uint32_t character_id, bool removed)
|
||||
{
|
||||
// if removing, sets removal timer on client inside the instance
|
||||
if (IsCurrentZoneDzInstance())
|
||||
{
|
||||
Client* client = entity_list.GetClientByCharID(character_id);
|
||||
if (client)
|
||||
{
|
||||
client->SetDzRemovalTimer(removed);
|
||||
}
|
||||
}
|
||||
else if (GetInstanceID() != 0)
|
||||
{
|
||||
uint32_t packsize = sizeof(ServerDzCharacter_Struct);
|
||||
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_DzCharacterChange, packsize));
|
||||
auto packbuf = reinterpret_cast<ServerDzCharacter_Struct*>(pack->pBuffer);
|
||||
packbuf->instance_id = GetInstanceID();
|
||||
packbuf->remove = removed;
|
||||
packbuf->character_id = character_id;
|
||||
worldserver.SendPacket(pack.get());
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::UpdateExpireTime(uint32_t seconds)
|
||||
{
|
||||
if (GetInstanceID() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_duration = seconds;
|
||||
m_expire_time = std::chrono::system_clock::now() + std::chrono::seconds(seconds);
|
||||
|
||||
auto new_duration = std::chrono::system_clock::to_time_t(m_expire_time) - m_start_time;
|
||||
|
||||
std::string query = fmt::format(SQL(
|
||||
UPDATE instance_list SET duration = {} WHERE id = {};
|
||||
), new_duration, GetInstanceID());
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success())
|
||||
{
|
||||
uint32_t packsize = sizeof(ServerInstanceUpdateTime_Struct);
|
||||
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_InstanceUpdateTime, packsize));
|
||||
auto packbuf = reinterpret_cast<ServerInstanceUpdateTime_Struct*>(pack->pBuffer);
|
||||
packbuf->instance_id = GetInstanceID();
|
||||
packbuf->new_duration = seconds;
|
||||
worldserver.SendPacket(pack.get());
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::SetCompass(const DynamicZoneLocation& location, bool update_db)
|
||||
{
|
||||
m_compass = location;
|
||||
|
||||
if (update_db)
|
||||
{
|
||||
SaveCompassToDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::SetSafeReturn(const DynamicZoneLocation& location, bool update_db)
|
||||
{
|
||||
m_safereturn = location;
|
||||
|
||||
if (update_db)
|
||||
{
|
||||
SaveSafeReturnToDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicZone::SetZoneInLocation(const DynamicZoneLocation& location, bool update_db)
|
||||
{
|
||||
m_zonein = location;
|
||||
m_has_zonein = true;
|
||||
|
||||
if (update_db)
|
||||
{
|
||||
SaveZoneInLocationToDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
bool DynamicZone::IsCurrentZoneDzInstance() const
|
||||
{
|
||||
return (zone && zone->GetInstanceID() != 0 && zone->GetInstanceID() == GetInstanceID());
|
||||
}
|
||||
|
||||
bool DynamicZone::IsInstanceID(uint32_t instance_id) const
|
||||
{
|
||||
return (GetInstanceID() != 0 && GetInstanceID() == instance_id);
|
||||
}
|
||||
|
||||
uint32_t DynamicZone::GetSecondsRemaining() const
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
if (m_expire_time > now)
|
||||
{
|
||||
auto remaining = m_expire_time - now;
|
||||
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>(remaining).count());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DynamicZone::HandleWorldMessage(ServerPacket* pack)
|
||||
{
|
||||
switch (pack->opcode)
|
||||
{
|
||||
case ServerOP_DzCharacterChange:
|
||||
{
|
||||
auto buf = reinterpret_cast<ServerDzCharacter_Struct*>(pack->pBuffer);
|
||||
Client* client = entity_list.GetClientByCharID(buf->character_id);
|
||||
if (client)
|
||||
{
|
||||
client->SetDzRemovalTimer(buf->remove); // instance kick timer
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
zone/dynamiczone.h
Normal file
117
zone/dynamiczone.h
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
* are required to give you total support for your newly bought product;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DYNAMICZONE_H
|
||||
#define DYNAMICZONE_H
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
class ServerPacket;
|
||||
|
||||
enum class DynamicZoneType : uint8_t // DynamicZoneActiveType
|
||||
{
|
||||
None = 0,
|
||||
Expedition,
|
||||
Tutorial,
|
||||
Task,
|
||||
Mission, // Shared Task
|
||||
Quest
|
||||
};
|
||||
|
||||
struct DynamicZoneLocation
|
||||
{
|
||||
uint32_t zone_id = 0;
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float z = 0.0f;
|
||||
float heading = 0.0f;
|
||||
|
||||
DynamicZoneLocation() {}
|
||||
DynamicZoneLocation(uint32_t zone_id_, float x_, float y_, float z_, float heading_)
|
||||
: zone_id(zone_id_), x(x_), y(y_), z(z_), heading(heading_) {}
|
||||
};
|
||||
|
||||
class DynamicZone
|
||||
{
|
||||
public:
|
||||
DynamicZone() = default;
|
||||
DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type);
|
||||
DynamicZone(std::string zone_shortname, uint32_t version, uint32_t duration, DynamicZoneType type);
|
||||
DynamicZone(DynamicZoneType type) : m_type(type) { }
|
||||
|
||||
static DynamicZone LoadDzFromDatabase(uint32_t instance_id);
|
||||
static void HandleWorldMessage(ServerPacket* pack);
|
||||
|
||||
DynamicZoneType GetType() const { return m_type; }
|
||||
DynamicZoneLocation GetCompassLocation() const { return m_compass; }
|
||||
DynamicZoneLocation GetSafeReturnLocation() const { return m_safereturn; }
|
||||
DynamicZoneLocation GetZoneInLocation() const { return m_zonein; }
|
||||
|
||||
uint32_t CreateInstance();
|
||||
void AddCharacter(uint32_t character_id);
|
||||
void SaveInstanceMembersToDatabase(const std::unordered_set<uint32_t> character_ids);
|
||||
|
||||
uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); }
|
||||
uint16_t GetInstanceID() const { return static_cast<uint16_t>(m_instance_id); };
|
||||
//uint32_t GetRealID() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); }
|
||||
uint32_t GetSecondsRemaining() const;
|
||||
uint16_t GetZoneID() const { return static_cast<uint16_t>(m_zone_id); };
|
||||
uint32_t GetZoneVersion() const { return m_version; };
|
||||
|
||||
bool HasZoneInLocation() const { return m_has_zonein; }
|
||||
bool IsCurrentZoneDzInstance() const;
|
||||
bool IsInstanceID(uint32_t instance_id) const;
|
||||
bool IsValid() const { return m_instance_id != 0; }
|
||||
void RemoveAllCharacters();
|
||||
void RemoveCharacter(uint32_t character_id);
|
||||
void SendInstanceCharacterChange(uint32_t character_id, bool removed);
|
||||
void SetCompass(const DynamicZoneLocation& location, bool update_db = false);
|
||||
void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false);
|
||||
void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false);
|
||||
void UpdateExpireTime(uint32_t seconds);
|
||||
|
||||
void LoadFromDatabase(uint32_t instance_id);
|
||||
uint32_t SaveToDatabase();
|
||||
|
||||
private:
|
||||
void DeleteFromDatabase();
|
||||
void SaveCompassToDatabase();
|
||||
void SaveSafeReturnToDatabase();
|
||||
void SaveZoneInLocationToDatabase();
|
||||
|
||||
uint32_t m_zone_id = 0;
|
||||
uint32_t m_instance_id = 0;
|
||||
uint32_t m_version = 0;
|
||||
uint32_t m_start_time = 0;
|
||||
uint32_t m_duration = 0;
|
||||
bool m_never_expires = false;
|
||||
bool m_has_zonein = false;
|
||||
DynamicZoneType m_type = DynamicZoneType::None;
|
||||
DynamicZoneLocation m_compass;
|
||||
DynamicZoneLocation m_safereturn;
|
||||
DynamicZoneLocation m_zonein;
|
||||
std::chrono::time_point<std::chrono::system_clock> m_expire_time; //uint64_t m_expire_time = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -5202,3 +5202,26 @@ std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float d
|
||||
return mob_list;
|
||||
}
|
||||
|
||||
void EntityList::GateAllClientsToSafeReturn()
|
||||
{
|
||||
DynamicZone dz;
|
||||
if (zone)
|
||||
{
|
||||
dz = DynamicZone::LoadDzFromDatabase(zone->GetInstanceID());
|
||||
|
||||
LogDynamicZones(
|
||||
"Sending all clients in zone: [{}] instance: [{}] to dz safereturn or bind",
|
||||
zone->GetZoneID(), zone->GetInstanceID()
|
||||
);
|
||||
}
|
||||
|
||||
for (const auto& client_list_iter : client_list)
|
||||
{
|
||||
Client* client = client_list_iter.second;
|
||||
if (client)
|
||||
{
|
||||
// falls back to gating clients to bind if dz invalid
|
||||
client->GoToDzSafeReturnOrBind(dz.GetSafeReturnLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ class Raid;
|
||||
class Spawn2;
|
||||
class Trap;
|
||||
|
||||
struct DynamicZoneSafeReturn;
|
||||
struct GuildBankItemUpdate_Struct;
|
||||
struct NewSpawn_Struct;
|
||||
struct QGlobal;
|
||||
@ -496,6 +497,8 @@ public:
|
||||
void UpdateFindableNPCState(NPC *n, bool Remove);
|
||||
void HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode);
|
||||
|
||||
void GateAllClientsToSafeReturn();
|
||||
|
||||
uint16 GetClientCount();
|
||||
void GetMobList(std::list<Mob*> &m_list);
|
||||
void GetNPCList(std::list<NPC*> &n_list);
|
||||
|
||||
@ -26,8 +26,8 @@
|
||||
#include "groups.h"
|
||||
#include "raids.h"
|
||||
#include "string_ids.h"
|
||||
#include "zonedb.h"
|
||||
#include "worldserver.h"
|
||||
#include "zonedb.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
|
||||
extern WorldServer worldserver;
|
||||
@ -46,10 +46,11 @@ const uint32_t Expedition::REPLAY_TIMER_ID = std::numeric_limits<uint32_t>::max(
|
||||
const uint32_t Expedition::EVENT_TIMER_ID = 1;
|
||||
|
||||
Expedition::Expedition(
|
||||
uint32_t id, std::string expedition_name, const ExpeditionMember& leader,
|
||||
uint32_t min_players, uint32_t max_players, bool replay_timer
|
||||
uint32_t id, const DynamicZone& dynamic_zone, std::string expedition_name,
|
||||
const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players, bool replay_timer
|
||||
) :
|
||||
m_id(id),
|
||||
m_dynamiczone(dynamic_zone),
|
||||
m_expedition_name(expedition_name),
|
||||
m_leader(leader),
|
||||
m_min_players(min_players),
|
||||
@ -59,7 +60,7 @@ Expedition::Expedition(
|
||||
}
|
||||
|
||||
Expedition* Expedition::TryCreate(
|
||||
Client* requester, std::string name, uint32_t min_players, uint32_t max_players, bool replay_timer)
|
||||
Client* requester, DynamicZone& dynamiczone, ExpeditionRequest& request)
|
||||
{
|
||||
if (!requester || !zone)
|
||||
{
|
||||
@ -67,10 +68,25 @@ Expedition* Expedition::TryCreate(
|
||||
}
|
||||
|
||||
// request parses leader, members list, and lockouts while validating
|
||||
ExpeditionRequest request(requester, name, min_players, max_players, replay_timer);
|
||||
if (!request.Validate())
|
||||
if (!request.Validate(requester))
|
||||
{
|
||||
LogExpeditionsModerate("Creation of [{}] by [{}] denied", name, requester->GetName());
|
||||
LogExpeditionsModerate(
|
||||
"Creation of [{}] by [{}] denied", request.GetExpeditionName(), requester->GetName()
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (dynamiczone.GetInstanceID() == 0)
|
||||
{
|
||||
dynamiczone.CreateInstance();
|
||||
}
|
||||
|
||||
if (dynamiczone.GetInstanceID() == 0)
|
||||
{
|
||||
// live uses this message when trying to enter an instance that isn't ready
|
||||
// we can use it as the client error message if instance creation fails
|
||||
requester->MessageString(Chat::Red, DZ_PREVENT_ENTERING);
|
||||
LogExpeditions("Failed to create a dynamic zone instance for expedition");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -78,19 +94,33 @@ Expedition* Expedition::TryCreate(
|
||||
|
||||
// unique expedition ids are created from database via auto-increment column
|
||||
auto expedition_id = ExpeditionDatabase::InsertExpedition(
|
||||
name, leader.char_id, min_players, max_players, replay_timer
|
||||
dynamiczone.GetInstanceID(),
|
||||
request.GetExpeditionName(),
|
||||
leader.char_id,
|
||||
request.GetMinPlayers(),
|
||||
request.GetMaxPlayers(),
|
||||
request.HasReplayTimer()
|
||||
);
|
||||
|
||||
if (expedition_id)
|
||||
{
|
||||
auto expedition = std::make_unique<Expedition>(
|
||||
expedition_id, name, leader, min_players, max_players, replay_timer
|
||||
);
|
||||
dynamiczone.SaveToDatabase();
|
||||
|
||||
auto expedition = std::unique_ptr<Expedition>(new Expedition(
|
||||
expedition_id,
|
||||
dynamiczone,
|
||||
request.GetExpeditionName(),
|
||||
leader,
|
||||
request.GetMinPlayers(),
|
||||
request.GetMaxPlayers(),
|
||||
request.HasReplayTimer()
|
||||
));
|
||||
|
||||
LogExpeditions(
|
||||
"Created [{}] ({}) leader: [{}] minplayers: [{}] maxplayers: [{}]",
|
||||
"Created [{}] ({}) instance id: [{}] leader: [{}] minplayers: [{}] maxplayers: [{}]",
|
||||
expedition->GetID(),
|
||||
expedition->GetName(),
|
||||
expedition->GetInstanceID(),
|
||||
expedition->GetLeaderName(),
|
||||
expedition->GetMinPlayers(),
|
||||
expedition->GetMaxPlayers()
|
||||
@ -104,7 +134,7 @@ Expedition* Expedition::TryCreate(
|
||||
Client* leader_client = request.GetLeaderClient();
|
||||
|
||||
Client::SendCrossZoneMessageString(
|
||||
leader_client, leader.name, Chat::Yellow, EXPEDITION_AVAILABLE, { name }
|
||||
leader_client, leader.name, Chat::Yellow, EXPEDITION_AVAILABLE, { request.GetExpeditionName() }
|
||||
);
|
||||
|
||||
auto inserted = zone->expedition_cache.emplace(expedition_id, std::move(expedition));
|
||||
@ -129,15 +159,19 @@ void Expedition::CacheExpeditions(MySQLRequestResult& results)
|
||||
{
|
||||
auto leader_id = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
|
||||
ExpeditionMember leader{ leader_id, row[7] }; // id, name
|
||||
auto instance_id = row[1] ? strtoul(row[1], nullptr, 10) : 0; // can be null from fk constraint
|
||||
|
||||
std::unique_ptr<Expedition> expedition = std::make_unique<Expedition>(
|
||||
DynamicZone dynamic_zone = DynamicZone::LoadDzFromDatabase(instance_id);
|
||||
|
||||
std::unique_ptr<Expedition> expedition = std::unique_ptr<Expedition>(new Expedition(
|
||||
expedition_id,
|
||||
dynamic_zone,
|
||||
row[2], // expedition name
|
||||
leader, // expedition leader
|
||||
strtoul(row[4], nullptr, 10), // min_players
|
||||
strtoul(row[5], nullptr, 10), // max_players
|
||||
(strtoul(row[6], nullptr, 10) != 0) // has_replay_timer
|
||||
);
|
||||
));
|
||||
|
||||
expedition->LoadMembers();
|
||||
expedition->SendUpdatesToZoneMembers();
|
||||
@ -145,7 +179,6 @@ void Expedition::CacheExpeditions(MySQLRequestResult& results)
|
||||
// don't bother caching empty expeditions
|
||||
if (expedition->GetMemberCount() > 0)
|
||||
{
|
||||
expedition->SendWorldGetOnlineMembers();
|
||||
zone->expedition_cache.emplace(expedition_id, std::move(expedition));
|
||||
}
|
||||
}
|
||||
@ -214,7 +247,7 @@ bool Expedition::CacheAllFromDatabase()
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::duration<float>>(end - start);
|
||||
LogExpeditions("Caching [{}] expeditions took {}s", zone->expedition_cache.size(), elapsed.count());
|
||||
LogExpeditions("Caching [{}] expedition(s) took {}s", zone->expedition_cache.size(), elapsed.count());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -251,8 +284,9 @@ void Expedition::LoadMembers()
|
||||
{
|
||||
auto character_id = strtoul(row[0], nullptr, 10);
|
||||
bool is_current_member = strtoul(row[1], nullptr, 10);
|
||||
AddInternalMember(row[2], character_id, is_current_member, true);
|
||||
AddInternalMember(row[2], character_id, ExpeditionMemberStatus::Offline, is_current_member);
|
||||
}
|
||||
SendWorldGetOnlineMembers();
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,6 +304,7 @@ void Expedition::SaveMembers(ExpeditionRequest& request)
|
||||
m_member_id_history.emplace(member.char_id);
|
||||
}
|
||||
ExpeditionDatabase::InsertMembers(m_id, m_members);
|
||||
m_dynamiczone.SaveInstanceMembersToDatabase(m_member_id_history); // all are current members here
|
||||
}
|
||||
|
||||
Expedition* Expedition::FindCachedExpeditionByCharacterID(uint32_t character_id)
|
||||
@ -317,9 +352,13 @@ Expedition* Expedition::FindCachedExpeditionByID(uint32_t expedition_id)
|
||||
|
||||
Expedition* Expedition::FindExpeditionByInstanceID(uint32_t instance_id)
|
||||
{
|
||||
// ask database since it may have expired
|
||||
auto expedition_id = ExpeditionDatabase::GetExpeditionIDFromInstanceID(instance_id);
|
||||
return Expedition::FindCachedExpeditionByID(expedition_id);
|
||||
if (instance_id)
|
||||
{
|
||||
// ask database since it may have expired
|
||||
auto expedition_id = ExpeditionDatabase::GetExpeditionIDFromInstanceID(instance_id);
|
||||
return Expedition::FindCachedExpeditionByID(expedition_id);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Expedition::HasLockout(const std::string& event_name)
|
||||
@ -329,7 +368,7 @@ bool Expedition::HasLockout(const std::string& event_name)
|
||||
|
||||
bool Expedition::HasReplayLockout()
|
||||
{
|
||||
return (m_lockouts.find(DZ_REPLAY_TIMER_NAME) != m_lockouts.end());
|
||||
return HasLockout(DZ_REPLAY_TIMER_NAME);
|
||||
}
|
||||
|
||||
bool Expedition::HasMember(uint32_t character_id)
|
||||
@ -419,7 +458,7 @@ void Expedition::AddInternalLockout(ExpeditionLockoutTimer&& lockout_timer)
|
||||
}
|
||||
|
||||
void Expedition::AddInternalMember(
|
||||
const std::string& char_name, uint32_t character_id, bool is_current_member, bool offline)
|
||||
const std::string& char_name, uint32_t character_id, ExpeditionMemberStatus status, bool is_current_member)
|
||||
{
|
||||
if (is_current_member)
|
||||
{
|
||||
@ -430,7 +469,6 @@ void Expedition::AddInternalMember(
|
||||
|
||||
if (it == m_members.end())
|
||||
{
|
||||
auto status = offline ? ExpeditionMemberStatus::Offline : ExpeditionMemberStatus::Online;
|
||||
m_members.emplace_back(ExpeditionMember{character_id, char_name, status});
|
||||
}
|
||||
}
|
||||
@ -446,6 +484,7 @@ bool Expedition::AddMember(const std::string& add_char_name, uint32_t add_char_i
|
||||
}
|
||||
|
||||
ExpeditionDatabase::InsertMember(m_id, add_char_id);
|
||||
m_dynamiczone.AddCharacter(add_char_id);
|
||||
|
||||
ProcessMemberAdded(add_char_name, add_char_id);
|
||||
SendWorldMemberChanged(add_char_name, add_char_id, false);
|
||||
@ -453,8 +492,24 @@ bool Expedition::AddMember(const std::string& add_char_name, uint32_t add_char_i
|
||||
return true;
|
||||
}
|
||||
|
||||
void Expedition::RemoveAllMembers()
|
||||
void Expedition::RemoveAllMembers(bool enable_removal_timers, bool update_dz_expire_time)
|
||||
{
|
||||
m_dynamiczone.RemoveAllCharacters();
|
||||
|
||||
if (enable_removal_timers)
|
||||
{
|
||||
// expedition holds member list (not dz) so inform dz members to start kick timers
|
||||
for (const auto& member : m_members)
|
||||
{
|
||||
m_dynamiczone.SendInstanceCharacterChange(member.char_id, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (update_dz_expire_time && RuleB(Expedition, EmptyDzShutdownEnabled))
|
||||
{
|
||||
m_dynamiczone.UpdateExpireTime(RuleI(Expedition, EmptyDzShutdownDelaySeconds));
|
||||
}
|
||||
|
||||
ExpeditionDatabase::DeleteAllMembers(m_id);
|
||||
ExpeditionDatabase::DeleteExpedition(m_id);
|
||||
|
||||
@ -471,6 +526,7 @@ bool Expedition::RemoveMember(const std::string& remove_char_name)
|
||||
}
|
||||
|
||||
ExpeditionDatabase::UpdateMemberRemoved(m_id, member.char_id);
|
||||
m_dynamiczone.RemoveCharacter(member.char_id);
|
||||
|
||||
ProcessMemberRemoved(member.name, member.char_id);
|
||||
SendWorldMemberChanged(member.name, member.char_id, true);
|
||||
@ -480,11 +536,14 @@ bool Expedition::RemoveMember(const std::string& remove_char_name)
|
||||
{
|
||||
ChooseNewLeader();
|
||||
}
|
||||
|
||||
if (m_members.empty())
|
||||
else if (m_members.empty())
|
||||
{
|
||||
// cache removal will occur in world message handler
|
||||
ExpeditionDatabase::DeleteExpedition(m_id);
|
||||
if (RuleB(Expedition, EmptyDzShutdownEnabled))
|
||||
{
|
||||
m_dynamiczone.UpdateExpireTime(RuleI(Expedition, EmptyDzShutdownDelaySeconds));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -506,6 +565,8 @@ void Expedition::SwapMember(Client* add_client, const std::string& remove_char_n
|
||||
// make remove and add atomic to avoid racing with separate world messages
|
||||
ExpeditionDatabase::UpdateMemberRemoved(m_id, member.char_id);
|
||||
ExpeditionDatabase::InsertMember(m_id, add_client->CharacterID());
|
||||
m_dynamiczone.RemoveCharacter(member.char_id);
|
||||
m_dynamiczone.AddCharacter(add_client->CharacterID());
|
||||
|
||||
ProcessMemberRemoved(member.name, member.char_id);
|
||||
ProcessMemberAdded(add_client->GetName(), add_client->CharacterID());
|
||||
@ -710,44 +771,36 @@ void Expedition::DzInviteResponse(
|
||||
add_client->GetName(), accepted, has_swap_name, swap_remove_name
|
||||
);
|
||||
|
||||
// if client accepts the invite we need to re-confirm there's no conflicts
|
||||
// a null leader_client is handled by SendLeaderMessage fallbacks
|
||||
// note current leader receives invite reply messages (if leader changed)
|
||||
bool was_swap_invite = (has_swap_name && !swap_remove_name.empty());
|
||||
|
||||
// null leader_client is handled by SendLeaderMessage fallbacks
|
||||
Client* leader_client = entity_list.GetClientByCharID(m_leader.char_id);
|
||||
|
||||
bool has_conflicts = false;
|
||||
if (accepted)
|
||||
if (!accepted)
|
||||
{
|
||||
has_conflicts = ProcessAddConflicts(leader_client, add_client, was_swap_invite);
|
||||
SendLeaderMessage(leader_client, Chat::Red, EXPEDITION_INVITE_DECLINED, { add_client->GetName() });
|
||||
return;
|
||||
}
|
||||
|
||||
bool was_swap_invite = (has_swap_name && !swap_remove_name.empty());
|
||||
bool has_conflicts = ProcessAddConflicts(leader_client, add_client, was_swap_invite);
|
||||
|
||||
// error if swapping and character was already removed before the accept
|
||||
if (accepted && was_swap_invite && !HasMember(swap_remove_name))
|
||||
if (was_swap_invite && !HasMember(swap_remove_name))
|
||||
{
|
||||
has_conflicts = true;
|
||||
}
|
||||
|
||||
if (accepted && !has_conflicts)
|
||||
{
|
||||
SendLeaderMessage(leader_client, Chat::Yellow, EXPEDITION_INVITE_ACCEPTED, { add_client->GetName() });
|
||||
}
|
||||
else if (accepted)
|
||||
if (has_conflicts)
|
||||
{
|
||||
SendLeaderMessage(leader_client, Chat::Red, EXPEDITION_INVITE_ERROR, { add_client->GetName() });
|
||||
}
|
||||
else
|
||||
{
|
||||
SendLeaderMessage(leader_client, Chat::Red, EXPEDITION_INVITE_DECLINED, { add_client->GetName() });
|
||||
}
|
||||
SendLeaderMessage(leader_client, Chat::Yellow, EXPEDITION_INVITE_ACCEPTED, { add_client->GetName() });
|
||||
|
||||
if (accepted && !has_conflicts)
|
||||
{
|
||||
// insert pending lockouts client will receive when entering dynamic zone.
|
||||
// only lockouts missing from the client when they join are added. all
|
||||
// missing lockouts are not applied on entering instance because client may
|
||||
// have a lockout that expires after joining and shouldn't receive it again.
|
||||
// only lockouts missing from client when they join are added. client may
|
||||
// have a lockout that expires after joining and shouldn't receive it again
|
||||
ExpeditionDatabase::DeletePendingLockouts(add_client->CharacterID());
|
||||
|
||||
std::vector<ExpeditionLockoutTimer> pending_lockouts;
|
||||
@ -771,7 +824,10 @@ void Expedition::DzInviteResponse(
|
||||
}
|
||||
}
|
||||
|
||||
ExpeditionDatabase::InsertCharacterLockouts(add_client->CharacterID(), pending_lockouts, false, true);
|
||||
bool add_immediately = m_dynamiczone.IsCurrentZoneDzInstance();
|
||||
|
||||
ExpeditionDatabase::InsertCharacterLockouts(
|
||||
add_client->CharacterID(), pending_lockouts, false, !add_immediately);
|
||||
|
||||
if (was_swap_invite)
|
||||
{
|
||||
@ -781,6 +837,11 @@ void Expedition::DzInviteResponse(
|
||||
{
|
||||
AddMember(add_client->GetName(), add_client->CharacterID());
|
||||
}
|
||||
|
||||
if (m_dynamiczone.IsCurrentZoneDzInstance())
|
||||
{
|
||||
SetMemberStatus(add_client, ExpeditionMemberStatus::InDynamicZone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1083,11 +1144,12 @@ void Expedition::ProcessMemberAdded(std::string char_name, uint32_t added_char_i
|
||||
if (member_client)
|
||||
{
|
||||
member_client->SetExpeditionID(GetID());
|
||||
member_client->SendDzCompassUpdate();
|
||||
SendClientExpeditionInfo(member_client);
|
||||
member_client->MessageString(Chat::Yellow, EXPEDITION_MEMBER_ADDED, char_name.c_str(), m_expedition_name.c_str());
|
||||
}
|
||||
|
||||
AddInternalMember(char_name, added_char_id);
|
||||
AddInternalMember(char_name, added_char_id, ExpeditionMemberStatus::Online);
|
||||
|
||||
SendUpdatesToZoneMembers(); // live sends full update when member added
|
||||
}
|
||||
@ -1116,6 +1178,7 @@ void Expedition::ProcessMemberRemoved(std::string removed_char_name, uint32_t re
|
||||
{
|
||||
ExpeditionDatabase::DeletePendingLockouts(member_client->CharacterID());
|
||||
member_client->SetExpeditionID(0);
|
||||
member_client->SendDzCompassUpdate();
|
||||
member_client->QueuePacket(CreateInfoPacket(true).get());
|
||||
member_client->MessageString(
|
||||
Chat::Yellow, EXPEDITION_REMOVED, it->name.c_str(), m_expedition_name.c_str()
|
||||
@ -1163,7 +1226,6 @@ void Expedition::SendUpdatesToZoneMembers(bool clear)
|
||||
{
|
||||
if (!m_members.empty())
|
||||
{
|
||||
//auto outapp_compass = CreateCompassPacket();
|
||||
auto outapp_info = CreateInfoPacket(clear);
|
||||
auto outapp_members = CreateMemberListPacket(clear);
|
||||
|
||||
@ -1173,6 +1235,7 @@ void Expedition::SendUpdatesToZoneMembers(bool clear)
|
||||
if (member_client)
|
||||
{
|
||||
member_client->SetExpeditionID(clear ? 0 : GetID());
|
||||
member_client->SendDzCompassUpdate();
|
||||
member_client->QueuePacket(outapp_info.get());
|
||||
member_client->QueuePacket(outapp_members.get());
|
||||
member_client->SendExpeditionLockoutTimers();
|
||||
@ -1222,8 +1285,8 @@ std::unique_ptr<EQApplicationPacket> Expedition::CreateInvitePacket(
|
||||
strn0cpy(outbuf->expedition_name, m_expedition_name.c_str(), sizeof(outbuf->expedition_name));
|
||||
strn0cpy(outbuf->swap_name, swap_remove_name.c_str(), sizeof(outbuf->swap_name));
|
||||
outbuf->swapping = !swap_remove_name.empty();
|
||||
//outbuf->dz_zone_id = m_dynamiczone.GetZoneID();
|
||||
//outbuf->dz_instance_id = m_dynamiczone.GetInstanceID();
|
||||
outbuf->dz_zone_id = m_dynamiczone.GetZoneID();
|
||||
outbuf->dz_instance_id = m_dynamiczone.GetInstanceID();
|
||||
return outapp;
|
||||
}
|
||||
|
||||
@ -1385,6 +1448,24 @@ void Expedition::SendWorldMemberStatus(uint32_t character_id, ExpeditionMemberSt
|
||||
worldserver.SendPacket(pack.get());
|
||||
}
|
||||
|
||||
void Expedition::SendWorldDzLocationUpdate(uint16_t server_opcode, const DynamicZoneLocation& location)
|
||||
{
|
||||
uint32_t pack_size = sizeof(ServerDzLocation_Struct);
|
||||
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(server_opcode, pack_size));
|
||||
auto buf = reinterpret_cast<ServerDzLocation_Struct*>(pack->pBuffer);
|
||||
buf->owner_id = GetID();
|
||||
buf->dz_zone_id = m_dynamiczone.GetZoneID();
|
||||
buf->dz_instance_id = m_dynamiczone.GetInstanceID();
|
||||
buf->sender_zone_id = zone ? zone->GetZoneID() : 0;
|
||||
buf->sender_instance_id = zone ? zone->GetInstanceID() : 0;
|
||||
buf->zone_id = location.zone_id;
|
||||
buf->x = location.x;
|
||||
buf->y = location.y;
|
||||
buf->z = location.z;
|
||||
buf->heading = location.heading;
|
||||
worldserver.SendPacket(pack.get());
|
||||
}
|
||||
|
||||
void Expedition::SendWorldMemberSwapped(
|
||||
const std::string& remove_char_name, uint32_t remove_char_id, const std::string& add_char_name, uint32_t add_char_id)
|
||||
{
|
||||
@ -1439,13 +1520,16 @@ void Expedition::HandleWorldMessage(ServerPacket* pack)
|
||||
case ServerOP_ExpeditionDeleted:
|
||||
{
|
||||
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
|
||||
if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id))
|
||||
auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id);
|
||||
if (zone && expedition)
|
||||
{
|
||||
auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id);
|
||||
if (expedition)
|
||||
if (!zone->IsZone(buf->sender_zone_id, buf->sender_instance_id))
|
||||
{
|
||||
expedition->SendUpdatesToZoneMembers(true);
|
||||
}
|
||||
|
||||
// remove even from sender zone
|
||||
zone->expedition_cache.erase(buf->expedition_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1538,14 +1622,14 @@ void Expedition::HandleWorldMessage(ServerPacket* pack)
|
||||
{
|
||||
for (uint32_t i = 0; i < buf->count; ++i)
|
||||
{
|
||||
auto entry = reinterpret_cast<ServerExpeditionCharacterEntry_Struct*>(&buf->entries[i]);
|
||||
auto is_online = entry->character_online;
|
||||
auto member = reinterpret_cast<ServerExpeditionCharacterEntry_Struct*>(&buf->entries[i]);
|
||||
auto is_online = member->character_online;
|
||||
auto status = is_online ? ExpeditionMemberStatus::Online : ExpeditionMemberStatus::Offline;
|
||||
if (is_online && expedition->GetInstanceID() == entry->character_instance_id)
|
||||
if (is_online && expedition->GetDynamicZone().IsInstanceID(member->character_instance_id))
|
||||
{
|
||||
status = ExpeditionMemberStatus::InDynamicZone;
|
||||
}
|
||||
expedition->UpdateMemberStatus(entry->character_id, status);
|
||||
expedition->UpdateMemberStatus(member->character_id, status);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1583,5 +1667,87 @@ void Expedition::HandleWorldMessage(ServerPacket* pack)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_ExpeditionDzCompass:
|
||||
case ServerOP_ExpeditionDzSafeReturn:
|
||||
case ServerOP_ExpeditionDzZoneIn:
|
||||
{
|
||||
auto buf = reinterpret_cast<ServerDzLocation_Struct*>(pack->pBuffer);
|
||||
if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id))
|
||||
{
|
||||
auto expedition = Expedition::FindCachedExpeditionByID(buf->owner_id);
|
||||
if (expedition)
|
||||
{
|
||||
if (pack->opcode == ServerOP_ExpeditionDzCompass)
|
||||
{
|
||||
expedition->SetDzCompass(buf->zone_id, buf->x, buf->y, buf->z, false);
|
||||
}
|
||||
else if (pack->opcode == ServerOP_ExpeditionDzSafeReturn)
|
||||
{
|
||||
expedition->SetDzSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false);
|
||||
}
|
||||
else if (pack->opcode == ServerOP_ExpeditionDzZoneIn)
|
||||
{
|
||||
expedition->SetDzZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Expedition::SetDzCompass(uint32_t zone_id, float x, float y, float z, bool update_db)
|
||||
{
|
||||
DynamicZoneLocation location{ zone_id, x, y, z, 0.0f };
|
||||
m_dynamiczone.SetCompass(location, update_db);
|
||||
|
||||
for (const auto& member : m_members)
|
||||
{
|
||||
Client* member_client = entity_list.GetClientByCharID(member.char_id);
|
||||
if (member_client)
|
||||
{
|
||||
member_client->SendDzCompassUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
if (update_db)
|
||||
{
|
||||
SendWorldDzLocationUpdate(ServerOP_ExpeditionDzCompass, location);
|
||||
}
|
||||
}
|
||||
|
||||
void Expedition::SetDzCompass(const std::string& zone_name, float x, float y, float z, bool update_db)
|
||||
{
|
||||
auto zone_id = ZoneID(zone_name.c_str());
|
||||
SetDzCompass(zone_id, x, y, z, update_db);
|
||||
}
|
||||
|
||||
void Expedition::SetDzSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db)
|
||||
{
|
||||
DynamicZoneLocation location{ zone_id, x, y, z, heading };
|
||||
|
||||
m_dynamiczone.SetSafeReturn(location, update_db);
|
||||
|
||||
if (update_db)
|
||||
{
|
||||
SendWorldDzLocationUpdate(ServerOP_ExpeditionDzSafeReturn, location);
|
||||
}
|
||||
}
|
||||
|
||||
void Expedition::SetDzSafeReturn(const std::string& zone_name, float x, float y, float z, float heading, bool update_db)
|
||||
{
|
||||
auto zone_id = ZoneID(zone_name.c_str());
|
||||
SetDzSafeReturn(zone_id, x, y, z, heading, update_db);
|
||||
}
|
||||
|
||||
void Expedition::SetDzZoneInLocation(float x, float y, float z, float heading, bool update_db)
|
||||
{
|
||||
DynamicZoneLocation location{ 0, x, y, z, heading };
|
||||
|
||||
m_dynamiczone.SetZoneInLocation(location, update_db);
|
||||
|
||||
if (update_db)
|
||||
{
|
||||
SendWorldDzLocationUpdate(ServerOP_ExpeditionDzZoneIn, location);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#ifndef EXPEDITION_H
|
||||
#define EXPEDITION_H
|
||||
|
||||
#include "dynamiczone.h"
|
||||
#include "expedition_lockout_timer.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@ -38,16 +39,6 @@ class ServerPacket;
|
||||
extern const char* const DZ_YOU_NOT_ASSIGNED;
|
||||
extern const char* const EXPEDITION_OTHER_BELONGS;
|
||||
|
||||
enum class DynamicZoneType : uint8_t // DynamicZoneActiveType
|
||||
{
|
||||
None = 0,
|
||||
Expedition,
|
||||
Tutorial,
|
||||
Task,
|
||||
Mission,
|
||||
Quest
|
||||
};
|
||||
|
||||
enum class ExpeditionMemberStatus : uint8_t
|
||||
{
|
||||
Unknown = 0,
|
||||
@ -73,11 +64,12 @@ class Expedition
|
||||
{
|
||||
public:
|
||||
Expedition() = delete;
|
||||
Expedition(uint32_t id, std::string expedition_name, const ExpeditionMember& leader,
|
||||
Expedition(
|
||||
uint32_t id, const DynamicZone& dz, std::string expedition_name, const ExpeditionMember& leader,
|
||||
uint32_t min_players, uint32_t max_players, bool replay_timer);
|
||||
|
||||
static Expedition* TryCreate(
|
||||
Client* requester, std::string name, uint32_t min_players, uint32_t max_players, bool replay_timer);
|
||||
static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, ExpeditionRequest& request);
|
||||
|
||||
static void CacheFromDatabase(uint32_t expedition_id);
|
||||
static bool CacheAllFromDatabase();
|
||||
static void CacheExpeditions(MySQLRequestResult& results);
|
||||
@ -89,10 +81,12 @@ public:
|
||||
static void HandleWorldMessage(ServerPacket* pack);
|
||||
|
||||
uint32_t GetID() const { return m_id; }
|
||||
uint16_t GetInstanceID() const { return m_dynamiczone.GetInstanceID(); }
|
||||
uint32_t GetLeaderID() const { return m_leader.char_id; }
|
||||
uint32_t GetMinPlayers() const { return m_min_players; }
|
||||
uint32_t GetMaxPlayers() const { return m_max_players; }
|
||||
uint32_t GetMemberCount() const { return static_cast<uint32_t>(m_members.size()); }
|
||||
const DynamicZone& GetDynamicZone() const { return m_dynamiczone; }
|
||||
const std::string& GetName() const { return m_expedition_name; }
|
||||
const std::string& GetLeaderName() const { return m_leader.name; }
|
||||
const std::unordered_map<std::string, ExpeditionLockoutTimer>& GetLockouts() const { return m_lockouts; }
|
||||
@ -101,7 +95,7 @@ public:
|
||||
bool AddMember(const std::string& add_char_name, uint32_t add_char_id);
|
||||
bool HasMember(const std::string& name);
|
||||
bool HasMember(uint32_t character_id);
|
||||
void RemoveAllMembers();
|
||||
void RemoveAllMembers(bool enable_removal_timers = true, bool update_dz_expire_time = true);
|
||||
bool RemoveMember(const std::string& remove_char_name);
|
||||
void SetMemberStatus(Client* client, ExpeditionMemberStatus status);
|
||||
void SetNewLeader(uint32_t new_leader_id, const std::string& new_leader_name);
|
||||
@ -125,19 +119,18 @@ public:
|
||||
void DzQuit(Client* requester);
|
||||
void DzKickPlayers(Client* requester);
|
||||
|
||||
#if 0
|
||||
bool AssignInstance(uint32_t instance_id, bool update_db = true);
|
||||
uint32_t CreateInstance(std::string zone, uint32_t version, uint32_t duration); // m_dynamiczone
|
||||
#endif
|
||||
uint32_t GetInstanceID() const { return 77; /*return m_instance_id;*/ } // todo: GetDynamicZoneID()
|
||||
DynamicZoneType GetType() const { return DynamicZoneType::Expedition; } // m_dynamiczone
|
||||
void SetDzCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false);
|
||||
void SetDzCompass(const std::string& zone_name, float x, float y, float z, bool update_db = false);
|
||||
void SetDzSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false);
|
||||
void SetDzSafeReturn(const std::string& zone_name, float x, float y, float z, float heading, bool update_db = false);
|
||||
void SetDzZoneInLocation(float x, float y, float z, float heading, bool update_db = false);
|
||||
|
||||
static const uint32_t REPLAY_TIMER_ID;
|
||||
static const uint32_t EVENT_TIMER_ID;
|
||||
|
||||
private:
|
||||
void AddInternalLockout(ExpeditionLockoutTimer&& lockout_timer);
|
||||
void AddInternalMember(const std::string& char_name, uint32_t char_id, bool is_current_member = true, bool offline = false);
|
||||
void AddInternalMember(const std::string& char_name, uint32_t char_id, ExpeditionMemberStatus status, bool is_current_member = true);
|
||||
bool ChooseNewLeader();
|
||||
bool ConfirmLeaderCommand(Client* requester);
|
||||
void LoadMembers();
|
||||
@ -152,6 +145,7 @@ private:
|
||||
void SendClientExpeditionInvite(Client* client, const std::string& inviter_name, const std::string& swap_remove_name);
|
||||
void SendLeaderMessage(Client* leader_client, uint16_t chat_type, uint32_t string_id, const std::initializer_list<std::string>& parameters = {});
|
||||
void SendUpdatesToZoneMembers(bool clear = false);
|
||||
void SendWorldDzLocationUpdate(uint16_t server_opcode, const DynamicZoneLocation& location);
|
||||
void SendWorldExpeditionUpdate(bool destroyed = false);
|
||||
void SendWorldGetOnlineMembers();
|
||||
void SendWorldAddPlayerInvite(const std::string& inviter_name, const std::string& swap_remove_name, const std::string& add_name);
|
||||
@ -174,11 +168,11 @@ private:
|
||||
std::unique_ptr<EQApplicationPacket> CreateLeaderNamePacket();
|
||||
|
||||
uint32_t m_id = 0;
|
||||
//uint32_t m_instance_id = 0; // todo: DynamicZone m_dynamiczone
|
||||
uint32_t m_min_players = 0;
|
||||
uint32_t m_max_players = 0;
|
||||
bool m_has_replay_timer = false;
|
||||
std::string m_expedition_name;
|
||||
DynamicZone m_dynamiczone { DynamicZoneType::Expedition };
|
||||
ExpeditionMember m_leader;
|
||||
std::vector<ExpeditionMember> m_members; // current members
|
||||
std::unordered_set<uint32_t> m_member_id_history; // track past members to allow invites for replay timer bypass
|
||||
|
||||
@ -26,17 +26,17 @@
|
||||
#include <fmt/core.h>
|
||||
|
||||
uint32_t ExpeditionDatabase::InsertExpedition(
|
||||
const std::string& expedition_name, uint32_t leader_id,
|
||||
uint32_t instance_id, const std::string& expedition_name, uint32_t leader_id,
|
||||
uint32_t min_players, uint32_t max_players, bool has_replay_lockout)
|
||||
{
|
||||
LogExpeditionsDetail("Inserting new expedition [{}] leader [{}]", expedition_name, leader_id);
|
||||
|
||||
std::string query = fmt::format(SQL(
|
||||
INSERT INTO expedition_details
|
||||
(expedition_name, leader_id, min_players, max_players, has_replay_timer)
|
||||
(instance_id, expedition_name, leader_id, min_players, max_players, has_replay_timer)
|
||||
VALUES
|
||||
('{}', {}, {}, {}, {});
|
||||
), expedition_name, leader_id, min_players, max_players, has_replay_lockout);
|
||||
({}, '{}', {}, {}, {}, {});
|
||||
), instance_id, expedition_name, leader_id, min_players, max_players, has_replay_lockout);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
|
||||
@ -36,7 +36,7 @@ class MySQLRequestResult;
|
||||
namespace ExpeditionDatabase
|
||||
{
|
||||
uint32_t InsertExpedition(
|
||||
const std::string& expedition_name, uint32_t leader_id,
|
||||
uint32_t instance_id, const std::string& expedition_name, uint32_t leader_id,
|
||||
uint32_t min_players, uint32_t max_players, bool has_replay_lockout);
|
||||
MySQLRequestResult LoadExpedition(uint32_t expedition_id);
|
||||
MySQLRequestResult LoadAllExpeditions();
|
||||
|
||||
@ -38,10 +38,8 @@ struct ExpeditionRequestConflict
|
||||
};
|
||||
|
||||
ExpeditionRequest::ExpeditionRequest(
|
||||
Client* requester, std::string expedition_name, uint32_t min_players,
|
||||
uint32_t max_players, bool has_replay_timer
|
||||
std::string expedition_name, uint32_t min_players, uint32_t max_players, bool has_replay_timer
|
||||
) :
|
||||
m_requester(requester),
|
||||
m_expedition_name(expedition_name),
|
||||
m_min_players(min_players),
|
||||
m_max_players(max_players),
|
||||
@ -49,8 +47,9 @@ ExpeditionRequest::ExpeditionRequest(
|
||||
{
|
||||
}
|
||||
|
||||
bool ExpeditionRequest::Validate()
|
||||
bool ExpeditionRequest::Validate(Client* requester)
|
||||
{
|
||||
m_requester = requester;
|
||||
if (!m_requester)
|
||||
{
|
||||
return false;
|
||||
@ -353,7 +352,7 @@ bool ExpeditionRequest::IsPlayerCountValidated(uint32_t member_count)
|
||||
bool requirements_met = true;
|
||||
|
||||
auto bypass_status = RuleI(Expedition, MinStatusToBypassPlayerCountRequirements);
|
||||
auto gm_bypass = (m_requester->GetGM() && m_requester->Admin() >= bypass_status);
|
||||
auto gm_bypass = (m_requester && m_requester->GetGM() && m_requester->Admin() >= bypass_status);
|
||||
|
||||
if (!gm_bypass && (member_count < m_min_players || member_count > m_max_players))
|
||||
{
|
||||
|
||||
@ -37,14 +37,17 @@ struct ExpeditionMember;
|
||||
class ExpeditionRequest
|
||||
{
|
||||
public:
|
||||
ExpeditionRequest(Client* requester, std::string expedition_name,
|
||||
uint32_t min_players, uint32_t max_players, bool has_replay_timer);
|
||||
ExpeditionRequest(std::string expedition_name, uint32_t min_players, uint32_t max_players, bool has_replay_timer);
|
||||
|
||||
bool Validate();
|
||||
bool Validate(Client* requester);
|
||||
|
||||
const std::string& GetExpeditionName() const { return m_expedition_name; }
|
||||
Client* GetLeaderClient() const { return m_leader; }
|
||||
uint32_t GetLeaderID() const { return m_leader_id; }
|
||||
const std::string& GetLeaderName() const { return m_leader_name; }
|
||||
uint32_t GetMinPlayers() const { return m_min_players; }
|
||||
uint32_t GetMaxPlayers() const { return m_max_players; }
|
||||
bool HasReplayTimer() const { return m_has_replay_timer; }
|
||||
std::vector<ExpeditionMember> TakeMembers() && { return std::move(m_members); }
|
||||
std::unordered_map<std::string, ExpeditionLockoutTimer> TakeLockouts() && { return std::move(m_lockouts); }
|
||||
|
||||
|
||||
@ -1646,14 +1646,14 @@ int Lua_Client::GetClientMaxLevel() {
|
||||
return self->GetClientMaxLevel();
|
||||
}
|
||||
|
||||
Lua_Expedition Lua_Client::CreateExpedition(std::string name, uint32 min_players, uint32 max_players) {
|
||||
Lua_Expedition Lua_Client::CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players) {
|
||||
Lua_Safe_Call_Class(Lua_Expedition);
|
||||
return self->CreateExpedition(name, min_players, max_players);
|
||||
return self->CreateExpedition(zone_name, version, duration, expedition_name, min_players, max_players);
|
||||
}
|
||||
|
||||
Lua_Expedition Lua_Client::CreateExpedition(std::string name, uint32 min_players, uint32 max_players, bool has_replay_timer) {
|
||||
Lua_Expedition Lua_Client::CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool has_replay_timer) {
|
||||
Lua_Safe_Call_Class(Lua_Expedition);
|
||||
return self->CreateExpedition(name, min_players, max_players, has_replay_timer);
|
||||
return self->CreateExpedition(zone_name, version, duration, expedition_name, min_players, max_players, has_replay_timer);
|
||||
}
|
||||
|
||||
Lua_Expedition Lua_Client::GetExpedition() {
|
||||
@ -1717,6 +1717,16 @@ bool Lua_Client::HasExpeditionLockout(std::string expedition_name, std::string e
|
||||
return self->HasExpeditionLockout(expedition_name, event_name);
|
||||
}
|
||||
|
||||
void Lua_Client::MovePCDynamicZone(uint32 zone_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->MovePCDynamicZone(zone_id);
|
||||
}
|
||||
|
||||
void Lua_Client::MovePCDynamicZone(std::string zone_name) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->MovePCDynamicZone(zone_name);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_client() {
|
||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||
.def(luabind::constructor<>())
|
||||
@ -2024,14 +2034,16 @@ luabind::scope lua_register_client() {
|
||||
.def("DisableAreaRegens", &Lua_Client::DisableAreaRegens)
|
||||
.def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel)
|
||||
.def("GetClientMaxLevel", (int(Lua_Client::*)(void))&Lua_Client::GetClientMaxLevel)
|
||||
.def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32))&Lua_Client::CreateExpedition)
|
||||
.def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, bool))&Lua_Client::CreateExpedition)
|
||||
.def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32))&Lua_Client::CreateExpedition)
|
||||
.def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32, bool))&Lua_Client::CreateExpedition)
|
||||
.def("GetExpedition", (Lua_Expedition(Lua_Client::*)(void))&Lua_Client::GetExpedition)
|
||||
.def("GetExpeditionLockouts", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetExpeditionLockouts)
|
||||
.def("GetExpeditionLockouts", (luabind::object(Lua_Client::*)(lua_State* L, std::string))&Lua_Client::GetExpeditionLockouts)
|
||||
.def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32))&Lua_Client::AddExpeditionLockout)
|
||||
.def("RemoveExpeditionLockout", (void(Lua_Client::*)(std::string, std::string))&Lua_Client::RemoveExpeditionLockout)
|
||||
.def("HasExpeditionLockout", (bool(Lua_Client::*)(std::string, std::string))&Lua_Client::HasExpeditionLockout);
|
||||
.def("HasExpeditionLockout", (bool(Lua_Client::*)(std::string, std::string))&Lua_Client::HasExpeditionLockout)
|
||||
.def("MovePCDynamicZone", (void(Lua_Client::*)(uint32))&Lua_Client::MovePCDynamicZone)
|
||||
.def("MovePCDynamicZone", (void(Lua_Client::*)(std::string))&Lua_Client::MovePCDynamicZone);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_inventory_where() {
|
||||
|
||||
@ -339,14 +339,16 @@ public:
|
||||
void SetClientMaxLevel(int value);
|
||||
int GetClientMaxLevel();
|
||||
|
||||
Lua_Expedition CreateExpedition(std::string name, uint32 min_players, uint32 max_players);
|
||||
Lua_Expedition CreateExpedition(std::string name, uint32 min_players, uint32 max_players, bool has_replay_timer);
|
||||
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players);
|
||||
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool has_replay_timer);
|
||||
Lua_Expedition GetExpedition();
|
||||
luabind::object GetExpeditionLockouts(lua_State* L);
|
||||
luabind::object GetExpeditionLockouts(lua_State* L, std::string expedition_name);
|
||||
void AddExpeditionLockout(std::string expedition_name, std::string event_name, uint32 seconds);
|
||||
void RemoveExpeditionLockout(std::string expedition_name, std::string event_name);
|
||||
bool HasExpeditionLockout(std::string expedition_name, std::string event_name);
|
||||
void MovePCDynamicZone(uint32 zone_id);
|
||||
void MovePCDynamicZone(std::string zone_name);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -41,6 +41,11 @@ uint32_t Lua_Expedition::GetID() {
|
||||
return self->GetID();
|
||||
}
|
||||
|
||||
int Lua_Expedition::GetInstanceID() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetDynamicZone().GetInstanceID();
|
||||
}
|
||||
|
||||
std::string Lua_Expedition::GetLeaderName() {
|
||||
Lua_Safe_Call_String();
|
||||
return self->GetLeaderName();
|
||||
@ -85,9 +90,14 @@ std::string Lua_Expedition::GetName() {
|
||||
return self->GetName();
|
||||
}
|
||||
|
||||
int Lua_Expedition::GetType() {
|
||||
int Lua_Expedition::GetSecondsRemaining() {
|
||||
Lua_Safe_Call_Int();
|
||||
return static_cast<int>(self->GetType());
|
||||
return self->GetDynamicZone().GetSecondsRemaining();
|
||||
}
|
||||
|
||||
int Lua_Expedition::GetZoneID() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetDynamicZone().GetZoneID();
|
||||
}
|
||||
|
||||
bool Lua_Expedition::HasLockout(std::string event_name) {
|
||||
@ -105,6 +115,31 @@ void Lua_Expedition::RemoveLockout(std::string event_name) {
|
||||
self->RemoveLockout(event_name);
|
||||
}
|
||||
|
||||
void Lua_Expedition::SetCompass(uint32_t zone_id, float x, float y, float z) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetDzCompass(zone_id, x, y, z, true);
|
||||
}
|
||||
|
||||
void Lua_Expedition::SetCompass(std::string zone_name, float x, float y, float z) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetDzCompass(zone_name, x, y, z, true);
|
||||
}
|
||||
|
||||
void Lua_Expedition::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetDzSafeReturn(zone_id, x, y, z, heading, true);
|
||||
}
|
||||
|
||||
void Lua_Expedition::SetSafeReturn(std::string zone_name, float x, float y, float z, float heading) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetDzSafeReturn(zone_name, x, y, z, heading, true);
|
||||
}
|
||||
|
||||
void Lua_Expedition::SetZoneInLocation(float x, float y, float z, float heading) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetDzZoneInLocation(x, y, z, heading, true);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_expedition() {
|
||||
return luabind::class_<Lua_Expedition>("Expedition")
|
||||
.def(luabind::constructor<>())
|
||||
@ -113,15 +148,22 @@ luabind::scope lua_register_expedition() {
|
||||
.def("AddLockout", (void(Lua_Expedition::*)(std::string, uint32_t))&Lua_Expedition::AddLockout)
|
||||
.def("AddReplayLockout", (void(Lua_Expedition::*)(uint32_t))&Lua_Expedition::AddReplayLockout)
|
||||
.def("GetID", (uint32_t(Lua_Expedition::*)(void))&Lua_Expedition::GetID)
|
||||
.def("GetInstanceID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetInstanceID)
|
||||
.def("GetLeaderName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetLeaderName)
|
||||
.def("GetLockouts", &Lua_Expedition::GetLockouts)
|
||||
.def("GetMemberCount", (uint32_t(Lua_Expedition::*)(void))&Lua_Expedition::GetMemberCount)
|
||||
.def("GetMembers", &Lua_Expedition::GetMembers)
|
||||
.def("GetName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetName)
|
||||
.def("GetType", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetType)
|
||||
.def("GetSecondsRemaining", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetSecondsRemaining)
|
||||
.def("GetZoneID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetZoneID)
|
||||
.def("HasLockout", (bool(Lua_Expedition::*)(std::string))&Lua_Expedition::HasLockout)
|
||||
.def("HasReplayLockout", (bool(Lua_Expedition::*)())&Lua_Expedition::HasReplayLockout)
|
||||
.def("RemoveLockout", (void(Lua_Expedition::*)(std::string))&Lua_Expedition::RemoveLockout);
|
||||
.def("RemoveLockout", (void(Lua_Expedition::*)(std::string))&Lua_Expedition::RemoveLockout)
|
||||
.def("SetCompass", (void(Lua_Expedition::*)(uint32_t, float, float, float))&Lua_Expedition::SetCompass)
|
||||
.def("SetCompass", (void(Lua_Expedition::*)(std::string, float, float, float))&Lua_Expedition::SetCompass)
|
||||
.def("SetSafeReturn", (void(Lua_Expedition::*)(uint32_t, float, float, float, float))&Lua_Expedition::SetSafeReturn)
|
||||
.def("SetSafeReturn", (void(Lua_Expedition::*)(std::string, float, float, float, float))&Lua_Expedition::SetSafeReturn)
|
||||
.def("SetZoneInLocation", (void(Lua_Expedition::*)(float, float, float, float))&Lua_Expedition::SetZoneInLocation);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_expedition_member_status() {
|
||||
@ -136,17 +178,4 @@ luabind::scope lua_register_expedition_member_status() {
|
||||
];
|
||||
}
|
||||
|
||||
luabind::scope lua_register_dynamiczone_types() {
|
||||
return luabind::class_<DynamicZoneType>("DynamicZoneType")
|
||||
.enum_("constants")
|
||||
[
|
||||
luabind::value("None", static_cast<int>(DynamicZoneType::None)),
|
||||
luabind::value("Expedition", static_cast<int>(DynamicZoneType::Expedition)),
|
||||
luabind::value("Tutorial", static_cast<int>(DynamicZoneType::Tutorial)),
|
||||
luabind::value("Task", static_cast<int>(DynamicZoneType::Task)),
|
||||
luabind::value("Mission", static_cast<int>(DynamicZoneType::Mission)),
|
||||
luabind::value("Quest", static_cast<int>(DynamicZoneType::Quest))
|
||||
];
|
||||
}
|
||||
|
||||
#endif // LUA_EQEMU
|
||||
|
||||
@ -38,7 +38,6 @@ namespace luabind {
|
||||
using adl::object;
|
||||
}
|
||||
|
||||
luabind::scope lua_register_dynamiczone_types();
|
||||
luabind::scope lua_register_expedition();
|
||||
luabind::scope lua_register_expedition_member_status();
|
||||
|
||||
@ -57,15 +56,22 @@ public:
|
||||
void AddLockout(std::string event_name, uint32_t seconds);
|
||||
void AddReplayLockout(uint32_t seconds);
|
||||
uint32_t GetID();
|
||||
int GetInstanceID();
|
||||
std::string GetLeaderName();
|
||||
uint32_t GetMemberCount();
|
||||
luabind::object GetMembers(lua_State* L);
|
||||
std::string GetName();
|
||||
int GetType();
|
||||
int GetSecondsRemaining();
|
||||
int GetZoneID();
|
||||
luabind::object GetLockouts(lua_State* L);
|
||||
bool HasLockout(std::string event_name);
|
||||
bool HasReplayLockout();
|
||||
void RemoveLockout(std::string event_name);
|
||||
void SetCompass(uint32 zone_id, float x, float y, float z);
|
||||
void SetCompass(std::string zone_name, float x, float y, float z);
|
||||
void SetSafeReturn(uint32 zone_id, float x, float y, float z, float heading);
|
||||
void SetSafeReturn(std::string zone_name, float x, float y, float z, float heading);
|
||||
void SetZoneInLocation(float x, float y, float z, float heading);
|
||||
};
|
||||
|
||||
#endif // LUA_EQEMU
|
||||
|
||||
@ -1110,7 +1110,6 @@ void LuaParser::MapFunctions(lua_State *L) {
|
||||
lua_register_ruleb(),
|
||||
lua_register_journal_speakmode(),
|
||||
lua_register_journal_mode(),
|
||||
lua_register_dynamiczone_types(),
|
||||
lua_register_expedition(),
|
||||
lua_register_expedition_member_status()
|
||||
];
|
||||
|
||||
@ -299,6 +299,7 @@
|
||||
#define EXPEDITION_REPLAY_TIMER 3504 //%1 cannot be added to this expedition for another %2D:%3H:%4M since they have recently played in this area.
|
||||
#define EXPEDITION_AVAILABLE 3507 //%1 is now available to you.
|
||||
#define DZADD_INVITE 3508 //Sending an invitation to: %1.
|
||||
#define DZ_PREVENT_ENTERING 3510 //A strange magical presence prevents you from entering. It's too dangerous to enter at the moment.
|
||||
#define DZADD_INVITE_FAIL 3511 //%1 could not be invited to join you.
|
||||
#define UNABLE_RETRIEVE_LEADER 3512 //Unable to retrieve information on the leader to check permissions.
|
||||
#define EXPEDITION_NOT_LEADER 3513 //You are not the expedition leader, only %1 can issue this command.
|
||||
@ -314,6 +315,7 @@
|
||||
#define EXPEDITION_INVITE_ERROR 3524 //%1 accepted your offer to join your expedition but could not due to error(s).
|
||||
#define EXPEDITION_INVITE_DECLINED 3525 //%1 has declined your offer to join your expedition.
|
||||
#define EXPEDITION_ASKED_TO_JOIN 3527 //%1 has asked you to join the expedition: %2. Would you like to join?
|
||||
#define DYNAMICZONE_WAY_IS_BLOCKED 3528 //The way is blocked to you. Perhaps you would be able to enter if there was a reason to come here.
|
||||
#define EXPEDITION_NO_TIMERS 3529 //You have no outstanding timers.
|
||||
#define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end.
|
||||
#define EXPEDITION_LEADER 3552 //Expedition Leader: %1
|
||||
|
||||
@ -2909,10 +2909,18 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
case ServerOP_ExpeditionGetOnlineMembers:
|
||||
case ServerOP_ExpeditionDzAddPlayer:
|
||||
case ServerOP_ExpeditionDzMakeLeader:
|
||||
case ServerOP_ExpeditionDzCompass:
|
||||
case ServerOP_ExpeditionDzSafeReturn:
|
||||
case ServerOP_ExpeditionDzZoneIn:
|
||||
{
|
||||
Expedition::HandleWorldMessage(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_DzCharacterChange:
|
||||
{
|
||||
DynamicZone::HandleWorldMessage(pack);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
std::cout << " Unknown ZSopcode:" << (int)pack->opcode;
|
||||
std::cout << " size:" << pack->size << std::endl;
|
||||
|
||||
@ -1491,7 +1491,14 @@ bool Zone::Process() {
|
||||
{
|
||||
if(Instance_Timer->Check())
|
||||
{
|
||||
entity_list.GateAllClients();
|
||||
// if this is a dynamic zone instance notify system associated with it
|
||||
Expedition* expedition = Expedition::FindExpeditionByInstanceID(GetInstanceID());
|
||||
if (expedition)
|
||||
{
|
||||
expedition->RemoveAllMembers(false, false); // entity list will teleport clients out immediately
|
||||
}
|
||||
// todo: move corpses to non-instanced version of dz at same coords (if no graveyard)
|
||||
entity_list.GateAllClientsToSafeReturn();
|
||||
database.DeleteInstance(GetInstanceID());
|
||||
Instance_Shutdown_Timer = new Timer(20000); //20 seconds
|
||||
}
|
||||
|
||||
@ -406,4 +406,3 @@ private:
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user