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();
}
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include <functional>
#include <unordered_map>
#include <vector>
#include <list>
#include <string>
#include <tuple>
namespace render {
class RenderDriver;
struct RenderState;
};
namespace profile {
struct Keybind;
};
typedef std::vector<std::string> argList;
class Console {
public:
struct ConsoleFunction {
std::function<bool(std::string&)> line;
std::function<void(argList&)> call;
std::function<bool()> destruct;
};
private:
std::unordered_map<std::string, ConsoleFunction> functions;
std::unordered_map<int, std::string> binds;
std::list<std::string> lines, history;
std::list<std::string>::const_iterator historyItem;
std::list<std::tuple<std::string, size_t, int>> undo, redo;
std::string currentLine;
size_t caret;
int length;
bool open;
bool permaStats;
render::RenderState *bg;
bool compact;
unsigned liveStats;
bool asMode;
std::string asModule;
std::string asEngine;
bool eraseSelection();
public:
void addCommand(std::string name, std::function<void(argList&)> function, bool replace = false);
void addCommand(std::string name, ConsoleFunction& func, bool replace = false);
void clearCommands();
void printLn(const std::string& line);
void execute(const std::string& command, bool echo);
void executeFile(const std::string& filename);
profile::Keybind* keybind;
void toggle();
void show();
void clear();
Console();
~Console();
bool preRender();
void render(render::RenderDriver& driver);
bool character(int code);
bool key(int code, bool pressed);
bool globalKey(int code, bool pressed);
};
extern Console console;
+106
View File
@@ -0,0 +1,106 @@
#pragma once
#include <string>
#include <stdint.h>
#include <vector>
enum ServerAccess {
SA_Public,
SA_Friends,
SA_Private,
};
struct QueueType {
std::string id;
};
struct CloudDownload {
unsigned long long id;
std::string path;
};
struct OwnedItem {
long long uid, type;
};
//GamePlatform
//Defines various interactions with third party game services (e.g. Steam)
class GamePlatform {
public:
GamePlatform() {}
virtual ~GamePlatform() {}
virtual void update() = 0;
virtual void logException(unsigned code, void* exception, unsigned version, const char* comment) = 0;
//Servers
virtual void announceServer(unsigned ip4, unsigned short port, const std::string& password) = 0;
virtual void announceDisconnect() = 0;
virtual void inviteFriend() = 0;
virtual std::string getLobbyConnectAddress(const std::string& lobbyID, std::string* pwd = nullptr) = 0;
virtual uint64_t getLobby() = 0;
virtual void joinLobby(uint64_t id) = 0;
virtual void enterQueue(const std::string& type, unsigned players, const std::string& version) = 0;
virtual bool inQueue() = 0;
virtual bool queueRequest() = 0;
virtual bool queuePlayerWait(unsigned& ready, unsigned& cap) = 0;
virtual bool queueReady() = 0;
virtual void leaveQueue() = 0;
virtual void acceptQueue() = 0;
virtual void rejectQueue() = 0;
virtual int remainingTime() const = 0;
virtual unsigned queueTotalPlayers() const = 0;
//Achievements/Stats
virtual void modStat(const std::string& id, int delta) = 0;
virtual void modStat(const std::string& id, float delta) = 0;
virtual bool getStat(const std::string& id, int& value) = 0;
virtual bool getStat(const std::string& id, float& value) = 0;
virtual bool getGlobalStat(const std::string& id, long long& value) = 0;
virtual bool getGlobalStat(const std::string& id, double& value) = 0;
virtual void unlockAchievement(const std::string& id) = 0;
//Cloud Files
virtual void addCloudFolder(const std::string& home, const std::string& folder) = 0;
virtual void writeCloudFile(const std::string& filename, const std::string& cloudname) = 0;
//Syncs changed files down from the cloud
virtual void syncCloudFiles(const std::string& home) = 0;
//Clear all files for debug purposes
virtual void flushCloud() = 0;
//User Generated Content
virtual void createCloudItem(const std::string& folder) = 0;
virtual void closeItem() = 0;
virtual void setItemTitle(const std::string& title) = 0;
virtual void setItemDescription(const std::string& desc) = 0;
virtual void setItemTags(const std::vector<std::string>& tags) = 0;
virtual void setItemVisibility() = 0;
virtual void setItemContents(const std::string& folder) = 0;
virtual void setItemImage(const std::string& filename) = 0;
virtual void commitItem(const std::string& changelog) = 0;
virtual double getUploadProgress() = 0;
virtual bool isItemUpdating() = 0;
virtual bool isItemActive() = 0;
virtual unsigned long long getItemID() = 0;
virtual bool getDownloadedItem(unsigned index, CloudDownload& download) = 0;
virtual unsigned getDownloadedItemCount() = 0;
virtual bool hasDLC(const std::string& dlcIdent) const = 0;
//Sell-out API
virtual void playtimeHeartbeat() = 0;
virtual void rewardPlaytime() = 0;
virtual void getOwnedItems(std::vector<OwnedItem>& ids) = 0;
virtual void getAwardedItems(std::vector<long long>& types) = 0;
virtual bool hasItem(long long typeID) = 0;
//Friends
virtual std::string getNickname() = 0;
virtual void openURL(const std::string& url) = 0;
#ifndef NSTEAM
static GamePlatform* acquireSteam();
#endif
};
File diff suppressed because it is too large Load Diff
+66
View File
@@ -0,0 +1,66 @@
#pragma once
#include <string>
#include <functional>
#include <vector>
#include <unordered_map>
//Initialized global data, should be done once per startup
bool initGlobal(bool loadGraphics = true, bool createWindow = true);
void destroyGlobal();
//Creates any thread local variables,
//should be called after initializing global
void initNewThread();
//Frees thread local variables
void cleanupThread();
//Add a cleanup stage to the current thread
void addThreadCleanup(std::function<void()> func);
//Switches over to a different mod
void initMods(const std::vector<std::string>& mods);
void destroyMod();
bool isPreloading();
void finishPreload();
void cancelLoad();
//Switches over to a different locale
void switchLocale(const std::string& locale);
//Initialize game state variables
extern bool game_running;
extern bool load_resources;
extern bool watch_resources;
extern bool use_sound;
extern bool use_steam;
extern bool monitor_files;
extern bool fullscreen;
extern bool useJIT;
extern std::string loadSaveName;
void initGame();
void destroyGame();
namespace net {
struct Message;
};
void setGameSettings(net::Message& msg);
void clearGameSettings();
struct GameConfig {
size_t count;
std::string* names;
double* values;
double* defaultValues;
std::unordered_map<std::string, size_t> indices;
GameConfig() : count(0), names(nullptr), values(nullptr) {
}
};
extern GameConfig gameConfig;
void readGameConfig(net::Message& msg);
void writeGameConfig(net::Message& msg);
class SaveFile;
void saveGameConfig(SaveFile& file);
void loadGameConfig(SaveFile& file);
bool hasDLC(const std::string& name);
+328
View File
@@ -0,0 +1,328 @@
#include "main/references.h"
#include "main/input_handling.h"
#include "main/initialization.h"
#include "main/tick.h"
#include "main/console.h"
#include "os/driver.h"
#include <set>
#include "processing.h"
#define func(x) std::function<decltype(x)>(x)
enum InputCall {
IC_MouseWheel,
IC_MouseDrag,
IC_MouseDragEnd,
IC_MouseMove,
IC_MouseClick,
IC_MouseButton,
IC_CharTyped,
IC_KeyEvent,
IC_DoubleClick,
IC_Overlay,
IC_COUNT
};
const char* InputCallDecls[IC_COUNT] = {
"void onMouseWheel(double,double)",
"void onMouseDragged(int, int, int, int, int)",
"bool onMouseDragEnd(int)",
"void onMouseMoved(int, int)",
"void onMouseClicked(int)",
"bool onMouseButton(int, bool)",
"bool onCharTyped(int)",
"bool onKeyEvent(int, int)",
"void onMouseDoubleClicked(int)",
"void onOverlayChanged(bool)",
};
asIScriptFunction* inputCalls[GS_COUNT][IC_COUNT];
scripts::Manager* manager[GS_COUNT];
INIT_FUNC(clear_input) {
memset(inputCalls, 0, IC_COUNT * GS_COUNT * sizeof(asIScriptFunction*));
memset(manager, 0, GS_COUNT * sizeof(scripts::Manager*));
} INIT_FUNC_END;
void clearInputScripts(GameState state) {
memset(inputCalls[state], 0, IC_COUNT * sizeof(asIScriptFunction*));
manager[state] = 0;
}
void bindInputScripts(GameState state, scripts::Manager* Manager) {
if(Manager)
manager[state] = Manager;
if(!Manager) {
clearInputScripts(state);
return;
}
for(unsigned i = 0; i < IC_COUNT; ++i)
inputCalls[state][i] = manager[state]->getFunction("input", InputCallDecls[i]);
}
void onOverlayToggle(bool state) {
if(manager[game_state] && inputCalls[game_state][IC_Overlay]) {
auto call = manager[game_state]->call(inputCalls[game_state][IC_Overlay]);
call.push(state);
call.call();
}
}
bool onWindowClose() {
game_state = GS_Quit;
return true;
}
bool onCharTyped(int key) {
if(console.character(key))
return true;
//Inform the script
bool ret = true;
if(manager[game_state] && inputCalls[game_state][IC_CharTyped]) {
auto call = manager[game_state]->call(inputCalls[game_state][IC_CharTyped]);
call.push(key);
call.call(ret);
}
return ret;
}
std::set<profile::Keybind*> pressedKeys;
void onModifiersChanged() {
foreach(key, pressedKeys)
(*key)->call(false);
pressedKeys.clear();
}
void clearPressedKeys() {
pressedKeys.clear();
}
bool onKeyEvent(int key, int keyaction) {
bool pressed = keyaction & os::KA_Pressed;
//Figure out if the modifier mask was changed
switch(key) {
case os::KEY_LCTRL:
case os::KEY_RCTRL:
case os::KEY_LALT:
case os::KEY_RALT:
case os::KEY_LSHIFT:
case os::KEY_RSHIFT:
onModifiersChanged();
break;
}
//Build key with modifiers
int mod_key = key;
if(mod_key >= 'A' && mod_key <= 'Z')
mod_key += 'a'-'A';
profile::Keybind* bind = devices.keybinds.global.getBind(mod_key);
if(devices.driver->ctrlKey)
mod_key |= profile::Mod_Ctrl;
if(devices.driver->altKey)
mod_key |= profile::Mod_Alt;
if(devices.driver->shiftKey)
mod_key |= profile::Mod_Shift;
profile::Keybind* mod_bind = devices.keybinds.global.getBind(mod_key);
if(mod_bind)
bind = mod_bind;
if(bind != console.keybind && console.key(key, pressed))
return true;
//Inform the script
if(manager[game_state] && inputCalls[game_state][IC_KeyEvent]) {
bool ret = false;
auto call = manager[game_state]->call(inputCalls[game_state][IC_KeyEvent]);
call.push(key);
call.push(keyaction);
call.call(ret);
if(ret)
return true;
}
if(console.globalKey(key, pressed))
return true;
//Trigger Keybinds
if(bind) {
bind->call(pressed);
if(pressed)
pressedKeys.insert(bind);
else
pressedKeys.erase(bind);
return true;
}
return true;
}
struct MouseKey {
bool pressed;
int pressed_time;
MouseKey() : pressed(false), pressed_time(0) {}
} mouseButtons[3];
int dragging = 0;
int pressed_x = 0, pressed_y = 0;
int cur_x = 0, cur_y = 0;
int tot_drag = 0;
const int dblClickTimeout = 200;
bool onMouseButton(int button, int pressed) {
//When the mouse is being dragged at the system level, don't pass it to the GUI
if(dragging) {
if(pressed) {
dragging |= (0x1 << button);
mouseButtons[button].pressed = true;
}
else {
dragging &= ~(0x1 << button);
mouseButtons[button].pressed = false;
}
if(!dragging) {
devices.driver->setCursorVisible(true);
if(tot_drag > 4) {
tot_drag = 0;
bool resetMouse = true;
if(manager[game_state] && inputCalls[game_state][IC_MouseDragEnd]) {
auto call = manager[game_state]->call(inputCalls[game_state][IC_MouseDragEnd]);
call.push(button);
call.call(resetMouse);
}
if(resetMouse)
devices.driver->setMousePos(pressed_x, pressed_y);
return true;
}
tot_drag = 0;
}
}
if(pressed == 0)
mouseButtons[button].pressed = false;
bool eventAbsorbed = false;
if(manager[game_state]) {
//Pass button event to scripts
if(inputCalls[game_state][IC_MouseButton]) {
auto call = manager[game_state]->call(inputCalls[game_state][IC_MouseButton]);
call.push(button);
call.push(pressed == 1);
call.call(eventAbsorbed);
}
//Pass click events to scripts
if(!pressed) {
int time = devices.driver->getTime();
if(mouseButtons[button].pressed_time > time - dblClickTimeout) {
mouseButtons[button].pressed_time = 0;
if(inputCalls[game_state][IC_DoubleClick]) {
auto call = manager[game_state]->call(inputCalls[game_state][IC_DoubleClick]);
call.push(button);
call.call();
}
}
else {
mouseButtons[button].pressed_time = time;
if(inputCalls[game_state][IC_MouseClick]) {
auto call = manager[game_state]->call(inputCalls[game_state][IC_MouseClick]);
call.push(button);
call.call();
}
}
}
}
if(pressed != 0 && !eventAbsorbed) {
mouseButtons[button].pressed = true;
if(!dragging) {
devices.driver->getMousePos(pressed_x, pressed_y);
cur_x = pressed_x;
cur_y = pressed_y;
}
}
return false;
}
bool onMouseMoved(int x, int y) {
int prevDragging = dragging;
dragging = (mouseButtons[0].pressed ? 0x1 : 0)
| (mouseButtons[1].pressed ? 0x2 : 0)
| (mouseButtons[2].pressed ? 0x4 : 0);
if(dragging) {
if(!prevDragging)
devices.driver->setCursorVisible(false);
if(manager[game_state] && inputCalls[game_state][IC_MouseDrag]) {
int dx = x-cur_x, dy = cur_y-y;
tot_drag += abs(dx) + abs(dy);
if(dx || dy) {
auto call = manager[game_state]->call(inputCalls[game_state][IC_MouseDrag]);
call.push(dragging);
call.push((int)(pressed_x / ui_scale));
call.push((int)(pressed_y / ui_scale));
call.push(dx);
call.push(dy);
call.call();
cur_y = y;
cur_x = x;
}
return true;
}
}
if(manager[game_state] && inputCalls[game_state][IC_MouseMove]) {
//Pass move event to scripts
auto call = manager[game_state]->call(inputCalls[game_state][IC_MouseMove]);
call.push((int)(x / ui_scale));
call.push((int)(y / ui_scale));
call.call();
}
return true;
}
bool onMouseWheel(double x, double y) {
if(manager[game_state] && inputCalls[game_state][IC_MouseWheel]) {
auto call = manager[game_state]->call(inputCalls[game_state][IC_MouseWheel]);
call.push(x);
call.push(y);
call.call();
}
return true;
}
void registerInput() {
//Add base callbacks for os driver
devices.driver->onWindowClose.add(onWindowClose);
//devices.driver->onResize.add(onWindowClose);
devices.driver->onScroll.add(onMouseWheel);
devices.driver->onMouseButton.add(onMouseButton);
devices.driver->onMouseMoved.add(onMouseMoved);
devices.driver->onCharTyped.add(onCharTyped);
devices.driver->onKeyEvent.add(onKeyEvent);
}
void inputTick() {
if(dragging) {
cur_x = pressed_x;
cur_y = pressed_y;
devices.driver->setMousePos(pressed_x, pressed_y);
}
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "main/tick.h"
#include "os/driver.h"
#include "scripts/manager.h"
void onOverlayToggle(bool state);
void registerInput();
void inputTick();
void bindInputScripts(GameState state ,scripts::Manager* manager);
void clearInputScripts(GameState state);
void clearPressedKeys();
+175
View File
@@ -0,0 +1,175 @@
#include "main/logging.h"
#include "main/console.h"
#include "str_util.h"
#include "files.h"
#include <stdio.h>
#include <stdarg.h>
#include <iostream>
#include <fstream>
#include <time.h>
LogLevel level = LL_Warning;
std::ofstream logFile;
std::fstream errorLog;
void createLog() {
logFile.open(path_join(getProfileRoot(), "log.txt"), std::ofstream::trunc | std::ofstream::out);
errorLog.open(path_join(getProfileRoot(), "errors.log.txt"), std::fstream::out | std::fstream::app);
}
void flushLog() {
logFile.flush();
errorLog.flush();
}
void logDate() {
time_t stamp = time(NULL);
struct tm* timeinfo;
timeinfo = localtime(&stamp);
char buffer[125];
strftime(buffer, 124, "%d %b %Y %H:%M:%S\n", timeinfo);
logFile << buffer;
}
void appendToErrorLog(const char* errMsg, bool timestamp, bool separateLogs) {
if(timestamp) {
time_t stamp = time(NULL);
struct tm* timeinfo;
timeinfo = localtime(&stamp);
char buffer[125];
strftime(buffer, 124, "%d %b %Y %H:%M:%S\n", timeinfo);
errorLog << buffer;
}
errorLog << errMsg;
if(separateLogs)
errorLog << "\n\n";
else
errorLog << "\n";
errorLog.flush();
}
void appendToErrorLog(const std::string& errMsg, bool timestamp) {
appendToErrorLog(errMsg.c_str(), timestamp);
}
void storeLog(const std::string& filename) {
//NOTE: This only happens during a crash situation, so we disregard the various possible side effects if something goes wrong
logFile.flush();
logFile.close();
std::string logPath = path_join(getProfileRoot(), "log.txt");
rename(logPath.c_str(), filename.c_str());
}
void setLogLevel(LogLevel level) {
::level = level;
}
LogLevel getLogLevel() {
return level;
}
void print(const std::string& msg) {
printf("%s\n", msg.c_str());
logFile << msg << std::endl;
console.printLn(msg);
}
void print(const char* format, ...) {
va_list ap;
va_start(ap, format);
printv(format, ap);
va_end(ap);
}
void printv(const char* format, va_list ap) {
char line[2048];
vsnprintf(line, 2048, format, ap);
printf("%s\n", line);
logFile << line << std::endl;
console.printLn(line);
}
void info(const std::string& msg) {
if(level >= LL_Info)
print(msg);
}
void info(const char* format, ...) {
va_list ap;
va_start(ap, format);
if(level >= LL_Info)
printv(format, ap);
va_end(ap);
}
void warn(const std::string& msg) {
if(level >= LL_Warning)
print(msg);
}
void warn(const char* format, ...) {
if(level >= LL_Warning) {
va_list ap;
va_start(ap, format);
printv(format, ap);
va_end(ap);
}
}
void error(const std::string& msg) {
if(level >= LL_Error)
print(msg);
}
void error(const char* format, ...) {
if(level >= LL_Error) {
va_list ap;
va_start(ap, format);
printv(format, ap);
va_end(ap);
}
}
void print(LogLevel lvl, const std::string& msg) {
if(level >= lvl)
print(msg);
}
void print(LogLevel lvl, const char* format, ...) {
if(level >= lvl) {
va_list ap;
va_start(ap, format);
printv(format, ap);
va_end(ap);
}
}
const char* sectionNames[NS_COUNT] = {
"Unknown",
"Network",
"Processing",
"Loading",
"Script Tick",
"Render",
"Startup",
"Message Thread",
"Animation"
};
Threaded(NativeSection) threadSection = NS_Unknown;
NativeSection enterSection(NativeSection section) {
auto& sect = threadSection;
NativeSection n = sect;
sect = section;
return n;
}
const char* getSectionName() {
if(threadSection < NS_COUNT)
return sectionNames[threadSection];
else
return sectionNames[NS_Unknown];
}
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "threads.h"
#include <string>
enum LogLevel {
LL_Error,
LL_Warning,
LL_Info,
};
void setLogLevel(LogLevel level);
LogLevel getLogLevel();
void createLog();
void flushLog();
void storeLog(const std::string& filename);
void info(const std::string& msg);
void warn(const std::string& msg);
void error(const std::string& msg);
void info(const char* format, ...);
void warn(const char* format, ...);
void error(const char* format, ...);
void print(const std::string& msg);
void print(const char* format, ...);
void print(LogLevel lvl, const std::string& msg);
void print(LogLevel lvl, const char* format, ...);
void printv(const char* format, va_list ap);
void appendToErrorLog(const std::string& errMsg, bool timestamp = true);
void appendToErrorLog(const char* errMsg, bool timestamp = true, bool separateLogs = true);
void logDate();
enum NativeSection {
NS_Unknown,
NS_Network,
NS_Processing,
NS_Loading,
NS_ScriptTick,
NS_Render,
NS_Startup,
NS_MessageThread,
NS_Animation,
NS_COUNT
};
//Sets the current section, returning the previous section
NativeSection enterSection(NativeSection section);
const char* getSectionName();
+25
View File
@@ -0,0 +1,25 @@
#include "profiler.h"
#include "main/references.h"
#include "main/logging.h"
namespace profiler {
double profileStart() {
return devices.driver->getAccurateTime();
}
void profileEnd(double startTime, const char* section) {
double delta = devices.driver->getAccurateTime() - startTime;
if(delta >= 5e-1)
print("%s: %.1fs\n", section, delta);
else if(delta >= 5e-4)
print("%s: %.1fms\n", section, delta * 1e3);
else if(delta >= 5e-7)
print("%s: %.1fus\n", section, delta * 1e6);
else
print("%s: %.1fns\n", section, delta * 1e9);
}
};
+12
View File
@@ -0,0 +1,12 @@
namespace profiler {
//Returns a value to pass to a call to 'profileEnd'
double profileStart();
//Prints out the time elapsed since 'startTime' as returned by 'profileStart'
void profileEnd(double startTime, const char* section);
#define profile( x, name ) { double _t = profiler::profileStart(); x; profiler::profileEnd(_t, name); }
};
+10
View File
@@ -0,0 +1,10 @@
#include "main/references.h"
references devices;
references::references() : physics(nullptr), nodePhysics(nullptr), cloud(nullptr) {
}
namespace audio {
bool disableSFX = false;
};
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include "render/driver.h"
#include "render/camera.h"
#include "resource/library.h"
#include "scripts/manager.h"
#include "os/driver.h"
#include "mods/mod_manager.h"
#include "profile/keybinds.h"
#include "profile/settings.h"
#include "resource/locale.h"
class Universe;
class NetworkManager;
class PhysicsWorld;
class GamePlatform;
namespace audio {
class ISoundDevice;
extern bool disableSFX;
};
struct references {
os::OSDriver* driver;
resource::Library library;
NetworkManager* network;
GamePlatform* cloud;
render::RenderDriver* render;
scene::Node* scene;
audio::ISoundDevice* sound;
Universe* universe;
PhysicsWorld* physics, *nodePhysics;
resource::Locale locale;
mods::Manager mods;
profile::Keybinds keybinds;
struct {
profile::Settings mod;
profile::Settings engine;
} settings;
struct {
scripts::Manager* server;
scripts::Manager* client;
scripts::Manager* menu;
scripts::Manager* cache_server;
scripts::Manager* cache_shadow;
} scripts;
struct {
asIScriptEngine* server;
asIScriptEngine* client;
asIScriptEngine* menu;
} engines;
references();
};
extern references devices;
+329
View File
@@ -0,0 +1,329 @@
#include "save_load.h"
#include "util/save_file.h"
#include "references.h"
#include "logging.h"
#include "obj/universe.h"
#include "obj/obj_group.h"
#include "empire.h"
#include "physics/physics_world.h"
#include "design/projectiles.h"
#include "main/tick.h"
#include "design/hull.h"
#include "design/subsystem.h"
#include "design/effector.h"
#include "main/initialization.h"
#include <map>
#include <assert.h>
extern std::map<int,ObjectGroup*> groups;
extern int SAVE_VERSION, START_VERSION;
void addSubsystemIdentifiers(SaveFile& file) {
unsigned cnt = getSubsystemDefCount();
for(unsigned i = 0; i < cnt; ++i) {
const SubsystemDef* def = getSubsystemDef(i);
file.addIdentifier(SI_Subsystem, def->index, def->id);
for(unsigned j = 0, jcnt = def->modules.size(); j < jcnt; ++j) {
auto* mod = def->modules[j];
file.addIdentifier(SI_SubsystemModule, mod->umodid, mod->umodident);
}
foreach(it, def->modifierIds) {
auto* mod = it->second;
file.addIdentifier(SI_SubsystemModifier, mod->umodifid, def->id+"::"+it->first);
}
}
for(auto it = shipVariableIndices.begin(), end = shipVariableIndices.end(); it != end; ++it)
file.addIdentifier(SI_ShipVar, it->second, it->first);
for(auto it = variableIndices.begin(), end = variableIndices.end(); it != end; ++it)
file.addIdentifier(SI_SubsystemVar, it->second, it->first);
for(auto it = hexVariableIndices.begin(), end = hexVariableIndices.end(); it != end; ++it)
file.addIdentifier(SI_HexVar, it->second, it->first);
for(auto it = variableIndices.begin(), end = variableIndices.end(); it != end; ++it)
file.addIdentifier(SI_SubsystemVar, it->second, it->first);
for(size_t i = 0, cnt = getEffectorDefinitionCount(); i < cnt; ++i) {
auto* effdef = getEffectorDefinition(i);
file.addIdentifier(SI_Effector, effdef->index, effdef->name);
}
for(size_t i = 0, cnt = getEffectDefinitionCount(); i < cnt; ++i) {
auto* effdef = getEffectDefinition(i);
file.addIdentifier(SI_Effect, effdef->id, effdef->name);
}
}
void addHullIdentifiers(SaveFile& file) {
unsigned cnt = getHullCount();
for(unsigned i = 0; i < cnt; ++i) {
const HullDef* hull = getHullDefinition(i);
file.addIdentifier(SI_Hull, hull->id, hull->ident);
}
}
void addShipsetIdentifiers(SaveFile& file) {
unsigned cnt = getShipsetCount();
for(unsigned i = 0; i < cnt; ++i) {
auto* set = getShipset(i);
file.addIdentifier(SI_Shipset, set->id, set->ident);
}
}
void addDummyIdentifiers(SaveFile& file) {
if(file < SFV_0007) {
unsigned i = 0;
file.addDummyLoadIdentifier(SI_Effector, i++, "CarpetBomb");
file.addDummyLoadIdentifier(SI_Effector, i++, "Railgun");
file.addDummyLoadIdentifier(SI_Effector, i++, "Laser");
file.addDummyLoadIdentifier(SI_Effector, i++, "PurpleLaser");
file.addDummyLoadIdentifier(SI_Effector, i++, "Rockets");
file.addDummyLoadIdentifier(SI_Effector, i++, "Missile");
file.addDummyLoadIdentifier(SI_Effector, i++, "Torpedo");
file.addDummyLoadIdentifier(SI_Effector, i++, "PopulationBomb");
file.addDummyLoadIdentifier(SI_Effector, i++, "WaveBeam");
file.addDummyLoadIdentifier(SI_Effector, i++, "StationArtillery");
}
}
bool saveGame(const std::string& filename) {
//TODO: Handle errors gracefully, or guarantee they cannot occur
try {
SaveFile* pfile = SaveFile::open(filename, SM_Write);
SaveFile& file = *pfile;
if(&file == 0) {
error("Unable to open file '%s'", filename.c_str());
return false;
}
addSubsystemIdentifiers(file);
addHullIdentifiers(file);
addShipsetIdentifiers(file);
devices.scripts.server->saveIdentifiers(file);
file.saveIdentifiers();
file << file.scriptVersion;
file << file.startVersion;
file << (unsigned)devices.mods.activeMods.size();
foreach(it, devices.mods.activeMods) {
file << (*it)->ident;
file << (*it)->version;
}
file.boundary();
saveGameConfig(file);
file.boundary();
file << devices.driver->getGameTime();
Empire::saveEmpires(file);
file.boundary();
saveEffectors(file);
file.boundary();
//Save physics
if(devices.physics) {
file << true;
devices.physics->writeSetup(file);
}
else {
file << false;
}
if(devices.nodePhysics) {
file << true;
devices.nodePhysics->writeSetup(file);
}
else {
file << false;
}
file.boundary();
//Save object id sequences
unsigned typeCount = getScriptObjectTypeCount();
file << typeCount;
for(unsigned i = 0; i < typeCount; ++i) {
auto* type = getScriptObjectType(i);
file << type->nextID.get();
}
file.boundary();
//Save object groups
unsigned groupCount = (unsigned)groups.size();
file << groupCount;
for(auto i = groups.begin(), end = groups.end(); i != end; ++i)
i->second->save(file);
file.boundary();
//Save objects
unsigned objectCount = (unsigned)devices.universe->children.size();
file << objectCount;
for(unsigned i = 0; i < objectCount; ++i)
devices.universe->children[i]->save(file);
file.boundary();
saveProjectiles(file);
file.boundary();
devices.scripts.server->save(file);
if(devices.scripts.client)
devices.scripts.client->save(file);
else
file << "";
file.close();
return true;
}
catch(SaveFileError& err) {
error("Failed to save: %s", err.text);
return false;
}
}
bool loadGame(const std::string& filename) {
#ifndef _DEBUG
try {
#endif
SaveFile& file = *SaveFile::open(filename, SM_Read);
if(&file == 0) {
error("Unable to open file '%s'", filename.c_str());
return false;
}
file.loadIdentifiers();
addDummyIdentifiers(file);
SaveFileInfo info;
readSaveFileInfo(file, info);
file.scriptVersion = info.version;
file.startVersion = info.startVersion;
SAVE_VERSION = file.scriptVersion;
START_VERSION = file.startVersion;
//Initialize server scripts
devices.scripts.server->init();
//Load identifiers
addSubsystemIdentifiers(file);
addHullIdentifiers(file);
addShipsetIdentifiers(file);
devices.scripts.server->saveIdentifiers(file);
file.scriptVersion = info.version;
file.startVersion = info.startVersion;
file.finalizeIdentifiers();
file.boundary();
loadGameConfig(file);
file.boundary();
devices.driver->resetGameTime(file);
resetGameTime();
Empire::loadEmpires(file);
file.boundary();
loadEffectors(file);
file.boundary();
//Load physics
if(file.read<bool>())
devices.physics = PhysicsWorld::fromSave(file);
if(file.read<bool>())
devices.nodePhysics = PhysicsWorld::fromSave(file);
file.boundary();
//Load object id sequences
unsigned typeCount = file;
for(unsigned i = 0; i < typeCount; ++i) {
auto* type = getScriptObjectType(i);
type->nextID = file.read<int>();
}
file.boundary();
//Load object groups
unsigned groupCount = file;
for(unsigned i = 0; i < groupCount; ++i)
new ObjectGroup(file);
file.boundary();
//Load objects
unsigned objectCount = file;
for(unsigned i = 0; i < objectCount; ++i) {
Object* obj = file;
obj->load(file);
devices.universe->children.push_back( obj ); obj->grab();
}
file.boundary();
//Perform post init for groups
for(auto i = groups.begin(), end = groups.end(); i != end;) {
ObjectGroup* group = i->second;
if(group->postLoad()) {
group->postInit();
++i;
}
else {
i = groups.erase(i);
group->drop();
}
}
loadProjectiles(file);
file.boundary();
devices.scripts.server->load(file);
//Objects that weren't loaded yet at this point are not valid
invalidateUninitializedObjects();
//Perform post load on objects
for(unsigned i = 0; i < objectCount; ++i)
devices.universe->children[i]->postLoad();
//Post load on effectors
postLoadEffectors();
//Initialize client scripts
devices.scripts.client->init();
//TODO: If we don't have a client, there could still be data to load (if we load it last, we don't have to care)
if(devices.scripts.client)
devices.scripts.client->load(file);
file.close();
return true;
#ifndef _DEBUG
}
catch(SaveFileError& err) {
error("Failed to load save '%s':\n %s", filename.c_str(), err.text);
return false;
}
#endif
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <string>
bool saveGame(const std::string& file);
bool loadGame(const std::string& file);
+862
View File
@@ -0,0 +1,862 @@
#include "main/tick.h"
#include "main/input_handling.h"
#include "main/references.h"
#include "main/logging.h"
#include "processing.h"
#include "obj/object.h"
#include "scene/animation/anim_node_sync.h"
#include "main/console.h"
#include "main/initialization.h"
#include "network/network_manager.h"
#include "ISoundDevice.h"
#include "files.h"
#include "design/projectiles.h"
#include "render/vertexBuffer.h"
#include "render/gl_framebuffer.h"
#include "save_load.h"
#include <cmath>
#include <stdio.h>
#include <stdint.h>
extern unsigned drawnSteps, bufferFlushes;
GameState game_state = GS_Menu;
std::string game_locale;
bool game_running = false;
bool reload_gui = false;
bool fullGC = false;
threads::Mutex texDestroyLock;
std::vector<const render::Texture*> queuedDestroyTextures;
void queueDestroyTexture(const render::Texture* tex) {
threads::Lock lock(texDestroyLock);
queuedDestroyTextures.push_back(tex);
}
void destroyQueuedTextures() {
if(queuedDestroyTextures.empty())
return;
threads::Lock lock(texDestroyLock);
for(unsigned i = 0; i < queuedDestroyTextures.size(); ++i)
delete queuedDestroyTextures[i];
queuedDestroyTextures.clear();
}
const render::Shader* fsShader = nullptr;
bool hide_ui = false;
double ui_scale = 1.0;
double scale_3d = 1.0;
double pixelSizeRatio = 1.0;
double* maxfps = nullptr;
//Seconds since last autosave
double autosaveTimer = 0.0;
extern double autosaveInterval;
threads::Signal scriptTickSignal;
extern bool queuedModSwitch;
extern std::vector<std::string> modSetup;
namespace scripts {
void logException();
};
class RunScriptTick : public processing::Action {
double time;
double& logTime;
bool gcLock;
scripts::Manager* manager;
public:
RunScriptTick(scripts::Manager* Manager, double Time, bool Gc, double& LogTime)
: time(Time), gcLock(Gc), manager(Manager), logTime(LogTime) {
}
bool run() {
auto prevSection = enterSection(NS_ScriptTick);
double start = devices.driver->getAccurateTime();
#ifndef _DEBUG
try {
#endif
#ifdef TRACE_GC_LOCK
manager->markGCImpossible();
#endif
manager->tick(time);
devices.network->managerNetworking(manager, time);
scriptTickSignal.signalDown();
#ifdef TRACE_GC_LOCK
manager->markGCPossible();
#endif
#ifndef _DEBUG
}
catch(...) {
scripts::logException();
throw;
}
#endif
logTime = devices.driver->getAccurateTime() - start;
enterSection(prevSection);
return true;
}
};
class RunScriptGC : public processing::Action {
scripts::Manager* manager;
bool full;
public:
RunScriptGC(scripts::Manager* Manager, bool FullCycle = false)
: manager(Manager), full(FullCycle) {
}
bool run() {
double start = devices.driver->getAccurateTime();
manager->pauseScriptThreads();
double gcStart = devices.driver->getAccurateTime();
int gcMode = manager->garbageCollect(full);
double end = devices.driver->getAccurateTime();
manager->resumeScriptThreads();
#ifdef PROFILE_PROCESSING
if(end - start > 0.016) {
const char* name = "Server";
if(manager == devices.scripts.client)
name = "Client";
else if(manager == devices.scripts.menu)
name = "Menu";
error("%s GC took %dms (Mode %d)", name, (int)((end - gcStart) * 1.0e3), gcMode);
if(gcStart - start > 0.01)
error("%s script pausing took %dms", name, (int)((gcStart - start) * 1.0e3));
}
#endif
scriptTickSignal.signalDown();
return true;
}
};
unsigned animationThreadCount = 6;
threads::Signal animationSignal, activeAnimCount;
threads::Mutex animDataLock;
double nearestNode = 9.0e35, furthestNode = 0.0;
double animation_s = 0.0, render_s = 0.0, present_s = 0.0;
threads::atomic_int animIndex(-1), animProcessed;
//Animates some nodes, returning how long it processed
double animateSomeNodes(int maxNodes) {
if(animIndex < 0)
return 0.0;
double furthest = 0.0;
double start = devices.driver->getAccurateTime();
auto* nodes = &devices.scene->children.front();
int count = (int)devices.scene->children.size();
int animCount = count/(animationThreadCount*16);
if(animCount == 0)
animCount = 1;
int index = (animIndex -= animCount);
while(index >= 0) {
int animated = 0;
for(int i = index; i > index - animCount && i >= 0; --i) {
auto* node = nodes[i];
node->animate();
++animated;
double d = node->sortDistance + node->abs_scale;
if(d > furthest)
furthest = d;
}
if(animated != 0)
animProcessed += animated;
maxNodes -= animated;
if(maxNodes < 0)
break;
index = (animIndex -= animCount);
}
animDataLock.lock();
//if(nearest < nearestNode)
// nearestNode = nearest;
if(furthest > furthestNode)
furthestNode = furthest;
animDataLock.release();
return devices.driver->getAccurateTime() - start;
}
volatile bool EndAnimationThreads = false;
threads::threadreturn threadcall animateNodes(void* arg) {
enterSection(NS_Animation);
double& logTime = *(double*)arg;
initNewThread();
activeAnimCount.signalUp();
//threads::setThreadPriority(threads::TP_High);
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCImpossible();
devices.scripts.client->markGCImpossible();
#endif
while(true) {
while(animIndex < 0 && !EndAnimationThreads)
threads::sleep(1);
if(EndAnimationThreads)
break;
animationSignal.signalUp();
double /*nearest = 9.0e35,*/ furthest = 0.0;
double start = devices.driver->getAccurateTime();
if(!devices.scene->children.empty()) {
auto* nodes = &devices.scene->children.front();
int animCount = (int)devices.scene->children.size()/(animationThreadCount*16);
if(animCount == 0)
animCount = 1;
int index = (animIndex -= animCount);
while(index >= 0) {
int animated = 0;
for(int i = index; i > index - animCount && i >= 0; --i) {
auto* node = nodes[i];
node->animate();
++animated;
double d = node->sortDistance + node->abs_scale;
if(d > furthest)
furthest = d;
}
if(animated != 0)
animProcessed += animated;
index = (animIndex -= animCount);
}
}
logTime = devices.driver->getAccurateTime() - start;
animDataLock.lock();
//if(nearest < nearestNode)
// nearestNode = nearest;
if(furthest > furthestNode)
furthestNode = furthest;
animDataLock.release();
animationSignal.signalDown();
threads::sleep(1);
}
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCPossible();
devices.scripts.client->markGCPossible();
#endif
activeAnimCount.signalDown();
cleanupThread();
return 0;
}
double frameLen_s = 0, frameTime_s = 0;
double realFrameLen = 0, realFrameTime;
int frameTime_ms = 0, frameLen_ms = 0;
unsigned frameNumber = 0;
std::vector<double> frames;
unsigned max_frames = 120;
void shader_gameTime(float* pFloats,unsigned short n,void*) {
do {
*pFloats = (float)frameTime_s;
++pFloats;
} while(--n);
}
void shader_frameTime(float* pFloats,unsigned short n,void*) {
do {
*pFloats = (float)realFrameTime;
++pFloats;
} while(--n);
}
void shader_gameTime_cycle(float* pFloats,unsigned short n,void* pArgs) {
float* period = (float*)pArgs;
do {
*pFloats = (float)(std::fmod(frameTime_s, double(*period)) / double(*period));
++period; ++pFloats;
} while(--n);
}
void shader_frameTime_cycle(float* pFloats,unsigned short n,void* pArgs) {
float* period = (float*)pArgs;
do {
*pFloats = (float)(std::fmod(realFrameTime, double(*period)) / double(*period));
++period; ++pFloats;
} while(--n);
}
void shader_gameTime_cycle_abs(float* pFloats,unsigned short n,void* pArgs) {
float* period = (float*)pArgs;
do {
*pFloats = (float)std::fmod(frameTime_s, double(*period));
++period; ++pFloats;
} while(--n);
}
void shader_frameTime_cycle_abs(float* pFloats,unsigned short n,void* pArgs) {
float* period = (float*)pArgs;
do {
*pFloats = (float)std::fmod(realFrameTime, double(*period));
++period; ++pFloats;
} while(--n);
}
void shader_pixelRatio(float* pFloats,unsigned short n,void* pArgs) {
*pFloats = (float)pixelSizeRatio;
}
#ifdef PROFILE_LOCKS
double lockProfileTimer = 0.0;
bool printLockProfile = false;
bool requireObserved = false;
#endif
double menuTickTime = 0, serverTickTime = 0, clientTickTime = 0, animationTime = 0;
double* animTimes = nullptr;
extern double lastLockGlobalUpdate, nextTargetUpdateTime, nextScriptGCTime;
void resetGameTime() {
nextTargetUpdateTime = lastLockGlobalUpdate = devices.driver->getGameTime();
frameTime_s = devices.driver->getGameTime() - 0.25;
nextScriptGCTime = devices.driver->getFrameTime();
}
void endAnimation() {
if(animTimes) {
EndAnimationThreads = true;
activeAnimCount.wait(0);
EndAnimationThreads = false;
delete[] animTimes;
animTimes = nullptr;
}
}
void idleLoadResources(int maxPriority) {
if(devices.library.processTextures(maxPriority, true))
return;
if(devices.library.processMeshes(maxPriority, 1))
return;
if(devices.library.processTextures(INT_MIN, true))
return;
if(devices.library.processMeshes(INT_MIN, 1))
return;
}
GameState prev_state = GS_Menu;
void tickGlobal(bool hasScripts) {
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCImpossible();
devices.scripts.client->markGCImpossible();
#endif
//Get frame timings
static int lastRender = 0;
if(queuedModSwitch) {
queuedModSwitch = false;
destroyMod();
initMods(modSetup);
game_state = GS_Menu;
}
if(game_state != prev_state) {
if(hasScripts) {
if(game_running && devices.scripts.client)
devices.scripts.client->stateChange();
if(devices.scripts.menu)
devices.scripts.menu->stateChange();
}
prev_state = game_state;
}
++frameNumber;
double frame_time = devices.driver->getGameTime() - 0.25;
frameTime_ms = devices.driver->getTime();
double real_frame = devices.driver->getFrameTime();
realFrameLen = real_frame - realFrameTime;
realFrameTime = real_frame;
int frame_ms = frameTime_ms - lastRender;
lastRender = frameTime_ms;
double frame_s = frame_time - frameTime_s;
frameTime_s = frame_time;
if(frame_ms > 250 || frame_s > 0.25) {
frame_ms = 250;
frame_s = 0.25;
}
else if(frame_ms < 0 || frame_s < 0.0) {
frame_ms = 0;
frame_s = 0.0;
}
frameLen_s = frame_s;
frameLen_ms = frame_ms;
frames.push_back(realFrameLen);
if(frames.size() > max_frames)
frames.erase(frames.begin());
//Run network manager tick
devices.network->tick(realFrameLen);
//Do lock profiling
#ifdef PROFILE_LOCKS
lockProfileTimer += frame_s;
if(lockProfileTimer >= 1.0) {
lockProfileTimer = 0.0;
if(printLockProfile) {
threads::profileMutexCycle([](threads::Mutex* mtx) {
if((mtx->observed || !requireObserved) && mtx->profileCount > 0) {
print("%s: %d locks", mtx->name.c_str(), mtx->profileCount);
}
});
threads::profileReadWriteMutexCycle([](threads::ReadWriteMutex* mtx) {
if((mtx->observed || !requireObserved) && (mtx->profileReadCount > 0 || mtx->profileWriteCount > 0)) {
print("%s: %d read, %d write", mtx->name.c_str(), mtx->profileReadCount, mtx->profileWriteCount);
}
});
printLockProfile = false;
}
else {
threads::profileMutexCycle(0);
threads::profileReadWriteMutexCycle(0);
}
}
#endif
idleLoadResources(isPreloading() ? -10 : INT_MIN);
//Do script GCs
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCPossible();
devices.scripts.client->markGCPossible();
#endif
if(hasScripts) {
scriptTickSignal.signal(1);
processing::queueAction(new RunScriptGC(devices.scripts.menu, fullGC));
if(game_running) {
scriptTickSignal.signalUp(1);
processing::queueAction(new RunScriptGC(devices.scripts.client, fullGC));
}
fullGC = false;
while(!scriptTickSignal.check(0)) {
processing::run();
threads::sleep(0);
}
}
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCImpossible();
devices.scripts.client->markGCImpossible();
#endif
//Run all the script ticks
if(hasScripts) {
scriptTickSignal.signal(1);
processing::queueAction(new RunScriptTick(devices.scripts.menu, realFrameLen, false, menuTickTime));
if(game_running) {
scriptTickSignal.signalUp(2);
processing::queueAction(new RunScriptTick(devices.scripts.server, frameLen_s, true, serverTickTime));
processing::queueAction(new RunScriptTick(devices.scripts.client, realFrameLen, false, clientTickTime));
}
}
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCPossible();
devices.scripts.client->markGCPossible();
#endif
}
std::unordered_map<std::string,time_t> guiModuleTimes;
void tickGuiReload() {
if(!devices.scripts.client)
return;
for(auto it = devices.scripts.client->modules.begin(); it != devices.scripts.client->modules.end(); ++it) {
scripts::Module& mod = *it->second;
scripts::File& fl = *mod.file;
time_t mtime = getModifiedTime(fl.path);
auto f = guiModuleTimes.find(mod.name);
if(f == guiModuleTimes.end()) {
guiModuleTimes[mod.name] = mtime;
}
else {
if(f->second < mtime)
devices.scripts.client->reload(mod.name);
guiModuleTimes[mod.name] = mtime;
}
}
}
static render::Texture* renderTarget = nullptr;
void getFrameRender(Image& img) {
if(!game_running || !devices.scripts.client)
return;
auto prev_state = game_state;
game_state = GS_Menu;
devices.scripts.client->preRender(0.0);
devices.scripts.client->render(0.0);
renderTarget->save(img);
devices.render->setRenderTarget(nullptr);
game_state = prev_state;
}
void renderFrame(scripts::Manager* uiScript, scripts::Manager* renderScript) {
drawnSteps = 0;
bufferFlushes = 0;
for(unsigned i = 0; i < render::FC_COUNT; ++i)
render::vbFlushCounts[i] = 0;
destroyQueuedTextures();
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCImpossible();
devices.scripts.client->markGCImpossible();
#endif
vec2i screenSize(devices.driver->win_width,devices.driver->win_height);
bool render = screenSize.width != 0 && screenSize.height != 0;
if(render) {
if(!renderTarget)
renderTarget = devices.render->createRenderTarget(screenSize * scale_3d);
if(renderTarget->size != screenSize * scale_3d) {
delete renderTarget;
renderTarget = devices.render->createRenderTarget(screenSize * scale_3d);
}
}
devices.sound->setListenerData(vec3f(devices.render->cam_pos), vec3f(), vec3f(devices.render->cam_pos + devices.render->cam_facing), vec3f(devices.render->cam_up));
if(!Object::GALAXY_CREATION)
scene::processNodeEvents();
if(renderScript)
renderScript->preRender(realFrameLen);
nearestNode = 9.0e35; furthestNode = 0.0;
static bool dumpAnimTimes = false;
if(!animTimes) {
animationThreadCount = std::max(1u,devices.driver->getProcessorCount()-1);
animTimes = new double[animationThreadCount]();
for(unsigned i = 0; i < animationThreadCount; ++i)
threads::createThread(animateNodes, &animTimes[i]);
#ifdef PROFILE_ANIMATION
console.addCommand("anim_times", [](argList& args) {
dumpAnimTimes = true;
}, true);
#endif
}
render::Texture* rt = nullptr;
const render::Shader* shad = nullptr;
if(fsShader || scale_3d != 1.0) {
rt = renderTarget;
shad = fsShader;
}
devices.library.reloadWatchedResources();
if(render) {
devices.render->setScreenSize(screenSize.x, screenSize.y);
devices.render->setRenderTarget(rt);
}
if(screenSize.y != 0)
pixelSizeRatio = (double)screenSize.y / 1024.0;
double animTotal = 0.0;
{
int nodeCount = (int)devices.scene->children.size();
animProcessed = 0;
//Animate nodes, signaling the animation threads to work if they get time
animIndex = nodeCount-1;
unsigned loops = 0;
while(true) {
bool waiting = false;
if(!scriptTickSignal.check(0)) {
waiting = true;
processing::run(true);
}
if(animProcessed != nodeCount) {
waiting = true;
animTotal += animateSomeNodes(128);
}
if(!waiting)
break;
if(++loops % 100 == 0)
threads::sleep(0);
}
//Sort the catch-all parent
devices.scene->sortChildren();
//Wait for all animation threads to report in
while(!animationSignal.check(0))
threads::sleep(0);
for(unsigned i = 0; i < animationThreadCount; ++i) {
double t = animTimes[i];
animTotal += t;
}
#ifdef PROFILE_ANIMATION
if(dumpAnimTimes)
scene::dumpAnimationProfile();
dumpAnimTimes = false;
#else
(void)dumpAnimTimes;
#endif
}
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCImpossible();
devices.scripts.client->markGCImpossible();
#endif
double animation_end = devices.driver->getAccurateTime();
animation_s = animTotal;
auto prevSection = enterSection(NS_Render);
if(render) {
devices.render->setDefaultRenderState();
//Animation system tells us the near and far distances of everything that gets rendered
devices.render->setNearFarPlanes(1.0, furthestNode * 1.1);
//3D prepare is done by scripts, since it needs the camera
devices.render->clearRenderPrepared();
if(renderScript)
renderScript->render(realFrameLen);
if(rt) {
devices.render->setRenderTarget(0);
if(devices.render->isRenderPrepared()) {
render::RenderState rs;
if(shad)
rs.shader = shad;
else
rs.shader = devices.library["Fullscreen"];
rs.lighting = false;
rs.culling = render::FC_None;
rs.depthWrite = false;
rs.depthTest = render::DT_NoDepthTest;
rs.constant = false;
rs.textures[0] = rt;
rs.textures[7] = ((render::glFrameBuffer*)rt)->depthTexture; //fuck you framebuffers aren't textures
auto* buffer = render::VertexBufferTCV::fetch(&rs);
auto* verts = buffer->request(1, render::PT_Quads);
verts[0].set(vec2f(0,0));
verts[1].set(vec2f(1,0));
verts[2].set(vec2f(1,1));
verts[3].set(vec2f(0,1));
buffer->draw();
}
}
}
//Check if we should scale the 2D interface
if(render && !hide_ui) {
static render::Texture* uirt = 0;
vec2i uiArea;
if(ui_scale != 1.0) {
uiArea = vec2d(devices.driver->win_width, devices.driver->win_height) / ui_scale;
if(uirt == 0) {
uirt = devices.render->createRenderTarget(uiArea);
}
else {
if(uirt->size != uiArea) {
delete uirt;
uirt = devices.render->createRenderTarget(uiArea);
}
}
devices.render->setScreenSize(uiArea.x, uiArea.y);
devices.render->setRenderTarget(uirt, true);
}
//2D prepare is done before scripts
devices.render->prepareRender2D();
if(uiScript)
uiScript->draw();
//Scale the 2D interface
if(uiArea.x != 0) {
devices.render->setScreenSize(screenSize.x, screenSize.y);
devices.render->setRenderTarget(0);
devices.render->prepareRender2D();
render::RenderState rs;
rs.lighting = false;
rs.constant = false;
rs.culling = render::FC_None;
rs.baseMat = render::MAT_Overlay;
rs.shader = devices.library["Fullscreen"];
rs.depthWrite = false;
rs.depthTest = render::DT_NoDepthTest;
rs.filterMin = render::TF_Linear;
rs.filterMag = render::TF_Linear;
rs.textures[0] = uirt;
auto* buffer = render::VertexBufferTCV::fetch(&rs);
auto* verts = buffer->request(1, render::PT_Quads);
verts[0].set(vec2f(0,0));
verts[1].set(vec2f(1,0));
verts[2].set(vec2f(1,1));
verts[3].set(vec2f(0,1));
buffer->draw();
}
}
if(render) {
if(hide_ui)
devices.render->prepareRender2D();
console.render(*devices.render);
render::renderVertexBuffers();
}
else {
threads::sleep(1);
}
double render_end = devices.driver->getAccurateTime();
devices.driver->swapBuffers(maxfps && *maxfps > 0 ? 1.0 / *maxfps : 0.0);
double present_end = devices.driver->getAccurateTime();
render_s = render_end - animation_end;
present_s = present_end - render_end;
#ifdef TRACE_GC_LOCK
devices.scripts.menu->markGCPossible();
devices.scripts.client->markGCPossible();
#endif
#ifdef _DEBUG
devices.render->reportErrors();
#endif
enterSection(prevSection);
}
void tickConsole() {
tickGlobal(false);
renderFrame(0, 0);
}
void tickGame() {
//General tick
tickGlobal();
if(devices.driver->getGameSpeed() > 0 && !devices.network->isClient) {
autosaveTimer += realFrameLen;
auto* interval = devices.settings.engine.getSetting("dAutosaveMinutes");
if(interval && interval->getDouble() >= 1.0 && autosaveTimer > interval->getDouble() * 60.0) {
int maxAutosaves = 1;
auto* count = devices.settings.engine.getSetting("iAutosaveCount");
if(count)
maxAutosaves = count->getInteger();
std::string prevName;
if(maxAutosaves > 1) {
for(int index = maxAutosaves; index > 0; --index) {
std::string fname = "autosave";
if(index != 1)
fname += toString(index);
fname += ".sr2";
fname = path_join(devices.mods.getGlobalProfile("saves"), fname);
if(fileExists(fname)) {
if(index == maxAutosaves)
remove(fname.c_str());
else
rename(fname.c_str(), prevName.c_str());
}
prevName = fname;
}
}
processing::pause();
saveGame(path_join(devices.mods.getGlobalProfile("saves"), "autosave.sr2"));
processing::resume();
autosaveTimer = 0.0;
}
}
if(reload_gui)
tickGuiReload();
renderFrame(devices.scripts.client, devices.scripts.client);
inputTick();
processing::runIsolation();
}
void tickMenu() {
//General tick
tickGlobal();
renderFrame(devices.scripts.menu, devices.scripts.menu);
inputTick();
}
std::unordered_map<std::string, time_t> monitoredFiles;
void monitorFile(const std::string& filename) {
if(monitor_files)
monitoredFiles[filename] = getModifiedTime(filename);
}
bool tickMonitor() {
if(isPreloading())
return false;
foreach(it, monitoredFiles) {
time_t mtime = getModifiedTime(it->first);
if(mtime > it->second) {
monitoredFiles.clear();
return true;
}
}
return false;
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <string>
#include <vector>
extern double animation_s, render_s, present_s;
namespace render {
class Shader;
class Camera;
};
struct Image;
extern const render::Shader* fsShader;
extern double frameLen_s, frameTime_s, realFrameLen;
extern int frameTime_ms, frameLen_ms;
extern bool reload_gui;
extern std::vector<double> frames;
extern unsigned max_frames;
enum GameState {
GS_Menu,
GS_Game,
GS_Test_Scripts,
GS_Monitor_Scripts,
GS_Console_Wait,
GS_Quit,
GS_Load_Prep,
GS_COUNT,
};
extern GameState game_state;
extern std::string game_locale;
extern bool game_running;
extern bool hide_ui;
extern double ui_scale;
void resetGameTime();
void getFrameRender(Image& img);
void tickGlobal(bool hasScripts = true);
void tickMenu();
void tickGame();
void tickConsole();
void monitorFile(const std::string& filename);
bool tickMonitor();
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#define GAME_VERSION 1
#define GAME_VERSION_NAME "0.0.1"
#define ENGINE_BUILD 1
#define SERVER_SCRIPT_BUILD 1
#define CLIENT_SCRIPT_BUILD 1
#define MENU_SCRIPT_BUILD 1
#ifdef _MSC_VER
#define ARCH_NAME "W"
#ifdef _M_AMD64
#define ARCH_BITS 64
#else
#define ARCH_BITS 32
#endif
#else
#ifdef __APPLE__
#define ARCH_NAME "A"
#else
#define ARCH_NAME "L"
#endif
#ifdef __amd64__
#define ARCH_BITS 64
#else
#define ARCH_BITS 32
#endif
#endif
#define BUILD_VERSION "DEV"