EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ConsoleCommands.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: Allan
24  Thanks to: avianrr for the idea
25 */
26 
27 #include <ios>
28 #include <iostream>
29 #include <fstream>
30 
31 #include "ConsoleCommands.h"
32 #include "../eve-common/EVEVersion.h"
33 #include "StatisticMgr.h"
35 #include "inventory/ItemDB.h"
36 #include "market/MarketDB.h"
37 #include "market/MarketMgr.h"
39 #include "threading/Threading.h"
40 // #include "testing/test.h"
41 
42 
44  :plscc(nullptr),
45  pBubbles(nullptr),
46  pSystems(nullptr),
47  pCommand(nullptr),
48  buf(nullptr),
49  tv(timeval()),
50  m_haltServer(false),
51  m_dbError(false)
52 {
53 }
54 
56 {
57  pCommand = cd;
58  tv.tv_sec = 0;
59  tv.tv_usec = 0;
60  UpdateStatus(); //initial status setting
61  sLog.Blue( " ConsoleCommand", "Console Commands Initialized." );
62  sLog.Yellow( " ConsoleCommand", "Enter 'h' for current list of supported commands." );
63 }
64 
66  if (m_haltServer) {
67  if (!m_dbError)
68  sEntityList.Shutdown();
69  return false;
70  }
71  /* reset timeouts because select() reset them */
72  tv.tv_sec = 0;
73  tv.tv_usec = 0;
74  FD_ZERO(&fds);
75  FD_SET(0, &fds);
76  select(1, &fds, nullptr, nullptr, &tv);
77  if (!FD_ISSET(0, &fds)) {
78  return true;
79  } else {
80  buf = (char*) malloc (BUFLEN);
81  if (fgets(buf, BUFLEN, stdin)) {
82  if (strncmp(buf, "x", 1) == 0) {
83  sLog.Warning(" EVEmu", "EXIT called. Breaking out of Main Loop.");
84  m_haltServer = true;
85  free (buf);
86  return false;
87  } else if (strncmp(buf, "h", 1) == 0) {
88  sLog.Green(" EVEmu", "Current Console Commands and Descriptions: ");
89  sLog.Warning("","");
90  sLog.Warning(" (h)elp", " Displays this dialog.");
91  sLog.Warning(" h(e)llo", " Displays the 'Hello World' message.");
92  sLog.Warning(" e(x)it", " Exits the server, saving all loaded items and loging out all connected clients.");
93  sLog.Warning(" (c)lients", " Displays connected clients, showing account, character, and ip.");
94  sLog.Warning(" (s)tatus", " Displays Server's System Status, showing threads, memory, and cpu time.");
95  sLog.Warning(" (v)ersion", " Displays server version.");
96  sLog.Warning(" (i)nformation", " Displays server information: version, memory, uptime, clients, items, systems, bubbles.");
97  sLog.Warning(" s(a)ve", " Immediatly saves all loaded items. *Broken*");
98  sLog.Warning(" (b)roadcast", " Broadcasts a message to all clients thru the LocalChat window. *Not Implemented*");
99  sLog.Warning(" (n)ote", " Broadcasts a message to all clients thru a notification window.");
100  sLog.Warning(" (m)essage", " Broadcasts a message to all clients thru a message window.");
101  sLog.Warning(" (p)rofile", " Prints a profile of current server runtimes. *Incomplete*");
102  sLog.Warning(" r(o)les", " Prints a list of common roles and their values.");
103  sLog.Warning(" c(o)mmands", " Prints a list of currently loaded Commands and their required role. (long list)");
104  sLog.Warning(" (t)est", " Prints the current test object *varies*");
105  sLog.Warning(" e(f)fects", " Compiles and prints all item effects.");
106  sLog.Warning(" threa(d)s", " Prints a list of current threads.");
107  sLog.Warning(" reload (l)ogs", " Reloads log.ini to change values without restarting server.");
108  sLog.Warning("(q)uery stat data", " Prints current statistic data.");
109  sLog.Warning(" hea(r) all", " Echo all chat msgs to console. *Not Implemented*");
110  } else if (strncmp(buf, "e", 1) == 0) {
111  sLog.Green(" EVEmu", "Server Hello:");
112  sLog.Magenta(" Server Says", " Hello World!" );
113  } else if (strncmp(buf, "c", 1) == 0) {
114  sLog.Green(" EVEmu", "Client Connection Information");
115  uint8 clients = sEntityList.GetClientCount();
116  sLog.Warning(" Connections", " %u Current Clients Online", clients);
117  sLog.Warning(" Connections", " %u Clients Connected since startup.", sEntityList.GetConnections() );
118  if (clients) {
119  std::vector<Client*> list;
120  sEntityList.GetClients(list);
121  for (auto cur : list) {
122  if (cur->GetChar().get() == nullptr) {
123  sLog.Magenta(" Connections", " Account %u Connected, but no character selected yet.", cur->GetUserID() );
124  continue;
125  } else
126  sLog.Warning(" Connections", " [(%u)%u] %s in %s(%u). %u Minutes Online.", \
127  cur->GetUserID(), cur->GetCharacterID(), cur->GetName(), \
128  cur->GetSystemName().c_str(), cur->GetLocationID(), cur->GetChar()->OnlineTime() );
129  }
130  }
131  } else if (strncmp(buf, "s", 1) == 0) {
132  std::string state = "";
133  int64 threads(0);
134  uint8 aThreads = std::thread::hardware_concurrency();
135  float vm(0.0f), rss(0.0f), user(0.0f), kernel(0.0f);
136  Status(state, threads, vm, rss, user, kernel);
137  sLog.Warning(" Server Status", " S: %s | T: %d(%u) | RSS: %.3fMb | VM: %.3fMb | U: %.2f | K: %.2f", \
138  state.data(), threads, aThreads, rss, vm, user, kernel );
139  sLog.Warning(" Connections", " %u Current Clients Online.", sEntityList.GetClientCount());
140  sLog.Warning(" Connections", " %u Clients Connected since startup.", sEntityList.GetConnections() );
141  if (sConfig.debug.UseProfiling) {
142  sLog.Green(" Server Profiling","Enabled.");
143  } else {
144  sLog.Warning(" Server Profiling","Disabled.");
145  }
146  if (sConfig.npc.StaticSpawns) {
147  sLog.Green(" Static Spawns","Enabled.");
148  } else {
149  sLog.Warning(" Static Spawns","Disabled.");
150  }
151  if (sConfig.npc.RoamingSpawns) {
152  sLog.Green(" Roaming Spawns","Enabled.");
153  } else {
154  sLog.Warning(" Roaming Spawns","Disabled.");
155  }
156  if (sConfig.rates.secRate != 1.0) {
157  sLog.Green(" SecStatus","Enabled at %.0f%%.", (sConfig.rates.secRate *100) );
158  } else {
159  sLog.Warning(" SecStatus","Normal.");
160  }
161  if (sConfig.rates.npcBountyMultiply != 1.0) {
162  sLog.Green(" Bountys","Enabled at %.0f%%.", (sConfig.rates.npcBountyMultiply *100) );
163  } else {
164  sLog.Warning(" Bountys","Normal.");
165  }
166  if (sConfig.rates.damageRate != 1.0) {
167  sLog.Green(" All Damages","Enabled at %.0f%%.", (sConfig.rates.damageRate *100) );
168  } else {
169  sLog.Warning(" All Damages","Normal.");
170  }
171  if (sConfig.rates.missileRoF != 1.0) {
172  sLog.Green(" Missile Dmg","Enabled at %.0f%%.", (sConfig.rates.missileRoF *100) );
173  } else {
174  sLog.Warning(" Missile Dmg","Normal.");
175  }
176  if (sConfig.rates.turretRoF != 1.0) {
177  sLog.Green(" Turret Dmg","Enabled at %.0f%%.", (sConfig.rates.turretRoF *100) );
178  } else {
179  sLog.Warning(" Turret Dmg","Normal.");
180  }
181  } else if (strncmp(buf, "v", 1) == 0) {
182  sLog.Green(" EVEmu", "Server Version:");
183  sLog.Warning(" Server Revision", " %s", EVEMU_REVISION );
184  sLog.Warning(" Build Date", " %s", EVEMU_BUILD_DATE );
185  } else if (strncmp(buf, "i", 1) == 0) {
186  sLog.Green(" EVEmu", "Server Information:");
187  sLog.Warning(" Server Revision", " %s", EVEMU_REVISION );
188  sLog.Warning(" Build Date", " %s", EVEMU_BUILD_DATE );
189  // memory
190  std::string state = "";
191  int64 threads(0);
192  uint8 aThreads = std::thread::hardware_concurrency();
193  float vm(0.0f), rss(0.0f), user(0.0f), kernel(0.0f);
194  Status(state, threads, vm, rss, user, kernel);
195  sLog.Warning(" Memory Usage", " RSS: %.3fMb VM: %.3fMb", rss, vm );
196  sLog.Warning(" Server Status", " S: %s | T: %li(%u) | U: %.2f | K: %.2f", \
197  state.data(), threads, aThreads, user, kernel );
198  std::string uptime;
199  sEntityList.GetUpTime(uptime);
200  sLog.Warning(" Server UpTime", " %s", uptime.c_str() );
201  // loaded items
202  sLog.Warning(" Loaded Items", " %u", sItemFactory.Count());
203  // loaded NPCs
204  sLog.Warning(" Loaded NPCs", " %u", sEntityList.GetNPCCount());
205  // loaded systems
206  sLog.Warning(" Active Systems", " %u", sEntityList.GetSystemCount());
207  // loaded stations
208  sLog.Warning(" Active Stations", " %u", sEntityList.GetStationCount());
209  // loaded bubbles
210  sLog.Warning(" Active Bubbles", " %u", sBubbleMgr.Count());
211  // current clients
212  sLog.Warning(" Connections", " %u Current Clients Online.", sEntityList.GetClientCount());
213  sLog.Warning(" Connections", " %u Clients Connected since startup.", sEntityList.GetConnections() );
214  } else if (strncmp(buf, "a", 1) == 0) {
215  sLog.Green(" EVEmu", "Server SaveAll:");
216  //sLog.Error(" Server Save", " Not Available Yet." );
217  sItemFactory.SaveItems();
218  } else if (strncmp(buf, "b", 1) == 0) {
219  sLog.Green(" EVEmu", "Server Broadcast:");
220  sLog.Error(" Server Broadcast", " Not Available Yet." );
222  //const char* buff = buf + 2;
223  //SendMessage(buff);
224  } else if (strncmp(buf, "n", 1) == 0) {
225  sLog.Green(" EVEmu", "Server Notify:");
226  const char* buff = buf + 2;
227  std::vector<Client*> list;
228  sEntityList.GetClients(list);
229  for (auto cur : list)
230  cur->SendNotifyMsg( buff );
231  sLog.Warning(" Console Command", " Notification sent to all online clients." );
232  } else if (strncmp(buf, "m", 1) == 0) {
233  sLog.Green(" EVEmu", "Server Modal Message:");
234  const char* buff = buf + 2;
235  std::vector<Client*> list;
236  sEntityList.GetClients(list);
237  for (auto cur : list)
238  cur->SendInfoModalMsg( buff );
239  sLog.Warning(" Console Command", " Modal Message sent to all online clients." );
240  } else if (strncmp(buf, "p", 1) == 0) {
241  sLog.Green(" EVEmu", "Server Profile:");
242  if (sConfig.debug.UseProfiling) {
243  sLog.Warning(" Current Stamp", " %u", sEntityList.GetStamp());
244  std::string uptime;
245  sEntityList.GetUpTime(uptime);
246  sLog.Warning(" Server UpTime", " %s", uptime.c_str() );
247  sLog.Warning(" Connections", " %u Current Clients Online.", sEntityList.GetClientCount());
248  sLog.Warning(" Connections", " %u Clients Connected since startup.", sEntityList.GetConnections() );
249  sProfiler.PrintProfile();
250  } else {
251  sLog.Error(" Server Profile", "Profiling is turned off.");
252  }
253  } else if (strncmp(buf, "r", 1) == 0) {
254  // enable console chat echo
255  } else if (strncmp(buf, "o", 1) == 0) {
256  sLog.Green(" EVEmu", "Common Account Roles:");
257  sLog.Warning(" Acct::Role::DEV", " %li(%p)", Acct::Role::DEV, Acct::Role::DEV);
258  sLog.Warning(" Acct::Role::STD", " %li(%p)", Acct::Role::STD, Acct::Role::STD);
259  sLog.Warning(" Acct::Role::VIP", " %li(%p)", Acct::Role::VIP, Acct::Role::VIP);
260  sLog.Warning(" Acct::Role::VIP+", " %li(%p)", Acct::Role::EPLAYER, Acct::Role::EPLAYER);
261  sLog.Warning(" Acct::Role::VIEW", " %li(%p)", Acct::Role::VIEW, Acct::Role::VIEW);
262  sLog.Warning(" Acct::Role::BOSS", " %li(%p)", Acct::Role::BOSS, Acct::Role::BOSS);
263  sLog.Warning(" Acct::Role::SLASH", " %li(%p)", Acct::Role::SLASH, Acct::Role::SLASH);
264  sLog.Warning(" Acct::Role::CREATOR", " %li(%p)", Acct::Role::CREATOR, Acct::Role::CREATOR);
265  std::printf("\n"); // spacer
266  sLog.Green(" EVEmu", "Common Corp Roles:");
267  sLog.Warning(" Corp::Role::All", " %li(%p)", Corp::Role::All, Corp::Role::All);
268  sLog.Warning(" Corp::Role::Cont", " %li(%p)", Corp::Role::AllContainer, Corp::Role::AllContainer);
269  sLog.Warning(" Corp::Role::Admin", " %li(%p)", Corp::Role::Admin, Corp::Role::Admin);
270  sLog.Warning(" Corp::Role::Hangar", " %li(%p)", Corp::Role::AllHangar, Corp::Role::AllHangar);
271  sLog.Warning(" Corp::Role::Account", " %li(%p)", Corp::Role::AllAccount, Corp::Role::AllAccount);
272  sLog.Warning("Corp::Role::Starbase", " %li(%p)", Corp::Role::AllStarbase, Corp::Role::AllStarbase);
273  } else if (strncmp(buf, "o", 1) == 0) {
275  } else if (strncmp(buf, "t", 1) == 0) {
276  Test();
277  } else if (strncmp(buf, "f", 1) == 0) {
278  std::string args(buf);
279  char& num(args.at(1));
280  FxProc(atoi(&num));
281  } else if (strncmp(buf, "d", 1) == 0) {
282  uint8 maxCount = sConfig.server.MaxThreadReport;
283  uint8 count = sThread.Count();
284  sLog.Blue(" Active Threads", "There are %u active threads running in the server.", count);
285  if (count > maxCount) {
286  sLog.Warning(" Active Threads", "Individual thread IDs are not displayed for more than %u active threads.", maxCount);
287  } else {
288  sThread.ListThreads();
289  }
290  } else if (strncmp(buf, "l", 1) == 0) {
291  /*
292  sLog.~NewLog();
293  sLog.InitializeLogging(sConfig.files.logDir);
294  */
295  if (load_log_settings(sConfig.files.logSettings.c_str())) {
296  sLog.Green(" EVEmu", "Log settings reloaded from %s", sConfig.files.logSettings.c_str() );
297  // reset config switches based on log settings
298  sConfig.debug.StackTrace = is_log_enabled(SERVER__STACKTRACE);
299  sConfig.debug.BeanCount = is_log_enabled(SERVER__BEANCOUNT);
300  sConfig.debug.IsTestServer = is_log_enabled(SERVER__TESTSERVER);
301  } else {
302  sLog.Warning(" EVEmu", "Unable to reload settings from %s", sConfig.files.logSettings.c_str() );
303  }
304  } else if (strncmp(buf, "q", 1) == 0) {
305  sLog.Green(" EVEmu", "Server Statistic Data:");
306  sStatMgr.PrintInfo();
307  } else {
308  sLog.Error(" EVEmu", "Command not recognized: %s", buf);
309  }
310  }
311  free (buf);
312  return true;
313  }
314 }
315 
316 void ConsoleCommand::SendMessage(const char* msg) {
317  // LSCChannel::SendMessage(Client * c, const char * message, bool self)
318  // this isnt how it works....need more info
319 }
320 
321 void ConsoleCommand::Status(std::string& state, int64& threads, float& vm_usage, float& resident_set, float& user, float& kernel)
322 {
323  // the fields we want
324  std::string ignore = "", run_state = "";
325  int64 num_threads(0); //this is saved from OS as long decimal....*sigh* gotta allocate long int for it or weird shit happens.
326  int64 vsize(0); //in bytes
327  int64 rss(0); //in pages
328  float utime(0.0f), stime(0.0f);
329 
330  // stat seems to give the most reliable results
331  std::ifstream ifs ("/proc/self/stat", std::ios_base::in);
332  ifs >> ignore >> ignore >> run_state >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore
333  >> ignore >> ignore >> ignore >> utime >> stime >> ignore >> ignore >> ignore >> ignore >> num_threads
334  >> ignore >> ignore >> vsize >> rss;
335  ifs.close();
336 
337  if (sConfig.debug.IsTestServer)
338  _log(SERVER__INFO, "ConsoleCommand::Status() proc/self/stat returns RSS: %li, VM: %li", rss, vsize);
339 
340  /* state = One character from the string "RSDZTW" where
341  R is running,
342  S is sleeping in an interruptible wait,
343  D is waiting in uninterruptible disk sleep,
344  Z is zombie,
345  T is traced or stopped (on a signal),
346  W is paging
347  */
348  state = run_state;
349  threads = num_threads;
350  user = utime / sysconf(_SC_CLK_TCK) / 100.0f;
351  kernel = stime / sysconf(_SC_CLK_TCK) / 100.0f;
352  vm_usage = ((vsize / sysconf(_SC_PAGE_SIZE)) / 1024.0f / 6);
353  //rss (in pages) * page_size(in bytes, converted to k), then convert to Mb.
354  resident_set = (rss * (sysconf(_SC_PAGE_SIZE) / 1024.0f) / 1024.0f);
355 }
356 
358 {
359  sLog.Green(" EVEmu", "Running Server Test Code:");
360  //sLog.Warning(" Server Test Code", "No Test Code at this time.");
361 
362  //sMktMgr.GetCruPrices();
363  //sLog.Blue(" MktPricing", "Base material Prices Updated");
364 
365  sMktMgr.SetBasePrice();
366 
367  // execute code to begin filling missing data in mission db.
368  // first step: get courier missionIDs
369  //sMissionDataMgr.UpdateMissionData();
370  //MarketDB::UpdateHistory();
371 }
372 
374  std::string state = "";
375  int64 threads(0);
376  float vm(0.0f), rss(0.0f), user(0.0f), kernel(0.0f);
377  Status(state, threads, vm, rss, user, kernel);
378  if (sConfig.debug.IsTestServer)
379  _log(SERVER__INFO, "Current Mem usage - RSS: %f, VM: %f", rss, vm);
380  ServiceDB::SaveServerStats(threads + sThread.Count(), rss, vm, user, kernel, sItemFactory.Count(), sBubbleMgr.Count());
381  sStatMgr.Process();
382 }
383 
385 
386  double start = GetTimeMSeconds();
387  sLog.Green(" EVEmu", "FxProcess(%u)", idx);
388 
389  // load items from db
390  std::map<uint16, std::string> typeIDs;
391  switch(idx) {
392  case 1: {
393  sLog.Green(" FxProcess", "Parsing Effects for Module Items.");
394  std::printf("\n"); // spacer
396  } break;
397  case 2: {
398  sLog.Green(" FxProcess", "Parsing Effects for Charge Items.");
399  std::printf("\n"); // spacer
401  } break;
402  case 3: {
403  sLog.Green(" FxProcess", "Parsing Effects for Subsystem Items.");
404  std::printf("\n"); // spacer
406  } break;
407  case 4: {
408  sLog.Green(" FxProcess", "Parsing Effects for Skill Items.");
409  std::printf("\n"); // spacer
411  } break;
412  case 5: {
413  sLog.Green(" FxProcess", "Parsing Effects for Implant Items.");
414  std::printf("\n"); // spacer
415  ItemDB::GetItems(EVEDB::invCategories::Implant, typeIDs); // this is implants and boosters
416  } break;
417  case 6: {
418  sLog.Green(" FxProcess", "Parsing Effects for Ship Items.");
419  std::printf("\n"); // spacer
421  } break;
422  case 9: {
423  sLog.Green(" FxProcess", "Parsing Effects for All Possible Items.");
424  std::printf("\n"); // spacer
429  ItemDB::GetItems(EVEDB::invCategories::Implant, typeIDs); // this is implants and boosters
431  } break;
432  default: {
433  sLog.Green(" FxProcess", "Usage of Item Fx process reporting.");
434  sLog.Green(" FxProcess", "fx where 'x' is a number corresponding to the item category you wish to process.");
435  sLog.Green(" FxProcess", "1=Modules, 2=Charges, 3=Subsystems, 4=Skills, 5=Implants/Boosters, 6=Ships, 9=all");
436  return;
437  }
438  }
439 
440  for (auto cur : typeIDs) {
441  sLog.Yellow("CC::FxProc", "Processing %s (%u)", cur.second.c_str(), cur.first);
442  // proc and print skill fx
443  std::vector< TypeEffects > typeEffMap;
444  sFxDataMgr.GetTypeEffect(cur.first, typeEffMap);
445  for (auto cur2: typeEffMap)
446  sFxProc.DecodeEffects(cur2.effectID);
447  }
448 
449  sLog.Magenta("CC::FxProc", "- effects processed in %.3fms", (GetTimeMSeconds() - start));
450 }
#define sConfig
A macro for easier access to the singleton.
CommandDispatcher * pCommand
unsigned __int8 uint8
Definition: eve-compat.h:46
#define sStatMgr
Definition: StatisticMgr.h:68
#define _log(type, fmt,...)
Definition: logsys.h:124
bool load_log_settings(const char *filename)
Definition: logsys.cpp:168
#define sProfiler
Definition: dbcore.cpp:39
struct timeval tv
#define sEntityList
Definition: EntityList.h:208
static void SaveServerStats(double threads, float rss, float vm, float user, float kernel, uint32 items, uint32 bubbles)
Definition: ServiceDB.cpp:336
* args
#define is_log_enabled(type)
Definition: logsys.h:78
#define sThread
Definition: Threading.h:50
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
void FxProc(uint8 idx=0)
double GetTimeMSeconds()
Definition: utils_time.cpp:104
#define sFxDataMgr
void Status(std::string &state, int64 &threads, float &vm_usage, float &resident_set, float &user, float &kernel)
void SendMessage(const char *msg)
#define sMktMgr
Definition: MarketMgr.h:86
void Initialize(CommandDispatcher *cd)
#define sFxProc
signed __int64 int64
Definition: eve-compat.h:51
static const char *const EVEMU_REVISION
Definition: EVEVersion.h:42
#define BUFLEN
#define sItemFactory
Definition: ItemFactory.h:165
static const char *const EVEMU_BUILD_DATE
Definition: EVEVersion.h:43
#define sBubbleMgr
entityID heal the character with the entityID note giving you detailed ship status information gives a list of all dynamic entities and players and their destinyState in this bubble shows some current destiny variables save all kick all and halt server immediate command list all items in current location s gives list of cargo contents and volumes in all holds list current session values show current ship DNA show current objects in their destiny state
static void GetItems(uint16 catID, std::map< uint16, std::string > &typeIDs)
Definition: ItemDB.cpp:367