849 lines
21 KiB
C++
849 lines
21 KiB
C++
#include "console.h"
|
|
#include "threads.h"
|
|
#include "str_util.h"
|
|
#include "render/driver.h"
|
|
#include "render/render_state.h"
|
|
#include "main/references.h"
|
|
#include "main/logging.h"
|
|
#include "util/format.h"
|
|
#include "profile/keybinds.h"
|
|
#include "network/network_manager.h"
|
|
#include "render/vertexBuffer.h"
|
|
|
|
threads::Mutex consoleLock;
|
|
Console console;
|
|
|
|
extern double menuTickTime, serverTickTime, clientTickTime, animationTime;
|
|
const size_t maxUndoSize = 50;
|
|
int baseTimer = 0;
|
|
|
|
enum LiveStat {
|
|
LS_Menu = 1,
|
|
LS_Server = 2,
|
|
LS_Client = 4,
|
|
LS_Tick = 8,
|
|
LS_Buffers = 16,
|
|
LS_FPS = 32,
|
|
LS_Bandwidth = 64
|
|
};
|
|
|
|
void asGet(const std::string& strEngine, const std::string& strModule,
|
|
asIScriptEngine*& sEngine, asIScriptModule*& sModule) {
|
|
|
|
//Find engine
|
|
scripts::Manager* man = 0;
|
|
if(strEngine == "menu")
|
|
man = devices.scripts.menu;
|
|
else if(strEngine == "client")
|
|
man = devices.scripts.client;
|
|
else if(strEngine == "server")
|
|
man = devices.scripts.server;
|
|
else
|
|
return;
|
|
|
|
//Find module
|
|
sEngine = man->engine;
|
|
|
|
if(strModule.empty()) {
|
|
sModule = sEngine->GetModule("ConsoleInput", asGM_ALWAYS_CREATE);
|
|
}
|
|
else {
|
|
scripts::Module* module = man->getModule(strModule.c_str());
|
|
|
|
if(!module)
|
|
return;
|
|
|
|
sModule = module->module;
|
|
}
|
|
}
|
|
|
|
void asGlobal(const std::string& strEngine, const std::string& strModule, const std::string& global) {
|
|
asIScriptEngine* sEngine = 0;
|
|
asIScriptModule* sModule = 0;
|
|
|
|
asGet(strEngine, strModule, sEngine, sModule);
|
|
|
|
if(!sEngine || !sModule)
|
|
return;
|
|
|
|
std::string code = global;
|
|
if(code[code.size() - 1] != ';')
|
|
code += ";";
|
|
|
|
sModule->CompileGlobalVar("ConsoleGlobal", code.c_str(), 0);
|
|
}
|
|
|
|
void asExec(const std::string& strEngine, const std::string& strModule, const std::string& code)
|
|
{
|
|
int r;
|
|
|
|
asIScriptEngine* sEngine = 0;
|
|
asIScriptModule* sModule = 0;
|
|
|
|
asGet(strEngine, strModule, sEngine, sModule);
|
|
|
|
if(!sEngine || !sModule)
|
|
return;
|
|
|
|
//Wrap code
|
|
std::string fCode;
|
|
|
|
if(code.find(";") == std::string::npos) {
|
|
fCode = "void ConsoleFunction() { print(";
|
|
fCode += code;
|
|
fCode += "); }";
|
|
}
|
|
else {
|
|
fCode = "void ConsoleFunction() { ";
|
|
fCode += code;
|
|
fCode += "; }";
|
|
}
|
|
|
|
//Compile function
|
|
asIScriptFunction* sFunc = 0;
|
|
r = sModule->CompileFunction("ConsoleFunction", fCode.c_str(), -1, 0, &sFunc);
|
|
if(r < 0)
|
|
return;
|
|
|
|
//Prepare context
|
|
asIScriptContext* ctx = sEngine->CreateContext();
|
|
r = ctx->Prepare(sFunc);
|
|
|
|
if(r >= 0) {
|
|
//Execute
|
|
r = ctx->Execute();
|
|
}
|
|
|
|
//Clean
|
|
sFunc->Release();
|
|
ctx->Release();
|
|
}
|
|
|
|
|
|
Console::Console() : open(false), permaStats(false), bg(new render::RenderState), compact(false), historyItem(history.cbegin()), asMode(false), keybind(0), liveStats(~0) {
|
|
bg->baseMat = render::MAT_Alpha;
|
|
bg->lighting = false;
|
|
bg->culling = render::FC_None;
|
|
bg->depthTest = render::DT_NoDepthTest;
|
|
|
|
addCommand("clear", [this](argList&) {
|
|
clear();
|
|
} );
|
|
|
|
addCommand("list", [this](argList&) {
|
|
std::string temp;
|
|
for(auto i = functions.cbegin(), end = functions.cend(); i != end; ++i) {
|
|
if(temp.empty()) {
|
|
temp = i->first;
|
|
}
|
|
else {
|
|
temp.insert(temp.size(), 16 - (temp.size() % 16), ' ');
|
|
temp += i->first;
|
|
if(temp.size() >= 16*5) {
|
|
printLn(temp);
|
|
temp.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!temp.empty())
|
|
printLn(temp);
|
|
} );
|
|
|
|
addCommand("resize", [this](argList& args) {
|
|
if(args.size() != 2) {
|
|
error("Usage: resize [width] [height]");
|
|
return;
|
|
}
|
|
|
|
devices.driver->setWindowSize(
|
|
toNumber<int>(args[0]),
|
|
toNumber<int>(args[1]));
|
|
} );
|
|
|
|
addCommand("compact", [this](argList& args) {
|
|
if(args.empty())
|
|
compact = !compact;
|
|
else if(streq_nocase(args[0], "true"))
|
|
compact = true;
|
|
else if(streq_nocase(args[0], "false"))
|
|
compact = false;
|
|
} );
|
|
|
|
addCommand("stats", [this](argList& args) {
|
|
if(args.empty())
|
|
permaStats = !permaStats;
|
|
else
|
|
permaStats = toBool(args[0]);
|
|
} );
|
|
|
|
addCommand("show", [this](argList& args) {
|
|
if(args.empty()) {
|
|
printLn("Toggles the showing of live data at the right");
|
|
printLn("Choose which piece of data to toggle: all, fps, menu, server, client, tick, buffers, bandwidth");
|
|
return;
|
|
}
|
|
|
|
auto& arg = args[0];
|
|
if(arg == "all") {
|
|
//Toggle all off if any are on, otherwise show all
|
|
liveStats = liveStats ? 0 : ~0;
|
|
}
|
|
else if(arg == "fps") {
|
|
liveStats ^= LS_FPS;
|
|
}
|
|
else if(arg == "menu") {
|
|
liveStats ^= LS_Menu;
|
|
}
|
|
else if(arg == "server") {
|
|
liveStats ^= LS_Server;
|
|
}
|
|
else if(arg == "client") {
|
|
liveStats ^= LS_Client;
|
|
}
|
|
else if(arg == "tick") {
|
|
liveStats ^= LS_Tick;
|
|
}
|
|
else if(arg == "buffers") {
|
|
liveStats ^= LS_Buffers;
|
|
}
|
|
else if(arg == "bandwidth") {
|
|
liveStats ^= LS_Bandwidth;
|
|
}
|
|
} );
|
|
|
|
addCommand("bind", [this](argList& args) {
|
|
if(args.size() > 1) {
|
|
int key = profile::getKeyFromDisplayName(args[0]);
|
|
if(key == 0) {
|
|
printLn(format("Unrecognized key '$1'", args[0]));
|
|
return;
|
|
}
|
|
else {
|
|
std::string command;
|
|
for(size_t i = 1, cnt = args.size(); i < cnt; ++i) {
|
|
if(!command.empty())
|
|
command += " ";
|
|
if(args[i].find(' ') != std::string::npos) {
|
|
command += "\"";
|
|
command += args[i];
|
|
command += "\"";
|
|
}
|
|
else {
|
|
command += args[i];
|
|
}
|
|
}
|
|
|
|
binds[key] = command;
|
|
}
|
|
}
|
|
else {
|
|
printLn("Bind requires a key and a command");
|
|
}
|
|
} );
|
|
|
|
addCommand("in", [this](argList& args) {
|
|
if(args.empty()) {
|
|
asModule = "";
|
|
asEngine = "client";
|
|
}
|
|
else {
|
|
if(args.size() == 1) {
|
|
asModule = args[0];
|
|
if(devices.scripts.menu && devices.scripts.menu->getModule(asModule.c_str())) {
|
|
asEngine = "menu";
|
|
}
|
|
else if(devices.scripts.client && devices.scripts.client->getModule(asModule.c_str())) {
|
|
asEngine = "client";
|
|
}
|
|
else if(devices.scripts.server && devices.scripts.server->getModule(asModule.c_str())) {
|
|
asEngine = "server";
|
|
}
|
|
else {
|
|
std::string temp;
|
|
temp += "Error: Could not find angelscript module '";
|
|
temp += asModule;
|
|
temp += "' in any engine.";
|
|
|
|
printLn(temp);
|
|
return;
|
|
}
|
|
}
|
|
else if(args.size() == 2) {
|
|
asEngine = args[0];
|
|
asModule = args[1];
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::string temp;
|
|
temp += "Setting console to angelscript mode for module '";
|
|
temp += asModule;
|
|
temp += "' in engine '";
|
|
temp += asEngine;
|
|
temp += "'.\n Type 'quit' to return to normal mode.";
|
|
|
|
printLn(temp);
|
|
asMode = true;
|
|
} );
|
|
}
|
|
|
|
Console::~Console() {
|
|
delete bg;
|
|
}
|
|
|
|
void Console::addCommand(std::string name, std::function<void(std::vector<std::string>&)> function, bool replace) {
|
|
toLowercase(name);
|
|
if(replace || functions.find(name) == functions.end()) {
|
|
ConsoleFunction& f = functions[name];
|
|
f.line = nullptr;
|
|
f.call = function;
|
|
f.destruct = nullptr;
|
|
}
|
|
else
|
|
error("Duplicate console command '%s'", name.c_str());
|
|
}
|
|
|
|
void Console::addCommand(std::string name, ConsoleFunction& func, bool replace) {
|
|
toLowercase(name);
|
|
if(replace || functions.find(name) == functions.end())
|
|
functions[name] = func;
|
|
else
|
|
error("Duplicate console command '%s'", name.c_str());
|
|
}
|
|
|
|
void Console::clearCommands() {
|
|
for(auto it = functions.begin(); it != functions.end();) {
|
|
if(it->second.destruct) {
|
|
if(it->second.destruct()) {
|
|
it = functions.erase(it);
|
|
continue;
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
bool Console::eraseSelection() {
|
|
if(length != 0) {
|
|
if(length > 0)
|
|
currentLine.erase(caret, length);
|
|
else {
|
|
currentLine.erase(caret+length, -length);
|
|
caret += length;
|
|
}
|
|
length = 0;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Console::character(int code) {
|
|
if(!open)
|
|
return false;
|
|
if(devices.keybinds.global.getBind(code) == keybind)
|
|
return false;
|
|
if(devices.driver->ctrlKey)
|
|
return true;
|
|
|
|
undo.push_back( std::tuple<std::string, size_t, int>(currentLine, caret, length) );
|
|
|
|
eraseSelection();
|
|
|
|
currentLine.insert(caret++, 1, (char)code);
|
|
return true;
|
|
}
|
|
|
|
bool Console::globalKey(int code, bool pressed) {
|
|
if(open)
|
|
return false;
|
|
|
|
auto bind = binds.find(profile::getModifiedKey(code, devices.driver->ctrlKey, devices.driver->altKey, devices.driver->shiftKey));
|
|
if(bind != binds.end()) {
|
|
if(pressed)
|
|
execute(bind->second, false);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Console::key(int code, bool pressed) {
|
|
if(!open)
|
|
return false;
|
|
if(!pressed)
|
|
return true;
|
|
std::tuple<std::string, size_t, int> previous(currentLine, caret, length);
|
|
|
|
switch(code) {
|
|
case 'A': //Select all
|
|
if(devices.driver->ctrlKey) {
|
|
caret = 0;
|
|
length = (int)currentLine.size();
|
|
baseTimer = devices.driver->getTime();
|
|
}
|
|
break;
|
|
case 'K':
|
|
//Kill line
|
|
if(devices.driver->ctrlKey) {
|
|
caret = 0;
|
|
currentLine = "";
|
|
}
|
|
break;
|
|
case 'C':
|
|
case 'X':
|
|
if(devices.driver->ctrlKey) {
|
|
if(length > 0)
|
|
devices.driver->setClipboard(currentLine.substr(caret, length));
|
|
else
|
|
devices.driver->setClipboard(currentLine.substr(caret+length, -length));
|
|
if(code == 'X') {
|
|
eraseSelection();
|
|
baseTimer = devices.driver->getTime();
|
|
}
|
|
}
|
|
break;
|
|
case 'V':
|
|
if(devices.driver->ctrlKey) {
|
|
eraseSelection();
|
|
std::string text = devices.driver->getClipboard();
|
|
currentLine.insert(caret, text);
|
|
baseTimer = devices.driver->getTime();
|
|
caret += text.size();
|
|
}
|
|
break;
|
|
case 'Y':
|
|
if(devices.driver->ctrlKey) {
|
|
if(!redo.empty()) {
|
|
undo.push_back(previous);
|
|
if(undo.size() > maxUndoSize)
|
|
undo.pop_front();
|
|
|
|
auto& prev = redo.back();
|
|
currentLine = std::get<0>(prev);
|
|
caret = std::get<1>(prev);
|
|
length = std::get<2>(prev);
|
|
|
|
redo.pop_back();
|
|
}
|
|
baseTimer = devices.driver->getTime();
|
|
return true; //Return here to prevent handling the line change by the undo system
|
|
}
|
|
break;
|
|
case 'Z':
|
|
if(devices.driver->ctrlKey) {
|
|
if(!undo.empty()) {
|
|
redo.push_back(previous);
|
|
if(redo.size() > maxUndoSize)
|
|
redo.pop_front();
|
|
|
|
auto& prev = undo.back();
|
|
currentLine = std::get<0>(prev);
|
|
caret = std::get<1>(prev);
|
|
length = std::get<2>(prev);
|
|
|
|
undo.pop_back();
|
|
}
|
|
baseTimer = devices.driver->getTime();
|
|
return true; //Return here to prevent handling the line change by the undo system
|
|
}
|
|
break;
|
|
case os::KEY_BACKSPACE:
|
|
if(!eraseSelection() && caret > 0)
|
|
currentLine.erase(--caret,1);
|
|
baseTimer = devices.driver->getTime();
|
|
break;
|
|
case os::KEY_DEL:
|
|
if(!eraseSelection() && caret < currentLine.size())
|
|
currentLine.erase(caret,1);
|
|
baseTimer = devices.driver->getTime();
|
|
break;
|
|
case os::KEY_LEFT:
|
|
if(caret > 0) {
|
|
caret -= 1;
|
|
if(devices.driver->shiftKey)
|
|
length += 1;
|
|
else
|
|
length = 0;
|
|
}
|
|
else if(!devices.driver->shiftKey) {
|
|
length = 0;
|
|
}
|
|
baseTimer = devices.driver->getTime();
|
|
break;
|
|
case os::KEY_RIGHT:
|
|
if(caret < currentLine.size()) {
|
|
caret += 1;
|
|
if(devices.driver->shiftKey)
|
|
length -= 1;
|
|
else
|
|
length = 0;
|
|
}
|
|
else if(!devices.driver->shiftKey) {
|
|
length = 0;
|
|
}
|
|
baseTimer = devices.driver->getTime();
|
|
break;
|
|
case os::KEY_HOME:
|
|
if(caret > 0) {
|
|
if(devices.driver->shiftKey)
|
|
length += (int)caret;
|
|
else
|
|
length = 0;
|
|
caret = 0;
|
|
}
|
|
else if(!devices.driver->shiftKey) {
|
|
length = 0;
|
|
}
|
|
baseTimer = devices.driver->getTime();
|
|
break;
|
|
case os::KEY_END:
|
|
if(caret < currentLine.size()) {
|
|
if(devices.driver->shiftKey)
|
|
length -= (int)(currentLine.size() - caret);
|
|
else
|
|
length = 0;
|
|
caret = currentLine.size();
|
|
}
|
|
else if(!devices.driver->shiftKey) {
|
|
length = 0;
|
|
}
|
|
baseTimer = devices.driver->getTime();
|
|
break;
|
|
case os::KEY_ENTER:
|
|
if(!currentLine.empty()) {
|
|
execute(currentLine, true);
|
|
history.push_front(currentLine);
|
|
historyItem = history.cend();
|
|
currentLine.clear();
|
|
caret = 0;
|
|
length = 0;
|
|
}
|
|
baseTimer = devices.driver->getTime();
|
|
break;
|
|
case os::KEY_UP:
|
|
if(!history.empty()) {
|
|
if(historyItem != history.cend())
|
|
++historyItem;
|
|
else
|
|
historyItem = history.cbegin();
|
|
|
|
if(historyItem != history.end())
|
|
currentLine = *historyItem;
|
|
else
|
|
currentLine.clear();
|
|
caret = currentLine.size();
|
|
length = 0;
|
|
}
|
|
break;
|
|
case os::KEY_DOWN:
|
|
if(!history.empty()) {
|
|
if(historyItem != history.cbegin())
|
|
--historyItem;
|
|
else
|
|
historyItem = history.cend();
|
|
|
|
if(historyItem != history.end())
|
|
currentLine = *historyItem;
|
|
else
|
|
currentLine.clear();
|
|
caret = currentLine.size();
|
|
length = 0;
|
|
}
|
|
break;
|
|
case os::KEY_TAB:
|
|
if(!currentLine.empty()) {
|
|
size_t space = currentLine.find(' ');
|
|
if(space == std::string::npos) {
|
|
const std::string* bestMatch = 0;
|
|
unsigned bestScore = (unsigned)-1;
|
|
|
|
for(auto i = functions.cbegin(), end = functions.cend(); i != end; ++i) {
|
|
size_t p = i->first.find(currentLine);
|
|
if(p == std::string::npos)
|
|
continue;
|
|
unsigned score = (unsigned)((p * 4) + (i->first.size() - currentLine.size()));
|
|
if(bestScore < score)
|
|
continue;
|
|
bestScore = score;
|
|
bestMatch = &i->first;
|
|
}
|
|
|
|
if(bestMatch) {
|
|
currentLine = *bestMatch + (char)' ';
|
|
caret = currentLine.size();
|
|
length = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//If the line has changed, store the previous state as an undo entry, and clear the redo list
|
|
if(std::get<0>(previous) != currentLine) {
|
|
redo.clear();
|
|
undo.push_back(previous);
|
|
if(undo.size() > maxUndoSize)
|
|
undo.pop_front();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Console::printLn(const std::string& line) {
|
|
consoleLock.lock();
|
|
if(line.find('\n') == std::string::npos)
|
|
lines.push_back(line);
|
|
else {
|
|
std::vector<std::string> split_lines;
|
|
split(line, split_lines, '\n');
|
|
for(unsigned i = 0; i < split_lines.size(); ++i)
|
|
lines.push_back(split_lines[i]);
|
|
}
|
|
consoleLock.release();
|
|
}
|
|
|
|
void Console::clear() {
|
|
consoleLock.lock();
|
|
lines.clear();
|
|
consoleLock.release();
|
|
}
|
|
|
|
void Console::execute(const std::string& command, bool echo) {
|
|
if(asMode) {
|
|
if(echo)
|
|
printLn(std::string("# ") + command);
|
|
|
|
if(command == "quit" || command == "q") {
|
|
asMode = false;
|
|
}
|
|
else if(command.compare(0, 7, "global ") == 0) {
|
|
asGlobal(asEngine, asModule, command.substr(7));
|
|
}
|
|
else {
|
|
asExec(asEngine, asModule, command);
|
|
}
|
|
}
|
|
else {
|
|
auto argStart = command.find_first_of(" \t");
|
|
std::string function = command.substr(0, argStart);
|
|
toLowercase(function);
|
|
|
|
auto func = functions.find(function);
|
|
if(func == functions.end()) {
|
|
if(echo)
|
|
printLn(std::string("Unrecognized command '") + function + "'");
|
|
return;
|
|
}
|
|
|
|
if(echo)
|
|
printLn(std::string("> ") + command);
|
|
|
|
if(func->second.line) {
|
|
std::string line;
|
|
if(argStart != std::string::npos)
|
|
line = command.substr(argStart + 1);
|
|
|
|
if(func->second.line(line))
|
|
return;
|
|
}
|
|
|
|
if(func->second.call) {
|
|
std::vector<std::string> args;
|
|
if(argStart != std::string::npos) {
|
|
auto end = argStart;
|
|
while(end != std::string::npos) {
|
|
auto start = command.find_first_not_of(" \t", end);
|
|
if(start == std::string::npos)
|
|
break;
|
|
if(command[start] == '\"') {
|
|
start += 1;
|
|
end = command.find('\"', start);
|
|
}
|
|
else {
|
|
end = command.find(' ', start);
|
|
}
|
|
|
|
args.push_back(command.substr(start, end-start));
|
|
}
|
|
}
|
|
|
|
func->second.call(args);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Console::executeFile(const std::string& filename) {
|
|
std::ifstream stream(filename);
|
|
std::string line;
|
|
while(stream.is_open() && stream.good()) {
|
|
std::getline(stream, line);
|
|
execute(line, false);
|
|
}
|
|
}
|
|
|
|
void Console::show() {
|
|
open = true;
|
|
}
|
|
|
|
void Console::toggle() {
|
|
open = !open;
|
|
}
|
|
|
|
Color bgCols[4] = {Color(0,0,0,230), Color(0,0,0,230), Color(0,0,0,230), Color(0,0,0,230) };
|
|
Color selCols[4] = {Color(0,0,255,80), Color(0,0,255,80), Color(0,0,255,80), Color(0,0,255,80) };
|
|
|
|
void Console::render(render::RenderDriver& driver) {
|
|
if(!open && !permaStats)
|
|
return;
|
|
vec2i screenSize(devices.driver->win_width,devices.driver->win_height);
|
|
if(screenSize.width == 0 || screenSize.height == 0)
|
|
return;
|
|
|
|
if(open && !compact)
|
|
driver.drawRectangle(recti(vec2i(0,0), screenSize), bg, 0, bgCols);
|
|
|
|
const gui::skin::Skin& skin = devices.library["Debug"];
|
|
auto& font = skin.getFont(gui::skin::getFontIndex("Normal"));
|
|
int lineHeight = font.getLineHeight();
|
|
|
|
if(!compact && (open || permaStats)) {
|
|
//FPS indicator
|
|
const float average_pct = 0.15f;
|
|
static float fps = 60;
|
|
static float tps = 4.f;
|
|
|
|
extern double realFrameLen, animation_s, render_s, present_s;
|
|
extern double prevTick_s;
|
|
|
|
int y = 1;
|
|
|
|
if(liveStats & LS_FPS) {
|
|
if(realFrameLen > 0.00001) {
|
|
fps = (1.f - average_pct) * fps + (average_pct * (float)(1.0 / realFrameLen));
|
|
|
|
if(prevTick_s > 0.00001) {
|
|
double gs = devices.driver->getGameSpeed();
|
|
if(gs > 0)
|
|
tps = (1.f - average_pct) * tps + (average_pct * (float)(1.0 / (prevTick_s / gs)));
|
|
}
|
|
|
|
char buffer[256];
|
|
sprintf(buffer, "%.1ftps %.1ffps (a=%4.1fms r=%4.1fms p=%4.1fms)", tps, fps, animation_s*1000.0, render_s*1000.0, present_s*1000.0);
|
|
font.draw(devices.render, buffer, screenSize.width - font.getDimension(buffer).width, 1);
|
|
}
|
|
else {
|
|
font.draw(devices.render, "inf fps", screenSize.width - font.getDimension("inf fps").width ,1);
|
|
}
|
|
|
|
y += 39;
|
|
}
|
|
|
|
int x = screenSize.width - 260;
|
|
|
|
if(liveStats & LS_FPS) {
|
|
devices.render->drawFPSGraph(recti(vec2i(x,y), vec2i(x + 240, y+60)));
|
|
y += 60 + lineHeight;
|
|
}
|
|
|
|
asUINT gc_size, gc_destroyed, gc_detected, gc_new, gc_newDestroyed;
|
|
|
|
if(devices.scripts.menu && (liveStats & LS_Menu)) {
|
|
devices.scripts.menu->engine->GetGCStatistics(&gc_size, &gc_destroyed, &gc_detected, &gc_new, &gc_newDestroyed);
|
|
std::string gc_state = format("Menu GC Entities: $1\n Destroyed: $2\n Detected: $3\n New: $4\n New Destroyed: $5", gc_size, gc_destroyed, gc_detected, gc_new, gc_newDestroyed);
|
|
font.draw(devices.render, gc_state.c_str(), x, y);
|
|
y += lineHeight * 6;
|
|
}
|
|
|
|
if(devices.scripts.client && (liveStats & LS_Client)) {
|
|
devices.scripts.client->engine->GetGCStatistics(&gc_size, &gc_destroyed, &gc_detected, &gc_new, &gc_newDestroyed);
|
|
std::string gc_state = format("Client GC Entities: $1\n Destroyed: $2\n Detected: $3\n New: $4\n New Destroyed: $5", gc_size, gc_destroyed, gc_detected, gc_new, gc_newDestroyed);
|
|
font.draw(devices.render, gc_state.c_str(), x, y);
|
|
y += lineHeight * 6;
|
|
}
|
|
|
|
if(devices.scripts.server && (liveStats & LS_Server)) {
|
|
devices.scripts.server->engine->GetGCStatistics(&gc_size, &gc_destroyed, &gc_detected, &gc_new, &gc_newDestroyed);
|
|
std::string gc_state = format("Server GC Entities: $1\n Destroyed: $2\n Detected: $3\n New: $4\n New Destroyed: $5", gc_size, gc_destroyed, gc_detected, gc_new, gc_newDestroyed);
|
|
font.draw(devices.render, gc_state.c_str(), x, y);
|
|
y += lineHeight * 6;
|
|
}
|
|
|
|
if(liveStats & LS_Tick) {
|
|
static double lastServerTick = 0, lastClientTick = 0;
|
|
if(serverTickTime > 0.0001)
|
|
lastServerTick = serverTickTime;
|
|
if(clientTickTime > 0.0001)
|
|
lastClientTick = clientTickTime;
|
|
|
|
std::string tickTimes = format("Server Script Tick: $1ms\nClient Script Tick: $2ms", toString(lastServerTick * 1000.0,2), toString(lastClientTick * 1000.0,2));
|
|
font.draw(devices.render, tickTimes.c_str(), x, y);
|
|
y += lineHeight * 3;
|
|
}
|
|
|
|
if(liveStats & LS_Buffers) {
|
|
extern unsigned drawnSteps, bufferFlushes;
|
|
std::string vbData = format("VB Steps: $1\nVB Flushes: $2\n Verts= $3\n Steps= $4\n Shader= $5", toString(drawnSteps), toString(bufferFlushes),
|
|
toString(render::vbFlushCounts[render::FC_VertexLimit]), toString(render::vbFlushCounts[render::FC_StepLimit]), toString(render::vbFlushCounts[render::FC_ShaderLimit]));
|
|
font.draw(devices.render, vbData.c_str(), x, y);
|
|
y += lineHeight * 6;
|
|
}
|
|
|
|
if(liveStats & LS_Bandwidth && devices.network->monitorBandwidth) {
|
|
std::string bwData = format("Incoming: $1/s\nOutgoing: $2/s\nQueued: $3",
|
|
toSize(devices.network->currentIncoming), toSize(devices.network->currentOutgoing), toString(devices.network->queuedPackets));
|
|
font.draw(devices.render, bwData.c_str(), x, y);
|
|
y += lineHeight * 4;
|
|
}
|
|
}
|
|
|
|
if(!open)
|
|
return;
|
|
|
|
int y = screenSize.height - (2 * lineHeight);
|
|
|
|
consoleLock.lock();
|
|
vec2i lineStart;
|
|
|
|
if(asMode) {
|
|
lineStart = font.getDimension("# ");
|
|
font.draw(&driver, "# ", 0, y+lineHeight);
|
|
}
|
|
else {
|
|
lineStart = font.getDimension("> ");
|
|
font.draw(&driver, "> ", 0, y+lineHeight);
|
|
}
|
|
|
|
font.draw(&driver, currentLine.c_str(), lineStart.width, y+lineHeight);
|
|
if(length != 0) {
|
|
driver.switchToRenderState(*bg);
|
|
|
|
vec2i corner(lineStart.x, y+lineHeight);
|
|
vec2i size;
|
|
if(length > 0) {
|
|
corner.x += font.getDimension(currentLine.substr(0, caret).c_str()).width;
|
|
size = font.getDimension(currentLine.substr(caret, length).c_str());
|
|
}
|
|
else {
|
|
corner.x += font.getDimension(currentLine.substr(0, caret+length).c_str()).width;
|
|
size = font.getDimension(currentLine.substr(caret+length, -length).c_str());
|
|
}
|
|
|
|
driver.drawRectangle(recti(corner, corner + size), bg, 0, selCols);
|
|
}
|
|
|
|
if((devices.driver->getTime() - baseTimer) % 2000 < 1000) {
|
|
int caret_x = font.getDimension(currentLine.substr(0, caret).c_str()).width + lineStart.width;
|
|
font.drawChar(&driver, '|', '\0', caret_x - 4, y+lineHeight);
|
|
}
|
|
|
|
if(!compact)
|
|
for(auto line = lines.rbegin(), end = lines.rend(); line != end && (y+lineHeight) > 0; ++line, y-=lineHeight)
|
|
font.draw(&driver, line->c_str(), 0, y);
|
|
consoleLock.release();
|
|
}
|