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
+16
View File
@@ -0,0 +1,16 @@
#pragma once
enum BasicTypes {
BT_Int,
BT_Double,
BT_Bool
};
struct BasicType {
BasicTypes type;
union {
int integer;
double decimal;
bool boolean;
};
};
+168
View File
@@ -0,0 +1,168 @@
#include "bbcode.h"
enum ParseState {
PS_Text,
PS_StartTag,
PS_EndTag
};
static const char* parseTag(BBCode::Tag& root, const char* str) {
bool escaped = false;
bool selfClose = false;
ParseState state = PS_Text;
const char* start = str;
unsigned tagDepth = 0;
while(*str != 0) {
unsigned char c = *str;
switch(state) {
case PS_Text:
if(c == '\\') {
if(escaped) {
escaped = false;
if(str != start) {
BBCode::Tag t;
t.type = -1;
t.argument = std::string(start, str - start);
root.contents.push_back(t);
}
++str;
start = str;
}
else {
escaped = true;
++str;
}
}
else if(c == '[') {
if(escaped) {
--str;
if(str != start) {
BBCode::Tag t;
t.type = -1;
t.argument = std::string(start, str - start);
root.contents.push_back(t);
}
++str;
escaped = false;
start = str;
++str;
}
else {
if(str != start) {
BBCode::Tag t;
t.type = -1;
t.argument = std::string(start, str - start);
root.contents.push_back(t);
}
state = PS_StartTag;
++str;
start = str;
}
}
else {
++str;
}
break;
case PS_StartTag:
if(c == '/') {
if(str == start) {
state = PS_EndTag;
++str;
start = str;
}
else {
selfClose = true;
++str;
}
}
else if(c == ']') {
if(tagDepth > 0) {
--tagDepth;
++str;
selfClose = true;
break;
}
std::string tagname;
if(selfClose)
tagname = std::string(start, (str - 1) - start);
else
tagname = std::string(start, str - start);
++str;
BBCode::Tag t;
auto argpos = tagname.find('=');
if(argpos != std::string::npos) {
t.name = tagname.substr(0, argpos);
if(argpos < tagname.size() - 1)
t.argument = tagname.substr(argpos+1);
}
else {
t.name = tagname;
}
if(selfClose)
selfClose = false;
else
str = parseTag(t, str);
state = PS_Text;
start = str;
root.contents.push_back(t);
}
else {
if(c == '[')
++tagDepth;
selfClose = false;
++str;
}
break;
case PS_EndTag:
if(c == ']') {
std::string tagname(start, str - start);
++str;
if(tagname != root.name)
throw "Unexpected close tag.";
return str;
}
else {
++str;
}
break;
default:
++str;
break;
}
}
if(state == PS_Text && str != start) {
BBCode::Tag t;
t.type = -1;
t.argument = std::string(start, str - start);
root.contents.push_back(t);
}
return str;
}
void BBCode::parse(const std::string& content) {
//Clear everything
root.contents.clear();
//Start parsing
parseTag(root, content.c_str());
}
void BBCode::clear() {
root.contents.clear();
}
BBCode::BBCode() {
}
BBCode::~BBCode() {
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <vector>
#include <string>
class BBCode {
public:
struct Tag {
int type;
int value;
std::string name;
std::string argument;
std::vector<Tag> contents;
Tag() : type(0), value(0) {
}
};
Tag root;
void parse(const std::string& content);
void clear();
BBCode();
~BBCode();
};
+296
View File
@@ -0,0 +1,296 @@
#include "util/elevation_map.h"
#include "main/references.h"
#include "compat/misc.h"
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include "BiPatch/bilinear.h"
#include "main/logging.h"
ElevationMap::ElevationMap()
: generated(false), grid(0) {
}
ElevationMap::~ElevationMap() {
if(generated)
free(grid);
}
void ElevationMap::clear() {
points.clear();
if(generated) {
free(grid);
grid = 0;
generated = false;
}
}
void ElevationMap::addPoint(const vec3d& point, double radius) {
Point p = {point, radius};
points.push_back(p);
}
void ElevationMap::generate(const vec2d& interval, double power) {
//Clear previous grid
if(generated) {
free(grid);
grid = 0;
}
//A grid with no points is pretty pointless (haha)
if(points.empty()) {
generated = true;
grid = 0;
gridStart = vec3d();
gridSize = vec2d();
gridInterval = vec2d();
return;
}
double start = devices.driver->getAccurateTime();
//Get the extents of the grid
vec2d topLeft(points[0].center.x, points[0].center.z);
vec2d botRight = topLeft;
double avgHeight = 0.0;
foreach(p, points) {
if(p->center.x - p->radius < topLeft.x)
topLeft.x = p->center.x - p->radius;
if(p->center.x + p->radius > botRight.x)
botRight.x = p->center.x + p->radius;
if(p->center.z - p->radius < topLeft.y)
topLeft.y = p->center.z - p->radius;
if(p->center.z + p->radius > botRight.y)
botRight.y = p->center.z + p->radius;
avgHeight += p->center.y;
}
avgHeight /= points.size();
gridSize = botRight - topLeft;
gridStart = vec3d(topLeft.x, avgHeight, topLeft.y);
gridInterval = interval;
gridResolution.x = (int)ceil(gridSize.x / gridInterval.x);
gridResolution.y = (int)ceil(gridSize.y / gridInterval.y);
minHeight = HUGE_VAL;
maxHeight = -HUGE_VAL;
//Create the big grid for optimization
std::vector<std::vector<Point>> bigGrid;
unsigned bigGridSize = std::max((unsigned)sqrt((double)points.size()), 1u);
bigGrid.resize(bigGridSize * bigGridSize);
double bigInterval = gridSize.x / bigGridSize;
foreach(p, points) {
unsigned bigX = std::min((unsigned)((p->center.x - topLeft.x) / bigInterval), bigGridSize - 1);
unsigned bigY = std::min((unsigned)((p->center.z - topLeft.y) / bigInterval), bigGridSize - 1);
bigGrid[bigX + (bigY * bigGridSize)].push_back(*p);
}
//Interpolate within the grid
grid = (float*)calloc(gridResolution.x * gridResolution.y, sizeof(float));
generated = true;
vec2d realPos;
double height;
double totalWeight = 0.0;
auto calcBucket = [&](unsigned bigX, unsigned bigY) {
if(bigX >= bigGridSize || bigY >= bigGridSize)
return;
auto& bucket = bigGrid[bigX + (bigY * bigGridSize)];
foreach(it, bucket) {
//Check if the point is inside a system
vec2d flatPos(it->center.x, it->center.z);
double dist = realPos.distanceTo(flatPos);
if(dist < it->radius) {
height = it->center.y;
totalWeight = 1.0;
break;
}
//Do inverse distance weighting
double w = 1.0 / pow(dist - it->radius, power);
height += it->center.y * w;
totalWeight += w;
}
};
for(int iy = 0; iy < gridResolution.y; ++iy) {
for(int ix = 0; ix < gridResolution.x; ++ix) {
realPos = vec2d(gridStart.x + ix * gridInterval.x,
gridStart.z + iy * gridInterval.y);
height = 0.0;
totalWeight = 0.0;
unsigned bigX = std::min((unsigned)((realPos.x - gridStart.x) / bigInterval), bigGridSize - 1);
unsigned bigY = std::min((unsigned)((realPos.y - gridStart.z) / bigInterval), bigGridSize - 1);
calcBucket(bigX - 1, bigY - 1);
calcBucket(bigX, bigY - 1);
calcBucket(bigX + 1, bigY - 1);
calcBucket(bigX - 1, bigY);
calcBucket(bigX, bigY);
calcBucket(bigX + 1, bigY);
calcBucket(bigX - 1, bigY + 1);
calcBucket(bigX, bigY + 1);
calcBucket(bigX + 1, bigY + 1);
if(totalWeight == 0) {
height = avgHeight;
}
else {
height /= totalWeight;
if(height < minHeight)
minHeight = height;
if(height > maxHeight)
maxHeight = height;
}
grid[iy * gridResolution.x + ix] = (float)height;
}
}
if(maxHeight == minHeight) {
maxHeight += 1.0;
minHeight -= 1.0;
}
double time = devices.driver->getAccurateTime() - start;
info("Elevation grid took %.3gms to calculate.", time * 1000.0);
}
double ElevationMap::lookup(int x, int y) {
if(gridResolution.x == 0 || gridResolution.y == 0)
return 0.0;
x = std::max(std::min(x, gridResolution.x - 1), 0);
y = std::max(std::min(y, gridResolution.y - 1), 0);
return (double)grid[y * gridResolution.x + x];
}
double ElevationMap::get(vec2d point) {
return get(point.x, point.y);
}
double ElevationMap::get(double x, double y) {
//Move coordinates to grid coordinates
x = (x - gridStart.x) / gridInterval.x;
y = (y - gridStart.z) / gridInterval.y;
//Get the coordinates of the nearby points
int lx = (int)floor(x);
int ly = (int)floor(y);
int rx = lx + 1;
int ry = ly + 1;
//Interpolate the values
double value = 0.0;
value += lookup(lx, ly) * ((double)rx - x) * ((double)ry - y);
value += lookup(rx, ly) * (x - (double)lx) * ((double)ry - y);
value += lookup(lx, ry) * ((double)rx - x) * (y - (double)ly);
value += lookup(rx, ry) * (x - (double)lx) * (y - (double)ly);
return value;
}
bool ElevationMap::getClosestPoint(const line3dd& inLine, vec3d& closestPoint) {
if(!generated)
return false;
//Limit the line to the confines of the 3d grid
// Always make sure the end goes to the other plane, or a line that stops before the region will never collide
vec3d start = inLine.start, end = inLine.end;
if(inLine.start.y > inLine.end.y) {
if(start.y > maxHeight)
inLine.intersectY(start, maxHeight, false);
inLine.intersectY(end, minHeight, false);
}
else {
if(start.y < minHeight)
inLine.intersectY(start, minHeight, false);
inLine.intersectY(end, maxHeight, false);
}
line3dd line(start, end);
vec3d lineDir = line.end - line.start;
BiPatch::Vector rayOrigin(line.start.x, line.start.y, line.start.z);
BiPatch::Vector rayDir(lineDir.x, lineDir.y, lineDir.z);
rayDir.normalize();
BiPatch::Vector uv;
//Flatten the line to intelligently chose grid spaces to test
line3dd flatLine(start, end);
flatLine.start.y = 0;
flatLine.end.y = 0;
vec3d flatPoint = flatLine.start;
vec3d flatDir = flatLine.getDirection();
int x = (int)floor((flatLine.start.x - gridStart.x) / gridInterval.x);
int y = (int)floor((flatLine.start.z - gridStart.z) / gridInterval.y);
for(unsigned checks = 0; checks < 1000; ++checks) {
//See if we have a collision
double absx = gridStart.x + (gridInterval.x * double(x));
double absy = gridStart.z + (gridInterval.y * double(y));
BiPatch::Vector tl(absx, lookup(x, y), absy);
BiPatch::Vector tr(absx + gridInterval.x, lookup(x+1, y), absy);
BiPatch::Vector bl(absx, lookup(x, y+1), absy + gridInterval.y);
BiPatch::Vector br(absx + gridInterval.x, lookup(x+1, y+1), absy + gridInterval.y);
BiPatch::BilinearPatch bp(tl, tr, bl, br);
if(bp.RayPatchIntersection(rayOrigin, rayDir, uv)) {
BiPatch::Vector point = bp.SrfEval(uv.x(), uv.y());
vec3d intersect(point.x(), point.y(), point.z());
//Once we have a collision, it must be the closest point
double dot = (intersect - line.start).dot(line.getDirection());
if(dot >= -0.0001) {
closestPoint = intersect;
return true;
}
}
//Step to the next grid section
if(flatDir.z > 0) {
vec3d intersect;
flatLine.intersectZ(intersect, absy + gridInterval.y, false);
if(intersect.x < absx + gridInterval.x && intersect.x > absx) {
y += 1;
}
else if(flatDir.x > 0) {
x += 1;
flatLine.intersectX(intersect, absx + gridInterval.x, false);
}
else {
x -= 1;
flatLine.intersectX(intersect, absx, false);
}
flatPoint = intersect;
}
else {//if(flatDir.z <= 0) {
vec3d intersect;
flatLine.intersectZ(intersect, absy, false);
if(intersect.x < absx + gridInterval.x && intersect.x > absx) {
y -= 1;
}
else if(flatDir.x > 0) {
x += 1;
flatLine.intersectX(intersect, absx + gridInterval.x, false);
}
else {
x -= 1;
flatLine.intersectX(intersect, absx, false);
}
flatPoint = intersect;
}
}
return false;
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include "vec2.h"
#include "vec3.h"
#include "line3d.h"
#include <vector>
/*
* An elevation map takes an irregularly spaced
* set of points, precalculates a regular grid of
* smoothly interpolated heights and then allows
* for elevation lookup for arbitrary locations.
*/
class ElevationMap {
public:
struct Point {
vec3d center;
double radius;
};
std::vector<Point> points;
bool generated;
float* grid;
vec3d gridStart;
vec2d gridSize;
vec2d gridInterval;
vec2i gridResolution;
double minHeight;
double maxHeight;
void clear();
void addPoint(const vec3d& point, double radius = 0.0);
void generate(const vec2d& interval, double power = 2.0);
double lookup(int x, int y);
double get(vec2d point);
double get(double x, double y);
bool getClosestPoint(const line3dd& line, vec3d& point);
ElevationMap();
~ElevationMap();
};
+121
View File
@@ -0,0 +1,121 @@
#include "format.h"
#include <stdio.h>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
void format(std::string& arg, const char* fmt, unsigned argn, FormatArg* argv) {
char buffer[2048];
unsigned left = 2048;
char* start = buffer;
while(*fmt != '\0' && left != 0) {
if(*fmt == '$') {
++fmt;
if(*fmt == '\0')
break;
if(*fmt >= '1' && *fmt <= '9') {
unsigned arg = *fmt - '1';
if(arg < argn) {
unsigned printed = argv[arg].print(start, left);
start += printed;
left -= printed;
}
else {
//Print nothing (safe error)
}
++fmt;
}
else {
*start = '$';
++start; --left;
if(left != 0) {
*start = *fmt;
++start;
--left;
++fmt;
}
}
}
else {
*start = *fmt;
++start; ++fmt; --left;
}
}
arg.assign(buffer, 2048 - left);
};
std::string format(const char* fmt, FormatArg a1) {
std::string output;
format(output, fmt, 1, &a1);
return output;
}
std::string format(const char* fmt, FormatArg a1, FormatArg a2) {
std::string output;
FormatArg args[2] = { a1, a2 };
format(output, fmt, 2, args);
return output;
}
std::string format(const char* fmt, FormatArg a1, FormatArg a2, FormatArg a3) {
std::string output;
FormatArg args[3] = { a1, a2, a3 };
format(output, fmt, 3, args);
return output;
}
std::string format(const char* fmt, FormatArg a1, FormatArg a2, FormatArg a3, FormatArg a4) {
std::string output;
FormatArg args[4] = { a1, a2, a3, a4 };
format(output, fmt, 4, args);
return output;
}
std::string format(const char* fmt, FormatArg a1, FormatArg a2, FormatArg a3, FormatArg a4, FormatArg a5) {
std::string output;
FormatArg args[5] = { a1, a2, a3, a4, a5 };
format(output, fmt, 5, args);
return output;
}
FormatArg::FormatArg() : type(Arg_int), i(0) {}
FormatArg::FormatArg(float F) : type(Arg_float), f(F) {}
FormatArg::FormatArg(double D) : type(Arg_double), d(D) {}
FormatArg::FormatArg(unsigned U) : type(Arg_unsigned), u(U) {}
FormatArg::FormatArg(int I) : type(Arg_int), i(I) {}
FormatArg::FormatArg(const char* C) : type(Arg_cstr), c(C) {}
FormatArg::FormatArg(const std::string& S) : type(Arg_string), s(&S) {}
unsigned FormatArg::print(char* buffer, unsigned space) {
int printed = 0;
switch(type) {
case Arg_float:
printed = snprintf(buffer, space, "%f", f);
break;
case Arg_double:
printed = snprintf(buffer, space, "%g", d);
break;
case Arg_unsigned:
printed = snprintf(buffer, space, "%u", u);
break;
case Arg_int:
printed = snprintf(buffer, space, "%i", i);
break;
case Arg_cstr:
printed = snprintf(buffer, space, "%s", c);
break;
case Arg_string:
if(s)
printed = snprintf(buffer, space, "%s", s->c_str());
break;
}
if(printed > 0)
return printed;
else
return 0;
}
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include <string>
//std::string format("format $1-5", args[0-4])
//
//Formats the fromat string into a std::string output
//Arguments are placed into the string anywhere $1 through $5 are found
//
//For example:
//format("test $1", 2.5f) returns "test 2.50000"
//
//Float, Double, unsigned, int, c string, and std::string arguments are supported
//
//There are no formatting options
struct FormatArg {
enum ArgType {
Arg_float,
Arg_double,
Arg_unsigned,
Arg_int,
Arg_cstr,
Arg_string
};
unsigned print(char* buffer, unsigned space);
FormatArg();
FormatArg(float F);
FormatArg(double D);
FormatArg(unsigned U);
FormatArg(int I);
FormatArg(const char* C);
FormatArg(const std::string& S);
unsigned type;
union {
float f;
double d;
unsigned u;
int i;
const char* c;
const std::string* s;
};
};
void format(std::string& arg, const char* fmt, unsigned argn, FormatArg* argv);
std::string format(const char* format, FormatArg a1);
std::string format(const char* format, FormatArg a1, FormatArg a2);
std::string format(const char* format, FormatArg a1, FormatArg a2, FormatArg a3);
std::string format(const char* format, FormatArg a1, FormatArg a2, FormatArg a3, FormatArg a4);
std::string format(const char* format, FormatArg a1, FormatArg a2, FormatArg a3, FormatArg a4, FormatArg a5);
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <string>
typedef double varInterpreter(void*,const std::string*);
typedef double varIndexInterpreter(void*,int);
typedef int varIndexConverter(const std::string*);
double nullConverter(void*,const std::string*);
struct FormulaError {
std::string msg;
FormulaError(const char* message) : msg(message) {}
FormulaError(std::string message) : msg(message) {}
};
class Formula {
public:
//Evaluates the formula
//Variables in the formula will be passed to <VarConverter>, which receives
//the name of the variable, and the <UserPointer> passed to evaluate
virtual double evaluate(varInterpreter VarConverter = nullConverter, void* UserPointer = nullptr, varIndexInterpreter VarIndexConverter = 0) = 0;
virtual ~Formula() {}
//Creates a formula from infix notation
//Can throw FormulaError if there is an error in the expression
static Formula* fromInfix(const char* expression, varIndexConverter VarConverter = 0, bool catchErrors = true);
};
;
+190
View File
@@ -0,0 +1,190 @@
#include "util/generic.h"
#include "str_util.h"
Generic::Generic() : type(GT_Bool), check(false) {
}
void Generic::fromString(const std::string& val) {
switch(type) {
case GT_Bool:
check = toBool(val);
break;
case GT_Integer:
num = toNumber<int>(val);
break;
case GT_Double:
flt = toNumber<double>(val);
break;
case GT_String:
*str = val;
break;
case GT_Enum: {
value = 0;
for(unsigned i = 0; i < values->size(); ++i) {
if(val == (*values)[i]) {
value = i;
break;
}
}
} break;
}
}
std::string Generic::toString() {
switch(type) {
default:
case GT_Bool:
return check ? "true" : "false";
break;
case GT_Integer:
return ::toString(num);
break;
case GT_Double:
return ::toString(flt,4);
break;
case GT_String:
return *str;
break;
case GT_Enum:
return (*values)[value];
break;
}
}
Generic::~Generic() {
if(type == GT_String)
delete str;
else if(type == GT_Enum)
delete values;
}
bool Generic::getBool() {
return check;
}
void Generic::setBool(bool val) {
if(type == GT_String)
delete str;
else if(type == GT_Enum)
delete values;
type = GT_Bool;
check = val;
}
Generic::operator int() {
return getInteger();
}
Generic::operator double() {
return getDouble();
}
Generic::operator bool() {
return getBool();
}
void Generic::operator=(int v) {
setInteger(v);
}
void Generic::operator=(double v) {
setDouble(v);
}
void Generic::operator=(bool v) {
setBool(v);
}
void NamedGeneric::operator=(int v) {
setInteger(v);
}
void NamedGeneric::operator=(double v) {
setDouble(v);
}
void NamedGeneric::operator=(bool v) {
setBool(v);
}
int Generic::getInteger() {
return num;
}
void Generic::setInteger(int val) {
if(type == GT_String)
delete str;
else if(type == GT_Enum)
delete values;
type = GT_Integer;
num = val;
}
double Generic::getDouble() {
return flt;
}
void Generic::setDouble(double val) {
if(type == GT_String)
delete str;
else if(type == GT_Enum)
delete values;
type = GT_Double;
flt = val;
}
std::string* Generic::getString() {
if(type == GT_String)
return str;
return 0;
}
void Generic::setString(const std::string& val) {
if(type == GT_String) {
*str = val;
}
else {
if(type == GT_Enum)
delete values;
type = GT_String;
str = new std::string(val);
}
}
Generic::Generic(bool def) {
type = GT_Bool;
check = def;
}
Generic::Generic(int def) {
type = GT_Integer;
num = def;
}
Generic::Generic(const std::string& def) {
type = GT_String;
str = new std::string(def);
}
Generic::Generic(double def) {
type = GT_Double;
flt = def;
}
NamedGeneric::NamedGeneric(const std::string& Name, bool def) : Generic(def), name(Name) {
}
NamedGeneric::NamedGeneric(const std::string& Name, int def) : Generic(def), name(Name) {
}
NamedGeneric::NamedGeneric(const std::string& Name, const char* def) : Generic(std::string(def)), name(Name) {
}
NamedGeneric::NamedGeneric(const std::string& Name, const std::string& def) : Generic(def), name(Name) {
}
NamedGeneric::NamedGeneric(const std::string& Name, double def) : Generic(def), name(Name) {
}
NamedGeneric::NamedGeneric() : Generic() {
}
+87
View File
@@ -0,0 +1,87 @@
#pragma once
#include <vector>
#include <map>
#include <string>
enum GenericType {
GT_Bool,
GT_Integer,
GT_Double,
GT_Enum,
GT_String,
GT_Pointer,
};
struct Generic {
GenericType type;
union {
bool check;
std::string* str;
void* ptr;
struct {
int num;
int num_min;
int num_max;
};
struct {
double flt;
double flt_min;
double flt_max;
};
struct {
std::vector<std::string>* values;
int value;
};
};
void fromString(const std::string& str);
std::string toString();
bool getBool();
void setBool(bool val);
int getInteger();
void setInteger(int val);
double getDouble();
void setDouble(double val);
std::string* getString();
void setString(const std::string& val);
void* getPtr();
void setPtr(void* ptr);
operator int();
operator double();
operator bool();
void operator=(int);
void operator=(double);
void operator=(bool);
Generic(bool def);
Generic(int def);
Generic(const std::string& def);
Generic(double def);
Generic();
~Generic();
};
struct NamedGeneric : Generic {
std::string name;
NamedGeneric(const std::string& Name, bool def);
NamedGeneric(const std::string& Name, int def);
NamedGeneric(const std::string& Name, const char* def);
NamedGeneric(const std::string& Name, const std::string& def);
NamedGeneric(const std::string& Name, double def);
void operator=(int);
void operator=(double);
void operator=(bool);
NamedGeneric();
};
+340
View File
@@ -0,0 +1,340 @@
#pragma once
#include "vec2.h"
#include "constants.h"
#include <math.h>
#include <stdio.h>
enum HexGridAdjacency {
HEX_DownLeft,
HEX_Down,
HEX_DownRight,
HEX_UpRight,
HEX_Up,
HEX_UpLeft,
};
//Stores a hex grid layed out as:
// Indices Positions
//0,0 2,0 0.0,0.0 1.5,0.0
// 1,0 0.75,0.5
//0,1 2,1 0.0,1.0 1.5,1.0
// 1,1 0.75,1.5
//0,2 2,2 0.0,2.0 1.5,2.0
template<class T = bool>
struct HexGrid {
T* data;
unsigned width, height;
HexGrid()
: data(0) {
}
HexGrid(vec2u size, T* Data)
: data(Data), width(size.width), height(size.height)
{
}
HexGrid(vec2u size)
: data(new T[size.width*size.height]), width(size.width), height(size.height)
{
}
HexGrid(unsigned Width, unsigned Height)
: data(new T[Width*Height]), width(Width), height(Height)
{
}
HexGrid(const HexGrid<T>& other)
: data(0), width(0), height(0)
{
*this = other;
}
~HexGrid() {
if(data)
delete[] data;
}
HexGrid<T>& operator=(const HexGrid<T>& other) {
resize(other.width, other.height);
for(unsigned x = 0; x < width; ++x) {
for(unsigned y = 0; y < height; ++y) {
unsigned index = x * height + y;
data[index] = other.data[index];
}
}
return *this;
}
void resize(unsigned Width, unsigned Height) {
if(data)
delete[] data;
data = new T[Width*Height];
width = Width;
height = Height;
}
void resize(vec2u size) {
if(data)
delete[] data;
data = new T[size.width*size.height];
width = size.width;
height = size.height;
}
vec2u size() const {
return vec2u(width, height);
}
size_t length() const {
return width * height;
}
void clear(T value) {
unsigned amount = width * height;
for(unsigned i = 0; i < amount; ++i)
data[i] = value;
}
void zero() {
memset(data, 0, sizeof(T) * width * height);
}
unsigned count(T value) const {
unsigned cnt = 0;
unsigned amount = width * height;
for(unsigned i = 0; i < amount; ++i)
if(data[i] == value)
++cnt;
return cnt;
}
bool valid(const vec2u& pos) const {
return pos.x < width && pos.y < height;
}
bool valid(const vec2u& pos, HexGridAdjacency dir) const {
vec2u p = pos;
if(!advance(p.x, p.y, dir))
return false;
return p.x < width && p.y < height;
}
T& get(unsigned x, unsigned y) {
return data[x*height + y];
}
const T& get(unsigned x, unsigned y) const {
return data[x*height + y];
}
T& get(unsigned x, unsigned y, HexGridAdjacency dir) {
advance(x, y, dir);
return data[x*height + y];
}
const T& get(unsigned x, unsigned y, HexGridAdjacency dir) const {
advance(x, y, dir);
return data[x*height + y];
}
T& get(vec2u pos) {
return data[pos.x*height + pos.y];
}
const T& get(vec2u pos) const {
return data[pos.x*height + pos.y];
}
T& get(vec2u pos, HexGridAdjacency dir) {
advance(pos.x, pos.y, dir);
return data[pos.x*height + pos.y];
}
const T& get(vec2u pos, HexGridAdjacency dir) const {
advance(pos.x, pos.y, dir);
return data[pos.x*height + pos.y];
}
const T& operator[](vec2u pos) const {
return data[pos.x*height + pos.y];
}
T& operator[](vec2u pos) {
return data[pos.x*height + pos.y];
}
const T& operator[](unsigned i) const {
return data[i];
}
T& operator[](unsigned i) {
return data[i];
}
static vec2d getEffectivePosition(unsigned x, unsigned y) {
if(x % 2)
return vec2d((double)x * 0.75, (double)y + 0.5);
else
return vec2d((double)x * 0.75, (double)y);
}
static vec2d getEffectivePosition(const vec2u& pos) {
if(pos.x % 2)
return vec2d((double)pos.x * 0.75, (double)pos.y + 0.5);
else
return vec2d((double)pos.x * 0.75, (double)pos.y);
}
static vec2i getGridPosition(const vec2d& pos) {
vec2i out;
unsigned x = (unsigned)floor(pos.x / 0.75);
double xoffset = pos.x - (x * 0.75);
//In a full tile
if(xoffset > 0.25) {
out.x = x;
if(x % 2 == 1)
out.y = (int)floor(pos.y - 0.5);
else
out.y = (int)floor(pos.y);
}
else {
unsigned y;
double yoffset;
if(x % 2 == 1) {
y = (unsigned)floor(pos.y - 0.5);
yoffset = pos.y - y - 0.5;
}
else {
y = (unsigned)floor(pos.y);
yoffset = pos.y - y;
}
if(yoffset < 0.5) {
double linex = 0.25 - (0.5 * yoffset);
if(xoffset < linex) {
out.x = x - 1;
if(x % 2 == 1)
out.y = y;
else
out.y = y - 1;
}
else {
out.x = x;
out.y = y;
}
}
else {
double linex = 0.5 * (yoffset - 0.5);
if(xoffset < linex) {
out.x = x - 1;
if(x % 2 == 1)
out.y = y + 1;
else
out.y = y;
}
else {
out.x = x;
out.y = y;
}
}
}
return out;
}
static bool advancePosition(vec2u& pos, const vec2u& size, HexGridAdjacency direction, unsigned amount = 1) {
//Are we in the offset (+0.5 y) column? (1,3,5,etc)
bool offset = (pos.x % 2) != 0;
int moveY = 0, moveX = 0;
switch(direction) {
case HEX_Up:
moveY = -1;
break;
case HEX_UpLeft:
moveX = -1;
if(!offset)
moveY = -1;
break;
case HEX_UpRight:
moveX = 1;
if(!offset)
moveY = -1;
break;
case HEX_DownLeft:
moveX = -1;
if(offset)
moveY = 1;
break;
case HEX_Down:
moveY = 1;
break;
case HEX_DownRight:
moveX = 1;
if(offset)
moveY = 1;
break;
}
bool inBounds = true;
if(moveY) {
unsigned newY = pos.y + (unsigned)moveY * amount;
if(newY >= size.height)
inBounds = false;
else
pos.y = newY;
}
if(moveX) {
unsigned newX = pos.x + (unsigned)moveX * amount;
if(newX >= size.width)
inBounds = false;
else
pos.x = newX;
}
return inBounds;
}
//Advances <x,y> toward <direction>
//Returns false if the result would be out of bounds of the grid
//If only one dimension is a valid destination, it will move in that dimension
bool advance(unsigned& x, unsigned& y, HexGridAdjacency direction, unsigned amount = 1) const {
vec2u pos(x, y);
bool val = advancePosition(pos, vec2u(width, height), direction, amount);
x = pos.x;
y = pos.y;
return val;
}
bool advance(vec2u& pos, HexGridAdjacency direction, unsigned amount = 1) const {
return advancePosition(pos, vec2u(width, height), direction, amount);
}
static HexGridAdjacency AdjacencyFromRadians(double radians) {
while(radians < 0)
radians += twopi;
int dir =((int)floor(radians / (pi / 3.0)) + 3) % 6;
return HexGridAdjacency(dir);
}
static double RadiansFromAdjacency(HexGridAdjacency adj) {
return ((double)(adj) - 3.0) * (pi / 3.0) + (twopi / 6.0 * 0.5);
}
};
+601
View File
@@ -0,0 +1,601 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
enum LinkContainerBehavior {
LCB_Unordered,
LCB_Ordered,
};
/* LinkContainer
* -------------
* Base type for a set of containers that are thread-safe to read from.
* Reads can occur at the same time as writes, but writes must not happen simultaneously!
*
* Intended for use with small data structures that need to be read from multiple threads,
* and can change but do so infrequently enough that the extra write overhead is negligible.
*
* CAVEATS:
* - Will never shrink in size. Seriously, only use for small sets.
* - Iterating over it is sometimes going to skip an element.
* - Getting by index is sometimes going to return null even if index < size.
* */
template<typename T, unsigned char PoolSize, LinkContainerBehavior Behavior>
class LinkContainer {
protected:
void* start;
unsigned int count;
struct PoolHeader {
unsigned char filledElements;
bool contiguous;
void* next;
};
struct PoolElem {
bool filled;
T data;
};
inline PoolHeader& getHeader(void* mem) const {
return *(PoolHeader*)mem;
}
inline PoolElem& getElem(void* mem, unsigned char index) const {
unsigned char* dataStart = ((unsigned char*)mem) + sizeof(PoolHeader);
unsigned char* elemData = dataStart + (sizeof(PoolElem) * index);
return *(PoolElem*)elemData;
}
inline PoolElem& getElem(void* at) const
{
return *(PoolElem*)at;
}
inline void* allocate(const T& data) {
auto size = sizeof(PoolHeader) + sizeof(PoolElem) * PoolSize;
void* pool = malloc(size);
memset(pool, 0, size);
auto& header = getHeader(pool);
header.filledElements = 1;
header.contiguous = true;
auto& elem = getElem(pool, 0);
elem.filled = true;
elem.data = data;
return pool;
}
inline void* getLast() const {
void* pool = start;
while(pool)
{
auto& header = getHeader(pool);
if(header.next)
pool = header.next;
else
return pool;
}
return nullptr;
}
void checkContiguous(void* pool) {
auto& header = getHeader(pool);
bool foundEmpty = false;
for(unsigned char i = 0; i < header.filledElements; ++i) {
if(!getElem(pool, i).filled) {
foundEmpty = true;
break;
}
}
header.contiguous = !foundEmpty;
}
void getIndex(unsigned int index, void*& outPool, unsigned char& outElem) const {
void* pool = start;
while(pool) {
auto& header = getHeader(pool);
unsigned char cnt = header.filledElements;
if(index < cnt) {
if(header.contiguous)
{
auto& elem = getElem(pool, index);
if(elem.filled)
{
outPool = pool;
outElem = index;
return;
}
}
for(unsigned char i = 0; i < PoolSize; ++i)
{
auto& elem = getElem(pool, i);
if(elem.filled) {
if(index == 0) {
outPool = pool;
outElem = i;
return;
}
else {
index--;
}
}
}
}
else
{
index -= cnt;
}
pool = header.next;
}
outPool = nullptr;
outElem = -1;
}
public:
LinkContainer()
: start(nullptr), count(0) {
}
~LinkContainer() {
auto* pool = start;
while(pool) {
void* next = getHeader(pool).next;
free(pool);
pool = next;
}
start = nullptr;
count = 0;
}
/* Add a new element to the container. If Behavior was set to Ordered, this
* will insert at the end. If not, it will insert at an arbitrary point in
* the container. */
void add(const T& data) {
// Simple case, we are empty
if(start == nullptr) {
start = allocate(data);
count++;
return;
}
// Go through pools and see what to do
if(Behavior == LCB_Unordered) {
// See if we have an existing pool we can insert into
void* pool = start;
while (pool) {
auto& header = getHeader(pool);
if(header.filledElements < PoolSize) {
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(!elem.filled) {
header.filledElements++;
elem.data = data;
elem.filled = true;
count++;
if(!header.contiguous) {
if(header.filledElements == PoolSize || header.filledElements == i+1)
header.contiguous = true;
else
checkContiguous(pool);
}
return;
}
}
}
if(header.next) {
pool = header.next;
}
else {
// Create a new pool at the end
header.next = allocate(data);
count++;
return;
}
}
}
else /*if(Behavior == LCB_Ordered)*/
{
// We can only insert into the last pool we have, otherwise we need to create a new one
auto* pool = getLast();
auto& header = getHeader(pool);
if(header.filledElements < PoolSize) {
unsigned char lastFilled = (unsigned char)-1;
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, PoolSize - i - 1);
if(elem.filled) {
lastFilled = PoolSize - i - 1;
break;
}
}
if(lastFilled + 1 >= PoolSize) {
// Must create a new pool
header.next = allocate(data);
count++;
}
else {
// Insert one past the last filled element
auto& elem = getElem(pool, lastFilled + 1);
header.filledElements++;
elem.data = data;
elem.filled = true;
count++;
}
}
}
}
bool contains(const T& data) const {
void* pool = start;
while(pool) {
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled && elem.data == data)
return true;
}
pool = getHeader(pool).next;
}
return false;
}
void removeAt(unsigned int index) {
void* pool;
unsigned char elem;
getIndex(index, pool, elem);
if(pool == nullptr || elem < 0)
{
return;
}
getElem(pool, elem).filled = false;
getHeader(pool).filledElements--;
count--;
checkContiguous(pool);
}
int removeAll(const T& value) {
int removedCount = 0;
void* pool = start;
while (pool) {
auto& header = getHeader(pool);
bool removed = false;
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled) {
if(elem.data == value) {
elem.filled = false;
removed = true;
header.filledElements--;
count--;
removedCount++;
}
}
}
if(removed)
checkContiguous(pool);
pool = header.next;
}
return removedCount;
}
void clear() {
void* pool = start;
while (pool) {
auto& header = getHeader(pool);
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled) {
header.contiguous = false;
elem.filled = false;
header.filledElements--;
count--;
}
}
pool = header.next;
}
}
/**
* Note that due to the threaded nature of this data structure,
* this may very well return a nullptr even if index < count,
* so make sure to always check.
*
* It can also of course return a stale (already removed) value,
* and iteration might temporarily miss an element that was there before.
*
* The data structure is guaranteed not to segfault from threaded use, but
* value may be slightly wrong sometimes.
*/
T* getAt(unsigned int index) const {
void* pool;
unsigned char elem;
getIndex(index, pool, elem);
if(pool != nullptr && elem >= 0)
{
return &getElem(pool, elem).data;
}
return nullptr;
}
unsigned int size() const {
return count;
}
template<typename CB>
void iterateAll(CB cb) const {
void* pool = start;
while(pool) {
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled)
cb(elem.data);
}
pool = getHeader(pool).next;
}
}
};
template<typename T, int PoolSize = 16>
class LinkArray : LinkContainer<T, PoolSize, LCB_Ordered> {};
template<typename T, int PoolSize = 16>
class LinkBucket : LinkContainer<T, PoolSize, LCB_Unordered> {};
/**
* A very simple O(n) map structure for int64 -> int64/double.
*
* Should be used for very small maps when the set of keys changes rarely, and
* threaded reading of key/value pairs is worth the key lookup and change
* overhead.
*
* Also directly supports value delta tracking.
*/
struct LinkMapElem {
uint64_t key;
bool dirty;
union {
uint64_t value;
double doubleValue;
};
union {
uint64_t prevValue;
double prevDoubleValue;
};
};
template<int PoolSize = 16>
class LinkMapBase : LinkContainer<struct LinkMapElem, 16, LCB_Unordered> {
private:
unsigned int dirtyCount;
union {
uint64_t defaultInt;
double defaultDouble;
};
inline LinkMapElem* getMapElem(uint64_t key) const {
void* pool = start;
while(pool) {
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled && elem.data.key == key)
return &elem.data;
}
pool = getHeader(pool).next;
}
return nullptr;
}
template<typename T, bool isDouble>
inline void setTyped(uint64_t key, uint64_t value, T dirtyResolution) {
void* emptyElem = nullptr;
void* emptyPool = nullptr;
void* pool = start;
while(pool) {
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled) {
if(elem.data.key == key) {
if(!elem.data.dirty && dirtyResolution >= 0) {
if(dirtyResolution == 0) {
elem.data.dirty = true;
}
else if(isDouble) {
elem.data.value = value;
if(fabs(elem.data.doubleValue - elem.data.prevDoubleValue) >= dirtyResolution) {
elem.data.dirty = true;
dirtyCount++;
}
}
else {
elem.data.value = value;
if(llabs((int64_t)elem.data.value - (int64_t)elem.data.prevValue) >= dirtyResolution) {
elem.data.dirty = true;
dirtyCount++;
}
}
}
else {
elem.data.value = value;
}
return;
}
}
else {
emptyElem = &elem;
emptyPool = pool;
}
}
pool = getHeader(pool).next;
}
if(emptyElem) {
auto& header = getHeader(emptyPool);
header.filledElements++;
auto& elem = getElem(emptyElem);
elem.data.key = key;
elem.data.dirty = true;
elem.data.value = value;
elem.data.prevValue = value;
elem.filled = true;
count++;
dirtyCount++;
}
else {
auto* pool = getLast();
LinkMapElem newElem;
newElem.key = key;
newElem.dirty = true;
newElem.value = value;
newElem.prevValue = value;
if(pool)
getHeader(pool).next = allocate(newElem);
else
start = allocate(newElem);
count++;
dirtyCount++;
}
}
public:
LinkMapBase() : defaultInt(0), dirtyCount(0) {
}
LinkMapBase(uint64_t defaultValue) : defaultInt(defaultValue), dirtyCount(0) {
}
LinkMapBase(double defaultValue) : defaultDouble(defaultValue), dirtyCount(0) {
}
void setDefaultValue(uint64_t newValue) {
defaultInt = newValue;
}
uint64_t getDefaultValue() const {
return defaultInt;
}
void setDefaultDouble(double newValue) {
defaultDouble = newValue;
}
double getDefaultDouble() const {
return defaultDouble;
}
unsigned int size() const {
return count;
}
uint64_t getKeyAtIndex(unsigned int index) const {
if(auto* elem = getAt(index))
return elem->key;
return -1;
}
uint64_t getAtIndex(unsigned int index) const {
if(auto* elem = getAt(index))
return elem->value;
return defaultInt;
}
double getDoubleAtIndex(unsigned int index) const {
if(auto* elem = getAt(index))
return elem->doubleValue;
return defaultDouble;
}
uint64_t get(uint64_t key) const {
auto* elem = getMapElem(key);
if(elem)
return elem->value;
else
return defaultInt;
}
double getDouble(uint64_t key) const {
auto* elem = getMapElem(key);
if(elem)
return elem->doubleValue;
else
return defaultDouble;
}
template<typename CB>
void iterateAll(CB cb) const {
void* pool = start;
while(pool) {
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled)
cb(elem.data.key, elem.data.value);
}
pool = getHeader(pool).next;
}
}
template<typename CB>
void iterateDirty(CB cb) const {
void* pool = start;
while(pool) {
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled && elem.data.dirty)
cb(elem.data.key, elem.data.value);
}
pool = getHeader(pool).next;
}
}
template<typename CB>
void handleDirty(CB cb) {
void* pool = start;
while(pool) {
for(unsigned char i = 0; i < PoolSize; ++i) {
auto& elem = getElem(pool, i);
if(elem.filled && elem.data.dirty) {
if(cb(elem.data.key, elem.data.value)) {
elem.data.prevValue = elem.data.value;
elem.data.dirty = false;
dirtyCount--;
}
}
}
pool = getHeader(pool).next;
}
}
bool getDirtyCount() const {
return dirtyCount;
}
bool hasDirty() const {
return dirtyCount != 0;
}
bool isDirty(uint64_t key) const {
auto* elem = getMapElem(key);
return elem != nullptr && elem->dirty;
}
bool contains(uint64_t key) const {
return getMapElem(key) != nullptr;
}
void set(uint64_t key, uint64_t value, int64_t dirtyResolution = 0) {
setTyped<int64_t,true>(key, value, dirtyResolution);
}
void setDouble(uint64_t key, double value, double dirtyResolution = 0.0) {
setTyped<double,true>(key, reinterpret_cast<uint64_t&>(value), dirtyResolution);
}
};
typedef LinkMapBase<> LinkMap;
+225
View File
@@ -0,0 +1,225 @@
#include "threads.h"
template<class T>
struct LockedType {
threads::Mutex lock;
T value;
LockedType() : value() {}
LockedType(T v) : value(v) {}
LockedType(const LockedType& other) : value(other.value) {}
~LockedType() {}
T operator+=(T v) {
lock.lock();
T r = value + v;
value = r;
lock.release();
return r;
}
T operator-=(T v) {
lock.lock();
T r = value - v;
value = r;
lock.release();
return r;
}
T operator*=(T v) {
lock.lock();
T r = value * v;
value = r;
lock.release();
return r;
}
T operator/=(T v) {
lock.lock();
T r = value / v;
value = r;
lock.release();
return r;
}
T operator|=(T v) {
lock.lock();
T r = value | v;
value = v;
lock.release();
return r;
}
T operator^=(T v) {
lock.lock();
T r = value ^ v;
value = v;
lock.release();
return r;
}
T operator&=(T v) {
lock.lock();
T r = value & v;
value = v;
lock.release();
return r;
}
T operator=(T v) {
lock.lock();
value = v;
lock.release();
return v;
}
T minimum(T v) {
lock.lock();
T r = v < value ? v : value;
value = r;
lock.release();
return r;
}
T maximum(T v) {
lock.lock();
T r = v > value ? v : value;
value = r;
lock.release();
return r;
}
T avg(T v) {
lock.lock();
T r = (T)( ((double)v + (double)value) * 0.5 );
value = r;
lock.release();
return r;
}
T consume(T amount) {
lock.lock();
T take;
if(value < amount)
if(value > 0)
take = value;
else
take = 0;
else
take = amount;
value -= take;
lock.release();
return take;
}
T interp(T toward, double percent) {
lock.lock();
T r = (T)( ((double)(toward - value) * percent) + (double)toward );
value = r;
lock.release();
return r;
}
T toggle() {
lock.lock();
T r = value == 0 ? T(1) : T(0);
value = r;
lock.release();
return r;
}
};
template<class T>
struct LockedHandle {
//Define an invalid pointer as a 'locked' state for a handle. Should be
//faster than a full mutex and takes less memory.
static const unsigned spinCount = 5;
static const size_t INVALID_PTR = (size_t)-1;
mutable void* value;
LockedHandle() : value(0) {}
LockedHandle(T* v) : value(0) { set(v); }
LockedHandle(const LockedHandle<T>& other) : value(0) { set(other.get()); }
~LockedHandle() { set(0); }
T* acquire() const {
void* ptr = value;
int spins = 0;
while(ptr == (void*)INVALID_PTR || threads::compare_and_swap(&value, ptr, (void*)INVALID_PTR) != ptr) {
ptr = value;
++spins;
if(spins == spinCount) {
threads::sleep(0);
spins = 0;
}
}
return (T*)ptr;
}
void release(T* ptr) const {
value = ptr;
}
LockedHandle& operator=(T* value) {
set(value);
return *this;
}
//Get the pointer value
// Will grab a refenence before returning,
// so make sure you release it when done.
T* get() const {
T* ptr = acquire();
if(ptr)
ptr->grab();
release(ptr);
return ptr;
}
//Safe get for when it's assured no writes are taking
//place. Still needs to avoid other reads, though.
T* get_safe() const {
T* ptr;
do {
ptr = (T*)value;
}
while(ptr == (T*)INVALID_PTR);
if(ptr)
ptr->grab();
return ptr;
}
//Set the value, keeping all the reference stuff valid.
void set(T* ptr) {
if(ptr)
ptr->grab();
set_withref(ptr);
}
void set_withref(T* ptr) {
void* newval = (void*)ptr;
void* oldval = (void*)value;
int spins = 0;
while(true) {
if(oldval == (void*)INVALID_PTR) {
oldval = value;
++spins;
if(spins == spinCount) {
threads::sleep(0);
spins = 0;
}
continue;
}
void* res = threads::compare_and_swap(&value, oldval, newval);
if(res == oldval)
break;
oldval = res;
};
ptr = (T*)oldval;
if(ptr)
ptr->drop();
}
};
+149
View File
@@ -0,0 +1,149 @@
#pragma once
#include "threads.h"
#include <functional>
//Implements a 'lockless' data type. Operations can still
//take longer amounts of time depending on concurrency, but
//are not as expensive as a full locked type.
#define LOCKLESS_PRE \
union { swappable s; T real; } local, stored;\
stored.s = swap;\
local.s = 0;\
T& previous = stored.real;\
do {
#define LOCKLESS_POST\
swappable chk = threads::compare_and_swap(&swap, stored.s, local.s);\
if(chk == stored.s)\
break;\
stored.s = chk;\
}\
while (true);
template<class T, class swappable = long long>
struct LocklessType {
static_assert(sizeof(T) <= sizeof(swappable), "type too big");
union {
swappable swap;
T value;
};
LocklessType() : swap(0) {}
LocklessType(T v) : swap(0) { set(v); }
LocklessType(const LocklessType& other) : value(other.value) {}
~LocklessType() {}
//Atomically perform an operation on the value
//The operation should be pure (no side effects) and
//efficient (can be executed multiple times viably).
// Returns the new value that was placed.
T set(const std::function<T(T)>& operation) {
union { swappable s; T real; } local, stored;
stored.s = swap;
local.s = 0;
do {
local.real = operation(stored.real);
swappable chk = threads::compare_and_swap(&swap, stored.s, local.s);
if(chk == stored.s)
break;
stored.s = chk;
} while(true);
return stored.s;
}
T get() {
return value;
}
T set(T v) {
value = v;
return v;
}
operator T() {
return get();
}
T operator+=(T v) {
return set([v](T p) -> T { return p + v; });
}
T operator-=(T v) {
return set([v](T p) -> T { return p - v; });
}
T operator*=(T v) {
return set([v](T p) -> T { return p * v; });
}
T operator/=(T v) {
return set([v](T p) -> T { return p / v; });
}
T operator|=(T v) {
return set([v](T p) -> T { return p | v; });
}
T operator^=(T v) {
return set([v](T p) -> T { return p ^ v; });
}
T operator&=(T v) {
return set([v](T p) -> T { return p & v; });
}
T operator=(T v) {
return set(v);
}
T operator|(T v) {
return value | v;
}
T operator&(T v) {
return value & v;
}
T minimum(T v) {
return set([v](T p) -> T { return v < p ? v : p; });
}
T maximum(T v) {
return set([v](T p) -> T { return v > p ? v : p; });
}
T avg(T v) {
return set([v](T p) -> T { return (T)(((double)v + (double)p) * 0.5); });
}
T consume(T amount) {
T take;
set([&](T p) -> T {
if(p < amount)
if(p > 0)
take = p;
else
take = 0;
else
take = amount;
return p - take;
});
return take;
}
T interp(T toward, double percent) {
return set([&](T p) -> T {
return (T)(((double)(toward - p) * percent) + (double)toward);
});
}
T toggle() {
return set([&](T p) -> T {
return p == 0 ? (T)1 : (T)0;
});
}
};
typedef LocklessType<int,int> LocklessInt;
typedef LocklessType<float,int> LocklessFloat;
typedef LocklessType<double,long long> LocklessDouble;
+68
View File
@@ -0,0 +1,68 @@
#include "mesh.h"
#include "constants.h"
Mesh* generateSphereMesh(unsigned int vertical, unsigned int horizontal) {
if(vertical < 2)
vertical = 2;
if(horizontal < 2)
horizontal = 2;
float vf = (float)vertical, hf = (float)horizontal;
Mesh& mesh = *(new Mesh());
mesh.vertices.reserve((vertical+1) * (horizontal+1));
//v == 0
for(unsigned int h = 0; h <= horizontal; ++h) {
Vertex vert;
vert.position = vec3f(0,1,0);
vert.normal = vert.position;
vert.u = (float)h/hf;
vert.v = 0;
mesh.vertices.push_back(vert);
}
for(unsigned int v = 1; v < vertical; ++v) {
float sinz = (float)sin(pi/2.f - (pi * (float)v/vf));
float cosz = (float)cos(pi/2.f - (pi * (float)v/vf));
for(unsigned int h = 0; h <= horizontal; ++h) {
float angle = (float)twopi * (float)(h % horizontal)/hf; //Force angle on both ends to be identical
Vertex vert;
vert.position = vec3f(cos(angle) * cosz, sinz, sin(angle) * cosz);
vert.normal = vert.position;
vert.u = (float)h/hf;
vert.v = (float)v/vf;
mesh.vertices.push_back(vert);
}
}
//v == vertical
for(unsigned int h = 0; h <= horizontal; ++h) {
Vertex vert;
vert.position = vec3f(0,-1,0);
vert.normal = vert.position;
vert.u = (float)h/hf;
vert.v = 1.f;
mesh.vertices.push_back(vert);
}
mesh.faces.reserve((2 * (vertical-2) * horizontal) + (2 * horizontal));
for(unsigned int v = 0; v < vertical; ++v) {
unsigned int stride = horizontal+1, line = v * stride, nextline = (v+1) * stride;
for(unsigned int h = 0; h < horizontal; ++h) {
if(v != 0)
mesh.faces.push_back(Mesh::Face(h+line,h+line+1,h+nextline));
if(v != vertical-1)
mesh.faces.push_back(Mesh::Face(h+line+1,h+nextline+1,h+nextline));
}
}
return &mesh;
}
+4
View File
@@ -0,0 +1,4 @@
#pragma once
struct Mesh;
Mesh* generateSphereMesh(unsigned int vertical, unsigned int horizontal);
+207
View File
@@ -0,0 +1,207 @@
#include "name_generator.h"
#include "compat/misc.h"
#include "str_util.h"
#include "util/random.h"
#include <fstream>
#ifndef NAMEGEN_MUTATE_END_PCT
#define NAMEGEN_MUTATE_END_PCT 0.1
#endif
NameGenerator::NameGenerator() {
clear();
}
void NameGenerator::clear() {
data.clear();
names.clear();
nameStarts.clear();
usedNames.clear();
preventDuplicates = false;
useGeneration = true;
mutationChance = 0;
}
void NameGenerator::read(const std::string& filename) {
std::ifstream file(filename);
skipBOM(file);
if(file.is_open()) {
while(true) {
std::string line;
std::getline(file, line);
if(file.fail())
break;
line = trim(line);
if(line.size() > 0)
addName(line);
}
}
}
void NameGenerator::write(const std::string& filename) {
std::ofstream file(filename);
foreach(it, names)
file << *it << "\n";
}
static int randomchar(bool start = false) {
if(start) {
return randomi((int)'A', (int)'Z');
}
else {
if(randomf() < NAMEGEN_MUTATE_END_PCT)
return 0;
return randomi((int)'a', (int)'z');
}
}
std::string NameGenerator::generate() {
if(names.size() == 0)
return "Error";
if(!useGeneration)
return names[randomi(0, names.size() - 1)];
std::string name;
while(true) {
std::pair<int, int> syl;
name = "";
//Add the start of a name
if(mutationChance != 0 && randomf() < mutationChance) {
syl.first = randomchar(true);
syl.second = randomchar();
u8append(name, syl.first);
u8append(name, syl.second);
}
else {
int chance = randomi(0, names.size() - 1);
int cum = 0;
foreach(it, nameStarts) {
cum += it->second;
if(chance < cum) {
syl.first = it->first.first;
syl.second = it->first.second;
u8append(name, syl.first);
u8append(name, syl.second);
break;
}
}
}
//Continue on
int next = 0;
do {
auto it = data.find(syl);
if(it == data.end())
break;
ProbData& pd = it->second;
if(mutationChance != 0 && randomf() < mutationChance) {
next = randomchar();
syl.first = syl.second;
syl.second = next;
if(next)
u8append(name, next);
}
else {
int chance = randomi(0, pd.occurances - 1);
int cum = 0;
foreach(it, pd.nextCount) {
cum += it->second;
if(chance < cum) {
next = it->first;
syl.first = syl.second;
syl.second = next;
if(next)
u8append(name, next);
break;
}
}
}
} while(next);
//Do duplicate prevention
if(!preventDuplicates)
return name;
if(usedNames.find(name) == usedNames.end()) {
usedNames.insert(name);
return name;
}
}
}
bool NameGenerator::hasName(const std::string& name) {
foreach(it, names)
if(*it == name)
return true;
return false;
}
unsigned NameGenerator::getNameCount() {
return names.size();
}
void NameGenerator::addName(const std::string& name) {
u8it it(name);
//Add start of word
std::pair<int,int> syl;
syl.first = it++;
if(!syl.first)
return;
syl.second = it++;
if(!syl.second)
return;
int next = it++;
if(!next)
return;
names.push_back(name);
auto f = nameStarts.find(syl);
if(f == nameStarts.end())
nameStarts[syl] = 1;
else
nameStarts[syl]++;
//Add association for lowercase
if(syl.first >= 'A' && syl.first <= 'Z')
addAssociation(syl.first-'A'+'a', syl.second, next);
//Add all the syllables
while(next) {
addAssociation(syl.first, syl.second, next);
syl.first = syl.second;
syl.second = next;
next = it++;
}
addAssociation(syl.first, syl.second, next);
}
void NameGenerator::addAssociation(int first, int second, int next) {
std::pair<int,int> syl;
syl.first = first;
syl.second = second;
ProbData* pd = 0;
auto it = data.find(syl);
if(it == data.end()) {
ProbData newData;
newData.occurances = 0;
data.insert(std::pair<std::pair<int,int>,ProbData>(syl, newData));
pd = &data[syl];
}
else {
pd = &it->second;
}
pd->occurances += 1;
auto f = pd->nextCount.find(next);
if(f == pd->nextCount.end())
pd->nextCount[next] = 1;
else
pd->nextCount[next]++;
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <map>
#include <unordered_map>
#include <vector>
#include <set>
//Second order markov chain name generator
class NameGenerator {
public:
struct ProbData {
int occurances;
std::unordered_map<int, int> nextCount;
};
std::map<std::pair<int,int>,ProbData> data;
std::vector<std::string> names;
std::map<std::pair<int,int>,int> nameStarts;
std::set<std::string> usedNames;
float mutationChance;
bool useGeneration;
bool preventDuplicates;
NameGenerator();
void clear();
void read(const std::string& filename);
void write(const std::string& filename);
bool hasName(const std::string& name);
void addName(const std::string& name);
void addAssociation(int first, int second, int next);
unsigned getNameCount();
std::string generate();
};
+187
View File
@@ -0,0 +1,187 @@
#include <random>
#include "threads.h"
#include <time.h>
#include "constants.h"
#include "vec3.h"
#include "vec2.h"
#include "main/references.h"
#include "os/driver.h"
#include "random.h"
#if defined(_MSC_VER)
#include <intrin.h>
#define GET_MSB(var, x) do{ unsigned long _index_; _BitScanReverse(&_index_, x); var = _index_ + 1; } while(false)
#elif defined(__GNUC__)
#define GET_MSB(var, x) do { var = (32 - __builtin_clz(x)); } while(false)
#else
unsigned _get_msb(unsigned v) {
unsigned index = 0;
while((1 << index) <= v)
++index;
return index;
}
#define GET_MSB(var, x) do { var = _get_msb(x); } while(false)
#endif
Threaded(std::mt19937*) engine;
void seed(unsigned long seed);
void initRandomizer() {
engine = new std::mt19937();
seed((unsigned long)time(0) ^ (unsigned long)threads::getThreadID());
}
void freeRandomizer() {
delete engine;
}
void seed(unsigned long seed) {
engine->seed(seed);
}
unsigned sysRandomi() {
unsigned ret = 0;
if(!devices.driver->systemRandom((unsigned char*)&ret, 4)) {
if(engine)
ret = (unsigned)randomi();
else {
initRandomizer();
ret = (unsigned)randomi();
freeRandomizer();
}
}
return ret;
}
double randomd() {
//Choose random numbers until we don't get max
unsigned m = engine->max();
unsigned r;
do {
r = (*engine)();
} while(r == m);
return (double)r / (double)m;
}
double randomd(double min, double max) {
return randomd() * (max - min) + min;
}
double normald(double min, double max, int steps) {
double sum = 0;
for(int i = 0; i < steps; ++i)
sum += randomd();
return min + (max-min)*sum/(double)steps;
}
float randomf() {
return (float)randomd();
}
float randomf(float min, float max) {
return (float)(randomd() * (double)(max - min)) + min;
}
unsigned randomi() {
return (*engine)();
}
int randomi(int min, int max) {
unsigned range = (unsigned)max - (unsigned)min;
if(range == 0)
return min;
unsigned msb;
GET_MSB(msb, range);
unsigned mask = 0xffffffff >> (32 - msb);
//Choose uniformly distributed values until one falls into our range
//Worst case scenario is the possible values is split in half (+1), so it will still resolve quickly
unsigned r;
do {
r = (unsigned)((*engine)());
} while((r & mask) > range);
return min + (r & mask);
}
vec3d random3d(double radius) {
double theta = randomd(0, twopi);
double u = randomd(-1.0, 1.0);
double s = sqrt(1.0-(u*u));
vec3d out;
out.x = s * cos(theta) * radius;
out.y = s * sin(theta) * radius;
out.z = u * radius;
return out;
}
vec3d random3d(double minRadius, double maxRadius) {
return random3d(minRadius + (maxRadius - minRadius) * sqrt(randomd()));
}
vec2d random2d(double radius) {
double theta = randomd(0, twopi);
return vec2d(radius * cos(theta), radius * sin(theta));
}
vec2d random2d(double minRadius, double maxRadius) {
return random2d(minRadius + (maxRadius - minRadius) * sqrt(randomd()));
}
class MersenneEngine : public RandomEngine {
std::mt19937 rnd;
public:
void seed(unsigned initial) {
rnd.seed((unsigned long)initial);
}
unsigned randomi() {
return rnd();
}
unsigned randomi(unsigned min, unsigned max) {
unsigned range = (unsigned)max - (unsigned)min;
if(range == 0)
return min;
unsigned msb;
GET_MSB(msb, range);
unsigned mask = 0xffffffff >> (32 - msb);
//Choose uniformly distributed values until one falls into our range
//Worst case scenario is the possible values is split in half (+1), so it will still resolve quickly
unsigned r;
do {
r = (unsigned)(rnd());
} while((r & mask) > range);
return min + (r & mask);
}
double randomd() {
unsigned m = rnd.max();
unsigned r;
do {
r = rnd();
} while(r == m);
return (double)r / (double)m;
}
double randomd(double min, double max) {
return (randomd() * (max - min)) + min;
}
};
RandomEngine* RandomEngine::makeMersenne(unsigned seed) {
auto* engine = new MersenneEngine();
engine->seed(seed);
return engine;
}
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include "vec3.h"
#include "vec2.h"
// Generates random numbers with a thread-local generator
void initRandomizer();
void freeRandomizer();
void seed(unsigned long seed);
//Returns a high-quality random integer (slow)
unsigned sysRandomi();
//Random double in (0.0, 1.0)
double randomd();
//Random double in (min, max)
double randomd(double min, double max);
//Pseudo-normal distribution within min,max
double normald(double min, double max, int steps = 4);
//Random float in (0.0, 1.0)
float randomf();
//Random float in (min, max)
float randomf(float min, float max);
//Random int in [0, INT_MAX]
unsigned randomi();
//Random int in [min, max]
int randomi(int min, int max);
//Random on sphere surface
vec3d random3d(double radius = 1.0);
//Random on a donut sphere
vec3d random3d(double minRadius, double maxRadius);
//Random on circle circumference
vec2d random2d(double radius = 1.0);
//Random on a donut circle
vec2d random2d(double minRadius, double maxRadius);
class RandomEngine {
public:
virtual void seed(unsigned initial) = 0;
virtual unsigned randomi() = 0;
virtual unsigned randomi(unsigned min, unsigned max) = 0;
virtual double randomd() = 0;
virtual double randomd(double min, double max) = 0;
static RandomEngine* makeMersenne(unsigned seed);
};
+129
View File
@@ -0,0 +1,129 @@
#pragma once
#include "threads.h"
template <class T>
class _RefCounted {
public:
mutable T refs;
_RefCounted() : refs(1) {}
void grab() const { ++refs; }
void drop() const { if(!--refs) delete this; }
virtual ~_RefCounted() {}
};
typedef _RefCounted<int> RefCounted;
typedef _RefCounted<threads::atomic_int> AtomicRefCounted;
template<class _T>
struct heldPointer {
//Actual Pointer - do not directly alter without handling the grab/drop yourself
_T* ptr;
//Handles all grabbing and dropping when a pointer changes value
inline void operator= (_T* newPtr) {
//Grabs the new first, incase new == old
//We don't explicitly check, because that's a relatively unlikely case, and only adds overhead where not necessary
if(newPtr)
newPtr->grab();
if(ptr)
ptr->drop();
ptr = newPtr;
}
inline void operator= (const heldPointer<_T>& other) {
//Grabs the new first, incase new == old
//We don't explicitly check, because that's a relatively unlikely case, and only adds overhead where not necessary
if(other.ptr)
other.ptr->grab();
if(ptr)
ptr->drop();
ptr = other.ptr;
}
inline void operator= (heldPointer<_T>&& other) {
if(ptr)
ptr->drop();
ptr = other.ptr;
other.ptr = 0;
}
inline bool operator< (const heldPointer<_T>& other) {
return ptr < other.ptr;
}
//Only drop()s the previous pointer, does not grab the new pointer
inline void set(_T* newPtr) {
if(ptr)
ptr->drop();
ptr = newPtr;
}
//Clears the value of the pointer, not handling reference counting
inline void reset() {
ptr = 0;
}
inline _T* operator-> () {
return ptr;
}
inline const _T* operator-> () const {
return ptr;
}
inline operator _T*() {
return ptr;
}
inline operator const _T*() const {
return ptr;
}
inline void swap(heldPointer<_T>& other) {
_T* swap_ptr = ptr;
ptr = other.ptr;
other.ptr = swap_ptr;
}
//Checks if the object pointed to is valid ( via ->isValid() )
//If it isn't drop the object, and return false
bool validate() {
if(ptr) {
if(ptr->isValid())
return true;
ptr->drop();
ptr = 0;
}
return false;
}
heldPointer() : ptr(0) {}
heldPointer(_T& start) : ptr(&start) {
ptr->grab();
}
heldPointer(_T* start) : ptr(start) {
if(ptr)
ptr->grab();
}
heldPointer(const heldPointer<_T>& copy) : ptr(copy.ptr) {
if(ptr)
ptr->grab();
}
heldPointer(heldPointer<_T>&& move) : ptr(move.ptr) {
move.ptr = 0;
}
~heldPointer() {
if(ptr)
ptr->drop();
}
};
+355
View File
@@ -0,0 +1,355 @@
#include "save_file.h"
#ifdef _MSC_VER
#include "zlib/zlib.h"
#else
#include "zlib.h"
#endif
#include <stdio.h>
#include "compat/misc.h"
#include "obj/object.h"
#include <unordered_map>
#include "files.h"
#include "main/logging.h"
#include "main/game_platform.h"
#include "main/references.h"
#include "scripts/context_cache.h"
const char* saveIdentifier = "$SR2";
const unsigned gzBufferSize = 128000;
class SaveFileWriter : public SaveFile {
unsigned boundaryID;
gzFile out;
const std::string destName, tempName;
std::vector<std::unordered_map<std::string,int>> identifiers;
bool addIdentifier(unsigned type, int id, const std::string& ident) {
if(type >= identifiers.size()) {
identifiers.resize(type+1);
}
else {
auto it = identifiers[type].find(ident);
if(it != identifiers[type].end())
return false;
}
identifiers[type][ident] = id;
return true;
}
void saveIdentifiers() {
unsigned cnt = identifiers.size();
*this << cnt;
for(unsigned i = 0; i < cnt; ++i) {
auto& mp = identifiers[i];
unsigned icnt = mp.size();
*this << icnt;
foreach(it, mp) {
*this << it->first;
*this << it->second;
}
}
}
void writeIdentifier(unsigned type, int id) {
*this << id;
}
void write(const void* source, unsigned bytes) {
if(bytes != 0)
gzwrite(out,source,bytes);
}
void close() {
std::string destTempName = destName + ".temp";
gzclose(out); out = 0;
//Move the existing save file to a .temp version
int oldMoved = rename(destName.c_str(), destTempName.c_str());
//Move our temp file to the intended destination
int result = rename(tempName.c_str(), destName.c_str());
if(result == 0) {
//Saving succeeded, remove the old save file
remove(destTempName.c_str());
if(devices.cloud)
devices.cloud->writeCloudFile(destName, std::string("saves/") + getBasename(destName));
delete this;
}
else {
//Saving failed, remove the temp file and replace the old save file
remove(tempName.c_str());
if(oldMoved == 0)
rename(destTempName.c_str(), destName.c_str());
delete this;
throw SaveFileError("Could not save file");
}
}
void read(void* dest, unsigned bytes) {
throw SaveFileError("Cannot read while writing");
}
void boundary() {
*this << boundaryID++;
}
public:
SaveFileWriter(const std::string& file) : boundaryID(100), destName(file), tempName(getTemporaryFile()) {
out = gzopen(tempName.c_str(),"wb1f");
scriptVersion = 0;
startVersion = 0;
if(out == 0)
throw SaveFileError("Could not open temporary file");
gzbuffer(out, gzBufferSize);
write(saveIdentifier, 4);
*this << SFV_Current;
}
};
void readSaveFileInfo(SaveFile& file, SaveFileInfo& info) {
file >> info.version;
if(file >= SFV_0005)
file >> info.startVersion;
else
info.startVersion = info.version;
unsigned cnt = 0;
file >> cnt;
info.mods.resize(cnt);
for(unsigned i = 0; i < cnt; ++i) {
file >> info.mods[i].id;
if(file >= SFV_0011)
file >> info.mods[i].version;
else
info.mods[i].version = 0;
}
}
bool getSaveFileInfo(const std::string& fname, SaveFileInfo& info) {
try {
auto* pfile = SaveFile::open(fname, SM_Read);
if(pfile == nullptr)
return false;
pfile->loadIdentifiers();
readSaveFileInfo(*pfile, info);
pfile->close();
return true;
}
catch(SaveFileError& err) {
error("Failed to read save '%s':\n %s", fname.c_str(), err.text);
return false;
}
}
class SaveFileReader : public SaveFile {
unsigned boundaryID;
gzFile in;
std::vector<std::unordered_map<std::string,int>> identifiers;
std::vector<std::unordered_map<int,int>> identMap;
std::vector<std::unordered_map<std::string,int>> loaded;
bool addIdentifier(unsigned type, int id, const std::string& ident) {
if(type >= identifiers.size()) {
identifiers.resize(type+1);
}
else {
auto it = identifiers[type].find(ident);
if(it != identifiers[type].end())
return false;
}
identifiers[type][ident] = id;
return true;
}
void addDummyLoadIdentifier(unsigned type, int id, const std::string& ident) {
if(type >= loaded.size())
loaded.resize(type+1);
loaded[type][ident] = id;
}
void loadIdentifiers() {
unsigned cnt = *this;
loaded.resize(cnt);
for(unsigned i = 0; i < cnt; ++i) {
auto& table = loaded[i];
unsigned icnt = *this;
std::string ident;
int id;
for(unsigned j = 0; j < icnt; ++j) {
*this >> ident;
*this >> id;
table[ident] = id;
}
}
}
void finalizeIdentifiers() {
unsigned cnt = (unsigned)loaded.size();
identMap.resize(cnt);
if(cnt > identifiers.size())
identifiers.resize(cnt);
for(unsigned i = 0; i < cnt; ++i) {
auto& mp = identifiers[i];
auto& table = identMap[i];
foreach(it, loaded[i]) {
auto ft = mp.find(it->first);
if(ft != mp.end())
table[it->second] = ft->second;
}
}
}
unsigned getPrevIdentifierCount(unsigned type) {
if(type >= identMap.size())
return 0;
return identMap[type].size();
}
unsigned getIdentifierCount(unsigned type) {
if(type >= identifiers.size())
return 0;
return identifiers[type].size();
}
int getIdentifier(unsigned type, int id) {
if(type >= identMap.size())
return -1;
auto ft = identMap[type].find(id);
if(ft == identMap[type].end())
return -1;
return ft->second;
}
int readIdentifier(unsigned type) {
int id;
*this >> id;
if(type >= identMap.size())
return -1;
auto ft = identMap[type].find(id);
if(ft == identMap[type].end())
return -1;
return ft->second;
}
void read(void* dest, unsigned bytes) {
if(bytes != 0) {
int readBytes = gzread(in, dest, bytes);
if(readBytes < int(bytes)) {
scripts::logException();
throw SaveFileError("Unexpected end of file");
}
}
}
void write(const void* source, unsigned bytes) {
throw SaveFileError("Cannot write while reading");
}
void close() {
gzclose(in);
delete this;
}
void boundary() {
unsigned checkID = *this;
if(checkID != boundaryID)
throw SaveFileError("Boundary did not match");
++boundaryID;
}
public:
SaveFileReader(const std::string& file) : boundaryID(100) {
scriptVersion = 0;
startVersion = 0;
in = gzopen(file.c_str(), "rb");
if(in == 0)
throw SaveFileError("Could not open save file");
gzbuffer(in, gzBufferSize);
char buff[5]; buff[4] = '\0';
read(buff, 4);
if(strcmp(buff, saveIdentifier) != 0)
throw SaveFileError("Not a Star Ruler save");
*this >> version;
if(version < SFV_EarliestSupported)
throw SaveFileError("Save file version no longer supported");
else if(version >= SFV_Future)
throw SaveFileError("Save file from a newer version, please update");
}
};
SaveFile* SaveFile::open(const std::string& file, SaveMode mode) {
if(mode == SM_Read)
return new SaveFileReader(file);
else if(mode == SM_Write)
return new SaveFileWriter(file);
else
throw SaveFileError("Invalid save file mode");
}
SaveFile& SaveFile::operator<<(const char* str) {
size_t len = strlen(str);
if(len > 0xffff)
throw SaveFileError("String too long");
unsigned short shortLen = (unsigned short)len;
write(&shortLen,2);
write(str,shortLen);
return *this;
}
SaveFile& SaveFile::operator<<(const std::string& str) {
size_t len = str.size();
if(len > 0xffff)
throw SaveFileError("String too long");
unsigned short shortLen = (unsigned short)len;
write(&shortLen,2);
if(shortLen > 0)
write(str.c_str(),shortLen);
return *this;
}
SaveFile& SaveFile::operator>>(std::string& str) {
unsigned short length;
read(&length,2);
if(length > 0) {
void* temp = alloca(length);
read(temp,length);
str.assign((const char*)temp, length);
}
return *this;
}
SaveFile& SaveFile::operator>>(Object*& obj) {
obj = getObjectByID(read<int>(), true);
return *this;
}
Object* SaveFile::readExistingObject() {
return getObjectByID(read<int>());
}
SaveFile& SaveFile::operator<<(const Object* obj) {
int id = obj ? obj->id : 0;
return *this << id;
}
SaveFile::~SaveFile() {}
+286
View File
@@ -0,0 +1,286 @@
#pragma once
#include <vector>
#include <string>
#include "network/message.h"
class Object;
//SaveFile
//
//Create with SaveFile::open("file path", SF_Read or SF_Write)
//Close with SaveFile.close()
//
//In write mode, write data via << syntax, e.g.
// SaveFile << myVariable;
// const char* and std::string may both be written this way
//Direct write is available via write(data, bytes)
// write(&myVariable, sizeof(myVariable))
//
//
//In read mode, read data via >> syntax, e.g.
// SaveFile >> myVariable;
// Strings written via << may be read into a std::string this way
//Direct read is available via read(data, bytes)
// read(&myVariable, sizeof(myVariable)
//
//To support different version formats, use comparison syntax, e.g.
// if(SaveFile > SFV_0005) {}
//
//Strings may be saved as a std::string or a const char*, but only loaded as std::string
//
//Errors are thrown as SaveFileError()
enum SaveMode {
SM_Read,
SM_Write
};
enum SaveIdentifier {
SI_Subsystem,
SI_SubsystemVar,
SI_HexVar,
SI_ShipVar,
SI_Hull,
SI_Shipset,
SI_Effector,
SI_Effect,
SI_SubsystemModule,
SI_SubsystemModifier,
SI_SCRIPT_START = 32,
};
struct SaveFileError {
const char* text;
SaveFileError(const char* Text) : text(Text) {}
};
struct SavedMod {
std::string id;
unsigned short version;
};
struct SaveFileInfo {
unsigned version;
unsigned startVersion;
std::vector<SavedMod> mods;
};
enum SaveFileVersion {
SFV_0000 = 0,
SFV_0001 = 0,
SFV_0002,
SFV_0003,
SFV_0004,
SFV_0005,
SFV_0006,
SFV_0007,
SFV_0008,
SFV_0009,
SFV_0010,
SFV_0011,
SFV_0012,
SFV_0013,
SFV_0014,
SFV_0015,
SFV_0016,
SFV_0017,
SFV_0018,
SFV_0019,
SFV_0020,
SFV_0021,
SFV_0022,
SFV_Future,
SFV_Current = SFV_Future - 1,
SFV_EarliestSupported = SFV_0000
};
class SaveFile {
protected:
virtual ~SaveFile();
SaveFileVersion version;
public:
unsigned scriptVersion;
unsigned startVersion;
//Opens a file in either read or write mode
//Throws SaveFileError if the file cannot be accessed
static SaveFile* open(const std::string& file, SaveMode mode);
//Closes and deletes the SaveFile
//Throws SaveFileError if something didn't succeed, but still deletes the SaveFile
virtual void close() = 0;
//Version checks
bool operator>(SaveFileVersion Version) const {
return version > Version;
}
bool operator>=(SaveFileVersion Version) const {
return version >= Version;
}
bool operator<(SaveFileVersion Version) const {
return version < Version;
}
bool operator<=(SaveFileVersion Version) const {
return version <= Version;
}
bool operator==(SaveFileVersion Version) const {
return version == Version;
}
bool operator!=(SaveFileVersion Version) const {
return version != Version;
}
//Helper functions
//Marks a boundary location; During a load, checks that the boundary is present
virtual void boundary() = 0;
//Write functions
virtual void write(const void* source, unsigned bytes) = 0;
template<class type>
SaveFile& operator<<(const type& data) {
write(&data, sizeof(type));
return *this;
}
template<class type>
SaveFile& operator<<(const type* data) {
write(data, sizeof(type));
return *this;
}
SaveFile& operator<<(const char* str);
SaveFile& operator<<(char* str) {
return *this << (const char*)str;
}
SaveFile& operator<<(const std::string& str);
SaveFile& operator<<(std::string& str) {
return *this << (const std::string&)str;
}
SaveFile& operator<<(const Object* obj);
SaveFile& operator<<(Object* obj) {
return *this << (const Object*)obj;
}
template<class type>
SaveFile& writeConditional(bool condition, const type& data) {
*this << condition;
if(condition)
*this << data;
return *this;
}
//Read function
virtual void read(void* dest, unsigned bytes) = 0;
template<class type>
type read() {
type temp;
read(&temp, sizeof(type));
return temp;
}
template<class type>
SaveFile& operator>>(type& data) {
read(&data, sizeof(type));
return *this;
}
template<class type>
SaveFile& operator>>(const type* data) {
read(data, sizeof(type));
return *this;
}
SaveFile& operator>>(std::string& str);
SaveFile& operator>>(Object*& obj);
Object* readExistingObject();
template<class type>
bool readConditional(type& data) {
bool condition;
*this >> condition;
if(condition)
*this >> data;
else
data = type();
return condition;
}
template<class type>
operator type() {
type temp;
*this >> temp;
return temp;
}
//Indentifier tables
virtual bool addIdentifier(unsigned type, int id, const std::string& ident) = 0;
virtual void addDummyLoadIdentifier(unsigned type, int id, const std::string& ident) {};
virtual void saveIdentifiers() {};
virtual void loadIdentifiers() {};
virtual void finalizeIdentifiers() {};
virtual int readIdentifier(unsigned type) { return -1; };
virtual int getIdentifier(unsigned type, int id) { return -1; };
virtual unsigned getPrevIdentifierCount(unsigned type) { return 0; }
virtual unsigned getIdentifierCount(unsigned type) { return 0; }
virtual void writeIdentifier(unsigned type, int id) {};
};
void readSaveFileInfo(SaveFile& file, SaveFileInfo& info);
bool getSaveFileInfo(const std::string& file, SaveFileInfo& info);
class SaveMessage : public net::Message {
public:
SaveFile& file;
SaveMessage(SaveFile& sav) : net::Message(), file(sav) {
}
SaveMessage(SaveMessage& other) : net::Message(other), file(other.file) {
}
void operator=(const SaveMessage& other) {
net::Message::operator=(other);
}
bool operator>(SaveFileVersion Version) const {
return file > Version;
}
bool operator>=(SaveFileVersion Version) const {
return file >= Version;
}
bool operator<(SaveFileVersion Version) const {
return file < Version;
}
bool operator<=(SaveFileVersion Version) const {
return file <= Version;
}
bool operator==(SaveFileVersion Version) const {
return file == Version;
}
bool operator!=(SaveFileVersion Version) const {
return file != Version;
}
};
+47
View File
@@ -0,0 +1,47 @@
#include "stat_history.h"
void StatEntry::addEvent(unsigned short type, const std::string& name) {
StatEvent* newEvent = new StatEvent;
newEvent->name = name;
newEvent->type = type;
if(!evt) {
evt = newEvent;
}
else {
newEvent->next = evt;
evt = newEvent;
}
}
StatHistory::StatHistory() : head(0), tail(0) {
}
StatEntry* StatHistory::addStatEntry(unsigned time) {
if(tail && tail->time >= time) {
return tail;
}
StatEntry* entry = new StatEntry;
entry->time = time;
if(tail) {
tail->next = entry;
entry->prev = tail;
tail = entry;
}
else {
head = entry;
tail = entry;
}
return entry;
}
const StatEntry* StatHistory::getHead() const {
return head;
}
StatEntry* StatHistory::getTail() const {
return tail;
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <string>
struct StatEvent {
std::string name;
StatEvent* next;
unsigned short type;
StatEvent() : type(0), next(0) {}
};
struct StatEntry {
StatEntry* next, *prev;
StatEvent* evt;
unsigned time;
union {
int asInt;
float asFloat;
};
void addEvent(unsigned short type, const std::string& name);
StatEntry() : next(0), prev(0), time(0), evt(0), asInt(0) {}
};
class StatHistory {
StatEntry* head, *tail;
public:
StatHistory();
StatEntry* addStatEntry(unsigned time);
const StatEntry* getHead() const;
StatEntry* getTail() const;
};
+227
View File
@@ -0,0 +1,227 @@
#include "threaded_loader.h"
#include "threads.h"
#include <unordered_map>
#include <set>
#include <list>
#include "main/references.h"
#include "main/logging.h"
namespace Loading {
struct Task;
std::function<void(void)> threadSetup, threadCleanup;
threads::atomic_int workers(0);
bool finalized = false;
double startTime;
threads::Mutex taskLock;
threads::Signal processingTasks;
bool tasksFinished;
std::unordered_map<std::string, Task*> namedTasks;
std::list<Task*> tasks;
auto nextTask = tasks.end();
struct Task {
std::string name;
std::vector<Task*> dependencies;
bool finished;
double executionTime;
int threadRestriction;
std::function<void(void)> _execute;
Task(const std::string& Name, const char* depends) : name(Name), finished(false), threadRestriction(-1) {
while(depends && depends[0] != '\0') {
const char* end = strchr(depends+1, ',');
if(end == 0)
end = strchr(depends, '\0');
std::string dependencyName(depends, end - depends);
if(dependencyName == name)
throw "Cylic loading depedency";
auto dependency = namedTasks.find(dependencyName);
if(dependency != namedTasks.end())
dependencies.push_back(dependency->second);
else
throw "Missing dependency";
depends = *end == ',' ? end+1 : end;
}
#ifdef _DEBUG
if(namedTasks.find(Name) != namedTasks.end())
throw "Duplicate task";
#endif
namedTasks[Name] = this;
}
bool mayExecute() {
if(threadRestriction != threads::invalidThreadID && threadRestriction != threads::getThreadID())
return false;
for(unsigned i = 0; i < dependencies.size(); ++i)
if(!dependencies[i]->isFinished())
return false;
return true;
}
bool isMyJob() {
return threadRestriction == threads::getThreadID();
}
void execute() {
if(isFinished())
return;
double start = devices.driver->getAccurateTime();
info("%s started at %.1fs on thread %i", name.c_str(), start - startTime, threads::getThreadID());
_execute();
double end = devices.driver->getAccurateTime();
executionTime = end - start;
finished = true;
info("%s took %.1fms on thread %i", name.c_str(), executionTime * 1000.0, threads::getThreadID());
}
bool isFinished() {
return finished;
}
};
void addTask(const std::string& name, const char* depends, std::function<void(void)> execute, int threadRestriction) {
Task* task = new Task(name, depends);
task->_execute = execute;
task->threadRestriction = threadRestriction;
taskLock.lock();
tasks.push_back(task);
taskLock.release();
}
bool finished() {
return tasksFinished;
}
threads::threadreturn threadcall processLoad(void* arg) {
if(threadSetup)
threadSetup();
while(!finalized || !tasks.empty()) {
process();
threads::sleep(1);
}
--workers;
//This lets us have accurate timings, since we want
//to know when the last task finished, not when
//the loading period is over (ie for preloading)
if(workers == 0) {
processingTasks.wait(0);
double totalTime = 0;
auto iTask = namedTasks.begin();
while(iTask != namedTasks.end()) {
totalTime += iTask->second->executionTime;
delete iTask->second;
iTask = namedTasks.erase(iTask);
}
double time = devices.driver->getAccurateTime(), loadTime = time - startTime;
print("Loaded in %.1f seconds", loadTime);
info("Tasks used a total of %.1f seconds (%d%% faster)", totalTime, (int)(100.0*totalTime/loadTime)-100);
tasksFinished = true;
}
if(threadCleanup)
threadCleanup();
return 0;
}
void prepare(unsigned threads, std::function<void(void)> threadPrep, std::function<void(void)> threadExit) {
threadSetup = threadPrep;
threadCleanup = threadExit;
tasksFinished = false;
startTime = devices.driver->getAccurateTime();
workers = threads;
for(unsigned i = 0; i < threads; ++i)
threads::createThread(processLoad,0);
}
void finalize() {
double time = devices.driver->getAccurateTime();
info("Preparing tasks took %.1f ms", (time - startTime) * 1000.0);
finalized = true;
}
void finish() {
while(workers != 0)
threads::sleep(0);
finalized = false;
nextTask = tasks.end();
}
void process() {
processingTasks.signalUp();
if(tasks.empty()) {
processingTasks.signalDown();
return;
}
taskLock.lock();
Task* task = 0;
for(auto i = tasks.begin(), end = tasks.end(); i != end; ++i) {
Task* check = *i;
if(check->isMyJob() && check->mayExecute()) {
task = check;
if(nextTask == i)
nextTask = tasks.erase(i);
else
tasks.erase(i);
break;
}
}
if(task == 0) {
while(tasks.empty() == false) {
if(nextTask == tasks.end())
nextTask = tasks.begin();
Task* check = *nextTask;
if(check->mayExecute()) {
nextTask = tasks.erase(nextTask);
task = check;
break;
}
++nextTask;
taskLock.release();
threads::sleep(0);
taskLock.lock();
}
}
taskLock.release();
if(task) {
task->execute();
if(!task->isFinished()) {
taskLock.lock();
tasks.push_back(task);
taskLock.release();
}
}
processingTasks.signalDown();
}
};
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <string>
#include <functional>
namespace Loading {
void prepare(unsigned threads, std::function<void(void)> threadPrep, std::function<void(void)> threadExit);
void finalize();
void finish();
bool finished();
void addTask(const std::string& name, const char* depends, std::function<void(void)> execute, int threadRestriction = 0);
void process();
};