Compare commits

...

57 Commits

Author SHA1 Message Date
KimLS b9b2bf0477 Integrating protobuffers 2019-06-13 15:16:12 -07:00
KimLS 0b26c80d8f Merge branch 'shared_tasks' of github.com:EQEmu/Server into shared_tasks_service 2019-06-10 21:53:41 -07:00
KimLS 8497042eef Some changes to routing and boilerplate for task service 2019-06-10 21:51:18 -07:00
Michael Cook (mackal) 1304b9c80f DB scheme changes 2019-06-10 22:40:05 -04:00
KimLS 65c9c86556 Some changes to the tasks service and the general routing strategy 2019-06-10 01:10:28 -07:00
Michael Cook (mackal) ece9251bd6 Merge branch 'master' into shared_tasks 2019-06-10 00:37:57 -04:00
Michael Cook (mackal) 3cbd0fe0f6 Add char_id to SharedTaskMember 2019-06-09 23:10:36 -04:00
Michael Cook (mackal) 8141f831b1 Merge branch 'master' into shared_tasks 2019-06-09 21:42:27 -04:00
KimLS b3a3d9bec5 Shared task service 2019-06-09 18:39:32 -07:00
Michael Cook (mackal) fa2b5166fb Add in SharedTaskState::MemberZoned hooks
This should be all of them ... at least where raid/group is done ...
2019-06-09 17:59:30 -04:00
Michael Cook (mackal) 8dc25c838b SharedTaskState::MemberZoned find by pointer vale not name 2019-06-09 17:59:20 -04:00
Michael Cook (mackal) 229de34afc Add Client::GetSharedTask 2019-06-09 17:53:45 -04:00
Michael Cook (mackal) 70bdd35b69 Add a Getter for ActiveShareTask 2019-06-09 17:52:11 -04:00
Michael Cook (mackal) e99354223d We need to update the ActiveSharedTask pointer
When we leave, we need to make sure it's managed as well!
2019-06-09 17:51:34 -04:00
Michael Cook (mackal) 8a8e922f46 We need some way to keep track of everyone who needs a lockout
This prevents exploits but is also rather annoying for people who leave
2019-06-09 17:50:33 -04:00
KimLS c1484a698c Comms stuff 2019-06-08 21:49:51 -07:00
Michael Cook (mackal) bd96d676be Work on getting tasks sent to other members
Some stuff just stubbed out for now
2019-06-08 23:43:36 -04:00
Michael Cook (mackal) 2a637a031b Other task stuff in zone is using time not our timer 2019-06-08 23:09:23 -04:00
Michael Cook (mackal) f7c7f5646e Rename PendSharedTask and update comment 2019-06-08 17:38:04 -04:00
Michael Cook (mackal) 577e67f4ee Move stuff around 2019-06-08 17:11:59 -04:00
Michael Cook (mackal) 1084b71d8d Use SQL to fix null 2019-06-08 17:07:41 -04:00
Michael Cook (mackal) c2ab2a232b Bug fix in SharedTaskManager::LoadSharedTaskState 2019-06-07 01:39:43 -04:00
Michael Cook (mackal) c5f739cbda Add shared_tasks loading in world 2019-06-07 01:16:44 -04:00
Michael Cook (mackal) 385732f403 Remove dead idea in SharedTaskManager::HandleTaskZoneCreated
We'll just load from DB if the task doesn't exist yet
2019-06-03 23:43:11 -04:00
Michael Cook (mackal) b972ec581f Rework accepted_time for shared_tasks 2019-06-02 23:32:53 -04:00
KimLS 32e04cd264 Fix for crash in world from shared tasks 2019-06-02 00:46:11 -07:00
Michael Cook (mackal) b0dff0c006 Some work on world's task activity states 2019-05-30 02:00:29 -04:00
Michael Cook (mackal) c093b3e2ab Stub out some more functions 2019-05-30 01:37:23 -04:00
Michael Cook (mackal) c5bf71f221 The requestor should at least be able to see the new shared task and get
a member list

