EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
SkillMgrService.cpp
Go to the documentation of this file.
1 /*
2  ------------------------------------------------------------------------------------
3  LICENSE:
4  ------------------------------------------------------------------------------------
5  This file is part of EVEmu: EVE Online Server Emulator
6  Copyright 2006 - 2021 The EVEmu Team
7  For the latest information visit https://evemu.dev
8  ------------------------------------------------------------------------------------
9  This program is free software; you can redistribute it and/or modify it under
10  the terms of the GNU Lesser General Public License as published by the Free Software
11  Foundation; either version 2 of the License, or (at your option) any later
12  version.
13 
14  This program is distributed in the hope that it will be useful, but WITHOUT
15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public License along with
19  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20  Place - Suite 330, Boston, MA 02111-1307, USA, or go to
21  http://www.gnu.org/copyleft/lesser.txt.
22  ------------------------------------------------------------------------------------
23  Author: Zhur
24  Updates: Allan
25 */
26 
27 #include "eve-server.h"
28 
29 #include "PyServiceCD.h"
31 
34 
36 : PyService(mgr, "skillMgr"),
37  m_dispatch(new Dispatcher(this))
38 {
39  _SetCallDispatcher(m_dispatch);
40 }
41 
43  delete m_dispatch;
44 }
45 
47  _log(CLIENT__MESSAGE, "SkillMgrService bind request for:");
48  bind_args->Dump(CLIENT__MESSAGE, " ");
49 
50  return(new SkillMgrBound(m_manager, m_db));
51 }
52 
54 : PyBoundObject(mgr),
55  m_dispatch(new Dispatcher(this)),
56  m_db(db)
57 {
59 
60  m_strBoundObjectName = "SkillMgrBound";
61 
62  PyCallable_REG_CALL(SkillMgrBound, InjectSkillIntoBrain);
63  PyCallable_REG_CALL(SkillMgrBound, GetSkillQueueAndFreePoints);
64  PyCallable_REG_CALL(SkillMgrBound, SaveSkillQueue);
65  PyCallable_REG_CALL(SkillMgrBound, AddToEndOfSkillQueue);
66  PyCallable_REG_CALL(SkillMgrBound, CharStartTrainingSkill);
67  PyCallable_REG_CALL(SkillMgrBound, CharStartTrainingSkillByTypeID);
68  PyCallable_REG_CALL(SkillMgrBound, CharStopTrainingSkill);
69  PyCallable_REG_CALL(SkillMgrBound, GetEndOfTraining);
70  PyCallable_REG_CALL(SkillMgrBound, GetSkillHistory);
71  PyCallable_REG_CALL(SkillMgrBound, CharAddImplant);
72  PyCallable_REG_CALL(SkillMgrBound, RemoveImplantFromCharacter);
73  PyCallable_REG_CALL(SkillMgrBound, GetRespecInfo);
74  PyCallable_REG_CALL(SkillMgrBound, RespecCharacter);
75  PyCallable_REG_CALL(SkillMgrBound, GetCharacterAttributeModifiers);
76 }
77 
79 {
80  delete m_dispatch;
81 }
82 
85 {
86  delete this;
87 }
88 
89 PyResult SkillMgrBound::Handle_GetRespecInfo( PyCallArgs& call ) {
90  return m_db.GetRespecInfo(call.client->GetCharacterID());
91 }
92 
93 PyResult SkillMgrBound::Handle_GetSkillQueueAndFreePoints(PyCallArgs &call) {
94  return call.client->GetChar()->SendSkillQueue();
95 }
96 
97 PyResult SkillMgrBound::Handle_GetEndOfTraining(PyCallArgs &call) {
98  return new PyLong( call.client->GetChar()->GetEndOfTraining() );
99 }
100 
101 PyResult SkillMgrBound::Handle_GetSkillHistory( PyCallArgs& call ) {
102  return call.client->GetChar()->GetSkillHistory();
103 }
104 
105 PyResult SkillMgrBound::Handle_CharStopTrainingSkill(PyCallArgs &call) {
106  // called when pausing skill queue
107  call.client->GetChar()->PauseSkillQueue();
108  // returns nothing
109  return nullptr;
110 }
111 
112 PyResult SkillMgrBound::Handle_CharStartTrainingSkill( PyCallArgs& call ) {
113  // sm.GetService('godma').GetSkillHandler().CharStartTrainingSkill(skillX.itemID, skillX.locationID)
114  Call_TwoIntegerArgs args;
115  if( !args.Decode( call.tuple ) )
116  {
117  codelog( SERVICE__ERROR, "%s: Failed to decode arguments.", GetName() );
118  return nullptr;
119  }
120 
121  _log(SKILL__WARNING, "Called CharStartTrainingSkill for itemID %i in location %i", args.arg1, args.arg2);
122  return nullptr;
123 }
124 
125 PyResult SkillMgrBound::Handle_AddToEndOfSkillQueue(PyCallArgs &call) {
126  // sm.StartService('godma').GetSkillHandler().AddToEndOfSkillQueue(skillID, nextLevel)
127  Call_TwoIntegerArgs args;
128  if (!args.Decode(&call.tuple)) {
129  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
130  return nullptr;
131  }
132 
133  CharacterRef cRef(call.client->GetChar());
134  cRef->AddToSkillQueue(args.arg1, args.arg2);
135  cRef->UpdateSkillQueueEndTime();
136  return nullptr;
137 }
138 
139 PyResult SkillMgrBound::Handle_InjectSkillIntoBrain(PyCallArgs &call)
140 {
141  Call_InjectSkillIntoBrain args;
142  if (!args.Decode(&call.tuple)) {
143  codelog( SERVICE__ERROR, "%s: Failed to decode arguments.", GetName() );
144  return nullptr;
145  }
146 
147  // make a list of skills successfully injected to display after injection
148  // name, ret value where 1=success, 2=prereqs, 3=already known, 4=split fail, 5=load fail
149  std::map<std::string, uint8> skills;
150  SkillRef skill(nullptr);
151  CharacterRef cRef(call.client->GetChar());
152  for (auto cur : args.skills) {
153  skill = sItemFactory.GetSkill(cur);
154  if (skill.get() == nullptr) {
155  _log( ITEM__ERROR, "%s: failed to load skill %u for injection.", call.client->GetName(), cur);
156  std::string str = "Invalid Name #";
157  str += std::to_string(cur);
158  skills.emplace(str, 5);
159  continue;
160  }
161 
162  skills.emplace(skill->itemName(), cRef->InjectSkillIntoBrain(skill));
163  }
164 
165  // build and populate status reply
166  if (skills.empty())
167  return nullptr;
168 
169  if (skills.size() == 1) {
170  std::string status;
171  switch (skills.begin()->second) {
172  //1=success, 2=prereqs, 3=already known, 4=split fail, 5=load fail
173  case 1: status = "<color=green>Success.</color>"; break;
174  case 2: status = "<color=red>Failed:</color> <color=yellow>Prerequisites incomplete.</color>"; break;
175  case 3: status = "<color=red>Failed:</color> <color=cyan>Skill already known.</color>"; break;
176  case 4: status = "<color=red>Failed:</color> <color=red>Stack split failure.</color>"; break;
177  case 5: status = "<color=red>Failed:</color> <color=maroon>Skill loading failure.</color>"; break;
178  default: status = "<color=red>Failed:</color> <color=red>Unknown Error.</color>"; break;
179  }
180  call.client->SendInfoModalMsg("Injection of %s: %s", skills.begin()->first.c_str(), status.c_str());
181  } else {
182  std::string status;
183  std::ostringstream str;
184  str.clear();
185  str << "The Injection of %u skills for %s has resulted in the following outcome.<br><br>"; //40
186 
187  for (auto cur : skills) {
188  switch (cur.second) {
189  //1=success, 2=prereqs, 3=already known, 4=split fail, 5=load fail
190  case 1: status = "<color=green>Success.</color>"; break;
191  case 2: status = "<color=red>Failed:</color> <color=yellow>Prerequisites incomplete.</color>"; break;
192  case 3: status = "<color=red>Failed:</color> <color=cyan>Skill already known.</color>"; break;
193  case 4: status = "<color=red>Failed:</color> <color=red>Stack split failure.</color>"; break;
194  case 5: status = "<color=red>Failed:</color> <color=maroon>Skill loading failure.</color>"; break;
195  default: status = "<color=red>Failed:</color> <color=red>Unknown Error.</color>"; break;
196  }
197  str << cur.first << " - " << status << "<br>"; //40 for name, 80 for status (120)
198  }
199 
200  int size = skills.size() * 120;
201  size += 100; // for header, including char name
202  char reply[size];
203  snprintf(reply, size, str.str().c_str(), skills.size(), call.client->GetName());
204 
205  call.client->SendInfoModalMsg(reply);
206  }
207 
208  PyTuple* tmp = new PyTuple(1);
209  tmp->SetItem(0, new PyString("OnSkillInjected"));
210  call.client->QueueDestinyEvent(&tmp);
211  return nullptr;
212 }
213 
214 PyResult SkillMgrBound::Handle_SaveSkillQueue(PyCallArgs &call) {
215  // called when previously-set skill queue changed
216  Call_SaveSkillQueue args;
217  if (!args.Decode(&call.tuple)) {
218  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
219  return nullptr;
220  }
221 
222  // xml decode will now check for and fix level being a float instead of int and leading to client freakout
223  CharacterRef cRef(call.client->GetChar());
224  _log(SKILL__QUEUE, "%s(%u) calling SaveSkillQueue()", cRef->name(), cRef->itemID());
225  cRef->ClearSkillQueue(true);
226  SkillQueue_Element el;
227  std::vector<PyRep*>::const_iterator cur = args.queue->begin(), end = args.queue->end();
228  for (; cur != end; ++cur) {
229  if (!el.Decode(*cur)) {
230  _log(SERVICE__ERROR, "%s: Failed to decode element of SkillQueue (%u). Skipping.", call.client->GetName(), *cur);
231  continue;
232  }
233  cRef->AddToSkillQueue( el.typeID, el.level );
234  }
235 
236  cRef->UpdateSkillQueueEndTime();
237  PyTuple* tmp = new PyTuple(1);
238  tmp->SetItem(0, new PyString("OnSkillTrainingSaved"));
239  call.client->QueueDestinyEvent(&tmp);
240  return nullptr;
241 }
242 
243 PyResult SkillMgrBound::Handle_CharStartTrainingSkillByTypeID( PyCallArgs& call )
244 {
245  // called when skill queue empty or paused
246  // sends skill typeID to start training
247  Call_SingleIntegerArg args;
248  if (!args.Decode(&call.tuple)) {
249  codelog( SERVICE__ERROR, "%s: Failed to decode arguments.", GetName() );
250  return nullptr;
251  }
252 
253  call.client->GetChar()->LoadPausedSkillQueue(args.arg);
254  return nullptr;
255 }
256 
257 PyResult SkillMgrBound::Handle_RespecCharacter(PyCallArgs &call)
258 {
259  Call_RespecCharacter args;
260  if (!args.Decode(&call.tuple)) {
261  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
262  return nullptr;
263  }
264 
265  CharacterRef cRef(call.client->GetChar());
266  if (cRef->GetSkillInTraining() != nullptr)
267  throw UserError ("RespecSkillInTraining");
268 
269  // return early if this is an illegal call
270  if (!m_db.ReportRespec(call.client->GetCharacterID()))
271  return nullptr;
272  uint8 multiplier(sConfig.character.statMultiplier);
273  cRef->SetAttribute(AttrCharisma, args.charisma * multiplier);
274  cRef->SetAttribute(AttrIntelligence, args.intelligence * multiplier);
275  cRef->SetAttribute(AttrMemory, args.memory * multiplier);
276  cRef->SetAttribute(AttrPerception, args.perception * multiplier);
277  cRef->SetAttribute(AttrWillpower, args.willpower * multiplier);
278  cRef->SaveAttributes();
279 
280  // no return value
281  return nullptr;
282 }
283 
284 PyResult SkillMgrBound::Handle_GetCharacterAttributeModifiers(PyCallArgs &call)
285 {
286  // for (itemID, typeID, operation, value,) in modifiers:
287 
288  /*
289  * client sends attrib# of stat in question...
290  * [PyString "GetCharacterAttributeModifiers"]
291  * [PyTuple 1 items]
292  * [PyInt 165]
293  * we return this...
294  * [PyList 1 items]
295  * [PyTuple 4 items]
296  * [PyIntegerVar 1866309449] << implantID
297  * [PyInt 9943] << implantTypeID
298  * [PyInt 2] << operation
299  * [PyFloat 3] << value
300  */
301  Call_SingleIntegerArg args;
302  if (!args.Decode(&call.tuple)) {
303  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
304  return nullptr;
305  }
306 
307  CharacterRef cRef(call.client->GetChar());
308  PyList* list = new PyList();
309  // for each implant, make tuple and put into list
310  PyTuple* tuple = new PyTuple(4);
311  tuple->SetItem(0, PyStatic.NewZero()); //implantID
312  tuple->SetItem(1, PyStatic.NewZero()); //implantTypeID
313  tuple->SetItem(2, PyStatic.NewZero()); //operation
314  tuple->SetItem(3, PyStatic.NewZero()); //value
315  list->AddItem(tuple);
316 
317  return list;
318 }
319 
320 PyResult SkillMgrBound::Handle_CharAddImplant( PyCallArgs& call )
321 {
322  //sends itemid
323  Call_SingleIntegerArg args;
324  if (!args.Decode(&call.tuple)) {
325  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
326  return nullptr;
327  }
328 
329  //{'FullPath': u'UI/Messages', 'messageID': 259242, 'label': u'OnlyOneBoosterActiveBody'}(u'You cannot consume the {typeName} as you are already using another similar booster {typeName2}.', None, {u'{typeName}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'typeName'}, u'{typeName2}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'typeName2'}})
330  //{'FullPath': u'UI/Messages', 'messageID': 259243, 'label': u'OnlyOneImplantActiveBody'}(u'You cannot install the {typeName} as there is already an implant installed in the slot it needs to occupy.', None, {u'{typeName}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'typeName'}})
331 
332  return nullptr;
333 }
334 
335 PyResult SkillMgrBound::Handle_RemoveImplantFromCharacter( PyCallArgs& call )
336 {
337  //sends itemid
338  Call_SingleIntegerArg args;
339  if (!args.Decode(&call.tuple)) {
340  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
341  return nullptr;
342  }
343 
344  return nullptr;
345 }
Base Python wire object.
Definition: PyRep.h:66
#define sConfig
A macro for easier access to the singleton.
Dispatcher *const m_dispatch
unsigned __int8 uint8
Definition: eve-compat.h:46
virtual void Release()
#define _log(type, fmt,...)
Definition: logsys.h:124
Python string.
Definition: PyRep.h:430
PyRep * GetRespecInfo(uint32 characterId)
void QueueDestinyEvent(PyTuple **multiEvent)
Definition: Client.cpp:2124
PyTuple * SendSkillQueue()
Definition: Character.cpp:660
void SendInfoModalMsg(const char *fmt,...)
Definition: Client.cpp:2756
PyCallable_Make_InnerDispatcher(SkillMgrService) PyCallable_Make_InnerDispatcher(SkillMgrBound) SkillMgrService
int32 GetCharacterID() const
Definition: Client.h:113
CharacterDB & m_db
std::string m_strBoundObjectName
Definition: PyBoundObject.h:54
CharacterRef GetChar() const
Definition: Client.h:164
Python tuple.
Definition: PyRep.h:567
void Dump(FILE *into, const char *pfx) const
Dumps object to file.
Definition: PyRep.cpp:84
void _SetCallDispatcher(CallDispatcher *d)
Definition: PyCallable.h:87
* args
void AddToSkillQueue(uint16 typeID, uint8 level)
Definition: Character.cpp:907
Dispatcher *const m_dispatch
SkillMgrBound(PyServiceMgr *mgr, CharacterDB &db)
int64 GetEndOfTraining()
Definition: Character.cpp:624
#define codelog(type, fmt,...)
Definition: logsys.h:128
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
#define snprintf
Definition: eve-compat.h:184
PyServiceMgr *const m_manager
Definition: PyService.h:91
#define PyStatic
Definition: PyRep.h:1209
const char * GetName() const
Definition: Client.h:94
Client *const client
Definition: PyCallable.h:49
Python object "ccp_exceptions.UserError".
Definition: PyExceptions.h:121
#define PyCallable_REG_CALL(c, m)
Definition: PyServiceCD.h:78
Definition: Client.h:66
CharacterDB m_db
virtual PyBoundObject * CreateBoundObject(Client *pClient, const PyRep *bind_args)
PyRep * GetSkillHistory()
Definition: Character.cpp:542
Dispatcher *const m_dispatch
bool ReportRespec(uint32 characterId)
#define sItemFactory
Definition: ItemFactory.h:165
void LoadPausedSkillQueue(uint16 typeID)
Definition: Character.cpp:758
virtual ~SkillMgrService()
void PauseSkillQueue()
Definition: Character.cpp:748
virtual ~SkillMgrBound()
Python list.
Definition: PyRep.h:639
const char * GetName() const
Definition: PyBoundObject.h:44
Python long integer.
Definition: PyRep.h:261
PyTuple * tuple
Definition: PyCallable.h:50