Merge pull request #1163 from hgtw/feat/dz-expeditions

[Expedition / DZ] HGTW DZ / Expedition System
This commit is contained in:
Chris Miles 2020-12-30 21:16:22 -06:00 committed by GitHub
commit de5b7f472d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
99 changed files with 11569 additions and 702 deletions

View File

@ -493,6 +493,9 @@ void Database::DeleteInstance(uint16 instance_id)
query = StringFormat("DELETE FROM spawn_condition_values WHERE instance_id=%u", instance_id);
QueryDatabase(query);
query = fmt::format("DELETE FROM dynamic_zones WHERE instance_id={}", instance_id);
QueryDatabase(query);
BuryCorpsesInInstance(instance_id);
}
@ -582,8 +585,7 @@ void Database::PurgeExpiredInstances()
QueryDatabase(fmt::format("DELETE FROM respawn_times WHERE instance_id IN ({})", imploded_instance_ids));
QueryDatabase(fmt::format("DELETE FROM spawn_condition_values WHERE instance_id IN ({})", imploded_instance_ids));
QueryDatabase(fmt::format("UPDATE character_corpses SET is_buried = 1, instance_id = 0 WHERE instance_id IN ({})", imploded_instance_ids));
QueryDatabase(fmt::format("DELETE FROM dynamic_zones WHERE instance_id IN ({})", imploded_instance_ids));
}
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)

View File

@ -50,6 +50,7 @@ namespace DatabaseSchema {
{"character_data", "id"},
{"character_disciplines", "id"},
{"character_enabledtasks", "charid"},
{"character_expedition_lockouts", "character_id"},
{"character_inspect_messages", "id"},
{"character_item_recast", "id"},
{"character_languages", "id"},
@ -114,6 +115,7 @@ namespace DatabaseSchema {
"character_data",
"character_disciplines",
"character_enabledtasks",
"character_expedition_lockouts",
"character_inspect_messages",
"character_item_recast",
"character_languages",
@ -305,7 +307,11 @@ namespace DatabaseSchema {
"banned_ips",
"bug_reports",
"bugs",
"dynamic_zones",
"eventlog",
"expedition_lockouts",
"expedition_members",
"expeditions",
"gm_ips",
"group_id",
"group_leaders",

View File

@ -136,20 +136,22 @@ N(OP_Dye),
N(OP_DynamicWall),
N(OP_DzAddPlayer),
N(OP_DzChooseZone),
N(OP_DzChooseZoneReply),
N(OP_DzCompass),
N(OP_DzExpeditionEndsWarning),
N(OP_DzExpeditionInfo),
N(OP_DzExpeditionList),
N(OP_DzJoinExpeditionConfirm),
N(OP_DzJoinExpeditionReply),
N(OP_DzLeaderStatus),
N(OP_DzExpeditionInvite),
N(OP_DzExpeditionInviteResponse),
N(OP_DzExpeditionLockoutTimers),
N(OP_DzListTimers),
N(OP_DzMakeLeader),
N(OP_DzMemberList),
N(OP_DzMemberStatus),
N(OP_DzMemberListName),
N(OP_DzMemberListStatus),
N(OP_DzPlayerList),
N(OP_DzQuit),
N(OP_DzRemovePlayer),
N(OP_DzSetLeaderName),
N(OP_DzSwapPlayer),
N(OP_Emote),
N(OP_EndLootRequest),
@ -271,6 +273,7 @@ N(OP_ItemVerifyRequest),
N(OP_ItemViewUnknown),
N(OP_Jump),
N(OP_KeyRing),
N(OP_KickPlayers),
N(OP_KnowledgeBase),
N(OP_LDoNButton),
N(OP_LDoNDisarmTraps),

View File

@ -4835,17 +4835,98 @@ struct BuffIcon_Struct
BuffIconEntry_Struct entries[0];
};
struct ExpeditionInfo_Struct
struct ExpeditionInvite_Struct
{
/*000*/ uint32 max_players;
/*004*/ char expedition_name[128];
/*132*/ char leader_name[64];
/*000*/ uint32 client_id; // unique character id
/*004*/ uint32 unknown004; // added after titanium
/*008*/ char inviter_name[64];
/*072*/ char expedition_name[128];
/*200*/ uint8 swapping; // 0: adding 1: swapping
/*201*/ char swap_name[64]; // if swapping, swap name being removed
/*265*/ uint8 padding[3];
/*268*/ uint16 dz_zone_id; // dz_id zone/instance pair, sent back in reply
/*270*/ uint16 dz_instance_id;
};
struct ExpeditionJoinPrompt_Struct
struct ExpeditionInviteResponse_Struct
{
/*000*/ char player_name[64];
/*064*/ char expedition_name[64];
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004; // added after titanium
/*008*/ uint16 dz_zone_id; // dz_id pair sent in invite
/*010*/ uint16 dz_instance_id;
/*012*/ uint8 accepted; // 0: declined 1: accepted
/*013*/ uint8 swapping; // 0: adding 1: swapping (sent in invite)
/*014*/ char swap_name[64]; // swap name sent in invite
/*078*/ uint8 unknown078; // padding garbage?
/*079*/ uint8 unknown079; // padding garbage?
};
struct ExpeditionInfo_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004; // added after titanium
/*008*/ uint32 assigned; // padded bool, 0: not in expedition (clear data), 1: in expedition
/*012*/ uint32 max_players;
/*016*/ char expedition_name[128];
/*144*/ char leader_name[64];
};
struct ExpeditionMemberEntry_Struct
{
/*000*/ char name[64]; // variable length, null terminated, max 0x40 (64)
/*064*/ uint8 expedition_status; // 0: unknown, 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead
};
struct ExpeditionMemberList_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 member_count;
/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length
};
struct ExpeditionMemberListName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status
/*012*/ char name[64];
};
struct ExpeditionLockoutTimerEntry_Struct
{
/*000*/ char expedition_name[128]; // variable length, null terminated, max 0x80 (128)
/*000*/ uint32 seconds_remaining;
/*000*/ int32 event_type; // seen -1 (0xffffffff) for replay timers and 1 for event timers
/*000*/ char event_name[256]; // variable length, null terminated, max 0x100 (256)
};
struct ExpeditionLockoutTimers_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionLockoutTimerEntry_Struct timers[0];
};
struct ExpeditionSetLeaderName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ char leader_name[64];
};
struct ExpeditionCommand_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char name[64];
};
struct ExpeditionCommandSwap_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char add_player_name[64]; // swap to (player must confirm)
/*072*/ char rem_player_name[64]; // swap from
};
struct ExpeditionExpireWarning
@ -4853,48 +4934,67 @@ struct ExpeditionExpireWarning
/*008*/ uint32 minutes_remaining;
};
struct ExpeditionCompassEntry_Struct
struct DynamicZoneCompassEntry_Struct
{
/*000*/ uint32 enabled; //guess
/*004*/ float y;
/*008*/ float x;
/*012*/ float z;
/*000*/ uint16 dz_zone_id; // target dz id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 dz_type; // 1: Expedition, 2: Tutorial (purple), 3: Task, 4: Mission, 5: Quest (green)
/*008*/ uint32 unknown008;
/*012*/ float y;
/*016*/ float x;
/*020*/ float z;
};
struct ExpeditionCompass_Struct
struct DynamicZoneCompass_Struct
{
/*000*/ uint32 client_id;
/*000*/ uint32 count;
/*004*/ ExpeditionCompassEntry_Struct entries[0];
/*004*/ DynamicZoneCompassEntry_Struct entries[0];
};
struct ExpeditionMemberEntry_Struct
struct DynamicZoneChooseZoneEntry_Struct
{
char name[64];
char status;
/*000*/ uint16 dz_zone_id; // dz_id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 unknown_id1; // seen 28 00 00 00 (40), sent back in reply
/*008*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest -- sent back in reply
/*012*/ uint32 unknown_id2; // possibly an id based on dz type, for expeditions this was same as dz_id (zone|instance) but task dz was different
/*016*/ char description[128]; // variable length, null terminated
/*144*/ char leader_name[64]; // variable length, null terminated
};
struct ExpeditionMemberList_Struct
struct DynamicZoneChooseZone_Struct
{
/*000*/ uint32 count;
/*004*/ ExpeditionMemberEntry_Struct entries[0];
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ DynamicZoneChooseZoneEntry_Struct choices[0];
};
struct ExpeditionLockoutEntry_Struct
struct DynamicZoneChooseZoneReply_Struct
{
/*000*/ uint32 time_left;
/*004*/ char expedition[128];
/*132*/ char expedition_event[128];
/*000*/ uint32 unknown000; // ff ff ff ff
/*004*/ uint32 unknown004; // seen 69 00 00 00
/*008*/ uint32 unknown008; // ff ff ff ff
/*012*/ uint32 unknown_id1; // from choose zone entry message
/*016*/ uint16 dz_zone_id; // dz_id pair
/*018*/ uint16 dz_instance_id;
/*020*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest
/*024*/ uint32 unknown_id2; // from choose zone entry message
/*028*/ uint32 unknown028; // 00 00 00 00
/*032*/ uint32 unknown032; // always same as unknown044
/*036*/ uint32 unknown036;
/*040*/ uint32 unknown040;
/*044*/ uint32 unknown044; // always same as unknown032
/*048*/ uint32 unknown048; // seen 01 00 00 00 and 02 00 00 00
};
struct ExpeditionLockoutList_Struct
struct KickPlayers_Struct
{
/*000*/ uint32 count;
/*004*/ ExpeditionLockoutEntry_Struct entries[0];
};
struct ExpeditionLeaderSet_Struct
{
/*000*/ char leader_name[64];
/*000*/ char char_name[64];
/*064*/ uint32 unknown064; // always 0
/*068*/ uint8 kick_expedition; // true if /kickplayers exp
/*069*/ uint8 kick_task; // true if /kickplayers task
/*070*/ uint8 padding[2];
};
struct CorpseDrag_Struct

View File

@ -118,6 +118,8 @@ namespace Logs {
Merchants,
ZonePoints,
Loot,
Expeditions,
DynamicZones,
MaxCategoryID /* Don't Remove this */
};
@ -194,7 +196,9 @@ namespace Logs {
"HotReload",
"Merchants",
"ZonePoints",
"Loot"
"Loot",
"Expeditions",
"DynamicZones",
};
}

View File

@ -601,6 +601,31 @@
OutF(LogSys, Logs::Detail, Logs::Loot, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogExpeditions(message, ...) do {\
if (LogSys.log_settings[Logs::Expeditions].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Expeditions, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogExpeditionsModerate(message, ...) do {\
if (LogSys.log_settings[Logs::Expeditions].is_category_enabled == 1)\
OutF(LogSys, Logs::Moderate, Logs::Expeditions, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogExpeditionsDetail(message, ...) do {\
if (LogSys.log_settings[Logs::Expeditions].is_category_enabled == 1)\
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__);\
@ -952,6 +977,21 @@
#define LogZonePointsDetail(message, ...) do {\
} while (0)
#define LogExpeditions(message, ...) do {\
} while (0)
#define LogExpeditionsModerate(message, ...) do {\
} while (0)
#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)

View File

@ -710,15 +710,48 @@ namespace RoF
FINISH_ENCODE();
}
ENCODE(OP_DzChooseZone)
{
SETUP_VAR_ENCODE(DynamicZoneChooseZone_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
buf.WriteUInt16(emu->choices[i].dz_zone_id);
buf.WriteUInt16(emu->choices[i].dz_instance_id);
buf.WriteUInt32(emu->choices[i].unknown_id1);
buf.WriteUInt32(emu->choices[i].dz_type);
buf.WriteUInt32(emu->choices[i].unknown_id2);
buf.WriteString(emu->choices[i].description);
buf.WriteString(emu->choices[i].leader_name);
}
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzCompass)
{
SETUP_VAR_ENCODE(ExpeditionCompass_Struct);
ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count);
SETUP_VAR_ENCODE(DynamicZoneCompass_Struct);
ALLOC_VAR_ENCODE(structs::DynamicZoneCompass_Struct,
sizeof(structs::DynamicZoneCompass_Struct) +
sizeof(structs::DynamicZoneCompassEntry_Struct) * emu->count
);
OUT(client_id);
OUT(count);
for (uint32 i = 0; i < emu->count; ++i)
{
OUT(entries[i].dz_zone_id);
OUT(entries[i].dz_instance_id);
OUT(entries[i].dz_type);
OUT(entries[i].x);
OUT(entries[i].y);
OUT(entries[i].z);
@ -742,81 +775,60 @@ namespace RoF
ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct);
OUT(client_id);
OUT(assigned);
OUT(max_players);
eq->unknown004 = 785316192;
eq->unknown008 = 435601;
strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strncpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionList)
ENCODE(OP_DzExpeditionInvite)
{
SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct);
ENCODE_LENGTH_EXACT(ExpeditionInvite_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInvite_Struct, structs::ExpeditionInvite_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
OUT(client_id);
strn0cpy(eq->inviter_name, emu->inviter_name, sizeof(eq->inviter_name));
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
OUT(swapping);
strn0cpy(eq->swap_name, emu->swap_name, sizeof(eq->swap_name));
OUT(dz_zone_id);
OUT(dz_instance_id);
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionLockoutTimers)
{
SETUP_VAR_ENCODE(ExpeditionLockoutTimers_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event));
ss.write((const char*)&null_term, sizeof(char));
buf.WriteString(emu->timers[i].expedition_name);
buf.WriteUInt32(emu->timers[i].seconds_remaining);
buf.WriteInt32(emu->timers[i].event_type);
buf.WriteString(emu->timers[i].event_name);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzJoinExpeditionConfirm)
ENCODE(OP_DzSetLeaderName)
{
ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct);
SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct);
ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct);
strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strncpy(eq->player_name, emu->player_name, sizeof(eq->player_name));
FINISH_ENCODE();
}
ENCODE(OP_DzLeaderStatus)
{
SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->leader_name, strlen(emu->leader_name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//1
ss.write((const char*)&client_id, sizeof(uint32));
__packet->size = ss.str().length();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
OUT(client_id);
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
@ -825,26 +837,43 @@ namespace RoF
{
SETUP_VAR_ENCODE(ExpeditionMemberList_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
for (uint32 i = 0; i < emu->count; ++i)
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->member_count);
for (uint32 i = 0; i < emu->member_count; ++i)
{
ss.write(emu->entries[i].name, strlen(emu->entries[i].name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].status, sizeof(char));
buf.WriteString(emu->members[i].name);
buf.WriteUInt8(emu->members[i].expedition_status);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListName)
{
ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct);
OUT(client_id);
OUT(add_name);
strn0cpy(eq->name, emu->name, sizeof(eq->name));
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListStatus)
{
auto emu = reinterpret_cast<ExpeditionMemberList_Struct*>((*p)->pBuffer);
if (emu->member_count == 1)
{
ENCODE_FORWARD(OP_DzMemberList);
}
}
ENCODE(OP_Emote)
{
EQApplicationPacket *in = *p;
@ -4388,6 +4417,84 @@ namespace RoF
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzAddPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzChooseZoneReply)
{
DECODE_LENGTH_EXACT(structs::DynamicZoneChooseZoneReply_Struct);
SETUP_DIRECT_DECODE(DynamicZoneChooseZoneReply_Struct, structs::DynamicZoneChooseZoneReply_Struct);
IN(unknown000);
IN(unknown004);
IN(unknown008);
IN(unknown_id1);
IN(dz_zone_id);
IN(dz_instance_id);
IN(dz_type);
IN(unknown_id2);
IN(unknown028);
IN(unknown032);
IN(unknown036);
IN(unknown040);
IN(unknown044);
IN(unknown048);
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzExpeditionInviteResponse)
{
DECODE_LENGTH_EXACT(structs::ExpeditionInviteResponse_Struct);
SETUP_DIRECT_DECODE(ExpeditionInviteResponse_Struct, structs::ExpeditionInviteResponse_Struct);
IN(dz_zone_id);
IN(dz_instance_id);
IN(accepted);
IN(swapping);
strn0cpy(emu->swap_name, eq->swap_name, sizeof(emu->swap_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzMakeLeader)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzRemovePlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzSwapPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommandSwap_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommandSwap_Struct, structs::ExpeditionCommandSwap_Struct);
strn0cpy(emu->add_player_name, eq->add_player_name, sizeof(emu->add_player_name));
strn0cpy(emu->rem_player_name, eq->rem_player_name, sizeof(emu->rem_player_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_Emote)
{
unsigned char *__eq_buffer = __packet->pBuffer;

View File

@ -759,15 +759,48 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_DzChooseZone)
{
SETUP_VAR_ENCODE(DynamicZoneChooseZone_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
buf.WriteUInt16(emu->choices[i].dz_zone_id);
buf.WriteUInt16(emu->choices[i].dz_instance_id);
buf.WriteUInt32(emu->choices[i].unknown_id1);
buf.WriteUInt32(emu->choices[i].dz_type);
buf.WriteUInt32(emu->choices[i].unknown_id2);
buf.WriteString(emu->choices[i].description);
buf.WriteString(emu->choices[i].leader_name);
}
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzCompass)
{
SETUP_VAR_ENCODE(ExpeditionCompass_Struct);
ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count);
SETUP_VAR_ENCODE(DynamicZoneCompass_Struct);
ALLOC_VAR_ENCODE(structs::DynamicZoneCompass_Struct,
sizeof(structs::DynamicZoneCompass_Struct) +
sizeof(structs::DynamicZoneCompassEntry_Struct) * emu->count
);
OUT(client_id);
OUT(count);
for (uint32 i = 0; i < emu->count; ++i)
{
OUT(entries[i].dz_zone_id);
OUT(entries[i].dz_instance_id);
OUT(entries[i].dz_type);
OUT(entries[i].x);
OUT(entries[i].y);
OUT(entries[i].z);
@ -791,81 +824,60 @@ namespace RoF2
ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct);
OUT(client_id);
OUT(assigned);
OUT(max_players);
eq->unknown004 = 785316192;
eq->unknown008 = 435601;
strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strncpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionList)
ENCODE(OP_DzExpeditionInvite)
{
SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct);
ENCODE_LENGTH_EXACT(ExpeditionInvite_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInvite_Struct, structs::ExpeditionInvite_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
OUT(client_id);
strn0cpy(eq->inviter_name, emu->inviter_name, sizeof(eq->inviter_name));
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
OUT(swapping);
strn0cpy(eq->swap_name, emu->swap_name, sizeof(eq->swap_name));
OUT(dz_zone_id);
OUT(dz_instance_id);
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionLockoutTimers)
{
SETUP_VAR_ENCODE(ExpeditionLockoutTimers_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event));
ss.write((const char*)&null_term, sizeof(char));
buf.WriteString(emu->timers[i].expedition_name);
buf.WriteUInt32(emu->timers[i].seconds_remaining);
buf.WriteInt32(emu->timers[i].event_type);
buf.WriteString(emu->timers[i].event_name);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzJoinExpeditionConfirm)
ENCODE(OP_DzSetLeaderName)
{
ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct);
SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct);
ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct);
strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strncpy(eq->player_name, emu->player_name, sizeof(eq->player_name));
FINISH_ENCODE();
}
ENCODE(OP_DzLeaderStatus)
{
SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->leader_name, strlen(emu->leader_name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//1
ss.write((const char*)&client_id, sizeof(uint32));
__packet->size = ss.str().length();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
OUT(client_id);
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
@ -874,26 +886,43 @@ namespace RoF2
{
SETUP_VAR_ENCODE(ExpeditionMemberList_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
for (uint32 i = 0; i < emu->count; ++i)
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->member_count);
for (uint32 i = 0; i < emu->member_count; ++i)
{
ss.write(emu->entries[i].name, strlen(emu->entries[i].name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].status, sizeof(char));
buf.WriteString(emu->members[i].name);
buf.WriteUInt8(emu->members[i].expedition_status);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListName)
{
ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct);
OUT(client_id);
OUT(add_name);
strn0cpy(eq->name, emu->name, sizeof(eq->name));
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListStatus)
{
auto emu = reinterpret_cast<ExpeditionMemberList_Struct*>((*p)->pBuffer);
if (emu->member_count == 1)
{
ENCODE_FORWARD(OP_DzMemberList);
}
}
ENCODE(OP_Emote)
{
EQApplicationPacket *in = *p;
@ -4585,6 +4614,84 @@ namespace RoF2
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzAddPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzChooseZoneReply)
{
DECODE_LENGTH_EXACT(structs::DynamicZoneChooseZoneReply_Struct);
SETUP_DIRECT_DECODE(DynamicZoneChooseZoneReply_Struct, structs::DynamicZoneChooseZoneReply_Struct);
IN(unknown000);
IN(unknown004);
IN(unknown008);
IN(unknown_id1);
IN(dz_zone_id);
IN(dz_instance_id);
IN(dz_type);
IN(unknown_id2);
IN(unknown028);
IN(unknown032);
IN(unknown036);
IN(unknown040);
IN(unknown044);
IN(unknown048);
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzExpeditionInviteResponse)
{
DECODE_LENGTH_EXACT(structs::ExpeditionInviteResponse_Struct);
SETUP_DIRECT_DECODE(ExpeditionInviteResponse_Struct, structs::ExpeditionInviteResponse_Struct);
IN(dz_zone_id);
IN(dz_instance_id);
IN(accepted);
IN(swapping);
strn0cpy(emu->swap_name, eq->swap_name, sizeof(emu->swap_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzMakeLeader)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzRemovePlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzSwapPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommandSwap_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommandSwap_Struct, structs::ExpeditionCommandSwap_Struct);
strn0cpy(emu->add_player_name, eq->add_player_name, sizeof(emu->add_player_name));
strn0cpy(emu->rem_player_name, eq->rem_player_name, sizeof(emu->rem_player_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_Emote)
{
unsigned char *__eq_buffer = __packet->pBuffer;

View File

@ -58,13 +58,16 @@ E(OP_DeleteCharge)
E(OP_DeleteItem)
E(OP_DeleteSpawn)
E(OP_DisciplineUpdate)
E(OP_DzChooseZone)
E(OP_DzCompass)
E(OP_DzExpeditionEndsWarning)
E(OP_DzExpeditionInfo)
E(OP_DzExpeditionList)
E(OP_DzJoinExpeditionConfirm)
E(OP_DzLeaderStatus)
E(OP_DzExpeditionInvite)
E(OP_DzExpeditionLockoutTimers)
E(OP_DzMemberList)
E(OP_DzMemberListName)
E(OP_DzMemberListStatus)
E(OP_DzSetLeaderName)
E(OP_Emote)
E(OP_ExpansionInfo)
E(OP_FormattedMessage)
@ -159,6 +162,12 @@ D(OP_ConsiderCorpse)
D(OP_Consume)
D(OP_Damage)
D(OP_DeleteItem)
D(OP_DzAddPlayer)
D(OP_DzChooseZoneReply)
D(OP_DzExpeditionInviteResponse)
D(OP_DzMakeLeader)
D(OP_DzRemovePlayer)
D(OP_DzSwapPlayer)
D(OP_Emote)
D(OP_EnvDamage)
D(OP_FaceChange)

View File

@ -4882,52 +4882,169 @@ struct VeteranClaim
/*076*/ uint32 action;
};
struct ExpeditionEntryHeader_Struct
struct ExpeditionInvite_Struct
{
/*000*/ uint32 client_id; // unique character id
/*004*/ uint32 unknown004;
/*008*/ char inviter_name[64];
/*072*/ char expedition_name[128];
/*200*/ uint8 swapping; // 0: adding 1: swapping
/*201*/ char swap_name[64]; // if swapping, swap name being removed
/*265*/ uint8 padding[3];
/*268*/ uint16 dz_zone_id; // dz_id zone/instance pair, sent back in reply
/*270*/ uint16 dz_instance_id;
};
struct ExpeditionInviteResponse_Struct
{
/*000*/ uint32 unknown000;
/*000*/ uint32 number_of_entries;
};
struct ExpeditionJoinPrompt_Struct
{
/*000*/ uint32 clientid;
/*004*/ uint32 unknown004;
/*008*/ char player_name[64];
/*072*/ char expedition_name[64];
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 clientid;
/*004*/ uint32 unknown004;
/*008*/ uint32 minutes_remaining;
/*008*/ uint16 dz_zone_id; // dz_id pair sent in invite
/*010*/ uint16 dz_instance_id;
/*012*/ uint8 accepted; // 0: declined 1: accepted
/*013*/ uint8 swapping; // 0: adding 1: swapping (sent in invite)
/*014*/ char swap_name[64]; // swap name sent in invite
/*078*/ uint8 unknown078; // padding garbage?
/*079*/ uint8 unknown079; // padding garbage?
};
struct ExpeditionInfo_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 unknown008;
/*008*/ uint32 assigned; // padded bool, 0: not in expedition (clear data), 1: in expedition
/*012*/ uint32 max_players;
/*016*/ char expedition_name[128];
/*142*/ char leader_name[64];
/*016*/ char expedition_name[128];
/*144*/ char leader_name[64];
//*208*/ uint32 unknown208; // live sends 01 00 00 00 here but client doesn't read it
};
struct ExpeditionCompassEntry_Struct
struct ExpeditionMemberEntry_Struct
{
/*000*/ float unknown000; //seen *((uint32*)) = 1584791871
/*004*/ uint32 enabled; //guess
/*008*/ uint32 unknown008; //seen 1019
/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64)
/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead
};
struct ExpeditionMemberList_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 member_count; // number of players in window
/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length
};
struct ExpeditionMemberListName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status
/*012*/ char name[64];
};
struct ExpeditionLockoutTimerEntry_Struct
{
/*000*/ char expedition_name[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ uint32 seconds_remaining;
/*000*/ int32 event_type; // seen -1 (0xffffffff) for replay timers and 1 for event timers
/*000*/ char event_name[1]; // variable length, null terminated, max 0x100 (256)
};
struct ExpeditionLockoutTimers_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionLockoutTimerEntry_Struct timers[0];
};
struct ExpeditionSetLeaderName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ char leader_name[64];
};
struct ExpeditionCommand_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char name[64];
};
struct ExpeditionCommandSwap_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char add_player_name[64]; // swap to (player must confirm)
/*072*/ char rem_player_name[64]; // swap from
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 minutes_remaining;
};
struct DynamicZoneCompassEntry_Struct
{
/*000*/ uint16 dz_zone_id; // target dz id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 dz_type; // 1: Expedition, 2: Tutorial (purple), 3: Task, 4: Mission, 5: Quest (green)
/*008*/ uint32 unknown008;
/*012*/ float y;
/*016*/ float x;
/*020*/ float z;
};
struct ExpeditionCompass_Struct
struct DynamicZoneCompass_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionCompassEntry_Struct entries[0];
/*008*/ DynamicZoneCompassEntry_Struct entries[0];
};
struct DynamicZoneChooseZoneEntry_Struct
{
/*000*/ uint16 dz_zone_id; // dz_id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 unknown_id1; // seen 28 00 00 00 (40), sent back in reply
/*008*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest -- sent back in reply
/*012*/ uint32 unknown_id2; // possibly an id based on dz type, for expeditions this was same as dz_id (zone|instance) but task dz was different
/*016*/ char description[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ char leader_name[1]; // variable length, null terminated, max 0x40 (64)
};
struct DynamicZoneChooseZone_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ DynamicZoneChooseZoneEntry_Struct choices[0];
};
struct DynamicZoneChooseZoneReply_Struct
{
/*000*/ uint32 unknown000; // ff ff ff ff
/*004*/ uint32 unknown004; // seen 69 00 00 00
/*008*/ uint32 unknown008; // ff ff ff ff
/*012*/ uint32 unknown_id1; // from choose zone entry message
/*016*/ uint16 dz_zone_id; // dz_id pair
/*018*/ uint16 dz_instance_id;
/*020*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest
/*024*/ uint32 unknown_id2; // from choose zone entry message
/*028*/ uint32 unknown028; // 00 00 00 00
/*032*/ uint32 unknown032; // always same as unknown044
/*036*/ uint32 unknown036;
/*040*/ uint32 unknown040;
/*044*/ uint32 unknown044; // always same as unknown032
/*048*/ uint32 unknown048; // seen 01 00 00 00 and 02 00 00 00
};
struct KickPlayers_Struct
{
/*000*/ char char_name[64];
/*064*/ uint32 unknown064; // always 0
/*068*/ uint8 kick_expedition; // true if /kickplayers exp
/*069*/ uint8 kick_task; // true if /kickplayers task
/*070*/ uint8 padding[2];
};
struct MaxCharacters_Struct

View File

@ -44,13 +44,16 @@ E(OP_DeleteCharge)
E(OP_DeleteItem)
E(OP_DeleteSpawn)
E(OP_DisciplineUpdate)
E(OP_DzChooseZone)
E(OP_DzCompass)
E(OP_DzExpeditionEndsWarning)
E(OP_DzExpeditionInfo)
E(OP_DzExpeditionList)
E(OP_DzJoinExpeditionConfirm)
E(OP_DzLeaderStatus)
E(OP_DzExpeditionInvite)
E(OP_DzExpeditionLockoutTimers)
E(OP_DzMemberList)
E(OP_DzMemberListName)
E(OP_DzMemberListStatus)
E(OP_DzSetLeaderName)
E(OP_Emote)
E(OP_ExpansionInfo)
E(OP_FormattedMessage)
@ -145,6 +148,12 @@ D(OP_ConsiderCorpse)
D(OP_Consume)
D(OP_Damage)
D(OP_DeleteItem)
D(OP_DzAddPlayer)
D(OP_DzChooseZoneReply)
D(OP_DzExpeditionInviteResponse)
D(OP_DzMakeLeader)
D(OP_DzRemovePlayer)
D(OP_DzSwapPlayer)
D(OP_Emote)
D(OP_EnvDamage)
D(OP_FaceChange)

View File

@ -4815,52 +4815,159 @@ struct VeteranClaim
/*076*/ uint32 action;
};
struct ExpeditionEntryHeader_Struct
struct ExpeditionInvite_Struct
{
/*000*/ uint32 client_id; // unique character id
/*004*/ uint32 unknown004;
/*008*/ char inviter_name[64];
/*072*/ char expedition_name[128];
/*200*/ uint8 swapping; // 0: adding 1: swapping
/*201*/ char swap_name[64]; // if swapping, swap name being removed
/*265*/ uint8 padding[3];
/*268*/ uint16 dz_zone_id; // dz_id zone/instance pair, sent back in reply
/*270*/ uint16 dz_instance_id;
};
struct ExpeditionInviteResponse_Struct
{
/*000*/ uint32 unknown000;
/*000*/ uint32 number_of_entries;
};
struct ExpeditionJoinPrompt_Struct
{
/*000*/ uint32 clientid;
/*004*/ uint32 unknown004;
/*008*/ char player_name[64];
/*072*/ char expedition_name[64];
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 clientid;
/*004*/ uint32 unknown004;
/*008*/ uint32 minutes_remaining;
/*008*/ uint16 dz_zone_id; // dz_id pair sent in invite
/*010*/ uint16 dz_instance_id;
/*012*/ uint8 accepted; // 0: declined 1: accepted
/*013*/ uint8 swapping; // 0: adding 1: swapping (sent in invite)
/*014*/ char swap_name[64]; // swap name sent in invite
/*078*/ uint8 unknown078; // padding garbage?
/*079*/ uint8 unknown079; // padding garbage?
};
struct ExpeditionInfo_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 unknown008;
/*008*/ uint32 assigned; // padded bool
/*012*/ uint32 max_players;
/*016*/ char expedition_name[128];
/*142*/ char leader_name[64];
/*016*/ char expedition_name[128];
/*144*/ char leader_name[64];
};
struct ExpeditionCompassEntry_Struct
struct ExpeditionMemberEntry_Struct
{
/*000*/ float unknown000; //seen *((uint32*)) = 1584791871
/*004*/ uint32 enabled; //guess
/*008*/ uint32 unknown008; //seen 1019
/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64)
/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead
};
struct ExpeditionMemberList_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 member_count; // number of players in window
/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length
};
struct ExpeditionMemberListName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status
/*012*/ char name[64];
};
struct ExpeditionLockoutTimerEntry_Struct
{
/*000*/ char expedition_name[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ uint32 seconds_remaining;
/*000*/ int32 event_type; // seen -1 (0xffffffff) for replay timers and 1 for event timers
/*000*/ char event_name[1]; // variable length, null terminated, max 0x100 (256)
};
struct ExpeditionLockoutTimers_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionLockoutTimerEntry_Struct timers[0];
};
struct ExpeditionSetLeaderName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ char leader_name[64];
};
struct ExpeditionCommand_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char name[64];
};
struct ExpeditionCommandSwap_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char add_player_name[64]; // swap to (player must confirm)
/*072*/ char rem_player_name[64]; // swap from
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 minutes_remaining;
};
struct DynamicZoneCompassEntry_Struct
{
/*000*/ uint16 dz_zone_id; // target dz id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 dz_type; // 1: Expedition, 2: Tutorial (purple), 3: Task, 4: Mission, 5: Quest (green)
/*008*/ uint32 unknown008;
/*012*/ float y;
/*016*/ float x;
/*020*/ float z;
};
struct ExpeditionCompass_Struct
struct DynamicZoneCompass_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionCompassEntry_Struct entries[0];
/*008*/ DynamicZoneCompassEntry_Struct entries[0];
};
struct DynamicZoneChooseZoneEntry_Struct
{
/*000*/ uint16 dz_zone_id; // dz_id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 unknown_id1; // sent back in reply
/*008*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest -- sent back in reply
/*012*/ uint32 unknown_id2; // possibly an id based on dz type, for expeditions this was same as dz_id (zone|instance) but task dz was different
/*016*/ char description[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ char leader_name[1]; // variable length, null terminated, max 0x40 (64)
};
struct DynamicZoneChooseZone_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ DynamicZoneChooseZoneEntry_Struct choices[0];
};
struct DynamicZoneChooseZoneReply_Struct
{
/*000*/ uint32 unknown000; // ff ff ff ff
/*004*/ uint32 unknown004; // seen 69 00 00 00
/*008*/ uint32 unknown008; // ff ff ff ff
/*012*/ uint32 unknown_id1; // from choose zone entry message
/*016*/ uint16 dz_zone_id; // dz_id pair
/*018*/ uint16 dz_instance_id;
/*020*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest
/*024*/ uint32 unknown_id2; // from choose zone entry message
/*028*/ uint32 unknown028; // 00 00 00 00
/*032*/ uint32 unknown032; // always same as unknown044
/*036*/ uint32 unknown036;
/*040*/ uint32 unknown040;
/*044*/ uint32 unknown044; // always same as unknown032
/*048*/ uint32 unknown048; // seen 01 00 00 00 and 02 00 00 00
};
struct MaxCharacters_Struct

View File

@ -483,15 +483,48 @@ namespace SoD
FINISH_ENCODE();
}
ENCODE(OP_DzChooseZone)
{
SETUP_VAR_ENCODE(DynamicZoneChooseZone_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
buf.WriteUInt16(emu->choices[i].dz_zone_id);
buf.WriteUInt16(emu->choices[i].dz_instance_id);
buf.WriteUInt32(emu->choices[i].unknown_id1);
buf.WriteUInt32(emu->choices[i].dz_type);
buf.WriteUInt32(emu->choices[i].unknown_id2);
buf.WriteString(emu->choices[i].description);
buf.WriteString(emu->choices[i].leader_name);
}
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzCompass)
{
SETUP_VAR_ENCODE(ExpeditionCompass_Struct);
ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count);
SETUP_VAR_ENCODE(DynamicZoneCompass_Struct);
ALLOC_VAR_ENCODE(structs::DynamicZoneCompass_Struct,
sizeof(structs::DynamicZoneCompass_Struct) +
sizeof(structs::DynamicZoneCompassEntry_Struct) * emu->count
);
OUT(client_id);
OUT(count);
for (uint32 i = 0; i < emu->count; ++i)
{
OUT(entries[i].dz_zone_id);
OUT(entries[i].dz_instance_id);
OUT(entries[i].dz_type);
OUT(entries[i].x);
OUT(entries[i].y);
OUT(entries[i].z);
@ -515,81 +548,60 @@ namespace SoD
ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct);
OUT(client_id);
OUT(assigned);
OUT(max_players);
eq->unknown004 = 785316192;
eq->unknown008 = 435601;
strcpy(eq->expedition_name, emu->expedition_name);
strcpy(eq->leader_name, emu->leader_name);
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionList)
ENCODE(OP_DzExpeditionInvite)
{
SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct);
ENCODE_LENGTH_EXACT(ExpeditionInvite_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInvite_Struct, structs::ExpeditionInvite_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
OUT(client_id);
strn0cpy(eq->inviter_name, emu->inviter_name, sizeof(eq->inviter_name));
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
OUT(swapping);
strn0cpy(eq->swap_name, emu->swap_name, sizeof(eq->swap_name));
OUT(dz_zone_id);
OUT(dz_instance_id);
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionLockoutTimers)
{
SETUP_VAR_ENCODE(ExpeditionLockoutTimers_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event));
ss.write((const char*)&null_term, sizeof(char));
buf.WriteString(emu->timers[i].expedition_name);
buf.WriteUInt32(emu->timers[i].seconds_remaining);
buf.WriteInt32(emu->timers[i].event_type);
buf.WriteString(emu->timers[i].event_name);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzJoinExpeditionConfirm)
ENCODE(OP_DzSetLeaderName)
{
ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct);
SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct);
ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct);
strcpy(eq->expedition_name, emu->expedition_name);
strcpy(eq->player_name, emu->player_name);
FINISH_ENCODE();
}
ENCODE(OP_DzLeaderStatus)
{
SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->leader_name, strlen(emu->leader_name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//1
ss.write((const char*)&client_id, sizeof(uint32));
__packet->size = ss.str().length();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
OUT(client_id);
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
@ -598,26 +610,44 @@ namespace SoD
{
SETUP_VAR_ENCODE(ExpeditionMemberList_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
for (uint32 i = 0; i < emu->count; ++i)
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->member_count);
for (uint32 i = 0; i < emu->member_count; ++i)
{
ss.write(emu->entries[i].name, strlen(emu->entries[i].name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].status, sizeof(char));
buf.WriteString(emu->members[i].name);
buf.WriteUInt8(emu->members[i].expedition_status);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListName)
{
ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct);
OUT(client_id);
OUT(add_name);
strn0cpy(eq->name, emu->name, sizeof(eq->name));
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListStatus)
{
auto emu = reinterpret_cast<ExpeditionMemberList_Struct*>((*p)->pBuffer);
if (emu->member_count == 1)
{
ENCODE_FORWARD(OP_DzMemberList);
}
}
ENCODE(OP_Emote)
{
EQApplicationPacket *in = *p;
@ -2974,6 +3004,84 @@ namespace SoD
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzAddPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzChooseZoneReply)
{
DECODE_LENGTH_EXACT(structs::DynamicZoneChooseZoneReply_Struct);
SETUP_DIRECT_DECODE(DynamicZoneChooseZoneReply_Struct, structs::DynamicZoneChooseZoneReply_Struct);
IN(unknown000);
IN(unknown004);
IN(unknown008);
IN(unknown_id1);
IN(dz_zone_id);
IN(dz_instance_id);
IN(dz_type);
IN(unknown_id2);
IN(unknown028);
IN(unknown032);
IN(unknown036);
IN(unknown040);
IN(unknown044);
IN(unknown048);
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzExpeditionInviteResponse)
{
DECODE_LENGTH_EXACT(structs::ExpeditionInviteResponse_Struct);
SETUP_DIRECT_DECODE(ExpeditionInviteResponse_Struct, structs::ExpeditionInviteResponse_Struct);
IN(dz_zone_id);
IN(dz_instance_id);
IN(accepted);
IN(swapping);
strn0cpy(emu->swap_name, eq->swap_name, sizeof(emu->swap_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzMakeLeader)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzRemovePlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzSwapPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommandSwap_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommandSwap_Struct, structs::ExpeditionCommandSwap_Struct);
strn0cpy(emu->add_player_name, eq->add_player_name, sizeof(emu->add_player_name));
strn0cpy(emu->rem_player_name, eq->rem_player_name, sizeof(emu->rem_player_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_Emote)
{
unsigned char *__eq_buffer = __packet->pBuffer;

View File

@ -35,13 +35,16 @@ E(OP_Consider)
E(OP_Damage)
E(OP_DeleteCharge)
E(OP_DeleteItem)
E(OP_DzChooseZone)
E(OP_DzCompass)
E(OP_DzExpeditionEndsWarning)
E(OP_DzExpeditionInfo)
E(OP_DzExpeditionList)
E(OP_DzJoinExpeditionConfirm)
E(OP_DzLeaderStatus)
E(OP_DzExpeditionInvite)
E(OP_DzExpeditionLockoutTimers)
E(OP_DzMemberList)
E(OP_DzMemberListName)
E(OP_DzMemberListStatus)
E(OP_DzSetLeaderName)
E(OP_Emote)
E(OP_ExpansionInfo)
E(OP_FormattedMessage)
@ -111,6 +114,12 @@ D(OP_Consider)
D(OP_ConsiderCorpse)
D(OP_Consume)
D(OP_DeleteItem)
D(OP_DzAddPlayer)
D(OP_DzChooseZoneReply)
D(OP_DzExpeditionInviteResponse)
D(OP_DzMakeLeader)
D(OP_DzRemovePlayer)
D(OP_DzSwapPlayer)
D(OP_Emote)
D(OP_FaceChange)
D(OP_FindPersonRequest)

View File

@ -4169,52 +4169,160 @@ struct VeteranReward
/*012*/ VeteranRewardItem items[8];
};
struct ExpeditionEntryHeader_Struct
struct ExpeditionInvite_Struct
{
/*000*/ uint32 client_id; // unique character id
/*004*/ uint32 unknown004;
/*008*/ char inviter_name[64];
/*072*/ char expedition_name[128];
/*200*/ uint8 swapping; // 0: adding 1: swapping
/*201*/ char swap_name[64]; // if swapping, swap name being removed
/*265*/ uint8 padding[3];
/*268*/ uint16 dz_zone_id; // dz_id zone/instance pair, sent back in reply
/*270*/ uint16 dz_instance_id;
};
struct ExpeditionInviteResponse_Struct
{
/*000*/ uint32 unknown000;
/*000*/ uint32 number_of_entries;
};
struct ExpeditionJoinPrompt_Struct
{
/*000*/ uint32 clientid;
/*004*/ uint32 unknown004;
/*008*/ char player_name[64];
/*072*/ char expedition_name[64];
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 clientid;
/*004*/ uint32 unknown004;
/*008*/ uint32 minutes_remaining;
/*008*/ uint16 dz_zone_id; // dz_id pair sent in invite
/*010*/ uint16 dz_instance_id;
/*012*/ uint8 accepted; // 0: declined 1: accepted
/*013*/ uint8 swapping; // 0: adding 1: swapping (sent in invite)
/*014*/ char swap_name[64]; // swap name sent in invite
/*078*/ uint8 unknown078; // padding garbage?
/*079*/ uint8 unknown079; // padding garbage?
};
struct ExpeditionInfo_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 unknown008;
/*008*/ uint32 assigned; // padded bool
/*012*/ uint32 max_players;
/*016*/ char expedition_name[128];
/*142*/ char leader_name[64];
/*016*/ char expedition_name[128];
/*144*/ char leader_name[64];
};
struct ExpeditionCompassEntry_Struct
struct ExpeditionMemberEntry_Struct
{
/*000*/ float unknown000; //seen *((uint32*)) = 1584791871
/*004*/ uint32 enabled; //guess
/*008*/ uint32 unknown008; //seen 1019
/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64)
/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead
};
struct ExpeditionMemberList_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 member_count; // number of players in window
/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length
};
struct ExpeditionMemberListName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status
/*012*/ char name[64];
};
struct ExpeditionLockoutTimerEntry_Struct
{
/*000*/ char expedition_name[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ uint32 seconds_remaining;
/*000*/ int32 event_type; // seen -1 (0xffffffff) for replay timers and 1 for event timers
/*000*/ char event_name[1]; // variable length, null terminated, max 0x100 (256)
};
struct ExpeditionLockoutTimers_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionLockoutTimerEntry_Struct timers[0];
};
struct ExpeditionSetLeaderName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ char leader_name[64];
};
struct ExpeditionCommand_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char name[64];
};
struct ExpeditionCommandSwap_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char add_player_name[64]; // swap to (player must confirm)
/*072*/ char rem_player_name[64]; // swap from
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 minutes_remaining;
};
struct DynamicZoneCompassEntry_Struct
{
/*000*/ uint16 dz_zone_id; // target dz id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 dz_type; // 1: Expedition, 2: Tutorial (purple), 3: Task, 4: Mission, 5: Quest (green)
/*008*/ uint32 unknown008;
/*012*/ float y;
/*016*/ float x;
/*020*/ float z;
};
struct ExpeditionCompass_Struct
struct DynamicZoneCompass_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionCompassEntry_Struct entries[0];
/*008*/ DynamicZoneCompassEntry_Struct entries[0];
};
struct DynamicZoneChooseZoneEntry_Struct
{
/*000*/ uint16 dz_zone_id; // dz_id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 unknown_id1; // sent back in reply
/*008*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest -- sent back in reply
/*012*/ uint32 unknown_id2; // possibly an id based on dz type, for expeditions this was same as dz_id (zone|instance) but task dz was different
/*016*/ char description[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ char leader_name[1]; // variable length, null terminated, max 0x40 (64)
};
struct DynamicZoneChooseZone_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ DynamicZoneChooseZoneEntry_Struct choices[0];
};
struct DynamicZoneChooseZoneReply_Struct
{
/*000*/ uint32 unknown000; // ff ff ff ff
/*004*/ uint32 unknown004; // seen 69 00 00 00
/*008*/ uint32 unknown008; // ff ff ff ff
/*012*/ uint32 unknown_id1; // from choose zone entry message
/*016*/ uint16 dz_zone_id; // dz_id pair
/*018*/ uint16 dz_instance_id;
/*020*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest
/*024*/ uint32 unknown_id2; // from choose zone entry message
/*028*/ uint32 unknown028; // 00 00 00 00
/*032*/ uint32 unknown032; // always same as unknown044
/*036*/ uint32 unknown036;
/*040*/ uint32 unknown040;
/*044*/ uint32 unknown044; // always same as unknown032
/*048*/ uint32 unknown048; // seen 01 00 00 00 and 02 00 00 00
};
struct AltCurrencySelectItem_Struct {

View File

@ -471,14 +471,48 @@ namespace SoF
FINISH_ENCODE();
}
ENCODE(OP_DzChooseZone)
{
SETUP_VAR_ENCODE(DynamicZoneChooseZone_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
buf.WriteUInt16(emu->choices[i].dz_zone_id);
buf.WriteUInt16(emu->choices[i].dz_instance_id);
buf.WriteUInt32(emu->choices[i].unknown_id1);
buf.WriteUInt32(emu->choices[i].dz_type);
buf.WriteUInt32(emu->choices[i].unknown_id2);
buf.WriteString(emu->choices[i].description);
buf.WriteString(emu->choices[i].leader_name);
}
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzCompass)
{
SETUP_VAR_ENCODE(ExpeditionCompass_Struct);
ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count);
SETUP_VAR_ENCODE(DynamicZoneCompass_Struct);
ALLOC_VAR_ENCODE(structs::DynamicZoneCompass_Struct,
sizeof(structs::DynamicZoneCompass_Struct) +
sizeof(structs::DynamicZoneCompassEntry_Struct) * emu->count
);
OUT(client_id);
OUT(count);
for (uint32 i = 0; i < emu->count; ++i)
{
OUT(entries[i].dz_zone_id);
OUT(entries[i].dz_instance_id);
OUT(entries[i].dz_type);
OUT(entries[i].x);
OUT(entries[i].y);
OUT(entries[i].z);
@ -502,80 +536,60 @@ namespace SoF
ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct);
OUT(client_id);
OUT(assigned);
OUT(max_players);
eq->enabled_max = 1;
strcpy(eq->expedition_name, emu->expedition_name);
strcpy(eq->leader_name, emu->leader_name);
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionList)
ENCODE(OP_DzExpeditionInvite)
{
SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct);
ENCODE_LENGTH_EXACT(ExpeditionInvite_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInvite_Struct, structs::ExpeditionInvite_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
for (int i = 0; i < emu->count; ++i)
OUT(client_id);
strn0cpy(eq->inviter_name, emu->inviter_name, sizeof(eq->inviter_name));
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
OUT(swapping);
strn0cpy(eq->swap_name, emu->swap_name, sizeof(eq->swap_name));
OUT(dz_zone_id);
OUT(dz_instance_id);
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionLockoutTimers)
{
SETUP_VAR_ENCODE(ExpeditionLockoutTimers_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event));
ss.write((const char*)&null_term, sizeof(char));
buf.WriteString(emu->timers[i].expedition_name);
buf.WriteUInt32(emu->timers[i].seconds_remaining);
buf.WriteInt32(emu->timers[i].event_type);
buf.WriteString(emu->timers[i].event_name);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzJoinExpeditionConfirm)
ENCODE(OP_DzSetLeaderName)
{
ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct);
SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct);
ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct);
strcpy(eq->expedition_name, emu->expedition_name);
strcpy(eq->player_name, emu->player_name);
FINISH_ENCODE();
}
ENCODE(OP_DzLeaderStatus)
{
SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
//ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->leader_name, strlen(emu->leader_name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//1
ss.write((const char*)&client_id, sizeof(uint32));
__packet->size = ss.str().length();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
OUT(client_id);
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
@ -584,26 +598,43 @@ namespace SoF
{
SETUP_VAR_ENCODE(ExpeditionMemberList_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
for (uint32 i = 0; i < emu->count; ++i)
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->member_count);
for (uint32 i = 0; i < emu->member_count; ++i)
{
ss.write(emu->entries[i].name, strlen(emu->entries[i].name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].status, sizeof(char));
buf.WriteString(emu->members[i].name);
buf.WriteUInt8(emu->members[i].expedition_status);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListName)
{
ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct);
OUT(client_id);
OUT(add_name);
strn0cpy(eq->name, emu->name, sizeof(eq->name));
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListStatus)
{
auto emu = reinterpret_cast<ExpeditionMemberList_Struct*>((*p)->pBuffer);
if (emu->member_count == 1)
{
ENCODE_FORWARD(OP_DzMemberList);
}
}
ENCODE(OP_Emote)
{
EQApplicationPacket *in = *p;
@ -2435,6 +2466,83 @@ namespace SoF
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzAddPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzChooseZoneReply)
{
DECODE_LENGTH_EXACT(structs::DynamicZoneChooseZoneReply_Struct);
SETUP_DIRECT_DECODE(DynamicZoneChooseZoneReply_Struct, structs::DynamicZoneChooseZoneReply_Struct);
emu->unknown000 = eq->unknown000;
emu->unknown008 = eq->unknown004;
IN(unknown_id1);
IN(dz_zone_id);
IN(dz_instance_id);
IN(dz_type);
IN(unknown_id2);
emu->unknown028 = eq->unknown024;
emu->unknown032 = eq->unknown028;
emu->unknown036 = eq->unknown032;
emu->unknown040 = eq->unknown036;
emu->unknown044 = eq->unknown040;
emu->unknown048 = eq->unknown044;
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzExpeditionInviteResponse)
{
DECODE_LENGTH_EXACT(structs::ExpeditionInviteResponse_Struct);
SETUP_DIRECT_DECODE(ExpeditionInviteResponse_Struct, structs::ExpeditionInviteResponse_Struct);
IN(dz_zone_id);
IN(dz_instance_id);
IN(accepted);
IN(swapping);
strn0cpy(emu->swap_name, eq->swap_name, sizeof(emu->swap_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzMakeLeader)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzRemovePlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzSwapPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommandSwap_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommandSwap_Struct, structs::ExpeditionCommandSwap_Struct);
strn0cpy(emu->add_player_name, eq->add_player_name, sizeof(emu->add_player_name));
strn0cpy(emu->rem_player_name, eq->rem_player_name, sizeof(emu->rem_player_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_Emote)
{
unsigned char *__eq_buffer = __packet->pBuffer;

View File

@ -36,13 +36,16 @@ E(OP_Damage)
E(OP_DeleteCharge)
E(OP_DeleteItem)
E(OP_DeleteSpawn)
E(OP_DzChooseZone)
E(OP_DzCompass)
E(OP_DzExpeditionEndsWarning)
E(OP_DzExpeditionInfo)
E(OP_DzExpeditionList)
E(OP_DzJoinExpeditionConfirm)
E(OP_DzLeaderStatus)
E(OP_DzExpeditionInvite)
E(OP_DzExpeditionLockoutTimers)
E(OP_DzMemberList)
E(OP_DzMemberListName)
E(OP_DzMemberListStatus)
E(OP_DzSetLeaderName)
E(OP_Emote)
E(OP_ExpansionInfo)
E(OP_FormattedMessage)
@ -103,6 +106,12 @@ D(OP_Consider)
D(OP_ConsiderCorpse)
D(OP_Consume)
D(OP_DeleteItem)
D(OP_DzAddPlayer)
D(OP_DzChooseZoneReply)
D(OP_DzExpeditionInviteResponse)
D(OP_DzMakeLeader)
D(OP_DzRemovePlayer)
D(OP_DzSwapPlayer)
D(OP_Emote)
D(OP_FaceChange)
D(OP_FindPersonRequest)

View File

@ -4088,43 +4088,150 @@ struct VeteranReward
/*012*/ VeteranRewardItem items[8];
};
struct ExpeditionExpireWarning
struct ExpeditionInvite_Struct
{
/*000*/ uint32 client_id;
/*004*/ char inviter_name[64];
/*068*/ char expedition_name[128];
/*196*/ uint8 swapping; // 0: adding 1: swapping
/*197*/ char swap_name[64]; // if swapping, swap name being removed
/*261*/ uint8 padding[3];
/*264*/ uint16 dz_zone_id; // dz_id zone/instance pair, sent back in reply
/*268*/ uint16 dz_instance_id;
};
struct ExpeditionInviteResponse_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 minutes_remaining;
/*004*/ uint16 dz_zone_id; // dz_id pair sent in invite
/*006*/ uint16 dz_instance_id;
/*008*/ uint8 accepted; // 0: declined 1: accepted
/*009*/ uint8 swapping; // 0: adding 1: swapping (sent in invite)
/*010*/ char swap_name[64]; // swap name sent in invite
/*074*/ uint8 unknown078; // padding/garbage?
/*075*/ uint8 unknown079; // padding/garbage?
};
struct ExpeditionInfo_Struct
{
/*000*/ uint32 clientid;
/*004*/ uint32 enabled_max;
/*000*/ uint32 client_id;
/*004*/ uint32 assigned; // padded bool
/*008*/ uint32 max_players;
/*012*/ char expedition_name[128];
/*142*/ char leader_name[64];
/*012*/ char expedition_name[128];
/*140*/ char leader_name[64];
};
struct ExpeditionCompassEntry_Struct
struct ExpeditionMemberEntry_Struct
{
/*000*/ float unknown000; //seen *((uint32*)) = 1584791871
/*004*/ uint32 enabled; //guess
/*008*/ uint32 unknown008; //seen 1019
/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64)
/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead
};
struct ExpeditionMemberList_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 member_count;
/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length
};
struct ExpeditionMemberListName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status
/*008*/ char name[64];
};
struct ExpeditionLockoutTimerEntry_Struct
{
/*000*/ char expedition_name[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ uint32 seconds_remaining;
/*000*/ int32 event_type; // seen -1 (0xffffffff) for replay timers and 1 for event timers
/*000*/ char event_name[1]; // variable length, null terminated, max 0x100 (256)
};
struct ExpeditionLockoutTimers_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionLockoutTimerEntry_Struct timers[0];
};
struct ExpeditionSetLeaderName_Struct
{
/*000*/ uint32 client_id;
/*004*/ char leader_name[64];
};
struct ExpeditionCommand_Struct
{
/*000*/ uint32 unknown000;
/*004*/ char name[64];
};
struct ExpeditionCommandSwap_Struct
{
/*000*/ uint32 unknown000;
/*004*/ char add_player_name[64]; // swap to (player must confirm)
/*068*/ char rem_player_name[64]; // swap from
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 client_id;
/*004*/ uint32 minutes_remaining;
};
struct DynamicZoneCompassEntry_Struct
{
/*000*/ uint16 dz_zone_id; // target dz id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 dz_type; // 1: Expedition, 2: Tutorial (purple), 3: Task, 4: Mission, 5: Quest (green)
/*008*/ uint32 unknown008;
/*012*/ float y;
/*016*/ float x;
/*020*/ float z;
};
struct ExpeditionCompass_Struct
struct DynamicZoneCompass_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionCompassEntry_Struct entries[0];
/*008*/ DynamicZoneCompassEntry_Struct entries[0];
};
struct ExpeditionJoinPrompt_Struct
struct DynamicZoneChooseZoneEntry_Struct
{
/*000*/ uint32 clientid;
/*004*/ char player_name[64];
/*068*/ char expedition_name[64];
/*000*/ uint16 dz_zone_id; // dz_id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 unknown_id1; // sent back in reply
/*008*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest -- sent back in reply
/*012*/ uint32 unknown_id2; // possibly an id based on dz type, for expeditions this was same as dz_id (zone|instance) but task dz was different
/*016*/ char description[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ char leader_name[1]; // variable length, null terminated, max 0x40 (64)
};
struct DynamicZoneChooseZone_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ DynamicZoneChooseZoneEntry_Struct choices[0];
};
struct DynamicZoneChooseZoneReply_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ uint32 unknown_id1;
/*012*/ uint16 dz_zone_id;
/*014*/ uint16 dz_instance_id;
/*016*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest
/*020*/ uint32 unknown_id2;
/*024*/ uint32 unknown024;
/*028*/ uint32 unknown028; // always same as unknown040
/*032*/ uint32 unknown032;
/*036*/ uint32 unknown036;
/*040*/ uint32 unknown040; // always same as unknown028
/*044*/ uint32 unknown044;
};
struct AltCurrencySelectItem_Struct {

View File

@ -414,15 +414,48 @@ namespace Titanium
FINISH_ENCODE();
}
ENCODE(OP_DzChooseZone)
{
SETUP_VAR_ENCODE(DynamicZoneChooseZone_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
buf.WriteUInt16(emu->choices[i].dz_zone_id);
buf.WriteUInt16(emu->choices[i].dz_instance_id);
buf.WriteUInt32(emu->choices[i].unknown_id1);
buf.WriteUInt32(emu->choices[i].dz_type);
buf.WriteUInt32(emu->choices[i].unknown_id2);
buf.WriteString(emu->choices[i].description);
buf.WriteString(emu->choices[i].leader_name);
}
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzCompass)
{
SETUP_VAR_ENCODE(ExpeditionCompass_Struct);
ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count);
SETUP_VAR_ENCODE(DynamicZoneCompass_Struct);
ALLOC_VAR_ENCODE(structs::DynamicZoneCompass_Struct,
sizeof(structs::DynamicZoneCompass_Struct) +
sizeof(structs::DynamicZoneCompassEntry_Struct) * emu->count
);
OUT(client_id);
OUT(count);
for (uint32 i = 0; i < emu->count; ++i)
{
OUT(entries[i].dz_zone_id);
OUT(entries[i].dz_instance_id);
OUT(entries[i].dz_type);
OUT(entries[i].x);
OUT(entries[i].y);
OUT(entries[i].z);
@ -446,80 +479,60 @@ namespace Titanium
ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct);
OUT(client_id);
OUT(assigned);
OUT(max_players);
eq->enabled_max = 1;
strcpy(eq->expedition_name, emu->expedition_name);
strcpy(eq->leader_name, emu->leader_name);
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionList)
ENCODE(OP_DzExpeditionInvite)
{
SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct);
ENCODE_LENGTH_EXACT(ExpeditionInvite_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInvite_Struct, structs::ExpeditionInvite_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
OUT(client_id);
strn0cpy(eq->inviter_name, emu->inviter_name, sizeof(eq->inviter_name));
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
OUT(swapping);
strn0cpy(eq->swap_name, emu->swap_name, sizeof(eq->swap_name));
OUT(dz_zone_id);
OUT(dz_instance_id);
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionLockoutTimers)
{
SETUP_VAR_ENCODE(ExpeditionLockoutTimers_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event));
ss.write((const char*)&null_term, sizeof(char));
buf.WriteString(emu->timers[i].expedition_name);
buf.WriteUInt32(emu->timers[i].seconds_remaining);
buf.WriteInt32(emu->timers[i].event_type);
buf.WriteString(emu->timers[i].event_name);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzJoinExpeditionConfirm)
ENCODE(OP_DzSetLeaderName)
{
ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct);
SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct);
ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct);
strcpy(eq->expedition_name, emu->expedition_name);
strcpy(eq->player_name, emu->player_name);
FINISH_ENCODE();
}
ENCODE(OP_DzLeaderStatus)
{
SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
//ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->leader_name, strlen(emu->leader_name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//1
ss.write((const char*)&client_id, sizeof(uint32));
__packet->size = ss.str().length();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
OUT(client_id);
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
@ -528,26 +541,43 @@ namespace Titanium
{
SETUP_VAR_ENCODE(ExpeditionMemberList_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
for (uint32 i = 0; i < emu->count; ++i)
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->member_count);
for (uint32 i = 0; i < emu->member_count; ++i)
{
ss.write(emu->entries[i].name, strlen(emu->entries[i].name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].status, sizeof(char));
buf.WriteString(emu->members[i].name);
buf.WriteUInt8(emu->members[i].expedition_status);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListName)
{
ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct);
OUT(client_id);
OUT(add_name);
strn0cpy(eq->name, emu->name, sizeof(eq->name));
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListStatus)
{
auto emu = reinterpret_cast<ExpeditionMemberList_Struct*>((*p)->pBuffer);
if (emu->member_count == 1)
{
ENCODE_FORWARD(OP_DzMemberList);
}
}
ENCODE(OP_Emote)
{
EQApplicationPacket *in = *p;
@ -1943,6 +1973,83 @@ namespace Titanium
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzAddPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzChooseZoneReply)
{
DECODE_LENGTH_EXACT(structs::DynamicZoneChooseZoneReply_Struct);
SETUP_DIRECT_DECODE(DynamicZoneChooseZoneReply_Struct, structs::DynamicZoneChooseZoneReply_Struct);
emu->unknown000 = eq->unknown000;
emu->unknown008 = eq->unknown004;
IN(unknown_id1);
IN(dz_zone_id);
IN(dz_instance_id);
IN(dz_type);
IN(unknown_id2);
emu->unknown028 = eq->unknown024;
emu->unknown032 = eq->unknown028;
emu->unknown036 = eq->unknown032;
emu->unknown040 = eq->unknown036;
emu->unknown044 = eq->unknown040;
emu->unknown048 = eq->unknown044;
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzExpeditionInviteResponse)
{
DECODE_LENGTH_EXACT(structs::ExpeditionInviteResponse_Struct);
SETUP_DIRECT_DECODE(ExpeditionInviteResponse_Struct, structs::ExpeditionInviteResponse_Struct);
IN(dz_zone_id);
IN(dz_instance_id);
IN(accepted);
IN(swapping);
strn0cpy(emu->swap_name, eq->swap_name, sizeof(emu->swap_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzMakeLeader)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzRemovePlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzSwapPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommandSwap_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommandSwap_Struct, structs::ExpeditionCommandSwap_Struct);
strn0cpy(emu->add_player_name, eq->add_player_name, sizeof(emu->add_player_name));
strn0cpy(emu->rem_player_name, eq->rem_player_name, sizeof(emu->rem_player_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_Emote)
{
unsigned char *__eq_buffer = __packet->pBuffer;

View File

@ -32,13 +32,16 @@ E(OP_Damage)
E(OP_DeleteCharge)
E(OP_DeleteItem)
E(OP_DeleteSpawn)
E(OP_DzChooseZone)
E(OP_DzCompass)
E(OP_DzExpeditionEndsWarning)
E(OP_DzExpeditionInfo)
E(OP_DzExpeditionList)
E(OP_DzJoinExpeditionConfirm)
E(OP_DzLeaderStatus)
E(OP_DzExpeditionInvite)
E(OP_DzExpeditionLockoutTimers)
E(OP_DzMemberList)
E(OP_DzMemberListName)
E(OP_DzMemberListStatus)
E(OP_DzSetLeaderName)
E(OP_Emote)
E(OP_FormattedMessage)
E(OP_GroundSpawn)
@ -86,6 +89,12 @@ D(OP_CharacterCreate)
D(OP_ClientUpdate)
D(OP_Consume)
D(OP_DeleteItem)
D(OP_DzAddPlayer)
D(OP_DzChooseZoneReply)
D(OP_DzExpeditionInviteResponse)
D(OP_DzMakeLeader)
D(OP_DzRemovePlayer)
D(OP_DzSwapPlayer)
D(OP_Emote)
D(OP_FaceChange)
D(OP_InspectAnswer)

View File

@ -3299,43 +3299,150 @@ struct VeteranReward
/*004*/ VeteranRewardItem item;
};
struct ExpeditionExpireWarning
struct ExpeditionInvite_Struct
{
/*000*/ uint32 client_id;
/*004*/ char inviter_name[64];
/*068*/ char expedition_name[128];
/*196*/ uint8 swapping; // 0: adding 1: swapping
/*197*/ char swap_name[64]; // if swapping, swap name being removed
/*261*/ uint8 padding[3];
/*264*/ uint16 dz_zone_id; // dz_id zone/instance pair, sent back in reply
/*268*/ uint16 dz_instance_id;
};
struct ExpeditionInviteResponse_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 minutes_remaining;
/*004*/ uint16 dz_zone_id; // dz_id pair sent in invite
/*006*/ uint16 dz_instance_id;
/*008*/ uint8 accepted; // 0: declined 1: accepted
/*009*/ uint8 swapping; // 0: adding 1: swapping (sent in invite)
/*010*/ char swap_name[64]; // swap name sent in invite
/*074*/ uint8 unknown078; // padding/garbage?
/*075*/ uint8 unknown079; // padding/garbage?
};
struct ExpeditionInfo_Struct
{
/*000*/ uint32 clientid;
/*004*/ uint32 enabled_max;
/*000*/ uint32 client_id;
/*004*/ uint32 assigned; // padded bool
/*008*/ uint32 max_players;
/*012*/ char expedition_name[128];
/*142*/ char leader_name[64];
/*012*/ char expedition_name[128];
/*140*/ char leader_name[64];
};
struct ExpeditionCompassEntry_Struct
struct ExpeditionMemberEntry_Struct
{
/*000*/ float unknown000; //seen *((uint32*)) = 1584791871
/*004*/ uint32 enabled; //guess
/*008*/ uint32 unknown008; //seen 1019
/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64)
/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead
};
struct ExpeditionMemberList_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 member_count;
/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length
};
struct ExpeditionMemberListName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status
/*008*/ char name[64];
};
struct ExpeditionLockoutTimerEntry_Struct
{
/*000*/ char expedition_name[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ uint32 seconds_remaining;
/*000*/ int32 event_type; // seen -1 (0xffffffff) for replay timers and 1 for event timers
/*000*/ char event_name[1]; // variable length, null terminated, max 0x100 (256)
};
struct ExpeditionLockoutTimers_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionLockoutTimerEntry_Struct timers[0];
};
struct ExpeditionSetLeaderName_Struct
{
/*000*/ uint32 client_id;
/*004*/ char leader_name[64];
};
struct ExpeditionCommand_Struct
{
/*000*/ uint32 unknown000;
/*004*/ char name[64];
};
struct ExpeditionCommandSwap_Struct
{
/*000*/ uint32 unknown000;
/*004*/ char add_player_name[64]; // swap to (player must confirm)
/*068*/ char rem_player_name[64]; // swap from
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 client_id;
/*004*/ uint32 minutes_remaining;
};
struct DynamicZoneCompassEntry_Struct
{
/*000*/ uint16 dz_zone_id; // target dz id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 dz_type; // 1: Expedition, 2: Tutorial (purple), 3: Task, 4: Mission, 5: Quest (green)
/*008*/ uint32 unknown008;
/*012*/ float y;
/*016*/ float x;
/*020*/ float z;
};
struct ExpeditionCompass_Struct
struct DynamicZoneCompass_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionCompassEntry_Struct entries[0];
/*008*/ DynamicZoneCompassEntry_Struct entries[0];
};
struct ExpeditionJoinPrompt_Struct
struct DynamicZoneChooseZoneEntry_Struct
{
/*000*/ uint32 clientid;
/*004*/ char player_name[64];
/*068*/ char expedition_name[64];
/*000*/ uint16 dz_zone_id; // dz_id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 unknown_id1; // sent back in reply
/*008*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest -- sent back in reply
/*012*/ uint32 unknown_id2; // possibly an id based on dz type, for expeditions this was same as dz_id (zone|instance) but task dz was different
/*016*/ char description[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ char leader_name[1]; // variable length, null terminated, max 0x40 (64)
};
struct DynamicZoneChooseZone_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ DynamicZoneChooseZoneEntry_Struct choices[0];
};
struct DynamicZoneChooseZoneReply_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ uint32 unknown_id1;
/*012*/ uint16 dz_zone_id;
/*014*/ uint16 dz_instance_id;
/*016*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest
/*020*/ uint32 unknown_id2;
/*024*/ uint32 unknown024;
/*028*/ uint32 unknown028; // always same as unknown040
/*032*/ uint32 unknown032;
/*036*/ uint32 unknown036;
/*040*/ uint32 unknown040; // always same as unknown028
/*044*/ uint32 unknown044;
};
struct LFGuild_SearchPlayer_Struct

View File

@ -613,14 +613,48 @@ namespace UF
FINISH_ENCODE();
}
ENCODE(OP_DzChooseZone)
{
SETUP_VAR_ENCODE(DynamicZoneChooseZone_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
buf.WriteUInt16(emu->choices[i].dz_zone_id);
buf.WriteUInt16(emu->choices[i].dz_instance_id);
buf.WriteUInt32(emu->choices[i].unknown_id1);
buf.WriteUInt32(emu->choices[i].dz_type);
buf.WriteUInt32(emu->choices[i].unknown_id2);
buf.WriteString(emu->choices[i].description);
buf.WriteString(emu->choices[i].leader_name);
}
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzCompass)
{
SETUP_VAR_ENCODE(ExpeditionCompass_Struct);
ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count);
SETUP_VAR_ENCODE(DynamicZoneCompass_Struct);
ALLOC_VAR_ENCODE(structs::DynamicZoneCompass_Struct,
sizeof(structs::DynamicZoneCompass_Struct) +
sizeof(structs::DynamicZoneCompassEntry_Struct) * emu->count
);
OUT(client_id);
OUT(count);
for (uint32 i = 0; i < emu->count; ++i)
{
OUT(entries[i].dz_zone_id);
OUT(entries[i].dz_instance_id);
OUT(entries[i].dz_type);
OUT(entries[i].x);
OUT(entries[i].y);
OUT(entries[i].z);
@ -644,81 +678,60 @@ namespace UF
ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct);
OUT(client_id);
OUT(assigned);
OUT(max_players);
eq->unknown004 = 785316192;
eq->unknown008 = 435601;
strcpy(eq->expedition_name, emu->expedition_name);
strcpy(eq->leader_name, emu->leader_name);
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionList)
ENCODE(OP_DzExpeditionInvite)
{
SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct);
ENCODE_LENGTH_EXACT(ExpeditionInvite_Struct);
SETUP_DIRECT_ENCODE(ExpeditionInvite_Struct, structs::ExpeditionInvite_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
OUT(client_id);
strn0cpy(eq->inviter_name, emu->inviter_name, sizeof(eq->inviter_name));
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
OUT(swapping);
strn0cpy(eq->swap_name, emu->swap_name, sizeof(eq->swap_name));
OUT(dz_zone_id);
OUT(dz_instance_id);
FINISH_ENCODE();
}
ENCODE(OP_DzExpeditionLockoutTimers)
{
SETUP_VAR_ENCODE(ExpeditionLockoutTimers_Struct);
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->count);
for (uint32 i = 0; i < emu->count; ++i)
{
ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event));
ss.write((const char*)&null_term, sizeof(char));
buf.WriteString(emu->timers[i].expedition_name);
buf.WriteUInt32(emu->timers[i].seconds_remaining);
buf.WriteInt32(emu->timers[i].event_type);
buf.WriteString(emu->timers[i].event_name);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzJoinExpeditionConfirm)
ENCODE(OP_DzSetLeaderName)
{
ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct);
SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct);
ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct);
strcpy(eq->expedition_name, emu->expedition_name);
strcpy(eq->player_name, emu->player_name);
FINISH_ENCODE();
}
ENCODE(OP_DzLeaderStatus)
{
SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write(emu->leader_name, strlen(emu->leader_name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&client_id, sizeof(uint32));//1
ss.write((const char*)&client_id, sizeof(uint32));
__packet->size = ss.str().length();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
OUT(client_id);
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
FINISH_ENCODE();
}
@ -727,26 +740,43 @@ namespace UF
{
SETUP_VAR_ENCODE(ExpeditionMemberList_Struct);
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
uint32 client_id = 0;
uint8 null_term = 0;
ss.write((const char*)&client_id, sizeof(uint32));
ss.write((const char*)&emu->count, sizeof(uint32));
for (uint32 i = 0; i < emu->count; ++i)
SerializeBuffer buf;
buf.WriteUInt32(emu->client_id);
buf.WriteUInt32(emu->member_count);
for (uint32 i = 0; i < emu->member_count; ++i)
{
ss.write(emu->entries[i].name, strlen(emu->entries[i].name));
ss.write((const char*)&null_term, sizeof(char));
ss.write((const char*)&emu->entries[i].status, sizeof(char));
buf.WriteString(emu->members[i].name);
buf.WriteUInt8(emu->members[i].expedition_status);
}
__packet->size = ss.str().length();
__packet->size = buf.size();
__packet->pBuffer = new unsigned char[__packet->size];
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListName)
{
ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct);
SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct);
OUT(client_id);
OUT(add_name);
strn0cpy(eq->name, emu->name, sizeof(eq->name));
FINISH_ENCODE();
}
ENCODE(OP_DzMemberListStatus)
{
auto emu = reinterpret_cast<ExpeditionMemberList_Struct*>((*p)->pBuffer);
if (emu->member_count == 1)
{
ENCODE_FORWARD(OP_DzMemberList);
}
}
ENCODE(OP_Emote)
{
EQApplicationPacket *in = *p;
@ -3315,6 +3345,84 @@ namespace UF
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzAddPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzChooseZoneReply)
{
DECODE_LENGTH_EXACT(structs::DynamicZoneChooseZoneReply_Struct);
SETUP_DIRECT_DECODE(DynamicZoneChooseZoneReply_Struct, structs::DynamicZoneChooseZoneReply_Struct);
IN(unknown000);
IN(unknown004);
IN(unknown008);
IN(unknown_id1);
IN(dz_zone_id);
IN(dz_instance_id);
IN(dz_type);
IN(unknown_id2);
IN(unknown028);
IN(unknown032);
IN(unknown036);
IN(unknown040);
IN(unknown044);
IN(unknown048);
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzExpeditionInviteResponse)
{
DECODE_LENGTH_EXACT(structs::ExpeditionInviteResponse_Struct);
SETUP_DIRECT_DECODE(ExpeditionInviteResponse_Struct, structs::ExpeditionInviteResponse_Struct);
IN(dz_zone_id);
IN(dz_instance_id);
IN(accepted);
IN(swapping);
strn0cpy(emu->swap_name, eq->swap_name, sizeof(emu->swap_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzMakeLeader)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzRemovePlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
strn0cpy(emu->name, eq->name, sizeof(emu->name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_DzSwapPlayer)
{
DECODE_LENGTH_EXACT(structs::ExpeditionCommandSwap_Struct);
SETUP_DIRECT_DECODE(ExpeditionCommandSwap_Struct, structs::ExpeditionCommandSwap_Struct);
strn0cpy(emu->add_player_name, eq->add_player_name, sizeof(emu->add_player_name));
strn0cpy(emu->rem_player_name, eq->rem_player_name, sizeof(emu->rem_player_name));
FINISH_DIRECT_DECODE();
}
DECODE(OP_Emote)
{
unsigned char *__eq_buffer = __packet->pBuffer;

View File

@ -38,13 +38,16 @@ E(OP_Damage)
E(OP_DeleteCharge)
E(OP_DeleteItem)
E(OP_DisciplineUpdate)
E(OP_DzChooseZone)
E(OP_DzCompass)
E(OP_DzExpeditionEndsWarning)
E(OP_DzExpeditionInfo)
E(OP_DzExpeditionList)
E(OP_DzJoinExpeditionConfirm)
E(OP_DzLeaderStatus)
E(OP_DzExpeditionInvite)
E(OP_DzExpeditionLockoutTimers)
E(OP_DzMemberList)
E(OP_DzMemberListName)
E(OP_DzMemberListStatus)
E(OP_DzSetLeaderName)
E(OP_Emote)
E(OP_ExpansionInfo)
E(OP_FormattedMessage)
@ -120,6 +123,12 @@ D(OP_ConsiderCorpse)
D(OP_Consume)
D(OP_Damage)
D(OP_DeleteItem)
D(OP_DzAddPlayer)
D(OP_DzChooseZoneReply)
D(OP_DzExpeditionInviteResponse)
D(OP_DzMakeLeader)
D(OP_DzRemovePlayer)
D(OP_DzSwapPlayer)
D(OP_Emote)
D(OP_EnvDamage)
D(OP_FaceChange)

View File

@ -4250,52 +4250,160 @@ struct VeteranReward
/*012*/ VeteranRewardItem items[8];
};
struct ExpeditionEntryHeader_Struct
struct ExpeditionInvite_Struct
{
/*000*/ uint32 client_id; // unique character id
/*004*/ uint32 unknown004;
/*008*/ char inviter_name[64];
/*072*/ char expedition_name[128];
/*200*/ uint8 swapping; // 0: adding 1: swapping
/*201*/ char swap_name[64]; // if swapping, swap name being removed
/*265*/ uint8 padding[3];
/*268*/ uint16 dz_zone_id; // dz_id zone/instance pair, sent back in reply
/*270*/ uint16 dz_instance_id;
};
struct ExpeditionInviteResponse_Struct
{
/*000*/ uint32 unknown000;
/*000*/ uint32 number_of_entries;
};
struct ExpeditionJoinPrompt_Struct
{
/*000*/ uint32 clientid;
/*004*/ uint32 unknown004;
/*008*/ char player_name[64];
/*072*/ char expedition_name[64];
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 clientid;
/*004*/ uint32 unknown004;
/*008*/ uint32 minutes_remaining;
/*008*/ uint16 dz_zone_id; // dz_id pair sent in invite
/*010*/ uint16 dz_instance_id;
/*012*/ uint8 accepted; // 0: declined 1: accepted
/*013*/ uint8 swapping; // 0: adding 1: swapping (sent in invite)
/*014*/ char swap_name[64]; // swap name sent in invite
/*078*/ uint8 unknown078; // padding garbage?
/*079*/ uint8 unknown079; // padding garbage?
};
struct ExpeditionInfo_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 unknown008;
/*008*/ uint32 assigned; // padded bool
/*012*/ uint32 max_players;
/*016*/ char expedition_name[128];
/*142*/ char leader_name[64];
/*016*/ char expedition_name[128];
/*144*/ char leader_name[64];
};
struct ExpeditionCompassEntry_Struct
struct ExpeditionMemberEntry_Struct
{
/*000*/ float unknown000; //seen *((uint32*)) = 1584791871
/*004*/ uint32 enabled; //guess
/*008*/ uint32 unknown008; //seen 1019
/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64)
/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead
};
struct ExpeditionMemberList_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 member_count; // number of players in window
/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length
};
struct ExpeditionMemberListName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status
/*012*/ char name[64];
};
struct ExpeditionLockoutTimerEntry_Struct
{
/*000*/ char expedition_name[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ uint32 seconds_remaining;
/*000*/ int32 event_type; // seen -1 (0xffffffff) for replay timers and 1 for event timers
/*000*/ char event_name[1]; // variable length, null terminated, max 0x100 (256)
};
struct ExpeditionLockoutTimers_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionLockoutTimerEntry_Struct timers[0];
};
struct ExpeditionSetLeaderName_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ char leader_name[64];
};
struct ExpeditionCommand_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char name[64];
};
struct ExpeditionCommandSwap_Struct
{
/*000*/ uint32 unknown000;
/*004*/ uint32 unknown004;
/*008*/ char add_player_name[64]; // swap to (player must confirm)
/*072*/ char rem_player_name[64]; // swap from
};
struct ExpeditionExpireWarning
{
/*000*/ uint32 client_id;
/*004*/ uint32 unknown004;
/*008*/ uint32 minutes_remaining;
};
struct DynamicZoneCompassEntry_Struct
{
/*000*/ uint16 dz_zone_id; // target dz id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 dz_type; // 1: Expedition, 2: Tutorial (purple), 3: Task, 4: Mission, 5: Quest (green)
/*008*/ uint32 unknown008;
/*012*/ float y;
/*016*/ float x;
/*020*/ float z;
};
struct ExpeditionCompass_Struct
struct DynamicZoneCompass_Struct
{
/*000*/ uint32 clientid;
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ ExpeditionCompassEntry_Struct entries[0];
/*008*/ DynamicZoneCompassEntry_Struct entries[0];
};
struct DynamicZoneChooseZoneEntry_Struct
{
/*000*/ uint16 dz_zone_id; // dz_id pair
/*002*/ uint16 dz_instance_id;
/*004*/ uint32 unknown_id1; // sent back in reply
/*008*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest -- sent back in reply
/*012*/ uint32 unknown_id2; // possibly an id based on dz type, for expeditions this was same as dz_id (zone|instance) but task dz was different
/*016*/ char description[1]; // variable length, null terminated, max 0x80 (128)
/*000*/ char leader_name[1]; // variable length, null terminated, max 0x40 (64)
};
struct DynamicZoneChooseZone_Struct
{
/*000*/ uint32 client_id;
/*004*/ uint32 count;
/*008*/ DynamicZoneChooseZoneEntry_Struct choices[0];
};
struct DynamicZoneChooseZoneReply_Struct
{
/*000*/ uint32 unknown000; // ff ff ff ff
/*004*/ uint32 unknown004; // seen 69 00 00 00
/*008*/ uint32 unknown008; // ff ff ff ff
/*012*/ uint32 unknown_id1; // from choose zone entry message
/*016*/ uint16 dz_zone_id; // dz_id pair
/*018*/ uint16 dz_instance_id;
/*020*/ uint32 dz_type; // 1: Expedition, 2: Tutorial, 3: Task, 4: Mission, 5: Quest
/*024*/ uint32 unknown_id2; // from choose zone entry message
/*028*/ uint32 unknown028; // 00 00 00 00
/*032*/ uint32 unknown032; // always same as unknown044
/*036*/ uint32 unknown036;
/*040*/ uint32 unknown040;
/*044*/ uint32 unknown044; // always same as unknown032
/*048*/ uint32 unknown048; // seen 01 00 00 00 and 02 00 00 00
};
struct AltCurrencySelectItem_Struct {

View File

@ -785,6 +785,20 @@ RULE_BOOL(Instances, RecycleInstanceIds, true, "Setting whether free instance ID
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
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, EmptyDzShutdownEnabled, true, "Enable early instance shutdown after last member of expedition removed")
RULE_INT(Expedition, EmptyDzShutdownDelaySeconds, 1500, "Seconds to set dynamic zone instance expiration if early shutdown enabled")
RULE_INT(Expedition, WorldExpeditionProcessRateMS, 6000, "Timer interval (ms) that world checks expedition states")
RULE_BOOL(Expedition, AlwaysNotifyNewLeaderOnChange, false, "Always notify clients when made expedition leader. If false (live-like) new leaders are only notified when made leader via /dzmakeleader")
RULE_REAL(Expedition, LockoutDurationMultiplier, 1.0, "Multiplies lockout duration by this value when new lockouts are added")
RULE_BOOL(Expedition, EnableInDynamicZoneStatus, false, "Enables the 'In Dynamic Zone' member status in expedition window. If false (live-like) players inside the dz will show as 'Online'")
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
#undef RULE_INT
#undef RULE_REAL

View File

@ -140,6 +140,35 @@
#define ServerOP_LFPUpdate 0x0213
#define ServerOP_LFPMatches 0x0214
#define ServerOP_ClientVersionSummary 0x0215
#define ServerOP_ExpeditionCreate 0x0400
#define ServerOP_ExpeditionDeleted 0x0401
#define ServerOP_ExpeditionLeaderChanged 0x0402
#define ServerOP_ExpeditionLockout 0x0403
#define ServerOP_ExpeditionMemberChange 0x0404
#define ServerOP_ExpeditionMemberSwap 0x0405
#define ServerOP_ExpeditionMemberStatus 0x0406
#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_ExpeditionCharacterLockout 0x040d
#define ServerOP_ExpeditionSaveInvite 0x040e
#define ServerOP_ExpeditionRequestInvite 0x040f
#define ServerOP_ExpeditionReplayOnJoin 0x0410
#define ServerOP_ExpeditionLockState 0x0411
#define ServerOP_ExpeditionMembersRemoved 0x0412
#define ServerOP_ExpeditionDzDuration 0x0413
#define ServerOP_ExpeditionLockoutDuration 0x0414
#define ServerOP_ExpeditionSecondsRemaining 0x0415
#define ServerOP_ExpeditionExpireWarning 0x0416
#define ServerOP_ExpeditionChooseNewLeader 0x0417
#define ServerOP_DzCharacterChange 0x0450
#define ServerOP_DzRemoveAllCharacters 0x0451
#define ServerOP_LSInfo 0x1000
#define ServerOP_LSStatus 0x1001
#define ServerOP_LSClientAuthLeg 0x1002
@ -257,6 +286,7 @@
#define ServerOP_CZTaskRemoveGroup 0x4560
#define ServerOP_CZTaskRemoveRaid 0x4561
#define ServerOP_CZTaskRemoveGuild 0x4562
#define ServerOP_CZClientMessageString 0x4563
#define ServerOP_WWAssignTask 0x4750
#define ServerOP_WWCastSpell 0x4751
@ -1433,6 +1463,14 @@ struct CZNPCSignal_Struct {
uint32 signal;
};
struct CZClientMessageString_Struct {
uint32 string_id;
uint16 chat_type;
char character_name[64];
uint32 args_size;
char args[1]; // null delimited
};
struct CZClientSignalByName_Struct {
char character_name[64];
uint32 signal;
@ -1958,6 +1996,146 @@ struct UCSServerStatus_Struct {
};
};
struct ServerExpeditionID_Struct {
uint32 expedition_id;
uint32 sender_zone_id;
uint32 sender_instance_id;
};
struct ServerExpeditionLeaderID_Struct {
uint32 expedition_id;
uint32 leader_id;
};
struct ServerExpeditionMemberChange_Struct {
uint32 expedition_id;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint8 removed; // 0: added, 1: removed
uint32 char_id;
char char_name[64];
};
struct ServerExpeditionMemberSwap_Struct {
uint32 expedition_id;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint32 add_char_id;
uint32 remove_char_id;
char add_char_name[64];
char remove_char_name[64];
};
struct ServerExpeditionMemberStatus_Struct {
uint32 expedition_id;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint8 status; // 0: unknown 1: Online 2: Offline 3: In Dynamic Zone 4: Link Dead
uint32 character_id;
};
struct ServerExpeditionCharacterEntry_Struct {
uint32 expedition_id;
uint32 character_id;
uint32 character_zone_id;
uint16 character_instance_id;
uint8 character_online; // 0: offline 1: online
};
struct ServerExpeditionCharacters_Struct {
uint32 sender_zone_id;
uint16 sender_instance_id;
uint32 count;
ServerExpeditionCharacterEntry_Struct entries[0];
};
struct ServerExpeditionLockout_Struct {
uint32 expedition_id;
uint64 expire_time;
uint32 duration;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint8 remove;
uint8 members_only;
int seconds_adjust;
char event_name[256];
};
struct ServerExpeditionLockState_Struct {
uint32 expedition_id;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint8 enabled;
uint8 lock_msg; // 0: none, 1: closing 2: trial begin
};
struct ServerExpeditionSetting_Struct {
uint32 expedition_id;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint8 enabled;
};
struct ServerExpeditionCharacterLockout_Struct {
uint8 remove;
uint32 character_id;
uint64 expire_time;
uint32 duration;
char uuid[37];
char expedition_name[128];
char event_name[256];
};
struct ServerExpeditionCharacterID_Struct {
uint32_t character_id;
};
struct ServerExpeditionUpdateDuration_Struct {
uint32_t expedition_id;
uint32_t new_duration_seconds;
};
struct ServerExpeditionExpireWarning_Struct {
uint32_t expedition_id;
uint32_t minutes_remaining;
};
struct ServerDzCommand_Struct {
uint32 expedition_id;
uint8 is_char_online; // 0: target name is offline, 1: online
char requester_name[64];
char target_name[64];
char remove_name[64]; // used for swap command
};
struct ServerDzCommandMakeLeader_Struct {
uint32 expedition_id;
uint32 requester_id;
uint8 is_online; // set by world, 0: new leader name offline, 1: online
uint8 is_success; // set by world, 0: makeleader failed, 1: success (is online member)
char new_leader_name[64];
};
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 zone_id;
uint16 instance_id;
uint8 remove; // 0: added 1: removed
uint32 character_id;
};
#pragma pack()
#endif

View File

@ -592,3 +592,15 @@ std::string numberToWords(unsigned long long int n)
return res;
}
// first letter capitalized and rest made lower case
std::string FormatName(const std::string& char_name)
{
std::string formatted(char_name);
if (!formatted.empty())
{
std::transform(formatted.begin(), formatted.end(), formatted.begin(), ::tolower);
formatted[0] = ::toupper(formatted[0]);
}
return formatted;
}

View File

@ -206,6 +206,6 @@ void MakeLowerString(const char *source, char *target);
void RemoveApostrophes(std::string &s);
std::string convert2digit(int n, std::string suffix);
std::string numberToWords(unsigned long long int n);
std::string FormatName(const std::string& char_name);
#endif

View File

@ -34,7 +34,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9158
#define CURRENT_BINARY_DATABASE_VERSION 9159
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027

View File

@ -375,16 +375,18 @@ OP_DzRemovePlayer=0x0dc1
OP_DzSwapPlayer=0x4995
OP_DzMakeLeader=0x17b2
OP_DzPlayerList=0x1aff
OP_DzJoinExpeditionConfirm=0x30df
OP_DzJoinExpeditionReply=0x15d4
OP_DzExpeditionInvite=0x30df
OP_DzExpeditionInviteResponse=0x15d4
OP_DzExpeditionInfo=0x3861
OP_DzExpeditionList=0x0b3b
OP_DzMemberStatus=0x26c2
OP_DzLeaderStatus=0x4021
OP_DzExpeditionEndsWarning=0x32eb
OP_DzExpeditionLockoutTimers=0x0b3b
OP_DzMemberList=0x348f
OP_DzMemberListName=0x26c2
OP_DzMemberListStatus=0x0000
OP_DzSetLeaderName=0x4021
OP_DzExpeditionEndsWarning=0x32eb
OP_DzCompass=0x0e01 # Was 0x4f09
OP_DzChooseZone=0x6e5e # Maybe 0x29d6
OP_DzChooseZoneReply=0x0000
# New Opcodes
OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ?

View File

@ -368,25 +368,28 @@ OP_AggroMeterLockTarget=0x1643
OP_AggroMeterTargetInfo=0x16bc
OP_AggroMeterUpdate=0x1781
OP_UnderWorld=0x2eb3 # clients sends up when they detect an underworld issue, might be useful for cheat detection
OP_KickPlayers=0x6770
# Expeditions
OP_DzQuit=0xb2e3
OP_DzListTimers=0x7b68
OP_DzAddPlayer=0x4701
OP_DzRemovePlayer=0x1abc
OP_DzSwapPlayer=0x405b
OP_DzMakeLeader=0x543d
OP_DzPlayerList=0x14c6
OP_DzJoinExpeditionConfirm=0x7f4b
OP_DzJoinExpeditionReply=0x1950
OP_DzListTimers=0x7b68
OP_DzExpeditionInvite=0x7f4b
OP_DzExpeditionInviteResponse=0x1950
OP_DzExpeditionInfo=0x9119
OP_DzExpeditionList=0x205f
OP_DzQuit=0xb2e3
OP_DzMemberStatus=0x32f0
OP_DzLeaderStatus=0x3de9
OP_DzExpeditionLockoutTimers=0x205f
OP_DzMemberList=0x5ae4
OP_DzExpeditionEndsWarning=0x383c
OP_DzMemberListName=0x32f0
OP_DzMemberListStatus=0x12F5
OP_DzSetLeaderName=0x3de9
OP_DzExpeditionEndsWarning=0x5189
OP_DzCompass=0x3e0e
OP_DzChooseZone=0x0b7d
OP_DzChooseZoneReply=0x4de1
# New Opcodes
OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ?

View File

@ -367,17 +367,18 @@ OP_DzRemovePlayer=0xa682
OP_DzSwapPlayer=0x0d8d
OP_DzMakeLeader=0x1caa
OP_DzPlayerList=0x74ca
OP_DzJoinExpeditionConfirm=0x1772
OP_DzJoinExpeditionReply=0x3c13
OP_DzExpeditionInvite=0x1772
OP_DzExpeditionInviteResponse=0x3c13
OP_DzExpeditionInfo=0x128e
OP_DzMemberStatus=0x4661
OP_DzLeaderStatus=0x226f
OP_DzExpeditionEndsWarning=0x1879
OP_DzExpeditionList=0x3657
OP_DzExpeditionLockoutTimers=0x3657
OP_DzMemberList=0x74e4
OP_DzMemberListName=0x4661
OP_DzMemberListStatus=0x1d99
OP_DzSetLeaderName=0x226f
OP_DzExpeditionEndsWarning=0x1879
OP_DzCompass=0x35d3
OP_DzChooseZone=0x0d8a
#0x1d99 was grouped with these too but I don't really know it's purpose.
OP_DzChooseZoneReply=0x5a67
# New Opcodes
OP_SpawnPositionUpdate=0x4656 #

View File

@ -348,16 +348,18 @@ OP_DzRemovePlayer=0x2ce8
OP_DzSwapPlayer=0x2c3e
OP_DzMakeLeader=0x1a75
OP_DzPlayerList=0x5116
OP_DzJoinExpeditionConfirm=0x1793
OP_DzJoinExpeditionReply=0x7a6f
OP_DzExpeditionInvite=0x1793
OP_DzExpeditionInviteResponse=0x7a6f
OP_DzExpeditionInfo=0x60a6
OP_DzMemberStatus=0x0516
OP_DzLeaderStatus=0x79d3
OP_DzExpeditionEndsWarning=0x5153
OP_DzExpeditionList=0x02ac
OP_DzExpeditionLockoutTimers=0x02ac
OP_DzMemberList=0x5e14
OP_DzMemberListName=0x0516
OP_DzMemberListStatus=0x0000
OP_DzSetLeaderName=0x79d3
OP_DzExpeditionEndsWarning=0x5153
OP_DzCompass=0x531d
OP_DzChooseZone=0x3c5b
OP_DzChooseZoneReply=0x0000
#Looting
OP_LootRequest=0x36E3 #Trevius 02/16/09

View File

@ -297,17 +297,18 @@ OP_DzRemovePlayer=0x540b
OP_DzSwapPlayer=0x794a
OP_DzMakeLeader=0x0ce9
OP_DzPlayerList=0xada0
OP_DzJoinExpeditionConfirm=0x3817
OP_DzJoinExpeditionReply=0x5da9
OP_DzExpeditionInvite=0x3817
OP_DzExpeditionInviteResponse=0x5da9
OP_DzExpeditionInfo=0x98e
OP_DzMemberStatus=0x1826
OP_DzLeaderStatus=0x7abc
OP_DzExpeditionEndsWarning=0x1c3f
OP_DzExpeditionList=0x7c12
OP_DzExpeditionLockoutTimers=0x7c12
OP_DzMemberList=0x9b6
OP_DzMemberListName=0x1826
OP_DzMemberListStatus=0x330d
OP_DzSetLeaderName=0x7abc
OP_DzExpeditionEndsWarning=0x1c3f
OP_DzCompass=0x28aa
OP_DzChooseZone=0x1022
#0x330d is something but I'm not sure what yet.
OP_DzChooseZoneReply=0x20e7
#bazaar trader stuff stuff:
#become and buy from

View File

@ -377,16 +377,18 @@ OP_DzRemovePlayer=0x054e
OP_DzSwapPlayer=0x4661
OP_DzMakeLeader=0x226f
OP_DzPlayerList=0x74e4
OP_DzJoinExpeditionConfirm=0x3c5e
OP_DzJoinExpeditionReply=0x1154
OP_DzExpeditionInvite=0x3c5e
OP_DzExpeditionInviteResponse=0x1154
OP_DzExpeditionInfo=0x1150
OP_DzMemberStatus=0x2d17
OP_DzLeaderStatus=0x2caf
OP_DzExpeditionEndsWarning=0x6ac2
OP_DzExpeditionList=0x70d8
OP_DzExpeditionLockoutTimers=0x70d8
OP_DzMemberList=0x15c4
OP_DzMemberListName=0x2d17
OP_DzMemberListStatus=0x0d98
OP_DzSetLeaderName=0x2caf
OP_DzExpeditionEndsWarning=0x6ac2
OP_DzCompass=0x01cb
OP_DzChooseZone=0x65e1
OP_DzChooseZoneReply=0xa682
#shroud
OP_ShroudSelectionWindow=0x72ad

View File

@ -412,6 +412,7 @@
9156|2020_08_16_virtual_zonepoints.sql|SHOW COLUMNS from `zone_points` LIKE 'is_virtual'|empty|
9157|2020_09_02_pet_taunting.sql|SHOW COLUMNS from `character_pet_info` LIKE 'taunting'|empty|
9158|2020_12_09_underworld.sql|SHOW COLUMNS from `zone` LIKE 'underworld_teleport_index'|empty|
9159|2020_12_22_expedition_system.sql|SELECT * FROM db_version WHERE version >= 9159|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,82 @@
CREATE TABLE `expeditions` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uuid` VARCHAR(36) NOT NULL,
`dynamic_zone_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`expedition_name` VARCHAR(128) NOT NULL,
`leader_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`min_players` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
`max_players` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
`add_replay_on_join` TINYINT(3) UNSIGNED NOT NULL DEFAULT 1,
`is_locked` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `dynamic_zone_id` (`dynamic_zone_id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
CREATE TABLE `expedition_lockouts` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`expedition_id` INT(10) UNSIGNED NOT NULL,
`event_name` VARCHAR(256) NOT NULL,
`expire_time` DATETIME NOT NULL DEFAULT current_timestamp(),
`duration` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`from_expedition_uuid` VARCHAR(36) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `expedition_id_event_name` (`expedition_id`, `event_name`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
CREATE TABLE `expedition_members` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`expedition_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`character_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`is_current_member` TINYINT(3) UNSIGNED NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
UNIQUE INDEX `expedition_id_character_id` (`expedition_id`, `character_id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
CREATE TABLE `character_expedition_lockouts` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`character_id` INT(10) UNSIGNED NOT NULL,
`expedition_name` VARCHAR(128) NOT NULL,
`event_name` VARCHAR(256) NOT NULL,
`expire_time` DATETIME NOT NULL DEFAULT current_timestamp(),
`duration` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`from_expedition_uuid` VARCHAR(36) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `character_id_expedition_name_event_name` (`character_id`, `expedition_name`, `event_name`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
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`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

View File

@ -9,6 +9,10 @@ SET(world_sources
console.cpp
eql_config.cpp
eqemu_api_world_data_service.cpp
expedition.cpp
expedition_database.cpp
expedition_message.cpp
expedition_state.cpp
launcher_link.cpp
launcher_list.cpp
lfplist.cpp
@ -39,6 +43,10 @@ SET(world_headers
console.h
eql_config.h
eqemu_api_world_data_service.h
expedition.h
expedition_database.h
expedition_message.h
expedition_state.h
launcher_link.h
launcher_list.h
lfplist.h

View File

@ -127,6 +127,9 @@ public:
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
void ProcessTellQueue();
void SetPendingExpeditionInvite(ServerPacket* pack) { p_pending_expedition_invite.reset(pack->Copy()); };
std::unique_ptr<ServerPacket> GetPendingExpeditionInvite() { return std::move(p_pending_expedition_invite); }
private:
void ClearVars(bool iAll = false);
@ -171,6 +174,8 @@ private:
// Tell Queue -- really a vector :D
std::vector<ServerChannelMessage_Struct *> tell_queue;
std::unique_ptr<ServerPacket> p_pending_expedition_invite = nullptr;
};
#endif /*CLIENTENTRY_H_*/

195
world/expedition.cpp Normal file
View File

@ -0,0 +1,195 @@
/**
* 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 "expedition.h"
#include "expedition_database.h"
#include "cliententry.h"
#include "clientlist.h"
#include "zonelist.h"
#include "zoneserver.h"
#include "../common/eqemu_logsys.h"
extern ClientList client_list;
extern ZSList zoneserver_list;
Expedition::Expedition(uint32_t expedition_id, uint32_t dz_id, uint32_t dz_instance_id,
uint32_t dz_zone_id, uint32_t start_time, uint32_t duration, uint32_t leader_id
) :
m_expedition_id(expedition_id),
m_dz_id(dz_id),
m_dz_instance_id(dz_instance_id),
m_dz_zone_id(dz_zone_id),
m_start_time(std::chrono::system_clock::from_time_t(start_time)),
m_duration(duration),
m_leader_id(leader_id)
{
m_expire_time = m_start_time + m_duration;
m_warning_cooldown_timer.Enable();
}
void Expedition::AddMember(uint32_t character_id)
{
auto it = std::find_if(m_member_ids.begin(), m_member_ids.end(),
[&](uint32_t member_id) { return member_id == character_id; });
if (it == m_member_ids.end())
{
m_member_ids.emplace_back(character_id);
}
}
bool Expedition::HasMember(uint32_t character_id)
{
return std::any_of(m_member_ids.begin(), m_member_ids.end(),
[&](uint32_t member_id) { return member_id == character_id; });
}
void Expedition::RemoveMember(uint32_t character_id)
{
m_member_ids.erase(std::remove_if(m_member_ids.begin(), m_member_ids.end(),
[&](uint32_t member_id) { return member_id == character_id; }
), m_member_ids.end());
if (!m_member_ids.empty() && character_id == m_leader_id)
{
ChooseNewLeader();
}
}
void Expedition::ChooseNewLeader()
{
// we don't track expedition member status in world so may choose a linkdead member
// this is fine since it will trigger another change when that member goes offline
auto it = std::find_if(m_member_ids.begin(), m_member_ids.end(), [&](uint32_t member_id) {
auto member_cle = (member_id != m_leader_id) ? client_list.FindCLEByCharacterID(member_id) : nullptr;
return (member_id != m_leader_id && member_cle && member_cle->GetOnline() == CLE_Status::InZone);
});
if (it == m_member_ids.end())
{
// no online members found, fallback to choosing any member
it = std::find_if(m_member_ids.begin(), m_member_ids.end(),
[&](uint32_t member_id) { return (member_id != m_leader_id); });
}
if (it != m_member_ids.end())
{
SetNewLeader(*it);
}
}
bool Expedition::SetNewLeader(uint32_t character_id)
{
if (!HasMember(character_id))
{
return false;
}
LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_expedition_id, m_leader_id, character_id);
ExpeditionDatabase::UpdateLeaderID(m_expedition_id, character_id);
m_leader_id = character_id;
SendZonesLeaderChanged();
return true;
}
void Expedition::SendZonesExpeditionDeleted()
{
uint32_t pack_size = sizeof(ServerExpeditionID_Struct);
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_ExpeditionDeleted, pack_size));
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
buf->expedition_id = GetID();
zoneserver_list.SendPacket(pack.get());
}
void Expedition::SendZonesDurationUpdate()
{
uint32_t packsize = sizeof(ServerExpeditionUpdateDuration_Struct);
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_ExpeditionDzDuration, packsize));
auto packbuf = reinterpret_cast<ServerExpeditionUpdateDuration_Struct*>(pack->pBuffer);
packbuf->expedition_id = GetID();
packbuf->new_duration_seconds = static_cast<uint32_t>(m_duration.count());
zoneserver_list.SendPacket(pack.get());
}
void Expedition::SendZonesExpireWarning(uint32_t minutes_remaining)
{
uint32_t pack_size = sizeof(ServerExpeditionExpireWarning_Struct);
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_ExpeditionExpireWarning, pack_size));
auto buf = reinterpret_cast<ServerExpeditionExpireWarning_Struct*>(pack->pBuffer);
buf->expedition_id = GetID();
buf->minutes_remaining = minutes_remaining;
zoneserver_list.SendPacket(pack.get());
}
void Expedition::SendZonesLeaderChanged()
{
uint32_t pack_size = sizeof(ServerExpeditionLeaderID_Struct);
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_ExpeditionLeaderChanged, pack_size));
auto buf = reinterpret_cast<ServerExpeditionLeaderID_Struct*>(pack->pBuffer);
buf->expedition_id = GetID();
buf->leader_id = m_leader_id;
zoneserver_list.SendPacket(pack.get());
}
void Expedition::UpdateDzSecondsRemaining(uint32_t seconds_remaining)
{
auto now = std::chrono::system_clock::now();
auto update_time = std::chrono::seconds(seconds_remaining);
auto current_remaining = m_expire_time - now;
if (current_remaining > update_time) // reduce only
{
LogExpeditionsDetail(
"Updating expedition [{}] dz instance [{}] seconds remaining to [{}]s",
GetID(), GetInstanceID(), seconds_remaining
);
// preserve original start time and adjust duration instead
m_expire_time = now + update_time;
m_duration = std::chrono::duration_cast<std::chrono::seconds>(m_expire_time - m_start_time);
ExpeditionDatabase::UpdateDzDuration(GetInstanceID(), static_cast<uint32_t>(m_duration.count()));
// update zone level caches and update the actual dz instance's timer
SendZonesDurationUpdate();
}
}
std::chrono::system_clock::duration Expedition::GetRemainingDuration() const
{
return m_expire_time - std::chrono::system_clock::now();
}
void Expedition::CheckExpireWarning()
{
if (m_warning_cooldown_timer.Check(false))
{
using namespace std::chrono_literals;
auto remaining = GetRemainingDuration();
if ((remaining > 14min && remaining < 15min) ||
(remaining > 4min && remaining < 5min) ||
(remaining > 0min && remaining < 1min))
{
int minutes = std::chrono::duration_cast<std::chrono::minutes>(remaining).count() + 1;
SendZonesExpireWarning(minutes);
m_warning_cooldown_timer.Start(70000); // 1 minute 10 seconds
}
}
}

73
world/expedition.h Normal file
View File

@ -0,0 +1,73 @@
/**
* 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 WORLD_EXPEDITION_H
#define WORLD_EXPEDITION_H
#include "../common/timer.h"
#include <chrono>
#include <cstdint>
#include <vector>
class Expedition
{
public:
Expedition() = default;
Expedition(uint32_t expedition_id, uint32_t dz_id, uint32_t dz_instance_id,
uint32_t dz_zone_id, uint32_t expire_time, uint32_t duration, uint32_t leader_id);
void AddMember(uint32_t character_id);
void RemoveMember(uint32_t character_id);
void RemoveAllMembers() { m_member_ids.clear(); }
void CheckExpireWarning();
void ChooseNewLeader();
uint32_t GetID() const { return m_expedition_id; }
uint16_t GetInstanceID() const { return static_cast<uint16_t>(m_dz_instance_id); }
uint16_t GetZoneID() const { return static_cast<uint16_t>(m_dz_zone_id); }
bool HasMember(uint32_t character_id);
bool IsEmpty() const { return m_member_ids.empty(); }
bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); }
bool IsPendingDelete() const { return m_pending_delete; }
bool IsValid() const { return m_expedition_id != 0; }
void SendZonesDurationUpdate();
void SendZonesExpeditionDeleted();
void SendZonesExpireWarning(uint32_t minutes_remaining);
bool SetNewLeader(uint32_t new_leader_id);
void SetPendingDelete(bool pending) { m_pending_delete = pending; }
void UpdateDzSecondsRemaining(uint32_t seconds_remaining);
std::chrono::system_clock::duration GetRemainingDuration() const;
private:
void SendZonesLeaderChanged();
uint32_t m_expedition_id = 0;
uint32_t m_dz_id = 0;
uint32_t m_dz_instance_id = 0;
uint32_t m_dz_zone_id = 0;
uint32_t m_leader_id = 0;
bool m_pending_delete = false;
Timer m_warning_cooldown_timer;
std::vector<uint32_t> m_member_ids;
std::chrono::seconds m_duration;
std::chrono::time_point<std::chrono::system_clock> m_start_time;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
};
#endif

View File

@ -0,0 +1,215 @@
/**
* 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 "expedition_database.h"
#include "expedition.h"
#include "worlddb.h"
void ExpeditionDatabase::PurgeExpiredExpeditions()
{
std::string query = SQL(
SELECT
expeditions.id
FROM expeditions
LEFT JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id
LEFT JOIN instance_list ON dynamic_zones.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
) expedition_members
ON expedition_members.expedition_id = expeditions.id
WHERE
instance_list.id IS NULL
OR expedition_members.member_count 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())
{
std::vector<uint32_t> expedition_ids;
for (auto row = results.begin(); row != results.end(); ++row)
{
expedition_ids.emplace_back(static_cast<uint32_t>(strtoul(row[0], nullptr, 10)));
}
if (!expedition_ids.empty())
{
ExpeditionDatabase::MoveMembersToSafeReturn(expedition_ids);
ExpeditionDatabase::DeleteExpeditions(expedition_ids);
}
}
}
void ExpeditionDatabase::PurgeExpiredCharacterLockouts()
{
std::string query = SQL(
DELETE FROM character_expedition_lockouts
WHERE expire_time <= NOW();
);
database.QueryDatabase(query);
}
std::vector<Expedition> ExpeditionDatabase::LoadExpeditions(uint32_t select_expedition_id)
{
std::vector<Expedition> expeditions;
std::string query = SQL(
SELECT
expeditions.id,
expeditions.dynamic_zone_id,
instance_list.id,
instance_list.zone,
instance_list.start_time,
instance_list.duration,
expeditions.leader_id,
expedition_members.character_id
FROM expeditions
INNER JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id
INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id
INNER JOIN expedition_members ON expedition_members.expedition_id = expeditions.id
AND expedition_members.is_current_member = TRUE
);
if (select_expedition_id != 0)
{
query.append(fmt::format(" WHERE expeditions.id = {};", select_expedition_id));
}
else
{
query.append(" ORDER BY expeditions.id;");
}
auto results = database.QueryDatabase(query);
if (results.Success())
{
uint32_t last_expedition_id = 0;
for (auto row = results.begin(); row != results.end(); ++row)
{
uint32_t expedition_id = strtoul(row[0], nullptr, 10);
if (last_expedition_id != expedition_id)
{
expeditions.emplace_back(
static_cast<uint32_t>(strtoul(row[0], nullptr, 10)), // expedition_id
static_cast<uint32_t>(strtoul(row[1], nullptr, 10)), // dz_id
static_cast<uint32_t>(strtoul(row[2], nullptr, 10)), // dz_instance_id
static_cast<uint32_t>(strtoul(row[3], nullptr, 10)), // dz_zone_id
static_cast<uint32_t>(strtoul(row[4], nullptr, 10)), // start_time
static_cast<uint32_t>(strtoul(row[5], nullptr, 10)), // duration
static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) // leader_id
);
}
last_expedition_id = expedition_id;
uint32_t member_id = static_cast<uint32_t>(strtoul(row[7], nullptr, 10));
expeditions.back().AddMember(member_id);
}
}
return expeditions;
}
Expedition ExpeditionDatabase::LoadExpedition(uint32_t expedition_id)
{
LogExpeditions("Loading expedition [{}] for world cache", expedition_id);
Expedition expedition;
auto expeditions = LoadExpeditions(expedition_id);
if (!expeditions.empty())
{
expedition = expeditions.front();
}
return expedition;
}
void ExpeditionDatabase::DeleteExpeditions(const std::vector<uint32_t>& expedition_ids)
{
LogExpeditionsDetail("Deleting [{}] expedition(s)", expedition_ids.size());
std::string expedition_ids_query = fmt::format("{}", fmt::join(expedition_ids, ","));
if (!expedition_ids_query.empty())
{
auto query = fmt::format("DELETE FROM expeditions WHERE id IN ({});", expedition_ids_query);
database.QueryDatabase(query);
query = fmt::format("DELETE FROM expedition_members WHERE expedition_id IN ({});", expedition_ids_query);
database.QueryDatabase(query);
query = fmt::format("DELETE FROM expedition_lockouts WHERE expedition_id IN ({});", expedition_ids_query);
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::UpdateDzDuration(uint16_t instance_id, uint32_t new_duration)
{
std::string query = fmt::format(
"UPDATE instance_list SET duration = {} WHERE id = {};",
new_duration, instance_id
);
database.QueryDatabase(query);
}
void ExpeditionDatabase::UpdateLeaderID(uint32_t expedition_id, uint32_t leader_id)
{
LogExpeditionsDetail("Updating leader [{}] for expedition [{}]", leader_id, expedition_id);
auto query = fmt::format(SQL(
UPDATE expeditions SET leader_id = {} WHERE id = {};
), leader_id, expedition_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::MoveMembersToSafeReturn(const std::vector<uint32_t>& expedition_ids)
{
LogExpeditionsDetail("Moving members from [{}] expedition(s) to safereturn", expedition_ids.size());
// only offline members still in expired dz zones should be updated here
std::string query = fmt::format(SQL(
UPDATE character_data
INNER JOIN expedition_members ON character_data.id = expedition_members.character_id
INNER JOIN expeditions ON expedition_members.expedition_id = expeditions.id
INNER JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id
INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id
AND character_data.zone_instance = instance_list.id
AND character_data.zone_id = instance_list.zone
SET
zone_id = IF(safe_return_zone_id > 0, safe_return_zone_id, zone_id),
zone_instance = IF(safe_return_zone_id > 0, 0, zone_instance),
x = IF(safe_return_zone_id > 0, safe_return_x, x),
y = IF(safe_return_zone_id > 0, safe_return_y, y),
z = IF(safe_return_zone_id > 0, safe_return_z, z),
heading = IF(safe_return_zone_id > 0, safe_return_heading, heading)
WHERE expeditions.id IN ({});
), fmt::join(expedition_ids, ","));
database.QueryDatabase(query);
}

View File

@ -0,0 +1,41 @@
/**
* 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 WORLD_EXPEDITION_DATABASE_H
#define WORLD_EXPEDITION_DATABASE_H
#include <cstdint>
#include <vector>
class Expedition;
namespace ExpeditionDatabase
{
void DeleteExpeditions(const std::vector<uint32_t>& expedition_ids);
std::vector<Expedition> LoadExpeditions(uint32_t select_expedition_id = 0);
Expedition LoadExpedition(uint32_t expedition_id);
void MoveMembersToSafeReturn(const std::vector<uint32_t>& expedition_ids);
void PurgeExpiredExpeditions();
void PurgeExpiredCharacterLockouts();
void UpdateDzDuration(uint16_t instance_id, uint32_t new_duration);
void UpdateLeaderID(uint32_t expedition_id, uint32_t leader_id);
};
#endif

View File

@ -0,0 +1,229 @@
/**
* 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 "expedition.h"
#include "expedition_message.h"
#include "expedition_state.h"
#include "cliententry.h"
#include "clientlist.h"
#include "zonelist.h"
#include "zoneserver.h"
#include "../common/servertalk.h"
#include <algorithm>
extern ClientList client_list;
extern ZSList zoneserver_list;
void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack)
{
switch (pack->opcode)
{
case ServerOP_ExpeditionChooseNewLeader:
{
ExpeditionMessage::ChooseNewLeader(pack);
break;
}
case ServerOP_ExpeditionCreate:
{
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
expedition_state.AddExpedition(buf->expedition_id);
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionMemberChange:
{
auto buf = reinterpret_cast<ServerExpeditionMemberChange_Struct*>(pack->pBuffer);
expedition_state.MemberChange(buf->expedition_id, buf->char_id, buf->removed);
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionMemberSwap:
{
auto buf = reinterpret_cast<ServerExpeditionMemberSwap_Struct*>(pack->pBuffer);
expedition_state.MemberChange(buf->expedition_id, buf->add_char_id, false);
expedition_state.MemberChange(buf->expedition_id, buf->remove_char_id, true);
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionMembersRemoved:
{
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
expedition_state.RemoveAllMembers(buf->expedition_id);
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionGetOnlineMembers:
{
ExpeditionMessage::GetOnlineMembers(pack);
break;
}
case ServerOP_ExpeditionDzAddPlayer:
{
ExpeditionMessage::AddPlayer(pack);
break;
}
case ServerOP_ExpeditionDzMakeLeader:
{
ExpeditionMessage::MakeLeader(pack);
break;
}
case ServerOP_ExpeditionCharacterLockout:
{
auto buf = reinterpret_cast<ServerExpeditionCharacterLockout_Struct*>(pack->pBuffer);
auto cle = client_list.FindCLEByCharacterID(buf->character_id);
if (cle && cle->Server())
{
cle->Server()->SendPacket(pack);
}
break;
}
case ServerOP_ExpeditionSaveInvite:
{
ExpeditionMessage::SaveInvite(pack);
break;
}
case ServerOP_ExpeditionRequestInvite:
{
ExpeditionMessage::RequestInvite(pack);
break;
}
case ServerOP_ExpeditionSecondsRemaining:
{
auto buf = reinterpret_cast<ServerExpeditionUpdateDuration_Struct*>(pack->pBuffer);
expedition_state.SetSecondsRemaining(buf->expedition_id, buf->new_duration_seconds);
break;
}
}
}
void ExpeditionMessage::AddPlayer(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerDzCommand_Struct*>(pack->pBuffer);
ClientListEntry* invited_cle = client_list.FindCharacter(buf->target_name);
if (invited_cle && invited_cle->Server())
{
// continue in the add target's zone
buf->is_char_online = true;
invited_cle->Server()->SendPacket(pack);
}
else
{
// add target not online, return to inviter
ClientListEntry* inviter_cle = client_list.FindCharacter(buf->requester_name);
if (inviter_cle && inviter_cle->Server())
{
inviter_cle->Server()->SendPacket(pack);
}
}
}
void ExpeditionMessage::MakeLeader(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerDzCommandMakeLeader_Struct*>(pack->pBuffer);
// notify requester (old leader) and new leader of the result
ZoneServer* new_leader_zs = nullptr;
ClientListEntry* new_leader_cle = client_list.FindCharacter(buf->new_leader_name);
if (new_leader_cle && new_leader_cle->Server())
{
auto expedition = expedition_state.GetExpedition(buf->expedition_id);
if (expedition)
{
buf->is_success = expedition->SetNewLeader(new_leader_cle->CharID());
}
buf->is_online = true;
new_leader_zs = new_leader_cle->Server();
new_leader_zs->SendPacket(pack);
}
// if old and new leader are in the same zone only send one message
ClientListEntry* requester_cle = client_list.FindCLEByCharacterID(buf->requester_id);
if (requester_cle && requester_cle->Server() && requester_cle->Server() != new_leader_zs)
{
requester_cle->Server()->SendPacket(pack);
}
}
void ExpeditionMessage::GetOnlineMembers(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerExpeditionCharacters_Struct*>(pack->pBuffer);
// not efficient but only requested during caching
char zone_name[64] = {0};
std::vector<ClientListEntry*> all_clients;
all_clients.reserve(client_list.GetClientCount());
client_list.GetClients(zone_name, all_clients);
for (uint32_t i = 0; i < buf->count; ++i)
{
auto it = std::find_if(all_clients.begin(), all_clients.end(), [&](const ClientListEntry* cle) {
return (cle && cle->CharID() == buf->entries[i].character_id);
});
if (it != all_clients.end())
{
buf->entries[i].character_zone_id = (*it)->zone();
buf->entries[i].character_instance_id = (*it)->instance();
buf->entries[i].character_online = true;
}
}
zoneserver_list.SendPacket(buf->sender_zone_id, buf->sender_instance_id, pack);
}
void ExpeditionMessage::SaveInvite(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerDzCommand_Struct*>(pack->pBuffer);
ClientListEntry* invited_cle = client_list.FindCharacter(buf->target_name);
if (invited_cle)
{
// store packet on cle and re-send it when client requests it
buf->is_char_online = true;
pack->opcode = ServerOP_ExpeditionDzAddPlayer;
invited_cle->SetPendingExpeditionInvite(pack);
}
}
void ExpeditionMessage::RequestInvite(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerExpeditionCharacterID_Struct*>(pack->pBuffer);
ClientListEntry* cle = client_list.FindCLEByCharacterID(buf->character_id);
if (cle)
{
auto invite_pack = cle->GetPendingExpeditionInvite();
if (invite_pack && cle->Server())
{
cle->Server()->SendPacket(invite_pack.get());
}
}
}
void ExpeditionMessage::ChooseNewLeader(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
auto expedition = expedition_state.GetExpedition(buf->expedition_id);
if (expedition)
{
expedition->ChooseNewLeader();
}
}

View File

@ -0,0 +1,37 @@
/**
* 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 WORLD_EXPEDITION_MESSAGE_H
#define WORLD_EXPEDITION_MESSAGE_H
class ServerPacket;
namespace ExpeditionMessage
{
void AddPlayer(ServerPacket* pack);
void ChooseNewLeader(ServerPacket* pack);
void GetOnlineMembers(ServerPacket* pack);
void HandleZoneMessage(ServerPacket* pack);
void MakeLeader(ServerPacket* pack);
void RequestInvite(ServerPacket* pack);
void SaveInvite(ServerPacket* pack);
};
#endif

158
world/expedition_state.cpp Normal file
View File

@ -0,0 +1,158 @@
/**
* 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 "expedition_state.h"
#include "expedition.h"
#include "expedition_database.h"
#include "zonelist.h"
#include "zoneserver.h"
#include "../common/eqemu_logsys.h"
#include <algorithm>
extern ZSList zoneserver_list;
ExpeditionState expedition_state;
Expedition* ExpeditionState::GetExpedition(uint32_t expedition_id)
{
auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(),
[&](const Expedition& expedition) { return expedition.GetID() == expedition_id; });
return (it != m_expeditions.end()) ? &(*it) : nullptr;
}
void ExpeditionState::LoadActiveExpeditions()
{
BenchTimer benchmark;
m_expeditions = ExpeditionDatabase::LoadExpeditions();
auto elapsed = benchmark.elapsed();
LogExpeditions("World caching [{}] expeditions took [{}s]", m_expeditions.size(), elapsed);
}
void ExpeditionState::AddExpedition(uint32_t expedition_id)
{
if (expedition_id == 0)
{
return;
}
auto expedition = ExpeditionDatabase::LoadExpedition(expedition_id);
if (expedition.IsValid())
{
auto existing_expedition = GetExpedition(expedition_id);
if (!existing_expedition)
{
m_expeditions.emplace_back(expedition);
}
}
}
void ExpeditionState::RemoveExpedition(uint32_t expedition_id)
{
m_expeditions.erase(std::remove_if(m_expeditions.begin(), m_expeditions.end(),
[&](const Expedition& expedition) {
return expedition.GetID() == expedition_id;
}
), m_expeditions.end());
}
void ExpeditionState::MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove)
{
auto expedition = GetExpedition(expedition_id);
if (expedition)
{
if (remove) {
expedition->RemoveMember(character_id);
} else {
expedition->AddMember(character_id);
}
}
}
void ExpeditionState::RemoveAllMembers(uint32_t expedition_id)
{
auto expedition = GetExpedition(expedition_id);
if (expedition)
{
expedition->RemoveAllMembers();
}
}
void ExpeditionState::SetSecondsRemaining(uint32_t expedition_id, uint32_t seconds_remaining)
{
auto expedition = GetExpedition(expedition_id);
if (expedition)
{
expedition->UpdateDzSecondsRemaining(seconds_remaining);
}
}
void ExpeditionState::Process()
{
if (!m_process_throttle_timer.Check())
{
return;
}
std::vector<uint32_t> expedition_ids;
for (auto it = m_expeditions.begin(); it != m_expeditions.end();)
{
bool is_deleted = false;
if (it->IsEmpty() || it->IsExpired())
{
// don't delete expedition until its dz instance is empty. this prevents
// an exploit where all members leave expedition and complete an event
// before being kicked from removal timer. the lockout could never be
// applied because the zone expedition cache was already invalidated.
auto dz_zoneserver = zoneserver_list.FindByInstanceID(it->GetInstanceID());
if (!dz_zoneserver || dz_zoneserver->NumPlayers() == 0)
{
LogExpeditions("Expedition [{}] expired or empty, notifying zones and deleting", it->GetID());
expedition_ids.emplace_back(it->GetID());
it->SendZonesExpeditionDeleted();
is_deleted = true;
}
if (it->IsEmpty() && !it->IsPendingDelete() && RuleB(Expedition, EmptyDzShutdownEnabled))
{
it->UpdateDzSecondsRemaining(RuleI(Expedition, EmptyDzShutdownDelaySeconds));
}
it->SetPendingDelete(true);
}
else
{
it->CheckExpireWarning();
}
it = is_deleted ? m_expeditions.erase(it) : it + 1;
}
if (!expedition_ids.empty())
{
ExpeditionDatabase::MoveMembersToSafeReturn(expedition_ids);
ExpeditionDatabase::DeleteExpeditions(expedition_ids);
}
}

50
world/expedition_state.h Normal file
View File

@ -0,0 +1,50 @@
/**
* 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 WORLD_EXPEDITION_STATE_H
#define WORLD_EXPEDITION_STATE_H
#include "../common/rulesys.h"
#include "../common/timer.h"
#include <cstdint>
#include <vector>
extern class ExpeditionState expedition_state;
class Expedition;
class ExpeditionState
{
public:
void AddExpedition(uint32_t expedition_id);
Expedition* GetExpedition(uint32_t expedition_id);
void LoadActiveExpeditions();
void MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove);
void Process();
void RemoveAllMembers(uint32_t expedition_id);
void RemoveExpedition(uint32_t expedition_id);
void SetSecondsRemaining(uint32_t expedition_id, uint32_t seconds_remaining);
private:
std::vector<Expedition> m_expeditions;
Timer m_process_throttle_timer{static_cast<uint32_t>(RuleI(Expedition, WorldExpeditionProcessRateMS))};
};
#endif

View File

@ -88,6 +88,8 @@ union semun {
#include "queryserv.h"
#include "web_interface.h"
#include "console.h"
#include "expedition_database.h"
#include "expedition_state.h"
#include "../common/net/servertalk_server.h"
#include "../zone/data_bucket.h"
@ -423,12 +425,19 @@ int main(int argc, char** argv) {
adventure_manager.LoadLeaderboardInfo();
LogInfo("Purging expired expeditions");
ExpeditionDatabase::PurgeExpiredExpeditions();
ExpeditionDatabase::PurgeExpiredCharacterLockouts();
LogInfo("Purging expired instances");
database.PurgeExpiredInstances();
Timer PurgeInstanceTimer(450000);
PurgeInstanceTimer.Start(450000);
LogInfo("Loading active expeditions");
expedition_state.LoadActiveExpeditions();
LogInfo("Loading char create info");
content_db.LoadCharacterCreateAllocations();
content_db.LoadCharacterCreateCombos();
@ -599,6 +608,7 @@ int main(int argc, char** argv) {
if (PurgeInstanceTimer.Check()) {
database.PurgeExpiredInstances();
database.PurgeAllDeletedDataBuckets();
ExpeditionDatabase::PurgeExpiredCharacterLockouts();
}
if (EQTimeTimer.Check()) {
@ -614,6 +624,7 @@ int main(int argc, char** argv) {
launcher_list.Process();
LFPGroupList.Process();
adventure_manager.Process();
expedition_state.Process();
if (InterserverTimer.Check()) {
InterserverTimer.Start();

View File

@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "ucs.h"
#include "queryserv.h"
#include "world_store.h"
#include "expedition_message.h"
extern ClientList client_list;
extern GroupLFPList LFPGroupList;
@ -1355,6 +1356,52 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
cle->ProcessTellQueue();
break;
}
case ServerOP_CZClientMessageString:
{
auto buf = reinterpret_cast<CZClientMessageString_Struct*>(pack->pBuffer);
client_list.SendPacket(buf->character_name, pack);
break;
}
case ServerOP_ExpeditionLockout:
case ServerOP_ExpeditionLockoutDuration:
case ServerOP_ExpeditionLockState:
case ServerOP_ExpeditionMemberStatus:
case ServerOP_ExpeditionReplayOnJoin:
case ServerOP_ExpeditionDzCompass:
case ServerOP_ExpeditionDzSafeReturn:
case ServerOP_ExpeditionDzZoneIn:
case ServerOP_ExpeditionExpireWarning:
{
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionChooseNewLeader:
case ServerOP_ExpeditionCreate:
case ServerOP_ExpeditionGetOnlineMembers:
case ServerOP_ExpeditionMemberChange:
case ServerOP_ExpeditionMemberSwap:
case ServerOP_ExpeditionMembersRemoved:
case ServerOP_ExpeditionDzAddPlayer:
case ServerOP_ExpeditionDzMakeLeader:
case ServerOP_ExpeditionCharacterLockout:
case ServerOP_ExpeditionSaveInvite:
case ServerOP_ExpeditionRequestInvite:
case ServerOP_ExpeditionSecondsRemaining:
{
ExpeditionMessage::HandleZoneMessage(pack);
break;
}
case ServerOP_DzCharacterChange:
case ServerOP_DzRemoveAllCharacters:
{
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);

View File

@ -22,6 +22,7 @@ SET(zone_sources
corpse.cpp
data_bucket.cpp
doors.cpp
dynamiczone.cpp
effects.cpp
embparser.cpp
embparser_api.cpp
@ -30,6 +31,10 @@ SET(zone_sources
encounter.cpp
entity.cpp
exp.cpp
expedition.cpp
expedition_database.cpp
expedition_lockout_timer.cpp
expedition_request.cpp
fastmath.cpp
fearpath.cpp
forage.cpp
@ -48,6 +53,7 @@ SET(zone_sources
lua_encounter.cpp
lua_entity.cpp
lua_entity_list.cpp
lua_expedition.cpp
lua_general.cpp
lua_group.cpp
lua_hate_list.cpp
@ -100,6 +106,7 @@ SET(zone_sources
perl_client.cpp
perl_doors.cpp
perl_entity.cpp
perl_expedition.cpp
perl_groups.cpp
perl_hateentry.cpp
perl_mob.cpp
@ -165,6 +172,7 @@ SET(zone_headers
corpse.h
data_bucket.h
doors.h
dynamiczone.h
embparser.h
embperl.h
embxs.h
@ -172,6 +180,10 @@ SET(zone_headers
entity.h
errmsg.h
event_codes.h
expedition.h
expedition_database.h
expedition_lockout_timer.h
expedition_request.h
fastmath.h
forage.h
global_loot_manager.h
@ -187,6 +199,7 @@ SET(zone_headers
lua_encounter.h
lua_entity.h
lua_entity_list.h
lua_expedition.h
lua_general.h
lua_group.h
lua_hate_list.h

View File

@ -40,6 +40,10 @@ extern volatile bool RunLoops;
#include "../common/data_verification.h"
#include "../common/profanity_manager.h"
#include "data_bucket.h"
#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"
@ -264,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));
@ -3201,6 +3206,27 @@ void Client::MessageString(uint32 type, uint32 string_id, const char* message1,
safe_delete(outapp);
}
void Client::MessageString(const CZClientMessageString_Struct* msg)
{
if (msg)
{
if (msg->args_size == 0)
{
MessageString(msg->chat_type, msg->string_id);
}
else
{
uint32_t outsize = sizeof(FormattedMessage_Struct) + msg->args_size;
auto outapp = std::unique_ptr<EQApplicationPacket>(new EQApplicationPacket(OP_FormattedMessage, outsize));
auto outbuf = reinterpret_cast<FormattedMessage_Struct*>(outapp->pBuffer);
outbuf->string_id = msg->string_id;
outbuf->type = msg->chat_type;
memcpy(outbuf->message, msg->args, msg->args_size);
QueuePacket(outapp.get());
}
}
}
// helper function, returns true if we should see the message
bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter)
{
@ -3397,6 +3423,13 @@ void Client::LinkDead()
if(raid){
raid->MemberZoned(this);
}
Expedition* expedition = GetExpedition();
if (expedition)
{
expedition->SetMemberStatus(this, ExpeditionMemberStatus::LinkDead);
}
// save_timer.Start(2500);
linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS));
SendAppearancePacket(AT_Linkdead, 1);
@ -6124,21 +6157,19 @@ void Client::CheckEmoteHail(Mob *target, const char* message)
void Client::MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 count)
{
auto outapp = new EQApplicationPacket(OP_DzCompass, sizeof(ExpeditionInfo_Struct) +
sizeof(ExpeditionCompassEntry_Struct) * count);
ExpeditionCompass_Struct *ecs = (ExpeditionCompass_Struct*)outapp->pBuffer;
//ecs->clientid = GetID();
ecs->count = count;
if (count) {
ecs->entries[0].x = in_x;
ecs->entries[0].y = in_y;
ecs->entries[0].z = in_z;
if (count == 0)
{
m_has_quest_compass = false;
}
else
{
m_has_quest_compass = true;
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()
@ -9459,3 +9490,501 @@ void Client::ShowDevToolsMenu()
void Client::SendChatLineBreak(uint16 color) {
Message(color, "------------------------------------------------");
}
void Client::SendCrossZoneMessage(
Client* client, const std::string& character_name, uint16_t chat_type, const std::string& message)
{
// if client is null, falls back to sending a cross zone message by name
if (!client && !character_name.empty())
{
client = entity_list.GetClientByName(character_name.c_str());
}
if (client)
{
client->Message(chat_type, message.c_str());
}
else if (!character_name.empty() && !message.empty())
{
uint32_t pack_size = sizeof(CZMessagePlayer_Struct);
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_CZMessagePlayer, pack_size));
auto buf = reinterpret_cast<CZMessagePlayer_Struct*>(pack->pBuffer);
buf->type = chat_type;
strn0cpy(buf->character_name, character_name.c_str(), sizeof(buf->character_name));
strn0cpy(buf->message, message.c_str(), sizeof(buf->message));
worldserver.SendPacket(pack.get());
}
}
void Client::SendCrossZoneMessageString(
Client* client, const std::string& character_name, uint16_t chat_type,
uint32_t string_id, const std::initializer_list<std::string>& arguments)
{
// if client is null, falls back to sending a cross zone message by name
if (!client && !character_name.empty()) // double check client isn't in this zone
{
client = entity_list.GetClientByName(character_name.c_str());
}
if (!client && character_name.empty())
{
return;
}
SerializeBuffer argument_buffer;
for (const auto& argument : arguments)
{
argument_buffer.WriteString(argument);
}
uint32_t args_size = static_cast<uint32_t>(argument_buffer.size());
uint32_t pack_size = sizeof(CZClientMessageString_Struct) + args_size;
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_CZClientMessageString, pack_size));
auto buf = reinterpret_cast<CZClientMessageString_Struct*>(pack->pBuffer);
buf->string_id = string_id;
buf->chat_type = chat_type;
strn0cpy(buf->character_name, character_name.c_str(), sizeof(buf->character_name));
buf->args_size = args_size;
memcpy(buf->args, argument_buffer.buffer(), argument_buffer.size());
if (client)
{
client->MessageString(buf);
}
else
{
worldserver.SendPacket(pack.get());
}
}
void Client::UpdateExpeditionInfoAndLockouts()
{
// this is processed by client after entering a zone
SendDzCompassUpdate();
m_expedition_lockouts = ExpeditionDatabase::LoadCharacterLockouts(CharacterID());
auto expedition = GetExpedition();
if (expedition)
{
expedition->SendClientExpeditionInfo(this);
// live synchronizes lockouts obtained during the active expedition to
// members once they zone into the expedition's dynamic zone instance
if (expedition->GetDynamicZone().IsCurrentZoneDzInstance())
{
expedition->SyncCharacterLockouts(CharacterID(), m_expedition_lockouts);
expedition->SetMemberStatus(this, ExpeditionMemberStatus::InDynamicZone);
}
else
{
expedition->SetMemberStatus(this, ExpeditionMemberStatus::Online);
}
}
SendExpeditionLockoutTimers();
// ask world for any pending invite we saved from a previous zone
RequestPendingExpeditionInvite();
}
Expedition* Client::CreateExpedition(DynamicZone& dz_instance, ExpeditionRequest& request)
{
return Expedition::TryCreate(this, dz_instance, request);
}
Expedition* Client::CreateExpedition(
const std::string& zone_name, uint32 version, uint32 duration, const std::string& expedition_name,
uint32 min_players, uint32 max_players, bool disable_messages)
{
DynamicZone dz_instance{ zone_name, version, duration, DynamicZoneType::Expedition };
ExpeditionRequest request{ expedition_name, min_players, max_players, disable_messages };
return Expedition::TryCreate(this, dz_instance, request);
}
Expedition* Client::GetExpedition() const
{
if (zone && m_expedition_id)
{
auto expedition_cache_iter = zone->expedition_cache.find(m_expedition_id);
if (expedition_cache_iter != zone->expedition_cache.end())
{
return expedition_cache_iter->second.get();
}
}
return nullptr;
}
void Client::AddExpeditionLockout(const ExpeditionLockoutTimer& lockout, bool update_db)
{
// todo: support for account based lockouts like live AoC expeditions
// if client already has this lockout, we're replacing it with the new one
m_expedition_lockouts.erase(std::remove_if(m_expedition_lockouts.begin(), m_expedition_lockouts.end(),
[&](const ExpeditionLockoutTimer& existing_lockout) {
return existing_lockout.IsSameLockout(lockout);
}
), m_expedition_lockouts.end());
m_expedition_lockouts.emplace_back(lockout);
if (update_db) // for quest api
{
ExpeditionDatabase::InsertCharacterLockouts(CharacterID(), { lockout });
}
SendExpeditionLockoutTimers();
}
void Client::AddNewExpeditionLockout(
const std::string& expedition_name, const std::string& event_name, uint32_t seconds, std::string uuid)
{
auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid);
AddExpeditionLockout(lockout, true);
}
void Client::AddExpeditionLockoutDuration(
const std::string& expedition_name, const std::string& event_name, int seconds,
const std::string& uuid, bool update_db)
{
auto it = std::find_if(m_expedition_lockouts.begin(), m_expedition_lockouts.end(),
[&](const ExpeditionLockoutTimer& lockout) {
return lockout.IsSameLockout(expedition_name, event_name);
});
if (it != m_expedition_lockouts.end())
{
it->AddLockoutTime(seconds);
if (!uuid.empty())
{
it->SetUUID(uuid);
}
if (update_db)
{
ExpeditionDatabase::InsertCharacterLockouts(CharacterID(), { *it });
}
SendExpeditionLockoutTimers();
}
else if (seconds > 0) // missing lockouts inserted for reductions would be instantly expired
{
auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid);
AddExpeditionLockout(lockout, update_db);
}
}
void Client::RemoveExpeditionLockout(
const std::string& expedition_name, const std::string& event_name, bool update_db)
{
m_expedition_lockouts.erase(std::remove_if(m_expedition_lockouts.begin(), m_expedition_lockouts.end(),
[&](const ExpeditionLockoutTimer& lockout) {
return lockout.IsSameLockout(expedition_name, event_name);
}
), m_expedition_lockouts.end());
if (update_db) // for quest api
{
ExpeditionDatabase::DeleteCharacterLockout(CharacterID(), expedition_name, event_name);
}
SendExpeditionLockoutTimers();
}
void Client::RemoveAllExpeditionLockouts(const std::string& expedition_name, bool update_db)
{
if (expedition_name.empty())
{
if (update_db)
{
ExpeditionDatabase::DeleteAllCharacterLockouts(CharacterID());
}
m_expedition_lockouts.clear();
}
else
{
if (update_db)
{
ExpeditionDatabase::DeleteAllCharacterLockouts(CharacterID(), expedition_name);
}
m_expedition_lockouts.erase(std::remove_if(m_expedition_lockouts.begin(), m_expedition_lockouts.end(),
[&](const ExpeditionLockoutTimer& lockout) {
return lockout.GetExpeditionName() == expedition_name;
}
), m_expedition_lockouts.end());
}
SendExpeditionLockoutTimers();
}
const ExpeditionLockoutTimer* Client::GetExpeditionLockout(
const std::string& expedition_name, const std::string& event_name, bool include_expired) const
{
for (const auto& expedition_lockout : m_expedition_lockouts)
{
if ((include_expired || !expedition_lockout.IsExpired()) &&
expedition_lockout.IsSameLockout(expedition_name, event_name))
{
return &expedition_lockout;
}
}
return nullptr;
}
std::vector<ExpeditionLockoutTimer> Client::GetExpeditionLockouts(
const std::string& expedition_name, bool include_expired)
{
std::vector<ExpeditionLockoutTimer> lockouts;
for (const auto& lockout : m_expedition_lockouts)
{
if ((include_expired || !lockout.IsExpired()) &&
lockout.GetExpeditionName() == expedition_name)
{
lockouts.emplace_back(lockout);
}
}
return lockouts;
}
bool Client::HasExpeditionLockout(
const std::string& expedition_name, const std::string& event_name, bool include_expired)
{
return (GetExpeditionLockout(expedition_name, event_name, include_expired) != nullptr);
}
void Client::SendExpeditionLockoutTimers()
{
std::vector<ExpeditionLockoutTimerEntry_Struct> lockout_entries;
// client displays lockouts rounded down to nearest minute, send lockouts
// with 60s offset added to compensate (live does this too)
constexpr uint32_t rounding_seconds = 60;
// erases expired lockouts while building lockout timer list
for (auto it = m_expedition_lockouts.begin(); it != m_expedition_lockouts.end();)
{
uint32_t seconds_remaining = it->GetSecondsRemaining();
if (seconds_remaining == 0)
{
it = m_expedition_lockouts.erase(it);
}
else
{
ExpeditionLockoutTimerEntry_Struct lockout;
strn0cpy(lockout.expedition_name, it->GetExpeditionName().c_str(), sizeof(lockout.expedition_name));
lockout.seconds_remaining = seconds_remaining + rounding_seconds;
lockout.event_type = it->IsReplayTimer() ? Expedition::REPLAY_TIMER_ID : Expedition::EVENT_TIMER_ID;
strn0cpy(lockout.event_name, it->GetEventName().c_str(), sizeof(lockout.event_name));
lockout_entries.emplace_back(lockout);
++it;
}
}
uint32_t lockout_count = static_cast<uint32_t>(lockout_entries.size());
uint32_t lockout_entries_size = sizeof(ExpeditionLockoutTimerEntry_Struct) * lockout_count;
uint32_t outsize = sizeof(ExpeditionLockoutTimers_Struct) + lockout_entries_size;
auto outapp = std::unique_ptr<EQApplicationPacket>(new EQApplicationPacket(OP_DzExpeditionLockoutTimers, outsize));
auto outbuf = reinterpret_cast<ExpeditionLockoutTimers_Struct*>(outapp->pBuffer);
outbuf->count = lockout_count;
if (!lockout_entries.empty())
{
memcpy(outbuf->timers, lockout_entries.data(), lockout_entries_size);
}
QueuePacket(outapp.get());
}
void Client::RequestPendingExpeditionInvite()
{
uint32_t packsize = sizeof(ServerExpeditionCharacterID_Struct);
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_ExpeditionRequestInvite, packsize));
auto packbuf = reinterpret_cast<ServerExpeditionCharacterID_Struct*>(pack->pBuffer);
packbuf->character_id = CharacterID();
worldserver.SendPacket(pack.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()
{
// client may be associated with multiple dynamic zone compasses in this zone
std::vector<DynamicZoneCompassEntry_Struct> compass_entries;
for (const auto& client_dz : GetDynamicZones())
{
auto compass = client_dz.dynamic_zone.GetCompassLocation();
if (zone && zone->GetZoneID() == compass.zone_id && zone->GetInstanceID() == 0)
{
DynamicZoneCompassEntry_Struct entry;
entry.dz_zone_id = static_cast<uint16_t>(client_dz.dynamic_zone.GetZoneID());
entry.dz_instance_id = static_cast<uint16_t>(client_dz.dynamic_zone.GetInstanceID());
entry.dz_type = static_cast<uint32_t>(client_dz.dynamic_zone.GetType());
entry.x = compass.x;
entry.y = compass.y;
entry.z = compass.z;
compass_entries.emplace_back(entry);
}
}
// compass set via MarkSingleCompassLocation()
if (m_has_quest_compass)
{
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->count = count;
memcpy(outbuf->entries, compass_entries.data(), entries_size);
QueuePacket(outapp.get());
}
void Client::GoToDzSafeReturnOrBind(const DynamicZone& dynamic_zone)
{
auto safereturn = dynamic_zone.GetSafeReturnLocation();
LogDynamicZonesDetail(
"Sending character [{}] to safereturn zone [{}] or bind", CharacterID(), safereturn.zone_id
);
if (!dynamic_zone.IsValid() || safereturn.zone_id == 0)
{
GoToBind();
}
else
{
MovePC(safereturn.zone_id, 0, safereturn.x, safereturn.y, safereturn.z, safereturn.heading);
}
}
std::vector<DynamicZoneInfo> Client::GetDynamicZones(uint32_t zone_id, int zone_version)
{
std::vector<DynamicZoneInfo> client_dzs;
// check client systems for any associated dynamic zones optionally filtered by zone
Expedition* expedition = GetExpedition();
if (expedition &&
(zone_id == 0 || expedition->GetDynamicZone().GetZoneID() == zone_id) &&
(zone_version < 0 || expedition->GetDynamicZone().GetZoneVersion() == zone_version))
{
client_dzs.emplace_back(expedition->GetName(), expedition->GetLeaderName(), expedition->GetDynamicZone());
}
// todo: tasks, missions (shared tasks), and quests with an associated dz to zone_id
return client_dzs;
}
void Client::MovePCDynamicZone(uint32 zone_id, int zone_version, bool msg_if_invalid)
{
if (zone_id == 0)
{
return;
}
auto client_dzs = GetDynamicZones(zone_id, zone_version);
if (client_dzs.empty())
{
if (msg_if_invalid)
{
MessageString(Chat::Red, DYNAMICZONE_WAY_IS_BLOCKED); // unconfirmed message
}
}
else if (client_dzs.size() == 1)
{
const DynamicZone& dz = client_dzs[0].dynamic_zone;
DynamicZoneLocation zonein = dz.GetZoneInLocation();
ZoneMode zone_mode = dz.HasZoneInLocation() ? ZoneMode::ZoneSolicited : ZoneMode::ZoneToSafeCoords;
MovePC(zone_id, dz.GetInstanceID(), zonein.x, zonein.y, zonein.z, zonein.heading, 0, zone_mode);
}
else
{
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->count = count;
for (int i = 0; i < client_dzs.size(); ++i)
{
outbuf->choices[i].dz_zone_id = client_dzs[i].dynamic_zone.GetZoneID();
outbuf->choices[i].dz_instance_id = client_dzs[i].dynamic_zone.GetInstanceID();
outbuf->choices[i].dz_type = static_cast<uint32_t>(client_dzs[i].dynamic_zone.GetType());
strn0cpy(outbuf->choices[i].description, client_dzs[i].description.c_str(), sizeof(outbuf->choices[i].description));
strn0cpy(outbuf->choices[i].leader_name, client_dzs[i].leader_name.c_str(), sizeof(outbuf->choices[i].leader_name));
}
QueuePacket(outapp.get());
}
}
void Client::MovePCDynamicZone(const std::string& zone_name, int zone_version, bool msg_if_invalid)
{
auto zone_id = ZoneID(zone_name.c_str());
MovePCDynamicZone(zone_id, zone_version, msg_if_invalid);
}

View File

@ -21,12 +21,18 @@
class Client;
class EQApplicationPacket;
class EQStream;
class DynamicZone;
class Expedition;
class ExpeditionLockoutTimer;
class ExpeditionRequest;
class Group;
class NPC;
class Object;
class Raid;
class Seperator;
class ServerPacket;
struct DynamicZoneInfo;
struct DynamicZoneLocation;
enum WaterRegionType : int;
namespace EQ
@ -283,6 +289,7 @@ public:
uint8 SlotConvert(uint8 slot,bool bracer=false);
void MessageString(uint32 type, uint32 string_id, uint32 distance = 0);
void MessageString(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0);
void MessageString(const CZClientMessageString_Struct* msg);
bool FilteredMessageCheck(Mob *sender, eqFilterType filter);
void FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id);
void FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter,
@ -1104,6 +1111,47 @@ public:
void MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 count=1);
// cross zone client messaging helpers (null client argument will fallback to messaging by name)
static void SendCrossZoneMessage(
Client* client, const std::string& client_name, uint16_t chat_type, const std::string& message);
static void SendCrossZoneMessageString(
Client* client, const std::string& client_name, uint16_t chat_type,
uint32_t string_id, const std::initializer_list<std::string>& arguments = {});
void AddExpeditionLockout(const ExpeditionLockoutTimer& lockout, bool update_db = false);
void AddExpeditionLockoutDuration(const std::string& expedition_name,
const std::string& event_Name, int seconds, const std::string& uuid = {}, bool update_db = false);
void AddNewExpeditionLockout(const std::string& expedition_name,
const std::string& event_name, uint32_t duration, std::string uuid = {});
Expedition* CreateExpedition(DynamicZone& dz_instance, ExpeditionRequest& request);
Expedition* CreateExpedition(
const std::string& zone_name, uint32 version, uint32 duration, const std::string& expedition_name,
uint32 min_players, uint32 max_players, bool disable_messages = 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 std::vector<ExpeditionLockoutTimer>& GetExpeditionLockouts() const { return m_expedition_lockouts; };
std::vector<ExpeditionLockoutTimer> GetExpeditionLockouts(const std::string& expedition_name, bool include_expired = false);
uint32 GetPendingExpeditionInviteID() const { return m_pending_expedition_invite.expedition_id; }
bool HasExpeditionLockout(const std::string& expedition_name, const std::string& event_name, bool include_expired = false);
bool IsInExpedition() const { return m_expedition_id != 0; }
void RemoveAllExpeditionLockouts(const std::string& expedition_name, bool update_db = false);
void RemoveExpeditionLockout(const std::string& expedition_name,
const std::string& event_name, bool update_db = false);
void RequestPendingExpeditionInvite();
void SendExpeditionLockoutTimers();
void SetExpeditionID(uint32 expedition_id) { m_expedition_id = expedition_id; };
void SetPendingExpeditionInvite(ExpeditionInvite&& invite) { m_pending_expedition_invite = invite; }
void UpdateExpeditionInfoAndLockouts();
void DzListTimers();
void SetDzRemovalTimer(bool enable_timer);
void SendDzCompassUpdate();
void GoToDzSafeReturnOrBind(const DynamicZone& dynamic_zone);
void MovePCDynamicZone(uint32 zone_id, int zone_version = -1, bool msg_if_invalid = true);
void MovePCDynamicZone(const std::string& zone_name, int zone_version = -1, bool msg_if_invalid = true);
std::vector<DynamicZoneInfo> GetDynamicZones(uint32_t zone_id = 0, int zone_version = -1);
void CalcItemScale();
bool CalcItemScale(uint32 slot_x, uint32 slot_y); // behavior change: 'slot_y' is now [RANGE]_END and not [RANGE]_END + 1
void DoItemEnterZone();
@ -1557,6 +1605,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;
@ -1658,6 +1707,12 @@ private:
int client_max_level;
uint32 m_expedition_id = 0;
ExpeditionInvite m_pending_expedition_invite { 0 };
std::vector<ExpeditionLockoutTimer> m_expedition_lockouts;
glm::vec3 m_quest_compass;
bool m_has_quest_compass = false;
#ifdef BOTS
public:

View File

@ -49,6 +49,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/zone_numbers.h"
#include "data_bucket.h"
#include "event_codes.h"
#include "expedition.h"
#include "expedition_database.h"
#include "guild_mgr.h"
#include "merc.h"
#include "petitions.h"
@ -191,6 +193,15 @@ void MapOpcodes()
ConnectedOpcodes[OP_DuelResponse2] = &Client::Handle_OP_DuelResponse2;
ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName;
ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye;
ConnectedOpcodes[OP_DzAddPlayer] = &Client::Handle_OP_DzAddPlayer;
ConnectedOpcodes[OP_DzChooseZoneReply] = &Client::Handle_OP_DzChooseZoneReply;
ConnectedOpcodes[OP_DzExpeditionInviteResponse] = &Client::Handle_OP_DzExpeditionInviteResponse;
ConnectedOpcodes[OP_DzListTimers] = &Client::Handle_OP_DzListTimers;
ConnectedOpcodes[OP_DzMakeLeader] = &Client::Handle_OP_DzMakeLeader;
ConnectedOpcodes[OP_DzPlayerList] = &Client::Handle_OP_DzPlayerList;
ConnectedOpcodes[OP_DzRemovePlayer] = &Client::Handle_OP_DzRemovePlayer;
ConnectedOpcodes[OP_DzSwapPlayer] = &Client::Handle_OP_DzSwapPlayer;
ConnectedOpcodes[OP_DzQuit] = &Client::Handle_OP_DzQuit;
ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote;
ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest;
ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage;
@ -266,6 +277,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore;
ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump;
ConnectedOpcodes[OP_KeyRing] = &Client::Handle_OP_KeyRing;
ConnectedOpcodes[OP_KickPlayers] = &Client::Handle_OP_KickPlayers;
ConnectedOpcodes[OP_LDoNButton] = &Client::Handle_OP_LDoNButton;
ConnectedOpcodes[OP_LDoNDisarmTraps] = &Client::Handle_OP_LDoNDisarmTraps;
ConnectedOpcodes[OP_LDoNInspect] = &Client::Handle_OP_LDoNInspect;
@ -885,6 +897,8 @@ void Client::CompleteConnect()
guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID());
}
UpdateExpeditionInfoAndLockouts();
/** Request adventure info **/
auto pack = new ServerPacket(ServerOP_AdventureDataRequest, 64);
strcpy((char*)pack->pBuffer, GetName());
@ -1701,6 +1715,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Task Packets */
LoadClientTaskState();
m_expedition_id = ExpeditionDatabase::GetExpeditionIDFromCharacterID(CharacterID());
/**
* DevTools Load Settings
*/
@ -5604,6 +5620,116 @@ void Client::Handle_OP_Dye(const EQApplicationPacket *app)
return;
}
void Client::Handle_OP_DzAddPlayer(const EQApplicationPacket *app)
{
auto expedition = GetExpedition();
if (expedition)
{
auto dzcmd = reinterpret_cast<ExpeditionCommand_Struct*>(app->pBuffer);
expedition->DzAddPlayer(this, dzcmd->name);
}
else
{
// the only /dz command that sends an error message if no active expedition
Message(Chat::System, DZ_YOU_NOT_ASSIGNED);
}
}
void Client::Handle_OP_DzChooseZoneReply(const EQApplicationPacket *app)
{
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;
}
auto client_dzs = GetDynamicZones();
auto it = std::find_if(client_dzs.begin(), client_dzs.end(), [&](const DynamicZoneInfo dz_info) {
return dz_info.dynamic_zone.IsSameDz(dzmsg->dz_zone_id, dzmsg->dz_instance_id);
});
if (it != client_dzs.end())
{
DynamicZoneLocation loc = it->dynamic_zone.GetZoneInLocation();
ZoneMode zone_mode = it->dynamic_zone.HasZoneInLocation() ? ZoneMode::ZoneSolicited : ZoneMode::ZoneToSafeCoords;
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)
{
auto expedition = Expedition::FindCachedExpeditionByID(m_pending_expedition_invite.expedition_id);
std::string swap_remove_name = m_pending_expedition_invite.swap_remove_name;
m_pending_expedition_invite = { 0 }; // clear before re-validating
if (expedition)
{
auto dzmsg = reinterpret_cast<ExpeditionInviteResponse_Struct*>(app->pBuffer);
expedition->DzInviteResponse(this, dzmsg->accepted, swap_remove_name);
}
}
void Client::Handle_OP_DzListTimers(const EQApplicationPacket *app)
{
DzListTimers();
}
void Client::Handle_OP_DzMakeLeader(const EQApplicationPacket *app)
{
auto expedition = GetExpedition();
if (expedition)
{
auto dzcmd = reinterpret_cast<ExpeditionCommand_Struct*>(app->pBuffer);
expedition->DzMakeLeader(this, dzcmd->name);
}
}
void Client::Handle_OP_DzPlayerList(const EQApplicationPacket *app)
{
auto expedition = GetExpedition();
if (expedition) {
expedition->DzPlayerList(this);
}
}
void Client::Handle_OP_DzRemovePlayer(const EQApplicationPacket *app)
{
auto expedition = GetExpedition();
if (expedition)
{
auto dzcmd = reinterpret_cast<ExpeditionCommand_Struct*>(app->pBuffer);
expedition->DzRemovePlayer(this, dzcmd->name);
}
}
void Client::Handle_OP_DzSwapPlayer(const EQApplicationPacket *app)
{
auto expedition = GetExpedition();
if (expedition)
{
auto dzcmd = reinterpret_cast<ExpeditionCommandSwap_Struct*>(app->pBuffer);
expedition->DzSwapPlayer(this, dzcmd->rem_player_name, dzcmd->add_player_name);
}
}
void Client::Handle_OP_DzQuit(const EQApplicationPacket *app)
{
auto expedition = GetExpedition();
if (expedition) {
expedition->DzQuit(this);
}
}
void Client::Handle_OP_Emote(const EQApplicationPacket *app)
{
if (app->size != sizeof(Emote_Struct)) {
@ -8874,6 +9000,23 @@ void Client::Handle_OP_KeyRing(const EQApplicationPacket *app)
KeyRingList();
}
void Client::Handle_OP_KickPlayers(const EQApplicationPacket *app)
{
auto buf = reinterpret_cast<KickPlayers_Struct*>(app->pBuffer);
if (buf->kick_expedition)
{
auto expedition = GetExpedition();
if (expedition)
{
expedition->DzKickPlayers(this);
}
}
else if (buf->kick_task)
{
// todo: shared tasks
}
}
void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app)
{
if (app->size < sizeof(bool))

View File

@ -101,6 +101,15 @@
void Handle_OP_DuelResponse2(const EQApplicationPacket *app);
void Handle_OP_DumpName(const EQApplicationPacket *app);
void Handle_OP_Dye(const EQApplicationPacket *app);
void Handle_OP_DzAddPlayer(const EQApplicationPacket *app);
void Handle_OP_DzChooseZoneReply(const EQApplicationPacket *app);
void Handle_OP_DzExpeditionInviteResponse(const EQApplicationPacket *app);
void Handle_OP_DzListTimers(const EQApplicationPacket *app);
void Handle_OP_DzMakeLeader(const EQApplicationPacket *app);
void Handle_OP_DzPlayerList(const EQApplicationPacket *app);
void Handle_OP_DzRemovePlayer(const EQApplicationPacket *app);
void Handle_OP_DzSwapPlayer(const EQApplicationPacket *app);
void Handle_OP_DzQuit(const EQApplicationPacket *app);
void Handle_OP_Emote(const EQApplicationPacket *app);
void Handle_OP_EndLootRequest(const EQApplicationPacket *app);
void Handle_OP_EnvDamage(const EQApplicationPacket *app);
@ -174,6 +183,7 @@
void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app);
void Handle_OP_Jump(const EQApplicationPacket *app);
void Handle_OP_KeyRing(const EQApplicationPacket *app);
void Handle_OP_KickPlayers(const EQApplicationPacket *app);
void Handle_OP_LDoNButton(const EQApplicationPacket *app);
void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app);
void Handle_OP_LDoNInspect(const EQApplicationPacket *app);

View File

@ -44,6 +44,7 @@
#include "../common/spdat.h"
#include "../common/string_util.h"
#include "event_codes.h"
#include "expedition.h"
#include "guild_mgr.h"
#include "map.h"
#include "petitions.h"
@ -160,6 +161,12 @@ bool Client::Process() {
if (TaskPeriodic_Timer.Check() && taskstate)
taskstate->TaskPeriodicChecks(this);
if (dynamiczone_removal_timer.Check() && zone && zone->GetInstanceID() != 0)
{
dynamiczone_removal_timer.Disable();
GoToDzSafeReturnOrBind(zone->GetDynamicZone());
}
if (linkdead_timer.Check()) {
LeaveGroup();
Save();
@ -172,6 +179,13 @@ bool Client::Process() {
if (myraid) {
myraid->MemberZoned(this);
}
Expedition* expedition = GetExpedition();
if (expedition)
{
expedition->SetMemberStatus(this, ExpeditionMemberStatus::Offline);
}
return false; //delete client
}
@ -560,6 +574,12 @@ bool Client::Process() {
client_state = CLIENT_LINKDEAD;
AI_Start(CLIENT_LD_TIMEOUT);
SendAppearancePacket(AT_Linkdead, 1);
Expedition* expedition = GetExpedition();
if (expedition)
{
expedition->SetMemberStatus(this, ExpeditionMemberStatus::LinkDead);
}
}
}
@ -691,6 +711,12 @@ void Client::OnDisconnect(bool hard_disconnect) {
}
}
Expedition* expedition = GetExpedition();
if (expedition && !bZoning)
{
expedition->SetMemberStatus(this, ExpeditionMemberStatus::Offline);
}
RemoveAllAuras();
Mob *Other = trade->With();

View File

@ -60,6 +60,7 @@
#include "data_bucket.h"
#include "command.h"
#include "expedition.h"
#include "guild_mgr.h"
#include "map.h"
#include "qglobals.h"
@ -198,6 +199,8 @@ int command_init(void)
command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", 80, command_disarmtrap) ||
command_add("distance", "- Reports the distance between you and your target.", 80, command_distance) ||
command_add("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", 50, command_doanim) ||
command_add("dz", "Manage expeditions and dynamic zone instances", 80, command_dz) ||
command_add("dzkickplayers", "Removes all players from current expedition. (/kickplayers alternative for pre-RoF clients)", 0, command_dzkickplayers) ||
command_add("editmassrespawn", "[name-search] [second-value] - Mass (Zone wide) NPC respawn timer editing command", 100, command_editmassrespawn) ||
command_add("emote", "['name'/'world'/'zone'] [type] [message] - Send an emote message", 80, command_emote) ||
command_add("emotesearch", "Searches NPC Emotes", 80, command_emotesearch) ||
@ -6825,6 +6828,210 @@ void command_doanim(Client *c, const Seperator *sep)
c->DoAnim(atoi(sep->arg[1]),atoi(sep->arg[2]));
}
void command_dz(Client* c, const Seperator* sep)
{
if (!c || !zone) {
return;
}
if (strcasecmp(sep->arg[1], "expedition") == 0)
{
if (strcasecmp(sep->arg[2], "list") == 0)
{
std::vector<Expedition*> expeditions;
for (const auto& expedition : zone->expedition_cache)
{
expeditions.emplace_back(expedition.second.get());
}
std::sort(expeditions.begin(), expeditions.end(),
[](const Expedition* lhs, const Expedition* rhs) {
return lhs->GetID() < rhs->GetID();
});
c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", expeditions.size()).c_str());
for (const auto& expedition : expeditions)
{
auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format(
"#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName());
auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format(
"#zoneinstance {}", expedition->GetInstanceID()), false, "zone");
auto seconds = expedition->GetDynamicZone().GetSecondsRemaining();
c->Message(Chat::White, fmt::format(
"expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]",
expedition->GetID(),
expedition->GetDynamicZoneID(),
expedition->GetName(),
leader_saylink,
zone_saylink,
ZoneName(expedition->GetDynamicZone().GetZoneID()),
expedition->GetDynamicZone().GetZoneID(),
expedition->GetInstanceID(),
expedition->GetDynamicZone().GetZoneVersion(),
expedition->GetMemberCount(),
seconds / 3600, // hours
(seconds / 60) % 60, // minutes
seconds % 60 // seconds
).c_str());
}
}
else if (strcasecmp(sep->arg[2], "reload") == 0)
{
Expedition::CacheAllFromDatabase();
c->Message(Chat::White, fmt::format(
"Reloaded [{}] expeditions to cache from database.", zone->expedition_cache.size()
).c_str());
}
else if (strcasecmp(sep->arg[2], "destroy") == 0 && sep->IsNumber(3))
{
auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10);
auto expedition = Expedition::FindCachedExpeditionByID(expedition_id);
if (expedition)
{
c->Message(Chat::White, fmt::format("Destroying expedition [{}] ({})",
expedition_id, expedition->GetName()).c_str());
expedition->RemoveAllMembers();
}
else
{
c->Message(Chat::Red, fmt::format("Failed to destroy expedition [{}]", sep->arg[3]).c_str());
}
}
else if (strcasecmp(sep->arg[2], "unlock") == 0 && sep->IsNumber(3))
{
auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10);
auto expedition = Expedition::FindCachedExpeditionByID(expedition_id);
if (expedition)
{
c->Message(Chat::White, fmt::format("Unlocking expedition [{}]", expedition_id).c_str());
expedition->SetLocked(false, ExpeditionLockMessage::None, true);
}
else
{
c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", sep->arg[3]).c_str());
}
}
}
else if (strcasecmp(sep->arg[1], "list") == 0)
{
std::string query = SQL(
SELECT
dynamic_zones.id,
dynamic_zones.type,
instance_list.id,
instance_list.zone,
instance_list.version,
instance_list.start_time,
instance_list.duration,
COUNT(instance_list_player.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
ORDER BY dynamic_zones.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[5], nullptr, 10);
auto duration = strtoul(row[6], nullptr, 10);
auto expire_time = std::chrono::system_clock::from_time_t(start_time + duration);
auto now = std::chrono::system_clock::now();
auto remaining = std::chrono::duration_cast<std::chrono::seconds>(expire_time - now);
auto seconds = std::max(0, static_cast<int>(remaining.count()));
bool is_expired = now > expire_time;
if (!is_expired || strcasecmp(sep->arg[2], "all") == 0)
{
uint32_t instance_id = strtoul(row[2], nullptr, 10);
auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#zoneinstance {}", instance_id), false, "zone");
c->Message(Chat::White, fmt::format(
"dz id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]",
strtoul(row[0], nullptr, 10), // dynamic_zone_id
strtoul(row[1], nullptr, 10), // dynamic_zone_type
zone_saylink,
strtoul(row[3], nullptr, 10), // instance_zone_id
instance_id, // instance_id
strtoul(row[4], nullptr, 10), // instance_zone_version
strtoul(row[7], nullptr, 10), // instance member_count
seconds / 3600, // hours
(seconds / 60) % 60, // minutes
seconds % 60 // seconds
).c_str());
}
}
}
}
else if (strcasecmp(sep->arg[1], "lockouts") == 0)
{
if (strcasecmp(sep->arg[2], "remove") == 0 && sep->arg[3][0] != '\0')
{
if (sep->arg[5][0] == '\0')
{
c->Message(Chat::White, fmt::format(
"Removing [{}] lockouts on [{}].", sep->arg[4][0] ? sep->arg[4] : "all", sep->arg[3]
).c_str());
}
else
{
c->Message(Chat::White, fmt::format(
"Removing [{}]:[{}] lockout on [{}].", sep->arg[4], sep->arg[5], sep->arg[3]
).c_str());
}
Expedition::RemoveLockoutsByCharacterName(sep->arg[3], sep->arg[4], sep->arg[5]);
}
}
else if (strcasecmp(sep->arg[1], "makeleader") == 0 && sep->IsNumber(2) && sep->arg[3][0] != '\0')
{
auto expedition_id = std::strtoul(sep->arg[2], nullptr, 10);
auto expedition = Expedition::FindCachedExpeditionByID(expedition_id);
if (expedition)
{
auto char_name = FormatName(sep->arg[3]);
c->Message(Chat::White, fmt::format("Setting expedition [{}] leader to [{}]", expedition_id, char_name).c_str());
expedition->SendWorldMakeLeaderRequest(c->CharacterID(), char_name);
}
else
{
c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", expedition_id).c_str());
}
}
else
{
c->Message(Chat::White, "#dz usage:");
c->Message(Chat::White, "#dz expedition list - list expeditions in current zone cache");
c->Message(Chat::White, "#dz expedition reload - reload expedition zone cache from database");
c->Message(Chat::White, "#dz expedition destroy <expedition_id> - destroy expedition globally (must be in cache)");
c->Message(Chat::White, "#dz expedition unlock <expedition_id> - unlock expedition");
c->Message(Chat::White, "#dz list [all] - list dynamic zone instances from database -- 'all' includes expired");
c->Message(Chat::White, "#dz lockouts remove <char_name> - delete all of character's expedition lockouts");
c->Message(Chat::White, "#dz lockouts remove <char_name> \"<expedition_name>\" - delete lockouts by expedition");
c->Message(Chat::White, "#dz lockouts remove <char_name> \"<expedition_name>\" \"<event_name>\" - delete lockout by expedition event");
c->Message(Chat::White, "#dz makeleader <expedition_id> <character_name> - set new expedition leader");
}
}
void command_dzkickplayers(Client* c, const Seperator* sep)
{
if (c)
{
auto expedition = c->GetExpedition();
if (expedition)
{
expedition->DzKickPlayers(c);
}
}
}
void command_editmassrespawn(Client* c, const Seperator* sep)
{
if (strcasecmp(sep->arg[1], "usage") == 0) {

View File

@ -92,6 +92,8 @@ void command_disablerecipe(Client *c, const Seperator *sep);
void command_disarmtrap(Client *c, const Seperator *sep);
void command_distance(Client *c, const Seperator *sep);
void command_doanim(Client *c, const Seperator *sep);
void command_dz(Client *c, const Seperator *sep);
void command_dzkickplayers(Client *c, const Seperator *sep);
void command_editmassrespawn(Client* c, const Seperator* sep);
void command_emote(Client *c, const Seperator *sep);
void command_emotesearch(Client* c, const Seperator *sep);

View File

@ -786,5 +786,12 @@ struct DamageHitInfo {
EQ::skills::SkillType skill;
};
struct ExpeditionInvite
{
uint32_t expedition_id;
std::string inviter_name;
std::string swap_remove_name;
};
#endif

View File

@ -38,6 +38,7 @@ Child of the Mob class.
#include "corpse.h"
#include "entity.h"
#include "expedition.h"
#include "groups.h"
#include "mob.h"
#include "raids.h"
@ -1275,6 +1276,20 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
return;
}
if (zone && zone->GetInstanceID() != 0)
{
// expeditions may prevent looting based on client's lockouts
auto expedition = Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID());
if (expedition && !expedition->CanClientLootCorpse(client, GetNPCTypeID(), GetID()))
{
client->MessageString(Chat::Red, LOOT_NOT_ALLOWED, inst->GetItem()->Name);
client->QueuePacket(app);
SendEndLootErrorPacket(client);
ResetLooter();
delete inst;
return;
}
}
// do we want this to have a fail option too?
parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0);

View File

@ -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;

544
zone/dynamiczone.cpp Normal file
View File

@ -0,0 +1,544 @@
/**
* 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"
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);
}
}
std::unordered_map<uint32_t, DynamicZone> DynamicZone::LoadMultipleDzFromDatabase(
const std::vector<uint32_t>& dynamic_zone_ids)
{
LogDynamicZonesDetail("Loading dynamic zone data for [{}] instances", dynamic_zone_ids.size());
std::string in_dynamic_zone_ids_query = fmt::format("{}", fmt::join(dynamic_zone_ids, ","));
std::unordered_map<uint32_t, DynamicZone> dynamic_zones;
if (!in_dynamic_zone_ids_query.empty())
{
std::string query = fmt::format(SQL(
{} WHERE dynamic_zones.id IN ({});
), DynamicZoneSelectQuery(), in_dynamic_zone_ids_query);
auto results = database.QueryDatabase(query);
if (results.Success())
{
for (auto row = results.begin(); row != results.end(); ++row)
{
DynamicZone dz;
dz.LoadDatabaseResult(row);
dynamic_zones.emplace(dz.GetID(), dz);
}
}
}
return dynamic_zones;
}
uint32_t DynamicZone::Create()
{
if (m_id != 0)
{
return m_id;
}
if (GetInstanceID() == 0)
{
CreateInstance();
}
m_id = SaveToDatabase();
return m_id;
}
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;
}
m_start_time = std::chrono::system_clock::now();
auto start_time = std::chrono::system_clock::to_time_t(m_start_time);
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.count());
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_never_expires = false;
m_expire_time = m_start_time + m_duration;
return m_instance_id;
}
std::string DynamicZone::DynamicZoneSelectQuery()
{
return std::string(SQL(
SELECT
instance_list.id,
instance_list.zone,
instance_list.version,
instance_list.start_time,
instance_list.duration,
instance_list.never_expires,
dynamic_zones.id,
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
));
}
void DynamicZone::LoadDatabaseResult(MySQLRequestRow& row)
{
m_instance_id = strtoul(row[0], nullptr, 10);
m_zone_id = strtoul(row[1], nullptr, 10);
m_version = strtoul(row[2], nullptr, 10);
m_start_time = std::chrono::system_clock::from_time_t(strtoul(row[3], nullptr, 10));
m_duration = std::chrono::seconds(strtoul(row[4], nullptr, 10));
m_expire_time = m_start_time + m_duration;
m_never_expires = (strtoul(row[5], nullptr, 10) != 0);
m_id = strtoul(row[6], nullptr, 10);
m_type = static_cast<DynamicZoneType>(strtoul(row[7], nullptr, 10));
m_compass.zone_id = strtoul(row[8], nullptr, 10);
m_compass.x = strtof(row[9], nullptr);
m_compass.y = strtof(row[10], nullptr);
m_compass.z = strtof(row[11], nullptr);
m_safereturn.zone_id = strtoul(row[12], nullptr, 10);
m_safereturn.x = strtof(row[13], nullptr);
m_safereturn.y = strtof(row[14], nullptr);
m_safereturn.z = strtof(row[15], nullptr);
m_safereturn.heading = strtof(row[16], nullptr);
m_zonein.x = strtof(row[17], nullptr);
m_zonein.y = strtof(row[18], nullptr);
m_zonein.z = strtof(row[19], nullptr);
m_zonein.heading = strtof(row[20], nullptr);
m_has_zonein = (strtoul(row[21], 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::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(bool enable_removal_timers)
{
if (GetInstanceID() == 0)
{
return;
}
if (enable_removal_timers)
{
// just remove all clients in bulk instead of only characters assigned to the instance
if (IsCurrentZoneDzInstance())
{
for (const auto& client_iter : entity_list.GetClientList())
{
if (client_iter.second)
{
client_iter.second->SetDzRemovalTimer(true);
}
}
}
else if (GetInstanceID() != 0)
{
uint32_t packsize = sizeof(ServerDzCharacter_Struct);
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_DzRemoveAllCharacters, packsize));
auto packbuf = reinterpret_cast<ServerDzCharacter_Struct*>(pack->pBuffer);
packbuf->zone_id = GetZoneID();
packbuf->instance_id = GetInstanceID();
packbuf->remove = true;
packbuf->character_id = 0;
worldserver.SendPacket(pack.get());
}
}
database.RemoveClientsFromInstance(GetInstanceID());
}
void DynamicZone::SaveInstanceMembersToDatabase(const std::vector<uint32_t>& character_ids)
{
LogDynamicZonesDetail("Saving [{}] instance members to database", character_ids.size());
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);
database.QueryDatabase(query);
}
}
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->zone_id = GetZoneID();
packbuf->instance_id = GetInstanceID();
packbuf->remove = removed;
packbuf->character_id = character_id;
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);
}
bool DynamicZone::IsSameDz(uint32_t zone_id, uint32_t instance_id) const
{
return zone_id == m_zone_id && instance_id == m_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::SetUpdatedDuration(uint32_t new_duration)
{
// preserves original start time, just modifies duration and expire time
m_duration = std::chrono::seconds(new_duration);
m_expire_time = m_start_time + m_duration;
LogDynamicZones("Updated zone [{}]:[{}] seconds remaining: [{}]",
m_zone_id, m_instance_id, GetSecondsRemaining());
if (zone && IsCurrentZoneDzInstance())
{
zone->SetInstanceTimer(GetSecondsRemaining());
}
}
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;
}
case ServerOP_DzRemoveAllCharacters:
{
auto buf = reinterpret_cast<ServerDzCharacter_Struct*>(pack->pBuffer);
if (buf->remove)
{
for (const auto& client_list_iter : entity_list.GetClientList())
{
if (client_list_iter.second)
{
client_list_iter.second->SetDzRemovalTimer(true);
}
}
}
break;
}
}
}

132
zone/dynamiczone.h Normal file
View File

@ -0,0 +1,132 @@
/**
* 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_map>
#include <vector>
class MySQLRequestRow;
class ServerPacket;
enum class DynamicZoneType : uint8_t
{
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() = default;
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(uint32_t dz_id) : m_id(dz_id) {}
DynamicZone(DynamicZoneType type) : m_type(type) {}
static std::unordered_map<uint32_t, DynamicZone> LoadMultipleDzFromDatabase(
const std::vector<uint32_t>& dynamic_zone_ids);
static void HandleWorldMessage(ServerPacket* pack);
uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); }
uint32_t GetID() const { return m_id; }
uint16_t GetInstanceID() const { return static_cast<uint16_t>(m_instance_id); }
uint32_t GetSecondsRemaining() const;
uint16_t GetZoneID() const { return static_cast<uint16_t>(m_zone_id); }
uint32_t GetZoneIndex() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); }
uint32_t GetZoneVersion() const { return m_version; }
DynamicZoneType GetType() const { return m_type; }
DynamicZoneLocation GetCompassLocation() const { return m_compass; }
DynamicZoneLocation GetSafeReturnLocation() const { return m_safereturn; }
DynamicZoneLocation GetZoneInLocation() const { return m_zonein; }
void AddCharacter(uint32_t character_id);
uint32_t Create();
uint32_t CreateInstance();
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; }
bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const;
void RemoveAllCharacters(bool enable_removal_timers = true);
void RemoveCharacter(uint32_t character_id);
void SaveInstanceMembersToDatabase(const std::vector<uint32_t>& character_ids);
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 SetUpdatedDuration(uint32_t seconds);
private:
static std::string DynamicZoneSelectQuery();
void LoadDatabaseResult(MySQLRequestRow& row);
void SaveCompassToDatabase();
void SaveSafeReturnToDatabase();
void SaveZoneInLocationToDatabase();
uint32_t SaveToDatabase();
uint32_t m_id = 0;
uint32_t m_zone_id = 0;
uint32_t m_instance_id = 0;
uint32_t m_version = 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::seconds m_duration;
std::chrono::time_point<std::chrono::system_clock> m_start_time;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
};
struct DynamicZoneInfo
{
std::string description; // from owning system
std::string leader_name;
DynamicZone dynamic_zone;
DynamicZoneInfo() = default;
DynamicZoneInfo(std::string desc, std::string leader, const DynamicZone& dz)
: description(std::move(desc)), leader_name(std::move(leader)), dynamic_zone(dz) {}
};
#endif

View File

@ -971,6 +971,9 @@ void PerlembParser::MapFunctions()
"package Doors;"
"&boot_Doors;" // load quest Doors XS
"package Expedition;"
"&boot_Expedition;"
#endif
"package main;"
"}"

View File

@ -29,6 +29,7 @@
#include "embparser.h"
#include "embxs.h"
#include "entity.h"
#include "expedition.h"
#include "queryserv.h"
#include "questmgr.h"
#include "zone.h"
@ -6059,6 +6060,265 @@ XS(XS__SetContentFlag)
XSRETURN_EMPTY;
}
XS(XS__get_expedition);
XS(XS__get_expedition) {
dXSARGS;
if (items != 0) {
Perl_croak(aTHX_ "Usage: quest::get_expedition()");
}
Expedition* RETVAL = nullptr;
if (zone && zone->GetInstanceID() != 0)
{
RETVAL = Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID());
}
EXTEND(sp, 1); // grow stack, function had 0 arguments
ST(0) = sv_newmortal(); // PUSHs(sv_newmortal());
if (RETVAL) {
sv_setref_pv(ST(0), "Expedition", (void*)RETVAL);
}
XSRETURN(1);
}
XS(XS__get_expedition_by_char_id);
XS(XS__get_expedition_by_char_id) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: quest::get_expedition_by_char_id(uint32 character_id)");
}
uint32 character_id = (int)SvUV(ST(0));
Expedition* RETVAL = Expedition::FindCachedExpeditionByCharacterID(character_id);
ST(0) = sv_newmortal();
if (RETVAL) {
sv_setref_pv(ST(0), "Expedition", (void*)RETVAL);
}
XSRETURN(1);
}
XS(XS__get_expedition_by_dz_id);
XS(XS__get_expedition_by_dz_id) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: quest::get_expedition_by_dz_id(uint32 dynamic_zone_id)");
}
uint32 dz_id = (int)SvUV(ST(0));
Expedition* RETVAL = Expedition::FindCachedExpeditionByDynamicZoneID(dz_id);
ST(0) = sv_newmortal();
if (RETVAL) {
sv_setref_pv(ST(0), "Expedition", (void*)RETVAL);
}
XSRETURN(1);
}
XS(XS__get_expedition_by_zone_instance);
XS(XS__get_expedition_by_zone_instance) {
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: quest::GetExpeditionByZoneInstance(uint16 zone_id, uint16 instance_id)");
}
uint16 zone_id = (uint16)SvUV(ST(0));
uint16 instance_id = (uint16)SvUV(ST(1));
Expedition* RETVAL = Expedition::FindCachedExpeditionByZoneInstance(zone_id, instance_id);
ST(0) = sv_newmortal();
if (RETVAL) {
sv_setref_pv(ST(0), "Expedition", (void*)RETVAL);
}
XSRETURN(1);
}
XS(XS__get_expedition_lockout_by_char_id);
XS(XS__get_expedition_lockout_by_char_id) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: quest::get_expedition_lockout_by_char_id(uint32 character_id, string expedition_name, string event_name)");
}
uint32_t character_id = static_cast<uint32_t>(SvUV(ST(0)));
std::string expedition_name = SvPV_nolen(ST(1));
std::string event_name = SvPV_nolen(ST(2));
auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(character_id);
auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const ExpeditionLockoutTimer& lockout) {
return lockout.IsSameLockout(expedition_name, event_name);
});
// mortalize so its refcnt is auto decremented on function exit to avoid leak
HV* hash = (HV*)sv_2mortal((SV*)newHV()); // hash refcnt +1 (mortal -1)
if (it != lockouts.end())
{
hv_store(hash, "remaining", strlen("remaining"), newSVuv(it->GetSecondsRemaining()), 0);
hv_store(hash, "uuid", strlen("uuid"), newSVpv(it->GetExpeditionUUID().c_str(), 0), 0);
}
ST(0) = sv_2mortal(newRV((SV*)hash)); // hash refcnt: 2 (-1 mortal), reference: 1 (-1 mortal)
XSRETURN(1);
}
XS(XS__get_expedition_lockouts_by_char_id);
XS(XS__get_expedition_lockouts_by_char_id) {
dXSARGS;
if (items != 1 && items != 2) {
Perl_croak(aTHX_ "Usage: quest::get_expedition_lockouts_by_char_id(uint32 character_id, [string expedition_name])");
}
HV* hash = newHV(); // hash refcnt +1 (non-mortal, newRV_noinc to not inc)
SV* hash_ref = nullptr; // for expedition event hash if filtering on expedition
uint32_t character_id = static_cast<uint32_t>(SvUV(ST(0)));
std::string expedition_name;
if (items == 2)
{
expedition_name = SvPV_nolen(ST(1));
}
auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(character_id);
for (const auto& lockout : lockouts)
{
uint32_t name_len = static_cast<uint32_t>(lockout.GetExpeditionName().size());
uint32_t event_len = static_cast<uint32_t>(lockout.GetEventName().size());
// hashes are stored through references inside other hashes/arrays. we need
// to wrap newHV in newRV references when inserting nested hash values.
// we use newRV_noinc to not increment the hash's ref count; rv will own it
SV** entry = hv_fetch(hash, lockout.GetExpeditionName().c_str(), name_len, false);
if (!entry)
{
// create expedition entry in hash with its value as ref to event hash
SV* event_hash_ref = newRV_noinc((SV*)newHV()); // ref takes ownership
if (!expedition_name.empty() && lockout.GetExpeditionName() == expedition_name)
{
hash_ref = event_hash_ref; // save ref for filtered expedition return
}
entry = hv_store(hash, lockout.GetExpeditionName().c_str(), name_len, event_hash_ref, 0);
}
// *entry is a reference to expedition's event hash (which it owns). the
// event entry in the hash will contain ref to a lockout detail hash
if (entry && SvROK(*entry) && SvTYPE(SvRV(*entry)) == SVt_PVHV) // is ref to hash type
{
HV* details_hash = newHV(); // refcnt +1, reference will take ownership
hv_store(details_hash, "remaining", strlen("remaining"), newSVuv(lockout.GetSecondsRemaining()), 0);
hv_store(details_hash, "uuid", strlen("uuid"), newSVpv(lockout.GetExpeditionUUID().c_str(), 0), 0);
HV* event_hash = (HV*)SvRV(*entry);
hv_store(event_hash, lockout.GetEventName().c_str(), event_len,
(SV*)newRV_noinc((SV*)details_hash), 0);
}
}
SV* rv = &PL_sv_undef;
if (!expedition_name.empty())
{
rv = hash_ref ? sv_2mortal(hash_ref) : &PL_sv_undef; // ref that owns event hash for expedition
}
else
{
rv = sv_2mortal(newRV_noinc((SV*)hash)); // takes ownership of expedition hash
}
ST(0) = rv;
XSRETURN(1);
}
XS(XS__add_expedition_lockout_all_clients);
XS(XS__add_expedition_lockout_all_clients) {
dXSARGS;
if (items != 3 && items != 4) {
Perl_croak(aTHX_ "Usage: quest::add_expedition_lockout_all_clients(string expedition_name, string event_name, uint32 seconds, [string uuid])");
}
std::string expedition_name = SvPV_nolen(ST(0));
std::string event_name = SvPV_nolen(ST(1));
uint32_t seconds = static_cast<uint32_t>(SvUV(ST(2)));
std::string uuid;
if (items == 4)
{
uuid = SvPV_nolen(ST(3));
}
auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid);
Expedition::AddLockoutClients(lockout);
XSRETURN_EMPTY;
}
XS(XS__add_expedition_lockout_by_char_id);
XS(XS__add_expedition_lockout_by_char_id) {
dXSARGS;
if (items != 4 && items != 5) {
Perl_croak(aTHX_ "Usage: quest::add_expedition_lockout_by_char_id(uint32 character_id, string expedition_name, string event_name, uint32 seconds, [string uuid])");
}
std::string uuid;
if (items == 5)
{
uuid = SvPV_nolen(ST(4));
}
uint32_t character_id = static_cast<uint32_t>(SvUV(ST(0)));
std::string expedition_name = SvPV_nolen(ST(1));
std::string event_name = SvPV_nolen(ST(2));
uint32_t seconds = static_cast<uint32_t>(SvUV(ST(3)));
Expedition::AddLockoutByCharacterID(character_id, expedition_name, event_name, seconds, uuid);
XSRETURN_EMPTY;
}
XS(XS__remove_expedition_lockout_by_char_id);
XS(XS__remove_expedition_lockout_by_char_id) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: quest::remove_expedition_lockout_by_char_id(uint32 character_id, string expedition_name, string event_name)");
}
uint32_t character_id = static_cast<uint32_t>(SvUV(ST(0)));
std::string expedition_name = SvPV_nolen(ST(1));
std::string event_name = SvPV_nolen(ST(2));
Expedition::RemoveLockoutsByCharacterID(character_id, expedition_name, event_name);
XSRETURN_EMPTY;
}
XS(XS__remove_all_expedition_lockouts_by_char_id);
XS(XS__remove_all_expedition_lockouts_by_char_id) {
dXSARGS;
if (items != 1 && items != 2) {
Perl_croak(aTHX_ "Usage: quest::remove_expedition_lockout_by_char_id(uint32 character_id, [string expedition_name])");
}
std::string expedition_name;
if (items == 2)
{
expedition_name = SvPV_nolen(ST(1));
}
uint32_t character_id = static_cast<uint32_t>(SvUV(ST(0)));
Expedition::RemoveLockoutsByCharacterID(character_id, expedition_name);
XSRETURN_EMPTY;
}
/*
This is the callback perl will look for to setup the
quest package's XSUBs
@ -6129,6 +6389,8 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "activespeakactivity"), XS__activespeakactivity, file);
newXS(strcpy(buf, "activespeaktask"), XS__activespeaktask, file);
newXS(strcpy(buf, "activetasksinset"), XS__activetasksinset, file);
newXS(strcpy(buf, "add_expedition_lockout_all_clients"), XS__add_expedition_lockout_all_clients, file);
newXS(strcpy(buf, "add_expedition_lockout_by_char_id"), XS__add_expedition_lockout_by_char_id, file);
newXS(strcpy(buf, "addldonloss"), XS__addldonpoints, file);
newXS(strcpy(buf, "addldonpoints"), XS__addldonpoints, file);
newXS(strcpy(buf, "addldonwin"), XS__addldonpoints, file);
@ -6263,6 +6525,12 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file);
newXS(strcpy(buf, "getclassname"), XS__getclassname, file);
newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file);
newXS(strcpy(buf, "get_expedition"), XS__get_expedition, file);
newXS(strcpy(buf, "get_expedition_by_char_id"), XS__get_expedition_by_char_id, file);
newXS(strcpy(buf, "get_expedition_by_dz_id"), XS__get_expedition_by_dz_id, file);
newXS(strcpy(buf, "get_expedition_by_zone_instance"), XS__get_expedition_by_zone_instance, file);
newXS(strcpy(buf, "get_expedition_lockout_by_char_id"), XS__get_expedition_lockout_by_char_id, file);
newXS(strcpy(buf, "get_expedition_lockouts_by_char_id"), XS__get_expedition_lockouts_by_char_id, file);
newXS(strcpy(buf, "getinventoryslotid"), XS__getinventoryslotid, file);
newXS(strcpy(buf, "getitemname"), XS__getitemname, file);
newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file);
@ -6328,6 +6596,8 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "rain"), XS__rain, file);
newXS(strcpy(buf, "rebind"), XS__rebind, file);
newXS(strcpy(buf, "reloadzonestaticdata"), XS__reloadzonestaticdata, file);
newXS(strcpy(buf, "remove_all_expedition_lockouts_by_char_id"), XS__remove_all_expedition_lockouts_by_char_id, file);
newXS(strcpy(buf, "remove_expedition_lockout_by_char_id"), XS__remove_expedition_lockout_by_char_id, file);
newXS(strcpy(buf, "removeitem"), XS__removeitem, file);
newXS(strcpy(buf, "removetitle"), XS__removetitle, file);
newXS(strcpy(buf, "repopzone"), XS__repopzone, file);

View File

@ -37,6 +37,7 @@ EXTERN_C XS(boot_HateEntry);
EXTERN_C XS(boot_Object);
EXTERN_C XS(boot_Doors);
EXTERN_C XS(boot_PerlPacket);
EXTERN_C XS(boot_Expedition);
#endif
#endif
@ -87,6 +88,7 @@ EXTERN_C void xs_init(pTHX)
newXS(strcpy(buf, "HateEntry::boot_HateEntry"), boot_HateEntry, file);
newXS(strcpy(buf, "Object::boot_Object"), boot_Object, file);
newXS(strcpy(buf, "Doors::boot_Doors"), boot_Doors, file);
newXS(strcpy(buf, "Expedition::boot_Expedition"), boot_Expedition, file);
;
#endif
#endif

View File

@ -32,6 +32,7 @@
#include "../common/features.h"
#include "../common/guilds.h"
#include "dynamiczone.h"
#include "guild_mgr.h"
#include "petitions.h"
#include "quest_parser_collection.h"
@ -5202,3 +5203,25 @@ std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float d
return mob_list;
}
void EntityList::GateAllClientsToSafeReturn()
{
DynamicZone dz;
if (zone)
{
dz = zone->GetDynamicZone();
LogDynamicZones(
"Sending all clients in zone: [{}] instance: [{}] to dz safereturn or bind",
zone->GetZoneID(), zone->GetInstanceID()
);
}
for (const auto& client_list_iter : client_list)
{
if (client_list_iter.second)
{
// falls back to gating clients to bind if dz invalid
client_list_iter.second->GoToDzSafeReturnOrBind(dz);
}
}
}

View File

@ -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);

2281
zone/expedition.cpp Normal file

File diff suppressed because it is too large Load Diff

242
zone/expedition.h Normal file
View File

@ -0,0 +1,242 @@
/**
* 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 EXPEDITION_H
#define EXPEDITION_H
#include "dynamiczone.h"
#include "expedition_lockout_timer.h"
#include "../common/eq_constants.h"
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
class Client;
class EQApplicationPacket;
struct ExpeditionInvite;
class ExpeditionRequest;
class MySQLRequestResult;
class ServerPacket;
extern const char* const DZ_YOU_NOT_ASSIGNED;
extern const char* const EXPEDITION_OTHER_BELONGS;
extern const char* const CREATE_NOT_ALL_ADDED;
enum class ExpeditionMemberStatus : uint8_t
{
Unknown = 0,
Online,
Offline,
InDynamicZone,
LinkDead
};
enum class ExpeditionLockMessage : uint8_t
{
None = 0,
Close,
Begin
};
struct ExpeditionMember
{
uint32_t char_id = 0;
std::string name;
ExpeditionMemberStatus status = ExpeditionMemberStatus::Online;
ExpeditionMember() = default;
ExpeditionMember(uint32_t char_id_, const std::string& name_)
: char_id(char_id_), name(name_) {}
ExpeditionMember(uint32_t char_id_, const std::string& name_, ExpeditionMemberStatus status_)
: char_id(char_id_), name(name_), status(status_) {}
bool IsValid() const { return char_id != 0 && !name.empty(); }
};
class Expedition
{
public:
Expedition() = delete;
Expedition(uint32_t id, const std::string& uuid, const DynamicZone& dz, const std::string& expedition_name,
const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players);
static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, ExpeditionRequest& request);
static void CacheFromDatabase(uint32_t expedition_id);
static bool CacheAllFromDatabase();
static Expedition* FindCachedExpeditionByCharacterID(uint32_t character_id);
static Expedition* FindCachedExpeditionByCharacterName(const std::string& char_name);
static Expedition* FindCachedExpeditionByDynamicZoneID(uint32_t dz_id);
static Expedition* FindCachedExpeditionByID(uint32_t expedition_id);
static Expedition* FindCachedExpeditionByZoneInstance(uint32_t zone_id, uint32_t instance_id);
static std::vector<ExpeditionLockoutTimer> GetExpeditionLockoutsByCharacterID(uint32_t character_id);
static void HandleWorldMessage(ServerPacket* pack);
static void AddLockoutByCharacterID(uint32_t character_id, const std::string& expedition_name,
const std::string& event_name, uint32_t seconds, const std::string& uuid = {});
static void AddLockoutByCharacterName(const std::string& character_name, const std::string& expedition_name,
const std::string& event_name, uint32_t seconds, const std::string& uuid = {});
static bool HasLockoutByCharacterID(uint32_t character_id,
const std::string& expedition_name, const std::string& event_name);
static bool HasLockoutByCharacterName(const std::string& character_name,
const std::string& expedition_name, const std::string& event_name);
static void RemoveLockoutsByCharacterID(uint32_t character_id,
const std::string& expedition_name = {}, const std::string& event_name = {});
static void RemoveLockoutsByCharacterName(const std::string& character_name,
const std::string& expedition_name = {}, const std::string& event_name = {});
static void AddLockoutClients(const ExpeditionLockoutTimer& lockout, uint32_t exclude_id = 0);
uint32_t GetDynamicZoneID() const { return m_dynamiczone.GetID(); }
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::string& GetUUID() const { return m_uuid; }
const std::unordered_map<std::string, ExpeditionLockoutTimer>& GetLockouts() const { return m_lockouts; }
const std::vector<ExpeditionMember>& GetMembers() const { return m_members; }
bool AddMember(const std::string& add_char_name, uint32_t add_char_id);
bool HasMember(const std::string& character_name);
bool HasMember(uint32_t character_id);
void RemoveAllMembers(bool enable_removal_timers = true);
bool RemoveMember(const std::string& remove_char_name);
void SetMemberStatus(Client* client, ExpeditionMemberStatus status);
void SwapMember(Client* add_client, const std::string& remove_char_name);
void SetLocked(bool lock_expedition, ExpeditionLockMessage lock_msg,
bool update_db = false, uint32_t msg_color = Chat::Yellow);
void AddLockout(const std::string& event_name, uint32_t seconds);
void AddLockoutDuration(const std::string& event_name, int seconds, bool members_only = true);
void AddReplayLockout(uint32_t seconds);
void AddReplayLockoutDuration(int seconds, bool members_only = true);
bool HasLockout(const std::string& event_name);
bool HasReplayLockout();
void RemoveLockout(const std::string& event_name);
void SetReplayLockoutOnMemberJoin(bool add_on_join, bool update_db = false);
void SyncCharacterLockouts(uint32_t character_id, std::vector<ExpeditionLockoutTimer>& client_lockouts);
void UpdateLockoutDuration(const std::string& event_name, uint32_t seconds, bool members_only = true);
bool CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id);
std::string GetLootEventByNPCTypeID(uint32_t npc_id);
std::string GetLootEventBySpawnID(uint32_t spawn_id);
void SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string& event_name);
void SetLootEventBySpawnID(uint32_t spawn_id, const std::string& event_name);
void SendClientExpeditionInfo(Client* client);
void SendWorldMakeLeaderRequest(uint32_t requester_id, const std::string& new_leader_name);
void SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name);
void DzAddPlayer(Client* requester, const std::string& add_char_name, const std::string& swap_remove_name = {});
void DzAddPlayerContinue(std::string leader_name, std::string add_char_name, std::string swap_remove_name = {});
void DzInviteResponse(Client* add_client, bool accepted, const std::string& swap_remove_name);
void DzMakeLeader(Client* requester, std::string new_leader_name);
void DzPlayerList(Client* requester);
void DzRemovePlayer(Client* requester, std::string remove_char_name);
void DzSwapPlayer(Client* requester, std::string remove_char_name, std::string add_char_name);
void DzQuit(Client* requester);
void DzKickPlayers(Client* requester);
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 SetDzSecondsRemaining(uint32_t seconds_remaining);
void SetDzZoneInLocation(float x, float y, float z, float heading, bool update_db = false);
static const int32_t REPLAY_TIMER_ID;
static const int32_t EVENT_TIMER_ID;
private:
static void CacheExpeditions(MySQLRequestResult& results);
static void SendWorldGetOnlineMembers(const std::vector<std::pair<uint32_t, uint32_t>>& expedition_character_ids);
static void SendWorldCharacterLockout(uint32_t character_id, const ExpeditionLockoutTimer& lockout, bool remove);
void AddLockout(const ExpeditionLockoutTimer& lockout, bool members_only = false);
void AddLockoutDurationClients(const ExpeditionLockoutTimer& lockout, int seconds, uint32_t exclude_id = 0);
void AddInternalMember(const std::string& char_name, uint32_t char_id, ExpeditionMemberStatus status);
bool ConfirmLeaderCommand(Client* requester);
bool ProcessAddConflicts(Client* leader_client, Client* add_client, bool swapping);
void ProcessLeaderChanged(uint32_t new_leader_id);
void ProcessLockoutDuration(const ExpeditionLockoutTimer& lockout, int seconds, bool members_only = false);
void ProcessLockoutUpdate(const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false);
void ProcessMakeLeader(Client* old_leader, Client* new_leader,
const std::string& new_leader_name, bool is_success, bool is_online);
void ProcessMemberAdded(const std::string& added_char_name, uint32_t added_char_id);
void ProcessMemberRemoved(const std::string& removed_char_name, uint32_t removed_char_id);
void SaveLockouts(ExpeditionRequest& request);
void SaveMembers(ExpeditionRequest& request);
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>& args = {});
void SendMembersExpireWarning(uint32_t minutes);
void SendNewMemberAddedToZoneMembers(const std::string& added_name);
void SendUpdatesToZoneMembers(bool clear = false, bool message_on_clear = true);
void SendWorldDzLocationUpdate(uint16_t server_opcode, const DynamicZoneLocation& location);
void SendWorldExpeditionUpdate(uint16_t server_opcode);
void SendWorldAddPlayerInvite(const std::string& inviter_name, const std::string& swap_remove_name,
const std::string& add_name, bool pending = false);
void SendWorldLockoutDuration(
const ExpeditionLockoutTimer& lockout, int seconds, bool members_only = false);
void SendWorldLockoutUpdate(
const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false);
void SendWorldMemberChanged(const std::string& char_name, uint32_t char_id, bool remove);
void SendWorldMemberStatus(uint32_t character_id, ExpeditionMemberStatus status);
void SendWorldMemberSwapped(const std::string& remove_char_name, uint32_t remove_char_id,
const std::string& add_char_name, uint32_t add_char_id);
void SendWorldSetSecondsRemaining(uint32_t seconds_remaining);
void SendWorldSettingChanged(uint16_t server_opcode, bool setting_value);
void TryAddClient(Client* add_client, const std::string& inviter_name,
const std::string& swap_remove_name, Client* leader_client = nullptr);
void UpdateDzDuration(uint32_t new_duration) { m_dynamiczone.SetUpdatedDuration(new_duration); }
void UpdateMemberStatus(uint32_t update_character_id, ExpeditionMemberStatus status);
ExpeditionMember GetMemberData(uint32_t character_id);
ExpeditionMember GetMemberData(const std::string& character_name);
std::unique_ptr<EQApplicationPacket> CreateExpireWarningPacket(uint32_t minutes_remaining);
std::unique_ptr<EQApplicationPacket> CreateInfoPacket(bool clear = false);
std::unique_ptr<EQApplicationPacket> CreateInvitePacket(const std::string& inviter_name, const std::string& swap_remove_name);
std::unique_ptr<EQApplicationPacket> CreateMemberListPacket(bool clear = false);
std::unique_ptr<EQApplicationPacket> CreateMemberListNamePacket(const std::string& name, bool remove_name);
std::unique_ptr<EQApplicationPacket> CreateMemberListStatusPacket(const std::string& name, ExpeditionMemberStatus status);
std::unique_ptr<EQApplicationPacket> CreateLeaderNamePacket();
uint32_t m_id = 0;
uint32_t m_min_players = 0;
uint32_t m_max_players = 0;
bool m_is_locked = false;
bool m_add_replay_on_join = true;
std::string m_uuid;
std::string m_expedition_name;
DynamicZone m_dynamiczone { DynamicZoneType::Expedition };
ExpeditionMember m_leader;
std::vector<ExpeditionMember> m_members;
std::unordered_map<std::string, ExpeditionLockoutTimer> m_lockouts;
std::unordered_map<uint32_t, std::string> m_npc_loot_events; // only valid inside dz zone
std::unordered_map<uint32_t, std::string> m_spawn_loot_events; // only valid inside dz zone
};
#endif

View File

@ -0,0 +1,643 @@
/**
* 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 "expedition_database.h"
#include "expedition.h"
#include "expedition_lockout_timer.h"
#include "zonedb.h"
#include "../common/database.h"
#include "../common/string_util.h"
#include <fmt/core.h>
uint32_t ExpeditionDatabase::InsertExpedition(
const std::string& uuid, uint32_t dz_id, const std::string& expedition_name,
uint32_t leader_id, uint32_t min_players, uint32_t max_players)
{
LogExpeditionsDetail(
"Inserting new expedition [{}] leader [{}] uuid [{}]", expedition_name, leader_id, uuid
);
std::string query = fmt::format(SQL(
INSERT INTO expeditions
(uuid, dynamic_zone_id, expedition_name, leader_id, min_players, max_players)
VALUES
('{}', {}, '{}', {}, {}, {});
), uuid, dz_id, EscapeString(expedition_name), leader_id, min_players, max_players);
auto results = database.QueryDatabase(query);
if (!results.Success())
{
LogExpeditions("Failed to obtain an expedition id for [{}]", expedition_name);
return 0;
}
return results.LastInsertedID();
}
std::string ExpeditionDatabase::LoadExpeditionsSelectQuery()
{
return std::string(SQL(
SELECT
expeditions.id,
expeditions.uuid,
expeditions.dynamic_zone_id,
expeditions.expedition_name,
expeditions.leader_id,
expeditions.min_players,
expeditions.max_players,
expeditions.add_replay_on_join,
expeditions.is_locked,
character_data.name leader_name,
expedition_members.character_id,
member_data.name
FROM expeditions
INNER JOIN character_data ON expeditions.leader_id = character_data.id
INNER JOIN expedition_members ON expeditions.id = expedition_members.expedition_id
AND expedition_members.is_current_member = TRUE
INNER JOIN character_data member_data ON expedition_members.character_id = member_data.id
));
}
MySQLRequestResult ExpeditionDatabase::LoadExpedition(uint32_t expedition_id)
{
LogExpeditionsDetail("Loading expedition [{}]", expedition_id);
std::string query = fmt::format(SQL(
{} WHERE expeditions.id = {};
), LoadExpeditionsSelectQuery(), expedition_id);
return database.QueryDatabase(query);
}
MySQLRequestResult ExpeditionDatabase::LoadAllExpeditions()
{
LogExpeditionsDetail("Loading all expeditions from database");
std::string query = fmt::format(SQL(
{} ORDER BY expeditions.id;
), LoadExpeditionsSelectQuery());
return database.QueryDatabase(query);
}
std::vector<ExpeditionLockoutTimer> ExpeditionDatabase::LoadCharacterLockouts(uint32_t character_id)
{
LogExpeditionsDetail("Loading character [{}] lockouts", character_id);
std::vector<ExpeditionLockoutTimer> lockouts;
auto query = fmt::format(SQL(
SELECT
from_expedition_uuid,
expedition_name,
event_name,
UNIX_TIMESTAMP(expire_time),
duration
FROM character_expedition_lockouts
WHERE character_id = {} AND expire_time > NOW();
), character_id);
auto results = database.QueryDatabase(query);
if (results.Success())
{
for (auto row = results.begin(); row != results.end(); ++row)
{
lockouts.emplace_back(
row[0], // expedition_uuid
row[1], // expedition_name
row[2], // event_name
strtoull(row[3], nullptr, 10), // expire_time
static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) // duration
);
}
}
return lockouts;
}
std::vector<ExpeditionLockoutTimer> ExpeditionDatabase::LoadCharacterLockouts(
uint32_t character_id, const std::string& expedition_name)
{
LogExpeditionsDetail("Loading character [{}] lockouts for [{}]", character_id, expedition_name);
std::vector<ExpeditionLockoutTimer> lockouts;
auto query = fmt::format(SQL(
SELECT
from_expedition_uuid,
event_name,
UNIX_TIMESTAMP(expire_time),
duration
FROM character_expedition_lockouts
WHERE
character_id = {}
AND expire_time > NOW()
AND expedition_name = '{}';
), character_id, EscapeString(expedition_name));
auto results = database.QueryDatabase(query);
if (results.Success())
{
for (auto row = results.begin(); row != results.end(); ++row)
{
lockouts.emplace_back(
row[0], // expedition_uuid
expedition_name,
row[1], // event_name
strtoull(row[2], nullptr, 10), // expire_time
static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) // duration
);
}
}
return lockouts;
}
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>>
ExpeditionDatabase::LoadMultipleExpeditionLockouts(
const std::vector<uint32_t>& expedition_ids)
{
LogExpeditionsDetail("Loading internal lockouts for [{}] expeditions", expedition_ids.size());
std::string in_expedition_ids_query = fmt::format("{}", fmt::join(expedition_ids, ","));
// these are loaded into the same container type expeditions use to store lockouts
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>> lockouts;
if (!in_expedition_ids_query.empty())
{
std::string query = fmt::format(SQL(
SELECT
expedition_lockouts.expedition_id,
expedition_lockouts.from_expedition_uuid,
expeditions.expedition_name,
expedition_lockouts.event_name,
UNIX_TIMESTAMP(expedition_lockouts.expire_time),
expedition_lockouts.duration
FROM expedition_lockouts
INNER JOIN expeditions ON expedition_lockouts.expedition_id = expeditions.id
WHERE expedition_id IN ({})
ORDER BY expedition_id;
), in_expedition_ids_query);
auto results = database.QueryDatabase(query);
if (results.Success())
{
for (auto row = results.begin(); row != results.end(); ++row)
{
auto expedition_id = strtoul(row[0], nullptr, 10);
lockouts[expedition_id].emplace(row[3], ExpeditionLockoutTimer{
row[1], // expedition_uuid
row[2], // expedition_name
row[3], // event_name
strtoull(row[4], nullptr, 10), // expire_time
static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) // original duration
});
}
}
}
return lockouts;
}
MySQLRequestResult ExpeditionDatabase::LoadMembersForCreateRequest(
const std::vector<std::string>& character_names, const std::string& expedition_name)
{
LogExpeditionsDetail(
"Loading data of [{}] characters for [{}] request", character_names.size(), expedition_name
);
std::string in_character_names_query;
for (const auto& character_name : character_names)
{
fmt::format_to(std::back_inserter(in_character_names_query), "'{}',", character_name);
}
MySQLRequestResult results;
if (!in_character_names_query.empty())
{
in_character_names_query.pop_back(); // trailing comma
// for create validation, loads each character's lockouts and possible current expedition
auto query = fmt::format(SQL(
SELECT
character_data.id,
character_data.name,
member.expedition_id,
lockout.from_expedition_uuid,
UNIX_TIMESTAMP(lockout.expire_time),
lockout.duration,
lockout.event_name
FROM character_data
LEFT JOIN character_expedition_lockouts lockout
ON character_data.id = lockout.character_id
AND lockout.expire_time > NOW()
AND lockout.expedition_name = '{}'
LEFT JOIN expedition_members member
ON character_data.id = member.character_id
AND member.is_current_member = TRUE
WHERE character_data.name IN ({})
ORDER BY FIELD(character_data.name, {})
), EscapeString(expedition_name), in_character_names_query, in_character_names_query);
results = database.QueryDatabase(query);
}
return results;
}
void ExpeditionDatabase::DeleteAllCharacterLockouts(uint32_t character_id)
{
LogExpeditionsDetail("Deleting all character [{}] lockouts", character_id);
if (character_id != 0)
{
std::string query = fmt::format(SQL(
DELETE FROM character_expedition_lockouts
WHERE character_id = {};
), character_id);
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::DeleteAllCharacterLockouts(
uint32_t character_id, const std::string& expedition_name)
{
LogExpeditionsDetail("Deleting all character [{}] lockouts for [{}]", character_id, expedition_name);
if (character_id != 0 && !expedition_name.empty())
{
std::string query = fmt::format(SQL(
DELETE FROM character_expedition_lockouts
WHERE character_id = {} AND expedition_name = '{}';
), character_id, EscapeString(expedition_name));
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::DeleteCharacterLockout(
uint32_t character_id, const std::string& expedition_name, const std::string& event_name)
{
LogExpeditionsDetail(
"Deleting character [{}] lockout: [{}]:[{}]", character_id, expedition_name, event_name
);
auto query = fmt::format(SQL(
DELETE FROM character_expedition_lockouts
WHERE
character_id = {}
AND expedition_name = '{}'
AND event_name = '{}';
), character_id, EscapeString(expedition_name), EscapeString(event_name));
database.QueryDatabase(query);
}
void ExpeditionDatabase::DeleteMembersLockout(
const std::vector<ExpeditionMember>& members,
const std::string& expedition_name, const std::string& event_name)
{
LogExpeditionsDetail("Deleting members lockout: [{}]:[{}]", expedition_name, event_name);
std::string query_character_ids;
for (const auto& member : members)
{
fmt::format_to(std::back_inserter(query_character_ids), "{},", member.char_id);
}
if (!query_character_ids.empty())
{
query_character_ids.pop_back(); // trailing comma
auto query = fmt::format(SQL(
DELETE FROM character_expedition_lockouts
WHERE character_id
IN ({})
AND expedition_name = '{}'
AND event_name = '{}';
), query_character_ids, EscapeString(expedition_name), EscapeString(event_name));
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::DeleteLockout(uint32_t expedition_id, const std::string& event_name)
{
LogExpeditionsDetail("Deleting expedition [{}] lockout event [{}]", expedition_id, event_name);
auto query = fmt::format(SQL(
DELETE FROM expedition_lockouts
WHERE expedition_id = {} AND event_name = '{}';
), expedition_id, EscapeString(event_name));
database.QueryDatabase(query);
}
uint32_t ExpeditionDatabase::GetExpeditionIDFromCharacterID(uint32_t character_id)
{
LogExpeditionsDetail("Getting expedition id for character [{}]", character_id);
uint32_t expedition_id = 0;
auto query = fmt::format(SQL(
SELECT expedition_id FROM expedition_members
WHERE character_id = {} AND is_current_member = TRUE;
), character_id);
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() > 0)
{
auto row = results.begin();
expedition_id = strtoul(row[0], nullptr, 10);
}
return expedition_id;
}
void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id,
const std::vector<ExpeditionLockoutTimer>& lockouts)
{
LogExpeditionsDetail("Inserting [{}] lockouts for character [{}]", lockouts.size(), character_id);
std::string insert_values;
for (const auto& lockout : lockouts)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
character_id,
lockout.GetExpireTime(),
lockout.GetDuration(),
lockout.GetExpeditionUUID(),
EscapeString(lockout.GetExpeditionName()),
EscapeString(lockout.GetEventName())
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO character_expedition_lockouts
(character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name)
VALUES {}
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration);
), insert_values);
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::InsertMembersLockout(
const std::vector<ExpeditionMember>& members, const ExpeditionLockoutTimer& lockout)
{
LogExpeditionsDetail(
"Inserting members lockout [{}]:[{}] with expire time [{}]",
lockout.GetExpeditionName(), lockout.GetEventName(), lockout.GetExpireTime()
);
std::string insert_values;
for (const auto& member : members)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
member.char_id,
lockout.GetExpireTime(),
lockout.GetDuration(),
lockout.GetExpeditionUUID(),
EscapeString(lockout.GetExpeditionName()),
EscapeString(lockout.GetEventName())
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO character_expedition_lockouts
(character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name)
VALUES {}
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration);
), insert_values);
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::InsertLockout(
uint32_t expedition_id, const ExpeditionLockoutTimer& lockout)
{
LogExpeditionsDetail(
"Inserting expedition [{}] lockout: [{}]:[{}] expire time: [{}]",
expedition_id, lockout.GetExpeditionName(), lockout.GetEventName(), lockout.GetExpireTime()
);
auto query = fmt::format(SQL(
INSERT INTO expedition_lockouts
(expedition_id, from_expedition_uuid, event_name, expire_time, duration)
VALUES
({}, '{}', '{}', FROM_UNIXTIME({}), {})
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration);
),
expedition_id,
lockout.GetExpeditionUUID(),
EscapeString(lockout.GetEventName()),
lockout.GetExpireTime(),
lockout.GetDuration()
);
database.QueryDatabase(query);
}
void ExpeditionDatabase::InsertLockouts(
uint32_t expedition_id, const std::unordered_map<std::string, ExpeditionLockoutTimer>& lockouts)
{
LogExpeditionsDetail("Inserting expedition [{}] lockouts", expedition_id);
std::string insert_values;
for (const auto& lockout : lockouts)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, '{}', '{}', FROM_UNIXTIME({}), {}),",
expedition_id,
lockout.second.GetExpeditionUUID(),
EscapeString(lockout.second.GetEventName()),
lockout.second.GetExpireTime(),
lockout.second.GetDuration()
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO expedition_lockouts
(expedition_id, from_expedition_uuid, event_name, expire_time, duration)
VALUES {}
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration);
), insert_values);
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::InsertMember(uint32_t expedition_id, uint32_t character_id)
{
LogExpeditionsDetail("Inserting character [{}] into expedition [{}]", character_id, expedition_id);
auto query = fmt::format(SQL(
INSERT INTO expedition_members
(expedition_id, character_id)
VALUES
({}, {})
ON DUPLICATE KEY UPDATE is_current_member = TRUE;
), expedition_id, character_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::InsertMembers(
uint32_t expedition_id, const std::vector<ExpeditionMember>& members)
{
LogExpeditionsDetail("Inserting characters into expedition [{}]", expedition_id);
std::string insert_values;
for (const auto& member : members)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, {}),",
expedition_id, member.char_id
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO expedition_members
(expedition_id, character_id)
VALUES {}
ON DUPLICATE KEY UPDATE is_current_member = TRUE;
), insert_values);
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::UpdateLockState(uint32_t expedition_id, bool is_locked)
{
LogExpeditionsDetail("Updating lock state [{}] for expedition [{}]", is_locked, expedition_id);
auto query = fmt::format(SQL(
UPDATE expeditions SET is_locked = {} WHERE id = {};
), is_locked, expedition_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::DeleteMember(uint32_t expedition_id, uint32_t character_id)
{
LogExpeditionsDetail("Removing member [{}] from expedition [{}]", character_id, expedition_id);
auto query = fmt::format(SQL(
UPDATE expedition_members SET is_current_member = FALSE
WHERE expedition_id = {} AND character_id = {};
), expedition_id, character_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::DeleteAllMembers(uint32_t expedition_id)
{
LogExpeditionsDetail("Removing all members of expedition [{}]", expedition_id);
auto query = fmt::format(SQL(
UPDATE expedition_members SET is_current_member = FALSE WHERE expedition_id = {};
), expedition_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join)
{
LogExpeditionsDetail("Updating replay lockout on join [{}] for expedition [{}]", add_on_join, expedition_id);
auto query = fmt::format(SQL(
UPDATE expeditions SET add_replay_on_join = {} WHERE id = {};
), add_on_join, expedition_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::AddLockoutDuration(const std::vector<ExpeditionMember>& members,
const ExpeditionLockoutTimer& lockout, int seconds)
{
LogExpeditionsDetail(
"Adding duration [{}] seconds to members lockouts [{}]:[{}]",
seconds, lockout.GetExpeditionName(), lockout.GetEventName());
std::string insert_values;
for (const auto& member : members)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
member.char_id,
lockout.GetExpireTime(),
lockout.GetDuration(),
lockout.GetExpeditionUUID(),
EscapeString(lockout.GetExpeditionName()),
EscapeString(lockout.GetEventName())
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO character_expedition_lockouts
(character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name)
VALUES {}
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = DATE_ADD(expire_time, INTERVAL {} SECOND),
duration = GREATEST(0, CAST(duration AS SIGNED) + {});
), insert_values, seconds, seconds);
database.QueryDatabase(query);
}
}

112
zone/expedition_database.h Normal file
View File

@ -0,0 +1,112 @@
/**
* 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 EXPEDITION_DATABASE_H
#define EXPEDITION_DATABASE_H
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
class Expedition;
class ExpeditionLockoutTimer;
struct ExpeditionMember;
class MySQLRequestResult;
namespace ExpeditionDatabase
{
uint32_t InsertExpedition(
const std::string& uuid, uint32_t instance_id, const std::string& expedition_name,
uint32_t leader_id, uint32_t min_players, uint32_t max_players);
std::string LoadExpeditionsSelectQuery();
MySQLRequestResult LoadExpedition(uint32_t expedition_id);
MySQLRequestResult LoadAllExpeditions();
MySQLRequestResult LoadMembersForCreateRequest(
const std::vector<std::string>& character_names, const std::string& expedition_name);
std::vector<ExpeditionLockoutTimer> LoadCharacterLockouts(uint32_t character_id);
std::vector<ExpeditionLockoutTimer> LoadCharacterLockouts(uint32_t character_id,
const std::string& expedition_name);
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>>
LoadMultipleExpeditionLockouts(const std::vector<uint32_t>& expedition_ids);
void DeleteAllMembers(uint32_t expedition_id);
void DeleteMember(uint32_t expedition_id, uint32_t character_id);
void DeleteAllCharacterLockouts(uint32_t character_id);
void DeleteAllCharacterLockouts(uint32_t character_id, const std::string& expedition_name);
void DeleteCharacterLockout(uint32_t character_id, const std::string& expedition_name,
const std::string& event_name);
void DeleteLockout(uint32_t expedition_id, const std::string& event_name);
void DeleteMembersLockout(const std::vector<ExpeditionMember>& members,
const std::string& expedition_name, const std::string& event_name);
uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id);
std::pair<std::vector<ExpeditionLockoutTimer>, std::vector<uint32_t>> GetMembersLockout(
const std::vector<ExpeditionMember>& members, const std::string& expedition_name,
const std::string& event_name);
void InsertCharacterLockouts(uint32_t character_id,
const std::vector<ExpeditionLockoutTimer>& lockouts);
void InsertMembersLockout(const std::vector<ExpeditionMember>& members,
const ExpeditionLockoutTimer& lockout);
void InsertLockout(uint32_t expedition_id, const ExpeditionLockoutTimer& lockout);
void InsertLockouts(uint32_t expedition_id,
const std::unordered_map<std::string, ExpeditionLockoutTimer>& lockouts);
void InsertMember(uint32_t expedition_id, uint32_t character_id);
void InsertMembers(uint32_t expedition_id, const std::vector<ExpeditionMember>& members);
void UpdateLockState(uint32_t expedition_id, bool is_locked);
void UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join);
void AddLockoutDuration(const std::vector<ExpeditionMember>& members,
const ExpeditionLockoutTimer& lockout, int seconds);
};
namespace LoadExpeditionColumns
{
enum eLoadExpeditionColumns
{
id = 0,
uuid,
dz_id,
expedition_name,
leader_id,
min_players,
max_players,
add_replay_on_join,
is_locked,
leader_name,
member_id,
member_name
};
};
namespace LoadMembersForCreateRequestColumns
{
enum eLoadMembersForCreateRequestColumns
{
character_id = 0,
character_name,
character_expedition_id,
lockout_uuid,
lockout_expire_time,
lockout_duration,
lockout_event_name
};
};
#endif

View File

@ -0,0 +1,101 @@
/**
* 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 "expedition_lockout_timer.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "../common/util/uuid.h"
#include <fmt/format.h>
const char* const DZ_REPLAY_TIMER_NAME = "Replay Timer"; // see December 14, 2016 patch notes
ExpeditionLockoutTimer::ExpeditionLockoutTimer(
const std::string& expedition_uuid, const std::string& expedition_name,
const std::string& event_name, uint64_t expire_time, uint32_t duration
) :
m_expedition_uuid(expedition_uuid),
m_expedition_name(expedition_name),
m_event_name(event_name),
m_expire_time(std::chrono::system_clock::from_time_t(expire_time)),
m_duration(duration)
{
if (event_name == DZ_REPLAY_TIMER_NAME)
{
m_is_replay_timer = true;
}
}
ExpeditionLockoutTimer ExpeditionLockoutTimer::CreateLockout(
const std::string& expedition_name, const std::string& event_name, uint32_t seconds, std::string uuid)
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
if (uuid.empty())
{
uuid = EQ::Util::UUID::Generate().ToString();
}
ExpeditionLockoutTimer lockout{uuid, expedition_name, event_name, 0, seconds};
lockout.Reset(); // sets expire time
return lockout;
}
uint32_t ExpeditionLockoutTimer::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;
}
ExpeditionLockoutTimer::DaysHoursMinutes ExpeditionLockoutTimer::GetDaysHoursMinutesRemaining() const
{
auto seconds = GetSecondsRemaining();
return ExpeditionLockoutTimer::DaysHoursMinutes{
fmt::format_int(seconds / 86400).str(), // days
fmt::format_int((seconds / 3600) % 24).str(), // hours
fmt::format_int((seconds / 60) % 60).str() // minutes
};
}
bool ExpeditionLockoutTimer::IsSameLockout(const ExpeditionLockoutTimer& compare_lockout) const
{
return compare_lockout.IsSameLockout(GetExpeditionName(), GetEventName());
}
bool ExpeditionLockoutTimer::IsSameLockout(
const std::string& expedition_name, const std::string& event_name) const
{
return GetExpeditionName() == expedition_name && GetEventName() == event_name;
}
void ExpeditionLockoutTimer::AddLockoutTime(int seconds)
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
auto new_duration = std::max(0, static_cast<int>(m_duration.count()) + seconds);
auto start_time = m_expire_time - m_duration;
m_duration = std::chrono::seconds(new_duration);
m_expire_time = start_time + m_duration;
}

View File

@ -0,0 +1,76 @@
/**
* 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 EXPEDITION_LOCKOUT_TIMER_H
#define EXPEDITION_LOCKOUT_TIMER_H
#include <chrono>
#include <string>
extern const char* const DZ_REPLAY_TIMER_NAME;
class ExpeditionLockoutTimer
{
public:
ExpeditionLockoutTimer() = default;
ExpeditionLockoutTimer(
const std::string& expedition_uuid, const std::string& expedition_name,
const std::string& event_name, uint64_t expire_time, uint32_t duration);
static ExpeditionLockoutTimer CreateLockout(
const std::string& expedition_name, const std::string& event_name,
uint32_t seconds, std::string uuid = {});
struct DaysHoursMinutes
{
std::string days;
std::string hours;
std::string mins;
};
void AddLockoutTime(int seconds);
uint32_t GetDuration() const { return static_cast<uint32_t>(m_duration.count()); }
uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); }
uint64_t GetStartTime() const { return std::chrono::system_clock::to_time_t(m_expire_time - m_duration); }
uint32_t GetSecondsRemaining() const;
DaysHoursMinutes GetDaysHoursMinutesRemaining() const;
const std::string& GetExpeditionName() const { return m_expedition_name; }
const std::string& GetExpeditionUUID() const { return m_expedition_uuid; }
const std::string& GetEventName() const { return m_event_name; }
bool IsExpired() const { return GetSecondsRemaining() == 0; }
bool IsFromExpedition(const std::string& uuid) const { return uuid == m_expedition_uuid; }
bool IsReplayTimer() const { return m_is_replay_timer; }
bool IsSameLockout(const ExpeditionLockoutTimer& compare_lockout) const;
bool IsSameLockout(const std::string& expedition_name, const std::string& event_name) const;
void Reset() { m_expire_time = std::chrono::system_clock::now() + m_duration; }
void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); }
void SetExpireTime(uint64_t expire_time) { m_expire_time = std::chrono::system_clock::from_time_t(expire_time); }
void SetUUID(const std::string& uuid) { m_expedition_uuid = uuid; }
private:
bool m_is_replay_timer = false;
std::string m_expedition_uuid; // expedition received in
std::string m_expedition_name;
std::string m_event_name;
std::chrono::seconds m_duration;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
};
#endif

397
zone/expedition_request.cpp Normal file
View File

@ -0,0 +1,397 @@
/**
* 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 "expedition_request.h"
#include "client.h"
#include "expedition.h"
#include "expedition_database.h"
#include "expedition_lockout_timer.h"
#include "groups.h"
#include "raids.h"
#include "string_ids.h"
#include "worldserver.h"
extern WorldServer worldserver;
constexpr char SystemName[] = "expedition";
struct ExpeditionRequestConflict
{
std::string character_name;
ExpeditionLockoutTimer lockout;
};
ExpeditionRequest::ExpeditionRequest(
std::string expedition_name, uint32_t min_players, uint32_t max_players, bool disable_messages
) :
m_expedition_name(expedition_name),
m_min_players(min_players),
m_max_players(max_players),
m_disable_messages(disable_messages)
{
}
bool ExpeditionRequest::Validate(Client* requester)
{
m_requester = requester;
if (!m_requester)
{
return false;
}
// a message is sent to leader for every member that fails a requirement
BenchTimer benchmark;
bool requirements_met = false;
Raid* raid = m_requester->GetRaid();
Group* group = m_requester->GetGroup();
if (raid)
{
requirements_met = CanRaidRequest(raid);
}
else if (group)
{
requirements_met = CanGroupRequest(group);
}
else // solo request
{
m_leader = m_requester;
m_leader_id = m_requester->CharacterID();
m_leader_name = m_requester->GetName();
requirements_met = CanMembersJoin({m_leader_name});
}
auto elapsed = benchmark.elapsed();
LogExpeditions("Create validation for [{}] members took [{}s]", m_members.size(), elapsed);
return requirements_met;
}
bool ExpeditionRequest::CanRaidRequest(Raid* raid)
{
m_leader = raid->GetLeader();
m_leader_name = raid->leadername;
m_leader_id = m_leader ? m_leader->CharacterID() : database.GetCharacterID(raid->leadername);
// live (as of September 16, 2020) supports creation even if raid count exceeds
// expedition max. members are added up to the max ordered by group number.
auto raid_members = raid->GetMembers();
if (raid_members.size() > m_max_players)
{
// stable_sort not needed, order within a raid group may not be what is displayed
std::sort(raid_members.begin(), raid_members.end(),
[&](const RaidMember& lhs, const RaidMember& rhs) {
if (m_leader_name == lhs.membername) { // leader always added first
return true;
} else if (m_leader_name == rhs.membername) {
return false;
}
return lhs.GroupNumber < rhs.GroupNumber;
});
m_not_all_added_msg = fmt::format(CREATE_NOT_ALL_ADDED, "raid", SystemName,
SystemName, m_max_players, "raid", raid_members.size());
}
// live still performs conflict checks for all members even those beyond max
std::vector<std::string> member_names;
for (int i = 0; i < raid_members.size(); ++i)
{
member_names.emplace_back(raid_members[i].membername);
}
return CanMembersJoin(member_names);
}
bool ExpeditionRequest::CanGroupRequest(Group* group)
{
m_leader = nullptr;
if (group->GetLeader() && group->GetLeader()->IsClient())
{
m_leader = group->GetLeader()->CastToClient();
}
// Group::GetLeaderName() is broken if group formed across zones, ask database instead
m_leader_name = m_leader ? m_leader->GetName() : GetGroupLeaderName(group->GetID()); // group->GetLeaderName();
m_leader_id = m_leader ? m_leader->CharacterID() : database.GetCharacterID(m_leader_name.c_str());
std::vector<std::string> member_names;
member_names.emplace_back(m_leader_name); // leader always added first
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i)
{
if (group->membername[i][0] && m_leader_name != group->membername[i])
{
member_names.emplace_back(group->membername[i]);
}
}
if (member_names.size() > m_max_players)
{
m_not_all_added_msg = fmt::format(CREATE_NOT_ALL_ADDED, "group", SystemName,
SystemName, m_max_players, "group", member_names.size());
}
return CanMembersJoin(member_names);
}
std::string ExpeditionRequest::GetGroupLeaderName(uint32_t group_id)
{
char leader_name_buffer[64] = { 0 };
database.GetGroupLeadershipInfo(group_id, leader_name_buffer);
return std::string(leader_name_buffer);
}
bool ExpeditionRequest::CanMembersJoin(const std::vector<std::string>& member_names)
{
if (member_names.empty())
{
return false;
}
bool requirements_met = true;
if (CheckMembersForConflicts(member_names))
{
requirements_met = false;
}
// live only checks player count requirement after other expensive checks pass (?)
// maybe it's done intentionally as a way to preview lockout conflicts
if (requirements_met)
{
requirements_met = IsPlayerCountValidated();
}
return requirements_met;
}
bool ExpeditionRequest::LoadLeaderLockouts()
{
// leader's lockouts are used to check member conflicts and later stored in expedition
auto lockouts = ExpeditionDatabase::LoadCharacterLockouts(m_leader_id, m_expedition_name);
for (auto& lockout : lockouts)
{
if (!lockout.IsExpired())
{
m_lockouts.emplace(lockout.GetEventName(), lockout);
// on live if leader has a replay lockout it never bothers checking for event conflicts
if (m_check_event_lockouts && lockout.IsReplayTimer())
{
m_check_event_lockouts = false;
}
}
}
return true;
}
bool ExpeditionRequest::CheckMembersForConflicts(const std::vector<std::string>& member_names)
{
// load data for each member and compare with leader lockouts
auto results = ExpeditionDatabase::LoadMembersForCreateRequest(member_names, m_expedition_name);
if (!results.Success() || !LoadLeaderLockouts())
{
LogExpeditions("Failed to load data to verify members for expedition request");
return true;
}
bool is_solo = (member_names.size() == 1);
bool has_conflicts = false;
using col = LoadMembersForCreateRequestColumns::eLoadMembersForCreateRequestColumns;
std::vector<ExpeditionRequestConflict> member_lockout_conflicts;
uint32_t last_character_id = 0;
for (auto row = results.begin(); row != results.end(); ++row)
{
uint32_t character_id = std::strtoul(row[col::character_id], nullptr, 10);
std::string character_name = row[col::character_name];
bool has_expedition = (row[col::character_expedition_id] != nullptr);
if (character_id != last_character_id)
{
// defaults to online status, if offline group members implemented this needs to change
m_members.emplace_back(character_id, character_name);
// process event lockout conflict messages from the previous character
for (const auto& member_lockout : member_lockout_conflicts)
{
SendLeaderMemberEventLockout(member_lockout.character_name, member_lockout.lockout);
}
member_lockout_conflicts.clear();
if (has_expedition)
{
has_conflicts = true;
SendLeaderMemberInExpedition(character_name, is_solo);
// solo requests break out early if requester in an expedition
if (is_solo)
{
return has_conflicts;
}
}
}
last_character_id = character_id;
// compare member lockouts with leader lockouts
if (row[col::lockout_uuid]) // lockout results may be null
{
auto expire_time = strtoull(row[col::lockout_expire_time], nullptr, 10);
uint32_t duration = strtoul(row[col::lockout_duration], nullptr, 10);
ExpeditionLockoutTimer lockout{
row[col::lockout_uuid], m_expedition_name, row[col::lockout_event_name], expire_time, duration
};
if (!lockout.IsExpired())
{
if (lockout.IsReplayTimer())
{
// replay timer conflict messages always show up before event conflicts
has_conflicts = true;
SendLeaderMemberReplayLockout(character_name, lockout, is_solo);
}
else if (m_check_event_lockouts && character_id != m_leader_id)
{
if (m_lockouts.find(lockout.GetEventName()) == m_lockouts.end())
{
// leader doesn't have this lockout. queue instead of messaging
// now so message comes after any replay lockout messages
has_conflicts = true;
member_lockout_conflicts.push_back({character_name, lockout});
}
}
}
}
}
// event lockout messages for last processed character
for (const auto& member_lockout : member_lockout_conflicts)
{
SendLeaderMemberEventLockout(member_lockout.character_name, member_lockout.lockout);
}
return has_conflicts;
}
void ExpeditionRequest::SendLeaderMessage(
uint16_t chat_type, uint32_t string_id, const std::initializer_list<std::string>& args)
{
if (!m_disable_messages)
{
Client::SendCrossZoneMessageString(m_leader, m_leader_name, chat_type, string_id, args);
}
}
void ExpeditionRequest::SendLeaderMemberInExpedition(const std::string& member_name, bool is_solo)
{
if (m_disable_messages)
{
return;
}
if (is_solo)
{
SendLeaderMessage(Chat::Red, EXPEDITION_YOU_BELONG);
}
else if (m_requester)
{
std::string message = fmt::format(EXPEDITION_OTHER_BELONGS, m_requester->GetName(), member_name);
Client::SendCrossZoneMessage(m_leader, m_leader_name, Chat::Red, message);
}
}
void ExpeditionRequest::SendLeaderMemberReplayLockout(
const std::string& member_name, const ExpeditionLockoutTimer& lockout, bool is_solo)
{
if (m_disable_messages)
{
return;
}
auto time_remaining = lockout.GetDaysHoursMinutesRemaining();
if (is_solo)
{
SendLeaderMessage(Chat::Red, EXPEDITION_YOU_PLAYED_HERE, {
time_remaining.days, time_remaining.hours, time_remaining.mins
});
}
else
{
SendLeaderMessage(Chat::Red, EXPEDITION_REPLAY_TIMER, {
member_name, time_remaining.days, time_remaining.hours, time_remaining.mins
});
}
}
void ExpeditionRequest::SendLeaderMemberEventLockout(
const std::string& member_name, const ExpeditionLockoutTimer& lockout)
{
if (m_disable_messages)
{
return;
}
auto time_remaining = lockout.GetDaysHoursMinutesRemaining();
SendLeaderMessage(Chat::Red, EXPEDITION_EVENT_TIMER, {
member_name,
lockout.GetEventName(),
time_remaining.days,
time_remaining.hours,
time_remaining.mins,
lockout.GetEventName()
});
}
bool ExpeditionRequest::IsPlayerCountValidated()
{
// note: offline group members count towards requirement but not added to expedition
bool requirements_met = true;
auto bypass_status = RuleI(Expedition, MinStatusToBypassPlayerCountRequirements);
auto gm_bypass = (m_requester && m_requester->GetGM() && m_requester->Admin() >= bypass_status);
if (m_members.size() > m_max_players)
{
// members were sorted at start, truncate after conflict checks to act like live
m_members.resize(m_max_players);
}
else if (!gm_bypass && m_members.size() < m_min_players)
{
requirements_met = false;
SendLeaderMessage(Chat::System, REQUIRED_PLAYER_COUNT, {
fmt::format_int(m_members.size()).str(),
fmt::format_int(m_min_players).str(),
fmt::format_int(m_max_players).str()
});
}
return requirements_met;
}

83
zone/expedition_request.h Normal file
View File

@ -0,0 +1,83 @@
/**
* 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 EXPEDITION_REQUEST_H
#define EXPEDITION_REQUEST_H
#include "expedition.h"
#include "expedition_lockout_timer.h"
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_map>
class Client;
class Group;
class MySQLRequestResult;
class Raid;
class ServerPacket;
class ExpeditionRequest
{
public:
ExpeditionRequest(
std::string expedition_name, uint32_t min_players, uint32_t max_players,
bool disable_messages = false);
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; }
const std::string& GetNotAllAddedMessage() const { return m_not_all_added_msg; }
uint32_t GetMinPlayers() const { return m_min_players; }
uint32_t GetMaxPlayers() const { return m_max_players; }
std::vector<ExpeditionMember> GetMembers() const { return m_members; }
std::unordered_map<std::string, ExpeditionLockoutTimer> GetLockouts() const { return m_lockouts; }
private:
bool CanMembersJoin(const std::vector<std::string>& member_names);
bool CanRaidRequest(Raid* raid);
bool CanGroupRequest(Group* group);
bool CheckMembersForConflicts(const std::vector<std::string>& member_names);
std::string GetGroupLeaderName(uint32_t group_id);
bool IsPlayerCountValidated();
bool LoadLeaderLockouts();
void SendLeaderMemberInExpedition(const std::string& member_name, bool is_solo);
void SendLeaderMemberReplayLockout(const std::string& member_name, const ExpeditionLockoutTimer& lockout, bool is_solo);
void SendLeaderMemberEventLockout(const std::string& member_name, const ExpeditionLockoutTimer& lockout);
void SendLeaderMessage(uint16_t chat_type, uint32_t string_id, const std::initializer_list<std::string>& args = {});
Client* m_requester = nullptr;
Client* m_leader = nullptr;
uint32_t m_leader_id = 0;
uint32_t m_min_players = 0;
uint32_t m_max_players = 0;
bool m_check_event_lockouts = true;
bool m_disable_messages = false;
std::string m_expedition_name;
std::string m_leader_name;
std::string m_not_all_added_msg;
std::vector<ExpeditionMember> m_members;
std::unordered_map<std::string, ExpeditionLockoutTimer> m_lockouts;
};
#endif

View File

@ -18,6 +18,7 @@
#include "../common/global_define.h"
#include "../common/eqemu_logsys.h"
#include "expedition.h"
#include "masterentity.h"
#include "npc_ai.h"
#include "../common/packet_functions.h"
@ -2503,3 +2504,24 @@ void Group::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_r
}
}
}
bool Group::DoesAnyMemberHaveExpeditionLockout(
const std::string& expedition_name, const std::string& event_name, int max_check_count)
{
if (max_check_count <= 0)
{
max_check_count = MAX_GROUP_MEMBERS;
}
for (int i = 0; i < MAX_GROUP_MEMBERS && i < max_check_count; ++i)
{
if (membername[i][0])
{
if (Expedition::HasLockoutByCharacterName(membername[i], expedition_name, event_name))
{
return true;
}
}
}
return false;
}

View File

@ -153,6 +153,8 @@ public:
inline int GetMentorPercent() { return mentor_percent; }
inline Client *GetMentoree() { return mentoree; }
bool DoesAnyMemberHaveExpeditionLockout(const std::string& expedition_name, const std::string& event_name, int max_check_count = 0);
Mob* members[MAX_GROUP_MEMBERS];
char membername[MAX_GROUP_MEMBERS][64];
uint8 MemberRoles[MAX_GROUP_MEMBERS];

View File

@ -4,7 +4,11 @@
#include <luabind/luabind.hpp>
#include "client.h"
#include "dynamiczone.h"
#include "expedition_lockout_timer.h"
#include "expedition_request.h"
#include "lua_client.h"
#include "lua_expedition.h"
#include "lua_npc.h"
#include "lua_item.h"
#include "lua_iteminst.h"
@ -1644,7 +1648,234 @@ int Lua_Client::GetClientMaxLevel() {
return self->GetClientMaxLevel();
}
DynamicZoneLocation GetDynamicZoneLocationFromTable(const luabind::object& lua_table)
{
DynamicZoneLocation zone_location;
if (luabind::type(lua_table) == LUA_TTABLE)
{
luabind::object lua_zone = lua_table["zone"];
// default invalid/missing args to 0
uint32_t zone_id = 0;
if (luabind::type(lua_zone) == LUA_TSTRING)
{
zone_id = ZoneID(luabind::object_cast<std::string>(lua_zone));
}
else if (luabind::type(lua_zone) == LUA_TNUMBER)
{
zone_id = luabind::object_cast<uint32_t>(lua_zone);
}
float x = (luabind::type(lua_table["x"]) != LUA_TNIL) ? luabind::object_cast<float>(lua_table["x"]) : 0.0f;
float y = (luabind::type(lua_table["y"]) != LUA_TNIL) ? luabind::object_cast<float>(lua_table["y"]) : 0.0f;
float z = (luabind::type(lua_table["z"]) != LUA_TNIL) ? luabind::object_cast<float>(lua_table["z"]) : 0.0f;
float h = (luabind::type(lua_table["h"]) != LUA_TNIL) ? luabind::object_cast<float>(lua_table["h"]) : 0.0f;
zone_location = { zone_id, x, y, z, h };
}
return zone_location;
}
Lua_Expedition Lua_Client::CreateExpedition(luabind::object expedition_table) {
Lua_Safe_Call_Class(Lua_Expedition);
if (luabind::type(expedition_table) != LUA_TTABLE)
{
return nullptr;
}
// luabind will catch thrown cast_failed exceptions for invalid/missing args
luabind::object instance_info = expedition_table["instance"];
luabind::object zone = instance_info["zone"];
uint32_t zone_id = 0;
if (luabind::type(zone) == LUA_TSTRING)
{
zone_id = ZoneID(luabind::object_cast<std::string>(zone));
}
else if (luabind::type(zone) == LUA_TNUMBER)
{
zone_id = luabind::object_cast<uint32_t>(zone);
}
uint32_t zone_version = luabind::object_cast<uint32_t>(instance_info["version"]);
uint32_t zone_duration = luabind::object_cast<uint32_t>(instance_info["duration"]);
DynamicZone dz{ zone_id, zone_version, zone_duration, DynamicZoneType::Expedition };
// the dz_info table supports optional hash entries for 'compass', 'safereturn', and 'zonein' data
if (luabind::type(expedition_table["compass"]) == LUA_TTABLE)
{
auto compass_loc = GetDynamicZoneLocationFromTable(expedition_table["compass"]);
dz.SetCompass(compass_loc);
}
if (luabind::type(expedition_table["safereturn"]) == LUA_TTABLE)
{
auto safereturn_loc = GetDynamicZoneLocationFromTable(expedition_table["safereturn"]);
dz.SetSafeReturn(safereturn_loc);
}
if (luabind::type(expedition_table["zonein"]) == LUA_TTABLE)
{
auto zonein_loc = GetDynamicZoneLocationFromTable(expedition_table["zonein"]);
dz.SetZoneInLocation(zonein_loc);
}
luabind::object expedition_info = expedition_table["expedition"];
std::string expedition_name = luabind::object_cast<std::string>(expedition_info["name"]);
uint32_t min_players = luabind::object_cast<uint32_t>(expedition_info["min_players"]);
uint32_t max_players = luabind::object_cast<uint32_t>(expedition_info["max_players"]);
bool disable_messages = false;
if (luabind::type(expedition_info["disable_messages"]) == LUA_TBOOLEAN)
{
disable_messages = luabind::object_cast<bool>(expedition_info["disable_messages"]);
}
ExpeditionRequest request{ expedition_name, min_players, max_players, disable_messages };
return self->CreateExpedition(dz, request);
}
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(zone_name, version, duration, expedition_name, min_players, 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, bool disable_messages) {
Lua_Safe_Call_Class(Lua_Expedition);
return self->CreateExpedition(zone_name, version, duration, expedition_name, min_players, max_players, disable_messages);
}
Lua_Expedition Lua_Client::GetExpedition() {
Lua_Safe_Call_Class(Lua_Expedition);
return self->GetExpedition();
}
luabind::object Lua_Client::GetExpeditionLockouts(lua_State* L)
{
auto lua_table = luabind::newtable(L);
if (d_)
{
auto self = reinterpret_cast<NativeType*>(d_);
auto lockouts = self->GetExpeditionLockouts();
for (const auto& lockout : lockouts)
{
auto lockout_table = lua_table[lockout.GetExpeditionName()];
if (luabind::type(lockout_table) != LUA_TTABLE)
{
lockout_table = luabind::newtable(L);
}
lockout_table[lockout.GetEventName()] = lockout.GetSecondsRemaining();
}
}
return lua_table;
}
luabind::object Lua_Client::GetExpeditionLockouts(lua_State* L, std::string expedition_name)
{
auto lua_table = luabind::newtable(L);
if (d_)
{
auto self = reinterpret_cast<NativeType*>(d_);
auto lockouts = self->GetExpeditionLockouts();
for (const auto& lockout : lockouts)
{
if (lockout.GetExpeditionName() == expedition_name)
{
lua_table[lockout.GetEventName()] = lockout.GetSecondsRemaining();
}
}
}
return lua_table;
}
std::string Lua_Client::GetLockoutExpeditionUUID(std::string expedition_name, std::string event_name) {
Lua_Safe_Call_String();
std::string uuid;
auto lockout = self->GetExpeditionLockout(expedition_name, event_name);
if (lockout)
{
uuid = lockout->GetExpeditionUUID();
}
return uuid;
}
void Lua_Client::AddExpeditionLockout(std::string expedition_name, std::string event_name, uint32 seconds) {
Lua_Safe_Call_Void();
self->AddNewExpeditionLockout(expedition_name, event_name, seconds);
}
void Lua_Client::AddExpeditionLockout(std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) {
Lua_Safe_Call_Void();
self->AddNewExpeditionLockout(expedition_name, event_name, seconds, uuid);
}
void Lua_Client::AddExpeditionLockoutDuration(std::string expedition_name, std::string event_name, int seconds) {
Lua_Safe_Call_Void();
self->AddExpeditionLockoutDuration(expedition_name, event_name, seconds, {}, true);
}
void Lua_Client::AddExpeditionLockoutDuration(std::string expedition_name, std::string event_name, int seconds, std::string uuid) {
Lua_Safe_Call_Void();
self->AddExpeditionLockoutDuration(expedition_name, event_name, seconds, uuid, true);
}
void Lua_Client::RemoveAllExpeditionLockouts() {
Lua_Safe_Call_Void();
self->RemoveAllExpeditionLockouts({}, true);
}
void Lua_Client::RemoveAllExpeditionLockouts(std::string expedition_name) {
Lua_Safe_Call_Void();
self->RemoveAllExpeditionLockouts(expedition_name, true);
}
void Lua_Client::RemoveExpeditionLockout(std::string expedition_name, std::string event_name) {
Lua_Safe_Call_Void();
self->RemoveExpeditionLockout(expedition_name, event_name, true);
}
bool Lua_Client::HasExpeditionLockout(std::string expedition_name, std::string event_name) {
Lua_Safe_Call_Bool();
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(uint32 zone_id, int zone_version) {
Lua_Safe_Call_Void();
return self->MovePCDynamicZone(zone_id, zone_version);
}
void Lua_Client::MovePCDynamicZone(uint32 zone_id, int zone_version, bool msg_if_invalid) {
Lua_Safe_Call_Void();
return self->MovePCDynamicZone(zone_id, zone_version, msg_if_invalid);
}
void Lua_Client::MovePCDynamicZone(std::string zone_name) {
Lua_Safe_Call_Void();
return self->MovePCDynamicZone(zone_name);
}
void Lua_Client::MovePCDynamicZone(std::string zone_name, int zone_version) {
Lua_Safe_Call_Void();
return self->MovePCDynamicZone(zone_name, zone_version);
}
void Lua_Client::MovePCDynamicZone(std::string zone_name, int zone_version, bool msg_if_invalid) {
Lua_Safe_Call_Void();
return self->MovePCDynamicZone(zone_name, zone_version, msg_if_invalid);
}
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
@ -1952,7 +2183,28 @@ luabind::scope lua_register_client() {
.def("EnableAreaRegens", &Lua_Client::EnableAreaRegens)
.def("DisableAreaRegens", &Lua_Client::DisableAreaRegens)
.def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel)
.def("GetClientMaxLevel", (int(Lua_Client::*)(void))&Lua_Client::GetClientMaxLevel);
.def("GetClientMaxLevel", (int(Lua_Client::*)(void))&Lua_Client::GetClientMaxLevel)
.def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(luabind::object))&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("GetLockoutExpeditionUUID", (std::string(Lua_Client::*)(std::string, std::string))&Lua_Client::GetLockoutExpeditionUUID)
.def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32))&Lua_Client::AddExpeditionLockout)
.def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32, std::string))&Lua_Client::AddExpeditionLockout)
.def("AddExpeditionLockoutDuration", (void(Lua_Client::*)(std::string, std::string, int))&Lua_Client::AddExpeditionLockoutDuration)
.def("AddExpeditionLockoutDuration", (void(Lua_Client::*)(std::string, std::string, int, std::string))&Lua_Client::AddExpeditionLockoutDuration)
.def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(void))&Lua_Client::RemoveAllExpeditionLockouts)
.def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(std::string))&Lua_Client::RemoveAllExpeditionLockouts)
.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("MovePCDynamicZone", (void(Lua_Client::*)(uint32))&Lua_Client::MovePCDynamicZone)
.def("MovePCDynamicZone", (void(Lua_Client::*)(uint32, int))&Lua_Client::MovePCDynamicZone)
.def("MovePCDynamicZone", (void(Lua_Client::*)(uint32, int, bool))&Lua_Client::MovePCDynamicZone)
.def("MovePCDynamicZone", (void(Lua_Client::*)(std::string))&Lua_Client::MovePCDynamicZone)
.def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int))&Lua_Client::MovePCDynamicZone)
.def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int, bool))&Lua_Client::MovePCDynamicZone);
}
luabind::scope lua_register_inventory_where() {

View File

@ -5,6 +5,7 @@
#include "lua_mob.h"
class Client;
class Lua_Expedition;
class Lua_Group;
class Lua_Raid;
class Lua_Inventory;
@ -332,12 +333,33 @@ public:
void EnableAreaRegens(int value);
void DisableAreaRegens();
void SetPrimaryWeaponOrnamentation(uint32 model_id);
void SetSecondaryWeaponOrnamentation(uint32 model_id);
void SetClientMaxLevel(int value);
int GetClientMaxLevel();
Lua_Expedition CreateExpedition(luabind::object expedition_info);
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 disable_messages);
Lua_Expedition GetExpedition();
luabind::object GetExpeditionLockouts(lua_State* L);
luabind::object GetExpeditionLockouts(lua_State* L, std::string expedition_name);
std::string GetLockoutExpeditionUUID(std::string expedition_name, std::string event_name);
void AddExpeditionLockout(std::string expedition_name, std::string event_name, uint32 seconds);
void AddExpeditionLockout(std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid);
void AddExpeditionLockoutDuration(std::string expedition_name, std::string event_name, int seconds);
void AddExpeditionLockoutDuration(std::string expedition_name, std::string event_name, int seconds, std::string uuid);
void RemoveAllExpeditionLockouts();
void RemoveAllExpeditionLockouts(std::string expedition_name);
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(uint32 zone_id, int zone_version);
void MovePCDynamicZone(uint32 zone_id, int zone_version, bool msg_if_invalid);
void MovePCDynamicZone(std::string zone_name);
void MovePCDynamicZone(std::string zone_name, int zone_version);
void MovePCDynamicZone(std::string zone_name, int zone_version, bool msg_if_invalid);
};
#endif

301
zone/lua_expedition.cpp Normal file
View File

@ -0,0 +1,301 @@
/**
* 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
*
*/
#ifdef LUA_EQEMU
#include "lua_expedition.h"
#include "expedition.h"
#include "zone_store.h"
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include <luabind/object.hpp>
void Lua_Expedition::AddLockout(std::string event_name, uint32_t seconds) {
Lua_Safe_Call_Void();
self->AddLockout(event_name, seconds);
}
void Lua_Expedition::AddLockoutDuration(std::string event_name, int seconds) {
Lua_Safe_Call_Void();
self->AddLockoutDuration(event_name, seconds);
}
void Lua_Expedition::AddLockoutDuration(std::string event_name, int seconds, bool members_only) {
Lua_Safe_Call_Void();
self->AddLockoutDuration(event_name, seconds, members_only);
}
void Lua_Expedition::AddReplayLockout(uint32_t seconds) {
Lua_Safe_Call_Void();
self->AddReplayLockout(seconds);
}
void Lua_Expedition::AddReplayLockoutDuration(int seconds) {
Lua_Safe_Call_Void();
self->AddReplayLockoutDuration(seconds);
}
void Lua_Expedition::AddReplayLockoutDuration(int seconds, bool members_only) {
Lua_Safe_Call_Void();
self->AddReplayLockoutDuration(seconds, members_only);
}
uint32_t Lua_Expedition::GetDynamicZoneID() {
Lua_Safe_Call_Int();
return self->GetDynamicZoneID();
}
uint32_t Lua_Expedition::GetID() {
Lua_Safe_Call_Int();
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();
}
luabind::object Lua_Expedition::GetLockouts(lua_State* L) {
luabind::object lua_table = luabind::newtable(L);
if (d_)
{
auto self = reinterpret_cast<NativeType*>(d_);
auto lockouts = self->GetLockouts();
for (const auto& lockout : lockouts)
{
lua_table[lockout.first] = lockout.second.GetSecondsRemaining();
}
}
return lua_table;
}
std::string Lua_Expedition::GetLootEventByNPCTypeID(uint32_t npc_type_id) {
Lua_Safe_Call_String();
return self->GetLootEventByNPCTypeID(npc_type_id);
}
std::string Lua_Expedition::GetLootEventBySpawnID(uint32_t spawn_id) {
Lua_Safe_Call_String();
return self->GetLootEventBySpawnID(spawn_id);
}
uint32_t Lua_Expedition::GetMemberCount() {
Lua_Safe_Call_Int();
return self->GetMemberCount();
}
luabind::object Lua_Expedition::GetMembers(lua_State* L) {
luabind::object lua_table = luabind::newtable(L);
if (d_)
{
auto self = reinterpret_cast<NativeType*>(d_);
for (const auto& member : self->GetMembers())
{
lua_table[member.name] = member.char_id;
}
}
return lua_table;
}
std::string Lua_Expedition::GetName() {
Lua_Safe_Call_String();
return self->GetName();
}
int Lua_Expedition::GetSecondsRemaining() {
Lua_Safe_Call_Int();
return self->GetDynamicZone().GetSecondsRemaining();
}
std::string Lua_Expedition::GetUUID() {
Lua_Safe_Call_String();
return self->GetUUID();
}
int Lua_Expedition::GetZoneID() {
Lua_Safe_Call_Int();
return self->GetDynamicZone().GetZoneID();
}
std::string Lua_Expedition::GetZoneName() {
Lua_Safe_Call_String();
return ZoneName(self->GetDynamicZone().GetZoneID());
}
int Lua_Expedition::GetZoneVersion() {
Lua_Safe_Call_Int();
return self->GetDynamicZone().GetZoneVersion();
}
bool Lua_Expedition::HasLockout(std::string event_name) {
Lua_Safe_Call_Bool();
return self->HasLockout(event_name);
}
bool Lua_Expedition::HasReplayLockout() {
Lua_Safe_Call_Bool();
return self->HasReplayLockout();
}
void Lua_Expedition::RemoveCompass() {
Lua_Safe_Call_Void();
self->SetDzCompass(0, 0, 0, 0, true);
}
void Lua_Expedition::RemoveLockout(std::string event_name) {
Lua_Safe_Call_Void();
self->RemoveLockout(event_name);
}
void Lua_Expedition::SetCompass(uint32_t zone_id, float x, float y, float z) {
Lua_Safe_Call_Void();
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();
self->SetDzCompass(zone_name, x, y, z, true);
}
void Lua_Expedition::SetLocked(bool lock_expedition) {
Lua_Safe_Call_Void();
self->SetLocked(lock_expedition, ExpeditionLockMessage::None, true);
}
void Lua_Expedition::SetLocked(bool lock_expedition, int lock_msg) {
Lua_Safe_Call_Void();
self->SetLocked(lock_expedition, static_cast<ExpeditionLockMessage>(lock_msg), true);
}
void Lua_Expedition::SetLocked(bool lock_expedition, int lock_msg, uint32_t msg_color) {
Lua_Safe_Call_Void();
self->SetLocked(lock_expedition, static_cast<ExpeditionLockMessage>(lock_msg), true, msg_color);
}
void Lua_Expedition::SetLootEventByNPCTypeID(uint32_t npc_type_id, std::string event_name) {
Lua_Safe_Call_Void();
self->SetLootEventByNPCTypeID(npc_type_id, event_name);
}
void Lua_Expedition::SetLootEventBySpawnID(uint32_t spawn_id, std::string event_name) {
Lua_Safe_Call_Void();
self->SetLootEventBySpawnID(spawn_id, event_name);
}
void Lua_Expedition::SetReplayLockoutOnMemberJoin(bool enable) {
Lua_Safe_Call_Void();
self->SetReplayLockoutOnMemberJoin(enable, true);
}
void Lua_Expedition::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading) {
Lua_Safe_Call_Void();
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();
self->SetDzSafeReturn(zone_name, x, y, z, heading, true);
}
void Lua_Expedition::SetSecondsRemaining(uint32_t seconds_remaining)
{
Lua_Safe_Call_Void();
self->SetDzSecondsRemaining(seconds_remaining);
}
void Lua_Expedition::SetZoneInLocation(float x, float y, float z, float heading) {
Lua_Safe_Call_Void();
self->SetDzZoneInLocation(x, y, z, heading, true);
}
void Lua_Expedition::UpdateLockoutDuration(std::string event_name, uint32_t duration) {
Lua_Safe_Call_Void();
self->UpdateLockoutDuration(event_name, duration);
}
void Lua_Expedition::UpdateLockoutDuration(std::string event_name, uint32_t duration, bool members_only) {
Lua_Safe_Call_Void();
self->UpdateLockoutDuration(event_name, duration, members_only);
}
luabind::scope lua_register_expedition() {
return luabind::class_<Lua_Expedition>("Expedition")
.def(luabind::constructor<>())
.property("null", &Lua_Expedition::Null)
.property("valid", &Lua_Expedition::Valid)
.def("AddLockout", (void(Lua_Expedition::*)(std::string, uint32_t))&Lua_Expedition::AddLockout)
.def("AddLockoutDuration", (void(Lua_Expedition::*)(std::string, int))&Lua_Expedition::AddLockoutDuration)
.def("AddLockoutDuration", (void(Lua_Expedition::*)(std::string, int, bool))&Lua_Expedition::AddLockoutDuration)
.def("AddReplayLockout", (void(Lua_Expedition::*)(uint32_t))&Lua_Expedition::AddReplayLockout)
.def("AddReplayLockoutDuration", (void(Lua_Expedition::*)(int))&Lua_Expedition::AddReplayLockoutDuration)
.def("AddReplayLockoutDuration", (void(Lua_Expedition::*)(int, bool))&Lua_Expedition::AddReplayLockoutDuration)
.def("GetDynamicZoneID", &Lua_Expedition::GetDynamicZoneID)
.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("GetLootEventByNPCTypeID", (std::string(Lua_Expedition::*)(uint32_t))&Lua_Expedition::GetLootEventByNPCTypeID)
.def("GetLootEventBySpawnID", (std::string(Lua_Expedition::*)(uint32_t))&Lua_Expedition::GetLootEventBySpawnID)
.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("GetSecondsRemaining", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetSecondsRemaining)
.def("GetUUID", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetUUID)
.def("GetZoneID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetZoneID)
.def("GetZoneName", &Lua_Expedition::GetZoneName)
.def("GetZoneVersion", &Lua_Expedition::GetZoneVersion)
.def("HasLockout", (bool(Lua_Expedition::*)(std::string))&Lua_Expedition::HasLockout)
.def("HasReplayLockout", (bool(Lua_Expedition::*)(void))&Lua_Expedition::HasReplayLockout)
.def("RemoveCompass", (void(Lua_Expedition::*)(void))&Lua_Expedition::RemoveCompass)
.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("SetLocked", (void(Lua_Expedition::*)(bool))&Lua_Expedition::SetLocked)
.def("SetLocked", (void(Lua_Expedition::*)(bool, int))&Lua_Expedition::SetLocked)
.def("SetLocked", (void(Lua_Expedition::*)(bool, int, uint32_t))&Lua_Expedition::SetLocked)
.def("SetLootEventByNPCTypeID", (void(Lua_Expedition::*)(uint32_t, std::string))&Lua_Expedition::SetLootEventByNPCTypeID)
.def("SetLootEventBySpawnID", (void(Lua_Expedition::*)(uint32_t, std::string))&Lua_Expedition::SetLootEventBySpawnID)
.def("SetReplayLockoutOnMemberJoin", (void(Lua_Expedition::*)(bool))&Lua_Expedition::SetReplayLockoutOnMemberJoin)
.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("SetSecondsRemaining", &Lua_Expedition::SetSecondsRemaining)
.def("SetZoneInLocation", (void(Lua_Expedition::*)(float, float, float, float))&Lua_Expedition::SetZoneInLocation)
.def("UpdateLockoutDuration", (void(Lua_Expedition::*)(std::string, uint32_t))&Lua_Expedition::UpdateLockoutDuration)
.def("UpdateLockoutDuration", (void(Lua_Expedition::*)(std::string, uint32_t, bool))&Lua_Expedition::UpdateLockoutDuration);
}
luabind::scope lua_register_expedition_lock_messages() {
return luabind::class_<ExpeditionLockMessage>("ExpeditionLockMessage")
.enum_("constants")
[
luabind::value("None", static_cast<int>(ExpeditionLockMessage::None)),
luabind::value("Close", static_cast<int>(ExpeditionLockMessage::Close)),
luabind::value("Begin", static_cast<int>(ExpeditionLockMessage::Begin))
];
}
#endif // LUA_EQEMU

98
zone/lua_expedition.h Normal file
View File

@ -0,0 +1,98 @@
/**
* 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 EQEMU_LUA_EXPEDITION_H
#define EQEMU_LUA_EXPEDITION_H
#ifdef LUA_EQEMU
#include "lua_ptr.h"
#include "../common/types.h"
#include <string>
class Expedition;
class Lua_Client;
struct lua_State;
namespace luabind {
struct scope;
namespace adl {
class object;
}
using adl::object;
}
luabind::scope lua_register_expedition();
luabind::scope lua_register_expedition_lock_messages();
class Lua_Expedition : public Lua_Ptr<Expedition>
{
typedef Expedition NativeType;
public:
Lua_Expedition() : Lua_Ptr(nullptr) { }
Lua_Expedition(Expedition *d) : Lua_Ptr(d) { }
virtual ~Lua_Expedition() { }
operator Expedition*() {
return reinterpret_cast<Expedition*>(GetLuaPtrData());
}
void AddLockout(std::string event_name, uint32_t seconds);
void AddLockoutDuration(std::string event_name, int seconds);
void AddLockoutDuration(std::string event_name, int seconds, bool members_only);
void AddReplayLockout(uint32_t seconds);
void AddReplayLockoutDuration(int seconds);
void AddReplayLockoutDuration(int seconds, bool members_only);
uint32_t GetDynamicZoneID();
uint32_t GetID();
int GetInstanceID();
std::string GetLeaderName();
luabind::object GetLockouts(lua_State* L);
std::string GetLootEventByNPCTypeID(uint32_t npc_type_id);
std::string GetLootEventBySpawnID(uint32_t spawn_id);
uint32_t GetMemberCount();
luabind::object GetMembers(lua_State* L);
std::string GetName();
int GetSecondsRemaining();
std::string GetUUID();
int GetZoneID();
std::string GetZoneName();
int GetZoneVersion();
bool HasLockout(std::string event_name);
bool HasReplayLockout();
void RemoveCompass();
void RemoveLockout(std::string event_name);
void SetCompass(uint32_t zone_id, float x, float y, float z);
void SetCompass(std::string zone_name, float x, float y, float z);
void SetLocked(bool lock_expedition);
void SetLocked(bool lock_expedition, int lock_msg);
void SetLocked(bool lock_expedition, int lock_msg, uint32_t color);
void SetLootEventByNPCTypeID(uint32_t npc_type_id, std::string event_name);
void SetLootEventBySpawnID(uint32_t spawn_id, std::string event_name);
void SetReplayLockoutOnMemberJoin(bool enable);
void SetSafeReturn(uint32_t 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 SetSecondsRemaining(uint32_t seconds_remaining);
void SetZoneInLocation(float x, float y, float z, float heading);
void UpdateLockoutDuration(std::string event_name, uint32_t duration);
void UpdateLockoutDuration(std::string event_name, uint32_t duration, bool members_only);
};
#endif // LUA_EQEMU
#endif // EQEMU_LUA_EXPEDITION_H

View File

@ -18,12 +18,14 @@
#include "lua_client.h"
#include "lua_npc.h"
#include "lua_entity_list.h"
#include "lua_expedition.h"
#include "quest_parser_collection.h"
#include "questmgr.h"
#include "qglobals.h"
#include "encounter.h"
#include "lua_encounter.h"
#include "data_bucket.h"
#include "expedition.h"
struct Events { };
struct Factions { };
@ -2178,6 +2180,118 @@ void lua_set_content_flag(std::string flag_name, bool enabled){
ZoneStore::SetContentFlag(flag_name, enabled);
}
Lua_Expedition lua_get_expedition() {
if (zone && zone->GetInstanceID() != 0)
{
return Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID());
}
return nullptr;
}
Lua_Expedition lua_get_expedition_by_char_id(uint32 char_id) {
return Expedition::FindCachedExpeditionByCharacterID(char_id);
}
Lua_Expedition lua_get_expedition_by_dz_id(uint32 dz_id) {
return Expedition::FindCachedExpeditionByDynamicZoneID(dz_id);
}
Lua_Expedition lua_get_expedition_by_zone_instance(uint32 zone_id, uint32 instance_id) {
return Expedition::FindCachedExpeditionByZoneInstance(zone_id, instance_id);
}
luabind::object lua_get_expedition_lockout_by_char_id(lua_State* L, uint32 char_id, std::string expedition_name, std::string event_name) {
luabind::adl::object lua_table = luabind::newtable(L);
auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id);
auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const ExpeditionLockoutTimer& lockout) {
return lockout.IsSameLockout(expedition_name, event_name);
});
if (it != lockouts.end())
{
lua_table["remaining"] = it->GetSecondsRemaining();
lua_table["uuid"] = it->GetExpeditionUUID();
}
return lua_table;
}
luabind::object lua_get_expedition_lockouts_by_char_id(lua_State* L, uint32 char_id) {
luabind::adl::object lua_table = luabind::newtable(L);
auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id);
for (const auto& lockout : lockouts)
{
auto lockout_table = lua_table[lockout.GetExpeditionName()];
if (luabind::type(lockout_table) != LUA_TTABLE)
{
lockout_table = luabind::newtable(L);
}
auto event_table = lockout_table[lockout.GetEventName()];
if (luabind::type(event_table) != LUA_TTABLE)
{
event_table = luabind::newtable(L);
}
event_table["remaining"] = lockout.GetSecondsRemaining();
event_table["uuid"] = lockout.GetExpeditionUUID();
}
return lua_table;
}
luabind::object lua_get_expedition_lockouts_by_char_id(lua_State* L, uint32 char_id, std::string expedition_name) {
luabind::adl::object lua_table = luabind::newtable(L);
auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(char_id);
for (const auto& lockout : lockouts)
{
if (lockout.GetExpeditionName() == expedition_name)
{
auto event_table = lua_table[lockout.GetEventName()];
if (luabind::type(event_table) != LUA_TTABLE)
{
event_table = luabind::newtable(L);
}
event_table["remaining"] = lockout.GetSecondsRemaining();
event_table["uuid"] = lockout.GetExpeditionUUID();
}
}
return lua_table;
}
void lua_add_expedition_lockout_all_clients(std::string expedition_name, std::string event_name, uint32 seconds) {
auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds);
Expedition::AddLockoutClients(lockout);
}
void lua_add_expedition_lockout_all_clients(std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) {
auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid);
Expedition::AddLockoutClients(lockout);
}
void lua_add_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name, uint32 seconds) {
Expedition::AddLockoutByCharacterID(char_id, expedition_name, event_name, seconds);
}
void lua_add_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name, uint32 seconds, std::string uuid) {
Expedition::AddLockoutByCharacterID(char_id, expedition_name, event_name, seconds, uuid);
}
void lua_remove_expedition_lockout_by_char_id(uint32 char_id, std::string expedition_name, std::string event_name) {
Expedition::RemoveLockoutsByCharacterID(char_id, expedition_name, event_name);
}
void lua_remove_all_expedition_lockouts_by_char_id(uint32 char_id) {
Expedition::RemoveLockoutsByCharacterID(char_id);
}
void lua_remove_all_expedition_lockouts_by_char_id(uint32 char_id, std::string expedition_name) {
Expedition::RemoveLockoutsByCharacterID(char_id, expedition_name);
}
#define LuaCreateNPCParse(name, c_type, default_value) do { \
cur = table[#name]; \
if(luabind::type(cur) != LUA_TNIL) { \
@ -2775,7 +2889,22 @@ luabind::scope lua_register_general() {
* Content flags
*/
luabind::def("is_content_flag_enabled", (bool(*)(std::string))&lua_is_content_flag_enabled),
luabind::def("set_content_flag", (void(*)(std::string, bool))&lua_set_content_flag)
luabind::def("set_content_flag", (void(*)(std::string, bool))&lua_set_content_flag),
luabind::def("get_expedition", &lua_get_expedition),
luabind::def("get_expedition_by_char_id", &lua_get_expedition_by_char_id),
luabind::def("get_expedition_by_dz_id", &lua_get_expedition_by_dz_id),
luabind::def("get_expedition_by_zone_instance", &lua_get_expedition_by_zone_instance),
luabind::def("get_expedition_lockout_by_char_id", &lua_get_expedition_lockout_by_char_id),
luabind::def("get_expedition_lockouts_by_char_id", (luabind::object(*)(lua_State*, uint32))&lua_get_expedition_lockouts_by_char_id),
luabind::def("get_expedition_lockouts_by_char_id", (luabind::object(*)(lua_State*, uint32, std::string))&lua_get_expedition_lockouts_by_char_id),
luabind::def("add_expedition_lockout_all_clients", (void(*)(std::string, std::string, uint32))&lua_add_expedition_lockout_all_clients),
luabind::def("add_expedition_lockout_all_clients", (void(*)(std::string, std::string, uint32, std::string))&lua_add_expedition_lockout_all_clients),
luabind::def("add_expedition_lockout_by_char_id", (void(*)(uint32, std::string, std::string, uint32))&lua_add_expedition_lockout_by_char_id),
luabind::def("add_expedition_lockout_by_char_id", (void(*)(uint32, std::string, std::string, uint32, std::string))&lua_add_expedition_lockout_by_char_id),
luabind::def("remove_expedition_lockout_by_char_id", &lua_remove_expedition_lockout_by_char_id),
luabind::def("remove_all_expedition_lockouts_by_char_id", (void(*)(uint32))&lua_remove_all_expedition_lockouts_by_char_id),
luabind::def("remove_all_expedition_lockouts_by_char_id", (void(*)(uint32, std::string))&lua_remove_all_expedition_lockouts_by_char_id)
];
}

View File

@ -107,6 +107,18 @@ Lua_Mob Lua_Group::GetMember(int index) {
return self->members[index];
}
bool Lua_Group::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name)
{
Lua_Safe_Call_Bool();
return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name);
}
bool Lua_Group::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name, int max_check_count)
{
Lua_Safe_Call_Bool();
return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name, max_check_count);
}
luabind::scope lua_register_group() {
return luabind::class_<Lua_Group>("Group")
.def(luabind::constructor<>())
@ -129,7 +141,9 @@ luabind::scope lua_register_group() {
.def("GetLowestLevel", (int(Lua_Group::*)(void))&Lua_Group::GetLowestLevel)
.def("TeleportGroup", (void(Lua_Group::*)(Lua_Mob,uint32,uint32,float,float,float,float))&Lua_Group::TeleportGroup)
.def("GetID", (int(Lua_Group::*)(void))&Lua_Group::GetID)
.def("GetMember", (Lua_Mob(Lua_Group::*)(int))&Lua_Group::GetMember);
.def("GetMember", (Lua_Mob(Lua_Group::*)(int))&Lua_Group::GetMember)
.def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Group::*)(std::string, std::string))&Lua_Group::DoesAnyMemberHaveExpeditionLockout)
.def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Group::*)(std::string, std::string, int))&Lua_Group::DoesAnyMemberHaveExpeditionLockout);
}
#endif

View File

@ -44,6 +44,8 @@ public:
void TeleportGroup(Lua_Mob sender, uint32 zone_id, uint32 instance_id, float x, float y, float z, float h);
int GetID();
Lua_Mob GetMember(int index);
bool DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name);
bool DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name, int max_check_count);
};
#endif

View File

@ -788,16 +788,18 @@ luabind::scope lua_register_packet_opcodes() {
luabind::value("DzRemovePlayer", static_cast<int>(OP_DzRemovePlayer)),
luabind::value("DzSwapPlayer", static_cast<int>(OP_DzSwapPlayer)),
luabind::value("DzMakeLeader", static_cast<int>(OP_DzMakeLeader)),
luabind::value("DzJoinExpeditionConfirm", static_cast<int>(OP_DzJoinExpeditionConfirm)),
luabind::value("DzJoinExpeditionReply", static_cast<int>(OP_DzJoinExpeditionReply)),
luabind::value("DzExpeditionInvite", static_cast<int>(OP_DzExpeditionInvite)),
luabind::value("DzExpeditionInviteResponse", static_cast<int>(OP_DzExpeditionInviteResponse)),
luabind::value("DzExpeditionInfo", static_cast<int>(OP_DzExpeditionInfo)),
luabind::value("DzMemberStatus", static_cast<int>(OP_DzMemberStatus)),
luabind::value("DzLeaderStatus", static_cast<int>(OP_DzLeaderStatus)),
luabind::value("DzMemberListName", static_cast<int>(OP_DzMemberListName)),
luabind::value("DzMemberListStatus", static_cast<int>(OP_DzMemberListStatus)),
luabind::value("DzSetLeaderName", static_cast<int>(OP_DzSetLeaderName)),
luabind::value("DzExpeditionEndsWarning", static_cast<int>(OP_DzExpeditionEndsWarning)),
luabind::value("DzExpeditionList", static_cast<int>(OP_DzExpeditionList)),
luabind::value("DzExpeditionLockoutTimers", static_cast<int>(OP_DzExpeditionLockoutTimers)),
luabind::value("DzMemberList", static_cast<int>(OP_DzMemberList)),
luabind::value("DzCompass", static_cast<int>(OP_DzCompass)),
luabind::value("DzChooseZone", static_cast<int>(OP_DzChooseZone)),
luabind::value("DzChooseZoneReply", static_cast<int>(OP_DzChooseZoneReply)),
luabind::value("BuffCreate", static_cast<int>(OP_BuffCreate)),
luabind::value("GuildStatus", static_cast<int>(OP_GuildStatus)),
luabind::value("BuffRemoveRequest", static_cast<int>(OP_BuffRemoveRequest)),

View File

@ -19,6 +19,7 @@
#include "lua_parser.h"
#include "lua_bit.h"
#include "lua_entity.h"
#include "lua_expedition.h"
#include "lua_item.h"
#include "lua_iteminst.h"
#include "lua_mob.h"
@ -1108,7 +1109,9 @@ void LuaParser::MapFunctions(lua_State *L) {
lua_register_ruler(),
lua_register_ruleb(),
lua_register_journal_speakmode(),
lua_register_journal_mode()
lua_register_journal_mode(),
lua_register_expedition(),
lua_register_expedition_lock_messages()
];
} catch(std::exception &ex) {

View File

@ -132,6 +132,17 @@ int Lua_Raid::GetGroupNumber(int index) {
return self->members[index].GroupNumber;
}
bool Lua_Raid::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name)
{
Lua_Safe_Call_Bool();
return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name);
}
bool Lua_Raid::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name, int max_check_count)
{
Lua_Safe_Call_Bool();
return self->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name, max_check_count);
}
luabind::scope lua_register_raid() {
return luabind::class_<Lua_Raid>("Raid")
@ -158,7 +169,9 @@ luabind::scope lua_register_raid() {
.def("TeleportRaid", (int(Lua_Raid::*)(Lua_Mob,uint32,uint32,float,float,float,float))&Lua_Raid::TeleportRaid)
.def("GetID", (int(Lua_Raid::*)(void))&Lua_Raid::GetID)
.def("GetMember", (Lua_Client(Lua_Raid::*)(int))&Lua_Raid::GetMember)
.def("GetGroupNumber", (int(Lua_Raid::*)(int))&Lua_Raid::GetGroupNumber);
.def("GetGroupNumber", (int(Lua_Raid::*)(int))&Lua_Raid::GetGroupNumber)
.def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Raid::*)(std::string, std::string))&Lua_Raid::DoesAnyMemberHaveExpeditionLockout)
.def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Raid::*)(std::string, std::string, int))&Lua_Raid::DoesAnyMemberHaveExpeditionLockout);
}
#endif

View File

@ -48,6 +48,8 @@ public:
int GetID();
Lua_Client GetMember(int index);
int GetGroupNumber(int index);
bool DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name);
bool DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, std::string event_name, int max_check_count);
};
#endif

View File

@ -37,12 +37,26 @@
#endif
#include "client.h"
#include "expedition.h"
#include "titles.h"
#ifdef THIS /* this macro seems to leak out on some systems */
#undef THIS
#endif
#define VALIDATE_THIS_IS_CLIENT \
do { \
if (sv_derived_from(ST(0), "Client")) { \
IV tmp = SvIV((SV*)SvRV(ST(0))); \
THIS = INT2PTR(Client*, tmp); \
} else { \
Perl_croak(aTHX_ "THIS is not of type Client"); \
} \
if (THIS == nullptr) { \
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); \
} \
} while (0);
XS(XS_Client_SendSound); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_SendSound) {
dXSARGS;
@ -6759,6 +6773,283 @@ XS(XS_Client_GetClientMaxLevel) {
XSRETURN(1);
}
XS(XS_Client_CreateExpedition);
XS(XS_Client_CreateExpedition) {
dXSARGS;
if (items != 7 && items != 8) {
Perl_croak(aTHX_ "Usage: Client::CreateExpedition(THIS, string zone_name, uint32 zone_version, uint32 duration, string expedition_name, uint32 min_players, uint32 max_players, [bool disable_messages = false])");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
std::string zone_name(SvPV_nolen(ST(1)));
uint32 zone_version = (uint32)SvUV(ST(2));
uint32 duration = (uint32)SvUV(ST(3));
std::string expedition_name(SvPV_nolen(ST(4)));
uint32 min_players = (uint32)SvUV(ST(5));
uint32 max_players = (uint32)SvUV(ST(6));
bool disable_messages = (items > 7) ? (bool)SvTRUE(ST(7)) : false;
Expedition* RETVAL = THIS->CreateExpedition(zone_name, zone_version, duration,
expedition_name, min_players, max_players, disable_messages);
ST(0) = sv_newmortal();
sv_setref_pv(ST(0), "Expedition", (void*)RETVAL);
XSRETURN(1);
}
XS(XS_Client_GetExpedition);
XS(XS_Client_GetExpedition) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Client::GetExpedition(THIS)");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
Expedition* RETVAL = THIS->GetExpedition();
ST(0) = sv_newmortal();
sv_setref_pv(ST(0), "Expedition", (void*)RETVAL);
XSRETURN(1);
}
XS(XS_Client_GetExpeditionLockouts);
XS(XS_Client_GetExpeditionLockouts) {
dXSARGS;
if (items != 1 && items != 2) {
Perl_croak(aTHX_ "Usage: Client::GetExpeditionLockouts(THIS, [string expedition_name])");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
HV* hash = newHV();
SV* hash_ref = nullptr; // for expedition event hash if filtering on expedition
std::string expedition_name;
if (items == 2)
{
expedition_name = SvPV_nolen(ST(1));
}
auto lockouts = THIS->GetExpeditionLockouts();
for (const auto& lockout : lockouts)
{
uint32_t name_len = static_cast<uint32_t>(lockout.GetExpeditionName().size());
uint32_t event_len = static_cast<uint32_t>(lockout.GetEventName().size());
SV** entry = hv_fetch(hash, lockout.GetExpeditionName().c_str(), name_len, false);
if (!entry)
{
SV* event_hash_ref = newRV_noinc((SV*)newHV()); // takes ownership of hash
if (!expedition_name.empty() && lockout.GetExpeditionName() == expedition_name)
{
hash_ref = event_hash_ref; // save ref to event hash for return
}
entry = hv_store(hash, lockout.GetExpeditionName().c_str(), name_len, event_hash_ref, 0);
}
if (entry && SvROK(*entry) && SvTYPE(SvRV(*entry)) == SVt_PVHV)
{
HV* event_hash = (HV*)SvRV(*entry);
hv_store(event_hash, lockout.GetEventName().c_str(), event_len,
newSVuv(lockout.GetSecondsRemaining()), 0);
}
}
SV* rv = &PL_sv_undef;
if (!expedition_name.empty())
{
rv = hash_ref ? sv_2mortal(hash_ref) : &PL_sv_undef; // ref that owns event hash for expedition
}
else
{
rv = sv_2mortal(newRV_noinc((SV*)hash)); // owns expedition hash
}
ST(0) = rv;
XSRETURN(1);
}
XS(XS_Client_GetLockoutExpeditionUUID);
XS(XS_Client_GetLockoutExpeditionUUID) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: Client::GetLockoutExpeditionUUID(THIS, string expedition_name, string event_name)");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
std::string expedition_name = SvPV_nolen(ST(1));
std::string event_name = SvPV_nolen(ST(2));
auto lockout = THIS->GetExpeditionLockout(expedition_name, event_name);
if (lockout)
{
XSRETURN_PV(lockout->GetExpeditionUUID().c_str());
}
XSRETURN_UNDEF;
}
XS(XS_Client_AddExpeditionLockout);
XS(XS_Client_AddExpeditionLockout) {
dXSARGS;
if (items != 4 && items != 5) {
Perl_croak(aTHX_ "Usage: Client::AddExpeditionLockout(THIS, string expedition_name, string event_name, uint32 seconds, [string uuid])");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
std::string expedition_name(SvPV_nolen(ST(1)));
std::string event_name(SvPV_nolen(ST(2)));
uint32 seconds = (uint32)SvUV(ST(3));
std::string uuid;
if (items == 5)
{
uuid = SvPV_nolen(ST(4));
}
THIS->AddNewExpeditionLockout(expedition_name, event_name, seconds, uuid);
XSRETURN_EMPTY;
}
XS(XS_Client_AddExpeditionLockoutDuration);
XS(XS_Client_AddExpeditionLockoutDuration) {
dXSARGS;
if (items != 4 && items != 5) {
Perl_croak(aTHX_ "Usage: Client::AddExpeditionLockoutDuration(THIS, string expedition_name, string event_name, int seconds, [string uuid])");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
std::string expedition_name(SvPV_nolen(ST(1)));
std::string event_name(SvPV_nolen(ST(2)));
int seconds = static_cast<int>(SvUV(ST(3)));
std::string uuid;
if (items == 5)
{
uuid = SvPV_nolen(ST(4));
}
THIS->AddExpeditionLockoutDuration(expedition_name, event_name, seconds, uuid, true);
XSRETURN_EMPTY;
}
XS(XS_Client_RemoveAllExpeditionLockouts);
XS(XS_Client_RemoveAllExpeditionLockouts) {
dXSARGS;
if (items != 1 && items != 2) {
Perl_croak(aTHX_ "Usage: Client::RemoveAllExpeditionLockouts(THIS, [string expedition_name])");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
std::string expedition_name;
if (items == 2)
{
expedition_name = SvPV_nolen(ST(1));
}
THIS->RemoveAllExpeditionLockouts(expedition_name, true);
XSRETURN_EMPTY;
}
XS(XS_Client_RemoveExpeditionLockout);
XS(XS_Client_RemoveExpeditionLockout) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: Client::RemoveExpeditionLockout(THIS, string expedition_name, string event_name)");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
std::string expedition_name(SvPV_nolen(ST(1)));
std::string event_name(SvPV_nolen(ST(2)));
THIS->RemoveExpeditionLockout(expedition_name, event_name, true);
XSRETURN_EMPTY;
}
XS(XS_Client_HasExpeditionLockout);
XS(XS_Client_HasExpeditionLockout) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: Client::HasExpeditionLockout(THIS, string expedition_name, string event_name)");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
std::string expedition_name(SvPV_nolen(ST(1)));
std::string event_name(SvPV_nolen(ST(2)));
bool result = THIS->HasExpeditionLockout(expedition_name, event_name);
ST(0) = boolSV(result);
XSRETURN(1);
}
XS(XS_Client_MovePCDynamicZone);
XS(XS_Client_MovePCDynamicZone) {
dXSARGS;
if (items != 2 && items != 3 && items != 4) {
Perl_croak(aTHX_ "Usage: Client::MovePCDynamicZone(THIS, uint32 zone_id | string zone_name, [int zone_version = -1], [bool message_if_invalid = true])");
}
Client* THIS = nullptr;
VALIDATE_THIS_IS_CLIENT;
if (SvTYPE(ST(1)) == SVt_PV)
{
std::string zone_name(SvPV_nolen(ST(1)));
int zone_version = (items >= 3) ? static_cast<int>(SvIV(ST(2))) : -1;
if (items == 4)
{
THIS->MovePCDynamicZone(zone_name, zone_version, (bool)SvTRUE(ST(3)));
}
else
{
THIS->MovePCDynamicZone(zone_name, zone_version);
}
}
else if (SvTYPE(ST(1)) == SVt_IV)
{
uint32 zone_id = (uint32)SvUV(ST(1));
int zone_version = (items >= 3) ? static_cast<int>(SvIV(ST(2))) : -1;
if (items == 3)
{
THIS->MovePCDynamicZone(zone_id, zone_version, (bool)SvTRUE(ST(2)));
}
else
{
THIS->MovePCDynamicZone(zone_id, zone_version);
}
}
else
{
Perl_croak(aTHX_ "Client::MovePCDynamicZone expected an integer or string");
}
XSRETURN_EMPTY;
}
#ifdef __cplusplus
extern "C"
@ -6786,6 +7077,8 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "AddAlternateCurrencyValue"), XS_Client_AddAlternateCurrencyValue, file, "$$$");
newXSproto(strcpy(buf, "AddCrystals"), XS_Client_AddCrystals, file, "$$");
newXSproto(strcpy(buf, "AddEXP"), XS_Client_AddEXP, file, "$$;$$");
newXSproto(strcpy(buf, "AddExpeditionLockout"), XS_Client_AddExpeditionLockout, file, "$$$$;$");
newXSproto(strcpy(buf, "AddExpeditionLockoutDuration"), XS_Client_AddExpeditionLockoutDuration, file, "$$$$;$");
newXSproto(strcpy(buf, "AddLevelBasedExp"), XS_Client_AddLevelBasedExp, file, "$$;$$");
newXSproto(strcpy(buf, "AddMoneyToPP"), XS_Client_AddMoneyToPP, file, "$$$$$$");
newXSproto(strcpy(buf, "AddPVPPoints"), XS_Client_AddPVPPoints, file, "$$");
@ -6804,6 +7097,7 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "CheckSpecializeIncrease"), XS_Client_CheckSpecializeIncrease, file, "$$");
newXSproto(strcpy(buf, "ClearCompassMark"), XS_Client_ClearCompassMark, file, "$");
newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$");
newXSproto(strcpy(buf, "CreateExpedition"), XS_Client_CreateExpedition, file, "$$$$$$$;$");
newXSproto(strcpy(buf, "Connected"), XS_Client_Connected, file, "$");
newXSproto(strcpy(buf, "DecreaseByID"), XS_Client_DecreaseByID, file, "$$$");
newXSproto(strcpy(buf, "DeleteItemInInventory"), XS_Client_DeleteItemInInventory, file, "$$;$$");
@ -6858,6 +7152,8 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "GetEndurance"), XS_Client_GetEndurance, file, "$");
newXSproto(strcpy(buf, "GetEnduranceRatio"), XS_Client_GetEnduranceRatio, file, "$");
newXSproto(strcpy(buf, "GetEXP"), XS_Client_GetEXP, file, "$");
newXSproto(strcpy(buf, "GetExpedition"), XS_Client_GetExpedition, file, "$");
newXSproto(strcpy(buf, "GetExpeditionLockouts"), XS_Client_GetExpeditionLockouts, file, "$;$");
newXSproto(strcpy(buf, "GetFace"), XS_Client_GetFace, file, "$");
newXSproto(strcpy(buf, "GetFactionLevel"), XS_Client_GetFactionLevel, file, "$$$$$$$$");
newXSproto(strcpy(buf, "GetFeigned"), XS_Client_GetFeigned, file, "$");
@ -6880,6 +7176,7 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "GetLDoNPointsTheme"), XS_Client_GetLDoNPointsTheme, file, "$");
newXSproto(strcpy(buf, "GetLDoNWins"), XS_Client_GetLDoNWins, file, "$");
newXSproto(strcpy(buf, "GetLDoNWinsTheme"), XS_Client_GetLDoNWinsTheme, file, "$$");
newXSproto(strcpy(buf, "GetLockoutExpeditionUUID"), XS_Client_GetLockoutExpeditionUUID, file, "$$$");
newXSproto(strcpy(buf, "GetMaxEndurance"), XS_Client_GetMaxEndurance, file, "$");
newXSproto(strcpy(buf, "GetModCharacterFactionLevel"), XS_Client_GetModCharacterFactionLevel, file, "$$");
newXSproto(strcpy(buf, "GetMoney"), XS_Client_GetMoney, file, "$$$");
@ -6907,6 +7204,7 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "GrantAlternateAdvancementAbility"), XS_Client_GrantAlternateAdvancementAbility, file, "$$$;$");
newXSproto(strcpy(buf, "GuildID"), XS_Client_GuildID, file, "$");
newXSproto(strcpy(buf, "GuildRank"), XS_Client_GuildRank, file, "$");
newXSproto(strcpy(buf, "HasExpeditionLockout"), XS_Client_HasExpeditionLockout, file, "$$$");
newXSproto(strcpy(buf, "HasSkill"), XS_Client_HasSkill, file, "$$");
newXSproto(strcpy(buf, "HasSpellScribed"), XS_Client_HasSkill, file, "$$");
newXSproto(strcpy(buf, "HasZoneFlag"), XS_Client_HasZoneFlag, file, "$$");
@ -6938,6 +7236,7 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "MaxSkill"), XS_Client_MaxSkill, file, "$$;$$");
newXSproto(strcpy(buf, "MemSpell"), XS_Client_MemSpell, file, "$$$;$");
newXSproto(strcpy(buf, "MovePC"), XS_Client_MovePC, file, "$$$$$$");
newXSproto(strcpy(buf, "MovePCDynamicZone"), XS_Client_MovePCDynamicZone, file, "$$;$$");
newXSproto(strcpy(buf, "MovePCInstance"), XS_Client_MovePCInstance, file, "$$$$$$$");
newXSproto(strcpy(buf, "MoveZone"), XS_Client_MoveZone, file, "$$");
newXSproto(strcpy(buf, "MoveZoneGroup"), XS_Client_MoveZoneGroup, file, "$$");
@ -6954,6 +7253,8 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$");
newXSproto(strcpy(buf, "ReadBook"), XS_Client_ReadBook, file, "$$$");
newXSproto(strcpy(buf, "RefundAA"), XS_Client_RefundAA, file, "$$");
newXSproto(strcpy(buf, "RemoveAllExpeditionLockouts"), XS_Client_RemoveAllExpeditionLockouts, file, "$;$");
newXSproto(strcpy(buf, "RemoveExpeditionLockout"), XS_Client_RemoveExpeditionLockout, file, "$$$");
newXSproto(strcpy(buf, "RemoveNoRent"), XS_Client_RemoveNoRent, file, "$");
newXSproto(strcpy(buf, "ResetAA"), XS_Client_ResetAA, file, "$");
newXSproto(strcpy(buf, "ResetDisciplineTimer"), XS_Client_ResetDisciplineTimer, file, "$$");

676
zone/perl_expedition.cpp Normal file
View File

@ -0,0 +1,676 @@
/**
* 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 "../common/features.h"
#ifdef EMBPERL_XS_CLASSES
#include "expedition.h"
#include "zone_store.h"
#include "embperl.h"
#include "../common/global_define.h"
#ifdef seed
#undef seed
#endif
#ifdef THIS /* this macro seems to leak out on some systems */
#undef THIS
#endif
#define VALIDATE_THIS_IS_EXPEDITION \
do { \
if (sv_derived_from(ST(0), "Expedition")) { \
IV tmp = SvIV((SV*)SvRV(ST(0))); \
THIS = INT2PTR(Expedition*, tmp); \
} else { \
Perl_croak(aTHX_ "THIS is not of type Expedition"); \
} \
if (THIS == nullptr) { \
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); \
} \
} while (0);
XS(XS_Expedition_AddLockout);
XS(XS_Expedition_AddLockout) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: Expedition::AddLockout(THIS, string event_name, uint32 seconds)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
std::string event_name(SvPV_nolen(ST(1)));
uint32_t seconds = static_cast<uint32_t>(SvUV(ST(2)));
THIS->AddLockout(event_name, seconds);
XSRETURN_EMPTY;
}
XS(XS_Expedition_AddLockoutDuration);
XS(XS_Expedition_AddLockoutDuration) {
dXSARGS;
if (items != 3 && items != 4) {
Perl_croak(aTHX_ "Usage: Expedition::AddLockout(THIS, string event_name, int seconds, [bool members_only = true])");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
std::string event_name(SvPV_nolen(ST(1)));
int seconds = static_cast<int>(SvUV(ST(2)));
if (items == 4)
{
bool members_only = (bool)SvTRUE(ST(3));
THIS->AddLockoutDuration(event_name, seconds, members_only);
}
else
{
THIS->AddLockoutDuration(event_name, seconds);
}
XSRETURN_EMPTY;
}
XS(XS_Expedition_AddReplayLockout);
XS(XS_Expedition_AddReplayLockout) {
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Expedition::AddReplayLockout(THIS, uint32 seconds)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
uint32_t seconds = static_cast<uint32_t>(SvUV(ST(1)));
THIS->AddReplayLockout(seconds);
XSRETURN_EMPTY;
}
XS(XS_Expedition_AddReplayLockoutDuration);
XS(XS_Expedition_AddReplayLockoutDuration) {
dXSARGS;
if (items != 2 && items != 3) {
Perl_croak(aTHX_ "Usage: Expedition::AddReplayLockoutDuration(THIS, int seconds, [bool members_only = true])");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
int seconds = static_cast<int>(SvUV(ST(1)));
if (items == 3)
{
bool members_only = (bool)SvTRUE(ST(2));
THIS->AddReplayLockoutDuration(seconds, members_only);
}
else
{
THIS->AddReplayLockoutDuration(seconds);
}
XSRETURN_EMPTY;
}
XS(XS_Expedition_GetDynamicZoneID);
XS(XS_Expedition_GetDynamicZoneID) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetDynamicZoneID(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetDynamicZoneID());
}
XS(XS_Expedition_GetID);
XS(XS_Expedition_GetID) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetID(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetID());
}
XS(XS_Expedition_GetInstanceID);
XS(XS_Expedition_GetInstanceID) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetInstanceID(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetInstanceID());
}
XS(XS_Expedition_GetLeaderName);
XS(XS_Expedition_GetLeaderName) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetLeaderName(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_PV(THIS->GetLeaderName().c_str());
}
XS(XS_Expedition_GetLockouts);
XS(XS_Expedition_GetLockouts) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetLockouts(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
HV* hash = newHV();
auto lockouts = THIS->GetLockouts();
for (const auto& lockout : lockouts)
{
hv_store(hash, lockout.first.c_str(), static_cast<uint32_t>(lockout.first.size()),
newSVuv(lockout.second.GetSecondsRemaining()), 0);
}
ST(0) = sv_2mortal(newRV_noinc((SV*)hash)); // take ownership of hash (refcnt remains 1)
XSRETURN(1);
}
XS(XS_Expedition_GetLootEventByNPCTypeID);
XS(XS_Expedition_GetLootEventByNPCTypeID) {
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Expedition::GetLootEventByNPCTypeID(THIS, uint32 npc_type_id)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
uint32_t npc_type_id = static_cast<uint32_t>(SvUV(ST(1)));
XSRETURN_PV(THIS->GetLootEventByNPCTypeID(npc_type_id).c_str());
}
XS(XS_Expedition_GetLootEventBySpawnID);
XS(XS_Expedition_GetLootEventBySpawnID) {
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Expedition::GetLootEventBySpawnID(THIS, uint32 spawn_id)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
uint32_t spawn_id = static_cast<uint32_t>(SvUV(ST(1)));
XSRETURN_PV(THIS->GetLootEventBySpawnID(spawn_id).c_str());
}
XS(XS_Expedition_GetMemberCount);
XS(XS_Expedition_GetMemberCount) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetMemberCount(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetMemberCount());
}
XS(XS_Expedition_GetMembers);
XS(XS_Expedition_GetMembers) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetMembers(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
HV* hash = newHV();
auto members = THIS->GetMembers();
for (const auto& member : members)
{
hv_store(hash, member.name.c_str(), static_cast<uint32_t>(member.name.size()),
newSVuv(member.char_id), 0);
}
ST(0) = sv_2mortal(newRV_noinc((SV*)hash));
XSRETURN(1);
}
XS(XS_Expedition_GetName);
XS(XS_Expedition_GetName) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetName(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_PV(THIS->GetName().c_str());
}
XS(XS_Expedition_GetSecondsRemaining);
XS(XS_Expedition_GetSecondsRemaining) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetSecondsRemaining(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetDynamicZone().GetSecondsRemaining());
}
XS(XS_Expedition_GetUUID);
XS(XS_Expedition_GetUUID) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetUUID(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_PV(THIS->GetUUID().c_str());
}
XS(XS_Expedition_GetZoneID);
XS(XS_Expedition_GetZoneID) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetZoneID(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetDynamicZone().GetZoneID());
}
XS(XS_Expedition_GetZoneName);
XS(XS_Expedition_GetZoneName) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetZoneName(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_PV(ZoneName(THIS->GetDynamicZone().GetZoneID()));
}
XS(XS_Expedition_GetZoneVersion);
XS(XS_Expedition_GetZoneVersion) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::GetZoneVersion(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
XSRETURN_UV(THIS->GetDynamicZone().GetZoneVersion());
}
XS(XS_Expedition_HasLockout);
XS(XS_Expedition_HasLockout) {
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Expedition::HasLockout(THIS, string event_name)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
std::string event_name(SvPV_nolen(ST(1)));
bool result = THIS->HasLockout(event_name);
ST(0) = boolSV(result);
XSRETURN(1);
}
XS(XS_Expedition_HasReplayLockout);
XS(XS_Expedition_HasReplayLockout) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::HasReplayLockout(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
bool result = THIS->HasReplayLockout();
ST(0) = boolSV(result);
XSRETURN(1);
}
XS(XS_Expedition_RemoveCompass);
XS(XS_Expedition_RemoveCompass) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: Expedition::RemoveCompass(THIS)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
THIS->SetDzCompass(0, 0, 0, 0, true);
XSRETURN_EMPTY;
}
XS(XS_Expedition_RemoveLockout);
XS(XS_Expedition_RemoveLockout) {
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Expedition::RemoveLockout(THIS, string event_name)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
std::string event_name(SvPV_nolen(ST(1)));
THIS->RemoveLockout(event_name);
XSRETURN_EMPTY;
}
XS(XS_Expedition_SetCompass);
XS(XS_Expedition_SetCompass) {
dXSARGS;
if (items != 5) {
Perl_croak(aTHX_ "Usage: Expedition::SetCompass(THIS, uint32 zone_id | string zone_name, float x, float y, float z)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
float x = static_cast<float>(SvNV(ST(2)));
float y = static_cast<float>(SvNV(ST(3)));
float z = static_cast<float>(SvNV(ST(4)));
if (SvTYPE(ST(1)) == SVt_PV)
{
std::string zone_name(SvPV_nolen(ST(1)));
THIS->SetDzCompass(zone_name, x, y, z, true);
}
else if (SvTYPE(ST(1)) == SVt_IV)
{
uint32_t zone_id = static_cast<uint32_t>(SvUV(ST(1)));
THIS->SetDzCompass(zone_id, x, y, z, true);
}
else
{
Perl_croak(aTHX_ "Expedition::SetCompass expected an integer or string");
}
XSRETURN_EMPTY;
}
XS(XS_Expedition_SetLocked);
XS(XS_Expedition_SetLocked) {
dXSARGS;
if (items != 2 && items != 3 && items != 4) {
Perl_croak(aTHX_ "Usage: Expedition::SetLocked(THIS, bool locked, [int lock_msg = 0], [uint32 color = 15])");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
bool locked = (bool)SvTRUE(ST(1));
int lock_msg = (items == 3) ? static_cast<int>(SvIV(ST(2))) : 0;
if (items == 4)
{
THIS->SetLocked(locked, static_cast<ExpeditionLockMessage>(lock_msg), true, (uint32)SvUV(ST(3)));
}
else
{
THIS->SetLocked(locked, static_cast<ExpeditionLockMessage>(lock_msg), true);
}
XSRETURN_EMPTY;
}
XS(XS_Expedition_SetLootEventByNPCTypeID);
XS(XS_Expedition_SetLootEventByNPCTypeID) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: Expedition::SetLootEventByNPCTypeID(THIS, uint32 npc_type_id, string event_name)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
uint32_t npc_type_id = static_cast<uint32_t>(SvUV(ST(1)));
std::string event_name(SvPV_nolen(ST(2)));
THIS->SetLootEventByNPCTypeID(npc_type_id, event_name);
XSRETURN_EMPTY;
}
XS(XS_Expedition_SetLootEventBySpawnID);
XS(XS_Expedition_SetLootEventBySpawnID) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: Expedition::SetLootEventBySpawnID(THIS, uint32 spawn_id, string event_name)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
uint32_t spawn_id = static_cast<uint32_t>(SvUV(ST(1)));
std::string event_name(SvPV_nolen(ST(2)));
THIS->SetLootEventBySpawnID(spawn_id, event_name);
XSRETURN_EMPTY;
}
XS(XS_Expedition_SetReplayLockoutOnMemberJoin);
XS(XS_Expedition_SetReplayLockoutOnMemberJoin) {
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Expedition::SetReplayLockoutOnMemberJoin(THIS, bool enable)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
bool enable = (bool)SvTRUE(ST(1));
THIS->SetReplayLockoutOnMemberJoin(enable, true);
XSRETURN_EMPTY;
}
XS(XS_Expedition_SetSafeReturn);
XS(XS_Expedition_SetSafeReturn) {
dXSARGS;
if (items != 6) {
Perl_croak(aTHX_ "Usage: Expedition::SetSafeReturn(THIS, uint32 zone_id | string zone_name, float x, float y, float z, float heading)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
float x = static_cast<float>(SvNV(ST(2)));
float y = static_cast<float>(SvNV(ST(3)));
float z = static_cast<float>(SvNV(ST(4)));
float heading = static_cast<float>(SvNV(ST(5)));
if (SvTYPE(ST(1)) == SVt_PV)
{
std::string zone_name(SvPV_nolen(ST(1)));
THIS->SetDzSafeReturn(zone_name, x, y, z, heading, true);
}
else if (SvTYPE(ST(1)) == SVt_IV)
{
uint32_t zone_id = static_cast<uint32_t>(SvUV(ST(1)));
THIS->SetDzSafeReturn(zone_id, x, y, z, heading, true);
}
else
{
Perl_croak(aTHX_ "Expedition::SetSafeReturn expected an integer or string");
}
XSRETURN_EMPTY;
}
XS(XS_Expedition_SetSecondsRemaining);
XS(XS_Expedition_SetSecondsRemaining) {
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Expedition::SetSecondsRemaining(THIS, uint32 seconds_remaining)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
uint32_t seconds_remaining = static_cast<uint32_t>(SvUV(ST(1)));
THIS->SetDzSecondsRemaining(seconds_remaining);
XSRETURN_EMPTY;
}
XS(XS_Expedition_SetZoneInLocation);
XS(XS_Expedition_SetZoneInLocation) {
dXSARGS;
if (items != 5) {
Perl_croak(aTHX_ "Usage: Expedition::SetZoneInLocation(THIS, float x, float y, float z, float heading)");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
float x = static_cast<float>(SvNV(ST(1)));
float y = static_cast<float>(SvNV(ST(2)));
float z = static_cast<float>(SvNV(ST(3)));
float heading = static_cast<float>(SvNV(ST(4)));
THIS->SetDzZoneInLocation(x, y, z, heading, true);
XSRETURN_EMPTY;
}
XS(XS_Expedition_UpdateLockoutDuration);
XS(XS_Expedition_UpdateLockoutDuration) {
dXSARGS;
if (items != 3 && items != 4) {
Perl_croak(aTHX_ "Usage: Expedition::UpdateLockoutDuration(THIS, string event_name, uint32 seconds, [bool members_only = true])");
}
Expedition* THIS = nullptr;
VALIDATE_THIS_IS_EXPEDITION;
std::string event_name(SvPV_nolen(ST(1)));
uint32_t seconds = static_cast<uint32_t>(SvUV(ST(2)));
if (items == 4)
{
bool members_only = (bool)SvTRUE(ST(3));
THIS->UpdateLockoutDuration(event_name, seconds, members_only);
}
else
{
THIS->UpdateLockoutDuration(event_name, seconds);
}
XSRETURN_EMPTY;
}
XS(boot_Expedition);
XS(boot_Expedition) {
dXSARGS;
char file[256];
strncpy(file, __FILE__, 256);
file[255] = 0;
if (items != 1) {
fprintf(stderr, "boot_Expedition does not take any arguments.");
}
char buf[128];
XS_VERSION_BOOTCHECK;
newXSproto(strcpy(buf, "AddLockout"), XS_Expedition_AddLockout, file, "$$$");
newXSproto(strcpy(buf, "AddLockoutDuration"), XS_Expedition_AddLockoutDuration, file, "$$$;$");
newXSproto(strcpy(buf, "AddReplayLockout"), XS_Expedition_AddReplayLockout, file, "$$");
newXSproto(strcpy(buf, "AddReplayLockoutDuration"), XS_Expedition_AddReplayLockoutDuration, file, "$$;$");
newXSproto(strcpy(buf, "GetDynamicZoneID"), XS_Expedition_GetDynamicZoneID, file, "$");
newXSproto(strcpy(buf, "GetID"), XS_Expedition_GetID, file, "$");
newXSproto(strcpy(buf, "GetInstanceID"), XS_Expedition_GetInstanceID, file, "$");
newXSproto(strcpy(buf, "GetLeaderName"), XS_Expedition_GetLeaderName, file, "$");
newXSproto(strcpy(buf, "GetLockouts"), XS_Expedition_GetLockouts, file, "$");
newXSproto(strcpy(buf, "GetLootEventByNPCTypeID"), XS_Expedition_GetLootEventByNPCTypeID, file, "$$");
newXSproto(strcpy(buf, "GetLootEventBySpawnID"), XS_Expedition_GetLootEventBySpawnID, file, "$$");
newXSproto(strcpy(buf, "GetMemberCount"), XS_Expedition_GetMemberCount, file, "$");
newXSproto(strcpy(buf, "GetMembers"), XS_Expedition_GetMembers, file, "$");
newXSproto(strcpy(buf, "GetName"), XS_Expedition_GetName, file, "$");
newXSproto(strcpy(buf, "GetSecondsRemaining"), XS_Expedition_GetSecondsRemaining, file, "$");
newXSproto(strcpy(buf, "GetUUID"), XS_Expedition_GetUUID, file, "$");
newXSproto(strcpy(buf, "GetZoneID"), XS_Expedition_GetZoneID, file, "$");
newXSproto(strcpy(buf, "GetZoneName"), XS_Expedition_GetZoneName, file, "$");
newXSproto(strcpy(buf, "GetZoneVersion"), XS_Expedition_GetZoneVersion, file, "$");
newXSproto(strcpy(buf, "HasLockout"), XS_Expedition_HasLockout, file, "$$");
newXSproto(strcpy(buf, "HasReplayLockout"), XS_Expedition_HasReplayLockout, file, "$");
newXSproto(strcpy(buf, "RemoveCompass"), XS_Expedition_RemoveCompass, file, "$");
newXSproto(strcpy(buf, "RemoveLockout"), XS_Expedition_RemoveLockout, file, "$$");
newXSproto(strcpy(buf, "SetCompass"), XS_Expedition_SetCompass, file, "$$$$$");
newXSproto(strcpy(buf, "SetLocked"), XS_Expedition_SetLocked, file, "$$;$$");
newXSproto(strcpy(buf, "SetLootEventByNPCTypeID"), XS_Expedition_SetLootEventByNPCTypeID, file, "$$$");
newXSproto(strcpy(buf, "SetLootEventBySpawnID"), XS_Expedition_SetLootEventBySpawnID, file, "$$$");
newXSproto(strcpy(buf, "SetReplayLockoutOnMemberJoin"), XS_Expedition_SetReplayLockoutOnMemberJoin, file, "$$");
newXSproto(strcpy(buf, "SetSafeReturn"), XS_Expedition_SetSafeReturn, file, "$$$$$$");
newXSproto(strcpy(buf, "SetSecondsRemaining"), XS_Expedition_SetSecondsRemaining, file, "$$");
newXSproto(strcpy(buf, "SetZoneInLocation"), XS_Expedition_SetZoneInLocation, file, "$$$$$");
newXSproto(strcpy(buf, "UpdateLockoutDuration"), XS_Expedition_UpdateLockoutDuration, file, "$$$;$");
HV* stash = gv_stashpvs("ExpeditionLockMessage", GV_ADD);
newCONSTSUB(stash, "None", newSViv(static_cast<int>(ExpeditionLockMessage::None)));
newCONSTSUB(stash, "Close", newSViv(static_cast<int>(ExpeditionLockMessage::Close)));
newCONSTSUB(stash, "Begin", newSViv(static_cast<int>(ExpeditionLockMessage::Begin)));
XSRETURN_YES;
}
#endif //EMBPERL_XS_CLASSES

View File

@ -576,6 +576,31 @@ XS(XS_Group_GetMember) {
XSRETURN(1);
}
XS(XS_Group_DoesAnyMemberHaveExpeditionLockout);
XS(XS_Group_DoesAnyMemberHaveExpeditionLockout) {
dXSARGS;
if (items != 3 && items != 4) {
Perl_croak(aTHX_ "Usage: Group::DoesAnyMemberHaveExpeditionLockout(THIS, string expedition_name, string event_name, [int max_check_count = 0])");
}
Group* THIS = nullptr;
if (sv_derived_from(ST(0), "Group")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
THIS = INT2PTR(Group *, tmp);
} else
Perl_croak(aTHX_ "THIS is not of type Group");
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
std::string expedition_name(SvPV_nolen(ST(1)));
std::string event_name(SvPV_nolen(ST(2)));
int max_check_count = (items == 4) ? static_cast<int>(SvIV(ST(3))) : 0;
bool result = THIS->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name, max_check_count);
ST(0) = boolSV(result);
XSRETURN(1);
}
#ifdef __cplusplus
extern "C"
#endif
@ -612,6 +637,7 @@ XS(boot_Group) {
newXSproto(strcpy(buf, "TeleportGroup"), XS_Group_TeleportGroup, file, "$$$$$$$");
newXSproto(strcpy(buf, "GetID"), XS_Group_GetID, file, "$");
newXSproto(strcpy(buf, "GetMember"), XS_Group_GetMember, file, "$$");
newXSproto(strcpy(buf, "DoesAnyMemberHaveExpeditionLockout"), XS_Group_DoesAnyMemberHaveExpeditionLockout, file, "$$$;$");
XSRETURN_YES;
}

View File

@ -545,6 +545,31 @@ XS(XS_Raid_GetMember) {
XSRETURN(1);
}
XS(XS_Raid_DoesAnyMemberHaveExpeditionLockout);
XS(XS_Raid_DoesAnyMemberHaveExpeditionLockout) {
dXSARGS;
if (items != 3 && items != 4) {
Perl_croak(aTHX_ "Usage: Raid::DoesAnyMemberHaveExpeditionLockout(THIS, string expedition_name, string event_name, [int max_check_count = 0])");
}
Raid* THIS = nullptr;
if (sv_derived_from(ST(0), "Raid")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
THIS = INT2PTR(Raid *, tmp);
} else
Perl_croak(aTHX_ "THIS is not of type Raid");
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
std::string expedition_name(SvPV_nolen(ST(1)));
std::string event_name(SvPV_nolen(ST(2)));
int max_check_count = (items == 4) ? static_cast<int>(SvIV(ST(3))) : 0;
bool result = THIS->DoesAnyMemberHaveExpeditionLockout(expedition_name, event_name, max_check_count);
ST(0) = boolSV(result);
XSRETURN(1);
}
#ifdef __cplusplus
extern "C"
#endif
@ -581,6 +606,7 @@ XS(boot_Raid) {
newXSproto(strcpy(buf, "TeleportRaid"), XS_Raid_TeleportRaid, file, "$$$$$$$");
newXSproto(strcpy(buf, "GetID"), XS_Raid_GetID, file, "$");
newXSproto(strcpy(buf, "GetMember"), XS_Raid_GetMember, file, "$$");
newXSproto(strcpy(buf, "DoesAnyMemberHaveExpeditionLockout"), XS_Raid_DoesAnyMemberHaveExpeditionLockout, file, "$$$;$");
XSRETURN_YES;
}

View File

@ -20,6 +20,7 @@
#include "client.h"
#include "entity.h"
#include "expedition.h"
#include "groups.h"
#include "mob.h"
#include "raids.h"
@ -1849,3 +1850,42 @@ void Raid::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_re
}
}
}
std::vector<RaidMember> Raid::GetMembers() const
{
std::vector<RaidMember> raid_members;
for (int i = 0; i < MAX_RAID_MEMBERS; ++i)
{
if (members[i].membername[0])
{
raid_members.emplace_back(members[i]);
}
}
return raid_members;
}
bool Raid::DoesAnyMemberHaveExpeditionLockout(
const std::string& expedition_name, const std::string& event_name, int max_check_count)
{
auto raid_members = GetMembers();
if (max_check_count > 0)
{
// priority is leader, group number, then ungrouped members
std::sort(raid_members.begin(), raid_members.end(),
[&](const RaidMember& lhs, const RaidMember& rhs) {
if (lhs.IsRaidLeader) {
return true;
} else if (rhs.IsRaidLeader) {
return false;
}
return lhs.GroupNumber < rhs.GroupNumber;
});
raid_members.resize(max_check_count);
}
return std::any_of(raid_members.begin(), raid_members.end(), [&](const RaidMember& raid_member) {
return Expedition::HasLockoutByCharacterName(raid_member.membername, expedition_name, event_name);
});
}

View File

@ -239,6 +239,10 @@ public:
void QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_required = true, bool ignore_sender = true, float distance = 0, bool group_only = true);
bool DoesAnyMemberHaveExpeditionLockout(const std::string& expedition_name, const std::string& event_name, int max_check_count = 0);
std::vector<RaidMember> GetMembers() const;
RaidMember members[MAX_RAID_MEMBERS];
char leadername[64];
protected:

View File

@ -293,8 +293,47 @@
#define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1.
#define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory.
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!
#define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end.
#define EXPEDITION_YOU_BELONG 3500 //You cannot create this expedition since you already belong to another.
#define EXPEDITION_YOU_PLAYED_HERE 3501 //You cannot create this expedition for another %1d:%2h:%3m since you have recently played here.
#define REQUIRED_PLAYER_COUNT 3503 //You do not meet the player count requirement. You have %1 players. You must have at least %2 and no more than %3.
#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.
#define EXPEDITION_NOT_MEMBER 3514 //%1 is not a member of this expedition.
#define EXPEDITION_REMOVED 3516 //%1 has been removed from %2.
#define DZSWAP_INVITE 3517 //Sending an invitation to: %1. They must accept in order to swap party members.
#define DZMAKELEADER_NOT_ONLINE 3518 //%1 is not currently online. You can only transfer leadership to an online member of the expedition you are in.
#define DZLIST_REPLAY_TIMER 3519 //You have %1d:%2h:%3m remaining until you may enter %4.
#define DZMAKELEADER_NAME 3520 //%1 has been made the leader for this expedition.
#define DZMAKELEADER_YOU 3521 //You have been made the leader of this expedition.
#define EXPEDITION_INVITE_ACCEPTED 3522 //%1 has accepted your offer to join your expedition.
#define EXPEDITION_MEMBER_ADDED 3523 //%1 has been added to %2.
#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
#define EXPEDITION_MEMBERS 3553 //Expedition Members: %1
#define EXPEDITION_EVENT_TIMER 3561 //%1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3D:%4H:%5M until they can experience it again. They may be added to the expedition later, once %2 has been completed.
#define LOOT_NOT_ALLOWED 3562 //You are not allowed to loot the item: %1.
#define DZ_UNABLE_RETRIEVE_LEADER 3583 //Unable to retrieve dynamic zone leader to check permissions.
#define DZADD_NOT_ALLOWING 3585 //The expedition is not allowing players to be added.
#define DZADD_NOT_ONLINE 3586 //%1 is not currently online. A player needs to be online to be added to a Dynamic Zone
#define DZADD_EXCEED_MAX 3587 //You can not add another player since you currently have the maximum number of players allowed (%1) in this zone.
#define DZADD_ALREADY_PART 3588 //You can not add %1 since they are already part of this zone.
#define DZADD_LEAVE_ZONE_FIRST 3589 //You can not add %1 since they first need to leave the zone before being allowed back in.
#define DZADD_ALREADY_ASSIGNED 3590 //%1 can not be added to this dynamic zone since they are already assigned to another dynamic zone.
#define DZADD_REPLAY_TIMER 3591 //%1 can not be added to this dynamic zone for another %2D:%3H:%4M since they have recently played this zone.
#define DZADD_EVENT_TIMER 3592 //%1 can not be added to this dynamic zone since they have recently experienced %2. They must wait for another %3D:%4H:%5M, or until event %2 has occurred.
#define DZADD_PENDING 3593 //%1 currently has an outstanding invitation to join this Dynamic Zone.
#define DZADD_PENDING_OTHER 3594 //%1 currently has an outstanding invitation to join another Dynamic Zone. Players may only have one invitation outstanding.
#define DZSWAP_CANNOT_REMOVE 3595 //%1 can not be removed from this dynamic zone since they are not assigned to it.
#define NOT_YOUR_TRAP 3671 //You cannot remove this, you are only allowed to remove traps you have set.
#define NO_CAST_ON_PET 4045 //You cannot cast this spell on your pet.
#define REWIND_WAIT 4059 //You must wait a bit longer before using the rewind command again.

View File

@ -42,6 +42,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "client.h"
#include "corpse.h"
#include "entity.h"
#include "expedition.h"
#include "quest_parser_collection.h"
#include "guild_mgr.h"
#include "mob.h"
@ -2846,7 +2847,6 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
break;
}
case ServerOP_ChangeSharedMem:
{
std::string hotfix_name = std::string((char*)pack->pBuffer);
@ -2881,6 +2881,45 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
}
break;
}
case ServerOP_CZClientMessageString:
{
auto buf = reinterpret_cast<CZClientMessageString_Struct*>(pack->pBuffer);
Client* client = entity_list.GetClientByName(buf->character_name);
if (client) {
client->MessageString(buf);
}
break;
}
case ServerOP_ExpeditionCreate:
case ServerOP_ExpeditionDeleted:
case ServerOP_ExpeditionLeaderChanged:
case ServerOP_ExpeditionLockout:
case ServerOP_ExpeditionLockoutDuration:
case ServerOP_ExpeditionLockState:
case ServerOP_ExpeditionMemberChange:
case ServerOP_ExpeditionMemberSwap:
case ServerOP_ExpeditionMemberStatus:
case ServerOP_ExpeditionMembersRemoved:
case ServerOP_ExpeditionReplayOnJoin:
case ServerOP_ExpeditionGetOnlineMembers:
case ServerOP_ExpeditionDzAddPlayer:
case ServerOP_ExpeditionDzMakeLeader:
case ServerOP_ExpeditionDzCompass:
case ServerOP_ExpeditionDzSafeReturn:
case ServerOP_ExpeditionDzZoneIn:
case ServerOP_ExpeditionDzDuration:
case ServerOP_ExpeditionCharacterLockout:
case ServerOP_ExpeditionExpireWarning:
{
Expedition::HandleWorldMessage(pack);
break;
}
case ServerOP_DzCharacterChange:
case ServerOP_DzRemoveAllCharacters:
{
DynamicZone::HandleWorldMessage(pack);
break;
}
default: {
std::cout << " Unknown ZSopcode:" << (int)pack->opcode;
std::cout << " size:" << pack->size << std::endl;
@ -3265,4 +3304,4 @@ void WorldServer::OnKeepAlive(EQ::Timer *t)
{
ServerPacket pack(ServerOP_KeepAlive, 0);
SendPacket(&pack);
}
}

View File

@ -37,6 +37,7 @@
#include "../common/string_util.h"
#include "../common/eqemu_logsys.h"
#include "expedition.h"
#include "guild_mgr.h"
#include "map.h"
#include "npc.h"
@ -1183,6 +1184,9 @@ bool Zone::Init(bool iStaticZone) {
petition_list.ClearPetitions();
petition_list.ReadDatabase();
LogInfo("Loading active Expeditions");
Expedition::CacheAllFromDatabase();
LogInfo("Loading timezone data");
zone->zone_time.setEQTimeZone(content_db.GetZoneTZ(zoneid, GetInstanceVersion()));
@ -1487,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
auto expedition = Expedition::FindCachedExpeditionByZoneInstance(GetZoneID(), GetInstanceID());
if (expedition)
{
expedition->RemoveAllMembers(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
}
@ -1497,20 +1508,29 @@ bool Zone::Process() {
if(Instance_Warning_timer == nullptr)
{
uint32 rem_time = Instance_Timer->GetRemainingTime();
uint32_t minutes_warning = 0;
if(rem_time < 60000 && rem_time > 55000)
{
entity_list.ExpeditionWarning(1);
Instance_Warning_timer = new Timer(10000);
minutes_warning = 1;
}
else if(rem_time < 300000 && rem_time > 295000)
{
entity_list.ExpeditionWarning(5);
Instance_Warning_timer = new Timer(10000);
minutes_warning = 5;
}
else if(rem_time < 900000 && rem_time > 895000)
{
entity_list.ExpeditionWarning(15);
Instance_Warning_timer = new Timer(10000);
minutes_warning = 15;
}
if (minutes_warning > 0)
{
// expedition expire warnings are handled by world
auto expedition = Expedition::FindCachedExpeditionByZoneInstance(GetZoneID(), GetInstanceID());
if (!expedition)
{
entity_list.ExpeditionWarning(minutes_warning);
Instance_Warning_timer = new Timer(10000);
}
}
}
else if(Instance_Warning_timer->Check())
@ -2699,3 +2719,25 @@ void Zone::SetInstanceTimeRemaining(uint32 instance_time_remaining)
Zone::instance_time_remaining = instance_time_remaining;
}
bool Zone::IsZone(uint32 zone_id, uint16 instance_id) const
{
return (zoneid == zone_id && instanceid == instance_id);
}
DynamicZone Zone::GetDynamicZone()
{
if (GetInstanceID() == 0)
{
return {}; // invalid
}
auto expedition = Expedition::FindCachedExpeditionByZoneInstance(GetZoneID(), GetInstanceID());
if (expedition)
{
return expedition->GetDynamicZone();
}
// todo: tasks, missions, and quests with an associated dz for this instance id
return {}; // invalid
}

View File

@ -33,6 +33,7 @@
#include "spawn2.h"
#include "spawngroup.h"
#include "aa_ability.h"
#include "dynamiczone.h"
#include "pathfinder_interface.h"
#include "global_loot_manager.h"
@ -81,6 +82,7 @@ struct item_tick_struct {
};
class Client;
class Expedition;
class Map;
class Mob;
class WaterMap;
@ -129,6 +131,7 @@ public:
bool IsPVPZone() { return pvpzone; }
bool IsSpellBlocked(uint32 spell_id, const glm::vec3 &location);
bool IsUCSServerAvailable() { return m_ucss_available; }
bool IsZone(uint32 zone_id, uint16 instance_id) const;
bool LoadGroundSpawns();
bool LoadZoneCFG(const char *filename, uint16 instance_id);
bool LoadZoneObjects();
@ -175,6 +178,7 @@ public:
void DumpMerchantList(uint32 npcid);
int SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold = false);
int32 MobsAggroCount() { return aggroedmobs; }
DynamicZone GetDynamicZone();
IPathfinder *pathing;
LinkedList<NPC_Emote_Struct *> NPCEmoteList;
@ -217,6 +221,8 @@ public:
std::vector<GridRepository::Grid> zone_grids;
std::vector<GridEntriesRepository::GridEntry> zone_grid_entries;
std::unordered_map<uint32, std::unique_ptr<Expedition>> expedition_cache;
time_t weather_timer;
Timer spawn2_timer;
Timer hot_reload_timer;
@ -402,4 +408,3 @@ private:
};
#endif

View File

@ -21,6 +21,7 @@
#include "../common/rulesys.h"
#include "../common/string_util.h"
#include "expedition.h"
#include "queryserv.h"
#include "quest_parser_collection.h"
#include "string_ids.h"
@ -404,6 +405,16 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc
if(this->GetPet())
entity_list.RemoveFromHateLists(this->GetPet());
if (GetPendingExpeditionInviteID() != 0)
{
// live re-invites if client zoned with a pending invite, save pending invite info in world
auto expedition = Expedition::FindCachedExpeditionByID(GetPendingExpeditionInviteID());
if (expedition)
{
expedition->SendWorldPendingInvite(m_pending_expedition_invite, GetName());
}
}
LogInfo("Zoning [{}] to: [{}] ([{}]) - ([{}]) x [{}] y [{}] z [{}]", m_pp.name, ZoneName(zone_id), zone_id, instance_id, dest_x, dest_y, dest_z);
//set the player's coordinates in the new zone so they have them