There are still a few issues that need to be solved this way.
We could try searching for the members in zone (by name ...) or we could
just fire off to world and world will have to send a special full
serialization so the new zone could load the state. Or at this point we
could just assume that we have saved the state to the DB (which hasn't
been coded yet) and any out of zone members will have to depend on
loading it up from the DB.
2019-05-26 00:38:28 -04:00
Michael Cook (mackal) 977c3ca3dc Tweak some stuff in world 2019-05-25 21:52:21 -04:00
Michael Cook (mackal) 408ce4650f Implement ClientListEntry::LoadTaskLockouts 2019-05-22 22:38:07 -04:00
Michael Cook (mackal) bc0f705227 Implement SharedTaskManager::LoadSharedTaskState 2019-05-22 15:30:00 -04:00
Michael Cook (mackal) 5c1ab3b24c Move ClientTaskInformation to global task header 2019-05-21 22:03:08 -04:00
Michael Cook (mackal) 85a858fcd6 More comments [skip ci] 2019-05-21 17:10:29 -04:00
Michael Cook (mackal) 6ab2e46f42 Implement SharedTaskManager::LoadSharedTasks 2019-05-21 17:00:34 -04:00
Michael Cook (mackal) 96acb1e638 Finish SharedTaskManager::HandleTaskRequest
Cleaned up some other functions as well
2019-05-21 00:07:14 -04:00
Michael Cook (mackal) eb98eef1b9 Flush out task verification steps 2019-05-18 23:58:36 -04:00
Michael Cook (mackal) e1bb3301a5 World should verify the full task to simplify things 2019-05-18 17:26:09 -04:00
Michael Cook (mackal) 358ce2ca94 Merge branch 'master' into shared_tasks 2019-05-16 22:21:31 -04:00
Michael Cook (mackal) 6856d1540b Fix spelling 2019-04-30 17:29:30 -04:00
Michael Cook (mackal) cd1306d52c Some tweaks to next ID logic and comment up a load function 2019-04-30 17:25:20 -04:00
Michael Cook (mackal) 20aa1cbe77 Merge branch 'master' into shared_tasks 2019-04-30 17:13:57 -04:00
Michael Cook (mackal) dfd9a3c714 Merge branch 'master' into shared_tasks 2019-04-26 17:57:31 -04:00
Michael Cook (mackal) c04bf79b0f Merge branch 'master' into shared_tasks 2019-03-25 19:42:52 -04:00
Michael Cook (mackal) 7286e6a37f Merge branch 'master' into shared_tasks 2018-12-16 14:46:35 -05:00
Michael Cook (mackal) a244cec9e8 Merge branch 'master' into shared_tasks 2018-10-22 20:29:02 -04:00
Michael Cook (mackal) 7062e2703b Merge branch 'master' into shared_tasks 2018-10-11 20:24:26 -04:00
Michael Cook (mackal) 5f53856fd4 Merge branch 'master' into shared_tasks 2018-10-09 14:22:20 -04:00
Michael Cook (mackal) 17d63bc3f6 Merge branch 'master' into shared_tasks 2018-10-08 12:58:25 -04:00
Michael Cook (mackal) 47fda0f747 Merge branch 'master' into shared_tasks 2018-09-26 23:24:27 -04:00
Michael Cook (mackal) e2c15dbc9e Merge branch 'master' into shared_tasks 2018-09-12 13:34:16 -04:00
Michael Cook (mackal) c089296538 Merge branch 'master' into shared_tasks 2018-09-08 00:25:31 -04:00
Michael Cook (mackal) 94f09e5287 Merge branch 'master' into shared_tasks 2018-09-07 20:18:22 -04:00
Michael Cook (mackal) 93133c289e More shared tasks stuff
Mostly just OOZ checking
2018-09-05 16:04:49 -04:00
Michael Cook (mackal) 8934235030 Merge branch 'master' into shared_tasks 2018-09-03 12:58:47 -04:00
Michael Cook (mackal) ea0a54ed60 More work on shared tasks 2018-09-02 23:14:15 -04:00
Michael Cook (mackal) 39544b4723 Shared Task WIP 2018-09-01 17:32:13 -04:00
81 changed files with 2778 additions and 908 deletions
+12 -2
View File
@@ -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
View File
@@ -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)
+2
View File
@@ -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),
+9
View File
@@ -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;
+170
View File
@@ -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 */
+23
View File
@@ -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
View File
@@ -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;
};
}
}
}
+1 -146
View File
@@ -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);
+3 -15
View File
@@ -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);
+28 -5
View File
@@ -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) {
+10 -13
View File
@@ -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;
+1 -149
View File
@@ -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) {
+1 -14
View File
@@ -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
};
}
}
+18
View File
@@ -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;
}
+1
View File
@@ -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
View File
@@ -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()
+75
View File
@@ -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;
}
+43
View File
@@ -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;
};
}
+75
View File
@@ -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);
}
}
+48
View File
@@ -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);
};
}
+7 -19
View File
@@ -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);
}
}
+4 -9
View File
@@ -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;
+1 -1
View File
@@ -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)
+2 -3
View File
@@ -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();
+3 -34
View File
@@ -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
View File
@@ -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:
+19
View File
@@ -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)
+17
View File
@@ -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() {
}
+12
View File
@@ -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();
};
+70
View File
@@ -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);
+23
View File
@@ -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;
};
}
+17
View File
@@ -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)
+61
View File
@@ -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);
+19
View File
@@ -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);
};
}
+17
View File
@@ -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)
+40
View File
@@ -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);
+23
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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
+3 -1
View File
@@ -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 #
+3 -1
View File
@@ -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)
+2
View File
@@ -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
+50
View File
@@ -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)
);
+4
View File
@@ -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
+1
View File
@@ -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,
+85
View File
@@ -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;
}
+20
View File
@@ -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;
};
+1 -7
View File
@@ -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;
}
}
+21
View File
@@ -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);
+73
View File
@@ -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);
}
}
}
+19
View File
@@ -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);
};
+535
View File
@@ -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;
}
+117
View File
@@ -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 */
-8
View File
@@ -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.");
-7
View File
@@ -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();
}
}
-1
View File
@@ -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
View File
@@ -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;
-1
View File
@@ -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(); } }
+4
View File
@@ -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;
+5
View File
@@ -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
View File
@@ -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; }
+15
View File
@@ -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))
+1
View File
@@ -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);
+17
View File
@@ -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
View File
@@ -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)
+18
View File
@@ -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);
+3
View 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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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