1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "stdpch.h" // First include for pre-compiled headers.
30 #include "nel/misc/file.h"
31 #include "nel/misc/async_file_manager.h"
33 #include "nel/3d/u_text_context.h"
36 #include "debug_client.h"
38 #include "client_cfg.h"
39 #include "net_manager.h"
40 #include "user_entity.h"
43 #include "user_agent.h"
44 #include "interface_v3/interface_manager.h"
45 #include "interface_v3/sphrase_manager.h"
47 #include "nel/gui/lua_helper.h"
48 using namespace NLGUI
;
49 #include "character_cl.h"
50 #include "r2/editor.h"
51 #include "r2/dmc/client_edition_module.h"
52 #include "nel/gui/lua_manager.h"
58 using namespace NLMISC
;
66 #if defined(NL_OS_WINDOWS)
67 MEMORYSTATUS MemoryStatus
;
69 bool FreezeGraph
= false;
71 //the NEL 3d textcontext
72 extern NL3D::UTextContext
*TextContext
;
73 extern uint8 ShowInfos
; // 0=no info 1=text info 2=graph info 3=streaming info 4=fps only
74 extern UDriver
*Driver
;
76 /// domain server version for patch
77 extern string R2ServerVersion
;
81 static const char *divisor
= "K";
83 // Check if the stack is empty (of debug str not infos).
84 uint DebugStackEmpty
= true;
85 // vector that contains debug strings.
86 std::vector
<std::string
> DebugStack
;
89 bool IsDebugFile
= false;
90 // Verbose mode about the animation of the selection.
91 bool VerboseAnimSelection
= false;
92 // Verbose mode about the animation of the user.
93 bool VerboseAnimUser
= false;
94 // Verbose Mode about visual properties.
95 bool VerboseVP
= false;
96 // Slot of the entity to display with the debug page.
97 CLFECOMMON::TCLEntityId WatchedEntitySlot
= CLFECOMMON::INVALID_SLOT
;
100 // To Display some Debug information
104 uint64 IngameEnterTime
= 0;
109 //-----------------------------------------------
111 // Push a string in a debug stack but will display only if there is a debug string in the stack when flush.
112 //-----------------------------------------------
113 void pushInfoStr(const std::string
&str
)
115 // Add the string to the debug stack.
116 if (!ClientCfg
.Light
)
117 DebugStack
.push_back(str
);
120 //-----------------------------------------------
122 // Push a string in a debug stack.
123 //-----------------------------------------------
124 void pushDebugStr(const std::string
&str
)
126 if (!ClientCfg
.Light
)
128 // Add the string to the debug stack.
129 DebugStack
.push_back("=> " + str
);
131 // Stack is no more empty.
132 DebugStackEmpty
= false;
136 //-----------------------------------------------
138 // Display 'title' and a warning for each element in the Debug Stack.
139 //-----------------------------------------------
140 void flushDebugStack(const std::string
&title
)
142 // If debug stack is not empty
148 string strTmp
= toString(" %s\n", title
.c_str());
149 DebugFile
.serialBuffer((uint8
*)strTmp
.c_str(), (uint
)strTmp
.size());
151 for(uint i
=0; i
<DebugStack
.size(); ++i
)
153 strTmp
= toString(" %s\n", DebugStack
[i
].c_str());
154 DebugFile
.serialBuffer((uint8
*)strTmp
.c_str(), (uint
)strTmp
.size());
157 // Empty line separator
158 strTmp
= toString("\n");
159 DebugFile
.serialBuffer((uint8
*)strTmp
.c_str(), (uint
)strTmp
.size());
161 // No Output File -> nldebug only if DisableNLDebug not set to true
162 else if (!DisableNLDebug
)
164 nldebug("%s", title
.c_str());
165 for(uint i
=0; i
<DebugStack
.size(); ++i
)
166 nldebug(" %s", DebugStack
[i
].c_str());
168 // Empty line separator
173 // Clean the stack (infos could remain in the stack so clean here).
175 // Stack is empty now.
176 DebugStackEmpty
= true;
177 }// flushDebugStack //
179 //-----------------------------------------------
181 // Set an output file to log debugs.
182 //-----------------------------------------------
183 void setDebugOutput(const std::string
&filename
)
193 // Open The Item Association File
194 if(!DebugFile
.open(filename
, false, true))
196 nlwarning("setDebugOutput: Cannot Open the '%s'.", filename
.c_str());
201 }// setDebugOutput //
205 //-----------------------------------------------
208 //-----------------------------------------------
209 void initDebugMemory()
211 #if defined(NL_OS_WINDOWS)
212 GlobalMemoryStatus(&MemoryStatus
);
215 nlwarning("The MemoryStatus structure is %ld bytes long.", stat.dwLength);
216 nlwarning("It should be %d.", sizeof (stat));
217 nlwarning("%ld percent of memory is in use.", stat.dwMemoryLoad);
218 nlwarning("There are %*ld total %sbytes of physical memory.", WIDTH, stat.dwTotalPhys/DIV, divisor);
219 nlwarning("There are %*ld free %sbytes of physical memory.", WIDTH, stat.dwAvailPhys/DIV, divisor);
220 nlwarning("There are %*ld total %sbytes of paging file.", WIDTH, stat.dwTotalPageFile/DIV, divisor);
221 nlwarning("There are %*ld free %sbytes of paging file.", WIDTH, stat.dwAvailPageFile/DIV, divisor);
222 nlwarning("There are %*lx total %sbytes of virtual memory.", WIDTH, stat.dwTotalVirtual/DIV, divisor);
223 nlwarning("There are %*lx free %sbytes of virtual memory.\n", WIDTH, stat.dwAvailVirtual/DIV, divisor);
228 //-----------------------------------------------
229 // memoryUsedSinceLastCall :
231 //-----------------------------------------------
232 double memoryUsedSinceLastCall()
234 #if defined(NL_OS_WINDOWS)
236 GlobalMemoryStatus(&stat
);
238 double mem
= (double)MemoryStatus
.dwAvailPhys
-(double)stat
.dwAvailPhys
;
245 nlwarning("%ld percent of memory is in use.", stat.dwMemoryLoad);
246 nlwarning("There are %*ld free %sbytes of physical memory.", WIDTH, stat.dwAvailPhys/DIV, divisor);
247 nlwarning("There are %*ld free %sbytes of paging file.", WIDTH, stat.dwAvailPageFile/DIV, divisor);
248 nlwarning("There are %*lx free %sbytes of virtual memory.\n", WIDTH, stat.dwAvailVirtual/DIV, divisor);
251 return std::numeric_limits
<double>::quiet_NaN();
258 CDebugClient::CDebugClient()
263 #define INFO if (info_is_active(ANIM_INFO)) nlinfo
264 #define INFO (info_is_active(ANIM_INFO)?nlinfo:)
267 INFO("fjkljf %gf %fhgf", dg, dsf);
270 // ***************************************************************************
272 CGraph
SpfGraph ("time per frame (100 ms)", 10.0f
, 10.0f
, 250.0f
, 100.0f
, CRGBA(0,128,0,255), 0, 100.0f
, 3);
273 CGraph
CurrentTaskGraph ("current task", 10.0f
, 120.0f
, 250.0f
, 50.0f
, CRGBA(0,0,128,255), 0, 1.0f
, 3);
274 CGraph
VRAMDownGraph ("vram download (1Mo)", 10.0f
, 180.0f
, 250.0f
, 50.0f
, CRGBA(128,0,0,255), 0, 1.f
, 3);
275 CGraph
VRAMUpGraph ("vram upload (1Mo)", 10.0f
, 240.0f
, 250.0f
, 50.0f
, CRGBA(128,0,0,255), 0, 1.f
, 3);
276 CGraph
OpenedFileGraph ("fopen (10)", 10.0f
, 300.0f
, 250.0f
, 50.0f
, CRGBA(128,128,0,255), 0, 10.f
, 3);
277 CGraph
ByteReadGraph ("byte read (500 ko)", 10.0f
, 360.0f
, 250.0f
, 100.0f
, CRGBA(0,128,128,255), 0, 500, 3);
278 CGraph
ByteReadGraphInstant ("instant byte read (500 ko)", 10.0f
, 470.0f
, 250.0f
, 100.0f
, CRGBA(64,0,128,255), 0, 500, 3);
279 CGraph
FileReadGraph ("fread (10)", 10.0f
, 580.0f
, 250.0f
, 50.0f
, CRGBA(128,0,128,255), 0, 10, 3);
280 CGraph
LuaMemGraph ("Lua memory (mb)", 290.0f
, 10.0f
, 250.0f
, 100.0f
, CRGBA(0,128,64,255), 0, 64.0f
, 3);
282 void displayStreamingDebug ()
284 // Yoyo: display with getPerformanceTime() for better precision.
285 static TTicks oldTick
= CTime::getPerformanceTime();
286 TTicks newTick
= CTime::getPerformanceTime();
287 double deltaTime
= CTime::ticksToSecond (newTick
-oldTick
);
289 static NLMISC::CValueSmoother smooth
;
290 smooth
.addValue((float)deltaTime
);
291 deltaTime
= deltaTime
* 5.0f
/ 6.0f
;
292 float deltaTimeSmooth
= smooth
.getSmoothValue () * 5.0f
/ 6.0f
;
297 static uint32 lastVRAMUsed
= Driver
->profileAllocatedTextureMemory();
298 uint32 VRAMUsed
= Driver
->profileAllocatedTextureMemory();
299 sint value
= (sint
)VRAMUsed
-(sint
)lastVRAMUsed
;
302 VRAMUpGraph
.addOneValue ((float)value
/(1024.f
*1024.f
));
303 VRAMDownGraph
.addOneValue (0);
307 VRAMDownGraph
.addOneValue ((float)(-value
)/(1024.f
*1024.f
));
308 VRAMUpGraph
.addOneValue (0);
310 lastVRAMUsed
= VRAMUsed
;
313 static uint32 lastFileOpened
= CIFile::getNumFileOpen();
314 uint32 fileOpened
= CIFile::getNumFileOpen();
315 OpenedFileGraph
.addOneValue((float)(fileOpened
-lastFileOpened
));
316 lastFileOpened
=fileOpened
;
319 static uint32 lastByteRead
= CIFile::getReadFromFile();
320 uint32 byteRead
= CIFile::getReadFromFile();
321 ByteReadGraph
.addOneValue((float)CIFile::getReadingFromFile()/1024.f
);
322 ByteReadGraphInstant
.addOneValue((float)(byteRead
-lastByteRead
)/1024.f
);
323 lastByteRead
=byteRead
;
326 static uint32 lastFileRead
= CIFile::getNumFileRead();
327 uint32 fileRead
= CIFile::getNumFileRead();
328 FileReadGraph
.addOneValue((float)(fileRead
-lastFileRead
));
329 lastFileRead
=fileRead
;
332 SpfGraph
.addOneValue (1000.f
*(float)deltaTime
);
335 LuaMemGraph
.addOneValue(CLuaManager::getInstance().getLuaState()->getGCCount() / 1024.f
);
337 // Count of waitinf instance
338 CurrentTaskGraph
.addOneValue (CAsyncFileManager::getInstance().isTaskRunning()?1.f
:0.f
);
345 float lineStep
= ClientCfg
.DebugLineStep
;
350 // Create a shadow when displaying a text.
351 TextContext
->setShaded(true);
352 TextContext
->setShadeOutline(false);
353 // Set the font size.
354 TextContext
->setFontSize(ClientCfg
.DebugFontSize
);
355 // Set the text color
356 TextContext
->setColor(ClientCfg
.DebugFontColor
);
360 TextContext
->setHotSpot(UTextContext::TopLeft
);
362 // FPS and Ms per frame
364 TextContext
->printfAt(0.01f
, line
, "STREAMING INFORMATION");
365 if(deltaTimeSmooth
!= 0.f
)
366 TextContext
->printfAt(0.8f
, line
,"%.1f fps", 1.f
/deltaTimeSmooth
);
368 TextContext
->printfAt(0.8f
, line
,"%.1f fps", 0.f
);
369 TextContext
->printfAt(0.9f
, line
, "%d ms", (uint
)(deltaTimeSmooth
*1000));
372 // Dump the task array
374 TextContext
->printfAt(0.3f
, line
,"Task manager:");
376 static vector
<string
> names
;
377 CAsyncFileManager::getInstance().dump(names
);
379 for (i
=0; i
<names
.size (); i
++)
381 TextContext
->printfAt(0.3f
, line
, " %s", names
[i
].c_str());
385 // Dump the opened file array
387 TextContext
->printfAt(0.65f
, line
,"Files opened:");
390 for (i
=0; i
<names
.size (); i
++)
392 TextContext
->printfAt(0.65f
, line
, " %s", names
[i
].c_str());
396 // No more shadow when displaying a text.
397 TextContext
->setShaded(false);
398 TextContext
->setShadeOutline(false);
402 // ***************************************************************************
404 Display short debug information of FartTP / reselectperso events
406 class CDebugConnectionHistory
414 NumDebugConnectionEvent
418 CDebugConnectionHistory()
421 for(uint i
=0;i
<NumDebugConnectionEvent
;i
++)
425 // Add a connection event
426 void debugAddConnectionEvent(TEvent ev
)
428 nlassert(ev
<NumDebugConnectionEvent
);
430 EventQueue
.push_back(ev
);
431 if(EventQueue
.size()>MaxQueueSize
)
432 EventQueue
.pop_front();
435 // display debug information in a string
436 void debugDisplayConnectionEvent(string
&str
)
439 for(uint i
=0;i
<NumDebugConnectionEvent
;i
++)
441 str
+= toString("%s: %d\n", CounterNames
[i
], EventCounters
[i
]);
443 // display queue of last events:
444 str
+= "Connection Events: ";
445 for(uint i
=0;i
<EventQueue
.size();i
++)
449 // Avoid crash in the crash log: in the improbable case where EventQueue has been crashed, at least don't crash here
450 uint index
= EventQueue
[i
];
451 clamp(index
,0U,uint(NumDebugConnectionEvent
-1));
452 str
+= EventNames
[index
];
458 static const char *EventNames
[NumDebugConnectionEvent
];
459 static const char *CounterNames
[NumDebugConnectionEvent
];
461 uint EventCounters
[NumDebugConnectionEvent
];
462 deque
<TEvent
> EventQueue
;
466 static CDebugConnectionHistory DebugConnectionHistory
;
467 const char *CDebugConnectionHistory::EventNames
[CDebugConnectionHistory::NumDebugConnectionEvent
]=
468 {"HOP", "FTP", "RSL"};
469 const char *CDebugConnectionHistory::CounterNames
[CDebugConnectionHistory::NumDebugConnectionEvent
]=
470 {"NumServerHOP", "NumFarTP", "NumReselectPerso"};
474 void crashLogAddServerHopEvent()
476 DebugConnectionHistory
.debugAddConnectionEvent(CDebugConnectionHistory::ServerHopEvent
);
478 void crashLogAddFarTpEvent()
480 DebugConnectionHistory
.debugAddConnectionEvent(CDebugConnectionHistory::FarTPEvent
);
482 void crashLogAddReselectPersoEvent()
484 DebugConnectionHistory
.debugAddConnectionEvent(CDebugConnectionHistory::ReselectPersoEvent
);
488 // ***************************************************************************
489 string
getDebugInformation()
493 str
+= toString("UserId: %u\n", NetMngr
.getUserId());
494 str
+= toString("HomeId: %u\n", CharacterHomeSessionId
.asInt());
495 extern TSessionId HighestMainlandSessionId
;
496 str
+= toString("ShardId: %u\n", HighestMainlandSessionId
.asInt());
497 extern bool IsInRingSession
;
500 if (getEditor().isInitialized())
501 str
+= toString("SessionId: %u\n", getEditor().getDMC().getEditionModule().getCurrentAdventureId().asInt());
502 extern R2::TUserRole UserRoleInSession
;
503 str
+= toString("Role: %s\n", UserRoleInSession
.toString().c_str());
507 str
+= toString("On a Mainland Shard\n");
509 CConfigFile::CVar
*varPtr
= ClientCfg
.ConfigFile
.getVarPtr("Application");
511 str
+= toString("Application: %s\n", varPtr
->asString(0).c_str());
513 str
+= toString("Application: NotFound\n");
517 str
+= toString("Player Name: '%s'\n", UserEntity
->getEntityName().c_str());
518 str
+= toString("UserPosition: %.2f %.2f %.2f\n", UserEntity
->pos().x
, UserEntity
->pos().y
, UserEntity
->pos().z
);
522 str
+= "No user entity information\n";
525 str
+= toString("ViewPosition: %.2f %.2f %.2f\n", View
.viewPos().x
, View
.viewPos().y
, View
.viewPos().z
);
526 uint64 timeInGame
= ingameTime1 ();
527 str
+= toString("Time in game: %dh %dmin %dsec\n", (uint
)(timeInGame
/(60*60*1000)), (uint
)(timeInGame
/(60*1000))%60, (uint
)(timeInGame
/1000)%60);
528 str
+= toString("LocalTime: %s\n", NLMISC::IDisplayer::dateToHumanString(time(NULL
)));
529 str
+= toString("ServerTick: %u\n", NetMngr
.getCurrentServerTick());
530 str
+= toString("ConnectState: %s\n", NetMngr
.getConnectionStateCStr());
531 str
+= toString("LocalAddress: %s\n", NetMngr
.getAddress().asString().c_str());
532 str
+= toString("Language: %s\n", CI18N::getCurrentLanguageName().c_str());
533 str
+= toString("ClientVersion: %s\n", getDebugVersion().c_str());
534 if (ClientCfg
.R2Mode
)
536 str
+= toString("PatchVersion: %s\n", R2ServerVersion
.c_str());
538 else if ((ShardSelected
>= 0) && (ShardSelected
< (sint32
)Shards
.size()))
540 str
+= toString("PatchVersion: %s\n", Shards
[ShardSelected
].Version
.c_str());
542 str
+= string("Client is ") + string((ClientCfg
.Local
?"off":"on")) + string("line\n");
543 // FarTP/ReselectPerso
544 DebugConnectionHistory
.debugDisplayConnectionEvent(str
);
549 void resetIngameTime ()
551 IngameEnterTime
= T1
;
554 uint64
ingameTime0 ()
556 return T0
- IngameEnterTime
;
559 uint64
ingameTime1 ()
561 return T1
- IngameEnterTime
;
564 // ***************************************************************************
566 void displayNetDebug ()
568 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
569 float lineStep
= ClientCfg
.DebugLineStep
;
574 // Create a shadow when displaying a text.
575 TextContext
->setShaded(true);
576 TextContext
->setShadeOutline(false);
577 // Set the font size.
578 TextContext
->setFontSize(ClientCfg
.DebugFontSize
);
579 // Set the text color
580 TextContext
->setColor(ClientCfg
.DebugFontColor
);
585 TextContext
->setHotSpot(UTextContext::BottomRight
);
587 // Database Synchronisation counter
589 uint val
= pIM
->getLocalSyncActionCounter() ;
590 val
&= pIM
->getLocalSyncActionCounterMask();
591 TextContext
->printfAt(1.f
, line
, "Local Counter: %d", val
);
594 val
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:INVENTORY:COUNTER")->getValue32();
595 val
&= pIM
->getLocalSyncActionCounterMask();
596 TextContext
->printfAt(1.f
, line
, "INVENTORY:COUNTER: %d", val
);
599 val
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:EXCHANGE:COUNTER")->getValue32();
600 val
&= pIM
->getLocalSyncActionCounterMask();
601 TextContext
->printfAt(1.f
, line
, "EXCHANGE:COUNTER: %d", val
);
604 val
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:TARGET:CONTEXT_MENU:COUNTER")->getValue32();
605 val
&= pIM
->getLocalSyncActionCounterMask();
606 TextContext
->printfAt(1.f
, line
, "TARGET:CONTEXT_MENU:COUNTER: %d", val
);
609 val
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:COUNTER")->getValue32();
610 val
&= pIM
->getLocalSyncActionCounterMask();
611 TextContext
->printfAt(1.f
, line
, "USER:COUNTER: %d", val
);
615 // SPhrase Execution Synchronisation Counter
616 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
618 uint srvVal
= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_COUNTER_NEXT
)->getValue32();
619 uint locVal
= pPM
->getPhraseNextExecuteCounter() ;
620 srvVal
&= PHRASE_EXECUTE_COUNTER_MASK
;
621 locVal
&= PHRASE_EXECUTE_COUNTER_MASK
;
622 TextContext
->printfAt(1.f
, line
, "NextAction (loc/srv): %d/%d", locVal
, srvVal
);
625 srvVal
= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_COUNTER_CYCLE
)->getValue32();
626 locVal
= pPM
->getPhraseCycleExecuteCounter();
627 srvVal
&= PHRASE_EXECUTE_COUNTER_MASK
;
628 locVal
&= PHRASE_EXECUTE_COUNTER_MASK
;
629 TextContext
->printfAt(1.f
, line
, "CycleAction (loc/srv): %d/%d", locVal
, srvVal
);
635 TextContext
->setHotSpot(UTextContext::BottomLeft
);
637 float xWatched
= 0.5f
;
638 // Display information about the debug entity slot.
639 if(WatchedEntitySlot
!= CLFECOMMON::INVALID_SLOT
)
642 TextContext
->printfAt(xWatched
, line
, "Watched");
644 // Get a pointer on the target.
645 CEntityCL
*watchedEntity
= EntitiesMngr
.entity(WatchedEntitySlot
);
647 watchedEntity
->displayDebugPropertyStages(xWatched
, line
, lineStep
);
649 // Display information about the user
653 TextContext
->printfAt(xUser
, line
, "User");
655 UserEntity
->displayDebugPropertyStages(xUser
, line
, lineStep
);
660 // ***************************************************************************
661 bool verboseVPAdvanceTest(CEntityCL
*en
, uint32 form
)
663 // TestYoyo: Use this method to test only a part of the entities
664 // was used initialy to debug mektoub mounts bugs.
669 if( NetMngr
.getUserId()!=1507 )
672 // creation test (by form to create)
675 CSheetId
sheetId(form
);
676 CSheetId
playerSheetId("matis.race_stats");
677 CSheetId
mektoubSheetId("chilb2.creature");
678 return (sheetId
==playerSheetId
|| sheetId
==mektoubSheetId
);
680 // update vp or remove test
683 CCharacterCL
*e
= dynamic_cast<CCharacterCL
*>(en
);
686 if( e
->isPlayer() || e
->getSheet()->Id
== CSheetId("chilb2.creature") )