Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+848
View File
@@ -0,0 +1,848 @@
#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();
}