EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ShipService.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  This program is distributed in the hope that it will be useful, but WITHOUT
14  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16  You should have received a copy of the GNU Lesser General Public License along with
17  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18  Place - Suite 330, Boston, MA 02111-1307, USA, or go to
19  http://www.gnu.org/copyleft/lesser.txt.
20  ------------------------------------------------------------------------------------
21  Author: Zhur
22  Rewrite: Allan
23 */
24 
25 #include "eve-server.h"
26 
27 #include "PyBoundObject.h"
28 #include "PyServiceCD.h"
29 
30 #include "EVEServerConfig.h"
31 #include "npc/Drone.h"
32 #include "planet/CustomsOffice.h"
33 #include "planet/Moon.h"
34 #include "pos/Structure.h"
35 #include "ship/ShipService.h"
36 #include "system/Container.h"
37 #include "system/DestinyManager.h"
38 #include "system/SystemBubble.h"
39 #include "system/SystemManager.h"
42 
43 
44 class ShipBound
45 : public PyBoundObject
46 {
47 public:
49 
51  : PyBoundObject(mgr),
52  m_db(db),
53  pShip(ship),
54  m_dispatch(new Dispatcher(this))
55  {
57 
58  m_strBoundObjectName = "ShipBound";
59 
62  PyCallable_REG_CALL(ShipBound, LeaveShip);
63  PyCallable_REG_CALL(ShipBound, ActivateShip);
65  PyCallable_REG_CALL(ShipBound, AssembleShip);
68  PyCallable_REG_CALL(ShipBound, ScoopDrone);
69  PyCallable_REG_CALL(ShipBound, ScoopToSMA);
70  PyCallable_REG_CALL(ShipBound, LaunchFromContainer);
71  PyCallable_REG_CALL(ShipBound, Jettison);
72  PyCallable_REG_CALL(ShipBound, ConfigureShip);
73  PyCallable_REG_CALL(ShipBound, GetShipConfiguration);
74  PyCallable_REG_CALL(ShipBound, SelfDestruct);
75 
76  PyCallable_REG_CALL(ShipBound, BoardStoredShip);
77  PyCallable_REG_CALL(ShipBound, StoreVessel);
78  }
79 
80  virtual ~ShipBound() {delete m_dispatch;}
81  virtual void Release() {
82  //I hate this statement
83  delete this;
84  }
85 
87  PyCallable_DECL_CALL(Eject);
88  PyCallable_DECL_CALL(LeaveShip);
89  PyCallable_DECL_CALL(ActivateShip);
91  PyCallable_DECL_CALL(AssembleShip);
93  PyCallable_DECL_CALL(Scoop);
94  PyCallable_DECL_CALL(ScoopDrone);
95  PyCallable_DECL_CALL(ScoopToSMA);
96  PyCallable_DECL_CALL(LaunchFromContainer);
97  PyCallable_DECL_CALL(Jettison);
98  PyCallable_DECL_CALL(ConfigureShip);
99  PyCallable_DECL_CALL(GetShipConfiguration);
100  PyCallable_DECL_CALL(SelfDestruct);
101 
102  PyCallable_DECL_CALL(BoardStoredShip);
103  PyCallable_DECL_CALL(StoreVessel);
104 
105 protected:
107  Dispatcher *const m_dispatch;
108 
109 private:
111 
112 };
113 
115 
117 : PyService(mgr, "ship"),
118  m_dispatch(new Dispatcher(this))
119 {
120  _SetCallDispatcher(m_dispatch);
121 
122  //PyCallable_REG_CALL(ShipService,);
123 
124 }
125 
127  delete m_dispatch;
128 }
129 
132  /*
133  * 23:08:44 [ClientMsg] ShipService bind request
134  * 23:08:44 [ClientMsg] Tuple: 2 elements
135  * 23:08:44 [ClientMsg] [ 0] Integer: 60014137 <<-- locationID
136  * 23:08:44 [ClientMsg] [ 1] Integer: 15 <<-- location's groupID
137  */
138  _log(CLIENT__MESSAGE, "ShipService bind request");
139  bind_args->Dump(CLIENT__MESSAGE, " ");
140  return new ShipBound(m_manager, m_db, pClient->GetShip().get());
141 }
142 
143 /* only called in space */
144 PyResult ShipBound::Handle_Board(PyCallArgs &call) {
145  if (call.client->IsSessionChange()) {
146  call.client->SendNotifyMsg("Session Change currently active.");
147  return nullptr;
148  }
149 
150  Call_BoardShip args;
151  // .arg1 (newShipID) - itemID of the ship to be boarded
152  // .arg2 (oldShipID) - itemID of the current ship
153  if (!args.Decode(&call.tuple)) {
154  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
155  return nullptr;
156  }
157 
158  Client* pClient = call.client;
159 
160  ShipSE* pShipSE = pClient->GetShipSE();
161  if (pShipSE == nullptr)
162  throw CustomError ("Invalid Ship. Ref: ServerError xxxxx");
164  if (pShipSE->isGlobal()) { /* close enough. cyno (isGlobal() = true), so this will work */
165  /* find proper error msg for this...im sure there is one */
166  throw CustomError ("You cannot eject current ship with an active Cyno Field.");
167  }
168 
169  // do we need this? yes....this needs more work in destiny to implement correctly
170  if (pShipSE->DestinyMgr()->GetSpeed() > 20)
171  throw CustomError ("You cannot eject current ship while moving faster than 20m/s. Ref: ServerError 05139.");
172 
173  SystemManager* pSystem = pClient->SystemMgr();
174  if (pSystem == nullptr) {
175  codelog(CLIENT__ERROR, "%s: Client has no system manager!", call.client->GetName());
176  return nullptr;
177  }
178 
179  // this will segfault if newShipID is invalid or not in system inventory
180  pShipSE = pSystem->GetSE(args.newShipID)->GetShipSE();
181 
182  if (pShipSE == nullptr) {
183  _log(SHIP__ERROR, "Handle_Board() - Failed to get new ship %u for %s.", args.newShipID, pClient->GetName());
184  throw CustomError ("Something bad happened as you prepared to board the ship. Ref: ServerError 25107.");
185  }
186 
187  if (pShipSE->GetTypeID() == itemTypeCapsule) {
188  codelog(ITEM__ERROR, "Empty Pod %u in space. SystemID %u.", args.newShipID, pSystem->GetID());
189  throw CustomError ("You already have a pod. These cannot be boarded manally.");
190  }
191 
192  //CantBoardTargeted
193 
194  // do we need this? yes....this needs more work in destiny to implement correctly
195  if (pShipSE->DestinyMgr()->GetSpeed() > 20)
196  throw CustomError ("You cannot board the ship while it's moving faster than 20m/s. Ref: ServerError 05139.");
197 
198  // should we eject player here and deny boarding new ship, or just leave char in current ship and return?
199  if (!pShipSE->GetShipItemRef()->ValidateBoardShip(pClient->GetChar()))
200  throw CustomError ("You do not have the skills to fly a %s.", pShipSE->GetName());
201 
202  float distance = pClient->GetShipSE()->GetPosition().distance(pShipSE->GetPosition());
203  // fudge for radii ?
204  if (distance > sConfig.world.shipBoardDistance)
205  throw CustomError ("You are too far from %s to board it.<br>You must be within %u meters to board this ship.",\
206  pShipSE->GetName(), sConfig.world.shipBoardDistance);
207 
208  pClient->Board(pShipSE);
209 
210  /* return error msg from this call, if applicable (not sure how yet), else nodeid and timestamp */
211  // returns nodeID and timestamp
212  PyTuple* tuple = new PyTuple(2);
213  tuple->SetItem(0, new PyString(GetBindStr())); // node info here
214  tuple->SetItem(1, new PyLong(GetFileTimeNow()));
215  return tuple;
216 }
217 
218 /* only called in space */
219 PyResult ShipBound::Handle_Eject(PyCallArgs &call) {
220  if (call.client->IsSessionChange()) {
221  call.client->SendNotifyMsg("Session Change currently active.");
222  return nullptr;
223  }
224 
225  //no arguments.
226  Client* pClient = call.client;
234  //{'FullPath': u'UI/Messages', 'messageID': 256625, 'label': u'NoEjectingToSpaceInStationBody'}(u"You can't eject into space while you're docked. Try leaving your ship the usual way.", None, None)
235 
236  SystemEntity* pShipSE = pClient->GetShipSE();
237  if (pShipSE == nullptr)
238  throw CustomError ("Invalid Ship. Ref: ServerError xxxxx");
240  if (pShipSE->isGlobal()) { /* close enough. cyno (isGlobal() = true), so this will work */
241  /* find proper error msg for this...im sure there is one */
242  throw CustomError ("You cannot eject with an active Cyno Field.");
243  }
244 
245  // do we need this? yes....this needs more work in destiny to implement correctly
246  if (pShipSE->DestinyMgr()->GetSpeed() > 20)
247  throw CustomError ("You cannot eject current ship while moving faster than 20m/s. Ref: ServerError 05139.");
248 
249  pClient->Eject();
250 
251  /* return error msg from this call, if applicable (not sure how yet), else nodeid and timestamp */
252  // returns nodeID and timestamp
253  PyTuple* tuple = new PyTuple(2);
254  tuple->SetItem(0, new PyString(GetBindStr())); // node info here
255  tuple->SetItem(1, new PyLong(GetFileTimeNow()));
256  return tuple;
257 }
258 
259 // NOTE LeaveShip and ActivateShip are working. dont fuck with them
260 /* only called when docked. */
261 PyResult ShipBound::Handle_LeaveShip(PyCallArgs &call)
262 {
263  if (call.client->IsSessionChange()) {
264  call.client->SendNotifyMsg("Session Change currently active.");
265  return nullptr;
266  }
267 
268  Call_SingleIntegerArg arg;
269  if (!arg.Decode(&call.tuple)) {
270  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
271  return nullptr;
272  }
273 
274  // sends itemID of ship to leave
275  Client* pClient = call.client;
276  uint32 podID = pClient->GetPodID();
277  ShipItemRef podRef = pClient->SystemMgr()->GetShipFromInventory(podID);
278  if (podRef.get() == nullptr)
279  podRef = sItemFactory.GetShip(podID);
280 
281  //verify owner (not sure why pod doesnt have correct owner...)
282  podRef->ChangeOwner(pClient->GetCharacterID(), false);
283  //move capsule into the players hangar
284  podRef->Move(pClient->GetStationID(), flagHangar, true);
285 
286  // capsuleID = shipsvc.LeaveShip(shipid)
287  return new PyInt(podID);
288 }
289 
290 /* only called when docked. */
291 PyResult ShipBound::Handle_ActivateShip(PyCallArgs &call) {
292  //self.instanceCache, self.instanceFlagQuantityCache, self.wbData = self.remoteShipMgr.ActivateShip(shipID, oldShipID)
293 
294  if (call.client->IsSessionChange()) {
295  call.client->SendNotifyMsg("Session Change currently active.");
296  return nullptr;
297  }
298  Call_BoardShip args;
299  // .arg1 (newShipID) - itemID of the ship to be boarded
300  // .arg2 (oldShipID) - itemID of the current ship
301  if (!args.Decode(&call.tuple)) {
302  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
303  return nullptr;
304  }
305 
306  Client* pClient = call.client;
307  ShipItemRef newShipRef = sItemFactory.GetShip(args.newShipID);
308  if (newShipRef.get() == nullptr) {
309  sLog.Error("ShipBound::Handle_ActivateShip()", "%s: Failed to get new ship %u.", pClient->GetName(), args.newShipID);
310  throw CustomError ("Something bad happened as you prepared to board the ship. Ref: ServerError 15173+1");
311  }
312  //ShipMustBeInPersonalHangar
313 
314  pClient->BoardShip(newShipRef);
315 
316  // response should return ship modules, loaded charges, and linked weapons
317  PyTuple* rsp = new PyTuple(3);
318  rsp->SetItem(0, newShipRef->GetShipState()); //dict of ship modules
319  rsp->SetItem(1, newShipRef->GetChargeState()); //dict of flagID/subLocation{loc, flag, typeID}
320  rsp->SetItem(2, newShipRef->GetLinkedWeapons()); // dict of linked modules
321  if (is_log_enabled(CLIENT__INFO))
322  rsp->Dump(CLIENT__INFO, " ");
323  return rsp;
324 }
325 
326 PyResult ShipBound::Handle_Undock(PyCallArgs &call) {
327 
328  //ShipIllegalTypeUndock
329 
330  /* we could have some fun with these....
331  * (258696, `Undock Delayed`)
332  * (258702, `Undock Prohibited`)
333  * (258700, `Undock Delayed`)
334  * (258703, `Officials have closed the undocking ramps in {system} due to heavy congestion. Please try again later.`)
335  * (258697, `{system} Traffic Control is currently offline and unable to process your undocking request. Please try again in a moment.`)
336  */
337 
338  Call_IntBoolArg args;
339  if (!args.Decode(&call.tuple)) {
340  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
341  throw CustomError ("Something bad happened as you prepared to board the ship. Ref: ServerError 15173");
342  }
343 
344  Client* pClient = call.client;
345  ShipItemRef pShip = pClient->GetShip();
346  if (pShip.get() == nullptr) {
347  sLog.Error("ShipBound::Handle_ActivateShip()", "%s: Failed to get ship item.", pClient->GetName());
348  throw CustomError ("Something bad happened as you prepared to board the ship. Ref: ServerError 15173");
349  }
350 
351  // nowhere near implementing this one yet....
352  bool ignoreContraband(args.arg2);
353 
354  // get vector of online modules as (k,v) pair,
355  // where key is slotID, value is moduleID
356  if (call.byname.find("onlineModules") != call.byname.end()) {
357  PyDict* onlineModules = call.byname["onlineModules"]->AsDict();
358  if (is_log_enabled(MODULE__INFO)) {
359  _log(MODULE__INFO, "Dumping 'onlineModules' List");
360  onlineModules->Dump(MODULE__INFO, " ");
361  }
362  PyDict::const_iterator cur = onlineModules->begin(), end = onlineModules->end();
363  for (; cur != end; ++cur)
364  pShip->AddModuleToOnlineVec(cur->second->AsInt()->value());
365  }
366 
367  pClient->UndockFromStation();
368 
369  // returns nodeID and timestamp
370  PyTuple* tuple = new PyTuple(2);
371  tuple->SetItem(0, new PyString(GetBindStr())); // node info here
372  tuple->SetItem(1, new PyLong(GetFileTimeNow()));
373  return tuple;
374 }
375 
376 PyResult ShipBound::Handle_Drop(PyCallArgs &call)
377 {
378  _log(SHIP__INFO, "ShipBound::Handle_Drop()");
379  call.Dump(SHIP__INFO);
380 
381  if (sDataMgr.IsStation(call.client->GetLocationID())) {
382  _log(SERVICE__ERROR, "%s: Trying to drop items when not in space!", call.client->GetName());
383  return nullptr;
384  }
385 
386  Call_Drop3 args;
387  if (!args.Decode(&call.tuple)) {
388  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
389  return nullptr;
390  }
391 
392  PyList* PyToDropList = args.toDrop;
393  uint32 ownerID = args.ownerID; // not sent for LaunchDrone() command (not needed)
394  //used for LaunchUpgradePlatformWarning
395  // args.ignoreWarning
396 
397  bool dropped = false, shipDrop = false;
398 
399  Client* pClient = call.client;
400  SystemManager* pSystem = pClient->SystemMgr();
401  if (pSystem == nullptr) {
402  codelog(CLIENT__ERROR, "%s: Client has no system manager!", call.client->GetName());
403  return nullptr;
404  }
405 
406  uint8 qty = 0;
407  uint32 itemID = 0;
408  double radius = pShip->radius();
409 
410  InventoryItemRef iRef(nullptr);
411  PyDict* dict = new PyDict();
412  for (uint32 i = 0; i < PyToDropList->size(); ++i) {
413  dropped = false;
414  PyList* list = new PyList();
415  GPoint location(pShip->position());
416  location.MakeRandomPointOnSphereLayer(500,1500);
417  qty = PyToDropList->items.at(i)->AsTuple()->items.at(1)->AsInt()->value();
418  itemID = PyToDropList->items.at(i)->AsTuple()->items.at(0)->AsInt()->value();
419  iRef = sItemFactory.GetItem(itemID);
420  if (iRef.get() == nullptr) {
421  sLog.Error("ShipBound::Handle_Drop()", "%s: Unable to find item %u to drop.", pClient->GetName(), itemID);
422  continue;
423  }
424 
425  switch (iRef->categoryID()) {
427  if (!sConfig.testing.EnableDrones) {
428  throw CustomError ("Drones are disabled.");
429  }
430 
431  if (pClient->GetChar()->GetAttribute(AttrMaxActiveDrones).get_uint32() < 1) {
432  throw UserError ("NoDroneManagementAbilities")
433  .AddFormatValue ("typeID", new PyInt (iRef->typeID ()));
434  //{'FullPath': u'UI/Messages', 'messageID': 259203, 'label': u'NoDroneManagementAbilitiesBody'}(u'You cannot launch {[item]typeID.nameWithArticle} because you do not have the ability to control any drones.', None, {u'{[item]typeID.nameWithArticle}': {'conditionalValues': [], 'variableType': 2, 'propertyName': 'nameWithArticle', 'args': 0, 'kwargs': {}, 'variableName': 'typeID'}})
435  }
436  if (pClient->GetChar()->GetAttribute(AttrMaxActiveDrones).get_uint32() <= pClient->GetShipSE()->DroneCount()) {
437  throw UserError ("NoDroneManagementAbilitiesLeft")
438  .AddFormatValue ("item", new PyInt (iRef->typeID ()))
439  .AddFormatValue ("limit", new PyInt (pClient->GetChar ()->GetAttribute (AttrMaxActiveDrones).get_uint32()));
440  //{'FullPath': u'UI/Messages', 'messageID': 259140, 'label': u'NoDroneManagementAbilitiesLeftBody'}(u'You cannot launch {[item]item.name} because you are already controlling {[numeric]limit} drones, as much as you have skill to.', None, {u'{[numeric]limit}': {'conditionalValues': [], 'variableType': 9, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'limit'}, u'{[item]item.name}': {'conditionalValues': [], 'variableType': 2, 'propertyName': 'name', 'args': 0, 'kwargs': {}, 'variableName': 'item'}})
441  }
442 
443  if (iRef->flag() != flagDroneBay) {
444  throw UserError ("DropItemNotInDroneBay")
445  .AddFormatValue ("item", new PyInt (iRef->typeID ()));
446  // {'FullPath': u'UI/Messages', 'messageID': 259680, 'label': u'DropItemNotInDroneBayBody'}(u'{[item]item.name} cannot be dropped because it is not in your drone bay.', None, {u'{[item]item.name}': {'conditionalValues': [], 'variableType': 2, 'propertyName': 'name', 'args': 0, 'kwargs': {}, 'variableName': 'item'}})
447  }
448 
449  // This item is a drone, so launch it into space:
450  if (qty > 1) {
451  for (uint8 i = 0; i < qty; ++i) {
452  InventoryItemRef newItem = iRef->Split(1);
453  if (newItem.get() == nullptr) {
454  _log(INV__ERROR, "ShipBound::Handle_Drop() - Error splitting item %u. Skipping.", iRef->itemID());
455  continue;
456  }
457  if (newItem->quantity() > 1)
458  _log(INV__ERROR, "ShipBound::Handle_Drop() - Split item %u qty > 1 (%u). Continuing.", newItem->itemID(), newItem->quantity());
459 
460  if (pClient->GetShipSE()->LaunchDrone(newItem)) {
461  dropped = true;
462  shipDrop = true;
463  list->AddItem(new PyInt(newItem->itemID()));
464  } else
465  throw UserError ("MaxBandwithExceeded2")
466  .AddTypeName ("droneNAme", newItem->typeID ())
467  .AddAmount ("droneBandwithUsed", newItem->GetAttribute (AttrDroneBandwidthUsed).get_uint32())
468  .AddAmount ("bandwidthLeft", pShip->GetAttribute (AttrDroneBandwidth).get_uint32 () - pShip->GetAttribute (AttrDroneBandwidthLoad).get_uint32());
469  }
470  } else {
471  if (pClient->GetShipSE()->LaunchDrone(iRef)) {
472  dropped = true;
473  shipDrop = true;
474  list->AddItem(new PyInt(iRef->itemID()));
475  } else
476  throw UserError ("MaxBandwithExceeded2")
477  .AddTypeName ("droneNAme", iRef->typeID ())
478  .AddAmount ("droneBandwithUsed", iRef->GetAttribute (AttrDroneBandwidthUsed).get_uint32())
479  .AddAmount ("bandwidthLeft", pShip->GetAttribute (AttrDroneBandwidth).get_uint32 () - pShip->GetAttribute (AttrDroneBandwidthLoad).get_uint32());
480  }
481  } break;
483  // test for existing tower
484  if (iRef->groupID() == EVEDB::invGroups::Control_Tower) {
485  if (pClient->SystemMgr()->GetClosestMoonSE(location)->GetMoonSE()->HasTower()) {
486  pClient->SendErrorMsg("This Moon already has a Control Tower in orbit. Aborting Drop.");
487  return nullptr;
488  }
489  } else {
490  if (!pClient->SystemMgr()->GetClosestMoonSE(location)->GetMoonSE()->HasTower()) {
491  pClient->SendErrorMsg("You need an anchored Control Tower before launching modules. Aborting Drop.");
492  return nullptr;
493  }
494  }
496  //{'FullPath': u'UI/Messages', 'messageID': 259679, 'label': u'DropNeedsPlayerCorpBody'}(u'In order to launch {[item]item.name} you need to be a member of a independent corporation.', None, {u'{[item]item.name}': {'conditionalValues': [], 'variableType': 2, 'propertyName': 'name', 'args': 0, 'kwargs': {}, 'variableName': 'item'}})
497  //{'FullPath': u'UI/Messages', 'messageID': 256411, 'label': u'CantInHighSecSpaceBody'}(u'You cannot do that as CONCORD currently restricts the launching, anchoring and control of that type of structure within CONCORD-protected space to authorized agents of the empires.', None, None)
498 
500  entity.ownerID = ownerID;
501  entity.factionID = pClient->GetWarFactionID();
502  entity.allianceID = pClient->GetAllianceID();
503  entity.corporationID = pClient->GetCorporationID();
504  entity.itemID = iRef->itemID();
505  entity.itemName = iRef->itemName();
506  entity.typeID = iRef->typeID();
507  entity.groupID = iRef->groupID();
508  entity.categoryID = iRef->categoryID();
509 
510  // Move item from cargo bay to space: (and send OnItemChange packet)
511  iRef->Move(pClient->GetLocationID(), flagNone, true);
512  iRef->SetPosition(location + iRef->radius() + radius);
513  iRef->ChangeOwner(entity.ownerID);
514 
515  entity.position = iRef->position();
516 
518  entity.planetID = pSystem->GetClosestPlanetID(location);
519 
520  SystemEntity* pSE = DynamicEntityFactory::BuildEntity(*pSystem, entity);
521  if (pSE == nullptr) {
522  //couldnt create entity. move item back to orig location and continue
523  iRef->Donate(pClient->GetCharacterID(), pShip->itemID(), flagCargoHold);
524  continue;
525  }
526 
527  // item was successfully created. set singleton
528  iRef->ChangeSingleton(true);
529 
530  dropped = true;
531  shipDrop = true;
532  pSE->GetPOSSE()->Drop(pClient->GetShipSE()->SysBubble());
533  pSystem->AddEntity(pSE);
534  list->AddItem(new PyInt(entity.itemID));
535  } break;
537  pClient->SendNotifyMsg("Launching Deployables isnt available yet.");
538  } break;
540  //Code for spawning sovereignty structures
541 
542  //Check if system is in empire space
543  SystemData sysData;
544  sDataMgr.GetSystemData(pClient->GetSystemID(), sysData);
545  if (sysData.factionID) {
546  pClient->SendErrorMsg("Launching sovereignty structures is forbidden in empire space.");
547  return nullptr;
548  }
549 
550  //Check if client is part of an alliance
551  if (pClient->GetAllianceID() == 0) {
552  pClient->SendErrorMsg("You must be part of an alliance to launch a sovereignty structure.");
553  return nullptr;
554  }
555 
556  switch (iRef->groupID()) {
558  // Make sure SBU is deployed in the same bubble as a gate
559  std::vector<uint16> gateBubbles;
560  for (auto cur: pSystem->GetOperationalStatics()) {
561  if (cur.second->IsGateSE())
562  {
563  gateBubbles.push_back(cur.second->SysBubble()->GetID());
564  }
565  }
566  if (!(std::find(gateBubbles.begin(), gateBubbles.end(),pClient->GetShipSE()->SysBubble()->GetID())!=gateBubbles.end()))
567  {
568  pClient->SendErrorMsg("Sovereignty blockade units must be deployed near a stargate.");
569  return nullptr;
570  }
571 
572  // Check if this system is currently claimed
573  if (svDataMgr.GetSystemAllianceID(pClient->GetSystemID()) == 0 ) {
574  pClient->SendErrorMsg("You cannot launch a Sovereignty Blockade Unit in an unclaimed system.");
575  return nullptr;
576  }
577 
578  // Check if this system is claimed by your alliance
579  if (svDataMgr.GetSystemAllianceID(pClient->GetSystemID()) == pClient->GetAllianceID()) {
580  pClient->SendErrorMsg("You cannot launch a Sovereignty Blockade Unit in a system claimed by your alliance");
581  return nullptr;
582  }
583 
584  // Check if there is already an SBU on this stargate
585  SystemEntity* pSE = pSystem->GetClosestGateSE(pClient->GetShipSE()->GetPosition());
586  if (pSE->GetGateSE()->HasSBU()) {
587  pClient->SendErrorMsg("There is already a Sovereignty Blockade Unit on this stargate. Aborting Drop.");
588  return nullptr;
589  }
590 
591  // Check if there is already an unanchored SBU in the current bubble
592  for (auto cur: pSystem->GetOperationalStatics()) {
593  if (cur.second->IsSBUSE())
594  {
595  if (cur.second->SysBubble()->GetID() == pClient->GetShipSE()->SysBubble()->GetID())
596  {
597  pClient->SendErrorMsg("There is already a Sovereignty Blockade Unit on this stargate. Aborting Drop.");
598  return nullptr;
599  }
600  }
601  }
602  } break;
603  //verify only one TCU or IHub is deployed in the system.
604  // todo: check Structure.h - these can be dropped and anchored, but not onlined if >1 in system
606  // Only one can be launched in a system
607  for (auto cur: pSystem->GetOperationalStatics()) {
608  if (cur.second->IsTCUSE())
609  {
610  pClient->SendErrorMsg("There is already a Territorial Claim Unit in this system. Aborting Drop.");
611  return nullptr;
612  }
613  }
614 
615  //This should never hit as there should always be a TCU in a claimed system
616  if (svDataMgr.GetSystemAllianceID(pClient->GetSystemID()) != 0 ) {
617  pClient->SendErrorMsg("This system has already been claimed. ");
618  return nullptr;
619  }
620  } break;
622  // Only one can be launched in a system
623  for (auto cur: pSystem->GetOperationalStatics()) {
624  if (cur.second->IsIHubSE())
625  {
626  pClient->SendErrorMsg("There is already an Infrastructure Hub this system. Aborting Drop.");
627  return nullptr;
628  }
629  }
630 
631  // Check if this system is not claimed by your alliance
632  if (svDataMgr.GetSystemAllianceID(pClient->GetSystemID()) != uint32(pClient->GetAllianceID())) {
633  pClient->SendErrorMsg("You cannot launch an Infrastructure Hub in a system not claimed by your alliance.");
634  return nullptr;
635  }
636  } break;
637  }
638 
640  entity.ownerID = ownerID;
641  entity.factionID = pClient->GetWarFactionID();
642  entity.allianceID = pClient->GetAllianceID();
643  entity.corporationID = pClient->GetCorporationID();
644  entity.itemID = iRef->itemID();
645  entity.itemName = iRef->itemName();
646  entity.typeID = iRef->typeID();
647  entity.groupID = iRef->groupID();
648  entity.categoryID = iRef->categoryID();
649 
650  // Move item from cargo bay to space: (and send OnItemChange packet)
651  iRef->Move(pClient->GetLocationID(), flagNone, true);
652  iRef->SetPosition(location + iRef->radius() + radius);
653  iRef->ChangeOwner(entity.ownerID);
654 
655  entity.position = iRef->position();
656 
657  SystemEntity* pSE = DynamicEntityFactory::BuildEntity(*pSystem, entity);
658  if (pSE == nullptr) {
659  //couldnt create entity. move item back to orig location and continue
660  iRef->Donate(pClient->GetCharacterID(), pShip->itemID(), flagCargoHold);
661  continue;
662  }
663 
664  // item was successfully created. set singleton
665  iRef->ChangeSingleton(true);
666 
667  dropped = true;
668  shipDrop = true;
669  pSE->GetPOSSE()->Drop(pClient->GetShipSE()->SysBubble());
670  pSystem->AddEntity(pSE);
671  list->AddItem(new PyInt(entity.itemID));
672  } break;
673  default: {
674  _log(INV__ERROR, "ShipBound::Handle_Drop() - Item %s (cat %u) is neither drone, structure or deployable.", iRef->name(), iRef->categoryID());
675  }
676  }
677 
678  // returns dict of dropped items as {itemID, data} where data is list of itemIDs dropped (split stack if applicable)
679  // however, on non-throw error, data is tuple of errID, errDetailsType, errDetails (unknown where these are defined)
680 
681  if (dropped) {
682  dict->SetItem(new PyInt(iRef->itemID()), list);
683  } else {
684  PyTuple* err = new PyTuple(3);
685  err->SetItem(0, new PyInt(1));
686  err->SetItem(1, new PyString("unsure"));
687  err->SetItem(2, new PyString("misc error"));
688  dict->SetItem(new PyInt(iRef->itemID()), err);
689  }
690  }
691 
692  // missing launch error throw msgs.... LaunchCPWarning, LaunchUpgradePlatformWarning
693  // - these are thrown before item is launched and will negate entire launch group until warning is approved, then entire group is reprocessed
694 
695  if (shipDrop)
696  pClient->GetShipSE()->DestinyMgr()->SendJettisonPacket();
697 
698  return dict;
699 }
700 
701 PyResult ShipBound::Handle_Scoop(PyCallArgs &call) {
702  Call_SingleIntegerArg arg;
703  if (!arg.Decode(&call.tuple)) {
704  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
705  //TODO: throw exception
706  return nullptr;
707  }
708 
709  Client* pClient(call.client);
710  SystemManager* pSysMgr(pClient->SystemMgr());
711  if (pSysMgr == nullptr) {
712  codelog(CLIENT__ERROR, "%s: Client has no system manager.", pClient->GetName());
713  return PyStatic.mtDict();
714  }
715  SystemEntity* pSE = pSysMgr->GetSE(arg.arg);
716  if (pSE == nullptr) {
717  _log(SERVICE__ERROR, "%s: Unable to find object %u to scoop.", pClient->GetName(), arg.arg);
718  return PyStatic.mtDict();
719  //{'FullPath': u'UI/Messages', 'messageID': 258825, 'label': u'ScoopObjectGoneBody'}(u'{target} is no longer there.', None, {u'{target}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'target'}})
720  }
721 
722  // check to see if this object is anchored and if so, refuse to scoop it
723  if (pSE->IsContainerSE())
724  if (pSE->GetContSE()->IsAnchored())
725  throw CustomError ("%s is anchored. Cannot scoop.", pSE->GetName());
726 
727  // check drones for other pilots control
728  if (pSE->IsDroneSE())
729  if (pSE->GetDroneSE()->IsEnabled())
730  if (pSE->GetDroneSE()->GetOwner() != pClient)
731  throw CustomError ("%s is under another pilot's control. Cannot scoop.", pSE->GetName());
732 
733  InventoryItemRef iRef = pSE->GetSelf();
734  if (iRef.get() == nullptr) {
735  codelog(CLIENT__ERROR, "ItemRef for %s not found.", arg.arg);
736  return PyStatic.mtDict();
737  }
738 
739  // Check cargo bay capacity:
740  if (pClient->GetShip()->GetMyInventory()->ValidateAddItem(flagCargoHold, iRef)) { // this will throw if it fails
741  // We have enough Cargo bay capacity to hold the item being scooped,
742  // so take ownership of it
743  iRef->ChangeOwner(pClient->GetCharacterID(), true);
744  // perform data cleanup for structures
745  if (pSE->IsPOSSE())
746  pSE->GetPOSSE()->Scoop();
747  // perform data cleanup for drones
748  if (pSE->IsDroneSE())
749  pClient->GetShipSE()->ScoopDrone(pSE);
750  // move it into the cargo bay:
751  pClient->MoveItem(iRef->itemID(), pClient->GetShipID(), flagCargoHold);
752  pSysMgr->RemoveEntity(pSE);
753  // delete SE since item is no longer in space
754  SafeDelete(pSE);
755  }
756 
757  return PyStatic.mtDict();
758 }
759 
760 PyResult ShipBound::Handle_ScoopDrone(PyCallArgs &call) {
761  Call_SingleIntList args;
762  if (!args.Decode(&call.tuple)) {
763  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
764  return PyStatic.mtDict();
765  }
766  // per patch notes, if ship is too far to scoop, it will automagically travel closer till drone is within range, then scoop and stop
767 
768  Client* pClient(call.client);
769  SystemEntity* pDroneSE(nullptr);
770  InventoryItemRef iRef(nullptr);
771  SystemManager* pSysMgr(pClient->SystemMgr());
772  std::vector<int32>::const_iterator cur = args.ints.begin();
773  for(; cur != args.ints.end(); ++cur) {
774  pDroneSE = pSysMgr->GetSE(*cur);
775  if (pDroneSE == nullptr) {
776  _log(SERVICE__ERROR, "%s: Unable to find droneSE %u to scoop.", pClient->GetName(), *cur);
777  continue;
778  }
779 
780  iRef = pDroneSE->GetSelf();
781  if (iRef.get() == nullptr) {
782  _log(SERVICE__ERROR, "%s: Unable to find droneItem %u to scoop.", pClient->GetName(), *cur);
783  continue;
784  }
785 
786  //AttrDroneBaySlotsLeft??
787  // Check to see that this is really a drone:
788  pClient->GetShip()->VerifyHoldType(flagDroneBay, iRef, pClient);
789 
790  // check ownership/control
791  if (pDroneSE->GetDroneSE()->IsEnabled())
792  if (pDroneSE->GetDroneSE()->GetOwner() != pClient)
793  throw CustomError ("The %s is under another pilot's control. Cannot scoop.", pDroneSE->GetName());
794 
795  // Check drone bay capacity:
796  if (pClient->GetShip()->GetMyInventory()->ValidateAddItem(flagDroneBay, iRef)) { // this will throw if it fails
797  // We have enough Drone bay capacity to hold the drone,
798  // so take ownership of it and move it into the Drone bay:
799  iRef->ChangeOwner(pClient->GetCharacterID(), true);
800  pClient->MoveItem(iRef->itemID(), pClient->GetShipID(), flagDroneBay);
801  pClient->GetShipSE()->ScoopDrone(pDroneSE);
802  pSysMgr->RemoveEntity(pDroneSE);
803  SafeDelete(pDroneSE);
804  }
805  }
806 
808  // returns error on error else mtDict
809  return PyStatic.mtDict();
810 }
811 
812 PyResult ShipBound::Handle_Jettison(PyCallArgs &call) {
813  Call_SingleIntList args;
814  if (!args.Decode(&call.tuple)) {
815  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
816  //TODO: throw exception
817  return nullptr;
818  }
819 
820  Client* pClient(call.client);
821  if (!pClient->IsInSpace()) {
822  _log(SERVICE__ERROR, "%s: Trying to jettison items when not in space!", pClient->GetName());
823  return nullptr;
824  }
825 
826  SystemManager* pSysMgr(pClient->SystemMgr());
827  //Get location of our ship
828  GPoint location(pClient->GetShipSE()->GetPosition());
829 
830  InventoryItemRef cRef(nullptr), iRef(nullptr);
831  CargoContainerRef jcRef(nullptr), ccRef(nullptr);
832  StructureItemRef sRef(nullptr);
833 
836  FactionData data = FactionData();
837  data.allianceID = pClient->GetAllianceID();
838  data.corporationID = pClient->GetCorporationID();
839  data.factionID = pClient->GetWarFactionID();
841  data.ownerID = pClient->GetCharacterID();
842 
843  // returns nodeID and timestamp
844  PyTuple* tuple = new PyTuple(2);
845  tuple->SetItem(0, new PyString(GetBindStr())); // node info here
846  tuple->SetItem(1, new PyLong(GetFileTimeNow()));
847 
848  //args contains id's of items to jettison
849  std::vector<int32>::iterator itr = args.ints.begin();
850  // loop thru items to see if there is a container in this list.
851  for (; itr != args.ints.end(); ++itr) {
852  // running this list twice is fuckedup, but not sure of another way to determine if container is in jettison list.
853  iRef = sItemFactory.GetItem(*itr);
854  if (iRef.get() == nullptr)
855  continue;
856  switch (iRef->categoryID()) {
860  sRef = sItemFactory.GetStructure(*itr);
861  if (sRef.get() == nullptr)
862  throw CustomError ("Unable to spawn Structure item of type %u.", sRef->typeID());
863 
864  sRef->Move(pClient->GetLocationID(), flagNone, true);
865  StructureSE* sSE = new StructureSE(sRef, *m_manager, pSysMgr, data);
866  location.MakeRandomPointOnSphere(1500.0 + sRef->type().radius());
867  sSE->SetPosition(location);
868  sRef->SaveItem();
869  pSysMgr->AddEntity(sSE);
870  pClient->GetShipSE()->DestinyMgr()->SendJettisonPacket();
871  itr = args.ints.erase(itr);
872  } break;
874  sRef = sItemFactory.GetStructure(*itr);
875  if (sRef.get() == nullptr)
876  throw CustomError ("Unable to spawn Structure item of type %u.", sRef->typeID());
877 
878  sRef->Move(pClient->GetLocationID(), flagNone, true);
879  CustomsSE* sSE = new CustomsSE(sRef, *m_manager, pSysMgr, data);
880  location.MakeRandomPointOnSphere(1500.0 + sRef->type().radius());
881  sSE->SetPosition(location);
882  sRef->SaveItem();
883  pSysMgr->AddEntity(sSE);
884  pClient->GetShipSE()->DestinyMgr()->SendJettisonPacket();
885  itr = args.ints.erase(itr);
886  } break;
888  cRef = sItemFactory.GetItem(*itr);
889  if (cRef.get() == nullptr)
890  throw CustomError ("Unable to spawn Deployable item of type %u.", cRef->typeID());
891 
892  cRef->Move(pClient->GetLocationID(), flagNone, true);
893  //flagUnanchored: for some DUMB reason, this flag, 1023 yields a PyNone when notifications
894  // are created inside InventoryItem::Move() from passing it into a PyInt() constructor...WTF?
895  DeployableSE* dSE = new DeployableSE(cRef, *m_manager, pSysMgr, data);
896  location.MakeRandomPointOnSphere(1500.0 + cRef->type().radius());
897  dSE->SetPosition(location);
898  cRef->SaveItem();
899  pSysMgr->AddEntity(dSE);
900  pClient->GetShipSE()->DestinyMgr()->SendJettisonPacket();
901  itr = args.ints.erase(itr);
902  } break;
905  // test for cargo container being launched
907  switch (iRef->groupID()) {
914  ccRef = sItemFactory.GetCargoContainer(*itr);
915  if (ccRef.get() == nullptr)
916  throw CustomError ("Unable to spawn item of type %u.", ccRef->typeID());
917 
918  ccRef->Move(pClient->GetLocationID(), flagNone, true);
919  ContainerSE* cSE = new ContainerSE(ccRef, *m_manager, pSysMgr, data);
920  location.MakeRandomPointOnSphere(500.0);
921  cSE->SetPosition(location);
922  ccRef->SaveItem();
923  pSysMgr->AddEntity(cSE);
924  pClient->GetShipSE()->DestinyMgr()->SendJettisonPacket();
925 
926  // container found. remove this item from list, then break out of here and use to contain all other non-pos items
927  args.ints.erase(itr);
928  itr = args.ints.end();
929  } break;
937  //these should need corp shit for use, i think.
938  sLog.Warning("Ship::Jettison", "%s: %s called to jettison.",pClient->GetName(), iRef->name());
939  } break;
941  // not sure what this is, can it be jettisoned?
942  sLog.Error("Ship::Jettison", "%s: %s called to jettison.",pClient->GetName(), iRef->name());
943  } break;
944  }
945  }
946  }
947  }
948 
949  // container check complete, loop thru list for other items
950  for (auto cur : args.ints) {
951  iRef = sItemFactory.GetItem(cur);
952  if (iRef.get() == nullptr)
953  continue;
954 
955  // item can be jettisoned. check if container was already created
956  if ((ccRef.get() == nullptr) and (jcRef.get() == nullptr)) {
957  if (!pClient->IsJetcanAvalible()) {
958  throw UserError ("ShpJettisonPending")
959  .AddTimeShort ("eta", pClient->JetcanTime() * EvE::Time::Second);
960  }
961  // Spawn jetcan then continue loop
962  location.MakeRandomPointOnSphere(500.0);
963  ItemData p_idata(
964  23, // 23 = cargo container
965  pClient->GetCharacterID(), //owner is Character? figure out how to test for corp owner
966  pClient->GetLocationID(),
967  flagNone,
968  "Jettisoned Cargo Container",
969  location);
970 
971  jcRef = sItemFactory.SpawnCargoContainer(p_idata);
972  if (jcRef.get() == nullptr)
973  throw CustomError ("Unable to spawn jetcan.");
974  // create new container
975  ContainerSE* cSE = new ContainerSE(jcRef, *m_manager, pSysMgr, data);
976 
977  jcRef->SetMySE(cSE);
978  // set anchored to avoid deletion when empty
979  jcRef->SetAnchor(true);
980  pSysMgr->AddEntity(cSE);
981  pClient->GetShipSE()->DestinyMgr()->SendJettisonPacket();
982  pClient->StartJetcanTimer();
983  }
984  // check current can for capacity limits.
985  //if over limit create new can? reject remaining cargo? delete? crash? run thru station naked?
986  if (ccRef.get() != nullptr) {
987  if (ccRef->GetMyInventory()->HasAvailableSpace(flagNone, iRef)) {
988  pClient->MoveItem(cur, ccRef->itemID(), flagNone);
989  } else {
990  // extra step, try to move as much items as possible, this needs a new item creation tho
991  float remainingCapacity = jcRef->GetMyInventory ()->GetRemainingCapacity (flagNone);
992  int32 maximumAmountOfItems = (int32) floor (remainingCapacity / iRef->GetAttribute (AttrVolume).get_float ());
993 
994  ItemData newItem(iRef->typeID(), iRef->ownerID(), jcRef->itemID(), flagNone, maximumAmountOfItems);
995  jcRef->AddItem(sItemFactory.SpawnItem(newItem));
996 
997  iRef->AlterQuantity (-maximumAmountOfItems, true);
998  _log(ITEM__WARNING, "%s: CargoContainer %u is full.", pClient->GetName(), ccRef->itemID());
999  throw UserError ("NotAllItemsWereMoved");
1000  }
1001  } else if (jcRef.get() != nullptr) {
1002  if (jcRef->GetMyInventory()->HasAvailableSpace(flagNone, iRef)) {
1003  pClient->MoveItem(cur, jcRef->itemID(), flagNone);
1004  } else {
1005  // extra step, try to move as much items as possible, this needs a new item creation tho
1006  float remainingCapacity = jcRef->GetMyInventory ()->GetRemainingCapacity (flagNone);
1007  int32 maximumAmountOfItems = (int32) floor (remainingCapacity / iRef->GetAttribute (AttrVolume).get_float ());
1008 
1009  ItemData newItem(iRef->typeID(), iRef->ownerID(), jcRef->itemID(), flagNone, maximumAmountOfItems);
1010  jcRef->AddItem(sItemFactory.SpawnItem(newItem));
1011 
1012  iRef->AlterQuantity (-maximumAmountOfItems, true);
1013 
1014  _log(ITEM__WARNING, "%s: Jetcan %u is full.", pClient->GetName(), jcRef->itemID());
1015  throw UserError ("NotAllItemsWereMoved");
1016  }
1017  } else {
1018  _log(ITEM__ERROR, "Jettison call for %s - no CC or Jcan.", pClient->GetName());
1019  throw CustomError ("Item container not found.", cRef->typeID());
1020  }
1021  continue;
1022  }
1023 
1024  return tuple;
1025 }
1026 
1027 PyResult ShipBound::Handle_AssembleShip(PyCallArgs &call) {
1028 
1029  /* 13:05:41 [BindDump] NodeID: 888444 BindID: 129 calling AssembleShip in service manager 'ShipBound'
1030  * 13:05:41 [BindDump] Call Arguments:
1031  * 13:05:41 [BindDump] Tuple: 1 elements
1032  * 13:05:41 [BindDump] [ 0] List: 5 elements
1033  * 13:05:41 [BindDump] [ 0] [ 0] Integer field: 140000073
1034  * 13:05:41 [BindDump] [ 0] [ 1] Integer field: 140000074
1035  * 13:05:41 [BindDump] [ 0] [ 2] Integer field: 140000075
1036  * 13:05:41 [BindDump] [ 0] [ 3] Integer field: 140000076
1037  * 13:05:41 [BindDump] [ 0] [ 4] Integer field: 140000077
1038  *
1039  * [PyTuple 2 items] << response to AssembleShip call
1040  * [PyList 1 items]
1041  * [PyPackedRow 33 bytes]
1042  * ["itemID" => <1002333477860> [I8]]
1043  * ["typeID" => <24700> [I4]]
1044  * ["ownerID" => <1661059544> [I4]]
1045  * ["locationID" => <61000012> [I8]]
1046  * ["flagID" => <4> [I2]]
1047  * ["quantity" => <-1> [I4]]
1048  * ["groupID" => <419> [I2]]
1049  * ["categoryID" => <6> [I2]]
1050  * ["customInfo" => <empty string> [Str]]
1051  * [PyDict 1 kvp]
1052  * [PyInt 10] << flagdisconnect??
1053  * [PyInt 0]
1054  */
1055 
1056  call.Dump(COLLECT__CALL_DUMP);
1057  if (call.tuple->empty())
1058  return nullptr;
1059 
1060  bool t3Ship = false;
1061  std::vector<int32> itemIDList;
1062  PyList* subSystemList(nullptr);
1063  if (call.byname.find("subSystems") != call.byname.end()) {
1064  /*
1065  * 21:29:15 [Bound] ShipBound::AssembleShip()
1066  * 21:29:15 [CollectCallDump] Call Arguments:
1067  * 21:29:15 [CollectCallDump] Tuple: 1 elements
1068  * 21:29:15 [CollectCallDump] [ 0] Integer: 140023628
1069  * 21:29:15 [CollectCallDump] Named Arguments:
1070  * 21:29:15 [CollectCallDump] subSystems
1071  * 21:29:15 [CollectCallDump] List: 5 elements
1072  * 21:29:15 [CollectCallDump] [ 0] Integer: 140023637
1073  * 21:29:15 [CollectCallDump] [ 1] Integer: 140023638
1074  * 21:29:15 [CollectCallDump] [ 2] Integer: 140023635
1075  * 21:29:15 [CollectCallDump] [ 3] Integer: 140023634
1076  * 21:29:15 [CollectCallDump] [ 4] Integer: 140023636
1077  *
1078  * 16:31:52 W AssembleShip: byname call is Invalid Type
1079  *
1080  */
1081  // this is a list of subSystems for t3 assembly
1082  t3Ship = true;
1083  itemIDList.push_back(PyRep::IntegerValueU32(call.tuple->GetItem(0)));
1084  subSystemList = call.byname.find("subSystems")->second->AsList();
1085  } else if (call.tuple->GetItem(0)->IsList()) {
1086  Call_AssembleShip args;
1087  if (!args.Decode(&call.tuple)) {
1088  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
1089  return nullptr;
1090  }
1091  itemIDList = args.items;
1092  } else if (call.tuple->size() == 2) {
1093  if (call.tuple->GetItem(0)->IsInt()
1094  and call.tuple->GetItem(1)->IsString()) {
1095  // This block is for how DNA calls AssembleShip
1096  // @TODO Ignoring name
1097  // Can't get xmlpktgen to pickup the change so.. lol
1098  //if (!argsNamed.Decode(&call.tuple)) {
1099  // codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
1100  // return nullptr;
1101  //}
1102  itemIDList.push_back(PyRep::IntegerValueU32(call.tuple->GetItem(0)));
1103  } else {
1104  sLog.Error("AssembleShip", "tuple size == 2 and ([0] != int and [1] != string) or some shit like that.");
1105  }
1106  } else {
1107  sLog.Error( "Handle_AssembleShip", "end of conditional" );
1108  return nullptr;
1109  }
1110 
1111  ShipItemRef ship(nullptr);
1112  for (auto cur : itemIDList) {
1113  ship = sItemFactory.GetShip(cur);
1114 
1115  if (ship.get() == nullptr) {
1116  _log(ITEM__ERROR, "Failed to load ship %u to assemble.", cur);
1117  continue;
1118  }
1119 
1120  //check if the ship is a stack
1121  if (ship->quantity() > 1) {
1122  // Split the stack into a new inventory item with quantity of one, cast to ShipItemRef then assembled:
1123  // original item stack will be left with qty-1 at original location
1124  ship = ShipItemRef::StaticCast(ship->Split(1, true));
1125  if (ship.get() == nullptr) {
1126  _log(ITEM__ERROR, "Failed to split stack to assemble ship %u.", cur);
1127  continue;
1128  }
1129  }
1130 
1131  if ( t3Ship ) {
1132  if (subSystemList == nullptr) {
1133  // send error here, then exit
1134  sLog.Error("AssembleShip", "subSystemList == nullptr for %s", call.client->GetName());
1135  return nullptr;
1136  }
1137  // Move the five specified subsystems to the newly assembled Tech 3 ship
1138  InventoryItemRef subSystemItem(nullptr);
1139  PyList::const_iterator itr = subSystemList->begin(), end = subSystemList->end();
1140  while (itr != end) {
1141  subSystemItem = sItemFactory.GetItem(PyRep::IntegerValueU32(*itr));
1142  if (subSystemItem.get() != nullptr)
1143  subSystemItem->Move(ship->itemID(), (EVEItemFlags)(subSystemItem->GetAttribute(AttrSubSystemSlot).get_uint32()), true);
1144  ++itr;
1145  }
1146  }
1147 
1148  ship->ChangeSingleton(true, true);
1149  }
1150  return nullptr;
1151 }
1152 
1153 PyResult ShipBound::Handle_GetShipConfiguration(PyCallArgs &call)
1154 {
1155  PyDict* dict = new PyDict();
1156  dict->SetItemString("allowFleetSMBUsage", new PyBool(call.client->GetShipSE()->GetFleetSMBUsage()));
1157  return dict;
1158 }
1159 
1160 PyResult ShipBound::Handle_ConfigureShip(PyCallArgs &call)
1161 {
1162  PyDict* dict = call.tuple->GetItem(0)->AsDict();
1163  call.client->GetShipSE()->SetFleetSMBUsage(dict->GetItemString("allowFleetSMBUsage")->AsBool());
1164 
1165  return nullptr;
1166 }
1167 
1168 
1179 PyResult ShipBound::Handle_LaunchFromContainer(PyCallArgs &call) {
1180  /*
1181  * def LaunchSMAContents(self, invItems):
1182  * ids = []
1183  * for invItem in invItems:
1184  * structureID = invItem.locationID
1185  * ids += [invItem.itemID] * invItem.stacksize
1186  *
1187  * LaunchFromContainer(structureID, ids)
1188  */
1189 
1190  _log(SERVICE__CALL_DUMP, "ShipBound::Handle_LaunchFromContainer()");
1191  call.Dump(SERVICE__CALL_DUMP);
1192 
1193  return nullptr;
1194 }
1195 
1196 
1197 // ShipMaintenanceArray
1198 PyResult ShipBound::Handle_ScoopToSMA(PyCallArgs &call) {
1199  // no packet data
1200 
1201  _log(SERVICE__CALL_DUMP, "ShipBound::Handle_ScoopToSMA()");
1202  call.Dump(SERVICE__CALL_DUMP);
1203 
1204  return nullptr;
1205 }
1206 
1207 
1208 PyResult ShipBound::Handle_BoardStoredShip(PyCallArgs &call) {
1209  // no packet data
1210 
1211  //sm.StartService('sessionMgr').PerformSessionChange('board', ship.BoardStoredShip, structureID, shipID)
1212  _log(SERVICE__CALL_DUMP, "ShipBound::Handle_BoardStoredShip()");
1213  call.Dump(SERVICE__CALL_DUMP);
1214 
1215  return nullptr;
1216 }
1217 
1218 PyResult ShipBound::Handle_StoreVessel(PyCallArgs &call) {
1219  // no packet data
1220 
1221  //sm.StartService('sessionMgr').PerformSessionChange('storeVessel', ship.StoreVessel, destID)
1222  _log(SERVICE__CALL_DUMP, "ShipBound::Handle_StoreVessel()");
1223  call.Dump(SERVICE__CALL_DUMP);
1224 
1225  return nullptr;
1226 }
1227 
1228 PyResult ShipBound::Handle_SelfDestruct(PyCallArgs &call) {
1316  /*{'messageKey': 'SelfDestructAborted2', 'dataID': 17879480, 'suppressable': False, 'bodyID': 258024, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 2405}
1317  * {'messageKey': 'SelfDestructAbortedOther2', 'dataID': 17879483, 'suppressable': False, 'bodyID': 258025, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 2406}
1318  * {'messageKey': 'SelfDestructCancelledExternal', 'dataID': 17876999, 'suppressable': False, 'bodyID': 257085, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 3182}
1319  * {'messageKey': 'SelfDestructCancelledWarp', 'dataID': 17878652, 'suppressable': False, 'bodyID': 257707, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 2564}
1320  * {'messageKey': 'SelfDestructImmediate', 'dataID': 17881593, 'suppressable': False, 'bodyID': 258829, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 1533}
1321  * {'messageKey': 'SelfDestructImmediateOther', 'dataID': 17881596, 'suppressable': False, 'bodyID': 258830, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 1534}
1322  * {'messageKey': 'SelfDestructInitiated', 'dataID': 17881805, 'suppressable': False, 'bodyID': 258902, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 1535}
1323  * {'messageKey': 'SelfDestructInitiatedOther', 'dataID': 17881599, 'suppressable': False, 'bodyID': 258831, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 1536}
1324  * {'messageKey': 'SelfDestructTimer', 'dataID': 17878655, 'suppressable': False, 'bodyID': 257708, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 2563}
1325  * {'messageKey': 'SelfDestructTooEarly', 'dataID': 17881605, 'suppressable': False, 'bodyID': 258833, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 1537}
1326  */
1327  /* return error msg from this call, if applicable, else nodeid and timestamp */
1328  // returns nodeID and timestamp
1329  PyTuple* tuple = new PyTuple(2);
1330  tuple->SetItem(0, new PyString(GetBindStr())); // node info here
1331  tuple->SetItem(1, new PyLong(GetFileTimeNow()));
1332  return tuple;
1333 }
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.
PyCallable_Make_InnerDispatcher(ShipService) ShipService
Dispatcher *const m_dispatch
bool IsSessionChange()
Definition: Client.h:241
Dispatcher *const m_dispatch
Definition: ShipService.h:42
unsigned __int8 uint8
Definition: eve-compat.h:46
void SetPosition(const GPoint &pos)
Definition: SystemEntity.h:212
virtual InventoryItemRef Split(int32 qty=0, bool notify=true, bool silent=false)
virtual DroneSE * GetDroneSE()
Definition: SystemEntity.h:135
SystemEntity * GetSE(uint32 entityID) const
double radius() const
uint32 GetLocationID() const
Definition: Client.h:151
void AddEntity(SystemEntity *pSE, bool addSignal=true)
UserError & AddTimeShort(const char *name, time_t time)
Shorthand method for adding the given time as a time string in the message (without minutes) ...
void SendErrorMsg(const char *fmt,...)
Definition: Client.cpp:2719
uint32 GetSystemID() const
Definition: Client.h:152
#define _log(type, fmt,...)
Definition: logsys.h:124
ShipDB & db
Definition: ShipService.cpp:50
PyRep * GetItem(size_t index) const
Returns Python object.
Definition: PyRep.h:602
Python string.
Definition: PyRep.h:430
void ScoopDrone(SystemEntity *pSE)
Definition: Ship.cpp:2835
PyRep * GetItemString(const char *key) const
Obtains database entry based on given key string.
Definition: PyRep.cpp:702
virtual MoonSE * GetMoonSE()
Definition: SystemEntity.h:102
PyBool * AsBool()
Definition: PyRep.h:128
void Drop(SystemBubble *pBubble)
Definition: Structure.cpp:561
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 factionID
uint32 ownerID() const
Definition: InventoryItem.h:99
virtual void AddItem(InventoryItemRef iRef)
const GPoint & position() const
void SendJettisonPacket() const
void VerifyHoldType(EVEItemFlags flag, InventoryItemRef iRef, Client *pClient=nullptr)
Definition: Ship.cpp:1972
SystemBubble * SysBubble()
Definition: SystemEntity.h:195
bool empty() const
Definition: PyRep.h:592
virtual ShipSE * GetShipSE()
Definition: SystemEntity.h:137
EVEItemFlags
Definition: EVE_Flags.h:13
ShipItemRef GetShipItemRef()
Definition: Ship.h:362
void MoveItem(uint32 itemID, uint32 location, EVEItemFlags flag)
Definition: Client.cpp:1674
UserError & AddTypeName(const char *name, uint32 typeID)
Shorthand method for adding a type's name.
bool IsString() const
Definition: PyRep.h:105
SystemEntity * GetClosestGateSE(const GPoint &myPos)
int32 GetCharacterID() const
Definition: Client.h:113
int32 GetWarFactionID() const
Definition: Client.h:126
PyDict * GetShipState()
Definition: Ship.cpp:2199
ShipDB & m_db
int32 GetCorporationID() const
Definition: Client.h:123
storage_type::const_iterator const_iterator
Definition: PyRep.h:644
uint32 GetID() const
Definition: SystemManager.h:80
uint8 DroneCount()
Definition: Ship.h:370
static uint32 IntegerValueU32(PyRep *pRep)
Definition: PyRep.cpp:134
std::string m_strBoundObjectName
Definition: PyBoundObject.h:54
bool ValidateBoardShip(CharacterRef who)
Definition: Ship.cpp:2267
UserError & AddFormatValue(const char *name, PyRep *value)
Fluent version of the protected AddKeyword, allows for adding a keyword to the exception.
ShipItem * pShip
const char * name()
bool IsInSpace()
Definition: Client.h:228
void SendNotifyMsg(const char *fmt,...)
Definition: Client.cpp:2776
Dispatcher *const m_dispatch
CharacterRef GetChar() const
Definition: Client.h:164
Python tuple.
Definition: PyRep.h:567
uint32 GetClosestPlanetID(const GPoint &myPos)
Definition: Ship.h:301
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
int32 GetAllianceID() const
Definition: Client.h:125
bool HasSBU()
Definition: SystemEntity.h:360
const GPoint & GetPosition() const
Definition: SystemEntity.h:211
void Move(uint32 new_location=locTemp, EVEItemFlags flag=flagNone, bool notify=false)
bool GetFleetSMBUsage()
Definition: Ship.h:372
bool IsList() const
Definition: PyRep.h:109
void AddItem(PyRep *i)
Definition: PyRep.h:701
void SafeDelete(T *&p)
Deletes and nullifies a pointer.
Definition: SafeMem.h:83
uint16 groupID() const
signed __int32 int32
Definition: eve-compat.h:49
PyCallable_Make_Dispatcher(ShipBound) ShipBound(PyServiceMgr *mgr
void _SetCallDispatcher(CallDispatcher *d)
Definition: PyCallable.h:87
Client * GetOwner()
Definition: Drone.h:74
void SetFleetSMBUsage(bool set=false)
Definition: Ship.h:373
* args
Python boolean.
Definition: PyRep.h:323
uint32 JetcanTime()
Definition: Client.h:267
#define is_log_enabled(type)
Definition: logsys.h:78
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
Definition: gpoint.h:33
ShipItemRef GetShipFromInventory(uint32 shipID)
DestinyManager * DestinyMgr()
Definition: SystemEntity.h:198
bool IsAnchored()
Definition: Container.h:164
PyRep * GetLinkedWeapons()
Definition: Ship.cpp:1691
virtual ~ShipBound()
Definition: ShipService.cpp:80
InventoryItemRef GetSelf()
Definition: SystemEntity.h:202
PyCallable_DECL_CALL(Board)
#define codelog(type, fmt,...)
Definition: logsys.h:128
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
static SystemEntity * BuildEntity(SystemManager &pSysMgr, const DBSystemDynamicEntity &entity)
Definition: ShipDB.h:37
SystemManager * SystemMgr() const
Definition: Client.h:92
virtual bool IsContainerSE()
Definition: SystemEntity.h:157
uint16 GetID()
Definition: SystemBubble.h:91
uint32 get_uint32()
Definition: EvilNumber.cpp:173
storage_type items
Definition: PyRep.h:708
std::string GetBindStr() const
void MakeRandomPointOnSphereLayer(double radiusInner, double radiusOuter)
Definition: gpoint.h:59
virtual ~ShipService()
Python integer.
Definition: PyRep.h:231
bool AlterQuantity(int32 qty, bool notify=false)
PyDict * AsDict()
Definition: PyRep.h:142
virtual SystemEntity * GetSE()
Definition: SystemEntity.h:97
PyServiceMgr *const m_manager
Definition: PyService.h:91
#define PyStatic
Definition: PyRep.h:1209
X * get() const
Definition: RefPtr.h:213
ShipItemRef GetShip() const
Definition: Client.h:167
const char * GetName() const
Definition: Client.h:94
void ChangeOwner(uint32 new_owner, bool notify=false)
void Board(ShipSE *newShipSE)
Definition: Client.cpp:1086
const char * GetName() const
Definition: SystemEntity.h:210
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
virtual void Release()
Definition: ShipService.cpp:81
SystemEntity * GetClosestMoonSE(const GPoint &myPos)
void AddModuleToOnlineVec(uint32 modID)
Definition: Ship.cpp:562
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
unsigned __int32 uint32
Definition: eve-compat.h:50
virtual bool isGlobal()
Definition: SystemEntity.h:142
virtual StructureSE * GetPOSSE()
Definition: SystemEntity.h:116
bool IsJetcanAvalible()
Definition: Client.cpp:1381
virtual bool IsPOSSE()
Definition: SystemEntity.h:163
uint32 corporationID
virtual void Scoop()
Definition: Structure.cpp:449
bool HasTower()
Definition: Moon.h:46
std::map< uint32, SystemEntity * > GetOperationalStatics()
const_iterator begin() const
Definition: PyRep.h:766
Definition: Ship.h:46
double GetFileTimeNow()
Definition: utils_time.cpp:84
bool LaunchDrone(InventoryItemRef dRef)
Definition: Ship.cpp:2792
PyServiceMgr *const m_manager
Definition: PyBoundObject.h:53
uint32 GetPodID() const
Definition: Client.h:170
void BoardShip(ShipItemRef newShipRef)
Definition: Client.cpp:1064
ShipSE * GetShipSE()
Definition: Client.h:168
storage_type::const_iterator const_iterator
Definition: PyRep.h:750
void UndockFromStation()
Definition: Client.cpp:975
EvilNumber GetAttribute(const uint16 attrID) const
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
void Dump(LogType type) const
Definition: PyCallable.cpp:81
bool IsEnabled()
Definition: Drone.h:108
PyDict * GetChargeState()
Definition: Ship.cpp:2224
virtual PyBoundObject * CreateBoundObject(Client *pClient, const PyRep *bind_args)
const_iterator end() const
Definition: PyRep.h:767
size_t size() const
Definition: PyRep.h:663
virtual ContainerSE * GetContSE()
Definition: SystemEntity.h:107
virtual StargateSE * GetGateSE()
Definition: SystemEntity.h:103
#define sItemFactory
Definition: ItemFactory.h:165
int32 GetStationID() const
Definition: Client.h:114
bool IsInt() const
Definition: PyRep.h:100
float get_float()
Definition: EvilNumber.cpp:184
GaExpInl GaFloat distance(const GaVec3 &oth) const
Definition: GaTypes.h:158
#define svDataMgr
uint16 GetTypeID()
Definition: SystemEntity.h:203
Inventory * GetMyInventory()
Definition: InventoryItem.h:91
ShipDB ShipItem * ship
Definition: ShipService.cpp:50
virtual bool IsDroneSE()
Definition: SystemEntity.h:187
void SetItem(PyRep *key, PyRep *value)
SetItem adds or sets a database entry.
Definition: PyRep.cpp:713
bool ValidateAddItem(EVEItemFlags flag, InventoryItemRef iRef) const
Definition: Inventory.cpp:747
uint16 typeID() const
uint8 categoryID() const
void StartJetcanTimer()
Definition: Client.h:268
Python list.
Definition: PyRep.h:639
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
void Eject()
Definition: Client.cpp:1133
const char * GetName() const
Definition: PyBoundObject.h:44
int32 quantity() const
Definition: InventoryItem.h:97
Python long integer.
Definition: PyRep.h:261
PyTuple * tuple
Definition: PyCallable.h:50
#define sDataMgr