EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
MarketProxyService.cpp
Go to the documentation of this file.
1 /*
2  ------------------------------------------------------------------------------------
3  LICENSE:
4  ------------------------------------------------------------------------------------
5  This file is part of EVEmu: EVE Online Server Emulator
6  Copyright 2006 - 2021 The EVEmu Team
7  For the latest information visit https://evemu.dev
8  ------------------------------------------------------------------------------------
9  This program is free software; you can redistribute it and/or modify it under
10  the terms of the GNU Lesser General Public License as published by the Free Software
11  Foundation; either version 2 of the License, or (at your option) any later
12  version.
13 
14  This program is distributed in the hope that it will be useful, but WITHOUT
15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public License along with
19  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20  Place - Suite 330, Boston, MA 02111-1307, USA, or go to
21  http://www.gnu.org/copyleft/lesser.txt.
22  ------------------------------------------------------------------------------------
23  Author: Zhur
24  Updates: Allan
25 */
26 
27 // this class isnt a singleton, but is instantiated once on server start.
28 
29 #include "PyServiceCD.h"
30 #include "StaticDataMgr.h"
31 #include "account/AccountService.h"
32 #include "cache/ObjCacheService.h"
33 #include "market/MarketMgr.h"
35 #include "station/StationDataMgr.h"
36 #include "system/SystemManager.h"
37 #include "standing/StandingDB.h"
38 
39 
40 /*
41  * MARKET__ERROR
42  * MARKET__WARNING
43  * MARKET__MESSAGE
44  * MARKET__DEBUG
45  * MARKET__TRACE
46  * MARKET__DB_ERROR
47  * MARKET__DB_TRACE
48  */
49 
51 
53 : PyService(mgr, "marketProxy"),
54  m_dispatch(new Dispatcher(this))
55 {
56  _SetCallDispatcher(m_dispatch);
57 
59  PyCallable_REG_CALL(MarketProxyService, GetStationAsks);
62  PyCallable_REG_CALL(MarketProxyService, GetMarketGroups);
64  PyCallable_REG_CALL(MarketProxyService, GetOldPriceHistory);
65  PyCallable_REG_CALL(MarketProxyService, GetNewPriceHistory);
66  PyCallable_REG_CALL(MarketProxyService, PlaceCharOrder);
68  PyCallable_REG_CALL(MarketProxyService, ModifyCharOrder);
69  PyCallable_REG_CALL(MarketProxyService, CancelCharOrder);
70  PyCallable_REG_CALL(MarketProxyService, CharGetNewTransactions);
71  PyCallable_REG_CALL(MarketProxyService, CorpGetNewTransactions);
72  PyCallable_REG_CALL(MarketProxyService, GetCorporationOrders);
73 }
74 
76  delete m_dispatch;
77 }
78 
79 PyResult MarketProxyService::Handle_GetMarketGroups(PyCallArgs &call) {
80  return sMktMgr.GetMarketGroups();
81 }
82 
83 PyResult MarketProxyService::Handle_StartupCheck(PyCallArgs &call) {
84  //if (sMktMgr.NeedsUpdate())
85  // sMktMgr.UpdatePriceHistory();
86  return nullptr;
87 }
88 
89 PyResult MarketProxyService::Handle_GetCharOrders(PyCallArgs &call) {
91 }
92 
93 PyResult MarketProxyService::Handle_GetCorporationOrders(PyCallArgs &call) {
95 }
96 
98 // station, system, region based on selection in market window
99 PyResult MarketProxyService::Handle_GetStationAsks(PyCallArgs &call) {
100  return m_db.GetStationAsks(call.client->GetStationID());
101 }
102 
103 PyResult MarketProxyService::Handle_GetSystemAsks(PyCallArgs &call) {
104  return m_db.GetSystemAsks(call.client->GetSystemID());
105 }
106 
107 PyResult MarketProxyService::Handle_GetRegionBest(PyCallArgs &call) {
108  return m_db.GetRegionBest(call.client->GetRegionID());
109 }
110 
111 // this is called 3x on every market transaction
112 PyResult MarketProxyService::Handle_GetOldPriceHistory(PyCallArgs &call) {
113  Call_SingleIntegerArg args;
114  if (!args.Decode(&call.tuple)) {
115  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
116  return nullptr;
117  }
118 
119  return sMktMgr.GetOldPriceHistory(call.client->GetRegionID(), args.arg);
120 }
121 
122 PyResult MarketProxyService::Handle_GetNewPriceHistory(PyCallArgs &call) {
123  Call_SingleIntegerArg args;
124  if (!args.Decode(&call.tuple)) {
125  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
126  return nullptr;
127  }
128 
129  return sMktMgr.GetNewPriceHistory(call.client->GetRegionID(), args.arg);
130 }
131 
132 PyResult MarketProxyService::Handle_CharGetNewTransactions(PyCallArgs &call)
133 {
134  Call_GetNewCharTransactions args;
135  if (!args.Decode(&call.tuple)) {
136  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
137  return nullptr;
138  }
139 
141  data.clientID = args.clientID;
142  data.isBuy = args.sellBuy;
143  data.price = args.minPrice;
144  data.quantity = args.quantity;
145  data.typeID = args.typeID;
146  data.time = args.fromDate;
148  return m_db.GetTransactions(call.client->GetCharacterID(), data);
149 }
150 
151 PyResult MarketProxyService::Handle_CorpGetNewTransactions(PyCallArgs &call)
152 {
153  Call_GetNewCorpTransactions args;
154  if (!args.Decode(&call.tuple)) {
155  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
156  return nullptr;
157  }
158 
160  data.clientID = args.clientID;
161  data.isBuy = args.sellBuy;
162  data.price = args.minPrice;
163  data.quantity = args.quantity;
164  data.typeID = args.typeID;
165  data.time = args.fromDate;
166  data.accountKey = args.accountKey;
167  data.memberID = args.memberID;
168  return m_db.GetTransactions(call.client->GetCorporationID(), data);
169 }
170 
171 PyResult MarketProxyService::Handle_GetOrders(PyCallArgs &call) {
172  Call_SingleIntegerArg args; //typeID
173  if (!args.Decode(&call.tuple)) {
174  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
175  return nullptr;
176  }
177 
178  PyRep* result(nullptr);
179  std::string method_name ("GetOrders_");
180  method_name += std::to_string(call.client->GetRegionID());
181  method_name += "_";
182  method_name += std::to_string(args.arg);
183  ObjectCachedMethodID method_id(GetName(), method_name.c_str());
184  //check to see if this method is in the cache already.
185  if (!m_manager->cache_service->IsCacheLoaded(method_id))
186  {
187  //this method is not in cache yet, load up the contents and cache it.
188  result = m_db.GetOrders(call.client->GetRegionID(), args.arg);
189  if (result == nullptr) {
190  _log(MARKET__DB_ERROR, "Failed to load cache, generating empty contents.");
191  result = PyStatic.NewNone();
192  }
193  m_manager->cache_service->GiveCache(method_id, &result);
194  }
195 
196  //now we know its in the cache one way or the other, so build a
197  //cached object cached method call result.
199 
200  /*{'FullPath': u'UI/Messages', 'messageID': 258616, 'label': u'MktMarketOpeningTitle'}(u'Market not open yet', None, None)
201  * {'FullPath': u'UI/Messages', 'messageID': 258617, 'label': u'MktMarketOpeningBody'}
202  * (u'{region} market region is currently opening up for business and is not yet ready.
203  * Please try again in a few minutes, or refer to another market region for your trading needs.',
204  * None, {u'{region}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'region'}})
205  */
206  return result;
207 }
208 
209 PyResult MarketProxyService::Handle_PlaceCharOrder(PyCallArgs &call) {
210  //self.GetMarketProxy().PlaceCharOrder(int(stationID), int(typeID), round(float(price), 2), int(quantity), int(bid), int(orderRange),
211  // itemID = None, int(minVolume = 1), int(duration = 14), useCorp = False, located = None)
212  // located = [officeFolderID, officeID] or None
213 
214  /*
215 21:47:34 [MktDump] Mkt::PlaceCharOrder()
216 21:47:34 [MktDump] Call_PlaceCharOrder
217 21:47:34 [MktDump] stationID=60014137
218 21:47:34 [MktDump] typeID=20327
219 21:47:34 [MktDump] price=100.0000000000000
220 21:47:34 [MktDump] quantity=1
221 21:47:34 [MktDump] bid=1
222 21:47:34 [MktDump] orderRange=4294967295
223 21:47:34 [MktDump] itemID=0
224 21:47:34 [MktDump] minVolume=1
225 21:47:34 [MktDump] duration=0
226 21:47:34 [MktDump] useCorp=0
227 21:47:34 [MktDump] located:
228 21:47:34 [MktDump] None
229 */
230  Call_PlaceCharOrder args;
231  if (!args.Decode(&call.tuple)) {
232  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
233  return nullptr;
234  }
235 
236  // not sent from call, but used in transactions
237  uint16 accountKey(Account::KeyType::Cash);
238 
239  _log(MARKET__DUMP, "Mkt::PlaceCharOrder()");
240  args.Dump(MARKET__DUMP, " ");
241 
242  //TODO: verify the validity of args.stationID (range vs. skill)
243 
244  if (args.useCorp) {
245  _log(MARKET__MESSAGE, "Denying Corp Market use for %s", call.client->GetName());
246  call.client->SendErrorMsg("Corporation Market transactions are not available at this time.");
247  return nullptr;
248  }
249 
259  if (args.bid and (args.itemID == 0)) { //buy
260  // check for corp usage and get standings with station owners
261  float fStanding(0), cStanding(0);
262  if (args.useCorp) {
263  // it is. perform checks and set needed variables for corp use
264  if (!IsPlayerCorp(call.client->GetCorporationID())) {
265  // cant buy items for npc corp...
266  call.client->SendErrorMsg("You cannot buy items for an NPC corp.");
267  return nullptr;
268  }
269  // this is a corp transaction. verify char can buy shit for corp...
270  // some corp error msgs in inventory.h, corp.h and market.h
271  // will need corp methods to determine member access rights for item location and roles....
272  // these may be written already. will have to check
273 
274  accountKey = call.client->GetCorpAccountKey();
275 
276  } else {
277 
278  }
279 
280  //is this standing order or immediate?
281  if (args.duration == 0) {
282  // immediate. look for open sell order that matches all reqs (price, qty, distance, etc)
283  // check distance shit, set order range and make station list. this shit will be nuts.
284  uint32 orderID = m_db.FindSellOrder(args);
285  if (orderID) {
286  // found one.
287  _log(MARKET__TRACE, "PlaceCharOrder - Found sell order #%u in %s for %s. (type %i, price %.2f, qty %i, range %i)", \
288  orderID, stDataMgr.GetStationName(args.stationID).c_str(), call.client->GetName(), args.typeID, args.price, args.quantity, args.orderRange);
289 
290  sMktMgr.ExecuteSellOrder(call.client, orderID, args);
291  return nullptr;
292  }
293 
294  _log(MARKET__TRACE, "PlaceCharOrder - Failed to satisfy buy order for %i of type %i at %.2f ISK.", \
295  args.quantity, args.typeID, args.price);
296  call.client->SendErrorMsg("No sell order found."); // find/implement type name here
297  return nullptr;
298  }
299 
300  // determine escrow amount
301  float money(args.price * args.quantity);
302 
303  // set save data
305  data.accountKey = accountKey;
306  data.minVolume = 1; // verify this
307  data.issued = GetFileTimeNow();
308  // fill items from sent data
309  data.bid = args.bid;
310  data.isCorp = args.useCorp;
311  data.typeID = args.typeID;
312  data.orderRange = args.orderRange;
313  data.ownerID = args.useCorp?call.client->GetCorporationID():call.client->GetCharacterID();
314  data.solarSystemID = sDataMgr.GetStationSystem(args.stationID);
315  data.regionID = sDataMgr.GetStationRegion(args.stationID);
316  data.stationID = args.stationID;
317  data.price = args.price;
318  data.volEntered = args.quantity;
319  data.volRemaining = args.quantity;
320  data.duration = args.duration;
321  data.memberID = args.useCorp?call.client->GetCharacterID():0;
322  data.escrow = money;
323 
324  // these need a bit more data
325  data.contraband = false; // does this need to check region/system? yes
326  data.jumps = 1; // not sure if this is used....
327 
328  // create buy order
329  uint32 orderID(m_db.StoreOrder(data));
330  if (orderID == 0) {
331  _log(MARKET__ERROR, "PlaceCharOrder - Failed to record buy order in the DB.");
332  call.client->SendErrorMsg("Failed to record the order.");
333  return nullptr;
334  }
335 
336  std::string reason = "DESC: Setting up buy order in ";
337  reason += stDataMgr.GetStationName(args.stationID).c_str();
338  // get data for computing broker fees
340  //call.client->GetChar()->GetStandingModified();
341  uint32 stationOwnerID = stDataMgr.GetOwnerID (call.client->GetStationID ());
342  float ownerStanding = StandingDB::GetStanding (stationOwnerID, call.client->GetCharacterID ());
343  float factionStanding = 0.0f;
344 
345  if (IsNPCCorp (stationOwnerID))
346  factionStanding = StandingDB::GetStanding(sDataMgr.GetCorpFaction (stationOwnerID), call.client->GetCharacterID());
347 
348  float fee = EvEMath::Market::BrokerFee(lvl, factionStanding, ownerStanding, money);
349  _log(MARKET__DEBUG, "PlaceCharOrder(buy) - %s: Escrow: %.2f, Fee: %.2f", args.useCorp?"Corp":"Player", money, fee);
350  // take monies and record actions
351  if (args.useCorp) {
352  AccountService::TranserFunds(call.client->GetCorporationID(), stDataMgr.GetOwnerID(args.stationID), fee, \
353  reason.c_str(), Journal::EntryType::Brokerfee, orderID, accountKey, Account::KeyType::Cash, call.client);
354  AccountService::TranserFunds(call.client->GetCorporationID(), stDataMgr.GetOwnerID(args.stationID), money, \
355  reason.c_str(), Journal::EntryType::MarketEscrow, orderID, accountKey, Account::KeyType::Escrow, call.client);
356  } else {
357  AccountService::TranserFunds(call.client->GetCharacterID(), stDataMgr.GetOwnerID(args.stationID), fee, \
358  reason.c_str(), Journal::EntryType::Brokerfee, orderID, accountKey);
359  AccountService::TranserFunds(call.client->GetCharacterID(), stDataMgr.GetOwnerID(args.stationID), money, \
360  reason.c_str(), Journal::EntryType::MarketEscrow, orderID, accountKey, Account::KeyType::Escrow);
361  }
362 
363  //send notification of new order...
364  sMktMgr.InvalidateOrdersCache(call.client->GetRegionID(), args.typeID);
365  sMktMgr.SendOnOwnOrderChanged(call.client, orderID, Market::Action::Add, args.useCorp);
366  } else {
367  //sell order
368  if (!args.located->IsNone()) {
369  // corp item in corp hangar
370  // located = [officeFolderID, officeID] or None
371  PyTuple* located = args.located->AsTuple();
372 
373 
374  }
375 
376  //verify that they actually have the item in the quantity specified...
377  InventoryItemRef iRef = sItemFactory.GetItem( args.itemID );
378  if (iRef.get() == nullptr) {
379  _log(ITEM__ERROR, "PlaceCharOrder - Failed to find item %i for sell order.", args.itemID);
380  call.client->SendErrorMsg("Unable to find item to sell.");
381  return nullptr;
382  }
383 
384  if (iRef->typeID() != args.typeID) {
385  _log(MARKET__MESSAGE, "PlaceCharOrder - Denying Sell of typeID %u using typeID %i.", call.client->GetName(), iRef->itemID(), iRef->typeID(), args.typeID);
386  call.client->SendErrorMsg("Invalid sell order item type.");
387  return nullptr;
388  }
389 
390  if (iRef->quantity() < args.quantity) {
391  // trying to sell more than they have
392  _log(MARKET__MESSAGE, "PlaceCharOrder - Denying inflated qty for %s", call.client->GetName());
393  call.client->SendErrorMsg("You cannot sell %i %s when you only have %i. If applicable, merge stacks and try again.", \
394  args.quantity, iRef->name(), iRef->quantity());
395  return nullptr;
396  }
397  //verify right to sell this thing..
398  if (args.useCorp) {
399  if (!IsPlayerCorp(call.client->GetCorporationID())) {
400  // cant sell npc corp items...
401  call.client->SendErrorMsg("You cannot sell items for an NPC corp.");
402  return nullptr;
403  }
404  // this is a corp transaction. verify char can sell corp shit...
405  // some corp error msgs in inventory.h, corp.h and market.h
406  // will need corp methods to determine member access rights for item location and roles....
407  // these may be written already. will have to check
408  accountKey = call.client->GetCorpAccountKey();
409  } else {
410  if ( iRef->ownerID() != call.client->GetCharacterID()) {
411  _log(MARKET__WARNING, "%s(%u) Tried to sell %i %s owned by %u in %s.", \
412  call.client->GetName(), call.client->GetCharID(), iRef->quantity(), \
413  iRef->name(), iRef->ownerID(), stDataMgr.GetStationName(args.stationID).c_str());
414  call.client->SendErrorMsg("You cannot sell items you do not own.");
415  return nullptr;
416  }
417  }
418 
419  //verify valid location
420  if (( iRef->locationID() != args.stationID) //item in station hanger
421  and !(call.client->GetShip()->GetMyInventory()->ContainsItem( iRef->itemID() ) //item is in our ship
422  and call.client->GetStationID() == args.stationID )) //and our ship is in the station
423  {
424  std::string itemLoc;
425  if (sDataMgr.IsStation(iRef->locationID())) {
426  itemLoc = stDataMgr.GetStationName(iRef->locationID());
427  } else if (sDataMgr.IsSolarSystem(iRef->locationID())) {
428  itemLoc = sDataMgr.GetSystemName(iRef->locationID());
429  } else {
430  itemLoc = "an Invalid Location";
431  }
432  _log(MARKET__ERROR, "%s Trying to sell %s(%u) in %s through %s while in %s", \
433  call.client->GetName(), iRef->name(), iRef->itemID(), itemLoc.c_str(), \
434  stDataMgr.GetStationName(args.stationID).c_str(), stDataMgr.GetStationName(call.client->GetStationID()).c_str());
435  call.client->SendErrorMsg("You cannot sell %s from %s while you are in %s. Locations mismatch", \
436  iRef->name(), stDataMgr.GetStationName(args.stationID).c_str(), stDataMgr.GetStationName(call.client->GetStationID()).c_str());
437  return nullptr;
438  }
439 
440  //TODO: verify orderRange against their skills. client may do this...verify
441 
442  // they are allowed to sell this thing...
443 
444  //is this standing order or immediate?
445  if (args.duration == 0) {
446  // immediate - loop to search and fill buy orders at or above asking price until qty depleted or no orders found
447  bool search(true);
448  uint32 orderID(0), origQty(args.quantity);
449  while (args.quantity and search) {
450  orderID = m_db.FindBuyOrder(args);
451  if (orderID) {
452  _log(MARKET__TRACE, "PlaceCharOrder - Found buy order #%u in %s for %s.", \
453  orderID, stDataMgr.GetStationName(args.stationID).c_str(), call.client->GetName());
454  search = sMktMgr.ExecuteBuyOrder(call.client, orderID, iRef, args);
455  }
456  }
457 
458  // test for qty change for correct msg.
459  if (args.quantity == 0) {
460  // completely fulfilled. msgs sent from ExecuteBuyOrder()
461  return nullptr;
462  } else if (args.quantity == origQty) {
463  //unable to find any order for this item using client parameters
464  // find/implement type name here?
465  _log(MARKET__TRACE, "PlaceCharOrder - Failed to find any buy order for type %i at %.2f ISK.", \
466  args.typeID, args.price);
467  call.client->SendErrorMsg("No buy order found.");
468  return nullptr;
469  } else {
470  // partially filled
471  _log(MARKET__TRACE, "PlaceCharOrder - Failed to find buy orders for remaining %i of type %i at %.2f ISK.", \
472  args.quantity, args.typeID, args.price);
473  call.client->SendErrorMsg("There were only buyers for %u of the %i items you wanted to sell.", args.quantity, origQty);
474  return nullptr;
475  }
476 
477  _log(MARKET__ERROR, "PlaceCharOrder - immediate order qty hit end of conditional.");\
478  return nullptr;
479  }
480 
481  // they will be placing a sell order.
482 
483  // set save data
485  data.accountKey = accountKey;
486  data.minVolume = 1; // verify this
487  data.issued = GetFileTimeNow();
488  // fill items from sent data
489  data.bid = args.bid;
490  data.isCorp = args.useCorp;
491  data.typeID = args.typeID;
492  data.orderRange = args.orderRange;
493  data.ownerID = iRef->ownerID();
494  data.solarSystemID = sDataMgr.GetStationSystem(args.stationID);
495  data.regionID = sDataMgr.GetStationRegion(args.stationID);
496  data.stationID = args.stationID;
497  data.price = args.price;
498  data.volEntered = args.quantity;
499  data.volRemaining = args.quantity;
500  data.duration = args.duration;
501  data.memberID = args.useCorp?call.client->GetCharacterID():0;
502 
503  // check seller's permissions on the wallet if it's for corp
504  if (data.isCorp) {
505  int64 corpRole = call.client->GetCorpRole ();
506 
507  // make sure the user has permissions to take money from the corporation account
508  if (
509  (accountKey == 1000 && (corpRole & Corp::Role::AccountCanTake1) == 0) ||
510  (accountKey == 1001 && (corpRole & Corp::Role::AccountCanTake2) == 0) ||
511  (accountKey == 1002 && (corpRole & Corp::Role::AccountCanTake3) == 0) ||
512  (accountKey == 1003 && (corpRole & Corp::Role::AccountCanTake4) == 0) ||
513  (accountKey == 1004 && (corpRole & Corp::Role::AccountCanTake5) == 0) ||
514  (accountKey == 1005 && (corpRole & Corp::Role::AccountCanTake6) == 0) ||
515  (accountKey == 1006 && (corpRole & Corp::Role::AccountCanTake7) == 0)
516  )
517  throw UserError("CrpAccessDenied").AddFormatValue ("reason", new PyString ("You do not have access to that wallet"));
518  }
519 
520  // these need a bit more data
521  data.contraband = iRef->contraband(); // does this need to check region/system?
522  data.jumps = 1; // not sure if this is used....
523 
524  // calculate total for broker fees
525  float total = args.price * args.quantity;
526  std::string reason = "DESC: Setting up sell order in ";
527  reason += stDataMgr.GetStationName(args.stationID).c_str();
528  // get data for computing broker fees
530  //call.client->GetChar()->GetStandingModified();
531  uint32 stationOwnerID = stDataMgr.GetOwnerID (call.client->GetStationID ());
532  float ownerStanding = StandingDB::GetStanding (stationOwnerID, call.client->GetCharacterID ());
533  float factionStanding = 0.0f;
534 
535  if (IsNPCCorp (stationOwnerID))
536  factionStanding = StandingDB::GetStanding(sDataMgr.GetCorpFaction (stationOwnerID), call.client->GetCharacterID());
537 
538  float fee = EvEMath::Market::BrokerFee(lvl, factionStanding, ownerStanding, total);
539  _log(MARKET__DEBUG, "PlaceCharOrder(sell) - %s: Total: %.2f, Fee: %.2f", args.useCorp?"Corp":"Player", total, fee);
540 
541  // take monies and record actions (taxes are paid when item sells)
542  if (args.useCorp) {
543  AccountService::TranserFunds(call.client->GetCorporationID(), stDataMgr.GetOwnerID(args.stationID), fee, \
544  reason.c_str(), Journal::EntryType::Brokerfee, 0, accountKey, Account::KeyType::Cash, call.client);
545  } else {
546  AccountService::TranserFunds(call.client->GetCharacterID(), stDataMgr.GetOwnerID(args.stationID), fee, \
547  reason.c_str(), Journal::EntryType::Brokerfee, 0, accountKey);
548  }
549 
550  //store the order in the DB.
551  uint32 orderID = m_db.StoreOrder(data);
552  if (orderID == 0) {
553  _log(MARKET__ERROR, "PlaceCharOrder - Failed to record sell order in the DB.");
554  call.client->SendErrorMsg("Failed to record the order in the DB!");
555  return nullptr;
556  }
557 
558  if (iRef->quantity() == args.quantity) {
559  //take item from seller
561  iRef->Delete();
562  } else {
563  //update the item.
564  if (!iRef->AlterQuantity(-args.quantity, true)) {
565  _log(MARKET__ERROR, "PlaceCharOrder - Failed to consume %i units from %s", args.quantity, iRef->name());
566  return nullptr;
567  }
568  }
569 
570  //notify client about new order.
571  sMktMgr.InvalidateOrdersCache(call.client->GetRegionID(), args.typeID);
572  sMktMgr.SendOnOwnOrderChanged(call.client, orderID, Market::Action::Add, args.useCorp);
573  }
574 
575  //returns nothing.
576  return nullptr;
577 }
578 
579 PyResult MarketProxyService::Handle_ModifyCharOrder(PyCallArgs &call) {
580  Call_ModifyCharOrder args;
581  if (!args.Decode(&call.tuple)) {
582  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
583  return nullptr;
584  }
585 
586  // client coded to throw error if price > 9223372036854.0
587  // we need to pull data from db for typeID and isCorp...
589  if (!m_db.GetOrderInfo(args.orderID, oInfo)) {
590  _log(MARKET__ERROR, "ModifyCharOrder - Failed to get info about order #%u.", args.orderID);
591  return nullptr;
592  }
593 
594  // there is no refund in broker fees.
595 
596  // adjust balance for price change
597  float money = (args.price - args.newPrice) * args.volRemaining;
598  std::string reason = "DESC: Altering Market Order #";
599  reason += std::to_string(args.orderID);
600  AccountService::TranserFunds(call.client->GetCharID(), stDataMgr.GetOwnerID(args.stationID), money,
601  reason.c_str(), Journal::EntryType::MarketEscrow, args.orderID,
603 
604  if (!m_db.AlterOrderPrice(args.orderID, args.newPrice)) {
605  _log(MARKET__ERROR, "ModifyCharOrder - Failed to modify price for order #%u.", call.client->GetName(), args.orderID);
606  return nullptr;
607  }
608 
609  sMktMgr.InvalidateOrdersCache(call.client->GetRegionID(), oInfo.typeID);
610  sMktMgr.SendOnOwnOrderChanged(call.client, args.orderID, Market::Action::Modify, oInfo.isCorp);
611 
612  return nullptr;
613 }
614 
615 PyResult MarketProxyService::Handle_CancelCharOrder(PyCallArgs &call) {
616  Call_CancelCharOrder args;
617  if (!args.Decode(&call.tuple)) {
618  _log(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
619  return nullptr;
620  }
621 
623  if (!m_db.GetOrderInfo(args.orderID, oInfo)) {
624  _log(MARKET__ERROR, "CancelCharOrder - Failed to get info about order #%u.", args.orderID);
625  return nullptr;
626  }
627 
628  if (oInfo.isBuy) {
629  // buy order only refunds escrow
630  float money = oInfo.price * oInfo.quantity;
631  // send wallet blink event and record the transaction in their journal.
632  std::string reason = "DESC: Canceling Market Order #";
633  reason += std::to_string(args.orderID);
634  AccountService::TranserFunds(stDataMgr.GetOwnerID(oInfo.stationID), call.client->GetCharID(), money,
635  reason.c_str(), Journal::EntryType::MarketEscrow, args.orderID,
637  } else {
638  ItemData idata(oInfo.typeID, ownerStation, locTemp, flagHangar, oInfo.quantity);
639  InventoryItemRef iRef = sItemFactory.SpawnItem(idata);
640  if (iRef.get() != nullptr)
641  iRef->Donate(call.client->GetCharacterID(), oInfo.stationID, flagHangar, true);
642  }
643 
644  PyRep* order(m_db.GetOrderRow(args.orderID));
645  if (!m_db.DeleteOrder(args.orderID)) {
646  _log(MARKET__ERROR, "CancelCharOrder - Failed to delete order #%u.", args.orderID);
647  return nullptr;
648  }
649 
650  sMktMgr.InvalidateOrdersCache(call.client->GetRegionID(), oInfo.typeID);
651  sMktMgr.SendOnOwnOrderChanged(call.client, args.orderID, Market::Action::Expiry, oInfo.isCorp, order);
652 
653  return nullptr;
654 }
655 
static float GetStanding(uint32 fromID, uint32 toID)
Definition: StandingDB.cpp:142
Base Python wire object.
Definition: PyRep.h:66
PyRep * GetRegionBest(uint32 regionID)
Definition: MarketDB.cpp:87
PyTuple * AsTuple()
Definition: PyRep.h:138
Dispatcher *const m_dispatch
unsigned __int8 uint8
Definition: eve-compat.h:46
#define IsNPCCorp(itemID)
Definition: EVE_Defines.h:238
void Donate(uint32 new_owner=ownerSystem, uint32 new_location=locTemp, EVEItemFlags new_flag=flagNone, bool notify=true)
bool GetOrderInfo(uint32 orderID, Market::OrderInfo &oInfo)
Definition: MarketDB.cpp:253
void SendErrorMsg(const char *fmt,...)
Definition: Client.cpp:2719
uint32 GetSystemID() const
Definition: Client.h:152
#define _log(type, fmt,...)
Definition: logsys.h:124
#define stDataMgr
Python string.
Definition: PyRep.h:430
uint32 quantity
Definition: EVE_Market.h:81
int64 GetCorpRole() const
Definition: Client.h:129
PyRep * GetOrdersForOwner(uint32 ownerID)
Definition: MarketDB.cpp:151
PyRep * GetStationAsks(uint32 stationID)
Definition: MarketDB.cpp:43
PyRep * GetTransactions(uint32 ownerID, Market::TxData &data)
Definition: MarketDB.cpp:349
uint32 solarSystemID
Definition: EVE_Market.h:106
uint32 ownerID() const
Definition: InventoryItem.h:99
PyRep * GetOrders(uint32 regionID, uint16 typeID)
Definition: MarketDB.cpp:109
uint32 GetRegionID() const
Definition: Client.h:154
Dispatcher *const m_dispatch
int32 GetCharacterID() const
Definition: Client.h:113
int32 GetCorporationID() const
Definition: Client.h:123
UserError & AddFormatValue(const char *name, PyRep *value)
Fluent version of the protected AddKeyword, allows for adding a keyword to the exception.
const char * name()
bool AlterOrderPrice(uint32 orderID, double new_price)
Definition: MarketDB.cpp:307
CharacterRef GetChar() const
Definition: Client.h:164
Python tuple.
Definition: PyRep.h:567
const char * GetName() const
Definition: PyService.h:54
* args
bool ContainsItem(uint32 itemID) const
Definition: Inventory.h:63
PyRep * GetOrderRow(uint32 orderID)
Definition: MarketDB.cpp:172
uint16 typeID
Definition: EVE_Market.h:77
SystemManager * SystemMgr() const
Definition: Client.h:92
PyCallable_Make_InnerDispatcher(MarketProxyService) MarketProxyService
static void TranserFunds(uint32 fromID, uint32 toID, double amount, std::string reason="", uint8 entryTypeID=Journal::EntryType::Undefined, uint32 referenceID=0, uint16 fromKey=Account::KeyType::Cash, uint16 toKey=Account::KeyType::Cash, Client *pClient=nullptr)
uint32 locationID() const
bool AlterQuantity(int32 qty, bool notify=false)
void RemoveItemFromInventory(InventoryItemRef item)
uint32 accountKey
Definition: EVE_Market.h:82
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
#define IsPlayerCorp(itemID)
Definition: EVE_Defines.h:241
#define sMktMgr
Definition: MarketMgr.h:86
uint32 GetCharID()
Definition: Client.h:166
uint32 memberID
Definition: EVE_Market.h:83
Client *const client
Definition: PyCallable.h:49
uint32 FindBuyOrder(Call_PlaceCharOrder &call)
Definition: MarketDB.cpp:196
Python object "ccp_exceptions.UserError".
Definition: PyExceptions.h:121
#define PyCallable_REG_CALL(c, m)
Definition: PyServiceCD.h:78
unsigned __int32 uint32
Definition: eve-compat.h:50
uint32 clientID
Definition: EVE_Market.h:78
void GiveCache(const PyRep *objectID, PyRep **contents)
float BrokerFee(uint8 brSkillLvl, float fStanding, float cStanding, float total)
Definition: EvEMath.cpp:202
double GetFileTimeNow()
Definition: utils_time.cpp:84
signed __int64 int64
Definition: eve-compat.h:51
int32 GetCorpAccountKey() const
Definition: Client.h:127
ObjCacheService * cache_service
Definition: PyServiceMgr.h:78
int8 GetSkillLevel(uint16 skillTypeID, bool zeroForNotInjected=true) const
Definition: Character.cpp:575
bool contraband() const
Definition: InventoryItem.h:95
virtual void Delete()
#define sItemFactory
Definition: ItemFactory.h:165
int32 GetStationID() const
Definition: Client.h:114
bool DeleteOrder(uint32 orderID)
Definition: MarketDB.cpp:316
uint32 StoreOrder(Market::SaveData &data)
Definition: MarketDB.cpp:325
Inventory * GetMyInventory()
Definition: InventoryItem.h:91
unsigned __int16 uint16
Definition: eve-compat.h:48
uint32 volRemaining
Definition: EVE_Market.h:109
uint16 typeID() const
PyObject * MakeObjectCachedMethodCallResult(const PyRep *objectID, const char *versionCheck="run")
PyRep * GetSystemAsks(uint32 solarSystemID)
Definition: MarketDB.cpp:65
uint32 itemID() const
Definition: InventoryItem.h:98
bool IsCacheLoaded(const PyRep *objectID) const
uint32 FindSellOrder(Call_PlaceCharOrder &call)
Definition: MarketDB.cpp:224
int32 quantity() const
Definition: InventoryItem.h:97
PyTuple * tuple
Definition: PyCallable.h:50
#define sDataMgr