mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 12:41:30 +00:00
Fix issues with OP_SpecialMesg handling
This should prevent any optimizations being done on the "1 char string" This also fully documents the packet and expands the uses of quest::say/QuestSay
This commit is contained in:
parent
16d6014a87
commit
9fe17f4d46
@ -75,6 +75,7 @@ public:
|
||||
uint32 ReadUInt32() { uint32 value = *(uint32 *)(pBuffer + _rpos); _rpos += sizeof(uint32); return value; }
|
||||
uint32 ReadUInt32(uint32 Offset) const { uint32 value = *(uint32 *)(pBuffer + Offset); return value; }
|
||||
void ReadString(char *str) { uint32 len = static_cast<uint32>(strlen((char *)(pBuffer + _rpos))) + 1; memcpy(str, pBuffer + _rpos, len); _rpos += len; }
|
||||
void ReadString(std::string &str) { str = reinterpret_cast<char *>(pBuffer + _rpos); _rpos += str.length() + 1; }
|
||||
void ReadString(char *str, uint32 Offset, uint32 MaxLength) const;
|
||||
|
||||
uint32 GetWritePosition() { return _wpos; }
|
||||
|
||||
@ -87,6 +87,7 @@ typedef enum {
|
||||
_eaMaxAppearance
|
||||
} EmuAppearance;
|
||||
|
||||
#define MT_NPCQuestSay 10
|
||||
// msg_type's for custom usercolors
|
||||
#define MT_Say 256
|
||||
#define MT_Tell 257
|
||||
|
||||
@ -1188,6 +1188,20 @@ struct SpecialMesg_Struct
|
||||
/*24*/ char message[1]; // What is being said?
|
||||
};
|
||||
|
||||
struct SpecialMesgHeader_Struct
|
||||
{
|
||||
/*00*/ char SpeakMode; // 2 shouts, 4 %1 %2, 3 %2, 5 tells group, 0 copy, default says
|
||||
/*01*/ char JournalMode; // 1 and 2 go to journal
|
||||
/*02*/ char language;
|
||||
/*03*/ uint32 msg_type; // Color of text (see MT_*** below)
|
||||
/*07*/ uint32 target_spawn_id; // Who is it being said to?
|
||||
/*11*/ // speaker's name
|
||||
/*xx*/ // unknown, location, client doesn't care
|
||||
/*xx*/ // unknown
|
||||
/*xx*/ // unknown
|
||||
/*xx*/ // message
|
||||
};
|
||||
|
||||
/*
|
||||
** When somebody changes what they're wearing or give a pet a weapon (model changes)
|
||||
** Length: 19 Bytes
|
||||
|
||||
@ -3199,43 +3199,35 @@ namespace RoF
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer;
|
||||
SerializeBuffer buf(in->size);
|
||||
buf.WriteInt8(in->ReadUInt8()); // speak mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // journal mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // language
|
||||
buf.WriteInt32(in->ReadUInt32()); // message type
|
||||
buf.WriteInt32(in->ReadUInt32()); // target spawn id
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
// break strlen optimizations!
|
||||
char *message = emu->sayer;
|
||||
auto sayer_length = std::char_traits<char>::length(message);
|
||||
message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats
|
||||
std::string name;
|
||||
in->ReadString(name); // NPC names max out at 63 chars
|
||||
|
||||
std::string old_message = message;
|
||||
buf.WriteString(name);
|
||||
|
||||
buf.WriteInt32(in->ReadUInt32()); // loc
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
|
||||
std::string old_message;
|
||||
std::string new_message;
|
||||
|
||||
in->ReadString(old_message);
|
||||
|
||||
ServerToRoFSayLink(new_message, old_message);
|
||||
|
||||
//in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1;
|
||||
in->size = sayer_length + new_message.length() + 25;
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
buf.WriteString(new_message);
|
||||
|
||||
char *OutBuffer = (char *)in->pBuffer;
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer);
|
||||
|
||||
// TODO: figure this shit out
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
}
|
||||
|
||||
ENCODE(OP_Stun)
|
||||
|
||||
@ -3266,43 +3266,35 @@ namespace RoF2
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer;
|
||||
SerializeBuffer buf(in->size);
|
||||
buf.WriteInt8(in->ReadUInt8()); // speak mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // journal mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // language
|
||||
buf.WriteInt32(in->ReadUInt32()); // message type
|
||||
buf.WriteInt32(in->ReadUInt32()); // target spawn id
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
// break strlen optimizations!
|
||||
char *message = emu->sayer;
|
||||
auto sayer_length = std::char_traits<char>::length(message);
|
||||
message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats
|
||||
std::string name;
|
||||
in->ReadString(name); // NPC names max out at 63 chars
|
||||
|
||||
std::string old_message = message;
|
||||
buf.WriteString(name);
|
||||
|
||||
buf.WriteInt32(in->ReadUInt32()); // loc
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
|
||||
std::string old_message;
|
||||
std::string new_message;
|
||||
|
||||
in->ReadString(old_message);
|
||||
|
||||
ServerToRoF2SayLink(new_message, old_message);
|
||||
|
||||
//in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1;
|
||||
in->size = sayer_length + new_message.length() + 25;
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
buf.WriteString(new_message);
|
||||
|
||||
char *OutBuffer = (char *)in->pBuffer;
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer);
|
||||
|
||||
// TODO: figure this shit out
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
}
|
||||
|
||||
ENCODE(OP_Stun)
|
||||
|
||||
@ -2069,43 +2069,35 @@ namespace SoD
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer;
|
||||
SerializeBuffer buf(in->size);
|
||||
buf.WriteInt8(in->ReadUInt8()); // speak mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // journal mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // language
|
||||
buf.WriteInt32(in->ReadUInt32()); // message type
|
||||
buf.WriteInt32(in->ReadUInt32()); // target spawn id
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
// break strlen optimizations!
|
||||
char *message = emu->sayer;
|
||||
auto sayer_length = std::char_traits<char>::length(message);
|
||||
message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats
|
||||
std::string name;
|
||||
in->ReadString(name); // NPC names max out at 63 chars
|
||||
|
||||
std::string old_message = message;
|
||||
buf.WriteString(name);
|
||||
|
||||
buf.WriteInt32(in->ReadUInt32()); // loc
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
|
||||
std::string old_message;
|
||||
std::string new_message;
|
||||
|
||||
in->ReadString(old_message);
|
||||
|
||||
ServerToSoDSayLink(new_message, old_message);
|
||||
|
||||
//in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1;
|
||||
in->size = sayer_length + new_message.length() + 25;
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
buf.WriteString(new_message);
|
||||
|
||||
char *OutBuffer = (char *)in->pBuffer;
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer);
|
||||
|
||||
// TODO: figure this shit out
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
}
|
||||
|
||||
ENCODE(OP_Stun)
|
||||
|
||||
@ -1720,43 +1720,35 @@ namespace SoF
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer;
|
||||
SerializeBuffer buf(in->size);
|
||||
buf.WriteInt8(in->ReadUInt8()); // speak mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // journal mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // language
|
||||
buf.WriteInt32(in->ReadUInt32()); // message type
|
||||
buf.WriteInt32(in->ReadUInt32()); // target spawn id
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
// break strlen optimizations!
|
||||
char *message = emu->sayer;
|
||||
auto sayer_length = std::char_traits<char>::length(message);
|
||||
message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats
|
||||
std::string name;
|
||||
in->ReadString(name);
|
||||
|
||||
std::string old_message = message;
|
||||
buf.WriteString(name);
|
||||
|
||||
buf.WriteInt32(in->ReadUInt32()); // loc
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
|
||||
std::string old_message;
|
||||
std::string new_message;
|
||||
|
||||
in->ReadString(old_message);
|
||||
|
||||
ServerToSoFSayLink(new_message, old_message);
|
||||
|
||||
//in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1;
|
||||
in->size = sayer_length + new_message.length() + 25;
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
buf.WriteString(new_message);
|
||||
|
||||
char *OutBuffer = (char *)in->pBuffer;
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer);
|
||||
|
||||
// TODO: figure this shit out
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
}
|
||||
|
||||
ENCODE(OP_Stun)
|
||||
|
||||
@ -1420,43 +1420,35 @@ namespace Titanium
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer;
|
||||
SerializeBuffer buf(in->size);
|
||||
buf.WriteInt8(in->ReadUInt8()); // speak mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // journal mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // language
|
||||
buf.WriteInt32(in->ReadUInt32()); // message type
|
||||
buf.WriteInt32(in->ReadUInt32()); // target spawn id
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
// break strlen optimizations!
|
||||
char *message = emu->sayer;
|
||||
auto sayer_length = std::char_traits<char>::length(message);
|
||||
message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats
|
||||
std::string name;
|
||||
in->ReadString(name); // NPC names max out at 63 chars
|
||||
|
||||
std::string old_message = message;
|
||||
buf.WriteString(name);
|
||||
|
||||
buf.WriteInt32(in->ReadUInt32()); // loc
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
|
||||
std::string old_message;
|
||||
std::string new_message;
|
||||
|
||||
in->ReadString(old_message);
|
||||
|
||||
ServerToTitaniumSayLink(new_message, old_message);
|
||||
|
||||
//in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1;
|
||||
in->size = sayer_length + new_message.length() + 25;
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
buf.WriteString(new_message);
|
||||
|
||||
char *OutBuffer = (char *)in->pBuffer;
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer);
|
||||
|
||||
// TODO: figure this shit out
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
}
|
||||
|
||||
ENCODE(OP_TaskDescription)
|
||||
|
||||
@ -2369,43 +2369,35 @@ namespace UF
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer;
|
||||
SerializeBuffer buf(in->size);
|
||||
buf.WriteInt8(in->ReadUInt8()); // speak mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // journal mode
|
||||
buf.WriteInt8(in->ReadUInt8()); // language
|
||||
buf.WriteInt32(in->ReadUInt32()); // message type
|
||||
buf.WriteInt32(in->ReadUInt32()); // target spawn id
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
// break strlen optimizations!
|
||||
char *message = emu->sayer;
|
||||
auto sayer_length = std::char_traits<char>::length(message);
|
||||
message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats
|
||||
std::string name;
|
||||
in->ReadString(name); // NPC names max out at 63 chars
|
||||
|
||||
std::string old_message = message;
|
||||
buf.WriteString(name);
|
||||
|
||||
buf.WriteInt32(in->ReadUInt32()); // loc
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
buf.WriteInt32(in->ReadUInt32());
|
||||
|
||||
std::string old_message;
|
||||
std::string new_message;
|
||||
|
||||
in->ReadString(old_message);
|
||||
|
||||
ServerToUFSayLink(new_message, old_message);
|
||||
|
||||
//in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1;
|
||||
in->size = sayer_length + new_message.length() + 25;
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
buf.WriteString(new_message);
|
||||
|
||||
char *OutBuffer = (char *)in->pBuffer;
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer);
|
||||
|
||||
// TODO: figure this shit out
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f);
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
}
|
||||
|
||||
ENCODE(OP_Stun)
|
||||
|
||||
@ -1277,21 +1277,19 @@ void Client::Message(uint32 type, const char* message, ...) {
|
||||
vsnprintf(buffer, 4096, message, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
size_t len = strlen(buffer);
|
||||
SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64);
|
||||
buf.WriteInt8(static_cast<int8>(Journal::SpeakMode::Raw));
|
||||
buf.WriteInt8(static_cast<int8>(Journal::Mode::None));
|
||||
buf.WriteInt8(0); // language
|
||||
buf.WriteUInt32(type);
|
||||
buf.WriteUInt32(0); // target spawn ID used for journal filtering, ignored here
|
||||
buf.WriteString(""); // send name, not applicable here
|
||||
buf.WriteInt32(0); // location, client seems to ignore
|
||||
buf.WriteInt32(0);
|
||||
buf.WriteInt32(0);
|
||||
buf.WriteString(buffer);
|
||||
|
||||
//client dosent like our packet all the time unless
|
||||
//we make it really big, then it seems to not care that
|
||||
//our header is malformed.
|
||||
//len = 4096 - sizeof(SpecialMesg_Struct);
|
||||
|
||||
uint32 len_packet = sizeof(SpecialMesg_Struct)+len;
|
||||
auto app = new EQApplicationPacket(OP_SpecialMesg, len_packet);
|
||||
SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer;
|
||||
sm->header[0] = 0x00; // Header used for #emote style messages..
|
||||
sm->header[1] = 0x00; // Play around with these to see other types
|
||||
sm->header[2] = 0x00;
|
||||
sm->msg_type = type;
|
||||
memcpy(sm->message, buffer, len+1);
|
||||
auto app = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
FastQueuePacket(&app);
|
||||
|
||||
@ -1308,67 +1306,25 @@ void Client::FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, cons
|
||||
vsnprintf(buffer, 4096, message, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
size_t len = strlen(buffer);
|
||||
SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64);
|
||||
buf.WriteInt8(static_cast<int8>(Journal::SpeakMode::Raw));
|
||||
buf.WriteInt8(static_cast<int8>(Journal::Mode::None));
|
||||
buf.WriteInt8(0); // language
|
||||
buf.WriteUInt32(type);
|
||||
buf.WriteUInt32(0); // target spawn ID used for journal filtering, ignored here
|
||||
buf.WriteString(""); // send name, not applicable here
|
||||
buf.WriteInt32(0); // location, client seems to ignore
|
||||
buf.WriteInt32(0);
|
||||
buf.WriteInt32(0);
|
||||
buf.WriteString(buffer);
|
||||
|
||||
//client dosent like our packet all the time unless
|
||||
//we make it really big, then it seems to not care that
|
||||
//our header is malformed.
|
||||
//len = 4096 - sizeof(SpecialMesg_Struct);
|
||||
|
||||
uint32 len_packet = sizeof(SpecialMesg_Struct) + len;
|
||||
auto app = new EQApplicationPacket(OP_SpecialMesg, len_packet);
|
||||
SpecialMesg_Struct* sm = (SpecialMesg_Struct*)app->pBuffer;
|
||||
sm->header[0] = 0x00; // Header used for #emote style messages..
|
||||
sm->header[1] = 0x00; // Play around with these to see other types
|
||||
sm->header[2] = 0x00;
|
||||
sm->msg_type = type;
|
||||
memcpy(sm->message, buffer, len + 1);
|
||||
auto app = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
FastQueuePacket(&app);
|
||||
|
||||
safe_delete_array(buffer);
|
||||
}
|
||||
|
||||
void Client::QuestJournalledMessage(const char *npcname, const char* message) {
|
||||
|
||||
// npcnames longer than 60 characters crash the client when they log back in
|
||||
const int MaxNPCNameLength = 60;
|
||||
// I assume there is an upper safe limit on the message length. Don't know what it is, but 4000 doesn't crash
|
||||
// the client.
|
||||
const int MaxMessageLength = 4000;
|
||||
|
||||
char OutNPCName[MaxNPCNameLength+1];
|
||||
char OutMessage[MaxMessageLength+1];
|
||||
|
||||
// Apparently Visual C++ snprintf is not C99 compliant and doesn't put the null terminator
|
||||
// in if the formatted string >= the maximum length, so we put it in.
|
||||
//
|
||||
snprintf(OutNPCName, MaxNPCNameLength, "%s", npcname); OutNPCName[MaxNPCNameLength]='\0';
|
||||
snprintf(OutMessage, MaxMessageLength, "%s", message); OutMessage[MaxMessageLength]='\0';
|
||||
|
||||
uint32 len_packet = sizeof(SpecialMesg_Struct) + strlen(OutNPCName) + strlen(OutMessage);
|
||||
auto app = new EQApplicationPacket(OP_SpecialMesg, len_packet);
|
||||
SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer;
|
||||
|
||||
sm->header[0] = 0;
|
||||
sm->header[1] = 2;
|
||||
sm->header[2] = 0;
|
||||
sm->msg_type = 0x0a;
|
||||
sm->target_spawn_id = GetID();
|
||||
|
||||
char *dest = &sm->sayer[0];
|
||||
|
||||
memcpy(dest, OutNPCName, strlen(OutNPCName) + 1);
|
||||
|
||||
dest = dest + strlen(OutNPCName) + 13;
|
||||
|
||||
memcpy(dest, OutMessage, strlen(OutMessage) + 1);
|
||||
|
||||
QueuePacket(app);
|
||||
|
||||
safe_delete(app);
|
||||
}
|
||||
|
||||
void Client::SetMaxHP() {
|
||||
if(dead)
|
||||
return;
|
||||
|
||||
@ -341,7 +341,6 @@ public:
|
||||
void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...);
|
||||
void Message(uint32 type, const char* message, ...);
|
||||
void FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...);
|
||||
void QuestJournalledMessage(const char *npcname, const char* message);
|
||||
void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber);
|
||||
void SendSound();
|
||||
void LearnRecipe(uint32 recipeID);
|
||||
|
||||
@ -272,6 +272,31 @@ enum class LootRequestType : uint8 {
|
||||
AllowedPVPDefined,
|
||||
};
|
||||
|
||||
namespace Journal {
|
||||
enum class SpeakMode : uint8 {
|
||||
Raw = 0, // this just uses the raw message
|
||||
Say = 1, // prints with "%1 says,%2 '%3'" if in another language else "%1 says '%2'"
|
||||
Shout = 2, // prints with "%1 shouts,%2 '%3'" if in another language else "%1 shouts '%2'"
|
||||
EmoteAlt = 3, // prints "%2", this should just be the same as raw ...
|
||||
Emote = 4, // prints "%1 %2" if message doesn't start with "\" or "@", else "%1%2"
|
||||
Group = 5 // prints "%1 tells the group,%2 '%3'"
|
||||
};
|
||||
|
||||
enum class Mode : uint8 {
|
||||
None = 0,
|
||||
Log1 = 1, // 1 and 2 log to journal
|
||||
Log2 = 2, // our current code uses 2
|
||||
};
|
||||
|
||||
struct Options {
|
||||
SpeakMode speak_mode;
|
||||
Mode journal_mode;
|
||||
int8 language;
|
||||
uint32 message_type;
|
||||
uint32 target_spawn_id; // who the message is talking to (limits journaling)
|
||||
};
|
||||
};
|
||||
|
||||
//this is our internal representation of the BUFF struct, can put whatever we want in it
|
||||
struct Buffs_Struct {
|
||||
uint16 spellid;
|
||||
|
||||
@ -155,12 +155,31 @@ XS(XS__say); // prototype to pass -Wmissing-prototypes
|
||||
XS(XS__say) {
|
||||
dXSARGS;
|
||||
|
||||
if (items == 1)
|
||||
quest_manager.say(SvPV_nolen(ST(0)));
|
||||
else if (items == 2)
|
||||
quest_manager.say(SvPV_nolen(ST(0)), (int) SvIV(ST(1)));
|
||||
else
|
||||
Perl_croak(aTHX_ "Usage: quest::say(string message, int language_id])");
|
||||
Journal::Options opts;
|
||||
// we currently default to these
|
||||
opts.speak_mode = Journal::SpeakMode::Say;
|
||||
opts.journal_mode = Journal::Mode::Log2;
|
||||
opts.language = 0;
|
||||
opts.message_type = MT_NPCQuestSay;
|
||||
if (items == 0 || items > 5) {
|
||||
Perl_croak(aTHX_ "Usage: quest::say(string message, [int language_id], [int message_type], [int speak_mode], [int journal_mode])");
|
||||
} else if (items == 2) {
|
||||
opts.language = (int)SvIV(ST(1));
|
||||
} else if (items == 3) {
|
||||
opts.language = (int)SvIV(ST(1));
|
||||
opts.message_type = (int)SvIV(ST(2));
|
||||
} else if (items == 4) {
|
||||
opts.language = (int)SvIV(ST(1));
|
||||
opts.message_type = (int)SvIV(ST(2));
|
||||
opts.speak_mode = (Journal::SpeakMode)SvIV(ST(3));
|
||||
} else if (items == 5) {
|
||||
opts.language = (int)SvIV(ST(1));
|
||||
opts.message_type = (int)SvIV(ST(2));
|
||||
opts.speak_mode = (Journal::SpeakMode)SvIV(ST(3));
|
||||
opts.journal_mode = (Journal::Mode)SvIV(ST(4));
|
||||
}
|
||||
|
||||
quest_manager.say(SvPV_nolen(ST(0)), opts);
|
||||
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
@ -3733,24 +3733,26 @@ bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z,
|
||||
}
|
||||
|
||||
void EntityList::QuestJournalledSayClose(Mob *sender, Client *QuestInitiator,
|
||||
float dist, const char* mobname, const char* message)
|
||||
float dist, const char* mobname, const char* message, Journal::Options &opts)
|
||||
{
|
||||
Client *c = nullptr;
|
||||
float dist2 = dist * dist;
|
||||
SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64);
|
||||
|
||||
// Send the message to the quest initiator such that the client will enter it into the NPC Quest Journal
|
||||
if (QuestInitiator) {
|
||||
auto buf = new char[strlen(mobname) + strlen(message) + 10];
|
||||
sprintf(buf, "%s says, '%s'", mobname, message);
|
||||
QuestInitiator->QuestJournalledMessage(mobname, buf);
|
||||
safe_delete_array(buf);
|
||||
}
|
||||
// Use the old method for all other nearby clients
|
||||
for (auto it = client_list.begin(); it != client_list.end(); ++it) {
|
||||
c = it->second;
|
||||
if(c && (c != QuestInitiator) && DistanceSquared(c->GetPosition(), sender->GetPosition()) <= dist2)
|
||||
c->Message_StringID(10, GENERIC_SAY, mobname, message);
|
||||
}
|
||||
buf.WriteInt8(static_cast<int8>(opts.speak_mode));
|
||||
buf.WriteInt8(static_cast<int8>(opts.journal_mode));
|
||||
buf.WriteInt8(opts.language);
|
||||
buf.WriteInt32(opts.message_type);
|
||||
buf.WriteInt32(opts.target_spawn_id);
|
||||
buf.WriteString(mobname);
|
||||
buf.WriteInt32(0); // location, client doesn't seem to do anything with this
|
||||
buf.WriteInt32(0);
|
||||
buf.WriteInt32(0);
|
||||
buf.WriteString(message);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
|
||||
// client only bothers logging if target spawn ID matches, safe to send to everyone
|
||||
QueueCloseClients(sender, outapp, false, dist);
|
||||
delete outapp;
|
||||
}
|
||||
|
||||
Corpse *EntityList::GetClosestCorpse(Mob *sender, const char *Name)
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
|
||||
#include "position.h"
|
||||
#include "zonedump.h"
|
||||
#include "common.h"
|
||||
|
||||
class Encounter;
|
||||
class Beacon;
|
||||
@ -337,7 +338,7 @@ public:
|
||||
void SendNimbusEffects(Client *c);
|
||||
void SendUntargetable(Client *c);
|
||||
void DuelMessage(Mob* winner, Mob* loser, bool flee);
|
||||
void QuestJournalledSayClose(Mob *sender, Client *QuestIntiator, float dist, const char* mobname, const char* message);
|
||||
void QuestJournalledSayClose(Mob *sender, Client *QuestIntiator, float dist, const char* mobname, const char* message, Journal::Options &opts);
|
||||
void GroupMessage(uint32 gid, const char *from, const char *message);
|
||||
void ExpeditionWarning(uint32 minutes_left);
|
||||
|
||||
|
||||
@ -36,6 +36,8 @@ struct BodyTypes { };
|
||||
struct Filters { };
|
||||
struct MessageTypes { };
|
||||
struct Rule { };
|
||||
struct Journal_SpeakMode { };
|
||||
struct Journal_Mode { };
|
||||
|
||||
struct lua_registered_event {
|
||||
std::string encounter_name;
|
||||
@ -2232,6 +2234,7 @@ luabind::scope lua_register_message_types() {
|
||||
return luabind::class_<MessageTypes>("MT")
|
||||
.enum_("constants")
|
||||
[
|
||||
luabind::value("NPCQuestSay", MT_NPCQuestSay),
|
||||
luabind::value("Say", MT_Say),
|
||||
luabind::value("Tell", MT_Tell),
|
||||
luabind::value("Group", MT_Group),
|
||||
@ -2362,4 +2365,27 @@ luabind::scope lua_register_ruleb() {
|
||||
];
|
||||
}
|
||||
|
||||
luabind::scope lua_register_journal_speakmode() {
|
||||
return luabind::class_<Journal_SpeakMode>("Journal_SpeakMode")
|
||||
.enum_("constants")
|
||||
[
|
||||
luabind::value("Raw", static_cast<int>(Journal::SpeakMode::Raw)),
|
||||
luabind::value("Say", static_cast<int>(Journal::SpeakMode::Say)),
|
||||
luabind::value("Shout", static_cast<int>(Journal::SpeakMode::Shout)),
|
||||
luabind::value("EmoteAlt", static_cast<int>(Journal::SpeakMode::EmoteAlt)),
|
||||
luabind::value("Emote", static_cast<int>(Journal::SpeakMode::Emote)),
|
||||
luabind::value("Group", static_cast<int>(Journal::SpeakMode::Group))
|
||||
];
|
||||
}
|
||||
|
||||
luabind::scope lua_register_journal_mode() {
|
||||
return luabind::class_<Journal_Mode>("Journal_Mode")
|
||||
.enum_("constants")
|
||||
[
|
||||
luabind::value("None", static_cast<int>(Journal::Mode::None)),
|
||||
luabind::value("Log1", static_cast<int>(Journal::Mode::Log1)),
|
||||
luabind::value("Log2", static_cast<int>(Journal::Mode::Log2))
|
||||
];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -19,6 +19,8 @@ luabind::scope lua_register_rules_const();
|
||||
luabind::scope lua_register_rulei();
|
||||
luabind::scope lua_register_ruler();
|
||||
luabind::scope lua_register_ruleb();
|
||||
luabind::scope lua_register_journal_speakmode();
|
||||
luabind::scope lua_register_journal_mode();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -755,7 +755,65 @@ void Lua_Mob::Say(const char *message) {
|
||||
|
||||
void Lua_Mob::QuestSay(Lua_Client client, const char *message) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->QuestJournalledSay(client, message);
|
||||
Journal::Options journal_opts;
|
||||
journal_opts.speak_mode = Journal::SpeakMode::Say;
|
||||
journal_opts.journal_mode = RuleB(NPC, EnableNPCQuestJournal) ? Journal::Mode::Log2 : Journal::Mode::None;
|
||||
journal_opts.language = 0;
|
||||
journal_opts.message_type = MT_NPCQuestSay;
|
||||
journal_opts.target_spawn_id = 0;
|
||||
self->QuestJournalledSay(client, message, journal_opts);
|
||||
}
|
||||
|
||||
void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::object opts) {
|
||||
Lua_Safe_Call_Void();
|
||||
|
||||
Journal::Options journal_opts;
|
||||
// defaults
|
||||
journal_opts.speak_mode = Journal::SpeakMode::Say;
|
||||
journal_opts.journal_mode = Journal::Mode::Log2;
|
||||
journal_opts.language = 0;
|
||||
journal_opts.message_type = MT_NPCQuestSay;
|
||||
journal_opts.target_spawn_id = 0;
|
||||
|
||||
if (luabind::type(opts) == LUA_TTABLE) {
|
||||
auto cur = opts["speak_mode"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
journal_opts.speak_mode = static_cast<Journal::SpeakMode>(luabind::object_cast<int>(cur));
|
||||
} catch (luabind::cast_failed) {
|
||||
}
|
||||
}
|
||||
|
||||
cur = opts["journal_mode"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
journal_opts.journal_mode = static_cast<Journal::Mode>(luabind::object_cast<int>(cur));
|
||||
} catch (luabind::cast_failed) {
|
||||
}
|
||||
}
|
||||
|
||||
cur = opts["language"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
journal_opts.language = luabind::object_cast<int>(cur);
|
||||
} catch (luabind::cast_failed) {
|
||||
}
|
||||
}
|
||||
|
||||
cur = opts["message_type"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
journal_opts.message_type = luabind::object_cast<int>(cur);
|
||||
} catch (luabind::cast_failed) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if rule disables it, we override provided
|
||||
if (!RuleB(NPC, EnableNPCQuestJournal))
|
||||
journal_opts.journal_mode = Journal::Mode::None;
|
||||
|
||||
self->QuestJournalledSay(client, message, journal_opts);
|
||||
}
|
||||
|
||||
void Lua_Mob::Shout(const char *message) {
|
||||
@ -2320,7 +2378,8 @@ luabind::scope lua_register_mob() {
|
||||
.def("Message", &Lua_Mob::Message)
|
||||
.def("Message_StringID", &Lua_Mob::Message_StringID)
|
||||
.def("Say", &Lua_Mob::Say)
|
||||
.def("QuestSay", &Lua_Mob::QuestSay)
|
||||
.def("QuestSay", (void(Lua_Mob::*)(Lua_Client,const char *))&Lua_Mob::QuestSay)
|
||||
.def("QuestSay", (void(Lua_Mob::*)(Lua_Client,const char *,luabind::adl::object))&Lua_Mob::QuestSay)
|
||||
.def("Shout", &Lua_Mob::Shout)
|
||||
.def("Emote", &Lua_Mob::Emote)
|
||||
.def("InterruptSpell", (void(Lua_Mob::*)(void))&Lua_Mob::InterruptSpell)
|
||||
|
||||
@ -169,6 +169,7 @@ public:
|
||||
void Message_StringID(int type, int string_id, uint32 distance);
|
||||
void Say(const char *message);
|
||||
void QuestSay(Lua_Client client, const char *message);
|
||||
void QuestSay(Lua_Client client, const char *message, luabind::adl::object opts);
|
||||
void Shout(const char *message);
|
||||
void Emote(const char *message);
|
||||
void InterruptSpell();
|
||||
|
||||
@ -1102,7 +1102,9 @@ void LuaParser::MapFunctions(lua_State *L) {
|
||||
lua_register_rules_const(),
|
||||
lua_register_rulei(),
|
||||
lua_register_ruler(),
|
||||
lua_register_ruleb()
|
||||
lua_register_ruleb(),
|
||||
lua_register_journal_speakmode(),
|
||||
lua_register_journal_mode()
|
||||
];
|
||||
|
||||
} catch(std::exception &ex) {
|
||||
|
||||
@ -2911,9 +2911,13 @@ void Mob::Emote(const char *format, ...)
|
||||
GENERIC_EMOTE, GetCleanName(), buf);
|
||||
}
|
||||
|
||||
void Mob::QuestJournalledSay(Client *QuestInitiator, const char *str)
|
||||
void Mob::QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts)
|
||||
{
|
||||
entity_list.QuestJournalledSayClose(this, QuestInitiator, 200, GetCleanName(), str);
|
||||
// just in case
|
||||
if (opts.target_spawn_id == 0 && QuestInitiator)
|
||||
opts.target_spawn_id = QuestInitiator->GetID();
|
||||
|
||||
entity_list.QuestJournalledSayClose(this, QuestInitiator, 200, GetCleanName(), str, opts);
|
||||
}
|
||||
|
||||
const char *Mob::GetCleanName()
|
||||
|
||||
@ -744,7 +744,7 @@ public:
|
||||
const char *message6 = 0, const char *message7 = 0, const char *message8 = 0, const char *message9 = 0);
|
||||
void Shout(const char *format, ...);
|
||||
void Emote(const char *format, ...);
|
||||
void QuestJournalledSay(Client *QuestInitiator, const char *str);
|
||||
void QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts);
|
||||
int32 GetItemStat(uint32 itemid, const char *identifier);
|
||||
|
||||
int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false);
|
||||
|
||||
@ -154,30 +154,19 @@ void QuestManager::echo(int colour, const char *str) {
|
||||
entity_list.MessageClose(initiator, false, 200, colour, str);
|
||||
}
|
||||
|
||||
void QuestManager::say(const char *str) {
|
||||
void QuestManager::say(const char *str, Journal::Options &opts) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (!owner) {
|
||||
Log(Logs::General, Logs::Quests, "QuestManager::say called with nullptr owner. Probably syntax error in quest file.");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if(RuleB(NPC, EnableNPCQuestJournal) && initiator) {
|
||||
owner->QuestJournalledSay(initiator, str);
|
||||
if (!RuleB(NPC, EnableNPCQuestJournal))
|
||||
opts.journal_mode = Journal::Mode::None;
|
||||
if (initiator) {
|
||||
opts.target_spawn_id = initiator->GetID();
|
||||
owner->QuestJournalledSay(initiator, str, opts);
|
||||
}
|
||||
else {
|
||||
owner->Say(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::say(const char *str, uint8 language) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (!owner) {
|
||||
Log(Logs::General, Logs::Quests, "QuestManager::say called with nullptr owner. Probably syntax error in quest file.");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
entity_list.ChannelMessage(owner, 8, language, str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -62,8 +62,7 @@ public:
|
||||
|
||||
//quest functions
|
||||
void echo(int colour, const char *str);
|
||||
void say(const char *str);
|
||||
void say(const char *str, uint8 language);
|
||||
void say(const char *str, Journal::Options &opts);
|
||||
void me(const char *str);
|
||||
void summonitem(uint32 itemid, int16 charges = -1);
|
||||
void write(const char *file, const char *str);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user