EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
TradeService.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: Luck (outline only)
24  Updates: Allan (coded working system)
25 */
26 
27 #include "eve-server.h"
28 
29 #include <unordered_map>
30 
31 #include "EntityList.h"
32 #include "PyBoundObject.h"
33 #include "PyServiceCD.h"
34 #include "StaticDataMgr.h"
35 #include "account/AccountService.h"
36 #include "station/TradeService.h"
37 #include "system/SystemManager.h"
38 #include "system/Container.h"
39 
41 
43 : public PyBoundObject
44 {
45  friend TradeService;
46 
47 public:
49 
51  : PyBoundObject(mgr),
52  m_dispatch(new Dispatcher(this))
53  {
55 
56  m_strBoundObjectName = "TradeBound";
57  m_TSvc = (TradeService*)(mgr->LookupService("trademgr"));
58 
60  PyCallable_REG_CALL(TradeBound, IsCEOTrade);
61  PyCallable_REG_CALL(TradeBound, GetItemID);
65  PyCallable_REG_CALL(TradeBound, ToggleAccept);
66  PyCallable_REG_CALL(TradeBound, OfferMoney);
68  }
69  virtual ~TradeBound()
70  {
71  delete m_dispatch;
72  }
73 
74  virtual void Release() {
75  //this needs to die
76  delete this;
77  }
78 
79  void ExchangeItems(Client* pClient, Client* pOther, TradeSession* pTSes);
80  void CancelTrade(Client* pClient, Client* pOther, TradeSession* pTSes);
81 
83  PyCallable_DECL_CALL(IsCEOTrade);
84  PyCallable_DECL_CALL(GetItemID);
85  PyCallable_DECL_CALL(GetItem);
87  PyCallable_DECL_CALL(MultiAdd);
88  PyCallable_DECL_CALL(ToggleAccept);
89  PyCallable_DECL_CALL(OfferMoney);
91 
92 protected:
93  Dispatcher *const m_dispatch;
94  TradeService* m_TSvc; // get registered TradeService object
95 };
96 
97 
99 : PyService(mgr, "trademgr"),
100  m_dispatch(new Dispatcher(this))
101 {
102  m_SvcMgr = mgr;
105 
107 }
108 
110  delete m_dispatch;
111 }
112 
114  // each client's trade session has it's own bound object.
115  // create code for multiple sessions per client, using TradeBound and TradeSession.
116  Trade_BindArgs args;
117  //crap
118  PyRep* tmp(bind_args->Clone());
119  if (!args.Decode(&tmp)) {
120  codelog(SERVICE__ERROR, "%s: Failed to decode bind args.", GetName());
121  return nullptr;
122  }
123 
124  _log(COLLECT__OTHER_DUMP, "Trade bind request for:");
125  args.Dump(COLLECT__OTHER_DUMP, " ");
126 
129  // check to see if this is target calling for a bound object. if not, create new session
130  std::map<uint32, ActiveSession>::iterator itr = m_activeSessions.find(args.myID);
131  if (itr == m_activeSessions.end()) {
132  TradeSession* pTSes = new TradeSession();
133  pClient->SetTradeSession(pTSes);
134  uint32 contID(GetTradeSessionID());
135  pTSes->m_tradeSession.containerID = contID;
136  pTSes->m_tradeSession.stationID = args.stationID;
137  pTSes->m_tradeSession.myID = args.myID;
138  pTSes->m_tradeSession.herID = args.herID;
139  pTSes->m_tradeSession.myState = false;
140  pTSes->m_tradeSession.herState = false;
141  pTSes->m_tradeSession.myMoney = args.myMoney;
142  pTSes->m_tradeSession.herMoney = args.herMoney;
143  pTSes->m_tradeSession.fileTime = args.fileTime;
145  cAS.myID = args.myID;
146  cAS.herID = args.herID;
147  cAS.contID = contID;
148  cAS.ourTS = pTSes;
149  m_activeSessions.insert(std::make_pair(args.myID, cAS));
150  m_activeSessions.insert(std::make_pair(args.herID, cAS));
151  } else {
152  pClient->SetTradeSession(itr->second.ourTS);
153  }
154 
155  TradeBound* pTB = new TradeBound(m_manager);
156  return pTB;
157 }
158 
159 PyResult TradeBound::Handle_OfferMoney(PyCallArgs &call) {
160  TradeSession* pTSes = call.client->GetTradeSession();
161  Client* pClient = sEntityList.FindClientByCharID(pTSes->m_tradeSession.myID);
162  Client* pOther = sEntityList.FindClientByCharID(pTSes->m_tradeSession.herID);
163  PyList* list = new PyList(2);
164 
165  if (call.client->GetCharacterID() == pTSes->m_tradeSession.myID) {
166  // this is 'my'
167  pTSes->m_tradeSession.myMoney = call.tuple->GetItem(0)->AsFloat()->value();
168  list->SetItem(0, new PyFloat(pTSes->m_tradeSession.myMoney)); //myMoney
169  list->SetItem(1, new PyFloat(pTSes->m_tradeSession.herMoney)); //herMoney
170  } else if (call.client->GetCharacterID() == pTSes->m_tradeSession.herID) {
171  // this is 'her'
172  pTSes->m_tradeSession.herMoney = call.tuple->GetItem(0)->AsFloat()->value();
173  list->SetItem(0, new PyFloat(pTSes->m_tradeSession.myMoney)); //myMoney
174  list->SetItem(1, new PyFloat(pTSes->m_tradeSession.herMoney)); //herMoney
175  } else {
176  list->SetItem(0, new PyFloat(0.0f)); //myMoney
177  list->SetItem(1, new PyFloat(0.0f)); //herMoney
178  _log(CLIENT__ERROR, "TradeBound::Handle_OfferMoney() : %s(%u) - clients are neither mine nor hers.", \
179  call.client->GetName(), call.client->GetCharacterID());
180  return PyStatic.NewNone();
181  }
182 
183  // reset states after offer changes..
184  pTSes->m_tradeSession.myState = false;
185  pTSes->m_tradeSession.herState = false;
186  // send changes
187  PyTuple* tuple = new PyTuple(3);
188  tuple->SetItem(0, new PyString("MoneyOffer"));
189  tuple->SetItem(1, new PyInt(pTSes->m_tradeSession.containerID));
190  tuple->SetItem(2, list);
191  PyIncRef(tuple);
192  // now send it, bypassing the extra shit and wrong dest name added in Client::SendNotification
193  pClient->SendNotification("OnTrade", "charid", &tuple);
194  pOther->SendNotification("OnTrade", "charid", &tuple);
195  // returns none
196  return PyStatic.NewNone();
197 }
198 
199 PyResult TradeBound::Handle_Abort(PyCallArgs &call) {
200  TradeSession* pTSes = call.client->GetTradeSession();
201  Client* pClient = sEntityList.FindClientByCharID(pTSes->m_tradeSession.myID);
202  Client* pOther = sEntityList.FindClientByCharID(pTSes->m_tradeSession.herID);
203 
204  CancelTrade(pClient, pOther, pTSes);
205 
206  PyTuple* tuple = new PyTuple(2);
207  tuple->SetItem(0, new PyString("Cancel"));
208  tuple->SetItem(1, new PyInt(pTSes->m_tradeSession.containerID));
209  PyIncRef(tuple);
210  // now send it, bypassing the extra shit and wrong dest name added in Client::SendNotification
211  pClient->SendNotification("OnTrade", "charid", &tuple);
212  pOther->SendNotification("OnTrade", "charid", &tuple);
215  SafeDelete(pTSes);
216  pClient->ClearTradeSession();
217  pOther->ClearTradeSession();
218  // returns none
219  return PyStatic.NewNone();
220 }
221 
222 void TradeBound::CancelTrade(Client* pClient, Client* pOther, TradeSession* pTSes)
223 {
224  // trade canceled. send items back to owner. (monies not taken at this point)
225  PyDict* dict = new PyDict();
227 
228  uint32 stationID = pTSes->m_tradeSession.stationID;
229  for (auto cur : pTSes->m_tradelist) {
230  InventoryItemRef itemRef = sItemFactory.GetItem(cur.itemID);
231  if (itemRef.get() == nullptr) {
232  _log(PLAYER__ERROR, "TradeBound::CancelTrade() - Failed to get ItemRef.");
233  continue;
234  }
235 
236  itemRef->Move(stationID, flagHangar, true);
237  }
238 }
239 
240 PyResult TradeBound::Handle_ToggleAccept(PyCallArgs &call) {
241  TradeSession* pTSes = call.client->GetTradeSession();
242  Client* pClient(nullptr);
243  Client* pOther(nullptr);
244 
245  bool myAccept = pTSes->m_tradeSession.myState;
246  bool herAccept = pTSes->m_tradeSession.herState;
247 
248  if (call.client->GetCharacterID() == pTSes->m_tradeSession.myID) {
249  // this is 'my'
250  pClient = sEntityList.FindClientByCharID(pTSes->m_tradeSession.myID);
251  pOther = sEntityList.FindClientByCharID(pTSes->m_tradeSession.herID);
252  myAccept = call.tuple->GetItem(0)->AsBool()->value();
253  } else if (call.client->GetCharacterID() == pTSes->m_tradeSession.herID) {
254  // this is 'her'
255  pClient = sEntityList.FindClientByCharID(pTSes->m_tradeSession.herID);
256  pOther = sEntityList.FindClientByCharID(pTSes->m_tradeSession.myID);
257  herAccept = call.tuple->GetItem(0)->AsBool()->value();
258  } else {
259  _log(PLAYER__TRADE_MESSAGE, "TradeBound::Handle_ToggleAccept() : %s(%u) - clients are neither mine nor hers.", \
260  call.client->GetName(), call.client->GetCharacterID());
261  return PyStatic.NewNone();
262  }
263 
264  bool forceTrade = false;
265  if (call.byname.find("forceTrade") != call.byname.cend())
266  if (!call.byname.find("forceTrade")->second->IsNone())
267  forceTrade = call.byname.find("forceTrade")->second->AsBool()->value();
268 
269  if (forceTrade) {
270  pTSes->m_tradeSession.myState = true;
271  pTSes->m_tradeSession.herState = true;
272  } else {
273  pTSes->m_tradeSession.myState = myAccept;
274  pTSes->m_tradeSession.herState = herAccept;
275  }
276 
277  _log(PLAYER__TRADE_MESSAGE, "TradeBound::Handle_ToggleAccept() is now %s/%s. forceTrade is %s.", \
278  (myAccept ? "true" : "false"), (herAccept ? "true" : "false"), (forceTrade ? "true" : "false"));
279 
280  PyTuple* tuple = new PyTuple(3);
281  tuple->SetItem(0, new PyString("StateToggle"));
282  tuple->SetItem(1, new PyBool(myAccept));
283  tuple->SetItem(2, new PyBool(herAccept));
284  PyIncRef(tuple);
285  // now send it, bypassing the extra shit and wrong dest name added in Client::SendNotification
286  pClient->SendNotification("OnTrade", "charid", &tuple);
287  pOther->SendNotification("OnTrade", "charid", &tuple);
288 
289  if (myAccept && herAccept) {
290  ExchangeItems(pClient, pOther, pTSes); // trade completed.
293  pClient->ClearTradeSession();
294  pOther->ClearTradeSession();
295  }
296 
297  // returns none
298  return PyStatic.NewNone();
299 }
300 
301 PyResult TradeBound::Handle_GetItemID(PyCallArgs &call) {
302  _log(CLIENT__CALL_DUMP, "TradeBound::Handle_GetItemID()");
303  call.Dump(CLIENT__CALL_DUMP);
304  // still not sure what this does...only returns PyNone in packet logs.
305  // returns none
306  return PyStatic.NewNone();
307 }
308 
310 PyResult TradeBound::Handle_Add(PyCallArgs &call) {
311  Call_TwoIntegerArgs args;
312  /* .arg1 = itemID to insert into trade
313  * .arg2 = item's current containerID
314  * call.byname "qty"
315  */
316  if (!args.Decode(&call.tuple)) {
317  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
318  return Handle_Abort(call);
319  }
320 
321  InventoryItemRef itemRef = sItemFactory.GetItem(args.arg1);
322  if (itemRef.get() == nullptr) {
323  _log(PLAYER__TRADE_MESSAGE, "TradeBound::Handle_Add() - Failed to get ItemRef.");
324  // should i abort trade, or just return null here? single add, so not a big deal.
325  // return null, let them try again if they want. maybe later add config option?
326  //Handle_Abort(call); << this will cancel and nullify the trade session
327  return PyStatic.NewNone();
328  }
329 
330  TradeSession* pTSes = call.client->GetTradeSession();
331  Client* pClient(sEntityList.FindClientByCharID(pTSes->m_tradeSession.myID));
332  Client* pOther(sEntityList.FindClientByCharID(pTSes->m_tradeSession.herID));
333 
334  if (call.client->GetCharacterID() == pTSes->m_tradeSession.myID) {
335  // this is 'my'
336  } else if (call.client->GetCharacterID() == pTSes->m_tradeSession.herID) {
337  // this is 'her'
338  } else {
339  _log(PLAYER__TRADE_MESSAGE, "TradeBound::Handle_Add() : %s(%u) & %s(%u) - clients are neither mine nor hers.", \
340  pClient->GetName(), pClient->GetCharacterID(), pOther->GetName(), pOther->GetCharacterID());
341  return PyStatic.NewNone();
342  }
343 
344  uint32 flag(0);
345  if (call.byname.find("flag") != call.byname.cend())
346  flag = PyRep::IntegerValueU32(call.byname.find("flag")->second);
347  uint32 qty(0);
348  if (call.byname.find("qty") != call.byname.cend())
349  qty = PyRep::IntegerValue(call.byname.find("qty")->second);
350 
351  uint32 tradeContainerID(pTSes->m_tradeSession.containerID);
353  mTI.itemID = args.arg1;
354  mTI.typeID = itemRef->typeID();
355  mTI.ownerID = call.client->GetCharacterID();
356  mTI.locationID = tradeContainerID;
357  mTI.flagID = itemRef->flag();
358  mTI.quantity = qty;
359  mTI.groupID = itemRef->groupID();
360  mTI.singleton = itemRef->isSingleton();
361  mTI.categoryID = itemRef->categoryID();
362  mTI.customInfo = "";
363  pTSes->m_tradelist.insert(pTSes->m_tradelist.end(), mTI);
364 
365  itemRef->Move(tradeContainerID, (EVEItemFlags)flag, true);
366 
367  PyDict* dict = new PyDict();
368  dict->SetItem(new PyInt(Inv::Update::Location), new PyInt(args.arg2));
369 
370  PyPackedRow* row = new PyPackedRow( sDataMgr.CreateHeader() );
371  row->SetField("itemID", new PyLong(mTI.itemID));
372  row->SetField("typeID", new PyInt(mTI.typeID));
373  row->SetField("ownerID", new PyInt(mTI.ownerID));
374  row->SetField("locationID", new PyInt(mTI.locationID));
375  row->SetField("flagID", new PyInt(mTI.flagID));
376  row->SetField("groupID", new PyInt(mTI.groupID));
377  row->SetField("categoryID", new PyInt(mTI.categoryID));
378  row->SetField("quantity", new PyInt(mTI.singleton?-1:mTI.quantity));
379  row->SetField("customInfo", new PyString(mTI.customInfo));
380 
381  PyTuple* tuple = new PyTuple(2);
382  tuple->SetItem(0, row);
383  tuple->SetItem(1, dict);
384  PyIncRef(tuple);
385  // now send it, bypassing the extra shit and wrong dest name added in Client::SendNotification
386  pClient->SendNotification("OnItemChange", "charid", &tuple);
387  pOther->SendNotification("OnItemChange", "charid", &tuple);
388 
389  // reset states after offer changes..
390  pTSes->m_tradeSession.myState = false;
391  pTSes->m_tradeSession.herState = false;
392  // return none
393  return PyStatic.NewNone();
394 }
395 
396 PyResult TradeBound::Handle_MultiAdd(PyCallArgs &call) {
397  TradeMultiAddList args;
398  /* .ints = list of itemIDs to insert into trade
399  * .contID = item's current containerID
400  * call.byname "flag"
401  */
402  if (!args.Decode(&call.tuple)) {
403  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
404  return Handle_Abort(call);
405  }
406 
407  uint32 flag(0);
408  if (call.byname.find("flag") != call.byname.cend())
409  flag = PyRep::IntegerValueU32(call.byname.find("flag")->second);
410 
411  PyDict* dict = new PyDict();
412  dict->SetItem(new PyInt(Inv::Update::Location), new PyInt(args.contID));
413 
414  TradeSession* pTSes = call.client->GetTradeSession();
415  Client* pClient = sEntityList.FindClientByCharID(pTSes->m_tradeSession.myID);
416  Client* pOther = sEntityList.FindClientByCharID(pTSes->m_tradeSession.herID);
417 
418  if (call.client->GetCharacterID() == pTSes->m_tradeSession.myID) {
419  // this is 'my'
420  } else if (call.client->GetCharacterID() == pTSes->m_tradeSession.herID) {
421  // this is 'her'
422  } else {
423  _log(PLAYER__TRADE_MESSAGE, "TradeBound::Handle_MultiAdd() : %s(%u) & %s(%u) - clients are neither mine nor hers.", \
424  pClient->GetName(), pClient->GetCharacterID(), pOther->GetName(), pOther->GetCharacterID());
425  return PyStatic.NewNone();
426  }
427 
428  uint32 charID = call.client->GetCharacterID();
429  uint32 tradeContID = pTSes->m_tradeSession.containerID;
430 
431  DBRowDescriptor* header = sDataMgr.CreateHeader();
432  std::vector<int32> list = args.ints;
433  for (auto cur : list) {
434  InventoryItemRef itemRef = sItemFactory.GetItem(cur);
435  if (itemRef.get() == nullptr) {
436  _log(PLAYER__ERROR, "TradeBound::Handle_Add() - Failed to get ItemRef.");
437  continue;
438  }
439 
442  mTI.itemID = cur;
443  mTI.typeID = itemRef->typeID();
444  mTI.ownerID = charID;
445  mTI.locationID = tradeContID;
446  mTI.flagID = flag;
447  mTI.quantity = itemRef->quantity();
448  mTI.groupID = itemRef->groupID();
449  mTI.singleton = itemRef->isSingleton();
450  mTI.categoryID = itemRef->categoryID();
451  mTI.customInfo = "";
452  pTSes->m_tradelist.insert(pTSes->m_tradelist.end(), mTI);
453  itemRef->Move(tradeContID, (EVEItemFlags)flag, true);
454 
455  PyPackedRow* row = new PyPackedRow( header );
456  row->SetField("itemID", new PyLong(mTI.itemID));
457  row->SetField("typeID", new PyInt(mTI.typeID));
458  row->SetField("ownerID", new PyInt(mTI.ownerID));
459  row->SetField("locationID", new PyInt(mTI.locationID));
460  row->SetField("flagID", new PyInt(mTI.flagID));
461  row->SetField("groupID", new PyInt(mTI.groupID));
462  row->SetField("categoryID", new PyInt(mTI.categoryID));
463  row->SetField("quantity", new PyInt(mTI.singleton?-1:mTI.quantity));
464  row->SetField("customInfo", new PyString(mTI.customInfo));
465  PyTuple* tuple = new PyTuple(2);
466  tuple->SetItem(0, row);
467  tuple->SetItem(1, dict);
468  PyIncRef(tuple);
469  // now send it, bypassing the extra shit and wrong dest name added in Client::SendNotification
470  pClient->SendNotification("OnItemChange", "charid", &tuple);
471  pOther->SendNotification("OnItemChange", "charid", &tuple);
472  }
473 
474  // reset states after offer changes.
475  pTSes->m_tradeSession.myState = false;
476  pTSes->m_tradeSession.herState = false;
477  // return none
478  return PyStatic.NewNone();
479 }
480 
481 PyResult TradeBound::Handle_GetItem(PyCallArgs &call) {
482  TradeSession* pTSes = call.client->GetTradeSession();
483  PyPackedRow* row = new PyPackedRow( sDataMgr.CreateHeader() );
484  row->SetField("itemID", new PyLong(pTSes->m_tradeSession.containerID));
485  row->SetField("typeID", new PyInt(53)); // type Trade Window
486  row->SetField("ownerID", PyStatic.NewOne()); // EvE_System
487  row->SetField("locationID", new PyInt(pTSes->m_tradeSession.stationID));
488  row->SetField("flagID", PyStatic.NewNone());
489  row->SetField("quantity", new PyInt(-1)); // singleton
490  row->SetField("groupID", new PyInt(EVEDB::invGroups::Trade_Session ) );
491  row->SetField("categoryID", new PyInt(EVEDB::invCategories::Trading));
492  row->SetField("customInfo", PyStatic.NewNone());
493  return row;
494 }
495 
496 PyResult TradeBound::Handle_IsCEOTrade(PyCallArgs &call) {
497  _log(CLIENT__CALL_DUMP, "TradeBound::Handle_IsCEOTrade()");
498  call.Dump(CLIENT__CALL_DUMP);
499 
500  //TODO will have to work on this later. need corps working correctly first.
501  return new PyBool(false);
502 }
503 
504 PyResult TradeBound::Handle_List(PyCallArgs &call) {
505  TradeSession* pTSes = call.client->GetTradeSession();
506  PyList* list = new PyList();
507 
508  DBRowDescriptor* header = sDataMgr.CreateHeader();
509  for (auto cur : pTSes->m_tradelist) {
510  PyPackedRow* row = new PyPackedRow( header );
511  row->SetField("itemID", new PyLong(cur.itemID));
512  row->SetField("typeID", new PyInt(cur.typeID));
513  row->SetField("ownerID", new PyInt(cur.ownerID));
514  row->SetField("locationID", new PyInt(cur.locationID));
515  row->SetField("flagID", new PyInt(cur.flagID));
516  row->SetField("groupID", new PyInt(cur.groupID));
517  row->SetField("quantity", new PyInt(cur.singleton?-1:cur.quantity));
518  row->SetField("categoryID", new PyInt(cur.categoryID));
519  row->SetField("customInfo", new PyString(cur.customInfo));
520  list->AddItem(row);
521  }
522 
523  PyTuple* tuple2 = new PyTuple(1);
524  tuple2->SetItem(0, list);
525  PyToken* token = new PyToken("__builtin__.set");
526  PyTuple* tuple = new PyTuple(2);
527  tuple->SetItem(0, token);
528  tuple->SetItem(1, tuple2);
529  TradeListData tld;
530  tld.tradeContainerID = pTSes->m_tradeSession.containerID;
531  tld.myID = pTSes->m_tradeSession.myID;
532  tld.herID = pTSes->m_tradeSession.herID;
533  tld.myMoney = pTSes->m_tradeSession.myMoney;
534  tld.herMoney = pTSes->m_tradeSession.herMoney;
535  tld.myState = pTSes->m_tradeSession.myState;
536  tld.herState = pTSes->m_tradeSession.herState;
537  tld.list = new PyObjectEx(false, tuple);
538  PyList* itemNames = new PyList(5);
539  itemNames->SetItem(0, new PyString("tradeContainerID"));
540  itemNames->SetItem(1, new PyString("traders"));
541  itemNames->SetItem(2, new PyString("state"));
542  itemNames->SetItem(3, new PyString("money"));
543  itemNames->SetItem(4, new PyString("items"));
544  TradeListRsp tlr;
545  tlr.header = itemNames;
546  tlr.line = tld.Encode();
547  return tlr.Encode();
548 }
549 
550 void TradeBound::ExchangeItems(Client* pClient, Client* pOther, TradeSession* pTSes) {
551  // trade completed. perform item and money exchange
552  if (pClient->GetCharacterID() == pTSes->m_tradeSession.herID) {
553  pOther = sEntityList.FindClientByCharID(pTSes->m_tradeSession.herID);
554  pClient = sEntityList.FindClientByCharID(pTSes->m_tradeSession.myID);
555  }
556  // transfer funds and add journal entries for both sides
557  std::string reason = "Player Trade between ";
558  reason += pClient->GetCharName();
559  reason += " and ";
560  reason += pOther->GetCharName();
561  reason += " in ";
562  reason += pClient->GetSystemName(); // use system name or station name here?
565 
566  PyDict* dict = new PyDict();
567  dict->SetItem(new PyInt(Inv::Update::Location), new PyInt(pTSes->m_tradeSession.containerID));
568 
569  uint32 stationID = pTSes->m_tradeSession.stationID;
570  for (auto cur : pTSes->m_tradelist) {
571  InventoryItemRef itemRef = sItemFactory.GetItem(cur.itemID);
572  if (!itemRef) {
573  _log(PLAYER__ERROR, "TradeBound::Handle_Add() - Failed to get ItemRef.");
574  continue;
575  }
576 
577  uint32 newOwnerID = (cur.ownerID == pTSes->m_tradeSession.myID ? pTSes->m_tradeSession.herID : pTSes->m_tradeSession.myID);
578  itemRef->ChangeOwner(newOwnerID, true);
579  itemRef->Move(stationID, flagHangar, true);
580 
581  if ((itemRef->categoryID() == EVEDB::invCategories::Ship)
583  || (itemRef->groupID() == EVEDB::invGroups::Cargo_Container)
586  m_TSvc->TransferContainerContents(pClient->SystemMgr(), itemRef, newOwnerID);
587  }
588 
589  PyTuple* tuple = new PyTuple(2);
590  tuple->SetItem(0, new PyString("TradeComplete"));
591  tuple->SetItem(1, new PyInt(pTSes->m_tradeSession.containerID));
592  PyIncRef(tuple);
593  // now send it, bypassing the extra shit and wrong dest name added in Client::SendNotification
594  pClient->SendNotification("OnTrade", "charid", &tuple);
595  pOther->SendNotification("OnTrade", "charid", &tuple);
596 }
597 
599 {
600  std::map<uint32, InventoryItemRef> InventoryMap;
601  InventoryMap.clear();
602 
603  if (itemRef->categoryID() == EVEDB::invCategories::Ship) {
604  // if we change this to use shipItem, this will need rework
606  ShipItemRef shipRef = pSysMgr->GetShipFromInventory(itemRef->itemID());
607  if (shipRef.get() == nullptr)
608  shipRef = sItemFactory.GetShip(itemRef->itemID());
609  if (!shipRef->GetMyInventory()->IsEmpty())
610  shipRef->GetMyInventory()->GetInventoryMap(InventoryMap);
611  } else {
612  CargoContainerRef contRef = pSysMgr->GetContainerFromInventory(itemRef->itemID());
613  if (contRef.get() == nullptr)
614  contRef = sItemFactory.GetCargoContainer(itemRef->itemID());
615  if (!contRef->IsEmpty())
616  contRef->GetMyInventory()->GetInventoryMap(InventoryMap);
617  }
618 
619  for (auto cur : InventoryMap)
620  cur.second->ChangeOwner(newOwnerID, true);
621 }
622 
623 PyResult TradeService::Handle_InitiateTrade(PyCallArgs &call) {
624  Client* target(nullptr);
625  if (call.client->GetTradeSession()) {
626  target = sEntityList.FindClientByCharID( call.client->GetTradeSession()->m_tradeSession.herID );
627  call.client->SendErrorMsg("You are currently trading with %s. You can only trade with one player at a time.", target->GetName());
628  return nullptr;
629  }
630 
631  Call_SingleIntegerArg args;
632  // .arg is char to trade with
633  if (!args.Decode(&call.tuple)) {
634  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
635  return nullptr;
636  }
637 
638  target = sEntityList.FindClientByCharID( args.arg );
639  if (target->GetTradeSession()) {
640  Client* otarget = sEntityList.FindClientByCharID( call.client->GetTradeSession()->m_tradeSession.herID );
641  call.client->SendErrorMsg("%s is currently trading with %s. Try again later.", target->GetName(), otarget->GetName());
642  return nullptr;
643  }
644 
645  // wtf is this ???
646  uint32 warID(0);
647  if (call.byname.find("warID") != call.byname.cend())
648  warID = PyRep::IntegerValue(call.byname.find("warID")->second);
649 
650  InitiateTradeRsp_NoCash rsp_nc;
651  rsp_nc.nodeID = call.client->services().GetNodeID();
652  rsp_nc.stationID = target->GetStationID();
653  rsp_nc.myID = call.client->GetCharacterID();
654  rsp_nc.herID = target->GetCharacterID();
655  rsp_nc.money = 0;
656  rsp_nc.state = 0;
657  rsp_nc.when = GetFileTimeNow();
658 
659  PyObject* resp = rsp_nc.Encode();
660  InitiateTrade(target, resp->Clone());
661 
662  return resp;
663 }
664 
665 void TradeService::InitiateTrade(Client* pClient, PyRep* resp) {
666  PyTuple* tuple = new PyTuple(3);
667  tuple->SetItem(0, new PyString("Initiate"));
668  tuple->SetItem(1, new PyInt(pClient->GetCharacterID()));
669  tuple->SetItem(2, resp);
670  // now send it, bypassing the extra shit and wrong dest name added in Client::SendNotification
671  pClient->SendNotification("OnTrade", "charid", &tuple);
672 }
673 
675  m_activeSessions.erase(myID);
676 }
677 
679  TradeSession* pTSes = pClient->GetTradeSession();
680  Client* pOther = sEntityList.FindClientByCharID(pTSes->m_tradeSession.herID);
681 
682  TradeBound* pTB = new TradeBound(m_manager);
683  pTB->CancelTrade(pClient, pOther, pTSes);
684 
685  PyTuple* tuple = new PyTuple(2);
686  tuple->SetItem(0, new PyString("Cancel"));
687  tuple->SetItem(1, new PyInt(pTSes->m_tradeSession.containerID));
688  PyIncRef(tuple);
689  // now send it, bypassing the extra shit and wrong dest name added in Client::SendNotification
690  pClient->SendNotification("OnTrade", "charid", &tuple);
691  pOther->SendNotification("OnTrade", "charid", &tuple);
694  SafeDelete(pTSes);
695  pClient->ClearTradeSession();
696  pOther->ClearTradeSession();
697  // returns none
698 }
699 
701 {
703  return ++m_SessionID;
704 
705  return (m_SessionID = minTradeCont);
706 }
TradeService(PyServiceMgr *mgr)
Base Python wire object.
Definition: PyRep.h:66
Dispatcher *const m_dispatch
void ExchangeItems(Client *pClient, Client *pOther, TradeSession *pTSes)
void SendNotification(const PyAddress &dest, EVENotificationStream &noti, bool seq=true)
Definition: Client.cpp:2245
uint32 m_SessionID
Definition: TradeService.h:67
std::map< uint32, ActiveSession > m_activeSessions
Definition: TradeService.h:56
double value() const
Definition: PyRep.h:309
PyCallable_DECL_CALL(List)
virtual ~TradeBound()
void SendErrorMsg(const char *fmt,...)
Definition: Client.cpp:2719
#define _log(type, fmt,...)
Definition: logsys.h:124
PyRep * GetItem(size_t index) const
Returns Python object.
Definition: PyRep.h:602
Python string.
Definition: PyRep.h:430
PyServiceMgr & services() const
Definition: Client.h:90
PyBool * AsBool()
Definition: PyRep.h:128
Python's dictionary.
Definition: PyRep.h:719
std::map< std::string, PyRep * > byname
Definition: PyCallable.h:51
virtual PyRep * Clone() const =0
Clones object.
virtual PyBoundObject * CreateBoundObject(Client *pClient, const PyRep *bind_args)
EVEItemFlags
Definition: EVE_Flags.h:13
PyRep * Clone() const
Clones object.
Definition: PyRep.cpp:773
Python floating point number.
Definition: PyRep.h:292
PyCallable_Make_Dispatcher(TradeBound) TradeBound(PyServiceMgr *mgr)
int32 GetCharacterID() const
Definition: Client.h:113
bool value() const
Definition: PyRep.h:340
#define sEntityList
Definition: EntityList.h:208
static uint32 IntegerValueU32(PyRep *pRep)
Definition: PyRep.cpp:134
std::string m_strBoundObjectName
Definition: PyBoundObject.h:54
uint32 GetNodeID() const
Definition: PyServiceMgr.h:67
Python tuple.
Definition: PyRep.h:567
const char * GetName() const
Definition: PyService.h:54
void TransferContainerContents(SystemManager *pSysMgr, InventoryItemRef itemRef, uint32 newOwnerID)
void Move(uint32 new_location=locTemp, EVEItemFlags flag=flagNone, bool notify=false)
void ClearTradeSession()
Definition: Client.h:310
void CancelTrade(Client *pClient)
void AddItem(PyRep *i)
Definition: PyRep.h:701
void SafeDelete(T *&p)
Deletes and nullifies a pointer.
Definition: SafeMem.h:83
uint16 groupID() const
void _SetCallDispatcher(CallDispatcher *d)
Definition: PyCallable.h:87
uint32 GetTradeSessionID()
TradeService * m_TSvc
* args
Python boolean.
Definition: PyRep.h:323
bool isSingleton() const
Definition: InventoryItem.h:96
ShipItemRef GetShipFromInventory(uint32 shipID)
Python extended object.
Definition: PyRep.h:861
#define maxTradeCont
Definition: EVE_Defines.h:130
Dispatcher *const m_dispatch
Python object.
Definition: PyRep.h:826
Python object "blue.DBRowDescriptor".
Definition: PyDatabase.h:41
#define codelog(type, fmt,...)
Definition: logsys.h:128
CargoContainerRef GetContainerFromInventory(uint32 contID)
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
SystemManager * SystemMgr() const
Definition: Client.h:92
virtual void Release()
static void DeleteInsuranceByShipID(uint32 shipID)
Definition: ShipDB.cpp:65
static void TranserFunds(uint32 fromID, uint32 toID, double amount, std::string reason="", uint8 entryTypeID=Journal::EntryType::Undefined, uint32 referenceID=0, uint16 fromKey=Account::KeyType::Cash, uint16 toKey=Account::KeyType::Cash, Client *pClient=nullptr)
Python integer.
Definition: PyRep.h:231
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:682
Session m_tradeSession
Definition: TradeService.h:105
TradeSession * GetTradeSession()
Definition: Client.h:311
friend TradeService
void InitiateTrade(Client *pClient, PyRep *resp)
PyServiceMgr *const m_manager
Definition: PyService.h:91
#define PyStatic
Definition: PyRep.h:1209
X * get() const
Definition: RefPtr.h:213
void SetTradeSession(TradeSession *ts)
Definition: Client.h:309
const char * GetName() const
Definition: Client.h:94
void ChangeOwner(uint32 new_owner, bool notify=false)
PyCallable_Make_InnerDispatcher(TradeService)
Client *const client
Definition: PyCallable.h:49
#define PyCallable_REG_CALL(c, m)
Definition: PyServiceCD.h:78
Definition: Client.h:66
PyServiceMgr * m_SvcMgr
Definition: TradeService.h:61
unsigned __int32 uint32
Definition: eve-compat.h:50
#define PyIncRef(op)
Definition: PyRep.h:56
EVEItemFlags flag() const
std::string GetCharName()
Definition: Client.h:165
void GetInventoryMap(std::map< uint32, InventoryItemRef > &invMap)
Definition: Inventory.cpp:453
Python token (eg. class name).
Definition: PyRep.h:522
double GetFileTimeNow()
Definition: utils_time.cpp:84
void RemoveActiveSession(uint32 myID)
Dispatcher *const m_dispatch
Definition: TradeService.h:59
bool SetField(uint32 index, PyRep *value)
Definition: PyRep.cpp:1031
void Dump(LogType type) const
Definition: PyCallable.cpp:81
#define minTradeCont
Definition: EVE_Defines.h:129
#define sItemFactory
Definition: ItemFactory.h:165
int32 GetStationID() const
Definition: Client.h:114
std::list< TradeItems > m_tradelist
Definition: TradeService.h:107
Packed row.
Definition: PyRep.h:961
static int64 IntegerValue(PyRep *pRep)
Definition: PyRep.cpp:118
PyFloat * AsFloat()
Definition: PyRep.h:126
std::string GetSystemName() const
Definition: Client.h:155
void CancelTrade(Client *pClient, Client *pOther, TradeSession *pTSes)
Inventory * GetMyInventory()
Definition: InventoryItem.h:91
bool IsEmpty()
Definition: Container.h:92
void SetItem(PyRep *key, PyRep *value)
SetItem adds or sets a database entry.
Definition: PyRep.cpp:713
uint16 typeID() const
uint8 categoryID() const
Python list.
Definition: PyRep.h:639
uint32 itemID() const
Definition: InventoryItem.h:98
const char * GetName() const
Definition: PyBoundObject.h:44
int32 quantity() const
Definition: InventoryItem.h:97
Python long integer.
Definition: PyRep.h:261
virtual ~TradeService()
PyTuple * tuple
Definition: PyCallable.h:50
#define sDataMgr