EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
InventoryBound.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, Captnoord
24  Rewrite: Allan
25 */
26 
27 #include "eve-server.h"
28 
29 #include "PyServiceCD.h"
30 #include "EVEServerConfig.h"
31 #include "StaticDataMgr.h"
33 #include "planet/CustomsOffice.h"
34 #include "planet/Planet.h"
35 #include "planet/Colony.h"
36 #include "pos/Structure.h"
38 #include "system/BookmarkDB.h"
39 #include "system/Container.h"
40 #include "system/SystemManager.h"
41 
43 
45 : PyBoundObject(mgr),
46 m_dispatch(new Dispatcher(this)),
47 pInventory(item->GetMyInventory()),
48 m_flag(flag),
49 m_self(item),
50 m_itemID(item->itemID()),
51 m_ownerID(ownerID),
52 m_passive(passive)
53 {
54  _SetCallDispatcher(m_dispatch);
55 
56  m_strBoundObjectName = "InventoryBound";
57 
62  PyCallable_REG_CALL(InventoryBound, RemoveChargeToCargo);
63  PyCallable_REG_CALL(InventoryBound, RemoveChargeToHangar);
66  PyCallable_REG_CALL(InventoryBound, StripFitting);
67  PyCallable_REG_CALL(InventoryBound, DestroyFitting);
68  PyCallable_REG_CALL(InventoryBound, ImportExportWithPlanet);
69  PyCallable_REG_CALL(InventoryBound, CreateBookmarkVouchers);
70  PyCallable_REG_CALL(InventoryBound, ListDroneBay);
72  PyCallable_REG_CALL(InventoryBound, RunRefiningProcess);
73  PyCallable_REG_CALL(InventoryBound, TakeOutTrash);
74 
75  _log(INV__BIND, "Created InventoryBound object %p for %s(%u) and ownerID %u with flag %s (passive: %s)", \
76  this, m_self->name(), m_itemID, ownerID, sDataMgr.GetFlagName(flag), (m_passive ? "true" : "false"));
77 }
78 
80 {
81  delete m_dispatch;
82 }
83 
84 PyResult InventoryBound::Handle_GetItem(PyCallArgs &call) {
85  _log(INV__MESSAGE, "Calling InventoryBound::GetItem() for %s(%u)", m_self->name(), m_itemID);
86  return m_self->GetItem();
87 }
88 
89 PyResult InventoryBound::Handle_StripFitting(PyCallArgs &call)
90 {
91  call.client->GetShip()->StripFitting();
92  return nullptr;
93 }
94 
95 PyResult InventoryBound::Handle_DestroyFitting(PyCallArgs &call) {
96  _log(INV__MESSAGE, "Calling InventoryBound::DestroyFitting() for %s(%u)", m_self->name(), m_itemID);
97  Call_SingleIntegerArg args;
98  if (!args.Decode(&call.tuple)){
99  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
100  }
101 
102  call.client->GetShip()->RemoveRig(sItemFactory.GetItem(args.arg));
103 
104  return nullptr;
105 }
106 
107 PyResult InventoryBound::Handle_StackAll(PyCallArgs &call) {
108  call.Dump(INV__DUMP);
109 
110  EVEItemFlags stackFlag = m_flag;
111 
112  if (call.tuple->items.size() != 0) {
113  Call_SingleIntegerArg arg;
114  if (!arg.Decode(&call.tuple)) {
115  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
116  return nullptr;
117  }
118 
119  stackFlag = (EVEItemFlags)arg.arg;
120  }
121 
122  _log(INV__MESSAGE, "Calling InventoryBound::StackAll() for %s(%u) in %s. Bound flag is %s", \
123  m_self->name(), m_itemID, sDataMgr.GetFlagName(stackFlag), sDataMgr.GetFlagName(m_flag));
124 
125  //Stack Items contained in this inventory
126  pInventory->StackAll(stackFlag, m_ownerID);
127 
128  return nullptr;
129 }
130 
131 PyResult InventoryBound::Handle_ImportExportWithPlanet(PyCallArgs &call) {
132  /*
133  customsOfficeInventory = sm.GetService('invCache').GetInventoryFromId(self.customsOfficeID)
134  customsOfficeInventory.ImportExportWithPlanet(self.spaceportPinID, importData, exportData, self.taxRate)
135  */
136 
137  /*
138  * 23:21:49 [PlanetPktTrace] Call_PlanetCustomsXfer
139  * 23:21:49 [PlanetPktTrace] spaceportPinID=140000640
140  * 23:21:49 [PlanetPktTrace] importData:
141  * 23:21:49 [PlanetPktTrace] Dictionary: 3 entries
142  * 23:21:49 [PlanetPktTrace] [ 0] Key: Integer: 140000606
143  * 23:21:49 [PlanetPktTrace] [ 0] Value: Integer: 1001
144  * 23:21:49 [PlanetPktTrace] [ 1] Key: Integer: 140000610
145  * 23:21:49 [PlanetPktTrace] [ 1] Value: Integer: 1000
146  * 23:21:49 [PlanetPktTrace] [ 2] Key: Integer: 140000608
147  * 23:21:49 [PlanetPktTrace] [ 2] Value: Integer: 1000
148  * 23:21:49 [PlanetPktTrace] exportData:
149  * 23:21:49 [PlanetPktTrace] Dictionary: Empty
150  * 23:21:49 [PlanetPktTrace] taxRate=0.0500000007451
151  */
152  //{'FullPath': u'UI/Messages', 'messageID': 256577, 'label': u'CannotImportNotEnoughWarehouseSpaceBody'}(u'You cannot import commodities to that spaceport, as it does not have sufficient storage space to handle the incoming goods.', None, None)
153  //{'FullPath': u'UI/Messages', 'messageID': 256626, 'label': u'CannotExportNotEnoughSpaceBody'}(u'You cannot export commodities to the customs office, as it does not have sufficient storage space to handle the incoming goods.', None, None)
154 
155  // this is (should be) customs office
157  _log(ITEM__ERROR, "%s: Called CustomsOffice xFer using non-co item %s(%u).", call.client->GetName(), m_self->name(), m_self->itemID());
158  return nullptr;
159  }
160 
161  Call_PlanetCustomsXfer args;
162  if (!args.Decode(&call.tuple)) {
163  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
164  return nullptr;
165  }
166  args.Dump(COLONY__PKT_TRACE);
167 
168  PyDict* dictIn = args.importData->AsDict();
169  std::map<uint32, uint16> importItems, exportItems;
170  for (PyDict::const_iterator itr = dictIn->begin(); itr != dictIn->end(); ++itr)
171  importItems[PyRep::IntegerValueU32(itr->first)] = PyRep::IntegerValue(itr->second);
172  PyDict* dictOut = args.exportData->AsDict();
173  for (PyDict::const_iterator itr = dictOut->begin(); itr != dictOut->end(); ++itr)
174  exportItems[PyRep::IntegerValueU32(itr->first)] = PyRep::IntegerValue(itr->second);
175 
176  // ok, so from here, we need to get officeRef->officeSE->planet->colony to make xfer....crazy shit
178  Colony* pColony = sRef->GetMySE()->GetCOSE()->GetPlanetSE()->GetColony(call.client);
179  pColony->PlanetXfer(args.spaceportPinID, importItems, exportItems, args.taxRate);
180 
181  return nullptr;
182 }
183 
184 PyResult InventoryBound::Handle_RemoveChargeToHangar(PyCallArgs &call) {
185  Call_RemoveCharge args;
186  if (!args.Decode(call.tuple->GetItem(0)->AsTuple())) {
187  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
188  return PyStatic.NewNone();
189  }
190 
192  //uint32 quantity = 0;
193  //if (call.tuple->size() == 2)
194  // quantity = PyRep::IntegerValue(call.tuple->GetItem(1));
195 
196  // this call is used to remove sublocation (charge) items, which is virtual to real.
197  // since our code does this, we will return "None" here to avoid client subsequently calling MultiAdd() or MultiMerge()
198  call.client->GetShip()->RemoveCharge((EVEItemFlags)args.flagID);
199  return PyStatic.NewNone();
200 }
201 
202 PyResult InventoryBound::Handle_RemoveChargeToCargo(PyCallArgs &call) {
203  Call_RemoveCharge args;
204  if (!args.Decode(call.tuple->GetItem(0)->AsTuple())) {
205  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
206  return PyStatic.NewNone();
207  }
208 
210  //uint32 quantity = 0;
211  //if (call.tuple->size() == 2)
212  // quantity = PyRep::IntegerValue(call.tuple->GetItem(1));
213 
214  // this call is used to remove sublocation (charge) items, which is virtual to real.
215  // since our code does this, we will return "None" here to avoid client subsequently calling MultiAdd() or MultiMerge()
216  call.client->GetShip()->RemoveCharge((EVEItemFlags)args.flagID);
217  return PyStatic.NewNone();
218 }
219 
220 PyResult InventoryBound::Handle_MultiMerge(PyCallArgs &call) {
221  _log(INV__MESSAGE, "IB::MultiMerge() called by %s(%u)", m_self->name(), m_itemID);
222  call.Dump(INV__DUMP);
223  //Decode Args
224  Call_MultiMerge args;
225  if (!args.Decode(&call.tuple)) {
226  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
227  return nullptr;
228  }
229 
230  sItemFactory.SetUsingClient(call.client);
231  Inventory* pInv(nullptr); // = sItemFactory.GetInventoryFromId(args.locationID);
232  /* args.locationID is NOT the destination...it is the source, and NOT what we're looking for here.
233  if (pInv == nullptr) {
234  _log(INV__WARNING, "Failed to get container inventory for locationID %u.", args.locationID);
235  return nullptr;
236  }
237  */
238 
239  std::vector<PyRep *>::const_iterator itr = args.mergeData->begin(), end = args.mergeData->end();
240  for (; itr != end; ++itr) {
241  // sourceID, destID, qty
242  MultiMergeData data;
243  if (!data.Decode( *itr )) {
244  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
245  continue;
246  }
247 
248  InventoryItemRef srcItem = sItemFactory.GetItem( data.sourceID );
249  if (srcItem.get() == nullptr) {
250  _log(INV__WARNING, "Failed to load source item %u. Skipping.", data.sourceID);
251  continue;
252  }
253 
254  InventoryItemRef destItem = sItemFactory.GetItem( data.destID );
255  if (destItem.get() == nullptr) {
256  _log(INV__WARNING, "Failed to load destination item %u. Skipping.", data.destID);
257  continue;
258  }
259 
260  // get inventory of destination container for ValidateAddItem() check
261  pInv = sItemFactory.GetInventoryFromId(destItem->locationID());
262  if (pInv == nullptr) {
263  _log(INV__WARNING, "Failed to get inventory for locationID %u.", destItem->locationID());
264  continue;
265  }
266 
267  if (pInv->ValidateAddItem(destItem->flag(), srcItem))
268  destItem->Merge( srcItem, data.qty, true );
269  // if false, error is thrown in ValidateAddItem() call
270  }
271  sItemFactory.UnsetUsingClient();
272 
273  return nullptr;
274 }
275 
276 /* this call is used for moving an item to *THIS* inventory
277  * Moving items to/from containers
278  * Removing Module/Charges from ship (using 'remove' button on item slot)
279  * Adding Modules to a specific slot on ship
280  */
281 PyResult InventoryBound::Handle_Add(PyCallArgs &call) {
282  if (is_log_enabled(INV__DUMP)) {
283  _log(INV__DUMP, "IB::Handle_Add() size= %u", call.tuple->size());
284  call.Dump(INV__DUMP);
285  }
286 
287  if (call.tuple->items.size() != 2) {
288  _log(INV__ERROR, "IB::Handle_Add() Unexpected number of elements in tuple: %u (should be 2).", call.tuple->items.size() );
289  return nullptr;
290  }
291 
292  Call_Add_2 args; // item and location
293  if (!args.Decode(&call.tuple)) {
294  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
295  return nullptr;
296  }
297 
298  uint16 toFlag = m_flag;
299  if (call.byname.find("flag") != call.byname.end())
300  toFlag = PyRep::IntegerValueU32(call.byname.find("flag")->second);
301  if (toFlag == flagLocked) {
302  // corp role 'equip config' can move locked items (per client)
303  _log(INV__ERROR, "IB::Handle_Add() - item %u from %u sent flagLocked. continuing but this needs to be fixed.", \
304  args.itemID, args.containerID);
305  toFlag = flagCargoHold;
306  }
307 
308  InventoryItemRef iRef = sItemFactory.GetItem(args.itemID);
309 
310  bool moveStack = false;
311  int32 quantity = 0;
312  if (call.byname.find("qty") != call.byname.end())
313  quantity = PyRep::IntegerValue(call.byname.find("qty")->second);
314 
315  if (call.byname.find("dividing") != call.byname.end()) {
316  // split stack, move original ref, leave remainder here with new item
317  InventoryItemRef newItem = iRef->Split(iRef->quantity() -quantity);
318  if (newItem.get() == nullptr) {
319  _log(INV__ERROR, "IB::Handle_Add() - Error splitting item %u. Skipping.", iRef->itemID());
320  return nullptr;
321  }
322  iRef = newItem;
323  args.itemID = iRef->itemID();
324  // we're not dividing the stack, so check for removing loaded charges
325  } else if ((iRef->categoryID() == EVEDB::invCategories::Charge) and (IsModuleSlot(iRef->flag()))) {
326  moveStack = true;
327  quantity = iRef->quantity();
328  } else if (call.client->IsInSpace() and (toFlag == flagCargoHold) and (quantity == 0)) {
329  moveStack = true;
330  quantity = iRef->quantity();
331  }
332 
333  float capacity = 0.0f;
334  if (call.byname.find("capacity") != call.byname.end())
335  capacity = PyRep::IntegerValueU32(call.byname.find("capacity")->second);
336 
337  if (quantity < 1)
338  quantity = 1;
339 
340  _log(INV__MESSAGE, "IB::Handle_Add() - moving %u %s(%u) from (%u:%s) to me(%s:%u:%s).", \
341  quantity, iRef->name(), args.itemID, args.containerID, sDataMgr.GetFlagName(iRef->flag()),\
342  m_self->name(), m_itemID, sDataMgr.GetFlagName(toFlag));
343 
344  std::vector<int32> items;
345  items.push_back(args.itemID);
346 
347  return MoveItems(call.client, items, (EVEItemFlags)toFlag, quantity, moveStack, capacity);
348 }
349 
350 // this call is for moving items to *THIS* inventory
351 PyResult InventoryBound::Handle_MultiAdd(PyCallArgs &call) {
352  if (is_log_enabled(INV__DUMP)) {
353  _log(INV__DUMP, "IB::Handle_MultiAdd() size= %u", call.tuple->size());
354  call.Dump(INV__DUMP);
355  }
356 
357  if (call.tuple->items.size() != 2) {
358  _log(INV__ERROR, "IB::Handle_MultiAdd() Unexpected number of elements in tuple: %u (should be 2).", call.tuple->items.size() );
359  return nullptr;
360  }
361 
362  Call_MultiAdd_2 args;
363  if (!args.Decode(&call.tuple)) {
364  //17:19:04 [DecodeError] Decode Call_MultiAdd_2 failed: Element 0 in list list_1 is not an integer: None
365  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
366  return nullptr;
367  }
368 
369  uint16 toFlag = m_flag;
370  if (call.byname.find("flag") != call.byname.end())
371  toFlag = PyRep::IntegerValueU32(call.byname.find("flag")->second);
372 
373  int32 quantity = 1;
374  if (call.byname.find("qty") != call.byname.end())
375  quantity = PyRep::IntegerValue(call.byname.find("qty")->second);
376 
377  //bool byname(fromManyFlags):true == unload charges from module referenced
378  bool moveStack = false;
379  if (call.byname.find("fromManyFlags") != call.byname.end())
380  if (!call.byname.find("fromManyFlags")->second->IsNone())
381  moveStack = true;
382 
383  float capacity = 0.0f;
384  if (call.byname.find("capacity") != call.byname.end())
385  capacity = PyRep::IntegerValueU32(call.byname.find("capacity")->second);
386 
387  if (capacity > 1) {
388  moveStack = true;
389  } else if (quantity < 1) {
390  moveStack = true;
391  }
392 
393  // moving TO hangar...move all items in stack, if applicable...this includes ship corp hangars - is this what we want here?
394  if (IsHangarFlag(toFlag))
395  moveStack = true;
396 
397  if (m_self->IsShipItem() and !moveStack) {
398  std::vector<InventoryItemRef> itemVec;
399  for (auto cur : args.itemIDs)
400  itemVec.push_back(sItemFactory.GetItem(cur));
401  args.itemIDs = CatSortItems(itemVec);
402  }
403 
404  _log(INV__MESSAGE, "IB::Handle_MultiAdd() - moving %u items from (%u:%s) to me(%s:%u:%s).", \
405  args.itemIDs.size(), args.containerID, sDataMgr.GetFlagName(m_flag), m_self->name(), m_itemID, sDataMgr.GetFlagName(toFlag));
406 
407  return MoveItems( call.client, args.itemIDs, (EVEItemFlags)toFlag, quantity, moveStack, capacity);
408 }
409 
410 PyRep* InventoryBound::MoveItems(Client* pClient, std::vector< int32 >& items, EVEItemFlags toFlag, int32 quantity, bool moveStack, float capacity)
411 { // complete method rewrite -allan 21Dec17
412  ShipItem* pShip = pClient->GetShip().get();
413  bool donating = false, ship = false, customs = false;
414  int32 origQty = quantity;
415 
416  // we will need to check *this for specific item-moving rules
417  switch (m_self->categoryID()) {
418  // specific container-type categories that may move items in/out
420  // this shouldnt hit. trading is handled in separate system
421  codelog(INV__ERROR, "IB::MoveItems() - Trading Category called.");
422  EvE::traceStack();
423  } break;
425  // this is all POS groups. use corp donating checks
426  donating = true;
427  // may have to reset flags based on type
428  switch (m_self->groupID()) {
430  // flag 0 = fuel bay, flag = 122 strontium bay (2nd storage)
431  } break;
436  // flag 27 = hislot0 using ammo capy
437  } break;
444  } break;
455  } break;
456  default: {
457  } break;
458  }
459  } break;
461  switch (m_self->groupID()) {
463  // standard station hangar
464  if (toFlag == flagCorpMarket)
465  donating = true;
466  if (toFlag == flagImpounded)
467  donating = true;
468  //if (toFlag == flagDelivery)
469  // donating = true;
470  if (toFlag == flagLocked) {
471  ; // do tests here for moving locked items (corp role config and ??)
472  }
473  } break;
475  // station offices, office and factory folders.
476  if (m_self->typeID() == EVEDB::invTypes::Office) //office. use corp donating checks
477  donating = true;
478  } break;
479  }
480  } break;
482  switch (m_self->groupID()) {
484  ; // not sure what to do here yet
485  } break;
487  // orbital command centers and customs offices
488  // we dont change owners of xfered items
489  customs = true;
490  } break;
491  }
492  } break;
494  // containers, wrecks, construction platform, station improve/upgrade platform,
495  if (IsPlayerCorp(m_ownerID))
496  donating = true;
497  } break;
499  // do module checks and specific removeItem() for these items
500  ship = true;
501  // will need to add checks on from container to verify donating flag here.
502  //donating = true;
503  } break;
504  }
505 
506  EVEItemFlags fromFlag(flagNone);
507  EVEItemFlags origFlag(toFlag);
508  InventoryItemRef iRef(nullptr);
509  sItemFactory.SetUsingClient(pClient);
510 
511  std::vector<int32>::const_iterator itr = items.begin();
512  for (; itr != items.end(); ++itr) {
513  // reset vars for adding multiple items
514  toFlag = origFlag;
515  quantity = origQty;
516 
517  iRef = sItemFactory.GetItem(*itr);
518  if (iRef.get() == nullptr) {
519  _log(INV__ERROR, "IB::MoveItems() - item %i not found. continuing.", (*itr));
520  continue;
521  }
522 
523  if (iRef->typeID() == EVEDB::invTypes::Bookmark) {
524  // update this to keep owner/creator and other data
525  iRef->Donate(m_ownerID, m_itemID, toFlag);
526  continue;
527  }
528 
529  fromFlag = iRef->flag();
530 
531  if (moveStack)
532  quantity = iRef->quantity();
533 
534  // if client send capy, it is TOTAL space in this container, NOT current space.
535  if (capacity > 0) {
536  if (quantity < 1)
537  quantity = iRef->quantity(); // assume all.
538  } else if (quantity < 1) {
539  _log(INV__ERROR, "IB::MoveItems() - Quantity < 1. Setting quantity = 1.");
540  quantity = 1;
541  }
542 
543  // check for items that need specific handling
544  if (IsRigSlot(fromFlag)) // cant remove rigs like this. send error.
545  throw UserError ("CannotRemoveUpgradeManually");
546  else if (IsModuleSlot(fromFlag)) {
547  // can we remove modules from an inactive ship? not yet...
548  if (pShip == nullptr)
549  throw CustomError ("Ship not found. The %s wasnt moved. Ref: ServerError 63290", iRef->name());
550 
551  //if (sDataMgr.IsSolarSystem(pShip->locationID()))
552  // throw CustomError ("You cannot remove modules in space.");
553 
554  // verify module isnt active here (before we get too far in processing)
555  GenericModule* pMod = pShip->GetModule(fromFlag);
556  if (pMod == nullptr)
557  throw CustomError ("That module was not found. Chances are this is a server error, and either docking or reloging will correct it.");
558  if (pMod->IsActive())
559  throw CustomError ("Your %s is currently active. You must wait for the cycle to complete before it can be removed.", pMod->GetSelf()->name());
560 
561  if (IsModuleSlot(toFlag)) {
562  if (sDataMgr.IsSolarSystem(pShip->locationID()))
563  throw CustomError ("You cannot exchange module slots in space.");
564  //ModulesNotLoadableInSpace <-- this needs {device} but i dont know what module it is
565 
566  // we are wanting to change slots on a fitted module.
567  pShip->MoveModuleSlot(fromFlag, toFlag);
568  Call_SingleIntegerArg result;
569  result.arg = iRef->itemID();
570  return result.Encode();
571  } else {
572  // are we just unloading charges?
573  if (iRef->categoryID() == EVEDB::invCategories::Charge) {
574  pShip->UnloadModule(pMod);
575  return nullptr;
576  }
577  pShip->RemoveItem(iRef);
578  }
579  }
580 
581  if (!moveStack and (quantity < iRef->quantity())) {
582  InventoryItemRef newItem = iRef->Split(quantity);
583  if (newItem.get() == nullptr) {
584  _log(INV__ERROR, "IB::MoveItems() - Error splitting item %u. Skipping.", iRef->itemID());
585  continue;
586  }
587  iRef = newItem;
588  if (iRef.get() == nullptr) {
589  _log(INV__ERROR, "IB::MoveItems() - Error getting split item. Skipping.");
590  continue;
591  }
592  if (iRef->quantity() > quantity) {
593  _log(INV__ERROR, "IB::MoveItems() - Split item %u qty(%u) > requested qty of %u. Continuing.", \
594  iRef->itemID(), iRef->quantity(), quantity);
595  }
596  }
597 
598  if (donating) {
599  iRef->Donate(m_ownerID, m_itemID, toFlag);
600  continue;
601  }
602 
603  // move item to new location
604  if (ship) {
605  // are we adding module to ship using autoFit?
606  if (toFlag == flagNone) {
607  // assert(iRef->categoryID() != EVEDB::invCategories::Charge); // crash here...this should NOT happen.
608  if (iRef->categoryID() == EVEDB::invCategories::Module) {
609  toFlag = pShip->FindAvailableModuleSlot(iRef);
610  if (toFlag == flagIllegal) {
611  pClient->SendNotifyMsg("Your ship has no available slots to fit this module. Putting the %s in your CargoHold.", iRef->name());
612  toFlag = flagCargoHold;
613  }
614  } else {
615  // this needs work to verify mFlag is correct for application and that it is init'd correctly
616  toFlag = m_flag;
617  }
618  }
619 
620  // verify item is allowed in container first
621  m_self->GetShipItem()->VerifyHoldType(toFlag, iRef, pClient); // this will throw if it fails
622 
623  // then check for module limits
624  if (IsModuleSlot(toFlag)) {
625  m_self->GetShipItem()->TryModuleLimitChecks(toFlag, iRef); // this will throw if it fails
626  } else if (IsCargoHoldFlag(toFlag) or IsHangarFlag(toFlag) or (toFlag == flagDroneBay)) {
627  pInventory->ValidateAddItem(toFlag, iRef); // this will throw if it fails
628  }
629 
630  // check adding item to ship...if it fails, return to previous container
631  if (m_self->GetShipItem()->AddItemByFlag(toFlag, iRef, pClient) < 1) {
632  //ALL items *should* have a loaded container item.
633  InventoryItemRef contRef = sItemFactory.GetItemContainer(*itr);
634  if (contRef.get() != nullptr) {
635  contRef->AddItem(iRef);
636  } else {
637  _log(INV__ERROR, "IB::MoveItems() - previous container for item %i not found. continuing.", (*itr));
638  }
639  continue;
640  }
641  } else if (customs) {
642  pInventory->ValidateAddItem(toFlag, iRef); // this will throw if it fails
644  sRef->AddItem(iRef);// this will throw if it fails
645  } else {
646  pInventory->ValidateAddItem(toFlag, iRef); // this will throw if it fails
647  iRef->Donate(m_ownerID, m_itemID, toFlag);
648  }
649  }
650 
651  sItemFactory.UnsetUsingClient();
652 
653  if (iRef.get() == nullptr)
654  return nullptr;
655 
656  if (items.size() == 1) {
657  //call returns itemID for single-item adds...not sure about others
658  Call_SingleIntegerArg result;
659  result.arg = iRef->itemID();
660  return result.Encode();
661  }
662 
663  return nullptr;
664 }
665 
666 std::vector< int32 > InventoryBound::CatSortItems(std::vector< InventoryItemRef >& itemVec)
667 {
668  /* sorts a vector of items by category, with loaded modules first (in slot order), then loaded charges (in slot order), then cargo
669  * if there is only one item, no sorting required...
670  * this is called on fitting a group of modules from MultiAdd
671  * -allan
672  */
673  std::vector<int32> items;
674  if (itemVec.size() < 2) {
675  items.push_back(itemVec.at(0)->itemID());
676  return items;
677  }
678 
679  uint16 count(0);
680  double start(0.0);
681  if (sConfig.debug.IsTestServer and sConfig.debug.UseProfiling)
682  start = GetTimeUSeconds();
683 
684  //begin basic sort
685  bool done(false);
686  InventoryItemRef tmp(nullptr);
687  while (!done) {
688  done = true; //assume sorted
689  //iterate though list
690  for (int i = 0, i2 = 1; (i < itemVec.size()) and (i2 < itemVec.size()); ++i, ++i2) {
691  //check if each pair is sorted by category. modules -> charges -> subsystems
692  if (itemVec[i]->categoryID() < itemVec[i2]->categoryID()) {
693  //it's not, so flip the values
694  tmp = itemVec[i];
695  itemVec[i] = itemVec[i2];
696  itemVec[i2] = tmp;
697  done = false; //we weren't sorted, so now go back and check if we are
698  }
699  ++count;
700  }
701  }
702 
703  for (auto cur : itemVec)
704  items.push_back(cur->itemID());
705 
706  if (sConfig.debug.IsTestServer and sConfig.debug.UseProfiling)
707  sLog.Warning("IB::CatSortItems", "%u items sorted in %.3fus with %u loops.", items.size(), (GetTimeUSeconds() - start), count);
708 
709  return items; //returns sorted list
710 }
711 
716 PyResult InventoryBound::Handle_List(PyCallArgs &call) {
717  if (pInventory == nullptr)
718  return PyStatic.NewNone();
719 
720  _log(INV__DUMP, "IB::List() dump.");
721  call.Dump(INV__DUMP);
722 
724  /* this item was originally bound to this flag, but can send specific flag on occasion
725  * usually flag is set for subLocation, shipHangar, POS items, and CCHold
726  def GetSubSystemInFlag(self, shipID, flagID):
727  items = shipInv.List(flagID)
728  */
729 
730  EVEItemFlags flag = m_flag, oldFlag = m_flag;
731  if (call.byname.find("flag") != call.byname.end())
732  flag = (EVEItemFlags)PyRep::IntegerValueU32(call.byname.find("flag")->second);
733 
734  if (call.tuple->size() > 0) {
735  Call_List arg;
736  if (!arg.Decode(&call.tuple))
737  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", call.client->GetName());
738  if (flag != arg.flag)
739  flag = (EVEItemFlags)arg.flag;
740  }
741 
742  _log(INV__MESSAGE, "IB::List() called by %s with ownerID %u for %s(%u:%s%s) - origFlag: %s", \
743  call.client->GetName(), ownerID, m_self->name(), m_itemID, sDataMgr.GetFlagName(flag), \
744  (m_passive ? ":passive" : ""), sDataMgr.GetFlagName(oldFlag));
745 
746  // check for owner type of this inventory for reference checks
747  if (IsOfficeID(m_itemID)) {
748  // office owned by corp in station
749  // check for owner or corp
750  if (call.client->GetCorporationID() != m_ownerID)
751  ; // calling char is not member of corp. send error?
752  } else if (IsPlayerItem(m_itemID)) {
753  // this is probably a ship calling for all items
754  // is also used for t3 ships, POS, subLocations, and possibly others that are 'player items'
755  flag = flagNone;
756  /*
757  } else if (IsPlayerCorp(m_itemID)) {
758  // this one probably will not be used.
759  // what items in a corp would be listed? corpItem dont have inventory
760  } else if (IsCharacterID(m_itemID)) { // this is checked in Inventory::List()
761  // this is asking for skill list...char is a container for skills
762  flag = flagNone;
763  } else if (sDataMgr.IsSolarSystem(m_itemID)) {
764  // not sure how to do this one...will have to check on WHEN system listing would be called
765  */
766  } else if (sDataMgr.IsStation(m_itemID)) {
767  // this will get owners items only, including corps
768 
769  } else if (IsControlBunker(m_itemID)) {
770  // not sure what this is yet
771 
772  }
773  /*
774  * if (IsCargoHoldFlag(flag)) {
775  * // check for owner or corpID
776  *
777 } else if (IsHangarFlag(flag)) {
778  // check for owner or corpID
779 
780  flag = flagNone;
781  if (call.client->GetCorporationID() != m_ownerID)
782  ; // calling char is not member of corp. send error?
783 } else if (IsOfficeFlag(flag)) {
784  // flags for npc station containers, owned by station, but items owned by corp
785  // this is market deliveries, impounded, etc.
786  // check for corpID
787 
788  flag = flagNone;
789  if (call.client->GetCorporationID() != m_ownerID)
790  ; // calling char is not member of corp. send error?
791 }
792 */
793 
794  return pInventory->List(flag, ownerID);
795 }
796 
797 PyResult InventoryBound::Handle_CreateBookmarkVouchers(PyCallArgs &call) {
798  /*
799  * bookmarksDeleted, newVouchers = self.CreateBookmarkVouchers(bookmarkIDs, flag, isMove)
800  */
801  call.Dump(BOOKMARK__CALL_DUMP);
802 
803  if (m_self->ownerID() != call.client->GetCharID())
804  throw UserError ("CanOnlyCreateVoucherInPersonalHangar");
805 
806  Call_CreateVouchers args;
807  if (!args.Decode(&call.tuple)) {
808  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
809  return nullptr;
810  }
811 
812  PyList* vouchers = new PyList();
813  PyList* deleted = new PyList();
814 
816  if (args.flag == flagCargoHold)
817  locationID = call.client->GetShipID();
818 
819  // when trying to copy vouchers to jetcan, location is solar system....grrrr
820  if (sDataMgr.IsSolarSystem(locationID)) {
821  args.flag = flagCargoHold;
822  locationID = call.client->GetShipID();
823  }
824 
825  if ( args.bmIDs->size() < 1 ) {
826  sLog.Error( "IB::Handle_CreateBookmarkVouchers()", "%s: args.bmIDs->size() == 0. Expected size > 0.", call.client->GetName() );
827  } else {
828  BookmarkDB m_db;
829  PyList::const_iterator itr = args.bmIDs->begin();
830  for (; itr != args.bmIDs->end(); ++itr) {
831  //ItemData ( typeID, ownerID, locationID, flag, quantity, customInfo, contraband)
832  ItemData iData( 51, call.client->GetCharacterID(), locTemp, flagNone, 1, itoa(PyRep::IntegerValueU32(*itr)));
833  InventoryItemRef iRef = sItemFactory.SpawnItem( iData );
834  if (iRef.get() == nullptr) {
835  codelog(ITEM__ERROR, "%s: Failed to spawn bookmark voucher for bmID %u", call.client->GetName(), PyRep::IntegerValueU32(*itr));
836  continue;
837  }
838  //iRef->Rename(std::to_string(BookmarkDB::GetBookmarkName(PyRep::IntegerValueU32(*itr))));
839  iRef->Move(locationID, (EVEItemFlags)args.flag, true);
840  /*
841  PyDict* dict = new PyDict();
842  dict->SetItemString("description", new PyString(BookmarkDB::GetBookmarkName(atoi(iRef->customInfo().c_str()))));
843  dict->SetItemString( "itemID", new PyInt(iRef->itemID()));
844  dict->SetItemString( "typeID", new PyInt(iRef->type().id()));
845  dict->SetItemString( "ownerID", new PyInt(iRef->ownerID()));
846  dict->SetItemString( "locationID", new PyInt(iRef->locationID()));
847  dict->SetItemString( "flagID", new PyInt(iRef->flag()));
848  dict->SetItemString( "quantity", new PyInt(iRef->quantity()));
849  dict->SetItemString( "groupID", new PyInt(iRef->type().groupID()));
850  dict->SetItemString( "categoryID", new PyInt(iRef->type().categoryID()));
851  dict->SetItemString( "customInfo", new PyString(iRef->customInfo()));
852  vouchers->AddItem(new PyObject("util.KeyVal", dict));
853  */
854  if (args.isMove) {
855  PyDict* dict = new PyDict();
856  // may need more here. not sure yet
857  //dict->SetItemString("description", new PyString(BookmarkDB::GetBookmarkName(atoi(iRef->customInfo().c_str()))));
858  dict->SetItemString("bookmarkID", new PyInt(PyRep::IntegerValueU32(*itr)));
859  deleted->AddItem(new PyObject("util.KeyVal", dict));
860  // change owner in db to remove bm from current owner's pnp window
862  }
863  }
864  }
865 
866  // when bm is copied to another players' places tab, copy data from db using bookmarkID stored in ItemData.customInfo
867 
868  PyTuple* tuple = new PyTuple(2);
869  tuple->SetItem(0, deleted );
870  tuple->SetItem(1, vouchers);
871  tuple->Dump(BOOKMARK__RSP_DUMP, " ");
872  return tuple;
873 }
874 
879 PyResult InventoryBound::Handle_TakeOutTrash(PyCallArgs &call) {
880  //TakeOutTrash([ invItem.itemID for invItem in invItems ])
881  sLog.Error("IB::TakeOutTrash", "Character '%s', self: '%s'(%u)", call.client->GetName(), m_self->name(), m_itemID);
882  _log(INV__MESSAGE, "%s Calling InventoryBound::TakeOutTrash() for %s(%u)", call.client->GetName(), m_self->name(), m_itemID);
883  call.Dump(INV__DUMP);
884  return nullptr;
885 }
886 
887 PyResult InventoryBound::Handle_SetPassword(PyCallArgs &call) {
888  _log(INV__MESSAGE, "%s Calling InventoryBound::SetPassword() for %s(%u)", call.client->GetName(), m_self->name(), m_itemID);
889  call.Dump(INV__DUMP);
890  return nullptr;
891 }
892 
893 PyResult InventoryBound::Handle_ListDroneBay(PyCallArgs &call) {
894  // i dont think this one is used....
895  sLog.Error("IB::ListDroneBay", "Character '%s', self: '%s'(%u)", call.client->GetName(), m_self->name(), m_itemID);
896  _log(INV__MESSAGE, "%s Calling InventoryBound::ListDroneBay() for %s(%u)", call.client->GetName(), m_self->name(), m_itemID);
897  call.Dump(INV__DUMP);
898  return nullptr;
899 }
900 
901 PyResult InventoryBound::Handle_RunRefiningProcess(PyCallArgs &call) {
902  _log(POS__MESSAGE, "%s Calling InventoryBound::RunRefiningProcess() for %s(%u)", call.client->GetName(), m_self->name(), m_itemID);
903  call.Dump(POS__DUMP);
904  return nullptr;
905 }
uint32 GetShipID() const
Definition: Client.h:150
Base Python wire object.
Definition: PyRep.h:66
#define sConfig
A macro for easier access to the singleton.
PyTuple * AsTuple()
Definition: PyRep.h:138
Dispatcher *const m_dispatch
virtual InventoryItemRef Split(int32 qty=0, bool notify=true, bool silent=false)
void Donate(uint32 new_owner=ownerSystem, uint32 new_location=locTemp, EVEItemFlags new_flag=flagNone, bool notify=true)
uint32 GetLocationID() const
Definition: Client.h:151
void UnloadModule(uint32 itemID)
Definition: Ship.h:130
void PlanetXfer(uint32 spaceportID, std::map< uint32, uint16 > importItems, std::map< uint32, uint16 > exportItems, double taxRate)
Definition: Colony.cpp:1048
#define _log(type, fmt,...)
Definition: logsys.h:124
PyRep * GetItem(size_t index) const
Returns Python object.
Definition: PyRep.h:602
void StackAll(EVEItemFlags flag, uint32 ownerID=0)
Definition: Inventory.cpp:575
EVEItemFlags m_flag
virtual bool Merge(InventoryItemRef to_merge, int32 qty=0, bool notify=true)
Python's dictionary.
Definition: PyRep.h:719
std::map< std::string, PyRep * > byname
Definition: PyCallable.h:51
size_t size() const
Definition: PyRep.h:591
uint32 ownerID() const
Definition: InventoryItem.h:99
virtual void AddItem(InventoryItemRef iRef)
void VerifyHoldType(EVEItemFlags flag, InventoryItemRef iRef, Client *pClient=nullptr)
Definition: Ship.cpp:1972
EVEItemFlags
Definition: EVE_Flags.h:13
PyCallable_Make_InnerDispatcher(InventoryBound) InventoryBound
int32 GetCharacterID() const
Definition: Client.h:113
int32 GetCorporationID() const
Definition: Client.h:123
storage_type::const_iterator const_iterator
Definition: PyRep.h:644
static uint32 IntegerValueU32(PyRep *pRep)
Definition: PyRep.cpp:134
Inventory * pInventory
PyRep * GetItem() const
const char * name()
bool IsInSpace()
Definition: Client.h:228
void SendNotifyMsg(const char *fmt,...)
Definition: Client.cpp:2776
Python tuple.
Definition: PyRep.h:567
Advanced version of UserError that allows to send a full custom message.
Definition: PyExceptions.h:453
void Dump(FILE *into, const char *pfx) const
Dumps object to file.
Definition: PyRep.cpp:84
itemID[count] Create count or of the specified item(from Insider)" ) COMMAND( goto
void AddItem(PyRep *i)
Definition: PyRep.h:701
uint16 groupID() const
signed __int32 int32
Definition: eve-compat.h:49
Definition: Colony.h:38
* args
#define is_log_enabled(type)
Definition: logsys.h:78
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
double GetTimeUSeconds()
Definition: utils_time.cpp:116
void ChangeOwner(uint32 bookmarkID, uint32 ownerID=1)
Definition: BookmarkDB.cpp:301
#define IsPlayerItem(itemID)
Definition: EVE_Defines.h:256
Python object.
Definition: PyRep.h:826
Inventory * pInventory
#define codelog(type, fmt,...)
Definition: logsys.h:128
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
uint32 locationID() const
Python integer.
Definition: PyRep.h:231
PyDict * AsDict()
Definition: PyRep.h:142
#define PyStatic
Definition: PyRep.h:1209
X * get() const
Definition: RefPtr.h:213
ShipItemRef GetShip() const
Definition: Client.h:167
virtual ShipItem * GetShipItem()
Definition: InventoryItem.h:82
const char * GetName() const
Definition: Client.h:94
#define IsPlayerCorp(itemID)
Definition: EVE_Defines.h:241
PyRep * MoveItems(Client *pClient, std::vector< int32 > &items, EVEItemFlags toFlag, int32 quantity, bool manyFlags, float capacity)
uint32 GetCharID()
Definition: Client.h:166
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
itemID[count] Create count or of the specified() x() entityID Translocate to the specified entity Immediately stops ship
static RefPtr StaticCast(const RefPtr< Y > &oth)
Acts as static_cast from one RefPtr to another.
Definition: RefPtr.h:238
std::vector< int32 > CatSortItems(std::vector< InventoryItemRef > &itemVec)
unsigned __int32 uint32
Definition: eve-compat.h:50
EVEItemFlags flag() const
void TryModuleLimitChecks(EVEItemFlags flag, InventoryItemRef iRef)
Definition: Ship.cpp:736
const_iterator begin() const
Definition: PyRep.h:766
Definition: Ship.h:46
#define IsOfficeID(itemID)
Definition: EVE_Defines.h:253
#define IsModuleSlot(flag)
Definition: EVE_Defines.h:350
GenericModule * GetModule(EVEItemFlags flag)
Definition: Ship.h:173
#define IsCargoHoldFlag(flag)
Definition: EVE_Defines.h:336
storage_type::const_iterator const_iterator
Definition: PyRep.h:750
typeID Spawn an NPC with the specified type text Search for items matching the specified query() type()() itemID() copy() materialLevel()() itemID(attributeID)-Retrieves attribute value." ) COMMAND( setattr
virtual void RemoveItem(InventoryItemRef iRef)
Definition: Ship.cpp:532
void Dump(LogType type) const
Definition: PyCallable.cpp:81
InventoryItemRef m_self
#define IsRigSlot(flag)
Definition: EVE_Defines.h:367
#define IsHangarFlag(flag)
Definition: EVE_Defines.h:344
void RemoveCharge(EVEItemFlags fromFlag)
Definition: Ship.cpp:718
const_iterator end() const
Definition: PyRep.h:767
void traceStack(void)
Definition: misc.cpp:169
uint32 AddItemByFlag(EVEItemFlags flag, InventoryItemRef iRef, Client *pClient=nullptr)
Definition: Ship.cpp:486
EVEItemFlags FindAvailableModuleSlot(InventoryItemRef iRef)
Definition: Ship.cpp:797
#define sItemFactory
Definition: ItemFactory.h:165
void StripFitting()
Definition: Ship.cpp:1311
storage_type items
Definition: PyRep.h:628
static int64 IntegerValue(PyRep *pRep)
Definition: PyRep.cpp:118
#define IsControlBunker(itemID)
Definition: EVE_Defines.h:318
Inventory * GetMyInventory()
Definition: InventoryItem.h:91
unsigned __int16 uint16
Definition: eve-compat.h:48
entityID heal the character with the entityID note giving you detailed ship status information gives a list of all dynamic entities and players and their destinyState in this bubble shows some current destiny variables save all items
CRowSet * List(EVEItemFlags flag, uint32 ownerID=0) const
Definition: Inventory.cpp:290
bool ValidateAddItem(EVEItemFlags flag, InventoryItemRef iRef) const
Definition: Inventory.cpp:747
uint16 typeID() const
virtual bool IsShipItem()
Definition: InventoryItem.h:85
uint8 categoryID() const
Python list.
Definition: PyRep.h:639
virtual ~InventoryBound()
const char * itoa(int64 num)
Convers num to string.
void MoveModuleSlot(EVEItemFlags slot1, EVEItemFlags slot2)
Definition: Ship.cpp:818
uint32 itemID() const
Definition: InventoryItem.h:98
void SetItemString(const char *key, PyRep *value)
SetItemString adds or sets a database entry.
Definition: PyRep.h:812
const char * GetName() const
Definition: PyBoundObject.h:44
Dispatcher *const m_dispatch
int32 quantity() const
Definition: InventoryItem.h:97
PyTuple * tuple
Definition: PyCallable.h:50
#define sDataMgr
void RemoveRig(InventoryItemRef iRef)
Definition: Ship.cpp:1067