EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ReprocessingService.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  Rewrite: Allan
25 */
26 
28 /*
29  * # Manufacturing Logging:
30  * MANUF__ERROR
31  * MANUF__WARNING
32  * MANUF__MESSAGE
33  * MANUF__INFO
34  * MANUF__DEBUG
35  * MANUF__TRACE
36  * MANUF__DUMP
37  */
38 
39 
40 #include "eve-server.h"
41 
42 #include "PyServiceCD.h"
43 #include "packets/Manufacturing.h"
46 #include "Station.h"
47 #include "system/SystemManager.h"
48 
51 
53 : PyService(mgr, "reprocessingSvc"),
54  m_dispatch(new Dispatcher(this))
55 {
56  _SetCallDispatcher(m_dispatch);
57 }
58 
60  delete m_dispatch;
61 }
62 
64  if (!bind_args->IsInt()) {
65  _log(SERVICE__ERROR, "%s: Non-integer bind argument '%s'", pClient->GetName(), bind_args->TypeString());
66  return nullptr;
67  }
68 
69  uint32 stationID = bind_args->AsInt()->value();
70  if (!sDataMgr.IsStation(stationID)) {
71  _log(SERVICE__ERROR, "%s: Expected stationID, but got %u.", pClient->GetName(), stationID);
72  return nullptr;
73  }
74 
75  return new ReprocessingServiceBound(m_manager, m_db, stationID);
76 }
77 
78 
80 : PyBoundObject(mgr),
81 m_dispatch(new Dispatcher(this)),
82 m_db(db),
83 m_stationCorpID(0),
84 m_staEfficiency(0.0f),
85 m_tax(0.0f)
86 {
88 
89  m_strBoundObjectName = "ReprocessingServiceBound";
90 
91  PyCallable_REG_CALL(ReprocessingServiceBound, GetOptionsForItemTypes);
92  PyCallable_REG_CALL(ReprocessingServiceBound, GetReprocessingInfo);
96 
97  m_stationRef = sItemFactory.GetStationItem(stationID);
98  if (m_stationRef.get() != nullptr)
100 }
101 
103  delete m_dispatch;
104 }
105 
107  //I hate this statement
108  delete this;
109 }
110 
111 PyResult ReprocessingServiceBound::Handle_GetOptionsForItemTypes(PyCallArgs &call) {
112  _log(MANUF__INFO, "%s: Calling GetOptionsForItemTypes().", call.client->GetName());
113  call.Dump(MANUF__DUMP);
114 
115  Call_GetOptionsForItemTypes args;
116  if (!args.Decode(&call.tuple)) {
117  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
118  call.client->SendErrorMsg("Internal Server Error. Ref: ServerError 01588.");
119  return nullptr;
120  }
121 
122  Rsp_GetOptionsForItemTypes rsp;
123  Rsp_GetOptionsForItemTypes_Arg arg;
124 
125  for (auto cur : args.typeIDs) {
126  arg.isRecyclable = sDataMgr.IsRecyclable(cur.first);
127  arg.isRefinable = sDataMgr.IsRefinable(cur.first);
128  rsp.typeIDs[cur.first] = arg.Encode();
129  }
130 
131  return rsp.Encode();
132 }
133 
134 PyResult ReprocessingServiceBound::Handle_GetReprocessingInfo(PyCallArgs &call) {
135  Client *pClient = call.client;
136  Rsp_GetReprocessingInfo rsp;
137  rsp.standing = GetStanding(pClient);
138  rsp.tax = CalcTax( rsp.standing );
139  rsp.yield = m_staEfficiency;
140  rsp.combinedyield = CalcReprocessingEfficiency(pClient);
141  return rsp.Encode();
142 }
143 
144 PyResult ReprocessingServiceBound::Handle_GetQuote(PyCallArgs &call) {
145  Call_SingleIntegerArg arg; // itemID
146  if (!arg.Decode(&call.tuple)) {
147  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
148  call.client->SendErrorMsg("Internal Server Error. Ref: ServerError 01588.");
149  return nullptr;
150  }
151 
152  return GetQuote(arg.arg, call.client);
153 }
154 
155 PyResult ReprocessingServiceBound::Handle_GetQuotes(PyCallArgs &call) {
156  // why shipID here? processing in cap indy ships?
157  Call_GetQuotes args;
158  if (!args.Decode(&call.tuple)) {
159  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
160  call.client->SendErrorMsg("Internal Server Error. Ref: ServerError 01588.");
161  return nullptr;
162  }
163 
164  Rsp_GetQuotes rsp;
165  for (auto cur : args.itemIDs) {
166  PyRep* quote = GetQuote(cur, call.client);
167  if (quote != nullptr)
168  rsp.quotes[cur] = quote;
169  }
170 
171  return rsp.Encode();
172 }
173 
174 PyResult ReprocessingServiceBound::Handle_Reprocess(PyCallArgs &call) {
175  if (!sDataMgr.IsStation(call.client->GetLocationID())) {
176  _log(MANUF__WARNING, "Character %s tried to reprocess, but isn't is station.", call.client->GetName());
177  return nullptr;
178  }
179 
180  _log(MANUF__INFO, "%s: Calling Reprocess().", call.client->GetName());
181  call.Dump(MANUF__DUMP);
182 
183  Call_Reprocess args;
184  if (!args.Decode(&call.tuple)) {
185  codelog(SERVICE__ERROR, "%s: Failed to decode arguments.", GetName());
186  call.client->SendErrorMsg("Internal Server Error. Ref: ServerError 01588.");
187  return nullptr;
188  }
189 
190  if (args.ownerID == 0) // should never hit.
191  args.ownerID = call.client->GetCharacterID();
192 
193  if (args.flag == flagNone) // should never hit.
194  args.flag = flagHangar;
195 
196  if (args.ownerID == call.client->GetCorporationID()) {
198  _log(MANUF__WARNING, "%s(%u) doesnt have FactoryManager role to access materials for reprocessing.", \
199  call.client->GetName(), call.client->GetCharacterID());
200  call.client->SendErrorMsg("You do not have the role \'Factory Manager\' which is required to access factory services on behalf of a corporation.");
201  //throw error here...dunno the format yet.
202  return nullptr;
203  }
204 
205  sRamMthd.HangarRolesCheck(call.client, args.flag);
206  }
207 
208  InventoryItemRef iRef(nullptr);
209  double tax = CalcTax(GetStanding(call.client));
210  for (auto cur : args.items) {
211  iRef = sItemFactory.GetItem(cur);
212  if (iRef.get() == nullptr)
213  continue;
214 
215  // this should never happen, but for sure ...
216  if (iRef->type().portionSize() > iRef->quantity()) {
217  throw UserError ("QuantityLessThanMinimumPortion")
218  .AddTypeName ("typename", iRef->typeID ())
219  .AddAmount ("portion", iRef->type ().portionSize ());
220  }
221 
222  float efficiency = CalcReprocessingEfficiency( call.client, iRef );
223 
224  // dont hit db for this shit...we kinda have to....dont have this data in static shit.
225  std::vector<Recoverable> recoverables;
226  if ( !m_db.GetRecoverables( iRef->typeID(), recoverables ) )
227  continue;
228 
229  std::vector<Recoverable>::iterator itr = recoverables.begin();
230  for (; itr != recoverables.end(); ++itr) {
231  uint32 full = itr->amountPerBatch * iRef->quantity() / iRef->type().portionSize();
232  uint32 quantity = uint32(full * efficiency * (1.0f - tax) );
233  if (quantity == 0)
234  continue;
235 
236  ItemData idata(itr->typeID, args.ownerID, locTemp, flagNone, quantity);
237  InventoryItemRef iRef2 = sItemFactory.SpawnItem( idata );
238  if (iRef2.get() == nullptr)
239  continue;
240 
241  // update this for corp usage
242  iRef2->Move(m_stationRef->GetID(), (EVEItemFlags)args.flag, true);
243  }
244 
245  uint32 qtyLeft = iRef->quantity() % iRef->type().portionSize();
246  if (qtyLeft) {
247  iRef->SetQuantity(qtyLeft, true);
248  } else {
249  iRef->Move(iRef->locationID(), flagJunkyardReprocessed, true);
250  m_stationRef->RemoveItem(iRef);
251  iRef->Delete();
252  }
253  }
254 
255  return nullptr;
256 }
257 
259  /* formula is:
260  reprocessingEfficiency = 0.375
261  *(1 + 0.02 * RefiningSkill)
262  *(1 + 0.04 * RefineryEfficiencySkill)
263  *(1 + 0.05 * OreProcessingSkill)
264  */
266  CharacterRef cRef = pClient->GetChar();
267  double efficiency = (0.375f
268  * (1 + (0.02f * cRef->GetSkillLevel(EvESkill::Refining))) // 2% lvl
269  * (1 + (0.04f * cRef->GetSkillLevel(EvESkill::RefineryEfficiency)))); // 4% lvl
270 
271  if (item.get() != nullptr) {
272  uint32 specificSkill = item->GetAttribute(AttrReprocessingSkillType).get_int();
273  if (specificSkill) {
274  efficiency *= (1 + 0.05f * cRef->GetSkillLevel(specificSkill));
275  } else {
276  efficiency *= (1 + 0.04f * cRef->GetSkillLevel(EvESkill::ScrapmetalProcessing)); // use Scrapmetal Processing as default
277  }
278  }
279 
280  efficiency += m_staEfficiency;
281 
282  if (efficiency > 1.0f)
283  efficiency = 1.05f; // should be 1.0 max
284 
285  return efficiency;
286 }
287 
289  InventoryItemRef iRef = sItemFactory.GetItem( itemID );
290  if (iRef.get() == nullptr)
291  return nullptr; // No action as GetQuote is also called for reprocessed items (probably for check)
292 
293  // update this for corp items
294  if (iRef->ownerID() == pClient->GetCorporationID()) {
296  int64 roles = pClient->GetRolesAtAll();
297  //roles = pClient->GetRolesAtBase() | pClient->GetRolesAtAll();
298  //roles = pClient->GetRolesAtHQ() | pClient->GetRolesAtAll();
299  //roles = pClient->GetRolesAtOther() | pClient->GetRolesAtAll();
301  _log(MANUF__WARNING, "%s(%u) doesnt have FactoryManager role to access materials for reprocessing.", \
302  pClient->GetName(), pClient->GetCharacterID());
303  pClient->SendErrorMsg("You do not have the role \'Factory Manager\' which is required to access factory services on behalf of a corporation.");
304  //throw error here...dunno the format yet.
305  return nullptr;
306  }
307 
308  // this throw "access denied to bom hangar" on error. probably wrong for this application
309  sRamMthd.HangarRolesCheck(pClient, iRef->flag());
310  } else if (iRef->ownerID() != pClient->GetCharacterID()) {
311  _log(SERVICE__ERROR, "Character %u tried to reprocess item %u of character %u.", pClient->GetCharacterID(), iRef->itemID(), iRef->ownerID());
312  pClient->SendErrorMsg("The requested item is not yours.");
313  return nullptr;
314  }
315 
316  if (iRef->quantity() < iRef->type().portionSize()) {
317  throw UserError ("QuantityLessThanMinimumPortion")
318  .AddTypeName ("typename", iRef->typeID ())
319  .AddAmount ("portion", iRef->type ().portionSize ());
320  }
321 
322  std::vector<Recoverable> recoverables;
323  if (!m_db.GetRecoverables( iRef->typeID(), recoverables))
324  return nullptr;
325 
326  Rsp_GetQuote quote;
327  quote.lines = new PyList();
328  quote.leftOvers = iRef->quantity() % iRef->type().portionSize();
329  quote.quantityToProcess = iRef->quantity() - quote.leftOvers;
330  quote.playerStanding = GetStanding(pClient);
331 
332  double tax = CalcTax( quote.playerStanding );
333  double efficiency = CalcReprocessingEfficiency(pClient, iRef);
334 
335  for (auto cur :recoverables) {
336  uint32 ratio = cur.amountPerBatch * quote.quantityToProcess / iRef->type().portionSize();
337  Rsp_GetQuote_Recoverables_Line line;
338  line.typeID = cur.typeID;
339  line.client = uint32(efficiency * (1.0f - tax) * ratio);
340  line.station = uint32(efficiency * tax * ratio);
341  line.unrecoverable = ratio - line.client - line.station;
342  quote.lines->AddItem( line.Encode() );
343  }
344 
345  return quote.Encode();
346 }
347 
350 {
351  float standing = StandingDB::GetStanding(m_stationCorpID, pClient->GetCharacterID());
352  if (standing < 0.0f) {
353  standing += ((10.0f + standing) * 0.04f * pClient->GetChar()->GetSkillLevel(EvESkill::Diplomacy));
354  } else {
355  standing += ((10.0f - standing) * 0.04f * pClient->GetChar()->GetSkillLevel(EvESkill::Connections));
356  }
357 
358  return EvE::max(standing, StandingDB::GetStanding(m_stationCorpID, pClient->GetCorporationID()));
359 }
360 
361 // this should be moved to eve math or eve calc's or w/e
362 float ReprocessingServiceBound::CalcTax(float standing) const {
363  //EvEMath::Refine::StationTaxesForReprocessing(standing);
364  float tax = m_tax - 0.75f / 100.0f * standing;
365  if (tax < 0.0f)
366  tax = 0.0f;
367  return tax;
368 }
369 
static float GetStanding(uint32 fromID, uint32 toID)
Definition: StandingDB.cpp:142
Base Python wire object.
Definition: PyRep.h:66
Dispatcher *const m_dispatch
float CalcReprocessingEfficiency(const Client *pClient, InventoryItemRef item=InventoryItemRef(nullptr)) const
uint32 GetLocationID() const
Definition: Client.h:151
void SendErrorMsg(const char *fmt,...)
Definition: Client.cpp:2719
#define _log(type, fmt,...)
Definition: logsys.h:124
int32 value() const
Definition: PyRep.h:247
int64 GetCorpRole() const
Definition: Client.h:129
virtual void RemoveItem(InventoryItemRef iRef)
uint32 ownerID() const
Definition: InventoryItem.h:99
EVEItemFlags
Definition: EVE_Flags.h:13
void GetRefineData(uint32 &stationCorpID, float &staEfficiency, float &tax)
Definition: Station.cpp:235
UserError & AddTypeName(const char *name, uint32 typeID)
Shorthand method for adding a type's name.
int32 GetCharacterID() const
Definition: Client.h:113
int32 GetCorporationID() const
Definition: Client.h:123
std::string m_strBoundObjectName
Definition: PyBoundObject.h:54
CharacterRef GetChar() const
Definition: Client.h:164
itemID[count] Create count or of the specified item(from Insider)" ) COMMAND( goto
void Move(uint32 new_location=locTemp, EVEItemFlags flag=flagNone, bool notify=false)
PyRep * GetQuote(uint32 itemID, Client *pClient)
void _SetCallDispatcher(CallDispatcher *d)
Definition: PyCallable.h:87
* args
const ItemType & type() const
virtual PyBoundObject * CreateBoundObject(Client *pClient, const PyRep *bind_args)
uint16 portionSize() const
Definition: ItemType.h:77
int64 get_int()
Definition: EvilNumber.cpp:166
#define codelog(type, fmt,...)
Definition: logsys.h:128
PyServiceMgr *const m_manager
Definition: PyService.h:91
X * get() const
Definition: RefPtr.h:213
const char * GetName() const
Definition: Client.h:94
Client *const client
Definition: PyCallable.h:49
Python object "ccp_exceptions.UserError".
Definition: PyExceptions.h:121
#define PyCallable_REG_CALL(c, m)
Definition: PyServiceCD.h:78
Definition: Client.h:66
int64 GetRolesAtAll() const
Definition: Client.h:131
unsigned __int32 uint32
Definition: eve-compat.h:50
EVEItemFlags flag() const
PyCallable_Make_InnerDispatcher(ReprocessingService) PyCallable_Make_InnerDispatcher(ReprocessingServiceBound) ReprocessingService
bool GetRecoverables(const uint32 typeID, std::vector< Recoverable > &into)
signed __int64 int64
Definition: eve-compat.h:51
int64 max(int64 x, int64 y=0)
Definition: misc.h:103
Dispatcher *const m_dispatch
EvilNumber GetAttribute(const uint16 attrID) const
float GetStanding(const Client *pClient) 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
int8 GetSkillLevel(uint16 skillTypeID, bool zeroForNotInjected=true) const
Definition: Character.cpp:575
float CalcTax(float standing) const
#define sItemFactory
Definition: ItemFactory.h:165
ReprocessingServiceBound(PyServiceMgr *mgr, ReprocessingDB &db, uint32 stationID)
bool IsInt() const
Definition: PyRep.h:100
#define sRamMthd
Definition: RamMethods.h:55
uint32 GetID()
Definition: Station.h:95
PyInt * AsInt()
Definition: PyRep.h:122
uint16 typeID() const
const char * TypeString() const
Definition: PyRep.cpp:76
Dispatcher *const m_dispatch
Python list.
Definition: PyRep.h:639
uint32 itemID() const
Definition: InventoryItem.h:98
const char * GetName() const
Definition: PyBoundObject.h:44
int32 quantity() const
Definition: InventoryItem.h:97
PyTuple * tuple
Definition: PyCallable.h:50
#define sDataMgr