mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-22 20:33:01 +00:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9b2bf0477 | |||
| 0b26c80d8f | |||
| 8497042eef | |||
| 1304b9c80f | |||
| 65c9c86556 | |||
| ece9251bd6 | |||
| 3cbd0fe0f6 | |||
| 8141f831b1 | |||
| b3a3d9bec5 | |||
| fa2b5166fb | |||
| 8dc25c838b | |||
| 229de34afc | |||
| 70bdd35b69 | |||
| e99354223d | |||
| 8a8e922f46 | |||
| c1484a698c | |||
| bd96d676be | |||
| 2a637a031b | |||
| f7c7f5646e | |||
| 577e67f4ee | |||
| 1084b71d8d | |||
| c2ab2a232b | |||
| c5f739cbda | |||
| 385732f403 | |||
| b972ec581f | |||
| 32e04cd264 | |||
| b0dff0c006 | |||
| c093b3e2ab | |||
| c5bf71f221 | |||
| 977c3ca3dc | |||
| 408ce4650f | |||
| bc0f705227 | |||
| 5c1ab3b24c | |||
| 85a858fcd6 | |||
| 6ab2e46f42 | |||
| 96acb1e638 | |||
| eb98eef1b9 | |||
| e1bb3301a5 | |||
| 358ce2ca94 | |||
| 6856d1540b | |||
| cd1306d52c | |||
| 20aa1cbe77 | |||
| dfd9a3c714 | |||
| c04bf79b0f | |||
| 7286e6a37f | |||
| a244cec9e8 | |||
| 7062e2703b | |||
| 5f53856fd4 | |||
| 17d63bc3f6 | |||
| 47fda0f747 | |||
| e2c15dbc9e | |||
| c089296538 | |||
| 94f09e5287 | |||
| 93133c289e | |||
| 8934235030 | |||
| ea0a54ed60 | |||
| 39544b4723 |
+12
-2
@@ -292,16 +292,17 @@ ADD_DEFINITIONS(-DGLM_ENABLE_EXPERIMENTAL)
|
||||
#Find everything we need
|
||||
FIND_PACKAGE(ZLIB REQUIRED)
|
||||
FIND_PACKAGE(MySQL REQUIRED)
|
||||
FIND_PACKAGE(Protobuf REQUIRED)
|
||||
IF(EQEMU_BUILD_PERL)
|
||||
FIND_PACKAGE(PerlLibs REQUIRED)
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${PERL_INCLUDE_PATH}")
|
||||
ENDIF(EQEMU_BUILD_PERL)
|
||||
|
||||
SET(SERVER_LIBS common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY} libuv fmt recast_navigation)
|
||||
SET(SERVER_LIBS common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY} protobuf::libprotoc protobuf::libprotobuf libuv fmt recast_navigation)
|
||||
|
||||
FIND_PACKAGE(Sodium REQUIRED)
|
||||
IF(SODIUM_FOUND)
|
||||
OPTION(EQEMU_ENABLE_SECURITY "Use Encryption For TCP Connections" ON)
|
||||
OPTION(EQEMU_ENABLE_SECURITY "Use Secure Loginserver Hashes" ON)
|
||||
IF(EQEMU_ENABLE_SECURITY)
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${SODIUM_INCLUDE_DIRS}")
|
||||
ADD_DEFINITIONS(-DENABLE_SECURITY)
|
||||
@@ -340,6 +341,7 @@ ENDIF(EQEMU_BUILD_LUA)
|
||||
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${ZLIB_INCLUDE_DIRS}")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${MySQL_INCLUDE_DIR}")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${Protobuf_INCLUDE_DIRS}")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/common/glm")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/cereal")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/libuv/include" )
|
||||
@@ -347,6 +349,13 @@ INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/libuv/src")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/format")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/recast/detour/include")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/recast/recast/include")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_BINARY_DIR}/common")
|
||||
|
||||
IF(VCPKG_TARGET_TRIPLET)
|
||||
MESSAGE(STATUS "Vcpkg Detected")
|
||||
MESSAGE(STATUS "Setting protobuf import dir: ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include")
|
||||
SET(Protobuf_IMPORT_DIRS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include")
|
||||
ENDIF()
|
||||
|
||||
IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS OR EQEMU_BUILD_HC)
|
||||
ADD_SUBDIRECTORY(common)
|
||||
@@ -359,6 +368,7 @@ IF(EQEMU_BUILD_SERVER)
|
||||
ADD_SUBDIRECTORY(ucs)
|
||||
ADD_SUBDIRECTORY(queryserv)
|
||||
ADD_SUBDIRECTORY(eqlaunch)
|
||||
ADD_SUBDIRECTORY(services/tasks)
|
||||
ENDIF(EQEMU_BUILD_SERVER)
|
||||
IF(EQEMU_BUILD_LOGIN)
|
||||
ADD_SUBDIRECTORY(loginserver)
|
||||
|
||||
+12
-2
@@ -54,6 +54,7 @@ SET(common_sources
|
||||
packet_functions.cpp
|
||||
perl_eqdb.cpp
|
||||
perl_eqdb_res.cpp
|
||||
platform.cpp
|
||||
proc_launcher.cpp
|
||||
profanity_manager.cpp
|
||||
ptimer.cpp
|
||||
@@ -63,6 +64,7 @@ SET(common_sources
|
||||
say_link.cpp
|
||||
serialize_buffer.cpp
|
||||
serverinfo.cpp
|
||||
service.cpp
|
||||
shareddb.cpp
|
||||
skills.cpp
|
||||
spdat.cpp
|
||||
@@ -71,8 +73,8 @@ SET(common_sources
|
||||
textures.cpp
|
||||
timer.cpp
|
||||
unix.cpp
|
||||
world_connection.cpp
|
||||
xml_parser.cpp
|
||||
platform.cpp
|
||||
json/jsoncpp.cpp
|
||||
net/console_server.cpp
|
||||
net/console_server_connection.cpp
|
||||
@@ -151,6 +153,7 @@ SET(common_headers
|
||||
fixed_memory_hash_set.h
|
||||
fixed_memory_variable_hash_set.h
|
||||
global_define.h
|
||||
global_tasks.h
|
||||
guild_base.h
|
||||
guilds.h
|
||||
inventory_profile.h
|
||||
@@ -195,6 +198,7 @@ SET(common_headers
|
||||
serialize_buffer.h
|
||||
serverinfo.h
|
||||
servertalk.h
|
||||
service.h
|
||||
shareddb.h
|
||||
skills.h
|
||||
spdat.h
|
||||
@@ -206,6 +210,7 @@ SET(common_headers
|
||||
unix.h
|
||||
useperl.h
|
||||
version.h
|
||||
world_connection.h
|
||||
xml_parser.h
|
||||
zone_numbers.h
|
||||
event/event_loop.h
|
||||
@@ -378,7 +383,12 @@ SOURCE_GROUP(Util FILES
|
||||
|
||||
INCLUDE_DIRECTORIES(Patches SocketLib StackWalker TinyXML)
|
||||
|
||||
ADD_LIBRARY(common ${common_sources} ${common_headers})
|
||||
protobuf_generate_cpp(PROTO_TASK_SRCS PROTO_TASK_HDRS "${CMAKE_CURRENT_SOURCE_DIR}/proto/task.proto")
|
||||
|
||||
ADD_LIBRARY(common
|
||||
${common_sources} ${common_headers}
|
||||
${PROTO_TASK_SRCS} ${PROTO_TASK_HDRS}
|
||||
)
|
||||
|
||||
IF(UNIX)
|
||||
SET_SOURCE_FILES_PROPERTIES("SocketLib/Mime.cpp" PROPERTY COMPILE_FLAGS -Wno-unused-result)
|
||||
|
||||
@@ -6,6 +6,7 @@ N(OP_0x0347),
|
||||
N(OP_AAAction),
|
||||
N(OP_AAExpUpdate),
|
||||
N(OP_AcceptNewTask),
|
||||
N(OP_AcceptNewSharedTask),
|
||||
N(OP_AckPacket),
|
||||
N(OP_Action),
|
||||
N(OP_Action2),
|
||||
@@ -461,6 +462,7 @@ N(OP_SetServerFilter),
|
||||
N(OP_SetStartCity),
|
||||
N(OP_SetTitle),
|
||||
N(OP_SetTitleReply),
|
||||
N(OP_SharedTaskMemberList),
|
||||
N(OP_Shielding),
|
||||
N(OP_ShopDelItem),
|
||||
N(OP_ShopEnd),
|
||||
|
||||
@@ -3698,6 +3698,7 @@ struct TaskMemberList_Struct {
|
||||
/*12*/ char list_pointer[0];
|
||||
/* list is of the form:
|
||||
char member_name[1] //null terminated string
|
||||
uint32 monster_mission; // class chosen
|
||||
uint8 task_leader //boolean flag
|
||||
*/
|
||||
};
|
||||
@@ -3799,6 +3800,14 @@ struct AcceptNewTask_Struct {
|
||||
uint32 task_master_id; //entity ID
|
||||
};
|
||||
|
||||
struct AcceptNewSharedTask_Struct {
|
||||
uint32 unknown00;
|
||||
uint32 unknown04;
|
||||
uint32 task_master_id;
|
||||
uint32 task_id;
|
||||
float unknown16;
|
||||
};
|
||||
|
||||
//was all 0's from client, server replied with same op, all 0's
|
||||
struct CancelTask_Struct {
|
||||
uint32 SequenceNumber;
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
|
||||
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 GLOBAL_TASKS_H
|
||||
#define GLOBAL_TASKS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* This file contains what is needed for both zone and world managers
|
||||
*/
|
||||
|
||||
#define MAXTASKS 10000
|
||||
#define MAXTASKSETS 1000
|
||||
// The Client has a hard cap of 19 active quests, 29 in SoD+
|
||||
#define MAXACTIVEQUESTS 19
|
||||
// The Max Chooser (Task Selector entries) is capped at 40 in the Titanium Client.
|
||||
#define MAXCHOOSERENTRIES 40
|
||||
// The Client has a hard cap of 20 activities per task.
|
||||
#define MAXACTIVITIESPERTASK 20
|
||||
// This is used to determine if a client's active task slot is empty.
|
||||
#define TASKSLOTEMPTY 0
|
||||
|
||||
// Command Codes for worldserver ServerOP_ReloadTasks
|
||||
#define RELOADTASKS 0
|
||||
#define RELOADTASKGOALLISTS 1
|
||||
#define RELOADTASKPROXIMITIES 2
|
||||
#define RELOADTASKSETS 3
|
||||
|
||||
// used for timer lockouts and /tasktimers
|
||||
struct TaskTimer {
|
||||
int ID; // ID used in task timer (replay group)
|
||||
int original_id; // original ID of the task (book keeping)
|
||||
int expires; // UNIX timestamp of when it expires, what happens with DLS? Fuck it.
|
||||
};
|
||||
|
||||
typedef enum { METHODSINGLEID = 0, METHODLIST = 1, METHODQUEST = 2 } TaskMethodType;
|
||||
|
||||
struct ActivityInformation {
|
||||
int StepNumber;
|
||||
int Type;
|
||||
std::string target_name; // name mob, location -- default empty
|
||||
std::string item_list; // likely defaults to empty
|
||||
std::string skill_list; // IDs ; separated -- default -1
|
||||
std::string spell_list; // IDs ; separated -- default 0
|
||||
std::string desc_override; // overrides auto generated description -- default empty
|
||||
int skill_id; // older clients, first id from above
|
||||
int spell_id; // older clients, first id from above
|
||||
int GoalID;
|
||||
TaskMethodType GoalMethod;
|
||||
int GoalCount;
|
||||
int DeliverToNPC;
|
||||
std::vector<int> ZoneIDs;
|
||||
std::string zones; // IDs ; searated, ZoneID is the first in this list for older clients -- default empty string
|
||||
bool Optional;
|
||||
|
||||
inline bool CheckZone(int zone_id) {
|
||||
if (ZoneIDs.empty())
|
||||
return true;
|
||||
return std::find(ZoneIDs.begin(), ZoneIDs.end(), zone_id) != ZoneIDs.end();
|
||||
}
|
||||
};
|
||||
|
||||
typedef enum { ActivitiesSequential = 0, ActivitiesStepped = 1 } SequenceType;
|
||||
|
||||
enum class TaskType {
|
||||
Task = 0, // can have at max 1
|
||||
Shared = 1, // can have at max 1
|
||||
Quest = 2, // can have at max 19 or 29 depending on client
|
||||
E = 3 // can have at max 19 or 29 depending on client, not present in live anymore
|
||||
};
|
||||
|
||||
enum class DurationCode {
|
||||
None = 0,
|
||||
Short = 1,
|
||||
Medium = 2,
|
||||
Long = 3
|
||||
};
|
||||
|
||||
// need to capture more, shared are just Radiant/Ebon though
|
||||
enum class PointType {
|
||||
None = 0,
|
||||
Radiant = 4,
|
||||
Ebon = 5,
|
||||
};
|
||||
|
||||
struct TaskInformation {
|
||||
TaskType type;
|
||||
int Duration;
|
||||
DurationCode dur_code; // description for time investment for when Duration == 0
|
||||
std::string Title; // max length 64
|
||||
std::string Description; // max length 4000, 2048 on Tit
|
||||
std::string Reward;
|
||||
std::string item_link; // max length 128 older clients, item link gets own string
|
||||
std::string completion_emote; // emote after completing task, yellow. Maybe should make more generic ... but yellow for now!
|
||||
int RewardID;
|
||||
int CashReward; // Expressed in copper
|
||||
int XPReward;
|
||||
int faction_reward; // just a npc_faction_id
|
||||
TaskMethodType RewardMethod;
|
||||
int reward_points; // DoN crystals for shared. Generic "points" for non-shared
|
||||
PointType reward_type; // 4 for Radiant Crystals else Ebon crystals when shared task
|
||||
int ActivityCount;
|
||||
SequenceType SequenceMode;
|
||||
int LastStep;
|
||||
short MinLevel;
|
||||
short MaxLevel;
|
||||
bool Repeatable;
|
||||
int replay_group; // ID of our replay timer group (0 means none)
|
||||
int min_players; // shared tasks
|
||||
int max_players;
|
||||
int task_lock_step; // task locks after this step is completed
|
||||
uint32 instance_zone_id; // instance shit
|
||||
uint32 zone_version;
|
||||
uint16 zone_in_zone_id;
|
||||
float zone_in_x;
|
||||
float zone_in_y;
|
||||
uint16 zone_in_object_id;
|
||||
float dest_x;
|
||||
float dest_y;
|
||||
float dest_z;
|
||||
float dest_h;
|
||||
/* int graveyard_zone_id;
|
||||
float graveyard_x;
|
||||
float graveyard_y;
|
||||
float graveyard_z;
|
||||
float graveyard_radius; */
|
||||
ActivityInformation Activity[MAXACTIVITIESPERTASK];
|
||||
};
|
||||
|
||||
typedef enum { ActivityHidden = 0, ActivityActive = 1, ActivityCompleted = 2 } ActivityState;
|
||||
|
||||
typedef enum { ActivityDeliver = 1, ActivityKill = 2, ActivityLoot = 3, ActivitySpeakWith = 4, ActivityExplore = 5,
|
||||
ActivityTradeSkill = 6, ActivityFish = 7, ActivityForage = 8, ActivityCastOn = 9, ActivitySkillOn = 10,
|
||||
ActivityTouch = 11, ActivityCollect = 13, ActivityGiveCash = 100 } ActivityType;
|
||||
|
||||
struct ClientActivityInformation {
|
||||
int ActivityID;
|
||||
int DoneCount;
|
||||
ActivityState State;
|
||||
bool Updated; // Flag so we know if we need to update the database
|
||||
};
|
||||
|
||||
struct ClientTaskInformation {
|
||||
int slot; // intrusive, but makes things easier :P
|
||||
int TaskID;
|
||||
int CurrentStep;
|
||||
int AcceptedTime;
|
||||
bool Updated;
|
||||
ClientActivityInformation Activity[MAXACTIVITIESPERTASK];
|
||||
};
|
||||
|
||||
#endif /* !GLOBAL_TASKS_H */
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "endian.h"
|
||||
#include <fmt/format.h>
|
||||
#include <cctype>
|
||||
#include <google/protobuf/message.h>
|
||||
|
||||
void EQ::Net::Packet::PutInt8(size_t offset, int8_t value)
|
||||
{
|
||||
@@ -167,6 +168,23 @@ void EQ::Net::Packet::PutData(size_t offset, void *data, size_t length)
|
||||
memcpy(((char*)Data() + offset), data, length);
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutProtobuf(size_t offset, const google::protobuf::Message *msg)
|
||||
{
|
||||
auto length = msg->ByteSizeLong();
|
||||
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Length() < offset + length) {
|
||||
if (!Resize(offset + length)) {
|
||||
throw std::out_of_range("Packet::PutProtobuf(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
msg->SerializeToArray((void*)((char*)Data() + offset), (int)length);
|
||||
}
|
||||
|
||||
int8_t EQ::Net::Packet::GetInt8(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 1) {
|
||||
@@ -276,6 +294,11 @@ std::string EQ::Net::Packet::GetCString(size_t offset) const
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
EQ::Net::StaticPacket EQ::Net::Packet::GetPacket(size_t offset, size_t length) const
|
||||
{
|
||||
return EQ::Net::StaticPacket((char*)Data() + offset, length);
|
||||
}
|
||||
|
||||
char ToSafePrint(unsigned char in) {
|
||||
if (std::isprint(in)) {
|
||||
return in;
|
||||
|
||||
+13
-1
@@ -8,8 +8,17 @@
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
}
|
||||
}
|
||||
|
||||
namespace EQ {
|
||||
namespace Net {
|
||||
class StaticPacket;
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
@@ -64,6 +73,7 @@ namespace EQ {
|
||||
void PutCString(size_t offset, const char *str);
|
||||
void PutPacket(size_t offset, const Packet &p);
|
||||
void PutData(size_t offset, void *data, size_t length);
|
||||
void PutProtobuf(size_t offset, const google::protobuf::Message *msg);
|
||||
|
||||
int8_t GetInt8(size_t offset) const;
|
||||
int16_t GetInt16(size_t offset) const;
|
||||
@@ -77,6 +87,8 @@ namespace EQ {
|
||||
double GetDouble(size_t offset) const;
|
||||
std::string GetString(size_t offset, size_t length) const;
|
||||
std::string GetCString(size_t offset) const;
|
||||
StaticPacket GetPacket(size_t offset, size_t length) const;
|
||||
google::protobuf::Message* GetProtobuf(size_t offset);
|
||||
|
||||
std::string ToString() const;
|
||||
std::string ToString(size_t line_length) const;
|
||||
@@ -127,4 +139,4 @@ namespace EQ {
|
||||
std::vector<char> m_data;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,34 +19,12 @@ EQ::Net::ServertalkClient::~ServertalkClient()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::Send(uint16_t opcode, EQ::Net::Packet &p)
|
||||
void EQ::Net::ServertalkClient::Send(uint16_t opcode, const EQ::Net::Packet &p)
|
||||
{
|
||||
EQ::Net::DynamicPacket out;
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
if (p.Length() == 0) {
|
||||
p.PutUInt8(0, 0);
|
||||
}
|
||||
|
||||
out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES);
|
||||
out.PutUInt16(4, opcode);
|
||||
|
||||
std::unique_ptr<unsigned char[]> cipher(new unsigned char[p.Length() + crypto_secretbox_MACBYTES]);
|
||||
|
||||
crypto_box_easy_afternm(&cipher[0], (unsigned char*)p.Data(), p.Length(), m_nonce_ours, m_shared_key);
|
||||
(*(uint64_t*)&m_nonce_ours[0])++;
|
||||
out.PutData(6, &cipher[0], p.Length() + crypto_secretbox_MACBYTES);
|
||||
}
|
||||
else {
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
}
|
||||
#else
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
#endif
|
||||
InternalSend(ServertalkMessage, out);
|
||||
}
|
||||
|
||||
@@ -188,51 +166,6 @@ void EQ::Net::ServertalkClient::ProcessReadBuffer()
|
||||
|
||||
void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p)
|
||||
{
|
||||
#ifdef ENABLE_SECURITY
|
||||
memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES);
|
||||
memset(m_nonce_ours, 0, crypto_box_NONCEBYTES);
|
||||
memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES);
|
||||
memset(m_shared_key, 0, crypto_box_BEFORENMBYTES);
|
||||
m_encrypted = false;
|
||||
|
||||
try {
|
||||
bool enc = p.GetInt8(0) == 1 ? true : false;
|
||||
|
||||
if (enc) {
|
||||
if (p.Length() == (1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) {
|
||||
memcpy(m_public_key_theirs, (char*)p.Data() + 1, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(m_nonce_theirs, (char*)p.Data() + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES);
|
||||
m_encrypted = true;
|
||||
|
||||
SendHandshake();
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogF(Logs::General, Logs::Error, "Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SendHandshake();
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
try {
|
||||
bool enc = p.GetInt8(0) == 1 ? true : false;
|
||||
|
||||
@@ -259,7 +192,6 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p)
|
||||
m_on_connect_cb(nullptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p)
|
||||
@@ -269,45 +201,6 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p)
|
||||
auto opcode = p.GetUInt16(4);
|
||||
if (length > 0) {
|
||||
auto data = p.GetString(6, length);
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
size_t message_len = length - crypto_secretbox_MACBYTES;
|
||||
std::unique_ptr<unsigned char[]> decrypted_text(new unsigned char[message_len]);
|
||||
if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key))
|
||||
{
|
||||
LogF(Logs::General, Logs::Error, "Error decrypting message from server");
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
return;
|
||||
}
|
||||
|
||||
EQ::Net::StaticPacket decrypted_packet(&decrypted_text[0], message_len);
|
||||
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, decrypted_packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, decrypted_packet);
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, packet);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
@@ -319,7 +212,6 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p)
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, packet);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
@@ -330,46 +222,9 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p)
|
||||
void EQ::Net::ServertalkClient::SendHandshake(bool downgrade)
|
||||
{
|
||||
EQ::Net::DynamicPacket handshake;
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
crypto_box_keypair(m_public_key_ours, m_private_key_ours);
|
||||
randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES);
|
||||
|
||||
crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours);
|
||||
|
||||
handshake.PutData(0, m_public_key_ours, crypto_box_PUBLICKEYBYTES);
|
||||
handshake.PutData(crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES);
|
||||
|
||||
memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES);
|
||||
|
||||
size_t cipher_length = m_identifier.length() + 1 + m_credentials.length() + 1 + crypto_secretbox_MACBYTES;
|
||||
size_t data_length = m_identifier.length() + 1 + m_credentials.length() + 1;
|
||||
|
||||
std::unique_ptr<unsigned char[]> signed_buffer(new unsigned char[cipher_length]);
|
||||
std::unique_ptr<unsigned char[]> data_buffer(new unsigned char[data_length]);
|
||||
|
||||
memset(&data_buffer[0], 0, data_length);
|
||||
memcpy(&data_buffer[0], m_identifier.c_str(), m_identifier.length());
|
||||
memcpy(&data_buffer[1 + m_identifier.length()], m_credentials.c_str(), m_credentials.length());
|
||||
|
||||
crypto_box_easy_afternm(&signed_buffer[0], &data_buffer[0], data_length, m_nonce_ours, m_shared_key);
|
||||
|
||||
(*(uint64_t*)&m_nonce_ours[0])++;
|
||||
|
||||
handshake.PutData(crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, &signed_buffer[0], cipher_length);
|
||||
}
|
||||
else {
|
||||
handshake.PutString(0, m_identifier);
|
||||
handshake.PutString(m_identifier.length() + 1, m_credentials);
|
||||
handshake.PutUInt8(m_identifier.length() + 1 + m_credentials.length(), 0);
|
||||
}
|
||||
#else
|
||||
handshake.PutString(0, m_identifier);
|
||||
handshake.PutString(m_identifier.length() + 1, m_credentials);
|
||||
handshake.PutUInt8(m_identifier.length() + 1 + m_credentials.length(), 0);
|
||||
#endif
|
||||
|
||||
if (downgrade) {
|
||||
InternalSend(ServertalkClientDowngradeSecurityHandshake, handshake);
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
#include "../event/timer.h"
|
||||
#include "servertalk_common.h"
|
||||
#include "packet.h"
|
||||
#ifdef ENABLE_SECURITY
|
||||
#include <sodium.h>
|
||||
#endif
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
@@ -18,7 +15,7 @@ namespace EQ
|
||||
ServertalkClient(const std::string &addr, int port, bool ipv6, const std::string &identifier, const std::string &credentials);
|
||||
~ServertalkClient();
|
||||
|
||||
void Send(uint16_t opcode, EQ::Net::Packet &p);
|
||||
void Send(uint16_t opcode, const EQ::Net::Packet &p);
|
||||
void SendPacket(ServerPacket *p);
|
||||
void OnConnect(std::function<void(ServertalkClient*)> cb) { m_on_connect_cb = cb; }
|
||||
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
@@ -26,6 +23,8 @@ namespace EQ
|
||||
bool Connected() const { return m_connecting != true; }
|
||||
|
||||
std::shared_ptr<EQ::Net::TCPConnection> Handle() { return m_connection; }
|
||||
|
||||
std::string GetIdentifier() const { return m_identifier; }
|
||||
private:
|
||||
void Connect();
|
||||
void ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length);
|
||||
@@ -51,17 +50,6 @@ namespace EQ
|
||||
std::unordered_map<uint16_t, std::function<void(uint16_t, EQ::Net::Packet&)>> m_message_callbacks;
|
||||
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
|
||||
std::function<void(ServertalkClient*)> m_on_connect_cb;
|
||||
|
||||
#ifdef ENABLE_SECURITY
|
||||
unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char m_private_key_ours[crypto_box_SECRETKEYBYTES];
|
||||
unsigned char m_nonce_ours[crypto_box_NONCEBYTES];
|
||||
|
||||
unsigned char m_public_key_theirs[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char m_nonce_theirs[crypto_box_NONCEBYTES];
|
||||
|
||||
unsigned char m_shared_key[crypto_box_BEFORENMBYTES];
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ EQ::Net::ServertalkLegacyClient::~ServertalkLegacyClient()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkLegacyClient::Send(uint16_t opcode, EQ::Net::Packet &p)
|
||||
void EQ::Net::ServertalkLegacyClient::Send(uint16_t opcode, const EQ::Net::Packet &p)
|
||||
{
|
||||
if (!m_connection)
|
||||
return;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace EQ
|
||||
ServertalkLegacyClient(const std::string &addr, int port, bool ipv6);
|
||||
~ServertalkLegacyClient();
|
||||
|
||||
void Send(uint16_t opcode, EQ::Net::Packet &p);
|
||||
void Send(uint16_t opcode, const EQ::Net::Packet &p);
|
||||
void SendPacket(ServerPacket *p);
|
||||
void OnConnect(std::function<void(ServertalkLegacyClient*)> cb) { m_on_connect_cb = cb; }
|
||||
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "servertalk_server.h"
|
||||
#include <regex>
|
||||
|
||||
EQ::Net::ServertalkServer::ServertalkServer()
|
||||
{
|
||||
@@ -19,16 +20,26 @@ void EQ::Net::ServertalkServer::Listen(const ServertalkServerOptions& opts)
|
||||
});
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::OnConnectionIdentified(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb)
|
||||
void EQ::Net::ServertalkServer::OnConnectionIdentified(const std::string &type, IdentityCallback cb)
|
||||
{
|
||||
m_on_ident.insert(std::make_pair(type, cb));
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::OnConnectionRemoved(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb)
|
||||
void EQ::Net::ServertalkServer::OnConnectionRemoved(const std::string &type, IdentityCallback cb)
|
||||
{
|
||||
m_on_disc.insert(std::make_pair(type, cb));
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::OnConnectionIdentified(IdentityCallback cb)
|
||||
{
|
||||
m_on_any_ident = cb;
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::OnConnectionRemoved(IdentityCallback cb)
|
||||
{
|
||||
m_on_any_disc = cb;
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::ConnectionDisconnected(ServertalkServerConnection *conn)
|
||||
{
|
||||
if (conn->GetIdentifier().empty()) {
|
||||
@@ -51,6 +62,11 @@ void EQ::Net::ServertalkServer::ConnectionDisconnected(ServertalkServerConnectio
|
||||
if (on_disc != m_on_disc.end()) {
|
||||
on_disc->second(*iter);
|
||||
}
|
||||
|
||||
if (m_on_any_disc) {
|
||||
m_on_any_disc(*iter);
|
||||
}
|
||||
|
||||
type->second.erase(iter);
|
||||
return;
|
||||
}
|
||||
@@ -65,9 +81,16 @@ void EQ::Net::ServertalkServer::ConnectionIdentified(ServertalkServerConnection
|
||||
auto iter = m_unident_connections.begin();
|
||||
while (iter != m_unident_connections.end()) {
|
||||
if (conn == iter->get()) {
|
||||
auto on_ident = m_on_ident.find(conn->GetIdentifier());
|
||||
if (on_ident != m_on_ident.end()) {
|
||||
on_ident->second(*iter);
|
||||
for (auto &ident : m_on_ident) {
|
||||
std::regex ident_regex(ident.first);
|
||||
|
||||
if (std::regex_match(conn->GetIdentifier(), ident_regex)) {
|
||||
ident.second(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_on_any_ident) {
|
||||
m_on_any_ident(*iter);
|
||||
}
|
||||
|
||||
if (m_ident_connections.count(conn->GetIdentifier()) > 0) {
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#ifdef ENABLE_SECURITY
|
||||
#include <sodium.h>
|
||||
#endif
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
@@ -22,13 +18,8 @@ namespace EQ
|
||||
std::string credentials;
|
||||
|
||||
ServertalkServerOptions() {
|
||||
#ifdef ENABLE_SECURITY
|
||||
encrypted = true;
|
||||
allow_downgrade = true;
|
||||
#else
|
||||
encrypted = false;
|
||||
allow_downgrade = true;
|
||||
#endif
|
||||
ipv6 = false;
|
||||
}
|
||||
};
|
||||
@@ -36,12 +27,16 @@ namespace EQ
|
||||
class ServertalkServer
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(std::shared_ptr<ServertalkServerConnection>)> IdentityCallback;
|
||||
|
||||
ServertalkServer();
|
||||
~ServertalkServer();
|
||||
|
||||
void Listen(const ServertalkServerOptions& opts);
|
||||
void OnConnectionIdentified(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb);
|
||||
void OnConnectionRemoved(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb);
|
||||
void OnConnectionIdentified(const std::string &type, IdentityCallback cb);
|
||||
void OnConnectionRemoved(const std::string &type, IdentityCallback cb);
|
||||
void OnConnectionIdentified(IdentityCallback cb);
|
||||
void OnConnectionRemoved(IdentityCallback cb);
|
||||
|
||||
private:
|
||||
void ConnectionDisconnected(ServertalkServerConnection *conn);
|
||||
@@ -52,8 +47,10 @@ namespace EQ
|
||||
std::vector<std::shared_ptr<ServertalkServerConnection>> m_unident_connections;
|
||||
std::map<std::string, std::vector<std::shared_ptr<ServertalkServerConnection>>> m_ident_connections;
|
||||
|
||||
std::map<std::string, std::function<void(std::shared_ptr<ServertalkServerConnection>)>> m_on_ident;
|
||||
std::map<std::string, std::function<void(std::shared_ptr<ServertalkServerConnection>)>> m_on_disc;
|
||||
std::map<std::string, IdentityCallback> m_on_ident;
|
||||
std::map<std::string, IdentityCallback> m_on_disc;
|
||||
IdentityCallback m_on_any_ident;
|
||||
IdentityCallback m_on_any_disc;
|
||||
bool m_encrypted;
|
||||
bool m_allow_downgrade;
|
||||
std::string m_credentials;
|
||||
|
||||
@@ -19,33 +19,12 @@ EQ::Net::ServertalkServerConnection::~ServertalkServerConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p)
|
||||
void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, const EQ::Net::Packet & p)
|
||||
{
|
||||
EQ::Net::DynamicPacket out;
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
if (p.Length() == 0) {
|
||||
p.PutUInt8(0, 0);
|
||||
}
|
||||
|
||||
out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES);
|
||||
out.PutUInt16(4, opcode);
|
||||
|
||||
std::unique_ptr<unsigned char[]> cipher(new unsigned char[p.Length() + crypto_secretbox_MACBYTES]);
|
||||
crypto_box_easy_afternm(&cipher[0], (unsigned char*)p.Data(), p.Length(), m_nonce_ours, m_shared_key);
|
||||
(*(uint64_t*)&m_nonce_ours[0])++;
|
||||
out.PutData(6, &cipher[0], p.Length() + crypto_secretbox_MACBYTES);
|
||||
}
|
||||
else {
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
}
|
||||
#else
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
#endif
|
||||
InternalSend(ServertalkMessage, out);
|
||||
}
|
||||
|
||||
@@ -155,29 +134,7 @@ void EQ::Net::ServertalkServerConnection::OnDisconnect(TCPConnection *c)
|
||||
void EQ::Net::ServertalkServerConnection::SendHello()
|
||||
{
|
||||
EQ::Net::DynamicPacket hello;
|
||||
|
||||
#ifdef ENABLE_SECURITY
|
||||
memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES);
|
||||
memset(m_nonce_ours, 0, crypto_box_NONCEBYTES);
|
||||
memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES);
|
||||
|
||||
if (m_encrypted) {
|
||||
hello.PutInt8(0, 1);
|
||||
|
||||
crypto_box_keypair(m_public_key_ours, m_private_key_ours);
|
||||
randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES);
|
||||
|
||||
hello.PutData(1, m_public_key_ours, crypto_box_PUBLICKEYBYTES);
|
||||
hello.PutData(1 + crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES);
|
||||
}
|
||||
else {
|
||||
hello.PutInt8(0, 0);
|
||||
}
|
||||
#else
|
||||
hello.PutInt8(0, 0);
|
||||
#endif
|
||||
|
||||
InternalSend(ServertalkServerHello, hello);
|
||||
}
|
||||
@@ -199,69 +156,6 @@ void EQ::Net::ServertalkServerConnection::InternalSend(ServertalkPacketType type
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, bool downgrade_security)
|
||||
{
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (downgrade_security && m_allow_downgrade && m_encrypted) {
|
||||
LogF(Logs::General, Logs::TCP_Connection, "Downgraded encrypted connection to plaintext because otherside didn't support encryption {0}:{1}",
|
||||
m_connection->RemoteIP(), m_connection->RemotePort());
|
||||
m_encrypted = false;
|
||||
}
|
||||
|
||||
if (m_encrypted) {
|
||||
try {
|
||||
if (p.Length() > (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) {
|
||||
memcpy(m_public_key_theirs, (char*)p.Data(), crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(m_nonce_theirs, (char*)p.Data() + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES);
|
||||
|
||||
crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours);
|
||||
|
||||
size_t cipher_len = p.Length() - crypto_box_PUBLICKEYBYTES - crypto_box_NONCEBYTES;
|
||||
size_t message_len = cipher_len - crypto_secretbox_MACBYTES;
|
||||
std::unique_ptr<unsigned char[]> decrypted_text(new unsigned char[message_len]);
|
||||
|
||||
if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)p.Data() + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, cipher_len, m_nonce_theirs, m_shared_key))
|
||||
{
|
||||
LogF(Logs::General, Logs::Error, "Error decrypting handshake from client, dropping connection.");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
m_identifier = (const char*)&decrypted_text[0];
|
||||
std::string credentials = (const char*)&decrypted_text[0] + (m_identifier.length() + 1);
|
||||
|
||||
if (!m_parent->CheckCredentials(credentials)) {
|
||||
LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection.");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
m_parent->ConnectionIdentified(this);
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
m_identifier = p.GetCString(0);
|
||||
auto credentials = p.GetCString(m_identifier.length() + 1);
|
||||
|
||||
if (!m_parent->CheckCredentials(credentials)) {
|
||||
LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection.");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
m_parent->ConnectionIdentified(this);
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
}
|
||||
#else
|
||||
try {
|
||||
m_identifier = p.GetCString(0);
|
||||
auto credentials = p.GetCString(m_identifier.length() + 1);
|
||||
@@ -278,7 +172,6 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b
|
||||
LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
|
||||
@@ -288,46 +181,6 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
|
||||
auto opcode = p.GetUInt16(4);
|
||||
if (length > 0) {
|
||||
auto data = p.GetString(6, length);
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
size_t message_len = length - crypto_secretbox_MACBYTES;
|
||||
std::unique_ptr<unsigned char[]> decrypted_text(new unsigned char[message_len]);
|
||||
|
||||
if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key))
|
||||
{
|
||||
LogF(Logs::General, Logs::Error, "Error decrypting message from client");
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
return;
|
||||
}
|
||||
|
||||
EQ::Net::StaticPacket decrypted_packet(&decrypted_text[0], message_len);
|
||||
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, decrypted_packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, decrypted_packet);
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, packet);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
@@ -339,7 +192,6 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, packet);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
#include "servertalk_common.h"
|
||||
#include "packet.h"
|
||||
#include <vector>
|
||||
#ifdef ENABLE_SECURITY
|
||||
#include <sodium.h>
|
||||
#endif
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
@@ -19,7 +16,7 @@ namespace EQ
|
||||
ServertalkServerConnection(std::shared_ptr<EQ::Net::TCPConnection> c, ServertalkServer *parent, bool encrypted, bool allow_downgrade);
|
||||
~ServertalkServerConnection();
|
||||
|
||||
void Send(uint16_t opcode, EQ::Net::Packet &p);
|
||||
void Send(uint16_t opcode, const EQ::Net::Packet &p);
|
||||
void SendPacket(ServerPacket *p);
|
||||
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
void OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
@@ -48,16 +45,6 @@ namespace EQ
|
||||
|
||||
bool m_encrypted;
|
||||
bool m_allow_downgrade;
|
||||
#ifdef ENABLE_SECURITY
|
||||
unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char m_private_key_ours[crypto_box_SECRETKEYBYTES];
|
||||
unsigned char m_nonce_ours[crypto_box_NONCEBYTES];
|
||||
|
||||
unsigned char m_public_key_theirs[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char m_nonce_theirs[crypto_box_NONCEBYTES];
|
||||
|
||||
unsigned char m_shared_key[crypto_box_BEFORENMBYTES];
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
package EQ.Proto;
|
||||
|
||||
message TaskMessage {
|
||||
int32 message = 1;
|
||||
google.protobuf.Any details = 2;
|
||||
}
|
||||
|
||||
message ClientTaskStateRequest {
|
||||
int32 client_id = 1;
|
||||
}
|
||||
|
||||
message ClientTaskStateResponse {
|
||||
int32 client_id = 1;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ public:
|
||||
const unsigned char *buffer() const { return m_buffer; }
|
||||
|
||||
friend class BasePacket;
|
||||
friend class ServerPacket;
|
||||
|
||||
private:
|
||||
void Grow(size_t new_size);
|
||||
|
||||
+233
-197
@@ -1,218 +1,220 @@
|
||||
#ifndef EQ_SOPCODES_H
|
||||
#define EQ_SOPCODES_H
|
||||
#pragma once
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "../common/packet_functions.h"
|
||||
#include "../common/eq_packet_structs.h"
|
||||
#include "../common/serialize_buffer.h"
|
||||
#include "../net/packet.h"
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
|
||||
#define SERVER_TIMEOUT 45000 // how often keepalive gets sent
|
||||
#define INTERSERVER_TIMER 10000
|
||||
#define LoginServer_StatusUpdateInterval 15000
|
||||
#define LoginServer_AuthStale 60000
|
||||
#define AUTHCHANGE_TIMEOUT 900 // in seconds
|
||||
constexpr auto INTERSERVER_TIMER = 10000;
|
||||
constexpr auto LoginServer_StatusUpdateInterval = 15000;
|
||||
|
||||
#define ServerOP_KeepAlive 0x0001 // packet to test if port is still open
|
||||
#define ServerOP_ChannelMessage 0x0002 // broadcast/guildsay
|
||||
#define ServerOP_SetZone 0x0003 // client -> server zoneinfo
|
||||
#define ServerOP_ShutdownAll 0x0004 // exit(0);
|
||||
#define ServerOP_ZoneShutdown 0x0005 // unload all data, goto sleep mode
|
||||
#define ServerOP_ZoneBootup 0x0006 // come out of sleep mode and load zone specified
|
||||
#define ServerOP_ZoneStatus 0x0007 // Shows status of all zones
|
||||
#define ServerOP_SetConnectInfo 0x0008 // Tells server address and port #
|
||||
#define ServerOP_EmoteMessage 0x0009 // Worldfarts
|
||||
#define ServerOP_ClientList 0x000A // Update worldserver's client list, for #whos
|
||||
#define ServerOP_Who 0x000B // #who
|
||||
#define ServerOP_ZonePlayer 0x000C // #zone, or #summon
|
||||
#define ServerOP_KickPlayer 0x000D // #kick
|
||||
//Certain ops needed for backwards compat with old LS can't enum without being really annoying.
|
||||
constexpr auto ServerOP_UsertoWorldReq = 0xAB00;
|
||||
constexpr auto ServerOP_UsertoWorldResp = 0xAB01;
|
||||
constexpr auto ServerOP_LSClientAuth = 0x1002;
|
||||
constexpr auto ServerOP_LSFatalError = 0x1003;
|
||||
constexpr auto ServerOP_SystemwideMessage = 0x1005;
|
||||
constexpr auto ServerOP_LSRemoteAddr = 0x1009;
|
||||
constexpr auto ServerOP_LSAccountUpdate = 0x100A;
|
||||
constexpr auto ServerOP_NewLSInfo = 0x1008;
|
||||
constexpr auto ServerOP_LSInfo = 0x1000;
|
||||
constexpr auto ServerOP_LSStatus = 0x1001;
|
||||
|
||||
#define ServerOP_RefreshGuild 0x000E // Notice to all zoneservers to refresh their guild cache for ID# in packet (ServerGuildRefresh_Struct)
|
||||
#define ServerOP_VoiceMacro 0x000F
|
||||
//#define ServerOP_GuildInvite 0x0010
|
||||
#define ServerOP_DeleteGuild 0x0011 // ServerGuildID_Struct
|
||||
#define ServerOP_GuildRankUpdate 0x0012
|
||||
#define ServerOP_GuildCharRefresh 0x0013
|
||||
#define ServerOP_GuildMemberUpdate 0x0014
|
||||
#define ServerOP_RequestOnlineGuildMembers 0x0015
|
||||
#define ServerOP_OnlineGuildMembersResponse 0x0016
|
||||
#define ServerOP_LFGuildUpdate 0x0017
|
||||
enum ServerOpcode : int
|
||||
{
|
||||
ServerOP_ChannelMessage,
|
||||
ServerOP_SetZone,
|
||||
ServerOP_ShutdownAll,
|
||||
ServerOP_ZoneShutdown,
|
||||
ServerOP_ZoneBootup,
|
||||
ServerOP_ZoneStatus,
|
||||
ServerOP_SetConnectInfo,
|
||||
ServerOP_EmoteMessage,
|
||||
ServerOP_ClientList,
|
||||
ServerOP_Who,
|
||||
ServerOP_ZonePlayer,
|
||||
ServerOP_KickPlayer,
|
||||
ServerOP_RefreshGuild,
|
||||
ServerOP_VoiceMacro,
|
||||
//ServerOP_GuildInvite,
|
||||
ServerOP_DeleteGuild,
|
||||
ServerOP_GuildRankUpdate,
|
||||
ServerOP_GuildCharRefresh,
|
||||
ServerOP_GuildMemberUpdate,
|
||||
ServerOP_RequestOnlineGuildMembers,
|
||||
ServerOP_OnlineGuildMembersResponse,
|
||||
ServerOP_LFGuildUpdate,
|
||||
ServerOP_FlagUpdate,
|
||||
ServerOP_GMGoto,
|
||||
ServerOP_MultiLineMsg,
|
||||
ServerOP_Lock,
|
||||
ServerOP_Motd,
|
||||
ServerOP_Uptime,
|
||||
ServerOP_Petition,
|
||||
ServerOP_KillPlayer,
|
||||
ServerOP_UpdateGM,
|
||||
ServerOP_RezzPlayer,
|
||||
ServerOP_ZoneReboot,
|
||||
ServerOP_ZoneToZoneRequest,
|
||||
ServerOP_AcceptWorldEntrance,
|
||||
ServerOP_ZAAuth,
|
||||
ServerOP_ZAAuthFailed,
|
||||
ServerOP_ZoneIncClient,
|
||||
ServerOP_ClientListKA,
|
||||
ServerOP_ChangeWID,
|
||||
ServerOP_IPLookup,
|
||||
ServerOP_LockZone,
|
||||
ServerOP_ItemStatus,
|
||||
ServerOP_OOCMute,
|
||||
ServerOP_Revoke,
|
||||
ServerOP_WebInterfaceCall,
|
||||
ServerOP_GroupIDReq,
|
||||
ServerOP_GroupIDReply,
|
||||
ServerOP_GroupLeave,
|
||||
ServerOP_RezzPlayerAccept,
|
||||
ServerOP_SpawnCondition,
|
||||
ServerOP_SpawnEvent,
|
||||
ServerOP_SetLaunchName,
|
||||
ServerOP_RezzPlayerReject,
|
||||
ServerOP_SpawnPlayerCorpse,
|
||||
ServerOP_Consent,
|
||||
ServerOP_Consent_Response,
|
||||
ServerOP_ForceGroupUpdate,
|
||||
ServerOP_OOZGroupMessage,
|
||||
ServerOP_DisbandGroup,
|
||||
ServerOP_GroupJoin,
|
||||
ServerOP_UpdateSpawn,
|
||||
ServerOP_SpawnStatusChange,
|
||||
ServerOP_ReloadTasks,
|
||||
ServerOP_DepopAllPlayersCorpses,
|
||||
ServerOP_ReloadTitles,
|
||||
ServerOP_QGlobalUpdate,
|
||||
ServerOP_QGlobalDelete,
|
||||
ServerOP_DepopPlayerCorpse,
|
||||
ServerOP_RequestTellQueue,
|
||||
ServerOP_ChangeSharedMem,
|
||||
ServerOP_WebInterfaceEvent,
|
||||
ServerOP_WebInterfaceSubscribe,
|
||||
ServerOP_WebInterfaceUnsubscribe,
|
||||
ServerOP_RaidAdd,
|
||||
ServerOP_RaidRemove,
|
||||
ServerOP_RaidDisband,
|
||||
ServerOP_RaidLockFlag,
|
||||
ServerOP_RaidGroupLeader,
|
||||
ServerOP_RaidLeader,
|
||||
ServerOP_RaidGroupSay,
|
||||
ServerOP_RaidSay,
|
||||
ServerOP_DetailsChange,
|
||||
ServerOP_UpdateGroup,
|
||||
ServerOP_RaidGroupDisband,
|
||||
ServerOP_RaidChangeGroup,
|
||||
ServerOP_RaidGroupAdd,
|
||||
ServerOP_RaidGroupRemove,
|
||||
ServerOP_GroupInvite,
|
||||
ServerOP_GroupFollow,
|
||||
ServerOP_GroupFollowAck,
|
||||
ServerOP_GroupCancelInvite,
|
||||
ServerOP_RaidMOTD,
|
||||
ServerOP_InstanceUpdateTime,
|
||||
ServerOP_AdventureRequest,
|
||||
ServerOP_AdventureRequestAccept,
|
||||
ServerOP_AdventureRequestDeny,
|
||||
ServerOP_AdventureRequestCreate,
|
||||
ServerOP_AdventureData,
|
||||
ServerOP_AdventureDataClear,
|
||||
ServerOP_AdventureCreateDeny,
|
||||
ServerOP_AdventureDataRequest,
|
||||
ServerOP_AdventureClickDoor,
|
||||
ServerOP_AdventureClickDoorReply,
|
||||
ServerOP_AdventureClickDoorError,
|
||||
ServerOP_AdventureLeave,
|
||||
ServerOP_AdventureLeaveReply,
|
||||
ServerOP_AdventureLeaveDeny,
|
||||
ServerOP_AdventureCountUpdate,
|
||||
ServerOP_AdventureZoneData,
|
||||
ServerOP_AdventureAssaCountUpdate,
|
||||
ServerOP_AdventureFinish,
|
||||
ServerOP_AdventureLeaderboard,
|
||||
ServerOP_WhoAll,
|
||||
ServerOP_FriendsWho,
|
||||
ServerOP_LFGMatches,
|
||||
ServerOP_LFPUpdate,
|
||||
ServerOP_LFPMatches,
|
||||
ServerOP_ClientVersionSummary,
|
||||
ServerOP_ListWorlds,
|
||||
ServerOP_PeerConnect,
|
||||
ServerOP_TaskRequest,
|
||||
ServerOP_TaskGrant,
|
||||
ServerOP_TaskReject,
|
||||
ServerOP_TaskAddPlayer,
|
||||
ServerOP_TaskRemovePlayer,
|
||||
ServerOP_TaskZoneCreated,
|
||||
ServerOP_TaskZoneFailed,
|
||||
ServerOP_EncapPacket,
|
||||
ServerOP_WorldListUpdate,
|
||||
ServerOP_WorldListRemove,
|
||||
ServerOP_TriggerWorldListRefresh,
|
||||
ServerOP_WhoAllReply,
|
||||
ServerOP_SetWorldTime,
|
||||
ServerOP_GetWorldTime,
|
||||
ServerOP_SyncWorldTime,
|
||||
ServerOP_RefreshCensorship,
|
||||
ServerOP_LSZoneInfo,
|
||||
ServerOP_LSZoneStart,
|
||||
ServerOP_LSZoneBoot,
|
||||
ServerOP_LSZoneShutdown,
|
||||
ServerOP_LSZoneSleep,
|
||||
ServerOP_LSPlayerLeftWorld,
|
||||
ServerOP_LSPlayerJoinWorld,
|
||||
ServerOP_LSPlayerZoneChange,
|
||||
ServerOP_LauncherConnectInfo,
|
||||
ServerOP_LauncherZoneRequest,
|
||||
ServerOP_LauncherZoneStatus,
|
||||
ServerOP_DoZoneCommand,
|
||||
ServerOP_UCSMessage,
|
||||
ServerOP_UCSMailMessage,
|
||||
ServerOP_ReloadRules,
|
||||
ServerOP_ReloadRulesWorld,
|
||||
ServerOP_CameraShake,
|
||||
ServerOP_QueryServGeneric,
|
||||
ServerOP_CZSignalClient,
|
||||
ServerOP_CZSignalClientByName,
|
||||
ServerOP_CZMessagePlayer,
|
||||
ServerOP_ReloadWorld,
|
||||
ServerOP_ReloadLogs,
|
||||
ServerOP_ReloadPerlExportSettings,
|
||||
ServerOP_CZSetEntityVariableByClientName,
|
||||
ServerOP_UCSServerStatusRequest,
|
||||
ServerOP_UCSServerStatusReply,
|
||||
ServerOP_Speech,
|
||||
ServerOP_QSPlayerLogTrades,
|
||||
ServerOP_QSPlayerLogHandins,
|
||||
ServerOP_QSPlayerLogNPCKills,
|
||||
ServerOP_QSPlayerLogDeletes,
|
||||
ServerOP_QSPlayerLogMoves,
|
||||
ServerOP_QSPlayerLogMerchantTransactions,
|
||||
ServerOP_QSSendQuery,
|
||||
ServerOP_CZSignalNPC,
|
||||
ServerOP_CZSetEntityVariableByNPCTypeID,
|
||||
ServerOP_WWMarquee,
|
||||
ServerOP_QSPlayerDropItem,
|
||||
ServerOP_RouteTo,
|
||||
|
||||
#define ServerOP_FlagUpdate 0x0018 // GM Flag updated for character, refresh the memory cache
|
||||
#define ServerOP_GMGoto 0x0019
|
||||
#define ServerOP_MultiLineMsg 0x001A
|
||||
#define ServerOP_Lock 0x001B // For #lock/#unlock inside server
|
||||
#define ServerOP_Motd 0x001C // For changing MoTD inside server.
|
||||
#define ServerOP_Uptime 0x001D
|
||||
#define ServerOP_Petition 0x001E
|
||||
#define ServerOP_KillPlayer 0x001F
|
||||
#define ServerOP_UpdateGM 0x0020
|
||||
#define ServerOP_RezzPlayer 0x0021
|
||||
#define ServerOP_ZoneReboot 0x0022
|
||||
#define ServerOP_ZoneToZoneRequest 0x0023
|
||||
#define ServerOP_AcceptWorldEntrance 0x0024
|
||||
#define ServerOP_ZAAuth 0x0025
|
||||
#define ServerOP_ZAAuthFailed 0x0026
|
||||
#define ServerOP_ZoneIncClient 0x0027 // Incoming client
|
||||
#define ServerOP_ClientListKA 0x0028
|
||||
#define ServerOP_ChangeWID 0x0029
|
||||
#define ServerOP_IPLookup 0x002A
|
||||
#define ServerOP_LockZone 0x002B
|
||||
#define ServerOP_ItemStatus 0x002C
|
||||
#define ServerOP_OOCMute 0x002D
|
||||
#define ServerOP_Revoke 0x002E
|
||||
#define ServerOP_WebInterfaceCall 0x002F
|
||||
#define ServerOP_GroupIDReq 0x0030
|
||||
#define ServerOP_GroupIDReply 0x0031
|
||||
#define ServerOP_GroupLeave 0x0032 // for disbanding out of zone folks
|
||||
#define ServerOP_RezzPlayerAccept 0x0033
|
||||
#define ServerOP_SpawnCondition 0x0034
|
||||
#define ServerOP_SpawnEvent 0x0035
|
||||
#define ServerOP_SetLaunchName 0x0036
|
||||
#define ServerOP_RezzPlayerReject 0x0037
|
||||
#define ServerOP_SpawnPlayerCorpse 0x0038
|
||||
#define ServerOP_Consent 0x0039
|
||||
#define ServerOP_Consent_Response 0x003a
|
||||
#define ServerOP_ForceGroupUpdate 0x003b
|
||||
#define ServerOP_OOZGroupMessage 0x003c
|
||||
#define ServerOP_DisbandGroup 0x003d //for disbanding a whole group cross zone
|
||||
#define ServerOP_GroupJoin 0x003e //for joining ooz folks
|
||||
#define ServerOP_UpdateSpawn 0x003f
|
||||
#define ServerOP_SpawnStatusChange 0x0040
|
||||
#define ServerOP_ReloadTasks 0x0060
|
||||
#define ServerOP_DepopAllPlayersCorpses 0x0061
|
||||
#define ServerOP_ReloadTitles 0x0062
|
||||
#define ServerOP_QGlobalUpdate 0x0063
|
||||
#define ServerOP_QGlobalDelete 0x0064
|
||||
#define ServerOP_DepopPlayerCorpse 0x0065
|
||||
#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues
|
||||
#define ServerOP_ChangeSharedMem 0x0067
|
||||
#define ServerOP_WebInterfaceEvent 0x0068
|
||||
#define ServerOP_WebInterfaceSubscribe 0x0069
|
||||
#define ServerOP_WebInterfaceUnsubscribe 0x0070
|
||||
/*Tasks*/
|
||||
ServerOP_GetClientTaskState
|
||||
};
|
||||
|
||||
#define ServerOP_RaidAdd 0x0100 //in use
|
||||
#define ServerOP_RaidRemove 0x0101 //in use
|
||||
#define ServerOP_RaidDisband 0x0102 //in use
|
||||
#define ServerOP_RaidLockFlag 0x0103 //in use
|
||||
#define ServerOP_RaidGroupLeader 0x0104 //in use
|
||||
#define ServerOP_RaidLeader 0x0105 //in use
|
||||
#define ServerOP_RaidGroupSay 0x0106 //in use
|
||||
#define ServerOP_RaidSay 0x0107 //in use
|
||||
#define ServerOP_DetailsChange 0x0108 //in use
|
||||
|
||||
#define ServerOP_UpdateGroup 0x010A //in use
|
||||
#define ServerOP_RaidGroupDisband 0x010B //in use
|
||||
#define ServerOP_RaidChangeGroup 0x010C //in use
|
||||
#define ServerOP_RaidGroupAdd 0x010D
|
||||
#define ServerOP_RaidGroupRemove 0x010E
|
||||
#define ServerOP_GroupInvite 0x010F
|
||||
#define ServerOP_GroupFollow 0x0110
|
||||
#define ServerOP_GroupFollowAck 0x0111
|
||||
#define ServerOP_GroupCancelInvite 0x0112
|
||||
#define ServerOP_RaidMOTD 0x0113
|
||||
|
||||
#define ServerOP_InstanceUpdateTime 0x014F
|
||||
#define ServerOP_AdventureRequest 0x0150
|
||||
#define ServerOP_AdventureRequestAccept 0x0151
|
||||
#define ServerOP_AdventureRequestDeny 0x0152
|
||||
#define ServerOP_AdventureRequestCreate 0x0153
|
||||
#define ServerOP_AdventureData 0x0154
|
||||
#define ServerOP_AdventureDataClear 0x0155
|
||||
#define ServerOP_AdventureCreateDeny 0x0156
|
||||
#define ServerOP_AdventureDataRequest 0x0157
|
||||
#define ServerOP_AdventureClickDoor 0x0158
|
||||
#define ServerOP_AdventureClickDoorReply 0x0159
|
||||
#define ServerOP_AdventureClickDoorError 0x015a
|
||||
#define ServerOP_AdventureLeave 0x015b
|
||||
#define ServerOP_AdventureLeaveReply 0x015c
|
||||
#define ServerOP_AdventureLeaveDeny 0x015d
|
||||
#define ServerOP_AdventureCountUpdate 0x015e
|
||||
#define ServerOP_AdventureZoneData 0x015f
|
||||
#define ServerOP_AdventureAssaCountUpdate 0x0160
|
||||
#define ServerOP_AdventureFinish 0x0161
|
||||
#define ServerOP_AdventureLeaderboard 0x0162
|
||||
|
||||
#define ServerOP_WhoAll 0x0210
|
||||
#define ServerOP_FriendsWho 0x0211
|
||||
#define ServerOP_LFGMatches 0x0212
|
||||
#define ServerOP_LFPUpdate 0x0213
|
||||
#define ServerOP_LFPMatches 0x0214
|
||||
#define ServerOP_ClientVersionSummary 0x0215
|
||||
#define ServerOP_LSInfo 0x1000
|
||||
#define ServerOP_LSStatus 0x1001
|
||||
#define ServerOP_LSClientAuth 0x1002
|
||||
#define ServerOP_LSFatalError 0x1003
|
||||
#define ServerOP_SystemwideMessage 0x1005
|
||||
#define ServerOP_ListWorlds 0x1006
|
||||
#define ServerOP_PeerConnect 0x1007
|
||||
#define ServerOP_NewLSInfo 0x1008
|
||||
#define ServerOP_LSRemoteAddr 0x1009
|
||||
#define ServerOP_LSAccountUpdate 0x100A
|
||||
|
||||
#define ServerOP_EncapPacket 0x2007 // Packet within a packet
|
||||
#define ServerOP_WorldListUpdate 0x2008
|
||||
#define ServerOP_WorldListRemove 0x2009
|
||||
#define ServerOP_TriggerWorldListRefresh 0x200A
|
||||
#define ServerOP_WhoAllReply 0x2010
|
||||
#define ServerOP_SetWorldTime 0x200B
|
||||
#define ServerOP_GetWorldTime 0x200C
|
||||
#define ServerOP_SyncWorldTime 0x200E
|
||||
#define ServerOP_RefreshCensorship 0x200F
|
||||
|
||||
#define ServerOP_LSZoneInfo 0x3001
|
||||
#define ServerOP_LSZoneStart 0x3002
|
||||
#define ServerOP_LSZoneBoot 0x3003
|
||||
#define ServerOP_LSZoneShutdown 0x3004
|
||||
#define ServerOP_LSZoneSleep 0x3005
|
||||
#define ServerOP_LSPlayerLeftWorld 0x3006
|
||||
#define ServerOP_LSPlayerJoinWorld 0x3007
|
||||
#define ServerOP_LSPlayerZoneChange 0x3008
|
||||
|
||||
#define ServerOP_UsertoWorldReq 0xAB00
|
||||
#define ServerOP_UsertoWorldResp 0xAB01
|
||||
|
||||
#define ServerOP_LauncherConnectInfo 0x3000
|
||||
#define ServerOP_LauncherZoneRequest 0x3001
|
||||
#define ServerOP_LauncherZoneStatus 0x3002
|
||||
#define ServerOP_DoZoneCommand 0x3003
|
||||
|
||||
#define ServerOP_UCSMessage 0x4000
|
||||
#define ServerOP_UCSMailMessage 0x4001
|
||||
#define ServerOP_ReloadRules 0x4002
|
||||
#define ServerOP_ReloadRulesWorld 0x4003
|
||||
#define ServerOP_CameraShake 0x4004
|
||||
#define ServerOP_QueryServGeneric 0x4005
|
||||
#define ServerOP_CZSignalClient 0x4006
|
||||
#define ServerOP_CZSignalClientByName 0x4007
|
||||
#define ServerOP_CZMessagePlayer 0x4008
|
||||
#define ServerOP_ReloadWorld 0x4009
|
||||
#define ServerOP_ReloadLogs 0x4010
|
||||
#define ServerOP_ReloadPerlExportSettings 0x4011
|
||||
#define ServerOP_CZSetEntityVariableByClientName 0x4012
|
||||
#define ServerOP_UCSServerStatusRequest 0x4013
|
||||
#define ServerOP_UCSServerStatusReply 0x4014
|
||||
/* Query Server OP Codes */
|
||||
#define ServerOP_QSPlayerLogTrades 0x5010
|
||||
#define ServerOP_QSPlayerLogHandins 0x5011
|
||||
#define ServerOP_QSPlayerLogNPCKills 0x5012
|
||||
#define ServerOP_QSPlayerLogDeletes 0x5013
|
||||
#define ServerOP_QSPlayerLogMoves 0x5014
|
||||
#define ServerOP_QSPlayerLogMerchantTransactions 0x5015
|
||||
#define ServerOP_QSSendQuery 0x5016
|
||||
#define ServerOP_CZSignalNPC 0x5017
|
||||
#define ServerOP_CZSetEntityVariableByNPCTypeID 0x5018
|
||||
#define ServerOP_WWMarquee 0x5019
|
||||
#define ServerOP_QSPlayerDropItem 0x5020
|
||||
|
||||
/* Query Serv Generic Packet Flag/Type Enumeration */
|
||||
enum { QSG_LFGuild = 0 };
|
||||
enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_RequestPlayerInfo, QSG_LFGuild_UpdateGuildInfo, QSG_LFGuild_GuildMatches,
|
||||
QSG_LFGuild_RequestGuildInfo };
|
||||
|
||||
#define ServerOP_Speech 0x4513
|
||||
|
||||
/************ PACKET RELATED STRUCT ************/
|
||||
class ServerPacket
|
||||
{
|
||||
@@ -248,6 +250,19 @@ public:
|
||||
_rpos = 0;
|
||||
}
|
||||
|
||||
ServerPacket(uint16 in_opcode, SerializeBuffer &buf)
|
||||
{
|
||||
compressed = false;
|
||||
size = buf.m_pos;
|
||||
buf.m_pos = 0;
|
||||
opcode = in_opcode;
|
||||
pBuffer = buf.m_buffer;
|
||||
buf.m_buffer = 0;
|
||||
buf.m_capacity = 0;
|
||||
_wpos = 0;
|
||||
_rpos = 0;
|
||||
}
|
||||
|
||||
ServerPacket* Copy() {
|
||||
if (this == 0) {
|
||||
return 0;
|
||||
@@ -1314,6 +1329,27 @@ struct UCSServerStatus_Struct {
|
||||
};
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
// shared task related communications
|
||||
struct ServerSharedTaskMember_Struct { // used for various things we just need the ID and a name (add, remove, etc)
|
||||
uint32 id;
|
||||
char name[64];
|
||||
};
|
||||
// error constants
|
||||
#define TASKJOINOOZ_CAN 0
|
||||
#define TASKJOINOOZ_NOTASK 1
|
||||
#define TASKJOINOOZ_HAVEONE 2
|
||||
#define TASKJOINOOZ_LEVEL 3
|
||||
#define TASKJOINOOZ_TIMER 4
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Routing
|
||||
*/
|
||||
|
||||
struct RouteToMessage
|
||||
{
|
||||
char filter[32];
|
||||
char identifier[32];
|
||||
char id[32];
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "service.h"
|
||||
#include "event/event_loop.h"
|
||||
#include "event/timer.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
struct EQ::Service::Impl
|
||||
{
|
||||
bool running;
|
||||
std::string identifier;
|
||||
size_t heartbeat_duration_ms;
|
||||
size_t sleep_duration_ms;
|
||||
std::unique_ptr<EQ::WorldConnection> world_connection;
|
||||
std::unique_ptr<EQ::Timer> heartbeat_timer;
|
||||
std::chrono::steady_clock::time_point last_time;
|
||||
};
|
||||
|
||||
EQ::Service::Service(const std::string &identifier, size_t heartbeat_duration_ms, size_t sleep_duration_ms)
|
||||
{
|
||||
_impl.reset(new Impl());
|
||||
_impl->running = false;
|
||||
_impl->identifier = identifier;
|
||||
_impl->heartbeat_duration_ms = heartbeat_duration_ms;
|
||||
_impl->sleep_duration_ms = sleep_duration_ms;
|
||||
}
|
||||
|
||||
EQ::Service::~Service()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Service::Run()
|
||||
{
|
||||
_impl->running = true;
|
||||
|
||||
OnStart();
|
||||
|
||||
//If start canceled our run then just quit, dont bother initializing everything else
|
||||
if (!_impl->running) {
|
||||
return;
|
||||
}
|
||||
|
||||
_impl->world_connection.reset(new EQ::WorldConnection(_impl->identifier));
|
||||
_impl->world_connection->SetOnRoutedMessageHandler([this](const std::string& filter, const std::string& identifier, const std::string& id, const EQ::Net::Packet& payload) {
|
||||
OnRoutedMessage(filter, identifier, id, payload);
|
||||
});
|
||||
_impl->last_time = std::chrono::steady_clock::now();
|
||||
|
||||
_impl->heartbeat_timer.reset(new EQ::Timer(_impl->heartbeat_duration_ms, true, [this](EQ::Timer *t) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto time_since = std::chrono::duration_cast<std::chrono::duration<double>>(now - _impl->last_time);
|
||||
OnHeartbeat(time_since.count());
|
||||
_impl->last_time = now;
|
||||
}));
|
||||
|
||||
auto &loop = EQ::EventLoop::Get();
|
||||
auto sleep_duration = _impl->sleep_duration_ms;
|
||||
while (_impl->running) {
|
||||
loop.Process();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_duration));
|
||||
}
|
||||
|
||||
_impl->heartbeat_timer.release();
|
||||
_impl->world_connection.release();
|
||||
|
||||
OnStop();
|
||||
}
|
||||
|
||||
void EQ::Service::RouteMessage(const std::string &filter, const std::string &id, const EQ::Net::Packet &p)
|
||||
{
|
||||
_impl->world_connection->RouteMessage(filter, id, p);
|
||||
}
|
||||
|
||||
void EQ::Service::Stop() {
|
||||
_impl->running = false;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "world_connection.h"
|
||||
#include "net/packet.h"
|
||||
#include "eqemu_logsys.h"
|
||||
#include "platform.h"
|
||||
#include "crash.h"
|
||||
|
||||
#define EQRegisterService(type) EQEmuLogSys LogSys; \
|
||||
int main(int argc, char **argv) { \
|
||||
LogSys.LoadLogSettingsDefaults(); \
|
||||
set_exception_handler(); \
|
||||
type srv; \
|
||||
srv.Run(); \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
class Service
|
||||
{
|
||||
public:
|
||||
Service(const std::string &identifier, size_t heartbeat_duration_ms, size_t sleep_duration);
|
||||
virtual ~Service();
|
||||
|
||||
void Run();
|
||||
|
||||
protected:
|
||||
virtual void OnStart() = 0;
|
||||
virtual void OnStop() = 0;
|
||||
virtual void OnHeartbeat(double time_since_last) = 0;
|
||||
virtual void OnRoutedMessage(const std::string& filter, const std::string& identifier, const std::string& id, const EQ::Net::Packet& payload) = 0;
|
||||
|
||||
void RouteMessage(const std::string &filter, const std::string &id, const EQ::Net::Packet& p);
|
||||
void Stop();
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> _impl;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "world_connection.h"
|
||||
#include "eqemu_config.h"
|
||||
#include "string_util.h"
|
||||
|
||||
EQ::WorldConnection::WorldConnection(const std::string &type) {
|
||||
auto config = EQEmuConfig::get();
|
||||
|
||||
m_connection.reset(new EQ::Net::ServertalkClient(config->WorldIP, config->WorldTCPPort, false, type, config->SharedKey));
|
||||
m_connection->OnConnect([this](EQ::Net::ServertalkClient *client) {
|
||||
if (m_on_connected) {
|
||||
m_on_connected();
|
||||
}
|
||||
});
|
||||
|
||||
m_connection->OnMessage(std::bind(&WorldConnection::_HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_connection->OnMessage(ServerOP_RouteTo, std::bind(&WorldConnection::_HandleRoutedMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
EQ::WorldConnection::~WorldConnection() {
|
||||
|
||||
}
|
||||
|
||||
void EQ::WorldConnection::SendPacket(ServerPacket* pack) {
|
||||
m_connection->SendPacket(pack);
|
||||
}
|
||||
|
||||
std::string EQ::WorldConnection::GetIP() const {
|
||||
return m_connection->Handle()->RemoteIP();
|
||||
}
|
||||
|
||||
uint16 EQ::WorldConnection::GetPort() const {
|
||||
return m_connection->Handle()->RemotePort();
|
||||
}
|
||||
|
||||
bool EQ::WorldConnection::Connected() const {
|
||||
return m_connection->Connected();
|
||||
}
|
||||
|
||||
void EQ::WorldConnection::RouteMessage(const std::string &filter, const std::string &id, const EQ::Net::Packet& payload)
|
||||
{
|
||||
if (!m_connection->Connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto identifier = m_connection->GetIdentifier();
|
||||
|
||||
RouteToMessage msg;
|
||||
strn0cpy(msg.filter, filter.c_str(), 32);
|
||||
strn0cpy(msg.identifier, identifier.c_str(), 32);
|
||||
strn0cpy(msg.id, id.c_str(), 32);
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.Reserve(sizeof(RouteToMessage) + payload.Length());
|
||||
out.PutData(0, &msg, sizeof(RouteToMessage));
|
||||
out.PutPacket(sizeof(RouteToMessage), payload);
|
||||
|
||||
m_connection->Send(ServerOP_RouteTo, out);
|
||||
}
|
||||
|
||||
void EQ::WorldConnection::_HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
{
|
||||
if (m_on_message) {
|
||||
m_on_message(opcode, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::WorldConnection::_HandleRoutedMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
{
|
||||
if (m_on_routed_message) {
|
||||
auto msg = (RouteToMessage*)p.Data();
|
||||
auto payload = p.GetPacket(sizeof(RouteToMessage), p.Length() - sizeof(RouteToMessage));
|
||||
|
||||
m_on_routed_message(msg->filter, msg->identifier, msg->id, payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "eq_packet_structs.h"
|
||||
#include "net/servertalk_client_connection.h"
|
||||
#include <functional>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
class WorldConnection
|
||||
{
|
||||
public:
|
||||
typedef std::function<void()> OnConnectedHandler;
|
||||
typedef std::function<void(uint16, const EQ::Net::Packet&)> OnMessageHandler;
|
||||
typedef std::function<void(const std::string&, const std::string&, const std::string&, const EQ::Net::Packet&)> OnRoutedMessageHandler;
|
||||
|
||||
WorldConnection(const std::string &type);
|
||||
virtual ~WorldConnection();
|
||||
|
||||
void SendPacket(ServerPacket* pack);
|
||||
std::string GetIP() const;
|
||||
uint16 GetPort() const;
|
||||
bool Connected() const;
|
||||
|
||||
void SetOnConnectedHandler(OnConnectedHandler handler) {
|
||||
m_on_connected = handler;
|
||||
};
|
||||
|
||||
void SetOnMessageHandler(OnMessageHandler handler) {
|
||||
m_on_message = handler;
|
||||
};
|
||||
|
||||
void SetOnRoutedMessageHandler(OnRoutedMessageHandler handler) {
|
||||
m_on_routed_message = handler;
|
||||
}
|
||||
|
||||
void RouteMessage(const std::string &filter, const std::string &id, const EQ::Net::Packet& payload);
|
||||
|
||||
protected:
|
||||
OnConnectedHandler m_on_connected;
|
||||
OnMessageHandler m_on_message;
|
||||
OnRoutedMessageHandler m_on_routed_message;
|
||||
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
|
||||
|
||||
private:
|
||||
void _HandleMessage(uint16 opcode, const EQ::Net::Packet& p);
|
||||
void _HandleRoutedMessage(uint16 opcode, const EQ::Net::Packet& p);
|
||||
};
|
||||
}
|
||||
@@ -25,16 +25,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "zone_launch.h"
|
||||
|
||||
WorldServer::WorldServer(std::map<std::string, ZoneLaunch *> &zones, const char *name, const EQEmuConfig *config)
|
||||
: m_name(name),
|
||||
:
|
||||
WorldConnection::WorldConnection("Launcher"),
|
||||
m_name(name),
|
||||
m_config(config),
|
||||
m_zones(zones)
|
||||
{
|
||||
m_connection.reset(new EQ::Net::ServertalkClient(config->WorldIP, config->WorldTCPPort, false, "Launcher", config->SharedKey));
|
||||
m_connection->OnConnect([this](EQ::Net::ServertalkClient *client) {
|
||||
OnConnected();
|
||||
});
|
||||
|
||||
m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
SetOnConnectedHandler(std::bind(&WorldServer::OnConnected, this));
|
||||
SetOnMessageHandler(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
WorldServer::~WorldServer() {
|
||||
@@ -57,19 +55,11 @@ void WorldServer::OnConnected() {
|
||||
}
|
||||
}
|
||||
|
||||
void WorldServer::HandleMessage(uint16 opcode, EQ::Net::Packet &p) {
|
||||
void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
ServerPacket tpack(opcode, p);
|
||||
ServerPacket *pack = &tpack;
|
||||
|
||||
switch (opcode) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case ServerOP_EmoteMessage:
|
||||
case ServerOP_KeepAlive: {
|
||||
// ignore this
|
||||
break;
|
||||
}
|
||||
case ServerOP_LauncherZoneRequest: {
|
||||
if (pack->size != sizeof(LauncherZoneRequest)) {
|
||||
Log(Logs::Detail, Logs::Launcher, "Invalid size of LauncherZoneRequest: %d", pack->size);
|
||||
@@ -126,8 +116,6 @@ void WorldServer::HandleMessage(uint16 opcode, EQ::Net::Packet &p) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WorldServer::SendStatus(const char *short_name, uint32 start_count, bool running) {
|
||||
auto pack = new ServerPacket(ServerOP_LauncherZoneStatus, sizeof(LauncherZoneStatus));
|
||||
LauncherZoneStatus* it = (LauncherZoneStatus*)pack->pBuffer;
|
||||
@@ -138,4 +126,4 @@ void WorldServer::SendStatus(const char *short_name, uint32 start_count, bool ru
|
||||
|
||||
m_connection->SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,28 +18,23 @@
|
||||
#ifndef WORLDSERVER_H
|
||||
#define WORLDSERVER_H
|
||||
|
||||
#include "../common/net/servertalk_client_connection.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "../common/world_connection.h"
|
||||
#include <queue>
|
||||
#include <map>
|
||||
|
||||
class ZoneLaunch;
|
||||
class EQEmuConfig;
|
||||
|
||||
class WorldServer {
|
||||
class WorldServer : public EQ::WorldConnection {
|
||||
public:
|
||||
WorldServer(std::map<std::string, ZoneLaunch *> &zones, const char *name, const EQEmuConfig *config);
|
||||
~WorldServer();
|
||||
|
||||
void HandleMessage(uint16 opcode, EQ::Net::Packet &p);
|
||||
|
||||
void SendStatus(const char *short_name, uint32 start_count, bool running);
|
||||
|
||||
private:
|
||||
virtual void OnConnected();
|
||||
void HandleMessage(uint16 opcode, const EQ::Net::Packet &p);
|
||||
void OnConnected();
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
|
||||
const char *const m_name;
|
||||
const EQEmuConfig *const m_config;
|
||||
std::map<std::string, ZoneLaunch *> &m_zones;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "../common/packet_dump.h"
|
||||
#include "../common/rulesys.h"
|
||||
|
||||
extern WorldServer *worldserver;
|
||||
extern std::unique_ptr<WorldServer> worldserver;
|
||||
extern Database database;
|
||||
|
||||
PlayerLookingForGuild::PlayerLookingForGuild(char *Name, char *Comments, uint32 Level, uint32 Class, uint32 AACount, uint32 Timezone, uint32 TimePosted)
|
||||
|
||||
@@ -39,7 +39,7 @@ Database database;
|
||||
LFGuildManager lfguildmanager;
|
||||
std::string WorldShortName;
|
||||
const queryservconfig *Config;
|
||||
WorldServer *worldserver = 0;
|
||||
std::unique_ptr<WorldServer> worldserver;
|
||||
EQEmuLogSys LogSys;
|
||||
|
||||
void CatchSignal(int sig_num) {
|
||||
@@ -88,8 +88,7 @@ int main() {
|
||||
}
|
||||
|
||||
/* Initial Connection to Worldserver */
|
||||
worldserver = new WorldServer;
|
||||
worldserver->Connect();
|
||||
worldserver.reset(new WorldServer());
|
||||
|
||||
/* Load Looking For Guild Manager */
|
||||
lfguildmanager.LoadDatabase();
|
||||
|
||||
@@ -43,49 +43,18 @@ extern Database database;
|
||||
extern LFGuildManager lfguildmanager;
|
||||
|
||||
WorldServer::WorldServer()
|
||||
: WorldConnection::WorldConnection("QueryServ")
|
||||
{
|
||||
SetOnMessageHandler(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
WorldServer::~WorldServer()
|
||||
{
|
||||
}
|
||||
|
||||
void WorldServer::Connect()
|
||||
{
|
||||
m_connection.reset(new EQ::Net::ServertalkClient(Config->WorldIP, Config->WorldTCPPort, false, "QueryServ", Config->SharedKey));
|
||||
m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
bool WorldServer::SendPacket(ServerPacket *pack)
|
||||
{
|
||||
m_connection->SendPacket(pack);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string WorldServer::GetIP() const
|
||||
{
|
||||
return m_connection->Handle()->RemoteIP();
|
||||
}
|
||||
|
||||
uint16 WorldServer::GetPort() const
|
||||
{
|
||||
return m_connection->Handle()->RemotePort();
|
||||
}
|
||||
|
||||
bool WorldServer::Connected() const
|
||||
{
|
||||
return m_connection->Connected();
|
||||
}
|
||||
|
||||
void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
{
|
||||
switch (opcode) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case ServerOP_KeepAlive: {
|
||||
break;
|
||||
}
|
||||
case ServerOP_Speech: {
|
||||
Server_Speech_Struct *SSS = (Server_Speech_Struct*)p.Data();
|
||||
std::string tmp1 = SSS->from;
|
||||
@@ -185,4 +154,4 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-10
@@ -18,20 +18,13 @@
|
||||
#ifndef WORLDSERVER_H
|
||||
#define WORLDSERVER_H
|
||||
|
||||
#include "../common/eq_packet_structs.h"
|
||||
#include "../common/net/servertalk_client_connection.h"
|
||||
#include "../common/world_connection.h"
|
||||
|
||||
class WorldServer
|
||||
class WorldServer : public EQ::WorldConnection
|
||||
{
|
||||
public:
|
||||
WorldServer();
|
||||
~WorldServer();
|
||||
|
||||
void Connect();
|
||||
bool SendPacket(ServerPacket* pack);
|
||||
std::string GetIP() const;
|
||||
uint16 GetPort() const;
|
||||
bool Connected() const;
|
||||
virtual ~WorldServer();
|
||||
|
||||
void HandleMessage(uint16 opcode, const EQ::Net::Packet &p);
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
|
||||
SET(service_sources
|
||||
tasks_database.cpp
|
||||
tasks_service.cpp
|
||||
)
|
||||
|
||||
SET(service_headers
|
||||
tasks_database.h
|
||||
tasks_service.h
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(tasks_service ${service_sources} ${service_headers})
|
||||
|
||||
INSTALL(TARGETS tasks_service RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
TARGET_LINK_LIBRARIES(tasks_service ${SERVER_LIBS})
|
||||
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "tasks_database.h"
|
||||
|
||||
TasksDatabase::TasksDatabase()
|
||||
: Database()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TasksDatabase::TasksDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port)
|
||||
: Database(host, user, passwd, database, port)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TasksDatabase::~TasksDatabase() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/global_define.h"
|
||||
#include "../../common/types.h"
|
||||
#include "../../common/database.h"
|
||||
|
||||
class TasksDatabase : public Database {
|
||||
public:
|
||||
TasksDatabase();
|
||||
TasksDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port);
|
||||
~TasksDatabase();
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "tasks_service.h"
|
||||
#include "../../common/eqemu_logsys.h"
|
||||
#include "../../common/eqemu_config.h"
|
||||
|
||||
EQ::TasksService::TasksService()
|
||||
: EQ::Service("Tasks", 100, 1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
EQ::TasksService::~TasksService() {
|
||||
|
||||
}
|
||||
|
||||
void EQ::TasksService::OnStart() {
|
||||
Log(Logs::General, Logs::Status, "Connecting to database...");
|
||||
|
||||
auto config = EQEmuConfig::get();
|
||||
|
||||
m_db.reset(new TasksDatabase());
|
||||
|
||||
auto r = m_db->Connect(
|
||||
config->DatabaseHost.c_str(),
|
||||
config->DatabaseUsername.c_str(),
|
||||
config->DatabasePassword.c_str(),
|
||||
config->DatabaseDB.c_str(),
|
||||
config->DatabasePort);
|
||||
|
||||
if (false == r) {
|
||||
Log(Logs::General, Logs::Status, "Unable to connect to database.");
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
Log(Logs::General, Logs::Status, "Connected to database.");
|
||||
|
||||
m_db->LoadLogSettings(LogSys.log_settings);
|
||||
LogSys.StartFileLogs();
|
||||
|
||||
//Load task info here
|
||||
}
|
||||
|
||||
void EQ::TasksService::OnStop() {
|
||||
m_db.release();
|
||||
}
|
||||
|
||||
void EQ::TasksService::OnHeartbeat(double time_since_last) {
|
||||
|
||||
}
|
||||
|
||||
void EQ::TasksService::OnRoutedMessage(const std::string& filter, const std::string& identifier, const std::string& id, const EQ::Net::Packet& payload)
|
||||
{
|
||||
LogF(Logs::General, Logs::Status, "On routed message with payload size {0}", payload.Length());
|
||||
|
||||
//auto msg_type = payload.GetInt32(0);
|
||||
//
|
||||
//switch (msg_type) {
|
||||
//case TaskGetClientTaskState:
|
||||
//{
|
||||
// Log(Logs::General, Logs::Status, "Task state request");
|
||||
// auto req = payload.GetSerialize<GetClientTaskStateRequest>(4);
|
||||
// //Get the task state request
|
||||
// break;
|
||||
//}
|
||||
//default:
|
||||
// break;
|
||||
//}
|
||||
}
|
||||
|
||||
EQRegisterService(EQ::TasksService);
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/service.h"
|
||||
#include "tasks_database.h"
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
class TasksService : public EQ::Service
|
||||
{
|
||||
public:
|
||||
TasksService();
|
||||
virtual ~TasksService();
|
||||
|
||||
protected:
|
||||
virtual void OnStart();
|
||||
virtual void OnStop();
|
||||
virtual void OnHeartbeat(double time_since_last);
|
||||
virtual void OnRoutedMessage(const std::string& filter, const std::string& identifier, const std::string& id, const EQ::Net::Packet& payload);
|
||||
|
||||
private:
|
||||
std::unique_ptr<TasksDatabase> m_db;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
|
||||
SET(service_sources
|
||||
test1_service.cpp
|
||||
)
|
||||
|
||||
SET(service_headers
|
||||
test1_service.h
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(test1_service ${service_sources} ${service_headers})
|
||||
|
||||
INSTALL(TARGETS test1_service RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
TARGET_LINK_LIBRARIES(test1_service ${SERVER_LIBS})
|
||||
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "test1_service.h"
|
||||
#include "../../common/eqemu_logsys.h"
|
||||
#include "../../common/eqemu_config.h"
|
||||
|
||||
EQ::Test1Service::Test1Service()
|
||||
: EQ::Service("Test1", 1, 1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct TestPacket
|
||||
{
|
||||
int64_t f1;
|
||||
int64_t f2;
|
||||
int64_t f3;
|
||||
int64_t f4;
|
||||
int64_t f5;
|
||||
int64_t f6;
|
||||
int64_t f7;
|
||||
char f8[4092];
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(f1, f2, f3, f4, f5, f6, f7, f8);
|
||||
}
|
||||
};
|
||||
|
||||
EQ::Test1Service::~Test1Service() {
|
||||
|
||||
}
|
||||
|
||||
void EQ::Test1Service::OnStart() {
|
||||
|
||||
}
|
||||
|
||||
void EQ::Test1Service::OnStop() {
|
||||
}
|
||||
|
||||
void EQ::Test1Service::OnHeartbeat(double time_since_last) {
|
||||
TestPacket p;
|
||||
p.f1 = 33;
|
||||
p.f2 = 43;
|
||||
p.f3 = 56;
|
||||
p.f4 = 90;
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutInt32(0, 1234);
|
||||
out.PutSerialize(4, p);
|
||||
|
||||
for (int i = 0; i < 250; ++i) {
|
||||
RouteMessage("Test2", "", out);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Test1Service::OnRoutedMessage(const std::string& filter, const std::string& identifier, const std::string& id, const EQ::Net::Packet& payload)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
EQRegisterService(EQ::Test1Service);
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/service.h"
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
class Test1Service : public EQ::Service
|
||||
{
|
||||
public:
|
||||
Test1Service();
|
||||
virtual ~Test1Service();
|
||||
|
||||
protected:
|
||||
virtual void OnStart();
|
||||
virtual void OnStop();
|
||||
virtual void OnHeartbeat(double time_since_last);
|
||||
virtual void OnRoutedMessage(const std::string& filter, const std::string& identifier, const std::string& id, const EQ::Net::Packet& payload);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
|
||||
SET(service_sources
|
||||
test2_service.cpp
|
||||
)
|
||||
|
||||
SET(service_headers
|
||||
test2_service.h
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(test2_service ${service_sources} ${service_headers})
|
||||
|
||||
INSTALL(TARGETS test2_service RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
TARGET_LINK_LIBRARIES(test2_service ${SERVER_LIBS})
|
||||
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "test2_service.h"
|
||||
#include "../../common/eqemu_logsys.h"
|
||||
#include "../../common/eqemu_config.h"
|
||||
|
||||
EQ::Test2Service::Test2Service()
|
||||
: EQ::Service("Test2", 3000, 1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
EQ::Test2Service::~Test2Service() {
|
||||
|
||||
}
|
||||
|
||||
void EQ::Test2Service::OnStart() {
|
||||
bytes = 0;
|
||||
packets = 0;
|
||||
}
|
||||
|
||||
void EQ::Test2Service::OnStop() {
|
||||
}
|
||||
|
||||
void EQ::Test2Service::OnHeartbeat(double time_since_last) {
|
||||
|
||||
auto bytes_per_sec = bytes / time_since_last;
|
||||
auto packets_per_sec = packets / time_since_last;
|
||||
printf("Transfer rate %.2f KB/sec %.2f Packets/sec\n", bytes_per_sec / 1000.0, packets_per_sec);
|
||||
|
||||
bytes = 0;
|
||||
packets = 0;
|
||||
}
|
||||
|
||||
void EQ::Test2Service::OnRoutedMessage(const std::string& filter, const std::string& identifier, const std::string& id, const EQ::Net::Packet& payload)
|
||||
{
|
||||
bytes += sizeof(RouteToMessage);
|
||||
bytes += payload.Length();
|
||||
packets++;
|
||||
}
|
||||
|
||||
EQRegisterService(EQ::Test2Service);
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common/service.h"
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
class Test2Service : public EQ::Service
|
||||
{
|
||||
public:
|
||||
Test2Service();
|
||||
virtual ~Test2Service();
|
||||
|
||||
protected:
|
||||
virtual void OnStart();
|
||||
virtual void OnStop();
|
||||
virtual void OnHeartbeat(double time_since_last);
|
||||
virtual void OnRoutedMessage(const std::string& filter, const std::string& identifier, const std::string& id, const EQ::Net::Packet& payload);
|
||||
|
||||
private:
|
||||
size_t bytes;
|
||||
size_t packets;
|
||||
};
|
||||
}
|
||||
+2
-2
@@ -40,7 +40,7 @@ ChatChannelList *ChannelList;
|
||||
Clientlist *g_Clientlist;
|
||||
EQEmuLogSys LogSys;
|
||||
Database database;
|
||||
WorldServer *worldserver = nullptr;
|
||||
std::unique_ptr<WorldServer> worldserver;
|
||||
|
||||
const ucsconfig *Config;
|
||||
|
||||
@@ -142,7 +142,7 @@ int main() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
worldserver = new WorldServer;
|
||||
worldserver.reset(new WorldServer());
|
||||
|
||||
while(RunLoops) {
|
||||
|
||||
|
||||
+3
-10
@@ -47,16 +47,16 @@ void Client50ToServerSayLink(std::string& serverSayLink, const std::string& clie
|
||||
void Client55ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink);
|
||||
|
||||
WorldServer::WorldServer()
|
||||
: WorldConnection::WorldConnection("UCS")
|
||||
{
|
||||
m_connection.reset(new EQ::Net::ServertalkClient(Config->WorldIP, Config->WorldTCPPort, false, "UCS", Config->SharedKey));
|
||||
m_connection->OnMessage(std::bind(&WorldServer::ProcessMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
SetOnMessageHandler(std::bind(&WorldServer::ProcessMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
WorldServer::~WorldServer()
|
||||
{
|
||||
}
|
||||
|
||||
void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p)
|
||||
void WorldServer::ProcessMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
{
|
||||
ServerPacket tpack(opcode, p);
|
||||
ServerPacket *pack = &tpack;
|
||||
@@ -65,13 +65,6 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p)
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case ServerOP_KeepAlive:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case ServerOP_UCSMessage:
|
||||
{
|
||||
char *Buffer = (char *)pack->pBuffer;
|
||||
|
||||
+3
-9
@@ -18,20 +18,14 @@
|
||||
#ifndef WORLDSERVER_H
|
||||
#define WORLDSERVER_H
|
||||
|
||||
#include "../net/servertalk_client_connection.h"
|
||||
#include "../common/eq_packet_structs.h"
|
||||
#include <memory>
|
||||
#include "../world_connection.h"
|
||||
|
||||
class WorldServer
|
||||
class WorldServer : public EQ::WorldConnection
|
||||
{
|
||||
public:
|
||||
WorldServer();
|
||||
~WorldServer();
|
||||
void ProcessMessage(uint16 opcode, EQ::Net::Packet &);
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
|
||||
void ProcessMessage(uint16 opcode, const EQ::Net::Packet &p);
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -563,6 +563,8 @@ OP_TaskHistoryRequest=0x6cf6
|
||||
OP_TaskHistoryReply=0x25eb
|
||||
OP_DeclineAllTasks=0x0000
|
||||
OP_TaskRequestTimer=0x4b76
|
||||
OP_AcceptNewSharedTask=0x3e5e
|
||||
OP_SharedTaskMemberList=0x4ddb
|
||||
|
||||
# Title opcodes
|
||||
OP_NewTitlesAvailable=0x45d1
|
||||
|
||||
@@ -568,6 +568,8 @@ OP_TaskHistoryRequest=0x5f1c
|
||||
OP_TaskHistoryReply=0x3d05
|
||||
OP_DeclineAllTasks=0x0000
|
||||
OP_TaskRequestTimer=0x7a48
|
||||
OP_AcceptNewSharedTask=0x6646
|
||||
OP_SharedTaskMemberList=0x1e7d
|
||||
|
||||
# Title opcodes
|
||||
OP_NewTitlesAvailable=0x0d32
|
||||
|
||||
@@ -534,6 +534,8 @@ OP_TaskHistoryReply=0x3d2a # C
|
||||
OP_CancelTask=0x726b # C
|
||||
OP_DeclineAllTasks=0x0000 #
|
||||
OP_TaskRequestTimer=0x2e70
|
||||
OP_AcceptNewSharedTask=0x4751
|
||||
OP_SharedTaskMemberList=0x55f4
|
||||
|
||||
|
||||
OP_Shroud=0x6d1f
|
||||
|
||||
@@ -500,7 +500,7 @@ OP_TaskHistoryRequest=0x3035 #
|
||||
OP_TaskHistoryReply=0x3A60 #
|
||||
OP_CancelTask=0x4db6 #Xinu or 0x2c8c or 0x4db6
|
||||
OP_DeclineAllTasks=0x0000 #not sure, 12 bytes
|
||||
OP_TaskMemberList=0x3713
|
||||
#OP_TaskMemberList=0x3713
|
||||
OP_TaskMemberInvite=0x3cde
|
||||
OP_TaskMemberInviteResponse=0x6cab
|
||||
OP_TaskMemberChange=0x354a
|
||||
@@ -510,6 +510,8 @@ OP_TaskRemovePlayer=0x516f
|
||||
OP_TaskPlayerList=0x0ad6
|
||||
OP_TaskQuit=0x2c8c
|
||||
OP_TaskRequestTimer=0x0b08
|
||||
OP_AcceptNewSharedTask=0x5bed
|
||||
OP_SharedTaskMemberList=0x3713
|
||||
|
||||
#Title opcodes
|
||||
OP_NewTitlesAvailable=0x179c #
|
||||
|
||||
@@ -458,7 +458,7 @@ OP_TaskActivityComplete=0x54eb
|
||||
OP_CompletedTasks=0x76a2 # ShowEQ 10/27/05
|
||||
OP_TaskDescription=0x5ef7 # ShowEQ 10/27/05
|
||||
OP_TaskActivity=0x682d # ShowEQ 10/27/05
|
||||
OP_TaskMemberList=0x722f #not sure
|
||||
#OP_TaskMemberList=0x722f #not sure
|
||||
OP_OpenNewTasksWindow=0x5e7c #combined with OP_AvaliableTask I think
|
||||
OP_AvaliableTask=0x0000
|
||||
OP_AcceptNewTask=0x207f
|
||||
@@ -475,6 +475,8 @@ OP_TaskRemovePlayer=0x37b9
|
||||
OP_TaskPlayerList=0x3961
|
||||
OP_TaskQuit=0x35dd
|
||||
OP_TaskRequestTimer=0x6a1d
|
||||
OP_AcceptNewSharedTask=0x194d
|
||||
OP_SharedTaskMemberList=0x722f
|
||||
#task complete related: 0x0000 (24 bytes), 0x0000 (8 bytes), 0x0000 (4 bytes)
|
||||
|
||||
|
||||
|
||||
@@ -557,6 +557,8 @@ OP_TaskHistoryReply=0x4524 # C
|
||||
OP_CancelTask=0x3bf5 # C
|
||||
OP_DeclineAllTasks=0x0000 #
|
||||
OP_TaskRequestTimer=0x719e
|
||||
OP_AcceptNewSharedTask=0x6ded
|
||||
OP_SharedTaskMemberList=0x584e
|
||||
|
||||
# Title opcodes
|
||||
OP_NewTitlesAvailable=0x4b49 # C
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
ALTER TABLE `tasks` ADD `reward_points` INT NOT NULL DEFAULT '0' AFTER `rewardmethod`;
|
||||
ALTER TABLE `tasks` ADD `reward_type` INT NOT NULL DEFAULT '0' AFTER `reward_points`;
|
||||
ALTER TABLE `tasks` ADD `replay_group` INT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `min_players` INT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `max_players` INT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `task_lock_step` INT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `instance_zone_id` INT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `zone_version` INT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `zone_in_zone_id` INT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `zone_in_x` FLOAT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `zone_in_y` FLOAT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `zone_in_object_id` TINYINT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `dest_x` FLOAT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `dest_y` FLOAT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `dest_z` FLOAT NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `tasks` ADD `dest_h` FLOAT NOT NULL DEFAULT '0';
|
||||
CREATE TABLE `task_replay_groups` (
|
||||
`id` INT NOT NULL,
|
||||
`duration` INT NOT NULL,
|
||||
`name` VARCHAR(128) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY(`id`)
|
||||
);
|
||||
CREATE TABLE `character_task_lockouts` (
|
||||
`character_id` INT NOT NULL,
|
||||
`replay_group` INT NOT NULL,
|
||||
`original_id` INT NOT NULL,
|
||||
`timestamp` INT NOT NULL,
|
||||
PRIMARY KEY(`character_id`, `replay_group`)
|
||||
);
|
||||
CREATE TABLE `shared_task_state` (
|
||||
`id` INT NOT NULL,
|
||||
`task_id` INT NOT NULL,
|
||||
`accepted_time` INT NOT NULL,
|
||||
`is_locked` TINYINT NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY(`id`)
|
||||
);
|
||||
CREATE TABLE `shared_task_activities` (
|
||||
`shared_task_id` INT NOT NULL,
|
||||
`activity_id` INT NOT NULL,
|
||||
`done_count` INT NOT NULL,
|
||||
`completed` TINYINT,
|
||||
PRIMARY KEY(`shared_task_id`, `activity_id`)
|
||||
);
|
||||
CREATE TABLE `shared_task_members` (
|
||||
`shared_task_id` INT NOT NULL,
|
||||
`character_id` INT NOT NULL,
|
||||
`character_name` VARCHAR(64) NOT NULL,
|
||||
`is_leader` TINYINT DEFAULT 0,
|
||||
PRIMARY KEY(shared_task_id, character_id)
|
||||
);
|
||||
@@ -15,6 +15,8 @@ SET(world_sources
|
||||
login_server_list.cpp
|
||||
net.cpp
|
||||
queryserv.cpp
|
||||
router.cpp
|
||||
shared_tasks.cpp
|
||||
ucs.cpp
|
||||
web_interface.cpp
|
||||
web_interface_eqw.cpp
|
||||
@@ -42,6 +44,8 @@ SET(world_headers
|
||||
login_server_list.h
|
||||
net.h
|
||||
queryserv.h
|
||||
router.h
|
||||
shared_tasks.h
|
||||
sof_char_create_data.h
|
||||
ucs.h
|
||||
web_interface.h
|
||||
|
||||
@@ -1221,6 +1221,7 @@ void Client::EnterWorld(bool TryBootup) {
|
||||
}
|
||||
|
||||
cle->SetChar(charid, char_name);
|
||||
cle->LoadTaskLockouts();
|
||||
database.UpdateLiveChar(char_name, GetAccountID());
|
||||
|
||||
Log(Logs::General, Logs::World_Server,
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "world_config.h"
|
||||
#include "../common/guilds.h"
|
||||
#include "../common/string_util.h"
|
||||
#include "shared_tasks.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
extern uint32 numplayers;
|
||||
extern LoginServerList loginserverlist;
|
||||
@@ -50,6 +53,8 @@ ClientListEntry::ClientListEntry(uint32 in_id, uint32 iLSID, const char* iLoginN
|
||||
pLFGToLevel = 0;
|
||||
pLFGMatchFilter = false;
|
||||
memset(pLFGComments, 0, 64);
|
||||
shared_task_id = 0;
|
||||
m_shared_task = nullptr;
|
||||
}
|
||||
|
||||
ClientListEntry::ClientListEntry(uint32 in_id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin)
|
||||
@@ -71,6 +76,8 @@ ClientListEntry::ClientListEntry(uint32 in_id, uint32 iAccID, const char* iAccNa
|
||||
pLFGToLevel = 0;
|
||||
pLFGMatchFilter = false;
|
||||
memset(pLFGComments, 0, 64);
|
||||
shared_task_id = 0;
|
||||
m_shared_task = nullptr;
|
||||
}
|
||||
|
||||
ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer* iZS, ServerClientList_Struct* scl, int8 iOnline)
|
||||
@@ -93,6 +100,8 @@ ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer* iZS, ServerClientList
|
||||
pLFGToLevel = 0;
|
||||
pLFGMatchFilter = false;
|
||||
memset(pLFGComments, 0, 64);
|
||||
shared_task_id = 0;
|
||||
m_shared_task = nullptr;
|
||||
|
||||
if (iOnline >= CLE_Status_Zoning)
|
||||
Update(iZS, scl, iOnline);
|
||||
@@ -105,6 +114,8 @@ ClientListEntry::~ClientListEntry() {
|
||||
Camp(); // updates zoneserver's numplayers
|
||||
client_list.RemoveCLEReferances(this);
|
||||
}
|
||||
if (m_shared_task != nullptr)
|
||||
m_shared_task->MemberLeftGame(this);
|
||||
for (auto &elem : tell_queue)
|
||||
safe_delete_array(elem);
|
||||
tell_queue.clear();
|
||||
@@ -248,6 +259,8 @@ void ClientListEntry::ClearVars(bool iAll) {
|
||||
pLFG = 0;
|
||||
gm = 0;
|
||||
pClientVersion = 0;
|
||||
shared_task_id = 0;
|
||||
m_shared_task = nullptr;
|
||||
for (auto &elem : tell_queue)
|
||||
safe_delete_array(elem);
|
||||
tell_queue.clear();
|
||||
@@ -261,6 +274,9 @@ void ClientListEntry::Camp(ZoneServer* iZS) {
|
||||
LSUpdate(pzoneserver);
|
||||
}
|
||||
|
||||
if (m_shared_task != nullptr)
|
||||
m_shared_task->MemberLeftGame(this);
|
||||
|
||||
ClearVars();
|
||||
|
||||
stale = 0;
|
||||
@@ -331,3 +347,72 @@ void ClientListEntry::ProcessTellQueue()
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns expire timestamp
|
||||
*/
|
||||
|
||||
int ClientListEntry::GetTaskLockoutExpire(int id) const
|
||||
{
|
||||
auto it = std::find_if(m_task_replay_timers.begin(), m_task_replay_timers.end(),
|
||||
[id](const TaskTimer &a) { return a.ID == id; });
|
||||
|
||||
if (it != m_task_replay_timers.end())
|
||||
return it->expires;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns seconds until expires
|
||||
* returns <= 0 if expired
|
||||
*/
|
||||
|
||||
int ClientListEntry::GetTaskLockoutTimeLeft(int id) const
|
||||
{
|
||||
auto it = std::find_if(m_task_replay_timers.begin(), m_task_replay_timers.end(),
|
||||
[id](const TaskTimer &a) { return a.ID == id; });
|
||||
|
||||
if (it != m_task_replay_timers.end())
|
||||
return it->expires - time(nullptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleans up expired lockouts from the DB
|
||||
*/
|
||||
bool ClientListEntry::CleanExpiredTaskLockouts() const
|
||||
{
|
||||
std::string query =
|
||||
StringFormat("DELETE FROM `character_task_lockouts` WHERE `character_id` = %i AND `timestamp` > %i", pcharid,
|
||||
Timer::GetCurrentTime());
|
||||
auto results = database.QueryDatabase(query);
|
||||
return results.Success();
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads task lockouts
|
||||
*/
|
||||
bool ClientListEntry::LoadTaskLockouts()
|
||||
{
|
||||
CleanExpiredTaskLockouts();
|
||||
std::string query = StringFormat(
|
||||
"SELECT `replay_group`, `original_id`, `timestamp` FROM `character_task_lockouts` WHERE `character_id` = %i",
|
||||
pcharid);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
if (results.RowCount() > 0) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
TaskTimer t;
|
||||
t.ID = atoi(row[0]);
|
||||
t.original_id = atoi(row[1]);
|
||||
t.expires = atoi(row[2]);
|
||||
m_task_replay_timers.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//#include "../common/eq_packet_structs.h"
|
||||
#include "../common/servertalk.h"
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/global_tasks.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -18,6 +19,7 @@
|
||||
|
||||
class ZoneServer;
|
||||
struct ServerClientList_Struct;
|
||||
class SharedTask;
|
||||
|
||||
class ClientListEntry {
|
||||
public:
|
||||
@@ -88,6 +90,17 @@ public:
|
||||
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
|
||||
void ProcessTellQueue();
|
||||
|
||||
// shared task stuff
|
||||
bool CleanExpiredTaskLockouts() const;
|
||||
bool LoadTaskLockouts();
|
||||
int GetTaskLockoutExpire(int id) const;
|
||||
int GetTaskLockoutTimeLeft(int id) const;
|
||||
inline int GetCurrentSharedTaskID() const { return shared_task_id; }
|
||||
inline void SetCurrentSharedTaskID(int in) { shared_task_id = in; }
|
||||
inline bool HasFreeSharedTaskSlot() const { return shared_task_id == 0; }
|
||||
inline void SetSharedTask(SharedTask *in) { m_shared_task = in; }
|
||||
inline SharedTask *GetSharedTask() const { return m_shared_task; }
|
||||
|
||||
private:
|
||||
void ClearVars(bool iAll = false);
|
||||
|
||||
@@ -129,6 +142,13 @@ private:
|
||||
bool pLFGMatchFilter;
|
||||
char pLFGComments[64];
|
||||
|
||||
// shared task stuff
|
||||
// stub for now
|
||||
int shared_task_id; // ID in the TaskManager
|
||||
SharedTask *m_shared_task; // just for quick reference so we can tell it to clean up our pointer
|
||||
|
||||
std::vector<TaskTimer> m_task_replay_timers;
|
||||
|
||||
// Tell Queue -- really a vector :D
|
||||
std::vector<ServerChannelMessage_Struct *> tell_queue;
|
||||
};
|
||||
|
||||
@@ -72,12 +72,6 @@ void LauncherLink::ProcessMessage(uint16 opcode, EQ::Net::Packet &p)
|
||||
ServerPacket *pack = &tpack;
|
||||
|
||||
switch (opcode) {
|
||||
case 0:
|
||||
break;
|
||||
case ServerOP_KeepAlive: {
|
||||
// ignore this
|
||||
break;
|
||||
}
|
||||
case ServerOP_ZAAuth: {
|
||||
Log(Logs::Detail, Logs::World_Server, "Got authentication from %s when they are already authenticated.", m_name.c_str());
|
||||
break;
|
||||
@@ -296,4 +290,4 @@ void LauncherLink::Shutdown() {
|
||||
auto pack = new ServerPacket(ServerOP_ShutdownAll);
|
||||
SendPacket(pack);
|
||||
delete pack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,8 @@ union semun {
|
||||
#include "queryserv.h"
|
||||
#include "web_interface.h"
|
||||
#include "console.h"
|
||||
#include "shared_tasks.h"
|
||||
#include "router.h"
|
||||
|
||||
#include "../common/net/servertalk_server.h"
|
||||
#include "../zone/data_bucket.h"
|
||||
@@ -103,6 +105,7 @@ bool holdzones = false;
|
||||
const WorldConfig *Config;
|
||||
EQEmuLogSys LogSys;
|
||||
WebInterfaceList web_interface;
|
||||
SharedTaskManager shared_tasks;
|
||||
|
||||
void CatchSignal(int sig_num);
|
||||
void CheckForServerScript(bool force_download = false);
|
||||
@@ -394,6 +397,9 @@ int main(int argc, char** argv) {
|
||||
adventure_manager.Load();
|
||||
adventure_manager.LoadLeaderboardInfo();
|
||||
|
||||
shared_tasks.LoadSharedTasks();
|
||||
shared_tasks.LoadSharedTaskState();
|
||||
|
||||
Log(Logs::General, Logs::World_Server, "Purging expired instances");
|
||||
database.PurgeExpiredInstances();
|
||||
|
||||
@@ -414,6 +420,7 @@ int main(int argc, char** argv) {
|
||||
std::unique_ptr<EQ::Net::ServertalkServer> server_connection;
|
||||
server_connection.reset(new EQ::Net::ServertalkServer());
|
||||
|
||||
Router router;
|
||||
EQ::Net::ServertalkServerOptions server_opts;
|
||||
server_opts.port = Config->WorldTCPPort;
|
||||
server_opts.ipv6 = false;
|
||||
@@ -497,6 +504,20 @@ int main(int argc, char** argv) {
|
||||
web_interface.RemoveConnection(connection);
|
||||
});
|
||||
|
||||
server_connection->OnConnectionIdentified([&router](std::shared_ptr<EQ::Net::ServertalkServerConnection> connection) {
|
||||
LogF(Logs::General, Logs::World_Server, "New connection from {0} with identifier {1}",
|
||||
connection->GetUUID(), connection->GetIdentifier());
|
||||
|
||||
router.AddConnection(connection);
|
||||
});
|
||||
|
||||
server_connection->OnConnectionRemoved([&router](std::shared_ptr<EQ::Net::ServertalkServerConnection> connection) {
|
||||
LogF(Logs::General, Logs::World_Server, "Removed connection from {0} with identifier {1}",
|
||||
connection->GetUUID(), connection->GetIdentifier());
|
||||
|
||||
router.RemoveConnection(connection);
|
||||
});
|
||||
|
||||
EQ::Net::EQStreamManagerOptions opts(9000, false, false);
|
||||
opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS);
|
||||
opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor);
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "router.h"
|
||||
#include "../common/string_util.h"
|
||||
|
||||
Router::Router()
|
||||
{
|
||||
}
|
||||
|
||||
Router::~Router()
|
||||
{
|
||||
}
|
||||
|
||||
void Router::AddConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection)
|
||||
{
|
||||
m_connections.push_back(connection);
|
||||
connection->OnMessage(ServerOP_RouteTo, std::bind(&Router::OnRouterMessage, this, connection, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void Router::RemoveConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection)
|
||||
{
|
||||
auto iter = m_connections.begin();
|
||||
while (iter != m_connections.end()) {
|
||||
if ((*iter) == connection) {
|
||||
m_connections.erase(iter);
|
||||
return;
|
||||
}
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void Router::OnRouterMessage(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection, uint16 opcode, const EQ::Net::Packet &p)
|
||||
{
|
||||
auto msg = (RouteToMessage*)p.Data();
|
||||
char to_id[32];
|
||||
strn0cpy(to_id, msg->id, 32);
|
||||
|
||||
strn0cpy(msg->id, connection->GetUUID().c_str(), 32);
|
||||
|
||||
|
||||
if (to_id[0] != '\0' && msg->filter[0] != '\0') {
|
||||
for (auto &connection : m_connections) {
|
||||
auto id = connection->GetUUID();
|
||||
auto identifier = connection->GetIdentifier();
|
||||
if (strcmp(to_id, id.c_str()) == 0) {
|
||||
connection->Send(ServerOP_RouteTo, p);
|
||||
}
|
||||
else if (strcmp(msg->filter, identifier.c_str()) == 0) {
|
||||
connection->Send(ServerOP_RouteTo, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msg->filter[0] != '\0') {
|
||||
for (auto &connection : m_connections) {
|
||||
auto identifier = connection->GetIdentifier();
|
||||
if (strcmp(msg->filter, identifier.c_str()) == 0) {
|
||||
connection->Send(ServerOP_RouteTo, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (to_id[0] != '\0') {
|
||||
for (auto &connection : m_connections) {
|
||||
auto id = connection->GetUUID();
|
||||
if (strcmp(to_id, id.c_str()) == 0) {
|
||||
connection->Send(ServerOP_RouteTo, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (auto &connection : m_connections) {
|
||||
connection->Send(ServerOP_RouteTo, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/net/servertalk_server_connection.h"
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
class Router
|
||||
{
|
||||
public:
|
||||
Router();
|
||||
~Router();
|
||||
|
||||
void AddConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection);
|
||||
void RemoveConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection);
|
||||
private:
|
||||
std::list<std::shared_ptr<EQ::Net::ServertalkServerConnection>> m_connections;
|
||||
|
||||
void OnRouterMessage(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection, uint16 opcode, const EQ::Net::Packet &p);
|
||||
};
|
||||
@@ -0,0 +1,535 @@
|
||||
#include "../common/string_util.h"
|
||||
#include "cliententry.h"
|
||||
#include "clientlist.h"
|
||||
#include "shared_tasks.h"
|
||||
#include "worlddb.h"
|
||||
#include "zonelist.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
extern ClientList client_list;
|
||||
extern ZSList zoneserver_list;
|
||||
extern SharedTaskManager shared_tasks;
|
||||
|
||||
void SharedTaskManager::HandleTaskRequest(ServerPacket *pack)
|
||||
{
|
||||
if (!pack)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Things done in zone:
|
||||
* Verified we were requesting a shared task
|
||||
* Verified leader has a slot available (guess we should double check this one)
|
||||
* Verified leader met level reqs
|
||||
* Verified repeatable or not completed (not doing that here?)
|
||||
* Verified leader doesn't have a lock out
|
||||
* Verified the group/raid met min/max player counts
|
||||
*/
|
||||
|
||||
char tmp_str[64] = { 0 };
|
||||
int task_id = pack->ReadUInt32();
|
||||
int npc_id = pack->ReadUInt32();
|
||||
pack->ReadString(tmp_str);
|
||||
std::string leader_name = tmp_str;
|
||||
int player_count = pack->ReadUInt32();
|
||||
std::vector<std::string> players;
|
||||
for (int i = 0; i < player_count; ++i) {
|
||||
pack->ReadString(tmp_str);
|
||||
players.push_back(tmp_str);
|
||||
}
|
||||
|
||||
// check if the task exist, we only load shared tasks in world, so we know the type is correct if found
|
||||
auto it = task_information.find(task_id);
|
||||
if (it == task_information.end()) { // not loaded! bad id or not shared task
|
||||
auto pc = client_list.FindCharacter(leader_name.c_str());
|
||||
if (pc) {
|
||||
// failure TODO: appropriate message
|
||||
auto pack = new ServerPacket(ServerOP_TaskReject, leader_name.size() + 1 + 8);
|
||||
pack->WriteUInt32(0); // string ID or just generic fail message
|
||||
pack->WriteUInt32(npc_id);
|
||||
pack->WriteString(leader_name.c_str());
|
||||
zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack);
|
||||
safe_delete(pack);
|
||||
} // oh well
|
||||
return;
|
||||
}
|
||||
|
||||
int id = GetNextID();
|
||||
auto ret = tasks.insert({id, {id, task_id}});
|
||||
if (!ret.second) {
|
||||
auto pc = client_list.FindCharacter(leader_name.c_str());
|
||||
if (pc) {
|
||||
// failure TODO: appropriate message
|
||||
auto pack = new ServerPacket(ServerOP_TaskReject, leader_name.size() + 1 + 8);
|
||||
pack->WriteUInt32(0); // string ID or just generic fail message
|
||||
pack->WriteUInt32(npc_id);
|
||||
pack->WriteString(leader_name.c_str());
|
||||
zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack);
|
||||
safe_delete(pack);
|
||||
} // oh well
|
||||
return;
|
||||
}
|
||||
|
||||
auto cle_leader = client_list.FindCharacter(leader_name.c_str());
|
||||
if (cle_leader == nullptr) {// something went wrong
|
||||
tasks.erase(ret.first);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cle_leader->HasFreeSharedTaskSlot()) { // they have a task already ...
|
||||
tasks.erase(ret.first);
|
||||
return;
|
||||
}
|
||||
|
||||
auto &task = ret.first->second;
|
||||
task.AddMember(leader_name, cle_leader, cle_leader->CharID(), true);
|
||||
|
||||
if (players.empty()) {
|
||||
// send instant success to leader
|
||||
SerializeBuffer buf(10);
|
||||
buf.WriteInt32(id); // shared task's ID
|
||||
buf.WriteInt32(task_id); // ID of the task's data
|
||||
buf.WriteInt32(npc_id); // NPC we're requesting from
|
||||
buf.WriteString(leader_name); // leader's name
|
||||
buf.WriteInt32(0); // member list minus leader
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_TaskGrant, buf);
|
||||
zoneserver_list.SendPacket(cle_leader->zone(), cle_leader->instance(), pack);
|
||||
safe_delete(pack);
|
||||
|
||||
task.SetCLESharedTasks();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &&name : players) {
|
||||
// look up CLEs by name, tell them we need to know if they can be added
|
||||
auto cle = client_list.FindCharacter(name.c_str());
|
||||
if (cle) {
|
||||
// make sure we don't have a shared task already
|
||||
if (!cle->HasFreeSharedTaskSlot()) {
|
||||
// failure TODO: appropriate message
|
||||
auto pack = new ServerPacket(ServerOP_TaskReject, leader_name.size() + 1 + 8);
|
||||
pack->WriteUInt32(0); // string ID or just generic fail message
|
||||
pack->WriteUInt32(npc_id);
|
||||
pack->WriteString(leader_name.c_str());
|
||||
zoneserver_list.SendPacket(cle_leader->zone(), cle_leader->instance(), pack);
|
||||
safe_delete(pack);
|
||||
tasks.erase(ret.first);
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure our level is right
|
||||
if (!AppropriateLevel(task_id, cle->level())) {
|
||||
// failure TODO: appropriate message
|
||||
auto pack = new ServerPacket(ServerOP_TaskReject, leader_name.size() + 1 + 8);
|
||||
pack->WriteUInt32(0); // string ID or just generic fail message
|
||||
pack->WriteUInt32(npc_id);
|
||||
pack->WriteString(leader_name.c_str());
|
||||
zoneserver_list.SendPacket(cle_leader->zone(), cle_leader->instance(), pack);
|
||||
safe_delete(pack);
|
||||
tasks.erase(ret.first);
|
||||
return;
|
||||
}
|
||||
|
||||
// check our lock out timer
|
||||
int expires = cle->GetTaskLockoutExpire(task_id);
|
||||
if ((expires - time(nullptr)) >= 0) {
|
||||
// failure TODO: appropriate message, we need to send the timestamp here
|
||||
auto pack = new ServerPacket(ServerOP_TaskReject, leader_name.size() + 1 + 8);
|
||||
pack->WriteUInt32(0); // string ID or just generic fail message
|
||||
pack->WriteUInt32(npc_id);
|
||||
pack->WriteString(leader_name.c_str());
|
||||
zoneserver_list.SendPacket(cle_leader->zone(), cle_leader->instance(), pack);
|
||||
safe_delete(pack);
|
||||
tasks.erase(ret.first);
|
||||
return;
|
||||
}
|
||||
|
||||
// we're good, add to task
|
||||
task.AddMember(name, cle, cle->CharID());
|
||||
}
|
||||
}
|
||||
|
||||
// this will also prevent any of these clients from requesting or being added to another, lets do it now before we tell zone
|
||||
task.SetCLESharedTasks();
|
||||
task.InitActivities();
|
||||
// fire off to zone we're done!
|
||||
SerializeBuffer buf(10 + 10 * players.size());
|
||||
buf.WriteInt32(id); // shared task's ID
|
||||
buf.WriteInt32(task_id); // ID of the task's data
|
||||
buf.WriteInt32(npc_id); // NPC we're requesting from
|
||||
buf.WriteInt32(task.GetAcceptedTime()); // time we accepted it
|
||||
buf.WriteString(leader_name); // leader's name
|
||||
task.SerializeMembers(buf, false); // everyone but leader
|
||||
|
||||
auto reply = new ServerPacket(ServerOP_TaskGrant, buf);
|
||||
zoneserver_list.SendPacket(cle_leader->zone(), cle_leader->instance(), reply);
|
||||
safe_delete(reply);
|
||||
|
||||
task.Save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Just sends the ID of the task that was successfully created zone side
|
||||
* We now need to tell all the other clients to join the task
|
||||
* We could probably try to find all the clients already in the zone and not
|
||||
* worry about them here, but it's simpler this way
|
||||
*/
|
||||
void SharedTaskManager::HandleTaskZoneCreated(ServerPacket *pack)
|
||||
{
|
||||
if (!pack)
|
||||
return;
|
||||
|
||||
int id = pack->ReadUInt32();
|
||||
|
||||
auto task = GetSharedTask(id);
|
||||
|
||||
if (!task) // hmm guess we should tell zone something is broken TODO
|
||||
return;
|
||||
|
||||
// we reuse this, easier this way
|
||||
auto outpack = new ServerPacket(ServerOP_TaskZoneCreated, sizeof(ServerSharedTaskMember_Struct));
|
||||
auto stm = (ServerSharedTaskMember_Struct *)outpack->pBuffer;
|
||||
stm->id = id;
|
||||
|
||||
for (auto &&m : task->members) {
|
||||
if (m.leader) // leader done!
|
||||
continue;
|
||||
|
||||
if (!m.cle) // hmmm
|
||||
continue;
|
||||
|
||||
if (!m.cle->Server()) // hmm
|
||||
continue;
|
||||
|
||||
strn0cpy(stm->name, m.name.c_str(), 64);
|
||||
zoneserver_list.SendPacket(m.cle->zone(), m.cle->instance(), outpack);
|
||||
}
|
||||
|
||||
safe_delete(outpack);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads in the tasks and task_activity tables
|
||||
* We limit to shared to save some memory
|
||||
* This can be called while reloading tasks (because deving etc)
|
||||
* This data is loaded into the task_information map
|
||||
*/
|
||||
|
||||
bool SharedTaskManager::LoadSharedTasks(int single_task)
|
||||
{
|
||||
std::string query;
|
||||
|
||||
if (single_task == 0) {
|
||||
query =
|
||||
StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, `reward`, "
|
||||
"`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`, `minlevel`, "
|
||||
"`maxlevel`, `repeatable`, `completion_emote`, `reward_points`, `reward_type`, "
|
||||
"`replay_group`, `min_players`, `max_players`, `task_lock_step`, `instance_zone_id`, "
|
||||
"`zone_version`, `zone_in_zone_id`, `zone_in_x`, `zone_in_y`, `zone_in_object_id`, "
|
||||
"`dest_x`, `dest_y`, `dest_z`, `dest_h` FROM `tasks` WHERE `type` = %i",
|
||||
static_cast<int>(TaskType::Shared));
|
||||
} else {
|
||||
query =
|
||||
StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, `reward`, "
|
||||
"`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`, `minlevel`, "
|
||||
"`maxlevel`, `repeatable`, `completion_emote`, `reward_points`, `reward_type`, "
|
||||
"`replay_group`, `min_players`, `max_players`, `task_lock_step`, `instance_zone_id`, "
|
||||
"`zone_version`, `zone_in_zone_id`, `zone_in_x`, `zone_in_y`, `zone_in_object_id`, "
|
||||
"`dest_x`, `dest_y`, `dest_z`, `dest_h` FROM `tasks` WHERE `id` = %i AND `type` = %i",
|
||||
single_task, static_cast<int>(TaskType::Shared));
|
||||
}
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
int task_id = atoi(row[0]);
|
||||
|
||||
auto &task = task_information[task_id];
|
||||
task.type = static_cast<TaskType>(atoi(row[1]));
|
||||
task.Duration = atoi(row[2]);
|
||||
task.dur_code = static_cast<DurationCode>(atoi(row[3]));
|
||||
task.Title = row[4];
|
||||
task.Description = row[5];
|
||||
task.Reward = row[6];
|
||||
task.RewardID = atoi(row[7]);
|
||||
task.CashReward = atoi(row[8]);
|
||||
task.XPReward = atoi(row[9]);
|
||||
task.RewardMethod = (TaskMethodType)atoi(row[10]);
|
||||
task.faction_reward = atoi(row[11]);
|
||||
task.MinLevel = atoi(row[12]);
|
||||
task.MaxLevel = atoi(row[13]);
|
||||
task.Repeatable = atoi(row[14]);
|
||||
task.completion_emote = row[15];
|
||||
task.reward_points = atoi(row[16]);
|
||||
task.reward_type = static_cast<PointType>(atoi(row[17]));
|
||||
task.replay_group = atoi(row[18]);
|
||||
task.min_players = atoi(row[19]);
|
||||
task.max_players = atoi(row[20]);
|
||||
task.task_lock_step = atoi(row[21]);
|
||||
task.instance_zone_id = atoi(row[22]);
|
||||
task.zone_version = atoi(row[23]);
|
||||
task.zone_in_zone_id = atoi(row[24]);
|
||||
task.zone_in_x = atof(row[25]);
|
||||
task.zone_in_y = atof(row[26]);
|
||||
task.zone_in_object_id = atoi(row[27]);
|
||||
task.dest_x = atof(row[28]);
|
||||
task.dest_y = atof(row[29]);
|
||||
task.dest_z = atof(row[30]);
|
||||
task.dest_h = atof(row[31]);
|
||||
task.ActivityCount = 0;
|
||||
task.SequenceMode = ActivitiesSequential;
|
||||
task.LastStep = 0;
|
||||
}
|
||||
|
||||
// hmm need to limit to shared tasks only ...
|
||||
if (single_task == 0)
|
||||
query = StringFormat(
|
||||
"SELECT `taskid`, `step`, `activityid`, `activitytype`, `target_name`, `item_list`, `skill_list`, "
|
||||
"`spell_list`, `description_override`, `goalid`, `goalmethod`, `goalcount`, `delivertonpc`, "
|
||||
"`zones`, `optional` FROM `task_activities` WHERE `activityid` < %i AND `taskid` IN (SELECT `id` "
|
||||
"FROM `tasks` WHERE `type` = %i) ORDER BY taskid, activityid ASC",
|
||||
MAXACTIVITIESPERTASK, static_cast<int>(TaskType::Shared));
|
||||
else
|
||||
query = StringFormat(
|
||||
"SELECT `taskid`, `step`, `activityid`, `activitytype`, `target_name`, `item_list`, `skill_list`, "
|
||||
"`spell_list`, `description_override`, `goalid`, `goalmethod`, `goalcount`, `delivertonpc`, "
|
||||
"`zones`, `optional` FROM `task_activities` WHERE `taskid` = %i AND `activityid` < %i AND `taskid` "
|
||||
"IN (SELECT `id` FROM `tasks` WHERE `type` = %i) ORDER BY taskid, activityid ASC",
|
||||
single_task, MAXACTIVITIESPERTASK, static_cast<int>(TaskType::Shared));
|
||||
results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
int task_id = atoi(row[0]);
|
||||
int step = atoi(row[1]);
|
||||
|
||||
int activity_id = atoi(row[2]);
|
||||
|
||||
if (activity_id < 0 || activity_id >= MAXACTIVITIESPERTASK) {
|
||||
// This shouldn't happen, as the SELECT is bounded by MAXTASKS
|
||||
continue;
|
||||
}
|
||||
|
||||
if (task_information.count(task_id) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &task = task_information[task_id];
|
||||
|
||||
task.Activity[task.ActivityCount].StepNumber = step;
|
||||
|
||||
if (step != 0)
|
||||
task.SequenceMode = ActivitiesStepped;
|
||||
|
||||
if (step > task.LastStep)
|
||||
task.LastStep = step;
|
||||
|
||||
// Task Activities MUST be numbered sequentially from 0. If not, log an error
|
||||
// and set the task to nullptr. Subsequent activities for this task will raise
|
||||
// ERR_NOTASK errors.
|
||||
// Change to (activityID != (task.ActivityCount + 1)) to index from 1
|
||||
if (activity_id != task.ActivityCount) {
|
||||
task_information.erase(task_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
task.Activity[task.ActivityCount].Type = atoi(row[3]);
|
||||
|
||||
task.Activity[task.ActivityCount].target_name = row[4];
|
||||
task.Activity[task.ActivityCount].item_list = row[5];
|
||||
task.Activity[task.ActivityCount].skill_list = row[6];
|
||||
task.Activity[task.ActivityCount].skill_id = atoi(row[6]); // for older clients
|
||||
task.Activity[task.ActivityCount].spell_list = row[7];
|
||||
task.Activity[task.ActivityCount].spell_id = atoi(row[7]); // for older clients
|
||||
task.Activity[task.ActivityCount].desc_override = row[8];
|
||||
|
||||
task.Activity[task.ActivityCount].GoalID = atoi(row[9]);
|
||||
task.Activity[task.ActivityCount].GoalMethod = (TaskMethodType)atoi(row[10]);
|
||||
task.Activity[task.ActivityCount].GoalCount = atoi(row[11]);
|
||||
task.Activity[task.ActivityCount].DeliverToNPC = atoi(row[12]);
|
||||
task.Activity[task.ActivityCount].zones = row[13];
|
||||
auto zones = SplitString(task.Activity[task.ActivityCount].zones, ';');
|
||||
for (auto && e : zones)
|
||||
task.Activity[task.ActivityCount].ZoneIDs.push_back(std::stoi(e));
|
||||
task.Activity[task.ActivityCount].Optional = atoi(row[14]);
|
||||
|
||||
task.ActivityCount++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called once during boot of world
|
||||
* We need to load next_id, clean up expired tasks (?), and populate the map
|
||||
*/
|
||||
bool SharedTaskManager::LoadSharedTaskState()
|
||||
{
|
||||
// one may think we should clean up expired tasks, but we don't just in case world is booting back up after a crash
|
||||
// we will clean them up in the normal process loop so zones get told to clean up
|
||||
std::string query = "SELECT `id`, `task_id`, `accepted_time`, `is_locked` FROM `shared_task_state`";
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
if (results.Success() && results.RowCount() > 0) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
int id = atoi(row[0]);
|
||||
|
||||
auto &task = tasks[id];
|
||||
task.SetID(id);
|
||||
task.SetTaskID(atoi(row[1]));
|
||||
task.SetAcceptedTime(atoi(row[2]));
|
||||
task.SetLocked(atoi(row[3]) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
query = "SELECT `shared_task_id`, `character_id`, `character_name`, `is_leader` FROM `shared_task_members` ORDER BY shared_task_id ASC";
|
||||
results = database.QueryDatabase(query);
|
||||
if (results.Success() && results.RowCount() > 0) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
int task_id = atoi(row[0]);
|
||||
// hmm not sure best way to do this, fine for now
|
||||
if (tasks.count(task_id) == 1)
|
||||
tasks[task_id].AddMember(row[2], nullptr, atoi(row[1]), atoi(row[3]) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Load existing tasks. We may not want to actually do this here and wait for a client to log in
|
||||
// But the crash case may actually dictate we should :P
|
||||
|
||||
// set next_id to highest used ID
|
||||
query = "SELECT IFNULL(MAX(id), 0) FROM shared_task_state";
|
||||
results = database.QueryDatabase(query);
|
||||
if (results.Success() && results.RowCount() == 1) {
|
||||
auto row = results.begin();
|
||||
next_id = atoi(row[0]);
|
||||
} else {
|
||||
next_id = 0; // oh well
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the next unused ID
|
||||
* Hopefully this does not grow too large.
|
||||
*/
|
||||
int SharedTaskManager::GetNextID()
|
||||
{
|
||||
next_id++;
|
||||
// let's not be extra clever here ...
|
||||
while (tasks.count(next_id) != 0)
|
||||
next_id++;
|
||||
|
||||
return next_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns true if the level fits in the task's defined range
|
||||
*/
|
||||
bool SharedTaskManager::AppropriateLevel(int id, int level) const
|
||||
{
|
||||
auto it = task_information.find(id);
|
||||
// doesn't exist
|
||||
if (it == task_information.end())
|
||||
return false;
|
||||
|
||||
auto &task = it->second;
|
||||
|
||||
if (task.MinLevel && level < task.MinLevel)
|
||||
return false;
|
||||
|
||||
if (task.MaxLevel && level > task.MaxLevel)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This will check if any tasks have expired
|
||||
*/
|
||||
void SharedTaskManager::Process()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* When a player leaves world they will tell us to clean up their pointer
|
||||
* This is NOT leaving the shared task, just crashed or something
|
||||
*/
|
||||
|
||||
void SharedTask::MemberLeftGame(ClientListEntry *cle)
|
||||
{
|
||||
auto it = std::find_if(members.begin(), members.end(), [cle](SharedTaskMember &m) { return m.cle == cle; });
|
||||
|
||||
// ahh okay ...
|
||||
if (it == members.end())
|
||||
return;
|
||||
|
||||
it->cle = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Serializes Members into the SerializeBuffer
|
||||
* Starts with count then followed by names null-termed
|
||||
* In the future this will include monster mission shit
|
||||
* This should probably send the SharedMember struct or something more like it, fine for now
|
||||
*/
|
||||
void SharedTask::SerializeMembers(SerializeBuffer &buf, bool include_leader) const
|
||||
{
|
||||
buf.WriteInt32(include_leader ? members.size() : members.size() - 1);
|
||||
|
||||
for (auto && m : members) {
|
||||
if (!include_leader && m.leader)
|
||||
continue;
|
||||
|
||||
buf.WriteString(m.name);
|
||||
// TODO: live also has monster mission class choice in here
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This sets the CLE's quick look up shared task stuff
|
||||
*/
|
||||
void SharedTask::SetCLESharedTasks()
|
||||
{
|
||||
for (auto &&m : members) {
|
||||
if (m.cle == nullptr) // shouldn't happen ....
|
||||
continue;
|
||||
|
||||
m.cle->SetSharedTask(this);
|
||||
m.cle->SetCurrentSharedTaskID(id);
|
||||
}
|
||||
}
|
||||
|
||||
void SharedTask::Save() const
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* sets up activity stuff
|
||||
*/
|
||||
void SharedTask::InitActivities()
|
||||
{
|
||||
task_state.TaskID = task_id;
|
||||
task_state.AcceptedTime = time(nullptr);
|
||||
task_state.Updated = true;
|
||||
task_state.CurrentStep = -1;
|
||||
|
||||
for (int i = 0; i < shared_tasks.GetTaskActivityCount(task_id); i++) {
|
||||
task_state.Activity[i].ActivityID = i;
|
||||
task_state.Activity[i].DoneCount = 0;
|
||||
task_state.Activity[i].State = ActivityHidden;
|
||||
task_state.Activity[i].Updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool SharedTask::UnlockActivities()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#ifndef SHARED_TASKS_H
|
||||
#define SHARED_TASKS_H
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../common/servertalk.h"
|
||||
#include "../common/global_tasks.h"
|
||||
#include "cliententry.h"
|
||||
|
||||
class ClientListEntry;
|
||||
|
||||
struct SharedTaskMember {
|
||||
std::string name;
|
||||
ClientListEntry *cle;
|
||||
int char_id;
|
||||
bool leader;
|
||||
// TODO: monster mission stuff
|
||||
SharedTaskMember() : cle(nullptr), char_id(0), leader(false) {}
|
||||
SharedTaskMember(std::string name, ClientListEntry *cle, int char_id, bool leader)
|
||||
: name(name), cle(cle), char_id(char_id), leader(leader)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class SharedTask {
|
||||
public:
|
||||
SharedTask() : id(0), task_id(0), locked(false) {}
|
||||
SharedTask(int id, int task_id) : id(id), task_id(task_id), locked(false) {}
|
||||
~SharedTask() {}
|
||||
|
||||
void AddMember(std::string name, ClientListEntry *cle = nullptr, int char_id = 0, bool leader = false)
|
||||
{
|
||||
members.push_back({name, cle, char_id, leader});
|
||||
if (leader)
|
||||
leader_name = name;
|
||||
if (char_id == 0)
|
||||
return;
|
||||
auto it = std::find(char_ids.begin(), char_ids.end(), char_id);
|
||||
if (it == char_ids.end())
|
||||
char_ids.push_back(char_id);
|
||||
}
|
||||
void MemberLeftGame(ClientListEntry *cle);
|
||||
inline const std::string &GetLeaderName() const { return leader_name; }
|
||||
inline SharedTaskMember *GetLeader() {
|
||||
auto it = std::find_if(members.begin(), members.end(), [](const SharedTaskMember &m) { return m.leader; });
|
||||
if (it != members.end())
|
||||
return &(*it);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SerializeMembers(SerializeBuffer &buf, bool include_leader = true) const;
|
||||
void SetCLESharedTasks();
|
||||
void InitActivities();
|
||||
bool UnlockActivities();
|
||||
|
||||
void Save() const; // save to database
|
||||
|
||||
private:
|
||||
inline void SetID(int in) { id = in; }
|
||||
inline void SetTaskID(int in) { task_id = in; }
|
||||
inline void SetAcceptedTime(int in) { task_state.AcceptedTime = in; }
|
||||
inline void SetLocked(bool in) { locked = in; }
|
||||
|
||||
inline int GetAcceptedTime() const { return task_state.AcceptedTime; }
|
||||
int id; // id we have in our map
|
||||
int task_id; // ID of the task we're on
|
||||
bool locked;
|
||||
std::string leader_name;
|
||||
std::vector<SharedTaskMember> members;
|
||||
std::vector<int> char_ids; // every char id of someone to be locked out, different in case they leave/removed
|
||||
ClientTaskInformation task_state; // book keeping
|
||||
|
||||
friend class SharedTaskManager;
|
||||
};
|
||||
|
||||
class SharedTaskManager {
|
||||
public:
|
||||
SharedTaskManager() : next_id(0) {}
|
||||
~SharedTaskManager() {}
|
||||
|
||||
bool LoadSharedTaskState();
|
||||
bool LoadSharedTasks(int single_task = 0);
|
||||
|
||||
bool AppropriateLevel(int id, int level) const;
|
||||
|
||||
inline SharedTask *GetSharedTask(int id) {
|
||||
auto it = tasks.find(id);
|
||||
if (it != tasks.end())
|
||||
return &it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline int GetTaskActivityCount(int task_id) const {
|
||||
auto it = task_information.find(task_id);
|
||||
if (it != task_information.end())
|
||||
return it->second.ActivityCount;
|
||||
else
|
||||
return 0; // hmm
|
||||
}
|
||||
|
||||
// IPC packet processing
|
||||
void HandleTaskRequest(ServerPacket *pack);
|
||||
void HandleTaskZoneCreated(ServerPacket *pack);
|
||||
|
||||
void Process();
|
||||
|
||||
private:
|
||||
int GetNextID();
|
||||
int next_id;
|
||||
std::unordered_map<int, SharedTask> tasks; // current active shared task states
|
||||
std::unordered_map<int, TaskInformation> task_information; // task info shit
|
||||
};
|
||||
|
||||
#endif /* !SHARED_TASKS_H */
|
||||
@@ -36,14 +36,6 @@ void UCSConnection::ProcessPacket(uint16 opcode, EQ::Net::Packet &p)
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case ServerOP_KeepAlive:
|
||||
{
|
||||
// ignore this
|
||||
break;
|
||||
}
|
||||
case ServerOP_ZAAuth:
|
||||
{
|
||||
Log(Logs::Detail, Logs::UCS_Server, "Got authentication from UCS when they are already authenticated.");
|
||||
|
||||
@@ -43,7 +43,6 @@ ZSList::ZSList()
|
||||
memset(pLockedZones, 0, sizeof(pLockedZones));
|
||||
|
||||
m_tick.reset(new EQ::Timer(5000, true, std::bind(&ZSList::OnTick, this, std::placeholders::_1)));
|
||||
m_keepalive.reset(new EQ::Timer(2500, true, std::bind(&ZSList::OnKeepAlive, this, std::placeholders::_1)));
|
||||
}
|
||||
|
||||
ZSList::~ZSList() {
|
||||
@@ -748,9 +747,3 @@ void ZSList::OnTick(EQ::Timer *t)
|
||||
web_interface.SendEvent(out);
|
||||
}
|
||||
|
||||
void ZSList::OnKeepAlive(EQ::Timer *t)
|
||||
{
|
||||
for (auto &zone : list) {
|
||||
zone->SendKeepAlive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ public:
|
||||
|
||||
private:
|
||||
void OnTick(EQ::Timer *t);
|
||||
void OnKeepAlive(EQ::Timer *t);
|
||||
uint32 NextID;
|
||||
std::list<std::unique_ptr<ZoneServer>> list;
|
||||
uint16 pLockedZones[MaxLockedZones];
|
||||
|
||||
+12
-13
@@ -35,6 +35,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "adventure_manager.h"
|
||||
#include "ucs.h"
|
||||
#include "queryserv.h"
|
||||
#include "shared_tasks.h"
|
||||
|
||||
extern ClientList client_list;
|
||||
extern GroupLFPList LFPGroupList;
|
||||
@@ -45,6 +46,7 @@ extern volatile bool UCSServerAvailable_;
|
||||
extern AdventureManager adventure_manager;
|
||||
extern UCSConnection UCSLink;
|
||||
extern QueryServConnection QSLink;
|
||||
extern SharedTaskManager shared_tasks;
|
||||
void CatchSignal(int sig_num);
|
||||
|
||||
ZoneServer::ZoneServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection, EQ::Net::ConsoleServer *console)
|
||||
@@ -186,12 +188,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
ServerPacket *pack = &tpack;
|
||||
|
||||
switch (opcode) {
|
||||
case 0:
|
||||
break;
|
||||
case ServerOP_KeepAlive: {
|
||||
// ignore this
|
||||
break;
|
||||
}
|
||||
case ServerOP_ZAAuth: {
|
||||
break;
|
||||
}
|
||||
@@ -1349,6 +1345,16 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
cle->ProcessTellQueue();
|
||||
break;
|
||||
}
|
||||
case ServerOP_TaskRequest:
|
||||
{
|
||||
shared_tasks.HandleTaskRequest(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_TaskZoneCreated:
|
||||
{
|
||||
shared_tasks.HandleTaskZoneCreated(pack);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Log(Logs::Detail, Logs::World_Server, "Unknown ServerOPcode from zone 0x%04x, size %d", pack->opcode, pack->size);
|
||||
@@ -1405,13 +1411,6 @@ void ZoneServer::SendGroupIDs() {
|
||||
delete pack;
|
||||
}
|
||||
|
||||
|
||||
void ZoneServer::SendKeepAlive()
|
||||
{
|
||||
ServerPacket pack(ServerOP_KeepAlive, 0);
|
||||
SendPacket(&pack);
|
||||
}
|
||||
|
||||
void ZoneServer::ChangeWID(uint32 iCharID, uint32 iWID) {
|
||||
auto pack = new ServerPacket(ServerOP_ChangeWID, sizeof(ServerChangeWID_Struct));
|
||||
ServerChangeWID_Struct* scw = (ServerChangeWID_Struct*)pack->pBuffer;
|
||||
|
||||
@@ -39,7 +39,6 @@ public:
|
||||
void SendPacket(ServerPacket* pack) { tcpc->SendPacket(pack); }
|
||||
void SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...);
|
||||
void SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message);
|
||||
void SendKeepAlive();
|
||||
bool SetZone(uint32 iZoneID, uint32 iInstanceID = 0, bool iStaticZone = false);
|
||||
void TriggerBootup(uint32 iZoneID = 0, uint32 iInstanceID = 0, const char* iAdminName = 0, bool iMakeStatic = false);
|
||||
void Disconnect() { auto handle = tcpc->Handle(); if (handle) { handle->Disconnect(); } }
|
||||
|
||||
@@ -1877,6 +1877,10 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk
|
||||
if (r)
|
||||
r->MemberZoned(this);
|
||||
|
||||
auto shared_task = GetSharedTask();
|
||||
if (shared_task)
|
||||
shared_task->MemberZoned(this);
|
||||
|
||||
dead_timer.Start(5000, true);
|
||||
m_pp.zone_id = m_pp.binds[0].zoneId;
|
||||
m_pp.zoneInstance = m_pp.binds[0].instance_id;
|
||||
|
||||
@@ -3396,6 +3396,11 @@ void Client::LinkDead()
|
||||
if(raid){
|
||||
raid->MemberZoned(this);
|
||||
}
|
||||
|
||||
auto shared_task = GetSharedTask();
|
||||
if (shared_task)
|
||||
shared_task->MemberZoned(this);
|
||||
|
||||
// save_timer.Start(2500);
|
||||
linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS));
|
||||
SendAppearancePacket(AT_Linkdead, 1);
|
||||
|
||||
+7
-2
@@ -1021,13 +1021,15 @@ public:
|
||||
inline void UpdateTasksOnExplore(int ExploreID) { if(taskstate) taskstate->UpdateTasksOnExplore(this, ExploreID); }
|
||||
inline bool UpdateTasksOnSpeakWith(int NPCTypeID) { if(taskstate) return taskstate->UpdateTasksOnSpeakWith(this, NPCTypeID); else return false; }
|
||||
inline bool UpdateTasksOnDeliver(std::list<EQEmu::ItemInstance*>& Items, int Cash, int NPCTypeID) { if (taskstate) return taskstate->UpdateTasksOnDeliver(this, Items, Cash, NPCTypeID); else return false; }
|
||||
inline void TaskSetSelector(Mob *mob, int TaskSetID) { if(taskmanager) taskmanager->TaskSetSelector(this, taskstate, mob, TaskSetID); }
|
||||
inline void TaskQuestSetSelector(Mob *mob, int count, int *tasks) { if(taskmanager) taskmanager->TaskQuestSetSelector(this, taskstate, mob, count, tasks); }
|
||||
inline void TaskSetSelector(Mob *mob, int TaskSetID, bool shared = false) { if(taskmanager) taskmanager->TaskSetSelector(this, taskstate, mob, TaskSetID, shared); }
|
||||
inline void TaskQuestSetSelector(Mob *mob, int count, int *tasks, bool shared = false) { if(taskmanager) taskmanager->TaskQuestSetSelector(this, taskstate, mob, count, tasks, shared); }
|
||||
inline void EnableTask(int TaskCount, int *TaskList) { if(taskstate) taskstate->EnableTask(CharacterID(), TaskCount, TaskList); }
|
||||
inline void DisableTask(int TaskCount, int *TaskList) { if(taskstate) taskstate->DisableTask(CharacterID(), TaskCount, TaskList); }
|
||||
inline bool IsTaskEnabled(int TaskID) { return (taskstate ? taskstate->IsTaskEnabled(TaskID) : false); }
|
||||
inline void ProcessTaskProximities(float X, float Y, float Z) { if(taskstate) taskstate->ProcessTaskProximities(this, X, Y, Z); }
|
||||
inline void AssignTask(int TaskID, int NPCID, bool enforce_level_requirement = false) { if (taskstate) taskstate->AcceptNewTask(this, TaskID, NPCID, enforce_level_requirement); }
|
||||
inline void AssignSharedTask(int TaskID, int NPCID, int id, int accepted_time, std::vector<std::string> &members) { if (taskstate) taskstate->AcceptNewSharedTask(this, TaskID, NPCID, id, accepted_time, members); }
|
||||
inline void AddToSharedTask(int TaskID) { if (taskstate) taskstate->AddToSharedTask(this, TaskID); }
|
||||
inline int ActiveSpeakTask(int NPCID) { if(taskstate) return taskstate->ActiveSpeakTask(NPCID); else return 0; }
|
||||
inline int ActiveSpeakActivity(int NPCID, int TaskID) { if(taskstate) return taskstate->ActiveSpeakActivity(NPCID, TaskID); else return 0; }
|
||||
inline void FailTask(int TaskID) { if(taskstate) taskstate->FailTask(this, TaskID); }
|
||||
@@ -1044,6 +1046,9 @@ public:
|
||||
inline int GetTaskActivityDoneCountFromTaskID(int TaskID, int ActivityID) { return (taskstate ? taskstate->GetTaskActivityDoneCountFromTaskID(TaskID, ActivityID) :0); }
|
||||
inline int ActiveTasksInSet(int TaskSet) { return (taskstate ? taskstate->ActiveTasksInSet(TaskSet) :0); }
|
||||
inline int CompletedTasksInSet(int TaskSet) { return (taskstate ? taskstate->CompletedTasksInSet(TaskSet) :0); }
|
||||
inline int GetTaskLockoutExpire(int id) { return 0; } // stub
|
||||
inline int GetTaskLockoutTimeLeft(int id) { return 0; } // stub
|
||||
inline SharedTaskState *GetSharedTask() { return taskstate ? taskstate->GetSharedTask() : nullptr; }
|
||||
|
||||
inline const EQEmu::versions::ClientVersion ClientVersion() const { return m_ClientVersion; }
|
||||
inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; }
|
||||
|
||||
@@ -115,6 +115,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_0x0193] = &Client::Handle_0x0193;
|
||||
ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction;
|
||||
ConnectedOpcodes[OP_AcceptNewTask] = &Client::Handle_OP_AcceptNewTask;
|
||||
ConnectedOpcodes[OP_AcceptNewSharedTask] = &Client::Handle_OP_AcceptNewSharedTask;
|
||||
ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest;
|
||||
ConnectedOpcodes[OP_AdventureLeaderboardRequest] = &Client::Handle_OP_AdventureLeaderboardRequest;
|
||||
ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase;
|
||||
@@ -1787,6 +1788,20 @@ void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app)
|
||||
taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id);
|
||||
}
|
||||
|
||||
void Client::Handle_OP_AcceptNewSharedTask(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size != sizeof(AcceptNewSharedTask_Struct)) {
|
||||
Log(Logs::General, Logs::None, "Size mismatch in OP_AcceptNewSharedTask expected %i got %i",
|
||||
sizeof(AcceptNewSharedTask_Struct), app->size);
|
||||
DumpPacket(app);
|
||||
return;
|
||||
}
|
||||
auto *ant = (AcceptNewSharedTask_Struct*)app->pBuffer;
|
||||
|
||||
if (ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate)
|
||||
taskstate->RequestSharedTask(this, ant->task_id, ant->task_master_id);
|
||||
}
|
||||
|
||||
void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size < sizeof(EntityId_Struct))
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
void Handle_0x01e7(const EQApplicationPacket *app);
|
||||
void Handle_OP_AAAction(const EQApplicationPacket *app);
|
||||
void Handle_OP_AcceptNewTask(const EQApplicationPacket *app);
|
||||
void Handle_OP_AcceptNewSharedTask(const EQApplicationPacket *app);
|
||||
void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app);
|
||||
void Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app);
|
||||
void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app);
|
||||
|
||||
@@ -149,6 +149,9 @@ bool Client::Process() {
|
||||
{
|
||||
myraid->MemberZoned(this);
|
||||
}
|
||||
auto shared_task = GetSharedTask();
|
||||
if (shared_task)
|
||||
shared_task->MemberZoned(this);
|
||||
return(false);
|
||||
}
|
||||
|
||||
@@ -171,6 +174,9 @@ bool Client::Process() {
|
||||
if (myraid) {
|
||||
myraid->MemberZoned(this);
|
||||
}
|
||||
auto shared_task = GetSharedTask();
|
||||
if (shared_task)
|
||||
shared_task->MemberZoned(this);
|
||||
return false; //delete client
|
||||
}
|
||||
|
||||
@@ -653,6 +659,9 @@ bool Client::Process() {
|
||||
myraid->MemberZoned(this);
|
||||
}
|
||||
}
|
||||
auto shared_task = GetSharedTask();
|
||||
if (shared_task)
|
||||
shared_task->MemberZoned(this);
|
||||
OnDisconnect(false);
|
||||
return false;
|
||||
}
|
||||
@@ -694,6 +703,10 @@ void Client::OnDisconnect(bool hard_disconnect) {
|
||||
if (MyRaid)
|
||||
MyRaid->MemberZoned(this);
|
||||
|
||||
auto shared_task = GetSharedTask();
|
||||
if (shared_task)
|
||||
shared_task->MemberZoned(this);
|
||||
|
||||
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
|
||||
|
||||
/* QS: PlayerLogConnectDisconnect */
|
||||
@@ -2101,6 +2114,10 @@ void Client::HandleRespawnFromHover(uint32 Option)
|
||||
if(r)
|
||||
r->MemberZoned(this);
|
||||
|
||||
auto shared_task = GetSharedTask();
|
||||
if (shared_task)
|
||||
shared_task->MemberZoned(this);
|
||||
|
||||
m_pp.zone_id = chosen->zone_id;
|
||||
m_pp.zoneInstance = chosen->instance_id;
|
||||
database.MoveCharacterToZone(CharacterID(), database.GetZoneName(chosen->zone_id));
|
||||
|
||||
+17
-7
@@ -39,6 +39,8 @@
|
||||
#include <ctime>
|
||||
#include <thread>
|
||||
|
||||
#include <task.pb.h>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
@@ -2831,14 +2833,22 @@ void command_spawn(Client *c, const Seperator *sep)
|
||||
|
||||
void command_test(Client *c, const Seperator *sep)
|
||||
{
|
||||
c->Message(15, "Triggering test command");
|
||||
EQ::Proto::ClientTaskStateRequest req;
|
||||
req.set_client_id(123);
|
||||
|
||||
if (sep->arg[1]) {
|
||||
c->SetPrimaryWeaponOrnamentation(atoi(sep->arg[1]));
|
||||
}
|
||||
if (sep->arg[2]) {
|
||||
c->SetSecondaryWeaponOrnamentation(atoi(sep->arg[2]));
|
||||
}
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutProtobuf(0, &req);
|
||||
|
||||
worldserver.RouteMessage("Tasks", "", p);
|
||||
|
||||
//c->Message(15, "Triggering test command");
|
||||
//
|
||||
//if (sep->arg[1]) {
|
||||
// c->SetPrimaryWeaponOrnamentation(atoi(sep->arg[1]));
|
||||
//}
|
||||
//if (sep->arg[2]) {
|
||||
// c->SetSecondaryWeaponOrnamentation(atoi(sep->arg[2]));
|
||||
//}
|
||||
}
|
||||
|
||||
void command_texture(Client *c, const Seperator *sep)
|
||||
|
||||
@@ -2127,6 +2127,23 @@ XS(XS__taskselector) {
|
||||
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS__sharedtaskselector);
|
||||
XS(XS__sharedtaskselector) {
|
||||
dXSARGS;
|
||||
if ((items >= 1) && (items <= MAXCHOOSERENTRIES)) {
|
||||
int tasks[MAXCHOOSERENTRIES];
|
||||
for (int i = 0; i < items; i++) {
|
||||
tasks[i] = (int) SvIV(ST(i));
|
||||
}
|
||||
quest_manager.taskselector(items, tasks, true);
|
||||
} else {
|
||||
Perl_croak(aTHX_ "Usage: quest::sharedtaskselector(int task_id, 2, 3, 4, 5 [up to 40])");
|
||||
}
|
||||
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS__task_setselector);
|
||||
XS(XS__task_setselector) {
|
||||
dXSARGS;
|
||||
@@ -3942,6 +3959,7 @@ EXTERN_C XS(boot_quest) {
|
||||
newXS(strcpy(buf, "targlobal"), XS__targlobal, file);
|
||||
newXS(strcpy(buf, "taskexploredarea"), XS__taskexploredarea, file);
|
||||
newXS(strcpy(buf, "taskselector"), XS__taskselector, file);
|
||||
newXS(strcpy(buf, "sharedtaskselector"), XS__sharedtaskselector, file);
|
||||
newXS(strcpy(buf, "task_setselector"), XS__task_setselector, file);
|
||||
newXS(strcpy(buf, "tasktimeleft"), XS__tasktimeleft, file);
|
||||
newXS(strcpy(buf, "toggle_spawn_event"), XS__toggle_spawn_event, file);
|
||||
|
||||
@@ -537,6 +537,9 @@ void EntityList::MobProcess()
|
||||
Log(Logs::General, Logs::Error, "About to delete a client still in a raid.");
|
||||
r->MemberZoned(mob->CastToClient());
|
||||
}
|
||||
auto shared_task = mob->CastToClient()->GetSharedTask();
|
||||
if (shared_task)
|
||||
shared_task->MemberZoned(mob);
|
||||
entity_list.RemoveClient(id);
|
||||
}
|
||||
|
||||
|
||||
+28
-1
@@ -581,6 +581,32 @@ void lua_task_selector(luabind::adl::object table) {
|
||||
quest_manager.taskselector(count, tasks);
|
||||
}
|
||||
|
||||
void lua_task_selector(luabind::adl::object table, bool shared) {
|
||||
if(luabind::type(table) != LUA_TTABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tasks[MAXCHOOSERENTRIES] = { 0 };
|
||||
int count = 0;
|
||||
|
||||
for(int i = 1; i <= MAXCHOOSERENTRIES; ++i) {
|
||||
auto cur = table[i];
|
||||
int cur_value = 0;
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
cur_value = luabind::object_cast<int>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
}
|
||||
} else {
|
||||
count = i - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
tasks[i - 1] = cur_value;
|
||||
}
|
||||
quest_manager.taskselector(count, tasks, shared);
|
||||
}
|
||||
|
||||
void lua_task_set_selector(int task_set) {
|
||||
quest_manager.tasksetselector(task_set);
|
||||
}
|
||||
@@ -1652,7 +1678,8 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("summon_all_player_corpses", &lua_summon_all_player_corpses),
|
||||
luabind::def("get_player_buried_corpse_count", &lua_get_player_buried_corpse_count),
|
||||
luabind::def("bury_player_corpse", &lua_bury_player_corpse),
|
||||
luabind::def("task_selector", &lua_task_selector),
|
||||
luabind::def("task_selector", (void(*)(luabind::adl::object))&lua_task_selector),
|
||||
luabind::def("task_selector", (void(*)(luabind::adl::object,bool))&lua_task_selector),
|
||||
luabind::def("task_set_selector", &lua_task_set_selector),
|
||||
luabind::def("enable_task", &lua_enable_task),
|
||||
luabind::def("disable_task", &lua_disable_task),
|
||||
|
||||
+4
-4
@@ -2221,10 +2221,10 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
|
||||
|
||||
#endif //BOTS
|
||||
|
||||
void QuestManager::taskselector(int taskcount, int *tasks) {
|
||||
void QuestManager::taskselector(int taskcount, int *tasks, bool shared) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && taskmanager)
|
||||
initiator->TaskQuestSetSelector(owner, taskcount, tasks);
|
||||
initiator->TaskQuestSetSelector(owner, taskcount, tasks, shared);
|
||||
}
|
||||
void QuestManager::enabletask(int taskcount, int *tasks) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
@@ -2249,11 +2249,11 @@ bool QuestManager::istaskenabled(int taskid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void QuestManager::tasksetselector(int tasksetid) {
|
||||
void QuestManager::tasksetselector(int tasksetid, bool shared) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
Log(Logs::General, Logs::Tasks, "[UPDATE] TaskSetSelector called for task set %i", tasksetid);
|
||||
if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && taskmanager)
|
||||
initiator->TaskSetSelector(owner, tasksetid);
|
||||
initiator->TaskSetSelector(owner, tasksetid, shared);
|
||||
}
|
||||
|
||||
bool QuestManager::istaskactive(int task) {
|
||||
|
||||
+2
-2
@@ -188,8 +188,8 @@ public:
|
||||
void playerfeature(char *feature, int setting);
|
||||
void npcfeature(char *feature, int setting);
|
||||
void popup(const char *title, const char *text, uint32 popupid, uint32 buttons, uint32 Duration);
|
||||
void taskselector(int taskcount, int *tasks);
|
||||
void tasksetselector(int tasksettid);
|
||||
void taskselector(int taskcount, int *tasks, bool shared = false);
|
||||
void tasksetselector(int tasksettid, bool shared = false);
|
||||
void enabletask(int taskcount, int *tasks);
|
||||
void disabletask(int taskcount, int *tasks);
|
||||
bool istaskenabled(int taskid);
|
||||
|
||||
@@ -359,6 +359,7 @@
|
||||
#define LDON_NO_LOCKPICK 7564 //You must have a lock pick in your inventory to do this.
|
||||
#define LDON_WAS_NOT_LOCKED 7565 //%1 was not locked.
|
||||
#define LDON_WAS_NOT_TRAPPED 7566 //%1 was not trapped
|
||||
#define TASK_REJECT_LOCKEDOUT_ME 8017 //This task can not be assigned to you because you must wait %1d:%2h:%3m before you can do another task of this type.
|
||||
#define GAIN_GROUP_LEADERSHIP_POINT 8585 //
|
||||
#define GAIN_RAID_LEADERSHIP_POINT 8589 //
|
||||
#define MAX_GROUP_LEADERSHIP_POINTS 8584 //
|
||||
@@ -369,6 +370,15 @@
|
||||
#define GAIN_GROUP_LEADERSHIP_EXP 8788 //
|
||||
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
|
||||
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining)
|
||||
#define TASK_REJECT_MAX_COUNT 8891 //You can not be assigned this shared task because your party exceeds the maximum allowed number of players.
|
||||
#define TASK_REJECT_LEADER_REQ 8892 //You can not be assigned this shared task because the leader does not meet the shared task requirements.
|
||||
#define TASK_REJECT_MIN_COUNT 8895 //You can not be assigned this shared task because your party does not contain the minimum required number of players.
|
||||
#define TASK_REJECT_HAVE_ONE 8935 //You may not request a shared task because you already have one.
|
||||
#define TASK_REJECT_RAID_HAVE_ONE 8936 //You may not request a shared task because someone in your raid, %1, already has one.
|
||||
#define TASK_REJECT_GROUP_HAVE_ONE 8937 //You may not request a shared task because someone in your group, %1, already has one.
|
||||
#define TASK_REJECT_LOCKEDOUT 8946 //You may not request this shared task because you must wait %1d:%2h:%3m before you can do another task of this type.
|
||||
#define TASK_REJECT_LOCKEDOUT_OTHER 8947 //You may not request this shared task because %1 must wait %2d:%3h:%4m before they can do another task of this type.
|
||||
#define SHARED_TASK_LOCK 8961 //Your shared task is now locked. You may no longer add or remove players.
|
||||
#define NO_MORE_TRAPS 9002 //You have already placed your maximum number of traps.
|
||||
#define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell.
|
||||
#define SLOW_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful.
|
||||
|
||||
+391
-51
@@ -30,16 +30,19 @@ Copyright (C) 2001-2008 EQEMu Development Team (http://eqemulator.net)
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/string_util.h"
|
||||
#include "../common/say_link.h"
|
||||
#include "../common/data_verification.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "entity.h"
|
||||
#include "mob.h"
|
||||
#include "string_ids.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
#include "queryserv.h"
|
||||
#include "quest_parser_collection.h"
|
||||
|
||||
extern QueryServ* QServ;
|
||||
extern WorldServer worldserver;
|
||||
|
||||
TaskManager::TaskManager() {
|
||||
for(int i=0; i<MAXTASKS; i++)
|
||||
@@ -116,15 +119,26 @@ bool TaskManager::LoadTasks(int singleTask)
|
||||
if (!LoadTaskSets())
|
||||
Log(Logs::Detail, Logs::Tasks, "TaskManager::LoadTasks LoadTaskSets failed");
|
||||
|
||||
query = StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, "
|
||||
"`reward`, `rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`,"
|
||||
"`minlevel`, `maxlevel`, `repeatable`, `completion_emote` FROM `tasks` WHERE `id` < %i",
|
||||
MAXTASKS);
|
||||
if (!LoadReplayGroups())
|
||||
Log(Logs::Detail, Logs::Tasks, "TaskManager::LoadTasks LoadReplayGroups failed");
|
||||
|
||||
query =
|
||||
StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, `reward`, "
|
||||
"`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`, `minlevel`, "
|
||||
"`maxlevel`, `repeatable`, `completion_emote`, `reward_points`, `reward_type`, "
|
||||
"`replay_group`, `min_players`, `max_players`, `task_lock_step`, `instance_zone_id`, "
|
||||
"`zone_version`, `zone_in_zone_id`, `zone_in_x`, `zone_in_y`, `zone_in_object_id`, "
|
||||
"`dest_x`, `dest_y`, `dest_z`, `dest_h` FROM `tasks` WHERE `id` < %i",
|
||||
MAXTASKS);
|
||||
} else
|
||||
query = StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, "
|
||||
"`reward`, `rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`,"
|
||||
"`minlevel`, `maxlevel`, `repeatable`, `completion_emote` FROM `tasks` WHERE `id` = %i",
|
||||
singleTask);
|
||||
query =
|
||||
StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, `reward`, "
|
||||
"`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`, `minlevel`, "
|
||||
"`maxlevel`, `repeatable`, `completion_emote`, `reward_points`, `reward_type`, "
|
||||
"`replay_group`, `min_players`, `max_players`, `task_lock_step`, `instance_zone_id`, "
|
||||
"`zone_version`, `zone_in_zone_id`, `zone_in_x`, `zone_in_y`, `zone_in_object_id`, "
|
||||
"`dest_x`, `dest_y`, `dest_z`, `dest_h` FROM `tasks` WHERE `id` = %i",
|
||||
singleTask);
|
||||
|
||||
const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTasks: %s";
|
||||
|
||||
@@ -160,6 +174,22 @@ bool TaskManager::LoadTasks(int singleTask)
|
||||
Tasks[taskID]->MaxLevel = atoi(row[13]);
|
||||
Tasks[taskID]->Repeatable = atoi(row[14]);
|
||||
Tasks[taskID]->completion_emote = row[15];
|
||||
Tasks[taskID]->reward_points = atoi(row[16]);
|
||||
Tasks[taskID]->reward_type = static_cast<PointType>(atoi(row[17]));
|
||||
Tasks[taskID]->replay_group = atoi(row[18]);
|
||||
Tasks[taskID]->min_players = atoi(row[19]);
|
||||
Tasks[taskID]->max_players = atoi(row[20]);
|
||||
Tasks[taskID]->task_lock_step = atoi(row[21]);
|
||||
Tasks[taskID]->instance_zone_id = atoi(row[22]);
|
||||
Tasks[taskID]->zone_version = atoi(row[23]);
|
||||
Tasks[taskID]->zone_in_zone_id = atoi(row[24]);
|
||||
Tasks[taskID]->zone_in_x = atof(row[25]);
|
||||
Tasks[taskID]->zone_in_y = atof(row[26]);
|
||||
Tasks[taskID]->zone_in_object_id = atoi(row[27]);
|
||||
Tasks[taskID]->dest_x = atof(row[28]);
|
||||
Tasks[taskID]->dest_y = atof(row[29]);
|
||||
Tasks[taskID]->dest_z = atof(row[30]);
|
||||
Tasks[taskID]->dest_h = atof(row[31]);
|
||||
Tasks[taskID]->ActivityCount = 0;
|
||||
Tasks[taskID]->SequenceMode = ActivitiesSequential;
|
||||
Tasks[taskID]->LastStep = 0;
|
||||
@@ -281,6 +311,22 @@ bool TaskManager::LoadTasks(int singleTask)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskManager::LoadReplayGroups()
|
||||
{
|
||||
replay_groups.clear();
|
||||
std::string query = "SELECT `id`, `name`, `duration` FROM `task_replay_groups` WHERE `id` > 0 ORDER BY `id` ASC";
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row)
|
||||
replay_groups[atoi(row[0])] = {row[1], atoi(row[2])};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskManager::SaveClientState(Client *c, ClientTaskState *state)
|
||||
{
|
||||
// I am saving the slot in the ActiveTasks table, because unless a Task is cancelled/completed, the client
|
||||
@@ -425,29 +471,22 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state)
|
||||
}
|
||||
|
||||
void Client::LoadClientTaskState() {
|
||||
|
||||
if(RuleB(TaskSystem, EnableTaskSystem) && taskmanager) {
|
||||
if(taskstate)
|
||||
safe_delete(taskstate);
|
||||
|
||||
taskstate = new ClientTaskState;
|
||||
if(!taskmanager->LoadClientState(this, taskstate)) {
|
||||
safe_delete(taskstate);
|
||||
}
|
||||
else {
|
||||
taskmanager->SendActiveTasksToClient(this);
|
||||
taskmanager->SendCompletedTasksToClient(this, taskstate);
|
||||
}
|
||||
}
|
||||
|
||||
//GetClientTaskStateRequest req;
|
||||
//req.client_id = CharacterID();
|
||||
//
|
||||
//EQ::Net::DynamicPacket p;
|
||||
//p.PutInt32(0, TaskGetClientTaskState);
|
||||
//p.PutSerialize(4, req);
|
||||
//
|
||||
//worldserver.RouteMessage("Tasks", "", p);
|
||||
}
|
||||
|
||||
void Client::RemoveClientTaskState() {
|
||||
|
||||
if(taskstate) {
|
||||
taskstate->CancelAllTasks(this);
|
||||
safe_delete(taskstate);
|
||||
}
|
||||
//if(taskstate) {
|
||||
// taskstate->CancelAllTasks(this);
|
||||
// safe_delete(taskstate);
|
||||
//}
|
||||
}
|
||||
|
||||
bool TaskManager::LoadClientState(Client *c, ClientTaskState *state)
|
||||
@@ -905,7 +944,7 @@ bool ClientTaskState::HasSlotForTask(TaskInformation *task)
|
||||
case TaskType::Task:
|
||||
return ActiveTask.TaskID == TASKSLOTEMPTY;
|
||||
case TaskType::Shared:
|
||||
return false; // todo
|
||||
return ActiveSharedTask == nullptr; // todo
|
||||
case TaskType::Quest:
|
||||
for (int i = 0; i < MAXACTIVEQUESTS; ++i)
|
||||
if (ActiveQuests[i].TaskID == TASKSLOTEMPTY)
|
||||
@@ -988,7 +1027,7 @@ int TaskManager::GetTaskMaxLevel(int TaskID)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID)
|
||||
void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID, bool shared)
|
||||
{
|
||||
int TaskList[MAXCHOOSERENTRIES];
|
||||
int TaskListIndex = 0;
|
||||
@@ -1024,14 +1063,14 @@ void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, i
|
||||
// we aren't currently on another, and if it's enabled if not all_enabled
|
||||
if ((all_enabled || state->IsTaskEnabled(task)) && AppropriateLevel(task, PlayerLevel) &&
|
||||
!state->IsTaskActive(task) && state->HasSlotForTask(Tasks[task]) && // this slot checking is a bit silly, but we allow mixing of task types ...
|
||||
(IsTaskRepeatable(task) || !state->IsTaskCompleted(task)))
|
||||
(IsTaskRepeatable(task) || !state->IsTaskCompleted(task)) && (shared == (Tasks[task]->type == TaskType::Shared)))
|
||||
TaskList[TaskListIndex++] = task;
|
||||
|
||||
++Iterator;
|
||||
}
|
||||
|
||||
if (TaskListIndex > 0) {
|
||||
SendTaskSelector(c, mob, TaskListIndex, TaskList);
|
||||
SendTaskSelector(c, mob, TaskListIndex, TaskList, shared);
|
||||
} else {
|
||||
mob->SayTo_StringID(c, CC_Yellow, MAX_ACTIVE_TASKS, c->GetName()); // check color, I think this might be only for (Shared) Tasks, w/e -- think should be yellow
|
||||
}
|
||||
@@ -1041,7 +1080,7 @@ void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, i
|
||||
|
||||
// unlike the non-Quest version of this function, it does not check enabled, that is assumed the responsibility of the quest to handle
|
||||
// we do however still want it to check the other stuff like level, active, room, etc
|
||||
void TaskManager::TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks)
|
||||
void TaskManager::TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks, bool shared)
|
||||
{
|
||||
int TaskList[MAXCHOOSERENTRIES];
|
||||
int TaskListIndex = 0;
|
||||
@@ -1058,12 +1097,12 @@ void TaskManager::TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *m
|
||||
// we aren't currently on another, and if it's enabled if not all_enabled
|
||||
if (AppropriateLevel(task, PlayerLevel) &&
|
||||
!state->IsTaskActive(task) && state->HasSlotForTask(Tasks[task]) && // this slot checking is a bit silly, but we allow mixing of task types ...
|
||||
(IsTaskRepeatable(task) || !state->IsTaskCompleted(task)))
|
||||
(IsTaskRepeatable(task) || !state->IsTaskCompleted(task)) && (shared == (Tasks[task]->type == TaskType::Shared)))
|
||||
TaskList[TaskListIndex++] = task;
|
||||
}
|
||||
|
||||
if (TaskListIndex > 0) {
|
||||
SendTaskSelector(c, mob, TaskListIndex, TaskList);
|
||||
SendTaskSelector(c, mob, TaskListIndex, TaskList, shared);
|
||||
} else {
|
||||
mob->SayTo_StringID(c, CC_Yellow, MAX_ACTIVE_TASKS, c->GetName()); // check color, I think this might be only for (Shared) Tasks, w/e -- think should be yellow
|
||||
}
|
||||
@@ -1071,11 +1110,11 @@ void TaskManager::TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *m
|
||||
return;
|
||||
}
|
||||
|
||||
void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList) {
|
||||
void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList, bool shared) {
|
||||
|
||||
if (c->ClientVersion() >= EQEmu::versions::ClientVersion::RoF)
|
||||
{
|
||||
SendTaskSelectorNew(c, mob, TaskCount, TaskList);
|
||||
SendTaskSelectorNew(c, mob, TaskCount, TaskList, shared);
|
||||
return;
|
||||
}
|
||||
// Titanium OpCode: 0x5e7c
|
||||
@@ -1108,7 +1147,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task
|
||||
|
||||
|
||||
buf.WriteUInt32(ValidTasks);
|
||||
buf.WriteUInt32(2); // task type, live doesn't let you send more than one type, but we do?
|
||||
buf.WriteUInt32(shared ? static_cast<uint32>(TaskType::Shared) : static_cast<uint32>(TaskType::Quest)); // hack, we need to send only shared tasks when doing shared tasks since they use different reply ops
|
||||
buf.WriteUInt32(mob->GetID());
|
||||
|
||||
for (int i = 0; i < TaskCount; i++) {
|
||||
@@ -1153,12 +1192,14 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, buf);
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
}
|
||||
|
||||
void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList)
|
||||
void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList, bool shared)
|
||||
{
|
||||
Log(Logs::General, Logs::Tasks, "[UPDATE] TaskSelector for %i Tasks", TaskCount);
|
||||
|
||||
@@ -1188,9 +1229,7 @@ void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *T
|
||||
SerializeBuffer buf(50 * ValidTasks);
|
||||
|
||||
buf.WriteUInt32(ValidTasks); // TaskCount
|
||||
buf.WriteUInt32(2); // Type, valid values: 0-3. 0 = Task, 1 = Shared Task, 2 = Quest, 3 = ??? -- should fix maybe some day, but we let more than 1 type through :P
|
||||
// so I guess an NPC can only offer one type of quests or we can only open a selection with one type :P (so quest call can tell us I guess)
|
||||
// this is also sent in OP_TaskDescription
|
||||
buf.WriteUInt32(shared ? static_cast<uint32>(TaskType::Shared) : static_cast<uint32>(TaskType::Quest)); // hack, we need to send only shared tasks when doing shared tasks since they use different reply ops
|
||||
buf.WriteUInt32(mob->GetID()); // TaskGiver
|
||||
|
||||
for (int i = 0; i < TaskCount; i++) { // max 40
|
||||
@@ -1240,6 +1279,8 @@ void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *T
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, buf);
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
@@ -1304,7 +1345,8 @@ ClientTaskState::ClientTaskState() {
|
||||
|
||||
ActiveTask.slot = 0;
|
||||
ActiveTask.TaskID = TASKSLOTEMPTY;
|
||||
// TODO: shared task
|
||||
|
||||
ActiveSharedTask = nullptr;
|
||||
}
|
||||
|
||||
ClientTaskState::~ClientTaskState() {
|
||||
@@ -2406,6 +2448,15 @@ bool TaskManager::IsTaskRepeatable(int TaskID) {
|
||||
return Task->Repeatable;
|
||||
}
|
||||
|
||||
SharedTaskState *TaskManager::CreateSharedTask(int id, int task_id)
|
||||
{
|
||||
auto ret = SharedTasks.insert({id, {id, task_id}});
|
||||
if (!ret.second) // hmm was already created
|
||||
return nullptr;
|
||||
|
||||
return &(ret.first->second);
|
||||
}
|
||||
|
||||
bool ClientTaskState::TaskOutOfTime(TaskType type, int Index)
|
||||
{
|
||||
// Returns true if the Task in the specified slot has a time limit that has been exceeded.
|
||||
@@ -2496,7 +2547,7 @@ void Client::SendTaskComplete(int TaskIndex) {
|
||||
tcs->unknown04 = 0x00000002;
|
||||
|
||||
Log.LogDebugType(Logs::Detail, Logs::Tasks, "SendTasksComplete");
|
||||
DumpPacket(outapp); fflush(stdout);
|
||||
DumpPacket(outapp, true); fflush(stdout);
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
@@ -2577,6 +2628,7 @@ void ClientTaskState::SendTaskHistory(Client *c, int TaskIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
@@ -2602,6 +2654,7 @@ void Client::SendTaskActivityComplete(int TaskID, int ActivityID, int TaskIndex,
|
||||
tac->task_completed = 0x00000001;
|
||||
tac->stage_complete = TaskIncomplete;
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
@@ -2631,6 +2684,8 @@ void Client::SendTaskFailed(int TaskID, int TaskIndex, TaskType type)
|
||||
|
||||
Log(Logs::General, Logs::Tasks, "[UPDATE] TaskFailed");
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
@@ -2686,6 +2741,7 @@ void TaskManager::SendCompletedTasksToClient(Client *c, ClientTaskState *State)
|
||||
buf = buf + 4;
|
||||
}
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
@@ -2710,6 +2766,8 @@ void TaskManager::SendTaskActivityShort(Client *c, int TaskID, int ActivityID, i
|
||||
outapp->WriteUInt32(0);
|
||||
outapp->WriteUInt32(0xffffffff);
|
||||
outapp->WriteUInt8(0);
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
c->FastQueuePacket(&outapp);
|
||||
|
||||
return;
|
||||
@@ -2727,7 +2785,7 @@ void TaskManager::SendTaskActivityShort(Client *c, int TaskID, int ActivityID, i
|
||||
tass->ActivityType = 0xffffffff;
|
||||
tass->unknown4 = 0x00000000;
|
||||
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
@@ -2789,6 +2847,8 @@ void TaskManager::SendTaskActivityLong(Client *c, int TaskID, int ActivityID, in
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_TaskActivity, buf);
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
@@ -2849,6 +2909,8 @@ void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_TaskActivity, buf);
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
@@ -3016,10 +3078,27 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, ClientTaskInf
|
||||
tdt->Points = 0x00000000; // Points Count TODO: this does have a visible affect on the client ...
|
||||
tdt->has_reward_selection = 0; // TODO: new rewards window
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
// TODO: stub
|
||||
SharedTaskState *TaskManager::LoadSharedTask(int id)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SharedTaskState *TaskManager::GetSharedTask(int id)
|
||||
{
|
||||
auto it = SharedTasks.find(id);
|
||||
if (it == SharedTasks.end())
|
||||
return nullptr;
|
||||
|
||||
return &(it->second);
|
||||
}
|
||||
|
||||
bool ClientTaskState::IsTaskActivityCompleted(TaskType type, int index, int ActivityID)
|
||||
{
|
||||
switch (type) {
|
||||
@@ -3140,6 +3219,8 @@ void ClientTaskState::CancelTask(Client *c, int SequenceNumber, TaskType type, b
|
||||
|
||||
Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask");
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
@@ -3222,10 +3303,8 @@ void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enfor
|
||||
if (ActiveTask.TaskID != TASKSLOTEMPTY)
|
||||
max_tasks = true;
|
||||
break;
|
||||
case TaskType::Shared: // TODO: shared tasks
|
||||
// if (something)
|
||||
max_tasks = true;
|
||||
break;
|
||||
case TaskType::Shared: // shared tasks shouldn't call this function. should we log?
|
||||
return;
|
||||
case TaskType::Quest:
|
||||
if (ActiveTaskCount == MAXACTIVEQUESTS)
|
||||
max_tasks = true;
|
||||
@@ -3267,9 +3346,8 @@ void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enfor
|
||||
case TaskType::Task:
|
||||
active_slot = &ActiveTask;
|
||||
break;
|
||||
case TaskType::Shared: // TODO: shared
|
||||
active_slot = nullptr;
|
||||
break;
|
||||
case TaskType::Shared: // shared aren't done here, should have returned already :P
|
||||
return;
|
||||
case TaskType::Quest:
|
||||
for (int i = 0; i < MAXACTIVEQUESTS; i++) {
|
||||
Log(Logs::General, Logs::Tasks,
|
||||
@@ -3324,6 +3402,213 @@ void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enfor
|
||||
parse->EventNPC(EVENT_TASK_ACCEPTED, npc, c, buf.c_str(), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is a proxy for OP_AccetNewSharedTask since it has to fire to
|
||||
* world. We do a handful of checks on the leader, then build a packet with a
|
||||
* list of all the members in our group/raid if applicable. The verification for
|
||||
* the other members is done in world.
|
||||
*/
|
||||
void ClientTaskState::RequestSharedTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement)
|
||||
{
|
||||
if (!taskmanager || TaskID < 0 || TaskID >= MAXTASKS) {
|
||||
c->Message(13, "Task system not functioning, or TaskID %i out of range.", TaskID);
|
||||
return;
|
||||
}
|
||||
|
||||
auto task = taskmanager->Tasks[TaskID];
|
||||
|
||||
if (task == nullptr) {
|
||||
c->Message(13, "Invalid TaskID %i", TaskID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (task->type != TaskType::Shared) {
|
||||
c->Message(13, "Trying to shared task a non shared task %i", TaskID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActiveSharedTask != nullptr) {
|
||||
c->Message_StringID(13, TASK_REJECT_HAVE_ONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (enforce_level_requirement && !taskmanager->AppropriateLevel(TaskID, c->GetLevel())) {
|
||||
c->Message(13, "You are outside the level range of this task.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!taskmanager->IsTaskRepeatable(TaskID) && IsTaskCompleted(TaskID))
|
||||
return;
|
||||
|
||||
if (task->replay_group) {
|
||||
auto expires = c->GetTaskLockoutTimeLeft(task->replay_group);
|
||||
if (expires > 0) {
|
||||
std::string days = std::to_string(expires / 86400);
|
||||
expires = expires % 86400;
|
||||
std::string hours = std::to_string(expires / 3600);
|
||||
expires = expires % 3600;
|
||||
std::string minutes = std::to_string(expires / 60);
|
||||
c->Message_StringID(13, TASK_REJECT_LOCKEDOUT, days.c_str(), hours.c_str(), minutes.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to verify we meet min_player and max_players for raid/group
|
||||
Group *group = nullptr;
|
||||
Raid *raid = nullptr;
|
||||
int player_count = 1; // 1 is just us!
|
||||
if (c->IsGrouped()) {
|
||||
group = c->GetGroup();
|
||||
player_count = group->GroupCount();
|
||||
} else if (c->IsRaidGrouped()) {
|
||||
raid = c->GetRaid();
|
||||
player_count = raid->RaidCount();
|
||||
}
|
||||
|
||||
if (!EQEmu::ValueWithin(player_count, task->min_players, task->max_players)) {
|
||||
if (player_count < task->min_players)
|
||||
c->Message_StringID(13, TASK_REJECT_MIN_COUNT);
|
||||
else
|
||||
c->Message_StringID(13, TASK_REJECT_MAX_COUNT);
|
||||
return;
|
||||
}
|
||||
|
||||
// okay, we verified a few things on the requestor, now we need to fire off to world to do the rest
|
||||
SerializeBuffer buf(25 + 10 * player_count);
|
||||
buf.WriteInt32(TaskID); // Task ID
|
||||
buf.WriteInt32(NPCID); // NPC we're requesting from
|
||||
buf.WriteString(c->GetName()); // leader name
|
||||
buf.WriteInt32(player_count - 1); // count, not leader
|
||||
if (group) {
|
||||
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) {
|
||||
if (group->members[i] == c) // skipping requestor
|
||||
continue;
|
||||
|
||||
// TODO: mercs/bots
|
||||
if (group->members[i] != nullptr && group->members[i]->IsClient())
|
||||
buf.WriteString(group->membername[i]);
|
||||
}
|
||||
} else if (raid) {
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; ++i) {
|
||||
if (raid->members[i].member == c) // skipping requestor
|
||||
continue;
|
||||
// TODO: bots if they ever can live in a raid
|
||||
if (raid->members[i].membername[0] != '\0')
|
||||
buf.WriteString(raid->members[i].membername);
|
||||
}
|
||||
}
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_TaskRequest, buf);
|
||||
worldserver.SendPacket(pack);
|
||||
delete pack;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ClientTaskState::AcceptNewSharedTask(Client *c, int TaskID, int NPCID, int id, int accepted_time,
|
||||
std::vector<std::string> &members)
|
||||
{
|
||||
// all of this data should have been verified already
|
||||
// first we need to create the new SharedTaskState
|
||||
auto task_state = taskmanager->CreateSharedTask(id, TaskID);
|
||||
if (!task_state) {
|
||||
// TODO: something failed, tell world
|
||||
return;
|
||||
}
|
||||
|
||||
// we need to init the activity now
|
||||
auto task_activity = task_state->GetActivity();
|
||||
|
||||
task_activity->TaskID = TaskID;
|
||||
task_activity->AcceptedTime = accepted_time;
|
||||
task_activity->Updated = true;
|
||||
task_activity->CurrentStep = -1;
|
||||
|
||||
for (int i = 0; i < taskmanager->Tasks[TaskID]->ActivityCount; i++) {
|
||||
task_activity->Activity[i].ActivityID = i;
|
||||
task_activity->Activity[i].DoneCount = 0;
|
||||
task_activity->Activity[i].State = ActivityHidden;
|
||||
task_activity->Activity[i].Updated = true;
|
||||
}
|
||||
|
||||
// TODO: figure out packet order
|
||||
// unsure if we unlock now packet wise, just copying normal tasks for now
|
||||
UnlockActivities(c->CharacterID(), *task_activity);
|
||||
|
||||
taskmanager->SendSingleActiveTaskToClient(c, *task_activity, false, true);
|
||||
c->Message(0, "You have been assigned the task '%s'.", taskmanager->Tasks[TaskID]->Title.c_str());
|
||||
|
||||
// send member list of just leader
|
||||
task_state->AddMember(c->GetName(), c, true);
|
||||
task_state->SendMembersList(c);
|
||||
|
||||
// send compass shit
|
||||
|
||||
// add everyone else and send that
|
||||
// we could try to find these members so they could know about it ... but ahh not sure :P
|
||||
for (auto &m : members)
|
||||
task_state->AddMember(m);
|
||||
|
||||
task_state->SendMembersList(c);
|
||||
|
||||
std::string buf = std::to_string(TaskID);
|
||||
NPC *npc = entity_list.GetID(NPCID)->CastToNPC();
|
||||
if(!npc) {
|
||||
c->Message(clientMessageYellow, "Task Giver ID is %i", NPCID);
|
||||
c->Message(clientMessageError, "Unable to find NPC to send EVENT_TASKACCEPTED to. Report this bug.");
|
||||
// TODO: ahh do we wanna do this? clean up world at least
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: save state -- for the client
|
||||
parse->EventNPC(EVENT_TASK_ACCEPTED, npc, c, buf.c_str(), 0);
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_TaskZoneCreated, sizeof(uint32)); // just the ID saying to continue
|
||||
pack->WriteUInt32(id);
|
||||
worldserver.SendPacket(pack);
|
||||
delete pack;
|
||||
|
||||
ActiveSharedTask = task_state;
|
||||
|
||||
return;
|
||||
|
||||
// there are a few issues we need to solve with this
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called when world sends ServerOP_TaskZoneCreated to trigger
|
||||
* members to join. If the task doesn't already exist, we need to load it from
|
||||
* the DB.
|
||||
*
|
||||
* This is also called in LoadClientTaskState() when they notice they have a
|
||||
* shared task they need to join. (Called from first OP_ZoneEntry)
|
||||
*/
|
||||
void ClientTaskState::AddToSharedTask(Client *c, int TaskID)
|
||||
{
|
||||
auto task = taskmanager->GetSharedTask(TaskID);
|
||||
if (!task)
|
||||
task = taskmanager->LoadSharedTask(TaskID);
|
||||
|
||||
if (!task) {// FUCK
|
||||
return;
|
||||
}
|
||||
|
||||
task->MemberEnterZone(c);
|
||||
|
||||
// send packets
|
||||
auto task_activity = task->GetActivity();
|
||||
taskmanager->SendSingleActiveTaskToClient(c, *task_activity, false, true);
|
||||
task->SendMembersList(c);
|
||||
|
||||
// So normally getting a task we would send EVENT_TASK_ACCEPTED here, but
|
||||
// this isn't an accept step. I guess we should add another event in case
|
||||
// they need the same thing TODO
|
||||
|
||||
ActiveSharedTask = task;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ClientTaskState::ProcessTaskProximities(Client *c, float X, float Y, float Z) {
|
||||
|
||||
float LastX = c->ProximityX();
|
||||
@@ -3537,3 +3822,58 @@ int TaskProximityManager::CheckProximities(float X, float Y, float Z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SharedTaskState::LockTask()
|
||||
{
|
||||
SetLocked(true);
|
||||
|
||||
for (auto & m : members)
|
||||
if (m.entity != nullptr)
|
||||
m.entity->Message_StringID(CC_Yellow, SHARED_TASK_LOCK);
|
||||
}
|
||||
|
||||
void SharedTaskState::MemberZoned(Mob *player)
|
||||
{
|
||||
auto it = std::find_if(members.begin(), members.end(),
|
||||
[&player](const SharedTaskMember &a) { return a.entity == player; });
|
||||
|
||||
if (it == members.end()) // guess they weren't in this group, w/e
|
||||
return;
|
||||
|
||||
it->entity = nullptr;
|
||||
}
|
||||
|
||||
void SharedTaskState::MemberEnterZone(Mob *player)
|
||||
{
|
||||
auto it = std::find_if(members.begin(), members.end(),
|
||||
[&player](const SharedTaskMember &a) { return a.name == player->GetName(); });
|
||||
|
||||
if (it == members.end()) // guess they weren't in this group, w/e
|
||||
return;
|
||||
|
||||
it->entity = player;
|
||||
}
|
||||
|
||||
void SharedTaskState::SendMembersList(Client *to) const
|
||||
{
|
||||
if (!to)
|
||||
return;
|
||||
|
||||
SerializeBuffer buf(sizeof(TaskMemberList_Struct) + 15 * members.size());
|
||||
buf.WriteInt32(0); // unknown ids
|
||||
buf.WriteInt32(0);
|
||||
buf.WriteInt32(members.size());
|
||||
|
||||
for (auto &&m : members) {
|
||||
buf.WriteString(m.name);
|
||||
buf.WriteInt32(0); // monster mission shit
|
||||
buf.WriteInt8(m.leader);
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_SharedTaskMemberList, buf);
|
||||
|
||||
DumpPacket(outapp, true);
|
||||
|
||||
to->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
|
||||
+69
-110
@@ -21,29 +21,14 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net)
|
||||
#define TASKS_H
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "../common/global_tasks.h"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#define MAXTASKS 10000
|
||||
#define MAXTASKSETS 1000
|
||||
// The Client has a hard cap of 19 active quests, 29 in SoD+
|
||||
#define MAXACTIVEQUESTS 19
|
||||
// The Max Chooser (Task Selector entries) is capped at 40 in the Titanium Client.
|
||||
#define MAXCHOOSERENTRIES 40
|
||||
// The Client has a hard cap of 20 activities per task.
|
||||
#define MAXACTIVITIESPERTASK 20
|
||||
// This is used to determine if a client's active task slot is empty.
|
||||
#define TASKSLOTEMPTY 0
|
||||
|
||||
// Command Codes for worldserver ServerOP_ReloadTasks
|
||||
//
|
||||
#define RELOADTASKS 0
|
||||
#define RELOADTASKGOALLISTS 1
|
||||
#define RELOADTASKPROXIMITIES 2
|
||||
#define RELOADTASKSETS 3
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
|
||||
class Client;
|
||||
class Mob;
|
||||
@@ -98,94 +83,6 @@ private:
|
||||
std::vector<TaskProximity> TaskProximities;
|
||||
};
|
||||
|
||||
typedef enum { METHODSINGLEID = 0, METHODLIST = 1, METHODQUEST = 2 } TaskMethodType;
|
||||
|
||||
struct ActivityInformation {
|
||||
int StepNumber;
|
||||
int Type;
|
||||
std::string target_name; // name mob, location -- default empty
|
||||
std::string item_list; // likely defaults to empty
|
||||
std::string skill_list; // IDs ; separated -- default -1
|
||||
std::string spell_list; // IDs ; separated -- default 0
|
||||
std::string desc_override; // overrides auto generated description -- default empty
|
||||
int skill_id; // older clients, first id from above
|
||||
int spell_id; // older clients, first id from above
|
||||
int GoalID;
|
||||
TaskMethodType GoalMethod;
|
||||
int GoalCount;
|
||||
int DeliverToNPC;
|
||||
std::vector<int> ZoneIDs;
|
||||
std::string zones; // IDs ; searated, ZoneID is the first in this list for older clients -- default empty string
|
||||
bool Optional;
|
||||
|
||||
inline bool CheckZone(int zone_id) {
|
||||
if (ZoneIDs.empty())
|
||||
return true;
|
||||
return std::find(ZoneIDs.begin(), ZoneIDs.end(), zone_id) != ZoneIDs.end();
|
||||
}
|
||||
};
|
||||
|
||||
typedef enum { ActivitiesSequential = 0, ActivitiesStepped = 1 } SequenceType;
|
||||
|
||||
enum class TaskType {
|
||||
Task = 0, // can have at max 1
|
||||
Shared = 1, // can have at max 1
|
||||
Quest = 2, // can have at max 19 or 29 depending on client
|
||||
E = 3 // can have at max 19 or 29 depending on client, not present in live anymore
|
||||
};
|
||||
|
||||
enum class DurationCode {
|
||||
None = 0,
|
||||
Short = 1,
|
||||
Medium = 2,
|
||||
Long = 3
|
||||
};
|
||||
|
||||
struct TaskInformation {
|
||||
TaskType type;
|
||||
int Duration;
|
||||
DurationCode dur_code; // description for time investment for when Duration == 0
|
||||
std::string Title; // max length 64
|
||||
std::string Description; // max length 4000, 2048 on Tit
|
||||
std::string Reward;
|
||||
std::string item_link; // max length 128 older clients, item link gets own string
|
||||
std::string completion_emote; // emote after completing task, yellow. Maybe should make more generic ... but yellow for now!
|
||||
int RewardID;
|
||||
int CashReward; // Expressed in copper
|
||||
int XPReward;
|
||||
int faction_reward; // just a npc_faction_id
|
||||
TaskMethodType RewardMethod;
|
||||
int ActivityCount;
|
||||
SequenceType SequenceMode;
|
||||
int LastStep;
|
||||
short MinLevel;
|
||||
short MaxLevel;
|
||||
bool Repeatable;
|
||||
ActivityInformation Activity[MAXACTIVITIESPERTASK];
|
||||
};
|
||||
|
||||
typedef enum { ActivityHidden = 0, ActivityActive = 1, ActivityCompleted = 2 } ActivityState;
|
||||
|
||||
typedef enum { ActivityDeliver = 1, ActivityKill = 2, ActivityLoot = 3, ActivitySpeakWith = 4, ActivityExplore = 5,
|
||||
ActivityTradeSkill = 6, ActivityFish = 7, ActivityForage = 8, ActivityCastOn = 9, ActivitySkillOn = 10,
|
||||
ActivityTouch = 11, ActivityCollect = 13, ActivityGiveCash = 100 } ActivityType;
|
||||
|
||||
|
||||
struct ClientActivityInformation {
|
||||
int ActivityID;
|
||||
int DoneCount;
|
||||
ActivityState State;
|
||||
bool Updated; // Flag so we know if we need to update the database
|
||||
};
|
||||
|
||||
struct ClientTaskInformation {
|
||||
int slot; // intrusive, but makes things easier :P
|
||||
int TaskID;
|
||||
int CurrentStep;
|
||||
int AcceptedTime;
|
||||
bool Updated;
|
||||
ClientActivityInformation Activity[MAXACTIVITIESPERTASK];
|
||||
};
|
||||
|
||||
struct CompletedTaskInformation {
|
||||
int TaskID;
|
||||
@@ -193,6 +90,49 @@ struct CompletedTaskInformation {
|
||||
bool ActivityDone[MAXACTIVITIESPERTASK];
|
||||
};
|
||||
|
||||
struct SharedTaskMember {
|
||||
std::string name;
|
||||
Mob *entity; // needs to be managed
|
||||
bool leader;
|
||||
SharedTaskMember() : entity(nullptr), leader(false) {}
|
||||
SharedTaskMember(std::string name, Mob *entity, bool leader) : name(name), entity(entity), leader(leader) {}
|
||||
};
|
||||
|
||||
class SharedTaskState {
|
||||
public:
|
||||
SharedTaskState() : locked(false) {}
|
||||
SharedTaskState(int id, int task_id) : id(id), task_id(task_id), locked(false) { }
|
||||
// ~SharedTaskState();
|
||||
|
||||
inline const bool IsLocked() const { return locked; }
|
||||
inline void SetLocked(bool v) { locked = v; }
|
||||
void LockTask(); // notified clients (if they are etc)
|
||||
|
||||
void MemberZoned(Mob *player); // player left zone, update their pointer
|
||||
void MemberEnterZone(Mob *player); // player entered zone, update their pointer
|
||||
|
||||
void AddMember(std::string name, Mob *entity = nullptr, bool leader = false)
|
||||
{
|
||||
members.push_back({name, entity, leader});
|
||||
if (leader)
|
||||
leader_name = name;
|
||||
}
|
||||
|
||||
void SendMembersList(Client *to) const;
|
||||
|
||||
ClientTaskInformation *GetActivity() { return &activity; }
|
||||
|
||||
friend class TaskManager;
|
||||
|
||||
private:
|
||||
int id;
|
||||
int task_id;
|
||||
std::vector<SharedTaskMember> members;
|
||||
std::string leader_name;
|
||||
ClientTaskInformation activity;
|
||||
bool locked;
|
||||
};
|
||||
|
||||
class ClientTaskState {
|
||||
|
||||
public:
|
||||
@@ -238,9 +178,15 @@ public:
|
||||
int ActiveTasksInSet(int TaskSetID);
|
||||
int CompletedTasksInSet(int TaskSetID);
|
||||
bool HasSlotForTask(TaskInformation *task);
|
||||
// shared task related functions
|
||||
void AcceptNewSharedTask(Client *c, int TaskID, int NPCID, int id, int accepted_time, std::vector<std::string> &members);
|
||||
void AddToSharedTask(Client *c, int TaskID);
|
||||
void RequestSharedTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement = false);
|
||||
|
||||
inline bool HasFreeTaskSlot() { return ActiveTask.TaskID == TASKSLOTEMPTY; }
|
||||
|
||||
inline SharedTaskState *GetSharedTask() { return ActiveSharedTask ; }
|
||||
|
||||
friend class TaskManager;
|
||||
|
||||
private:
|
||||
@@ -255,6 +201,8 @@ private:
|
||||
info = &ActiveTask;
|
||||
break;
|
||||
case TaskType::Shared:
|
||||
if (index == 0 && ActiveSharedTask)
|
||||
info = ActiveSharedTask->GetActivity();
|
||||
break;
|
||||
case TaskType::Quest:
|
||||
if (index < MAXACTIVEQUESTS)
|
||||
@@ -273,6 +221,7 @@ private:
|
||||
};
|
||||
ClientTaskInformation ActiveTasks[MAXACTIVEQUESTS + 1];
|
||||
};
|
||||
SharedTaskState *ActiveSharedTask; // pointer to our shared task managed by TaskManager
|
||||
// Shared tasks should be limited to 1 as well
|
||||
std::vector<int> EnabledTasks;
|
||||
std::vector<CompletedTaskInformation> CompletedTasks;
|
||||
@@ -280,6 +229,10 @@ private:
|
||||
bool CheckedTouchActivities;
|
||||
};
|
||||
|
||||
struct TaskReplayGroups {
|
||||
std::string name;
|
||||
int duration;
|
||||
};
|
||||
|
||||
class TaskManager {
|
||||
|
||||
@@ -289,18 +242,19 @@ public:
|
||||
int GetActivityCount(int TaskID);
|
||||
bool LoadSingleTask(int TaskID);
|
||||
bool LoadTasks(int SingleTask=0);
|
||||
bool LoadReplayGroups();
|
||||
void ReloadGoalLists();
|
||||
inline void LoadProximities(int ZoneID) { ProximityManager.LoadProximities(ZoneID); }
|
||||
bool LoadTaskSets();
|
||||
bool LoadClientState(Client *c, ClientTaskState *state);
|
||||
bool SaveClientState(Client *c, ClientTaskState *state);
|
||||
void SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList);
|
||||
void SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList);
|
||||
void SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList, bool shared = false); // dumb hack cuz we do dumb things
|
||||
void SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList, bool shared = false);
|
||||
bool AppropriateLevel(int TaskID, int PlayerLevel);
|
||||
int GetTaskMinLevel(int TaskID);
|
||||
int GetTaskMaxLevel(int TaskID);
|
||||
void TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID);
|
||||
void TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks); // task list provided by QuestManager (perl/lua)
|
||||
void TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID, bool shared = false);
|
||||
void TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks, bool shared = false); // task list provided by QuestManager (perl/lua)
|
||||
void SendActiveTasksToClient(Client *c, bool TaskComplete=false);
|
||||
void SendSingleActiveTaskToClient(Client *c, ClientTaskInformation &task_info, bool TaskComplete, bool BringUpTaskJournal = false);
|
||||
void SendTaskActivityShort(Client *c, int TaskID, int ActivityID, int ClientTaskIndex);
|
||||
@@ -316,6 +270,9 @@ public:
|
||||
bool IsTaskRepeatable(int TaskID);
|
||||
friend class ClientTaskState;
|
||||
|
||||
SharedTaskState *LoadSharedTask(int id); // loads the shared task state
|
||||
SharedTaskState *CreateSharedTask(int id, int task_id);
|
||||
SharedTaskState *GetSharedTask(int id);
|
||||
|
||||
private:
|
||||
TaskGoalListManager GoalListManager;
|
||||
@@ -323,6 +280,8 @@ private:
|
||||
TaskInformation* Tasks[MAXTASKS];
|
||||
std::vector<int> TaskSets[MAXTASKSETS];
|
||||
void SendActiveTaskDescription(Client *c, int TaskID, ClientTaskInformation &task_info, int StartTime, int Duration, bool BringUpTaskJournal=false);
|
||||
std::unordered_map<int, SharedTaskState> SharedTasks;
|
||||
std::map<int, TaskReplayGroups> replay_groups;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+103
-44
@@ -79,33 +79,50 @@ WorldServer::~WorldServer() {
|
||||
|
||||
void WorldServer::Connect()
|
||||
{
|
||||
m_connection.reset(new EQ::Net::ServertalkClient(Config->WorldIP, Config->WorldTCPPort, false, "Zone", Config->SharedKey));
|
||||
m_connection->OnConnect([this](EQ::Net::ServertalkClient *client) {
|
||||
OnConnected();
|
||||
});
|
||||
|
||||
m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
bool WorldServer::SendPacket(ServerPacket *pack)
|
||||
{
|
||||
m_connection->SendPacket(pack);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string WorldServer::GetIP() const
|
||||
{
|
||||
return m_connection->Handle()->RemoteIP();
|
||||
}
|
||||
|
||||
uint16 WorldServer::GetPort() const
|
||||
{
|
||||
return m_connection->Handle()->RemotePort();
|
||||
m_connection.reset(new EQ::WorldConnection("Zone"));
|
||||
m_connection->SetOnConnectedHandler(std::bind(&WorldServer::OnConnected, this));
|
||||
m_connection->SetOnMessageHandler(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
bool WorldServer::Connected() const
|
||||
{
|
||||
return m_connection->Connected();
|
||||
if (m_connection) {
|
||||
return m_connection->Connected();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorldServer::SendPacket(ServerPacket *pack)
|
||||
{
|
||||
if (m_connection) {
|
||||
m_connection->SendPacket(pack);
|
||||
}
|
||||
}
|
||||
|
||||
std::string WorldServer::GetIP() const
|
||||
{
|
||||
if (m_connection) {
|
||||
return m_connection->GetIP();
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
uint16 WorldServer::GetPort() const
|
||||
{
|
||||
if (m_connection) {
|
||||
return m_connection->GetPort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WorldServer::RouteMessage(const std::string &filter, const std::string &id, const EQ::Net::Packet &p)
|
||||
{
|
||||
if (m_connection) {
|
||||
m_connection->RouteMessage(filter, id, p);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldServer::SetZoneData(uint32 iZoneID, uint32 iInstanceID) {
|
||||
@@ -179,14 +196,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
ServerPacket *pack = &tpack;
|
||||
|
||||
switch (opcode) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case ServerOP_KeepAlive: {
|
||||
// ignore this
|
||||
break;
|
||||
}
|
||||
// World is tellins us what port to use.
|
||||
// World is tellins us what port to use.
|
||||
case ServerOP_SetConnectInfo: {
|
||||
if (pack->size != sizeof(ServerConnectInfo))
|
||||
break;
|
||||
@@ -1942,6 +1952,56 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_TaskGrant:
|
||||
{
|
||||
int id = pack->ReadUInt32();
|
||||
int task_id = pack->ReadUInt32();
|
||||
int taskmaster_id = pack->ReadUInt32();
|
||||
int accepted_time = pack->ReadUInt32();
|
||||
char name[64] = { 0 };
|
||||
pack->ReadString(name);
|
||||
auto client = entity_list.GetClientByName(name);
|
||||
if (client) { // guess we just do nothing if they're not here
|
||||
std::vector<std::string> members;
|
||||
int count = pack->ReadUInt32();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
pack->ReadString(name);
|
||||
members.push_back(name);
|
||||
}
|
||||
client->AssignSharedTask(task_id, taskmaster_id, id, accepted_time, members);
|
||||
} else {
|
||||
// TODO: something failed, tell world to clean up
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_TaskReject:
|
||||
{
|
||||
// this will be reworked to not use the pend shit, we will depend on hoping successive requests just get thrown out
|
||||
int message = pack->ReadUInt32();
|
||||
int taskmaster_id = pack->ReadUInt32();
|
||||
char name[64] = { 0 };
|
||||
pack->ReadString(name);
|
||||
auto client = entity_list.GetClientByName(name);
|
||||
if (client) {
|
||||
if (message == 0)
|
||||
client->Message(13, "Shared task assignment has failed.");
|
||||
else if (message > 0)
|
||||
client->Message_StringID(13, message);
|
||||
// negative nothing I guess
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_TaskZoneCreated:
|
||||
{
|
||||
int task_id = pack->ReadUInt32();
|
||||
char name[64] = { 0 };
|
||||
pack->ReadString(name);
|
||||
|
||||
auto client = entity_list.GetClientByName(name);
|
||||
if (client)
|
||||
client->AddToSharedTask(task_id);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
std::cout << " Unknown ZSopcode:" << (int)pack->opcode;
|
||||
std::cout << " size:" << pack->size << std::endl;
|
||||
@@ -1987,9 +2047,9 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu
|
||||
scm->queued = 0;
|
||||
strcpy(scm->message, buffer);
|
||||
|
||||
bool ret = SendPacket(pack);
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, uint32 type, const char* message, ...) {
|
||||
@@ -2025,9 +2085,9 @@ bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to
|
||||
sem->minstatus = to_minstatus;
|
||||
strcpy(sem->message, buffer);
|
||||
|
||||
bool ret = SendPacket(pack);
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldServer::SendVoiceMacro(Client* From, uint32 Type, char* Target, uint32 MacroNumber, uint32 GroupOrRaidID) {
|
||||
@@ -2062,15 +2122,19 @@ bool WorldServer::SendVoiceMacro(Client* From, uint32 Type, char* Target, uint32
|
||||
|
||||
svm->MacroNumber = MacroNumber;
|
||||
|
||||
bool Ret = SendPacket(pack);
|
||||
SendPacket(pack);
|
||||
|
||||
safe_delete(pack);
|
||||
|
||||
return Ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldServer::RezzPlayer(EQApplicationPacket* rpack, uint32 rezzexp, uint32 dbid, uint16 opcode)
|
||||
{
|
||||
if (!Connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Log(Logs::Detail, Logs::Spells, "WorldServer::RezzPlayer rezzexp is %i (0 is normal for RezzComplete", rezzexp);
|
||||
auto pack = new ServerPacket(ServerOP_RezzPlayer, sizeof(RezzPlayer_Struct));
|
||||
RezzPlayer_Struct* sem = (RezzPlayer_Struct*)pack->pBuffer;
|
||||
@@ -2078,14 +2142,9 @@ bool WorldServer::RezzPlayer(EQApplicationPacket* rpack, uint32 rezzexp, uint32
|
||||
sem->rez = *(Resurrect_Struct*)rpack->pBuffer;
|
||||
sem->exp = rezzexp;
|
||||
sem->dbid = dbid;
|
||||
bool ret = SendPacket(pack);
|
||||
if (ret)
|
||||
Log(Logs::Detail, Logs::Spells, "Sending player rezz packet to world spellid:%i", sem->rez.spellid);
|
||||
else
|
||||
Log(Logs::Detail, Logs::Spells, "NOT Sending player rezz packet to world");
|
||||
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldServer::SendReloadTasks(int Command, int TaskID) {
|
||||
|
||||
+6
-5
@@ -18,8 +18,8 @@
|
||||
#ifndef WORLDSERVER_H
|
||||
#define WORLDSERVER_H
|
||||
|
||||
#include "../common/eq_packet_structs.h"
|
||||
#include "../common/net/servertalk_client_connection.h"
|
||||
#include "../common/world_connection.h"
|
||||
#include <memory>
|
||||
|
||||
class ServerPacket;
|
||||
class EQApplicationPacket;
|
||||
@@ -31,10 +31,11 @@ public:
|
||||
~WorldServer();
|
||||
|
||||
void Connect();
|
||||
bool SendPacket(ServerPacket* pack);
|
||||
bool Connected() const;
|
||||
void SendPacket(ServerPacket* pack);
|
||||
std::string GetIP() const;
|
||||
uint16 GetPort() const;
|
||||
bool Connected() const;
|
||||
void RouteMessage(const std::string &filter, const std::string &id, const EQ::Net::Packet& p);
|
||||
|
||||
void HandleMessage(uint16 opcode, const EQ::Net::Packet &p);
|
||||
|
||||
@@ -72,7 +73,7 @@ private:
|
||||
uint32 cur_groupid;
|
||||
uint32 last_groupid;
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
|
||||
std::unique_ptr<EQ::WorldConnection> m_connection;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user