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
+478
View File
@@ -0,0 +1,478 @@
#include "scripts/binds.h"
#include "obj/lock.h"
#include "obj/object.h"
#include "obj/universe.h"
#include "obj/obj_group.h"
#include "empire.h"
#include "main/references.h"
#include "render/render_state.h"
#include "render/render_mesh.h"
#include "render/lighting.h"
#include "scene/mesh_node.h"
#include "scene/mesh_icon_node.h"
#include "scene/icon_node.h"
#include "scene/animation/anim_node_sync.h"
#include "scene/animation/anim_group.h"
#include "scene/scripted_node.h"
#include "scene/culling_node.h"
#include "util/random.h"
#include "vec2.h"
#include "color.h"
#include "processing.h"
#include "physics/physics_world.h"
namespace scripts {
struct ObjectDesc {
int type;
unsigned flags;
std::string name;
double radius;
vec3d position;
Empire* owner;
bool delayedCreation;
ObjectDesc() : type(0), flags(objValid | objUninitialized),
radius(1.0), owner(0), delayedCreation(false) {
}
void reset() {
type = 0;
flags = objValid | objUninitialized;
radius = 1.0;
owner = 0;
delayedCreation = false;
name.clear();
position = vec3d();
}
};
void createDesc(void* mem) {
new(mem) ObjectDesc();
}
bool hintLocks = false;
LockGroup* hintedGroup = 0;
unsigned hintID = 0;
Object* makeObject(ObjectDesc& desc) {
ScriptObjectType* stype = getScriptObjectType(desc.type);
if(stype == 0) {
throwException("Invalid object type to create.");
return 0;
}
LockGroup* group = 0;
if(hintLocks)
group = hintedGroup;
else if(LockGroup* active = getActiveLockGroup())
group = active;
Object* obj = Object::create(stype, group);
obj->flags = desc.flags;
obj->name = desc.name;
obj->radius = desc.radius;
obj->position = desc.position;
if(desc.owner)
obj->owner = desc.owner;
else
obj->owner = Empire::getDefaultEmpire();
if(!obj->getFlag(objNoPhysics))
obj->physItem = devices.physics->registerItem(AABBoxd::fromCircle(obj->position, obj->radius), obj);
if(desc.delayedCreation)
obj->setFlag(objStopTicking, true);
obj->owner->registerObject(obj);
devices.universe->addChild(obj);
obj->init();
if(hintLocks) {
if(!group) {
hintedGroup = obj->lockGroup;
hintID = randomi(1,INT_MAX);
obj->lockHint = hintID;
}
else {
obj->lockHint = hintID;
}
}
obj->grab();
if(!desc.delayedCreation)
obj->postInit();
return obj;
}
ObjectGroup* makeGroup(ObjectDesc& desc, unsigned count) {
ScriptObjectType* stype = getScriptObjectType(desc.type);
if(stype == 0) {
throwException("Invalid object type to create.");
return 0;
}
LockGroup* lockGroup = 0;
if(hintLocks)
lockGroup = hintedGroup;
ObjectGroup* group = new ObjectGroup(count);
group->grab();
for(unsigned i = 0; i < count; ++i) {
Object* obj = Object::create(stype, lockGroup);
if(hintLocks)
obj->lockHint = hintID;
lockGroup = obj->lockGroup;
obj->name = desc.name;
obj->radius = desc.radius;
obj->position = desc.position;
if(desc.owner)
obj->owner = desc.owner;
else
obj->owner = Empire::getDefaultEmpire();
if(desc.delayedCreation)
obj->setFlag(objStopTicking, true);
obj->group = group;
group->grab();
if(!obj->getFlag(objNoPhysics)) {
obj->physItem = group->getPhysicsItem(i);
obj->physItem->bound = AABBoxd::fromCircle(obj->position, obj->radius);
obj->physItem->gridLocation = 0;
obj->physItem->type = PIT_Object;
obj->physItem->object = obj;
obj->grab();
}
group->setObject(i, obj);
}
group->postInit();
for(unsigned i = 0; i < count; ++i) {
Object* obj = group->getObject(i);
obj->owner->registerObject(obj);
devices.universe->addChild(obj);
obj->init();
if(!desc.delayedCreation)
obj->postInit();
}
return group;
}
struct LightDesc {
Colorf diffuse;
Colorf specular;
float att_quadratic;
vec3f position;
float radius;
LightDesc() : att_quadratic(0.f) {
}
};
void createLight(void* mem) {
new(mem) LightDesc();
}
void makeLight(LightDesc& desc) {
auto* light = new render::light::PointLight();
light->diffuse = desc.diffuse;
light->specular = desc.specular;
light->att_quadratic = desc.att_quadratic;
light->position = desc.position;
light->radius = desc.radius;
render::light::registerLight(light);
}
void makeNodeLight(LightDesc& desc, scene::Node* follow) {
auto* light = new render::light::NodePointLight(follow); //grabbed by constructor
light->diffuse = desc.diffuse;
light->specular = desc.specular;
light->att_quadratic = desc.att_quadratic;
light->position = desc.position;
light->radius = desc.radius;
render::light::registerLight(light);
}
struct MeshDesc {
const render::RenderState* material;
const render::RenderMesh* mesh;
const render::SpriteSheet* iconSheet;
unsigned iconIndex;
bool memorable;
Colorf color;
MeshDesc() : material(0), mesh(0), iconSheet(0), iconIndex(0), memorable(false) {
}
};
void createMeshDesc(void* mem) {
new(mem) MeshDesc();
}
void bindMesh(Object* obj, MeshDesc& desc) {
if(obj->node) {
throwException("Binding mesh to object that already has a node.");
return;
}
if(!desc.material || !desc.mesh) {
throwException("Mesh descriptor data incomplete (missing material/model?).");
return;
}
scene::Node* mesh;
if(!desc.iconSheet)
mesh = new scene::MeshNode(desc.mesh, desc.material);
else
mesh = new scene::MeshIconNode(desc.mesh, desc.material,
desc.iconSheet, desc.iconIndex);
mesh->position = mesh->abs_position = obj->position;
mesh->scale = mesh->abs_scale = obj->radius;
mesh->color = desc.color;
mesh->setObject(obj);
mesh->animator = scene::NodeSyncAnimator::getSingleton();
mesh->createPhysics();
mesh->setFlag(scene::NF_Memorable, desc.memorable);
auto* player = Empire::getPlayerEmpire();
if(player)
mesh->visible = obj->isVisibleTo(player);
obj->node = mesh;
mesh->queueReparent(devices.scene);
}
void bindGroupIcon(ObjectGroup* group, const render::SpriteSheet* sheet, unsigned spriteIndex) {
scene::Node* icon = new scene::IconNode(sheet, spriteIndex);
icon->position = group->getCenter();
double radius = group->getOwner()->radius;
icon->scale = 0.3 * (1.0 + radius) / (3.0 + radius);
icon->animator = new scene::GroupAnim(group);
icon->queueReparent(devices.scene);
}
scene::Node* bindCullingNode(Object* obj, const vec3d& pos, double radius) {
if(obj->node) {
throwException("Binding culling node to object that already has a node.");
return nullptr;
}
scene::Node* node = new scene::CullingNode(pos, radius);
node->grab();
node->setObject(obj);
obj->node = node;
node->queueReparent(devices.scene);
return node;
}
scene::Node* createCullingNode(const vec3d& pos, double radius) {
scene::Node* node = new scene::CullingNode(pos, radius);
node->grab();
node->queueReparent(devices.scene);
return node;
}
scene::Node* bindNode(Object* obj, const std::string& typeName) {
scene::Node* node = scene::ScriptedNode::create(typeName);
if(node) {
node->grab();
node->position = obj->position;
node->scale = obj->radius;
node->setObject(obj);
node->animator = scene::NodeSyncAnimator::getSingleton();
node->createPhysics();
auto* player = Empire::getPlayerEmpire();
if(player)
node->visible = obj->isVisibleTo(player);
obj->node = node;
node->queueReparent(devices.scene);
}
return node;
}
scene::Node* makeNode(const std::string& typeName) {
scene::Node* node = scene::ScriptedNode::create(typeName);
if(node)
node->queueReparent(devices.scene);
return node;
}
bool* startLockHint() {
hintLocks = true;
return &hintLocks;
}
bool* objLockHint(Object* obj) {
hintLocks = true;
hintedGroup = obj->lockGroup;
if(obj->lockHint)
hintID = obj->lockHint;
else
hintID = randomi(1,INT_MAX);
return &hintLocks;
}
void endLockHint(bool* mem) {
hintLocks = false;
hintedGroup = 0;
}
void RegisterObjectCreation(bool declarations) {
if(declarations) {
ClassBind desc("ObjectDesc", asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C, sizeof(ObjectDesc));
ClassBind mdesc("MeshDesc", asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C, sizeof(MeshDesc));
ClassBind ldesc("LightDesc", asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C, sizeof(LightDesc));
ClassBind lhint("LockHint", asOBJ_REF | asOBJ_SCOPED, 0);
return;
}
ClassBind desc("ObjectDesc");
classdoc(desc, "Descriptor for data to create objects.");
ClassBind mdesc("MeshDesc");
classdoc(mdesc, "Descriptor to create a mesh representation for an object.");
ClassBind ldesc("LightDesc");
classdoc(ldesc, "Descriptor to create a 3d light instance.");
ClassBind lhint("LockHint");
classdoc(lhint, "Scoped structure. Any objects created within the same lock hint will"
" start in the same lock group, reducing initial variability.");
//Objects
desc.addMember("ObjectType type", offsetof(ObjectDesc, type))
doc("Type of the object to create.");
desc.addMember("uint flags", offsetof(ObjectDesc, flags))
doc("Flags the object starts with.");
desc.addMember("string name", offsetof(ObjectDesc, name))
doc("Name the object is given.");
desc.addMember("bool delayedCreation", offsetof(ObjectDesc, delayedCreation))
doc("If true, delay finalizing the creation until the script can do"
" some of its own initialization steps. Don't forget to call"
" finalizeCreation() on the object afterwards.");
desc.addMember("double radius", offsetof(ObjectDesc, radius))
doc("Size of the object's bounding sphere.");
desc.addMember("vec3d position", offsetof(ObjectDesc, position))
doc("Position the object is spawned at.");
desc.addMember("Empire@ owner", offsetof(ObjectDesc, owner))
doc("Initial owner of the object.");
desc.addMethod("void reset()", asMETHOD(ObjectDesc, reset))
doc("Reset the descriptor to its initial empty state.");
desc.addConstructor("void f()", asFUNCTION(createDesc));
bind("Object@ makeObject(const ObjectDesc& desc)", asFUNCTION(makeObject))
doc("Create an object based on a description of it.",
"Object descriptor to use.", "Created object.");
bind("ObjectGroup@ makeObjectGroup(ObjectDesc& desc, uint count)", asFUNCTION(makeGroup))
doc("Create an object group based on a description of an object.",
"Object descriptor to use.", "Number of objects to create in the group.", "Created object.");
//Meshes
mdesc.addMember("const Material@ material", offsetof(MeshDesc, material))
doc("Texture to render on the model.");
mdesc.addMember("const Model@ model", offsetof(MeshDesc, mesh))
doc("Model to use to display the object.");
mdesc.addMember("const SpriteSheet@ iconSheet", offsetof(MeshDesc, iconSheet))
doc("Spritesheet to render distant icon from, if given.");
mdesc.addMember("uint iconIndex", offsetof(MeshDesc, iconIndex))
doc("Icon in the spritesheet to use as a distant icon.");
mdesc.addMember("Colorf color", offsetof(MeshDesc, color))
doc("Base color to set for the graphics node.");
mdesc.addMember("bool memorable", offsetof(MeshDesc, memorable))
doc("Whether the node represents a memorable object.");
mdesc.addConstructor("void f()", asFUNCTION(createMeshDesc));
bind("void bindMesh(Object& obj, MeshDesc& desc)", asFUNCTION(bindMesh))
doc("Bind a graphical representation to an object in accordance to a mesh descriptor.",
"Game object to bind to.", "Descriptor for the mesh to display.");
bind("void bindGroupIcon(ObjectGroup& group, const SpriteSheet@ sheet, uint index)", asFUNCTION(bindGroupIcon))
doc("Binds an icon to represent a ship group.", "", "", "");
bind("Node@ bindCullingNode(Object& obj, const vec3d&in position, double radius)", asFUNCTION(bindCullingNode))
doc("Bind a node that can be used for region culling to the object.", "Object to bind to.",
"Center position for the cullable region.", "Radius of the cullable region.", "Handle to the created node.");
bind("Node@ createCullingNode(const vec3d&in position, double radius)", asFUNCTION(createCullingNode))
doc("Create a node that can be used for region culling.",
"Center position for the cullable region.", "Radius of the cullable region.",
"Handle to the created node.");
bind("Node@ bindNode(Object& obj, const string &in type)", asFUNCTION(bindNode))
doc("Bind a custom graphical node to an object.", "Object to bind to.", "Type of script node to create.", "Handle to the created node.");
bind("Node@ createNode(const string &in type)", asFUNCTION(makeNode));
//Lights
ldesc.addMember("Colorf diffuse", offsetof(LightDesc, diffuse))
doc("Diffuse light color.");
ldesc.addMember("Colorf specular", offsetof(LightDesc, specular))
doc("Specular light color.");
ldesc.addMember("float att_quadratic", offsetof(LightDesc, att_quadratic))
doc("Quadratic attenuation.");
ldesc.addMember("float radius", offsetof(LightDesc, radius))
doc("Radius associated with the light.");
ldesc.addMember("vec3f position", offsetof(LightDesc, position))
doc("Position of the light in 3d space.");
ldesc.addConstructor("void f()", asFUNCTION(createLight));
bind("void makeLight(LightDesc& desc)", asFUNCTION(makeLight))
doc("Create a light based on a description of it.",
"Light descriptor to use.");
bind("void makeLight(LightDesc& desc, Node& node)", asFUNCTION(makeNodeLight))
doc("Create a light following a particular scene node.",
"Light descriptor to use.", "Node to follow with the light.");
//Locking hints
lhint.addFactory("LockHint@ f()", asFUNCTION(startLockHint));
lhint.addFactory("LockHint@ f(Object& obj)", asFUNCTION(objLockHint));
lhint.addExternBehaviour(asBEHAVE_RELEASE, "void f()", asFUNCTION(endLockHint));
}
};
File diff suppressed because it is too large Load Diff
+425
View File
@@ -0,0 +1,425 @@
#include "scripts/binds.h"
#include "main/references.h"
#include "main/logging.h"
#include "util/refcount.h"
#include "str_util.h"
#include "manager.h"
#include <fstream>
#include <sstream>
#include <stdio.h>
#include <time.h>
#include "files.h"
std::set<std::string> validPaths;
namespace scripts {
//Only allow access on files inside the star ruler directory or the profile
bool isAccessible(const std::string& filename) {
std::string workdir = getWorkingDirectory();
if(path_inside(workdir, filename))
return true;
std::string profiledir = getProfileRoot();
if(path_inside(profiledir, filename))
return true;
for(auto path = validPaths.begin(), end = validPaths.end(); path != end; ++path)
if(path_inside(*path, filename))
return true;
return false;
}
std::string getModProfile() {
return devices.mods.getProfile();
}
std::string getModProfileChild(const std::string& dirname) {
return devices.mods.getProfile(dirname);
}
std::string getBaseProfileChild(const std::string& dirname) {
return devices.mods.getGlobalProfile(dirname);
}
static void createDir(const std::string& dirname) {
if(!isAccessible(dirname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return;
}
makeDirectory(dirname);
}
static long long modTime(const std::string& fname) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return 0;
}
return (long long)getModifiedTime(fname);
}
static bool isDir(const std::string& dirname) {
if(!isAccessible(dirname))
return false;
return isDirectory(dirname);
}
static bool fileEx(const std::string& fname) {
if(!isAccessible(fname))
return false;
return fileExists(fname);
}
class ReadFile : public DataReader {
threads::atomic_int references;
public:
ReadFile(const std::string& filename, bool AllowLines) : DataReader(filename, AllowLines), references(1) {
if(!isAccessible(filename))
scripts::throwException("Cannot access file outside game or profile directories.");
}
void error(const std::string& str) {
::error("Error: %s\n %s", position().c_str(), str.c_str());
}
void grab() {
++references;
}
void drop() {
if(--references == 0)
delete this;
}
};
static ReadFile* makeReadFile(const std::string& filename, bool allowLines) {
return new ReadFile(filename.c_str(), allowLines);
}
static bool readNext(ReadFile& rf) {
return rf++;
}
class WriteFile : public AtomicRefCounted {
std::fstream file;
public:
int curIndent;
bool allowMultiline;
std::string endl;
WriteFile(const char* filename) : file(filename, std::ios_base::out | std::ios_base::binary), curIndent(0), allowMultiline(true), endl("\r\n") {
std::string fname = filename;
if(!isAccessible(fname))
scripts::throwException("Cannot access file outside game or profile directories.");
}
bool is_open() const { return file.is_open() && file.good(); }
bool is_atEnd() const { return file.eof(); }
void indent(int amount = 1) {
curIndent += amount;
}
void deindent(int amount = 1) {
curIndent -= amount;
}
void writeLine(const std::string& line) {
file << line << endl;
}
void writeKeyValue(const std::string& key, const std::string& val) {
if(!is_open()) {
throwException("File is not open");
return;
}
for(int i = 0; i < curIndent; ++i)
file << " ";
if(allowMultiline) {
size_t prev = 0;
size_t pos = val.find('\n');
if(pos != std::string::npos) {
file << key << ": <<" << endl;
do {
std::string line = val.substr(prev, pos - prev);
if(line.empty()) {
file << endl;
}
else {
for(int i = 0; i < curIndent+1; ++i)
file << " ";
file << val.substr(prev, pos - prev) << endl;
}
prev = pos+1;
if(prev < val.size()) {
pos = val.find('\n', prev);
if(pos == std::string::npos)
pos = val.size();
}
else {
break;
}
}
while(true);
for(int i = 0; i < curIndent; ++i)
file << " ";
file << ">>" << endl;
return;
}
}
else {
if(val.find('\n') != std::string::npos) {
file << key << ": " << escape(val) << endl;
return;
}
}
file << key << ": " << val << endl;
}
void writeEmptyLines(unsigned char n) {
unsigned i = n;
while(i--)
file << endl;
}
};
static WriteFile* makeWriteFile(const std::string& filename) {
return new WriteFile(filename.c_str());
}
const std::string err("ERR");
class FileList : public AtomicRefCounted {
public:
std::vector<std::pair<std::string,std::string>> files;
void navigate(const std::string& dirname, const std::string& filter, bool recurse, bool resolve) {
files.clear();
std::map<std::string, std::string> fmap;
std::string root = getProfileRoot();
if(!resolve || path_inside(root, dirname)) {
listDir(dirname, filter, recurse);
}
else {
devices.mods.listFiles(dirname, fmap, filter.c_str(), recurse);
files.reserve(fmap.size());
foreach(it, fmap)
files.push_back(std::pair<std::string,std::string>(it->first, it->second));
}
}
void listDir(const std::string& dirname, const std::string& filter, bool recurse = true) {
std::vector<std::string> flatfiles;
listDirectory(dirname, flatfiles, filter.c_str());
foreach(it, flatfiles) {
std::string abspath = path_join(dirname, *it);
files.push_back(std::pair<std::string,std::string>(*it, abspath));
if(recurse && ::isDirectory(abspath))
listDir(abspath, filter);
}
}
unsigned size() {
return (unsigned)files.size();
}
const std::string& relative(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return err;
}
return files[i].first;
}
std::string basename(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return err;
}
return getBasename(files[i].first);
}
std::string extension(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return err;
}
auto pos = files[i].first.rfind('.');
if(pos == std::string::npos)
return "";
return files[i].first.substr(pos+1);
}
const std::string& absolute(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return err;
}
return files[i].second;
}
bool isDirectory(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return false;
}
return ::isDirectory(files[i].second);
}
};
FileList* makeFileList(const std::string& dirname, const std::string& filter, bool recurse, bool resolve) {
FileList* flist = new FileList();
flist->navigate(dirname, filter, recurse, resolve);
return flist;
}
FileList* makeFileList_e() {
return new FileList();
}
static std::string resolveFilename(const std::string& fname) {
return devices.mods.resolve(fname);
}
//TODO: Restrict to the active mod?
static void deleteFile(const std::string& fname) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return;
}
if(isDirectory(fname)) {
std::vector<std::string> inside;
listDirectory(fname, inside);
foreach(it, inside)
deleteFile(path_join(fname, *it));
}
remove(fname.c_str());
}
static long long systemTime() {
time_t t;
::time(&t);
return (long long)t;
}
static std::string strftime_wrap(const std::string& format, long long stamp) {
time_t time = (time_t)stamp;
struct tm* timeinfo;
timeinfo = localtime(&time);
char buffer[120];
unsigned cnt = strftime(buffer, 120, format.c_str(), timeinfo);
return std::string(buffer, cnt);
}
void RegisterDatafiles() {
bind("string get_profileRoot()", asFUNCTION(getProfileRoot))
doc("", "Path to the game's global profile folder.");
bind("string get_modProfile()", asFUNCTION(getModProfile))
doc("", "Path to the current mod's profile folder.");
bind("string get_modProfile(const string&in folder)", asFUNCTION(getModProfileChild))
doc("Get the path to a child directory in the mod's profile.",
"Child directory", "Path to the folder.");
bind("string get_baseProfile(const string&in folder)", asFUNCTION(getBaseProfileChild))
doc("Get the path to a child directory in the base profile.",
"Child directory", "Path to the folder.");
bind("string resolve(const string&in filename)", asFUNCTION(resolveFilename))
doc("Resolves the filename to an absolute path to an overriden file. "
"The top-most mod with a file in the same relative path is used.",
"Relative filename to resolve.", "");
bind("void deleteFile(const string&in filename)", asFUNCTION(deleteFile))
doc("Deletes the specified file. Must be within the profile or game folders.", "");
bind("void makeDirectory(const string&in dirname)", asFUNCTION(createDir))
doc("Create a new directory.", "Name of the directory to create.");
bind("bool isDirectory(const string&in path)", asFUNCTION(isDir))
doc("Check whether a particular path is a directory.", "Path of the directory to check.",
"Whether a directory exists at that path.");
bind("int64 getModifiedTime(const string&in path)", asFUNCTION(modTime))
doc("Get the timestamp a file was last modified at.", "Path of file.",
"Last modification time of file.");
bind("bool fileExists(const string&in path)", asFUNCTION(fileEx))
doc("Check whether a particular file exists.", "Path of the file to check.",
"Whether a file exists at that path.");
bind("string path_join(const string&in first, const string&in second)", asFUNCTION(path_join))
doc("Concatenate two file paths.", "First path.", "Second path.", "Concatenated path.");
bind("string path_up(const string&in path)", asFUNCTION(path_up))
doc("Go up one directory from a path.", "Path to go up from.", "Parent path.");
bind("bool path_inside(const string&in dirname, const string&in fname)", asFUNCTION(path_inside))
doc("Check whether a filename is inside a directory.", "Folder name.", "Filename"., "Whether the file is inside the folder.");
bind("string getBasename(const string&in path, bool includeExtension = true)", asFUNCTION(getBasename))
doc("Get the basename of a path.", "Path to get basename from.", "Whether to include the extension in the basename.",
"Path basename.");
bind("int64 getSystemTime()", asFUNCTION(systemTime));
bind("string strftime(const string& format, int64 time)", asFUNCTION(strftime_wrap));
ClassBind read("ReadFile", asOBJ_REF);
read.addFactory("ReadFile@ f(const string &in filename, bool allowLines = false)", asFUNCTION(makeReadFile))
doc("Opens a text datafile for reading.", "Path to the file to open. Must be within the profile or game folders.", "", "");
read.setReferenceFuncs(asMETHOD(ReadFile,grab), asMETHOD(ReadFile,drop));
read.addMember("int indent", offsetof(ReadFile, indent))
doc("Number of indents for the current line. Each space or tab counts as one indent.");
read.addMember("bool allowLines", offsetof(ReadFile, allowLines));
read.addMember("bool fullLine", offsetof(ReadFile, fullLine));
read.addMember("bool allowMultiline", offsetof(ReadFile, allowMultiline));
read.addMember("bool skipComments", offsetof(ReadFile, skipComments));
read.addMember("bool skipEmpty", offsetof(ReadFile, skipEmpty));
read.addMember("string line", offsetof(ReadFile, line))
doc("Full (trimmed) line.");
read.addMember("string key", offsetof(ReadFile, key))
doc("Key from a Key:Value pair line.");
read.addMember("string value", offsetof(ReadFile, value))
doc("Value from a Key:Value pair line.");
read.addMethod("void error(const string& message)", asMETHOD(ReadFile, error))
doc("Display an error message sourced from this file.", "Error message.");
read.addMethod("string position()", asMETHOD(ReadFile, position))
doc("Returns a formatted representation of the position in the file.", "A string like 'data/states.txt | Line 80'");
read.addExternMethod("bool opPostInc()", asFUNCTION(readNext))
doc("Advances to the next non-empty line.", "Returns true if there was a line to advance to.");
ClassBind write("WriteFile", asOBJ_REF);
write.addFactory("WriteFile@ f(const string &in)", asFUNCTION(makeWriteFile));
write.setReferenceFuncs(asMETHOD(WriteFile,grab), asMETHOD(WriteFile,drop));
write.addMember("bool allowMultiline", offsetof(WriteFile, allowMultiline))
doc("Whether multi-line values should be written in multiline format or squashed into a single line.");
write.addMethod("bool get_open() const", asMETHOD(WriteFile,is_open));
write.addMethod("bool get_atEnd() const", asMETHOD(WriteFile,is_atEnd));
write.addMethod("void indent(int amount = 1)", asMETHOD(WriteFile, indent));
write.addMethod("void deindent(int amount = 1)", asMETHOD(WriteFile, deindent));
write.addMethod("void writeLine(const string &in)", asMETHOD(WriteFile, writeLine));
write.addMethod("void writeKeyValue(const string &in, const string &in)", asMETHOD(WriteFile, writeKeyValue));
write.addMethod("void writeEmptyLines(uint8 count = 1)", asMETHOD(WriteFile, writeEmptyLines));
ClassBind flist("FileList", asOBJ_REF);
flist.addFactory("FileList@ f(const string &in dirname, const string &in filter, bool recurse = false, bool resolve = true)", asFUNCTION(makeFileList));
flist.addFactory("FileList@ f()", asFUNCTION(makeFileList_e));
flist.setReferenceFuncs(asMETHOD(FileList,grab), asMETHOD(FileList,drop));
flist.addMethod("void navigate(const string&in dirname, const string&in filter, bool recurse = false, bool resolve = true)", asMETHOD(FileList, navigate));
flist.addMethod("uint get_length()", asMETHOD(FileList, size));
flist.addMethod("const string& get_relativePath(uint num)", asMETHOD(FileList, relative));
flist.addMethod("string get_basename(uint num)", asMETHOD(FileList, basename));
flist.addMethod("string get_extension(uint num)", asMETHOD(FileList, extension));
flist.addMethod("const string& get_path(uint num)", asMETHOD(FileList, absolute));
flist.addMethod("bool get_isDirectory(uint num)", asMETHOD(FileList, isDirectory));
}
};
File diff suppressed because it is too large Load Diff
+450
View File
@@ -0,0 +1,450 @@
#include "binds.h"
#include "obj/object.h"
#include "empire.h"
#include "util/format.h"
#include "main/logging.h"
#include "main/initialization.h"
#include "main/references.h"
#include "processing.h"
#include "scripts/script_components.h"
#include "scene/scripted_node.h"
extern std::vector<ScriptObjectType*> objTypeList;
extern const StateDefinition* empStates;
extern std::unordered_map<std::string, scene::scriptNodeType*> nodeTypes;
namespace scripts {
extern Object* objTarget(Object* obj, unsigned num);
extern void bindObject(ClassBind& object, ScriptObjectType* type, bool server);
extern void bindScriptNode(ClassBind& bind);
static Object* baseCast(Object* obj) {
if(obj)
obj->grab();
return obj;
}
static void castObj(asIScriptGeneric* f) {
Object* obj = (Object*)f->GetObject();
if(!obj || obj->type != f->GetFunction()->GetUserData()) {
f->SetReturnAddress(0);
}
else {
obj->grab();
f->SetReturnAddress(obj);
}
}
static void isObjectType(asIScriptGeneric* f) {
Object* obj = (Object*)f->GetObject();
if(!obj || obj->type != f->GetFunction()->GetUserData())
f->SetReturnByte(false);
else
f->SetReturnByte(true);
}
static scene::Node* nodeBaseCast(scene::ScriptedNode* node) {
if(node)
node->grab();
return node;
}
static void nodeCast(asIScriptGeneric* f) {
scene::ScriptedNode* node = dynamic_cast<scene::ScriptedNode*>((scene::Node*)f->GetObject());
if(!node || &node->type != f->GetFunction()->GetUserData()) {
f->SetReturnAddress(0);
}
else {
node->grab();
f->SetReturnAddress(node);
}
}
//Stored component offsets for each object type
void SetObjectTypeOffsets() {
auto* bpState = getStateValueType("Blueprint");
for(auto i = objTypeList.begin(), end = objTypeList.end(); i != end; ++i) {
ScriptObjectType* type = *i;
const StateDefinition& def = *type->states;
unsigned compCnt = getComponentCount();
type->componentOffsets.resize(compCnt, 0);
type->optionalComponents.resize(compCnt, false);
unsigned base = sizeof(Object);
StateDefinition::align(base);
for(auto var = def.types.begin(), endvar = def.types.end(); var != endvar; ++var) {
auto& mtype = *var->def;
if(&mtype == bpState)
type->blueprintOffset = base + var->offset;
bool optional = false;
Component* comp = getComponent(&mtype, &optional);
if(comp) {
if(type->componentOffsets[comp->id] != 0) {
error("ERROR: Object type '%s' has two components of type '%s'.",
type->name.c_str(), comp->name.c_str());
}
else {
type->componentOffsets[comp->id] = base + var->offset;
type->optionalComponents[comp->id] = optional;
}
}
}
}
}
static void writeEmpStates(Empire* emp, net::Message& msg) {
auto* def = Empire::getEmpireStates();
if(!def)
return;
def->syncWrite(msg, emp + 1);
}
static void readEmpStates(Empire* emp, net::Message& msg) {
auto* def = Empire::getEmpireStates();
if(!def)
return;
def->syncRead(msg, emp + 1);
}
static void restrictedState(asIScriptGeneric* f) {
Object* obj = (Object*)f->GetObject();
auto* member = (StateDefinition::stateDefMember*)f->GetFunction()->GetUserData();
if(obj->owner != Empire::getPlayerEmpire()) {
info("Cannot access state on unowned objects.");
member->def->setReturn(f, nullptr);
}
else {
void* memory = (void*)(obj + 1);
StateDefinition::align(memory);
member->def->setReturn(f, ((char*)memory) + member->offset);
}
}
static void paramStateGet(asIScriptGeneric* f) {
Object* obj = (Object*)f->GetObject();
auto* member = (StateDefinition::stateDefMember*)f->GetFunction()->GetUserData();
void* memory = (void*)(obj + 1);
StateDefinition::align(memory);
member->def->setReturn(f, ((char*)memory) + member->offset);
}
static void paramStateSet(asIScriptGeneric* f) {
Object* obj = (Object*)f->GetObject();
auto* member = (StateDefinition::stateDefMember*)f->GetFunction()->GetUserData();
void* memory = (void*)(obj + 1);
StateDefinition::align(memory);
member->def->setFromParam(f, ((char*)memory) + member->offset);
}
static void nodeFactory(asIScriptGeneric* f) {
auto* type = (scene::scriptNodeType*)f->GetFunction()->GetUserData();
scene::Node* node = new scene::ScriptedNode(type);
if(processing::isRunning())
node->queueReparent(devices.scene);
else
devices.scene->addChild(node);
*(scene::Node**)f->GetAddressOfReturnLocation() = node;
}
static double configGet(const std::string& name) {
auto it = gameConfig.indices.find(name);
if(it == gameConfig.indices.end())
return 0;
return gameConfig.values[it->second];
}
static void configSet(const std::string& name, double value) {
auto it = gameConfig.indices.find(name);
if(it != gameConfig.indices.end())
gameConfig.values[it->second] = value;
}
static double configGet_id(unsigned id) {
if(id >= gameConfig.count)
return 0;
return gameConfig.values[id];
}
static void configSet_id(unsigned id, double value) {
if(id >= gameConfig.count)
return;
gameConfig.values[id] = value;
}
static std::string configGetName(unsigned id) {
if(id >= gameConfig.count)
return "";
return gameConfig.names[id];
}
static unsigned configGetIndex(const std::string& name) {
auto it = gameConfig.indices.find(name);
if(it != gameConfig.indices.end())
return it->second;
return (unsigned)-1;
}
void RegisterObjectDefinitions() {
//Create the object types (states may refer to them)
for(auto i = objTypeList.begin(), end = objTypeList.end(); i != end; ++i) {
ScriptObjectType* type = *i;
ClassBind object(type->name.c_str(), asOBJ_REF);
}
}
static std::unordered_map<std::string,unsigned> empAttribNames;
static std::vector<std::string> empAttribIdents;
static std::vector<size_t> empAttribOffsets;
double getEmpAttrib(Empire* emp, unsigned index) {
if(index >= empAttribOffsets.size())
return 0.0;
return *(double*)(((char*)emp) + empAttribOffsets[index]);
}
void setEmpAttrib(Empire* emp, unsigned index, double value) {
if(index >= empAttribOffsets.size())
return;
*(double*)(((char*)emp) + empAttribOffsets[index]) = value;
}
unsigned getEmpAttribIndex(const std::string& str) {
auto it = empAttribNames.find(str);
if(it == empAttribNames.end())
return (unsigned)-1;
return it->second;
}
std::string getEmpAttribName(unsigned id) {
if(id >= empAttribIdents.size())
return "";
return empAttribIdents[id];
}
void buildEmpAttribIndices() {
empAttribNames.clear();
empAttribIdents.clear();
empAttribOffsets.clear();
auto* doubleType = getStateValueType("double");
if(!empStates)
return;
unsigned base = sizeof(Empire);
StateDefinition::align(base);
const StateDefinition& def = *empStates;
for(auto var = def.types.begin(), endvar = def.types.end(); var != endvar; ++var) {
//Bind member
auto& mtype = *var->def;
//Doubles are accessible generically in order to facilitate empire attributes
if(&mtype == doubleType && var->attribute) {
empAttribNames[var->name] = empAttribOffsets.size();
empAttribOffsets.push_back(base + var->offset);
empAttribIdents.push_back(var->name);
}
}
}
void RegisterDynamicTypes(bool server) {
//Bind global object states
{
ClassBind object("Object");
const StateDefinition& def = getStateDefinition("Object");
if(&def != &errorStateDefinition) {
//Add state list vars
unsigned base = sizeof(Object);
StateDefinition::align(base);
for(auto var = def.types.begin(), endvar = def.types.end(); var != endvar; ++var) {
//Bind member
auto& mtype = *var->def;
if(mtype.isParam()) {
if(var->def->returnable()) {
if(var->access != SR_Visible) {
object.addGenericMethod(format("$1 get_$2() const",
mtype.returnType.empty() ? mtype.type : mtype.returnType,
var->name).c_str(),
asFUNCTION(restrictedState), (void*)&*var)
doc("Member created from datafiles.", "");
}
else {
object.addGenericMethod(format("$1 get_$2() const",
mtype.returnType.empty() ? mtype.type : mtype.returnType,
var->name).c_str(),
asFUNCTION(paramStateGet), (void*)&*var)
doc("Member created from datafiles.", "");
}
}
if(server) {
object.addGenericMethod(format("void set_$2($1 value)",
mtype.type, var->name).c_str(),
asFUNCTION(paramStateSet), (void*)&*var)
doc("Member created from datafiles.", "");
}
}
else if(server || var->access == SR_Visible) {
object.addMember(format("$1 $2", mtype.type, var->name).c_str(), base + var->offset)
doc("Member created from datafiles.");
}
else if(var->access == SR_Restricted) {
if(var->def->returnable()) {
object.addGenericMethod(format("$1 get_$2() const",
mtype.returnType.empty() ? mtype.type : mtype.returnType,
var->name).c_str(),
asFUNCTION(restrictedState), (void*)&*var)
doc("Member created from datafiles.", "");
}
}
}
}
}
//Bind each custom object type, adding its state list variables in
EnumBind objType("ObjectType", false);
objType["OT_COUNT"] = objTypeList.size();
for(auto i = objTypeList.begin(), end = objTypeList.end(); i != end; ++i) {
ScriptObjectType* type = *i;
const StateDefinition& def = *type->states;
EnumBind objType("ObjectType", false);
objType[std::string("OT_")+type->name] = type->id;
ClassBind genObj("Object");
ClassBind object(type->name.c_str());
classdoc(object,"Derived Object created based on datafiles. Can be cast to and from an Object@.");
bindObject(object, type, server);
RegisterObjectComponentWrappers(object, server, type);
object.addExternMethod("Object@ opImplCast() const",
asFUNCTION(baseCast));
genObj.addGenericMethod(format("$1@ opCast() const", object.name).c_str(),
asFUNCTION(castObj), type);
genObj.addGenericMethod(format("bool get_is$1() const", object.name).c_str(),
asFUNCTION(isObjectType), type);
//Add state list vars
unsigned base = sizeof(Object);
StateDefinition::align(base);
for(auto var = def.types.begin(), endvar = def.types.end(); var != endvar; ++var) {
//Bind member
auto& mtype = *var->def;
if(mtype.isParam()) {
if(var->def->returnable()) {
if(var->access != SR_Visible) {
object.addGenericMethod(format("$1 get_$2() const",
mtype.returnType.empty() ? mtype.type : mtype.returnType,
var->name).c_str(),
asFUNCTION(restrictedState), (void*)&*var)
doc("Member created from datafiles.", "");
}
else {
object.addGenericMethod(format("$1 get_$2() const",
mtype.returnType.empty() ? mtype.type : mtype.returnType,
var->name).c_str(),
asFUNCTION(paramStateGet), (void*)&*var)
doc("Member created from datafiles.", "");
}
}
if(server) {
object.addGenericMethod(format("void set_$2($1 value)",
mtype.type, var->name).c_str(),
asFUNCTION(paramStateSet), (void*)&*var)
doc("Member created from datafiles.", "");
}
}
else if(server || var->access == SR_Visible) {
object.addMember(format("$1 $2", mtype.type, var->name).c_str(), base + var->offset)
doc("Member created from datafiles.");
}
else if(var->access == SR_Restricted) {
if(var->def->returnable()) {
object.addGenericMethod(format("$1 get_$2() const",
mtype.returnType.empty() ? mtype.type : mtype.returnType,
var->name).c_str(),
asFUNCTION(restrictedState), (void*)&*var)
doc("Member created from datafiles.", "");
}
}
}
}
//Bind game config
{
Namespace ns("config");
for(size_t i = 0; i < gameConfig.count; ++i)
bindGlobal(format("double $1", gameConfig.names[i]).c_str(), (void*)&gameConfig.values[i]);
bind("double get(const string& name)", asFUNCTION(configGet));
bind("void set(const string& name, double value)", asFUNCTION(configSet));
bind("double get(uint index)", asFUNCTION(configGet_id));
bind("void set(uint index, double value)", asFUNCTION(configSet_id));
bind("string getName(uint index)", asFUNCTION(configGetName));
bind("uint getIndex(const string& name)", asFUNCTION(configGetIndex));
}
//Bind empire states
if(empStates) {
const StateDefinition& def = *empStates;
auto* doubleType = getStateValueType("double");
EnumBind attr("EmpireAttribute");
ClassBind emp("Empire");
unsigned base = sizeof(Empire);
StateDefinition::align(base);
bind("uint getEmpireAttribute(const string& name)", asFUNCTION(getEmpAttribIndex));
bind("string getEmpireAttributeName(uint id)", asFUNCTION(getEmpAttribName));
emp.addExternMethod("double get_attributes(uint index) const", asFUNCTION(getEmpAttrib));
if(server)
emp.addExternMethod("void set_attributes(uint index, double value)", asFUNCTION(setEmpAttrib));
emp.addExternMethod("void writeSyncedStates(Message& msg)", asFUNCTION(writeEmpStates));
emp.addExternMethod("void readSyncedStates(Message& msg)", asFUNCTION(readEmpStates));
for(auto var = def.types.begin(), endvar = def.types.end(); var != endvar; ++var) {
//Bind member
auto& mtype = *var->def;
emp.addMember(format("$1 $2", mtype.type, var->name).c_str(), base + var->offset)
doc("Member created from datafiles.");
//Doubles are accessible generically in order to facilitate empire attributes
if(&mtype == doubleType && var->attribute)
attr[std::string("EA_")+var->name] = empAttribNames[var->name];
}
attr["EA_COUNT"] = empAttribNames.size();
}
//Node methods
for(auto i = nodeTypes.begin(), end = nodeTypes.end(); i != end; ++i) {
auto* type = i->second;
ClassBind baseNode("Node");
ClassBind node(type->name.c_str(), asOBJ_REF);
classdoc(node,"Derived Node created based on datafiles. Can be cast to and from a Node@.");
node.addGenericFactory(format("$1@ f()", type->name).c_str(), asFUNCTION(nodeFactory), type);
node.addExternMethod("Node@ opImplCast() const",
asFUNCTION(nodeBaseCast));
baseNode.addGenericMethod(format("$1@ opCast() const", type->name).c_str(),
asFUNCTION(nodeCast), type);
RegisterNodeMethodWrappers(node, server, type);
bindScriptNode(node);
}
}
};
+483
View File
@@ -0,0 +1,483 @@
#include "scripts/binds.h"
#include "obj/object.h"
#include "empire.h"
#include "empire_stats.h"
#include "main/references.h"
#include "design/design.h"
#include "vec2.h"
#include "scripts/script_components.h"
#include "util/stat_history.h"
#include "network/network_manager.h"
extern Empire* defaultEmpire;
extern Empire* playerEmpire;
extern Empire* spectatorEmpire;
extern std::unordered_map<std::string,unsigned> statIndices;
namespace scripts {
static const Design* getDesignByName(Empire* emp, const std::string& name) {
return emp->getDesign(name, true);
}
static const Design* getDesignById(Empire* emp, int id) {
return emp->getDesign(id, true);
}
static unsigned getDesignCount(Empire* emp) {
return emp->designIds.size();
}
static unsigned getDesignClassCount(Empire* emp) {
return emp->designClassIds.size();
}
static Empire* empFactory() {
return new Empire();
}
static Empire* empFactory_b(bool unlisted) {
return new Empire(unlisted ? INVALID_EMPIRE : UNLISTED_EMPIRE);
}
static void writeDelta(Empire* emp, net::Message& msg) {
emp->writeDelta(msg);
}
static void readDelta(Empire* emp, net::Message& msg) {
try {
emp->readDelta(msg);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
}
static bool ssUnlocked(Empire* emp, const SubsystemDef* def) {
threads::ReadLock lock(emp->subsystemDataMutex);
Empire::SubsystemData* ssdata = emp->getSubsystemData(def);
return ssdata ? ssdata->unlocked : false;
}
static bool ssModUnlocked(Empire* emp, const SubsystemDef* def, const SubsystemDef::ModuleDesc* mod) {
threads::ReadLock lock(emp->subsystemDataMutex);
Empire::SubsystemData* ssdata = emp->getSubsystemData(def);
if(!ssdata)
return false;
if((int)ssdata->modulesUnlocked.size() <= mod->index)
return false;
return ssdata->modulesUnlocked[mod->index];
}
static void ssSetModUnlock(Empire* emp, const SubsystemDef* def, const SubsystemDef::ModuleDesc* mod, bool val) {
threads::WriteLock lock(emp->subsystemDataMutex);
Empire::SubsystemData* ssdata = emp->getSubsystemData(def);
if(ssdata) {
if((int)ssdata->modulesUnlocked.size() <= mod->index)
ssdata->modulesUnlocked.resize(mod->index+1, false);
ssdata->modulesUnlocked[mod->index] = val;
ssdata->delta = true;
}
}
static void ssSetUnlock(Empire* emp, const SubsystemDef* def, bool val) {
threads::WriteLock lock(emp->subsystemDataMutex);
Empire::SubsystemData* ssdata = emp->getSubsystemData(def);
if(ssdata) {
ssdata->unlocked = val;
ssdata->delta = true;
}
}
static void removeModifier(Empire* emp, const SubsystemDef* def, unsigned id) {
threads::WriteLock lock(emp->subsystemDataMutex);
Empire::SubsystemData* ssdata = emp->getSubsystemData(def);
if(ssdata) {
ssdata->stages.erase(id);
ssdata->delta = true;
}
}
static void addModifier_d(asIScriptGeneric* f) {
Empire* emp = (Empire*)f->GetObject();
threads::WriteLock lock(emp->subsystemDataMutex);
const SubsystemDef* def = (const SubsystemDef*)f->GetArgAddress(0);
Empire::SubsystemData* ssdata = emp->getSubsystemData(def);
if(!ssdata)
return;
//Find the modifier
std::string& modname = *(std::string*)f->GetArgAddress(1);
auto it = def->modifierIds.find(modname);
if(it == def->modifierIds.end()) {
scripts::throwException(
format("Error: Could not find subsystem modifier '$1.$2'.\n",
def->id, modname).c_str());
return;
}
const SubsystemDef::ModifyStage* stage = it->second;
//Check arguments
if((int)stage->argumentNames.size() != f->GetArgCount() - 2) {
scripts::throwException(
format("Error: Subsystem modifier '$1.$2'"
" expects $3 arguments, got $4.\n",
def->id, modname,
(unsigned)stage->argumentNames.size(),
f->GetArgCount() - 1).c_str());
return;
}
//Create applied stage
SubsystemDef::AppliedStage as;
as.stage = stage;
for(unsigned i = 0, cnt = stage->argumentNames.size(); i < cnt; ++i)
as.arguments[i] = f->GetArgFloat(i+2);
//Add stage to data
unsigned id = ssdata->nextStageId++;
ssdata->stages[id] = as;
ssdata->delta = true;
f->SetReturnDWord(id);
}
struct StatReader {
const StatHistory* history;
const StatEntry* entry;
const StatEvent* evt;
Empire* emp;
unsigned id;
StatReader(const StatHistory* History, Empire* Emp, unsigned ID) : history(History), entry(0), evt(0), emp(Emp), id(ID) {}
~StatReader() {
if(emp)
emp->unlockStatHistory(id);
}
int get_int() const {
if(entry)
return entry->asInt;
else
return 0;
}
float get_float() const {
if(entry)
return entry->asFloat;
else
return 0;
}
unsigned get_time() const {
if(entry)
return entry->time;
else
return 0;
}
unsigned get_evt_type() const {
if(evt)
return evt->type;
else
return 0;
}
std::string get_evt_name() const {
if(evt)
return evt->name;
else
return "";
}
bool toHead() {
if(!history)
return false;
if(const StatEntry* head = history->getHead()) {
entry = head;
evt = 0;
return true;
}
else {
return false;
}
}
bool toTail() {
if(!history)
return false;
if(const StatEntry* tail = history->getTail()) {
entry = tail;
evt = 0;
return true;
}
else {
return false;
}
}
bool advance(int amount) {
if(!history)
return false;
if(amount > 0) {
evt = 0;
if(entry == 0) {
entry = history->getHead();
if(entry)
return advance(amount-1);
else
return false;
}
do {
entry = entry->next;
if(entry == 0)
return false;
} while(--amount != 0);
return entry != 0;
}
else if(amount < 0) {
evt = 0;
if(entry == 0) {
entry = history->getTail();
if(entry)
return advance(amount+1);
else
return false;
}
do {
entry = entry->prev;
if(entry == 0)
return false;
} while(--amount != 0);
return entry != 0;
}
else {
evt = 0;
return entry != 0;
}
}
bool advanceEvent() {
if(!history)
return false;
if(entry) {
if(evt == 0) {
evt = entry->evt;
return evt != 0;
}
else if(evt->next) {
evt = evt->next;
return true;
}
else {
return false;
}
}
return false;
}
static void destroy(StatReader* reader) {
delete reader;
}
};
StatReader* getStatHistory(Empire* emp, unsigned id) {
const StatHistory* history = emp->lockStatHistory(id);
if(!history) {
return new StatReader(0, 0, 0);
}
else {
return new StatReader(history, emp, id);
}
}
bool empIsHostile(Empire& from, Empire* to) {
if(to == nullptr)
return false;
return from.hostileMask & to->mask;
}
bool empIsHostile_restricted(Empire& from, Empire* to) {
if(to == nullptr)
return false;
//if(&from != playerEmpire && to != playerEmpire)
// return false;
return from.hostileMask & to->mask;
}
void empSetHostile(Empire& from, Empire& to, bool value) {
threads::Lock lock(from.maskMutex);
if(value)
from.hostileMask |= to.mask;
else
from.hostileMask &= ~to.mask;
}
static bool empControlled(Empire& emp) {
return devices.network->getCurrentPlayer().controls(&emp);
}
static bool empViewed(Empire& emp) {
return devices.network->getCurrentPlayer().views(&emp);
}
void RegisterEmpireBinds(bool declarations, bool server) {
if(declarations) {
ClassBind emp("Empire", asOBJ_REF | asOBJ_NOCOUNT);
return;
}
ClassBind emp("Empire");
emp.addFactory("Empire@ f()", asFUNCTION(empFactory));
emp.addFactory("Empire@ f(bool unlisted)", asFUNCTION(empFactory_b));
emp.addMember("string name", offsetof(Empire,name));
emp.addMember("Player@ player", offsetof(Empire,player));
emp.addMember("uint8 id", offsetof(Empire,id));
emp.addMember("int index", offsetof(Empire,index));
emp.addMember("uint mask", offsetof(Empire,mask));
emp.addMember("uint visionMask", offsetof(Empire,visionMask));
emp.addMember("Color color", offsetof(Empire,color));
emp.addMember("const Material@ background", offsetof(Empire,background));
emp.addMember("const Material@ portrait", offsetof(Empire,portrait));
emp.addMember("const Material@ flag", offsetof(Empire,flag));
emp.addMember("string backgroundDef", offsetof(Empire,backgroundDef));
emp.addMember("string portraitDef", offsetof(Empire,portraitDef));
emp.addMember("string flagDef", offsetof(Empire,flagDef));
emp.addMember("uint flagID", offsetof(Empire,flagID));
emp.addMember("const Shipset@ shipset", offsetof(Empire, shipset));
emp.addMember("string effectorSkin", offsetof(Empire, effectorSkin));
if(server) {
emp.addMember("uint hostileMask", offsetof(Empire,hostileMask));
emp.addExternMethod("bool isHostile(Empire@ emp)", asFUNCTION(empIsHostile));
emp.addExternMethod("void setHostile(Empire& emp, bool value)", asFUNCTION(empSetHostile));
emp.addMethod("void cacheVision()", asMETHOD(Empire,cacheVision));
}
else {
emp.addExternMethod("bool isHostile(Empire@ emp)", asFUNCTION(empIsHostile_restricted));
emp.addExternMethod("bool get_controlled() const", asFUNCTION(empControlled));
emp.addExternMethod("bool get_viewable() const", asFUNCTION(empViewed));
}
emp.addMethod("uint get_objectCount()", asMETHOD(Empire, objectCount));
emp.addMethod("Object@+ get_objects(uint i)", asMETHOD(Empire, findObject));
//Stats
{
Namespace ns("stat");
EnumBind statTypes("EmpireStat");
for(auto i = statIndices.begin(), end = statIndices.end(); i != end; ++i)
statTypes[i->first] = i->second;
}
bind("string get_statName(stat::EmpireStat)", asFUNCTION(getEmpireStatName));
ClassBind stats("StatHistory", asOBJ_REF | asOBJ_SCOPED);
classdoc(stats, "Accesses the history of an empire's stat. Locks the stat, so limit access to these histories.");
stats.addFactory("StatHistory@ f(Empire&, stat::EmpireStat)", asFUNCTION(getStatHistory));
stats.addExternBehaviour(asBEHAVE_RELEASE, "void f()", asFUNCTION(StatReader::destroy));
stats.addMethod("bool toTail()", asMETHOD(StatReader,toTail))
doc("Advances to the most recent stat entry.", "True if there was a tail to move to.");
stats.addMethod("bool toHead()", asMETHOD(StatReader,toHead))
doc("Advances to the earliest stat entry.", "True if there was a head to move to.");
stats.addMethod("bool advance(int offset = 1)", asMETHOD(StatReader,advance))
doc("Advances to another stat entry.", "Number of entries to move. Positive is later in time, negative is earlier. Advancing 0 resets the event reader. If not on a valid entry, advances to a valid entry at the correspending head or tail. ", "Returns true if an entry existed at the offset.");
stats.addMethod("int get_intVal() const", asMETHOD(StatReader,get_int))
doc("Returns the stat's value as an integer.", "");
stats.addMethod("float get_floatVal() const", asMETHOD(StatReader,get_float))
doc("Returns the stat's value as a float.", "");
stats.addMethod("uint get_time() const", asMETHOD(StatReader,get_time))
doc("Returns the stat's timestamp in seconds from the start of the game.", "");
stats.addMethod("bool advanceEvent()", asMETHOD(StatReader,advanceEvent))
doc("Advances to the next event for the current stat.", "True if there was an event to advance to.");
stats.addMethod("uint get_eventType() const", asMETHOD(StatReader,get_evt_type))
doc("Returns the current event's type identifier.", "");
stats.addMethod("string get_eventName() const", asMETHOD(StatReader,get_evt_name))
doc("Returns the current event's name.", "");
if(server) {
emp.addMethod("void recordStat(stat::EmpireStat, int)", asMETHODPR(Empire,recordStat,(unsigned,int),void))
doc("Records an int value for the specified empire stat at the current game time", "Stat index.", "Stat value.");
emp.addMethod("void recordStat(stat::EmpireStat, float)", asMETHODPR(Empire,recordStat,(unsigned,float),void))
doc("Records a float value for the specified empire stat at the current game time", "Stat index.", "Stat value.");
emp.addMethod("void recordStatDelta(stat::EmpireStat, int)", asMETHODPR(Empire,recordStatDelta,(unsigned,int),void))
doc("Records an int delta for the specified empire stat at the current game time", "Stat index.", "Stat delta.");
emp.addMethod("void recordStatDelta(stat::EmpireStat, float)", asMETHODPR(Empire,recordStatDelta,(unsigned,float),void))
doc("Records a float delta for the specified empire stat at the current game time", "Stat index.", "Stat delta.");
emp.addMethod("void recordEvent(stat::EmpireStat, uint16, const string &in)", asMETHOD(Empire,recordEvent))
doc("Records an event for the specified empire stat at the current game time", "Stat index.", "Event type.", "Event name.");
}
//Designs
emp.addMember("ReadWriteMutex designMutex", offsetof(Empire, designMutex));
emp.addExternMethod("uint get_designCount() const", asFUNCTION(getDesignCount));
emp.addExternMethod("const Design@ getDesign(const string &in)", asFUNCTION(getDesignByName));
emp.addExternMethod("const Design@ getDesign(int id)", asFUNCTION(getDesignById));
emp.addExternMethod("const Design@ get_designs(int)", asFUNCTION(getDesignById));
emp.addExternMethod("uint get_designClassCount() const", asFUNCTION(getDesignClassCount));
emp.addMethod("const DesignClass@ getDesignClass(int id)", asMETHODPR(Empire, getDesignClass, (int), DesignClass*));
emp.addMethod("const DesignClass@ getDesignClass(const string &in, bool add = true)", asMETHODPR(Empire, getDesignClass, (const std::string&, bool), DesignClass*));
emp.addMethod("bool addDesign(const DesignClass& cls, const Design&)", asMETHOD(Empire, addDesign));
emp.addMethod("bool changeDesign(const Design&, const Design&, const DesignClass@ cls = null)", asMETHODPR(Empire, changeDesign, (const Design*, const Design*, DesignClass*), bool));
emp.addMethod("const Design@+ updateDesign(const Design&, bool onlyOutdated = false)", asMETHODPR(Empire, updateDesign, (const Design*, bool), const Design*));
emp.addMethod("void flagDesignOld(const Design&)", asMETHODPR(Empire, flagDesignOld, (const Design*), void))
doc("Marks a design as out of date, to be updated whenever it is requested for construction.", "");
emp.addMethod("bool get_valid()", asMETHOD(Empire, valid));
//Subsystem modifiers
emp.addMember("ReadWriteMutex subsystemDataMutex", offsetof(Empire, subsystemDataMutex));
emp.addExternMethod("bool isUnlocked(const SubsystemDef& sys, const ModuleDef& mod)", asFUNCTION(ssModUnlocked));
emp.addExternMethod("bool isUnlocked(const SubsystemDef& sys)", asFUNCTION(ssUnlocked));
emp.addExternMethod("void setUnlocked(const SubsystemDef& sys, const ModuleDef& mod, bool unlocked)", asFUNCTION(ssSetModUnlock));
emp.addExternMethod("void setUnlocked(const SubsystemDef& sys, bool unlocked)", asFUNCTION(ssSetUnlock));
emp.addExternMethod("void removeModifier(const SubsystemDef& sys, uint id)", asFUNCTION(removeModifier));
emp.addExternMethod("void writeDelta(Message& msg)", asFUNCTION(writeDelta));
emp.addExternMethod("void readDelta(Message& msg)", asFUNCTION(readDelta));
std::string dargs;
for(unsigned i = 0; i <= MODIFY_STAGE_MAXARGS; ++i) {
emp.addGenericMethod(
format("uint addModifier(const SubsystemDef& def, const string&in$1)", dargs).c_str(),
asFUNCTION(addModifier_d));
dargs += ", float";
}
//Global empire lists
bindGlobal("Empire@ playerEmpire", &playerEmpire)
doc("The player's current empire.");
bindGlobal("Empire@ spectatorEmpire", &spectatorEmpire);
bindGlobal("Empire@ defaultEmpire", &defaultEmpire);
bind("uint getEmpireCount()", asFUNCTION(Empire::getEmpireCount));
bind("Empire@ getEmpire(uint index)", asFUNCTION(Empire::getEmpireByIndex));
bind("Empire@ getEmpireByID(uint8 id)", asFUNCTION(Empire::getEmpireByID));
RegisterEmpireComponentWrappers(emp, server);
}
};
+765
View File
@@ -0,0 +1,765 @@
#include "binds.h"
#include "empire.h"
#include "obj/object.h"
#include "main/references.h"
#include "main/logging.h"
#include "main/initialization.h"
#include "scripts/manager.h"
#include "scripts/generic_call.h"
#include "network/player.h"
#include "network/network_manager.h"
#include "processing.h"
#include "network.h"
#include "../as_addons/include/scriptarray.h"
#include "threads.h"
#include <string>
#include <tuple>
namespace scripts {
struct EventDesc {
int id;
GenericCallDesc sendToServer;
GenericCallDesc sendToClient;
bool onlyPrimitive;
bool recvLocal;
bool recvServer;
bool recvShadow;
bool recvMenu;
bool passPlayer;
std::string recvEngine;
std::string recvModule;
GenericCallDesc recvCall;
Manager* recvManager;
asIScriptFunction* recvFunc;
};
threads::Mutex eventDescMutex;
std::vector<EventDesc*> evtDescs;
void ClearEvents() {
threads::Lock lock(eventDescMutex);
foreach(it, evtDescs)
delete *it;
evtDescs.clear();
}
void ReadEvents(const std::string& filename) {
threads::Lock lock(eventDescMutex);
DataReader datafile(filename);
while(datafile++) {
std::string& line = datafile.line;
//Split the line into the call and handler part
std::vector<std::string> parts;
split(line, parts, "->", true);
if(parts.size() != 2) {
error(datafile.position() + " - ERROR: Invalid event format.");
continue;
}
std::string& call = parts[0];
std::string& handler = parts[1];
//Make the call descriptors
GenericCallDesc sendToServer(call);
bool local = false;
//Split the handler identifier
if(handler.compare(0, 6, "local ") == 0) {
local = true;
handler = handler.substr(6);
}
//Non-local RPCs cannot have return values
if(!local && sendToServer.returnType.type) {
error(datafile.position() + " - ERROR: Non-local events cannot have return values.");
continue;
}
else if(sendToServer.returnType == GT_Custom_Handle) {
error(datafile.position() + " - ERROR: Return type cannot be a custom class.");
continue;
}
std::vector<std::string> scopes;
split(handler, scopes, "::", true);
bool recvServer = false;
bool recvShadow = false;
bool recvMenu = false;
bool primitive = true;
if(scopes[0] == "server") {
recvServer = true;
recvShadow = false;
}
else if(scopes[0] == "shadow") {
recvServer = false;
recvShadow = true;
}
else if(scopes[0] == "menu_client") {
recvServer = false;
recvMenu = true;
}
else if(scopes[0] == "menu_server") {
recvServer = true;
recvMenu = true;
}
if(local && !recvServer && !recvShadow) {
error(datafile.position() + " - ERROR: Send-to-client events cannot be marked as local.");
continue;
}
GenericCallDesc recvCall;
GenericCallDesc sendToClient;
sendToClient.name = sendToServer.name;
sendToClient.argCount = 1;
sendToClient.arguments[0] = GT_Player_Ref;
recvCall.returnType = sendToServer.returnType;
recvCall.returnsArray = sendToServer.returnsArray;
for(unsigned i = 0; i < sendToServer.argCount; ++i) {
auto& arg = sendToServer.arguments[i];
recvCall.arguments[recvCall.argCount++] = arg;
if(arg.type == GT_Custom_Handle) {
arg.customName = "Serializable";
primitive = false;
}
sendToClient.arguments[sendToClient.argCount++] = arg;
}
if(scopes.size() != 3) {
error((datafile.position() + " - ERROR: Invalid event handler format '%s'.").c_str(), handler.c_str());
continue;
}
recvCall.name = scopes[2];
EventDesc* desc = new EventDesc();
desc->id = (int)evtDescs.size();
desc->recvServer = recvServer;
desc->recvShadow = recvShadow;
desc->recvMenu = recvMenu;
desc->sendToServer = sendToServer;
desc->sendToClient = sendToClient;
desc->recvLocal = local;
desc->recvEngine = scopes[0];
desc->recvModule = scopes[1];
desc->recvCall = recvCall;
desc->recvFunc = 0;
desc->passPlayer = false;
desc->onlyPrimitive = primitive;
if(desc->sendToServer.returnsArray) {
desc->sendToServer.returnType.type = GT_Custom_Handle;
desc->sendToServer.returnType.customName = "DataList";
}
if(desc->sendToClient.returnsArray) {
desc->sendToClient.returnType.type = GT_Custom_Handle;
desc->sendToClient.returnType.customName = "DataList";
}
evtDescs.push_back(desc);
}
}
void BindEventBinds(bool menu) {
threads::Lock lock(eventDescMutex);
foreach(it, evtDescs) {
EventDesc& desc = **it;
if(!devices.network->isClient) {
if(desc.recvShadow)
continue;
}
if(menu != desc.recvMenu)
continue;
Manager* man = 0;
if(desc.recvMenu)
man = devices.scripts.menu;
else if(desc.recvEngine == "server" || desc.recvEngine == "shadow")
man = devices.scripts.server;
else
man = devices.scripts.client;
desc.recvManager = man;
if(!desc.recvServer || desc.recvLocal || !devices.network->isClient) {
desc.recvFunc = man->getFunction(desc.recvModule.c_str(),
desc.recvCall.declaration(true).c_str());
if(!desc.recvFunc) {
if(desc.recvServer || desc.recvShadow) {
desc.recvCall.prepend(GT_Player_Ref);
desc.passPlayer = true;
desc.recvFunc = man->getFunction(desc.recvModule.c_str(),
desc.recvCall.declaration(true).c_str());
}
if(!desc.recvFunc) {
error("Events: Could not find function %s::%s::%s",
desc.recvEngine.c_str(), desc.recvModule.c_str(),
desc.recvCall.declaration(true).c_str());
continue;
}
}
}
unsigned aCnt = desc.recvCall.argCount;
for(unsigned i = 0; i < aCnt; ++i) {
auto& arg = desc.recvCall.arguments[i];
//Find the correct type id for the receiving function
if(arg.type == GT_Custom_Handle) {
std::string in_name = "Serializable";
auto* mod = man->engine->GetModule(desc.recvModule.c_str());
if(mod) {
int cid = mod->GetTypeIdByDecl(arg.customName.c_str());
arg.customType = (void*)man->engine->GetTypeInfoById(cid);
}
asITypeInfo *rcvType, *srType, *clType;
if(desc.recvMenu) {
rcvType = man->engine->GetTypeInfoByName(in_name.c_str());
srType = man->engine->GetTypeInfoByName(in_name.c_str());
clType = man->engine->GetTypeInfoByName(in_name.c_str());
}
else {
rcvType = man->engine->GetTypeInfoByName(in_name.c_str());
srType = devices.scripts.server->engine->GetTypeInfoByName(in_name.c_str());
clType = devices.scripts.client->engine->GetTypeInfoByName(in_name.c_str());
}
if(!srType || !clType || !rcvType) {
error("Error: Could not find interface type '%s'.", in_name.c_str());
}
else {
arg.customRead = (void*)rcvType->GetMethodByDecl("void read(Message&)");
arg.customWrite = (void*)rcvType->GetMethodByDecl("void write(Message&)");
unsigned r = i;
if(desc.recvServer && desc.passPlayer)
--r;
desc.sendToClient.arguments[r+1].customWrite = (void*)srType->GetMethodByDecl("void write(Message&)");
desc.sendToServer.arguments[r].customType = arg.customType;
desc.sendToServer.arguments[r].customRead = arg.customRead;
desc.sendToServer.arguments[r].customWrite = (void*)clType->GetMethodByDecl("void write(Message&)");
}
if(!arg.customType) {
error("Error: Could not find class '%s' in module '%s' for event description.",
arg.customName.c_str(), desc.recvModule.c_str());
}
}
}
}
}
Player ALL_PLAYERS(-1);
Player SERVER_PLAYER(-2);
GenericValue handleSendToServer(void* arg, GenericCallData& args) {
GenericValue retVal;
EventDesc& desc = *(EventDesc*)arg;
args.desc = desc.sendToServer;
if(desc.recvLocal || !devices.network->isClient) {
if(desc.recvFunc && desc.recvManager) {
//Transfer custom classes from one engine to the other
if(desc.onlyPrimitive) {
Call cl = desc.recvManager->call(desc.recvFunc);
//Append player argument
if(desc.passPlayer)
cl.push(&devices.network->currentPlayer);
//Append other arguments
args.pushTo(cl);
retVal = desc.recvCall.call(cl);
}
else {
net::Message msg(net::MT_Application, net::MF_Managed);
std::vector<asIScriptObject*> resultObjects;
for(unsigned i = 0; i < args.desc.argCount; ++i) {
auto& arg = args.desc.arguments[i];
if(arg.type != GT_Custom_Handle)
continue;
if(!arg.customWrite || !arg.customRead || !arg.customType || !desc.recvManager)
continue;
//Write to message
{
scripts::Call cl = getActiveManager()->call((asIScriptFunction*)arg.customWrite);
cl.setObject((asIScriptObject*)args.values[i].ptr);
cl.push(&msg);
cl.call();
}
//Create object
asITypeInfo* type = (asITypeInfo*)arg.customType;
asIScriptObject* ret = (asIScriptObject*)desc.recvManager->engine->CreateScriptObject(type);
//Call read function
{
scripts::Call cl = desc.recvManager->call((asIScriptFunction*)arg.customRead);
cl.setObject(ret);
cl.push(&msg);
cl.call();
}
msg.reset();
resultObjects.push_back(ret);
}
Call cl = desc.recvManager->call(desc.recvFunc);
//Append player argument
if(desc.passPlayer)
cl.push(&devices.network->currentPlayer);
//Append other arguments
unsigned customIndex = 0;
auto transferCustom = [&](ArgumentDesc& arg, asIScriptObject* ptr) -> asIScriptObject* {
return resultObjects[customIndex++];
};
args.pushTo(cl, 0, transferCustom);
retVal = desc.recvCall.call(cl);
}
}
}
else {
auto writeCustom = [](net::Message& msg, ArgumentDesc& adesc, GenericValue& val) {
if(adesc.type != GT_Custom_Handle)
return;
if(adesc.customWrite) {
//Call write function
scripts::Call cl = getActiveManager()->call((asIScriptFunction*)adesc.customWrite);
cl.setObject(val.script);
cl.push(&msg);
cl.call();
}
else {
error("Could not find custom write for argument.");
}
};
net::Message msg(MT_Event_Call, net::MF_Managed);
msg << desc.id;
args.write(msg, 0, writeCustom);
devices.network->send(msg);
}
return retVal;
}
GenericValue handleSendToClient_local(void* arg, GenericCallData& args) {
EventDesc& desc = *(EventDesc*)arg;
args.desc = desc.sendToServer;
auto writeCustom = [&](net::Message& msg, ArgumentDesc& adesc, GenericValue& val) {
if(adesc.customWrite) {
//Call write function
scripts::Call cl = devices.scripts.server->call((asIScriptFunction*)adesc.customWrite);
cl.setObject(val.script);
cl.push(&msg);
cl.call();
}
};
net::Message msg(MT_Event_Call, net::MF_Managed);
msg << desc.id;
args.write(msg, 0, writeCustom);
msg.finalize();
msg.rewind();
handleEventMessage(&devices.network->currentPlayer, msg);
return GenericValue();
}
GenericValue handleSendToClient(void* arg, GenericCallData& args) {
EventDesc& desc = *(EventDesc*)arg;
Player* pl = args.values[0].player;
args.desc = desc.sendToClient;
auto writeCustom = [&](net::Message& msg, ArgumentDesc& adesc, GenericValue& val) {
if(adesc.customWrite) {
//Call write function
scripts::Call cl = devices.scripts.server->call((asIScriptFunction*)adesc.customWrite);
cl.setObject(val.script);
cl.push(&msg);
cl.call();
}
};
net::Message msg(MT_Event_Call, net::MF_Managed);
msg << desc.id;
args.write(msg, 1, writeCustom);
if(!devices.network->isServer || pl == nullptr || pl == &devices.network->currentPlayer) {
msg.finalize();
msg.rewind();
handleEventMessage(&devices.network->currentPlayer, msg);
}
else if(pl == &ALL_PLAYERS || pl->id == -1) {
devices.network->sendAll(msg);
handleEventMessage(&devices.network->currentPlayer, msg);
}
else
devices.network->send(pl, msg);
return GenericValue();
}
threads::Mutex queuedEventLock;
std::stack<std::tuple<Player,net::Message*>> queuedClientEvents;
void processEvents() {
while(!queuedClientEvents.empty()) {
queuedEventLock.lock();
auto item = queuedClientEvents.top();
queuedClientEvents.pop();
queuedEventLock.release();
handleEventMessage(&std::get<0>(item), *std::get<1>(item));
delete std::get<1>(item);
}
}
scripts::Manager* handleEventMessage(Player* from, net::Message& msg, bool interceptMenu) {
int eventId;
msg >> eventId;
if(eventId < 0 || eventId >= (int)evtDescs.size())
return nullptr;
EventDesc& edesc = *evtDescs[eventId];
if(interceptMenu && edesc.recvMenu)
return edesc.recvManager;
if(!edesc.recvMenu && !game_running) {
msg.rewind();
threads::Lock lock(queuedEventLock);
queuedClientEvents.push(std::tuple<Player,net::Message*>(*from,new net::Message(msg)));
return nullptr;
}
GenericCallDesc& cdesc = edesc.recvCall;
GenericCallData data(cdesc);
auto readCustom = [edesc](net::Message& msg, ArgumentDesc& desc, GenericValue& val) {
if(desc.customType && desc.customRead && edesc.recvManager) {
//Create object
asITypeInfo* type = (asITypeInfo*)desc.customType;
val.script = (asIScriptObject*)edesc.recvManager->engine->CreateScriptObject(type);
//Call read function
scripts::Call cl = edesc.recvManager->call((asIScriptFunction*)desc.customRead);
cl.setObject(val.script);
cl.push(&msg);
cl.call();
}
};
if((edesc.recvServer || edesc.recvShadow) && edesc.passPlayer) {
data.values[0].player = from;
data.read(msg, 1, readCustom);
}
else {
data.read(msg, 0, readCustom);
}
if(edesc.recvFunc && edesc.recvManager) {
Call cl = edesc.recvManager->call(edesc.recvFunc);
data.pushTo(cl);
cl.call();
}
return nullptr;
}
static void linkEmpire(Player& inPl, Empire* emp) {
threads::ReadLock lock(devices.network->playerLock);
Player* pl = devices.network->getPlayer(inPl.id);
if(!pl)
return;
if(pl->emp && pl->emp != Empire::getSpectatorEmpire()) {
pl->emp->player = nullptr;
pl->emp->lastPlayer = net::Address();
}
pl->emp = emp;
pl->changedEmpire = true;
pl->controlMask = 0;
pl->viewMask = ~0;
if(emp && emp != Empire::getSpectatorEmpire()) {
emp->player = pl;
emp->lastPlayer = pl->address;
pl->controlMask = emp->mask;
pl->viewMask = emp->mask;
}
}
static bool playerEquals(Player& player, Player& other) {
return player.id == other.id;
}
static asITypeInfo* getPlayerArrayType() {
return (asITypeInfo*)asGetActiveContext()->GetEngine()->GetUserData(EDID_playerArray);
}
static CScriptArray* getPlayers() {
CScriptArray* results = new CScriptArray(0, getPlayerArrayType());
results->Reserve(devices.network->players.size());
{
threads::ReadLock lock(devices.network->playerLock);
foreach(it, devices.network->players)
results->InsertLast(&it->second);
}
return results;
}
static Player* getPlayer(int id) {
return devices.network->getPlayer(id);
}
static std::string playerName(Player& player) {
return std::string(player.nickname);
}
static void ctor_addr(void* mem) {
new(mem) net::Address();
}
static void ctor_addrv(void* mem, const std::string& hostname, int port) {
new(mem) net::Address(hostname, port);
}
static net::Address& cpy_addr(net::Address& into, const net::Address& from) {
into = from;
return into;
}
static bool eq_addr(const net::Address& addr, const net::Address& other) {
return addr == other;
}
static void dtor_addr(net::Address& addr) {
addr.~Address();
}
static void ctor_game(void* mem) {
new(mem) net::Game();
}
static net::Game& cpy_game(net::Game& into, const net::Game& from) {
into = from;
return into;
}
static void dtor_game(net::Game& addr) {
addr.~Game();
}
static void queryServers() {
devices.network->queryServers();
}
static bool isQuerying() {
return devices.network->query && devices.network->query->updating;
}
static void getQueriedServers(CScriptArray* arr) {
threads::Mutex mtx(devices.network->queryMtx);
std::vector<net::Game>& list = devices.network->queryResult;
foreach(it, list)
arr->InsertLast(&(*it));
list.clear();
}
static void mpKick(int playerId) {
devices.network->kick(playerId);
}
static void mpConnect(net::Game& game, bool disablePunchthrough, const std::string& pwd) {
devices.network->connect(game, disablePunchthrough, pwd);
}
static void hostGame(const std::string& gamename, int port, unsigned maxPlayers, bool isPublic, bool punchthrough, const std::string& password) {
devices.network->host(gamename, port, maxPlayers, isPublic, punchthrough, password);
}
static unsigned dcReason() {
return devices.network->disconnection;
}
static float glxProgress() {
return devices.network->galaxyProgress;
}
static bool glxAwaiting() {
return devices.network->isClient && devices.network->serverReady &&
!devices.network->currentPlayer.hasGalaxy;
}
class IsolateAction : public processing::Action {
asIScriptObject* obj;
public:
IsolateAction(asIScriptObject* object) : obj(object) {}
bool run() {
auto* func = obj->GetObjectType()->GetMethodByDecl("void call()");
if(func) {
auto& mana = scripts::Manager::fromEngine(obj->GetEngine());
auto call = mana.call(func);
call.setObject(obj);
call.call();
}
return true;
}
~IsolateAction() {
obj->Release();
}
};
static void runIsolate(asIScriptObject* obj) {
if(obj == nullptr)
return;
auto* act = new IsolateAction(obj);
processing::queueIsolationAction(act);
}
void RegisterEventBinds(bool server, bool shadow, bool menu) {
threads::Lock lock(eventDescMutex);
ClassBind player("Player", asOBJ_REF | asOBJ_NOCOUNT, 0);
player.addMember("const int id", offsetof(Player, id));
player.addMember("Empire@ emp", offsetof(Player, emp));
if(server) {
player.addMember("uint controlMask", offsetof(Player, controlMask));
player.addMember("uint viewMask", offsetof(Player, viewMask));
}
else {
player.addMember("const uint controlMask", offsetof(Player, controlMask));
player.addMember("const uint viewMask", offsetof(Player, viewMask));
}
player.addMethod("bool controls(Empire& emp)", asMETHOD(Player, controls));
player.addMethod("bool views(Empire& emp)", asMETHOD(Player, views));
player.addExternMethod("string get_name()", asFUNCTION(playerName));
player.addExternMethod("bool opEquals(const Player& other) const", asFUNCTION(playerEquals));
if(server)
player.addExternMethod("void linkEmpire(Empire@ empire)", asFUNCTION(linkEmpire));
bindGlobal("const Player ALL_PLAYERS", &ALL_PLAYERS);
bindGlobal("const Player SERVER_PLAYER", &SERVER_PLAYER);
bindGlobal("Player CURRENT_PLAYER", &devices.network->currentPlayer);
bind("array<Player@>@ getPlayers()", asFUNCTION(getPlayers));
bind("Player@ getPlayer(int id)", asFUNCTION(getPlayer));
ClassBind ga("GameAddress", asOBJ_VALUE | asOBJ_APP_CLASS_CDA, sizeof(net::Address));
ga.addConstructor("void f()", asFUNCTION(ctor_addr));
ga.addConstructor("void f(const string& hostname, int port = 2048)", asFUNCTION(ctor_addrv));
ga.addDestructor("void f()", asFUNCTION(dtor_addr));
ga.addExternMethod("GameAddress& opAssign(const GameAddress& other)", asFUNCTION(cpy_addr));
ga.addExternMethod("bool opEquals(const GameAddress& other) const", asFUNCTION(eq_addr));
ga.addMember("int port", offsetof(net::Address, port));
ga.addMethod("string toString(bool showPort = true) const", asMETHOD(net::Address, toString));
ClassBind gs("GameServer", asOBJ_VALUE | asOBJ_APP_CLASS_CDA, sizeof(net::Game));
gs.addConstructor("void f()", asFUNCTION(ctor_game));
gs.addDestructor("void f()", asFUNCTION(dtor_game));
gs.addExternMethod("GameServer& opAssign(const GameServer& other)", asFUNCTION(cpy_game));
gs.addMember("GameAddress address", offsetof(net::Game, address));
gs.addMember("string name", offsetof(net::Game, name));
gs.addMember("string mod", offsetof(net::Game, mod));
gs.addMember("uint16 players", offsetof(net::Game, players));
gs.addMember("uint16 maxPlayers", offsetof(net::Game, maxPlayers));
gs.addMember("int punchPort", offsetof(net::Game, punchPort));
gs.addMember("int version", offsetof(net::Game, version));
gs.addMember("bool started", offsetof(net::Game, started));
gs.addMember("bool isLocal", offsetof(net::Game, isLocal));
gs.addMember("bool password", offsetof(net::Game, password));
player.addMember("GameAddress address", offsetof(Player, address));
EnumBind dr("DisconnectReason");
dr["DR_Timeout"] = net::DR_Timeout;
dr["DR_Error"] = net::DR_Error;
dr["DR_Close"] = net::DR_Close;
dr["DR_Kick"] = net::DR_Kick;
dr["DR_Version"] = net::DR_Version;
dr["DR_Password"] = net::DR_Password;
bind("DisconnectReason get_mpDisconnectReason()", asFUNCTION(dcReason));
bind("float get_galaxySendProgress()", asFUNCTION(glxProgress));
bind("bool get_awaitingGalaxy()", asFUNCTION(glxAwaiting));
bind("void mpQueryServers()", asFUNCTION(queryServers));
bind("bool mpIsQuerying()", asFUNCTION(isQuerying));
bind("void mpGetServers(array<GameServer>& list)", asFUNCTION(getQueriedServers));
bind("void mpKick(int playerId)", asFUNCTION(mpKick));
bind("void mpHost(const string& gamename = \"\", uint port = 2048, uint maxPlayers = 0, bool isPublic = true, bool punchthrough = true, const string& password = \"\")",
asFUNCTION(hostGame));
bind("void mpConnect(const GameServer& game, bool disablePunchthrough = false, const string& password = \"\")", asFUNCTION(mpConnect));
if(void* plArray = getEngine()->GetTypeInfoById(getEngine()->GetTypeIdByDecl("array<Player@>")))
getEngine()->SetUserData(plArray, EDID_playerArray);
if(server) {
if(!shadow)
bindGlobal("Player HOST_PLAYER", &devices.network->currentPlayer);
}
bindGlobal("int MP_VERSION", &NetworkManager::MP_VERSION);
foreach(it, evtDescs) {
EventDesc* desc = *it;
if(desc->recvMenu && !menu)
continue;
//Register the functions to send
if(server) {
if(!desc->recvServer) {
if(shadow)
bindGeneric(desc->sendToServer, handleSendToClient_local, desc, true);
else
bindGeneric(desc->sendToClient, handleSendToClient, desc, true);
}
}
else if(menu && desc->recvMenu) {
if(desc->recvServer)
bindGeneric(desc->sendToServer, handleSendToServer, desc, true);
else
bindGeneric(desc->sendToClient, handleSendToClient, desc, true);
}
else {
if(desc->recvServer)
bindGeneric(desc->sendToServer, handleSendToServer, desc, true);
}
}
//Adding console commands
InterfaceBind isolateClass("IsolateHook");
isolateClass.addMethod("void call()");
bind("void isolate_run(IsolateHook@ hook)", asFUNCTION(runIsolate));
}
};
+527
View File
@@ -0,0 +1,527 @@
#include "binds.h"
#include "util/formula.h"
#include "util/refcount.h"
#include "util/save_file.h"
#include "network/message.h"
#include "main/logging.h"
#include "scripts/context_cache.h"
#include "general_states.h"
#include <unordered_map>
#include <vector>
namespace scripts {
class FormulaNamespace;
static int varIndex(const std::string* str);
static double varName(void* user, const std::string* name);
static double nsVar(void* user, int index);
static threads::ReadWriteMutex nsVarMutex;
static std::unordered_map<std::string, int> nsVariables;
static std::vector<std::string> nsVariableNames;
class FormulaNamespace : public AtomicRefCounted {
public:
enum VarType {
vConstant,
vFormula,
};
struct Var {
VarType type;
union {
double decimal;
Formula* formula;
};
std::string* str;
Var() : type(vConstant), decimal(0.0), str(0) {
}
~Var() {
switch(type) {
case vFormula:
delete formula;
break;
}
delete str;
}
void setType(VarType newType) {
switch(type) {
case vFormula:
delete formula;
break;
}
type = newType;
}
void setString(const std::string& value) {
if(!str)
str = new std::string();
*str = value;
}
void write(net::Message& msg) {
msg << (uint8_t)type;
switch(type) {
case vConstant:
msg << decimal;
break;
case vFormula:
if(str)
msg << *str;
else
msg << "0";
break;
}
}
void read(net::Message& msg) {
auto prevType = type;
uint8_t utp;
msg >> utp;
type = (VarType)utp;
switch(type) {
case vConstant:
msg >> decimal;
break;
case vFormula:
if(!str)
str = new std::string();
msg >> *str;
if(prevType == vFormula && formula)
delete formula;
formula = Formula::fromInfix(str->c_str(), &varIndex);
break;
}
}
};
threads::ReadWriteMutex mtx;
std::vector<Var> variables;
std::vector<int> indexes;
int lookup(const std::string& name, bool create = true) {
{
threads::ReadLock nl(nsVarMutex);
auto it = nsVariables.find(name);
if(it != nsVariables.end())
return it->second;
}
if(create) {
int globInd = -1;
{
threads::WriteLock wl(nsVarMutex);
globInd = nsVariables.size();
nsVariables[name] = globInd;
nsVariableNames.push_back(name);
}
{
threads::WriteLock wl(mtx);
int index = variables.size();
variables.push_back(Var());
if((unsigned)globInd >= indexes.size())
indexes.resize(globInd+1, -1);
indexes[globInd] = index;
}
return globInd;
}
else {
return -1;
}
}
inline int fromGlobalIndex(int index) {
if((unsigned)index >= indexes.size())
return -1;
return indexes[index];
}
double get(const std::string& name) {
threads::ReadLock rl(mtx);
int index = lookup(name, false);
if(index == -1) {
error("Formula variable '%s' does not exist.", name.c_str());
scripts::logException();
return 0.0;
}
return get(index);
}
double get(int globalIndex) {
threads::ReadLock rl(mtx);
int index = fromGlobalIndex(globalIndex);
if(index == -1) {
if(globalIndex > 0 && globalIndex < (int)nsVariableNames.size()) {
//Formula variable exists, just hasn't been set in this particular namespace,
//which means we should consider it a 0 so we don't fuck with execution order
//error("Formula variable '%s' does not exist.", nsVariableNames[globalIndex].c_str());
//scripts::logException();
return 0.0;
}
else {
error("Invalid namespace variable index.");
scripts::logException();
}
return 0.0;
}
Var& v = variables[index];
switch(v.type) {
case vConstant:
return v.decimal;
case vFormula:
return v.formula->evaluate(&varName, this, &nsVar);
}
return 0.0;
}
bool has(const std::string& name) {
threads::ReadLock rl(mtx);
return fromGlobalIndex(lookup(name, false)) != -1;
}
void setConstant(const std::string& name, double value) {
threads::WriteLock wl(mtx);
int index = lookup(name, true);
setConstant(index, value);
}
void setConstant(int globIndex, double value) {
threads::WriteLock wl(mtx);
int index = fromGlobalIndex(globIndex);
if(index == -1) {
index = variables.size();
variables.push_back(Var());
if((unsigned)globIndex >= indexes.size())
indexes.resize(globIndex+1, -1);
indexes[globIndex] = index;
}
Var& v = variables[index];
v.setType(vConstant);
v.decimal = value;
}
void modConstant(int globIndex, double value) {
threads::WriteLock wl(mtx);
int index = fromGlobalIndex(globIndex);
if(index == -1) {
index = variables.size();
variables.push_back(Var());
if((unsigned)globIndex >= indexes.size())
indexes.resize(globIndex+1, -1);
indexes[globIndex] = index;
}
Var& v = variables[index];
if(v.type == vConstant) {
v.decimal = v.decimal + value;
}
else {
double last = get(globIndex);
v.setType(vConstant);
v.decimal = last + value;
}
}
void setFormula(const std::string& name, const std::string& formula) {
threads::ReadLock rl(mtx);
int index = lookup(name, true);
setFormula(index, formula);
}
void setFormula(int globIndex, const std::string& formula) {
threads::WriteLock wl(mtx);
int index = fromGlobalIndex(globIndex);
if(index == -1) {
index = variables.size();
variables.push_back(Var());
if((unsigned)globIndex >= indexes.size())
indexes.resize(globIndex+1, -1);
indexes[globIndex] = index;
}
Var& v = variables[index];
v.setType(vFormula);
v.formula = Formula::fromInfix(formula.c_str(), &varIndex);
v.setString(formula);
}
void write(net::Message& msg) {
threads::ReadLock rl(mtx);
msg << (unsigned)variables.size();
for(unsigned i = 0, cnt = indexes.size(); i < cnt; ++i) {
if(indexes[i] == -1)
continue;
msg << nsVariableNames[i];
variables[indexes[i]].write(msg);
}
}
void read(net::Message& msg) {
try {
threads::WriteLock wl(mtx);
unsigned varCnt = 0;
msg >> varCnt;
for(unsigned i = 0; i < varCnt; ++i) {
std::string name;
msg >> name;
int globIndex = lookup(name, true);
int index = fromGlobalIndex(globIndex);
if(index == -1) {
index = variables.size();
variables.push_back(Var());
if((unsigned)globIndex >= indexes.size())
indexes.resize(globIndex+1, -1);
indexes[globIndex] = index;
}
variables[index].read(msg);
}
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
}
};
static FormulaNamespace* makeNamespace() {
return new FormulaNamespace();
}
static int varIndex(const std::string* name) {
{
threads::ReadLock nl(nsVarMutex);
auto it = nsVariables.find(*name);
if(it != nsVariables.end())
return it->second;
}
threads::WriteLock wl(nsVarMutex);
auto it = nsVariables.find(*name);
if(it != nsVariables.end())
return it->second;
int globInd = nsVariables.size();
nsVariables[*name] = globInd;
nsVariableNames.push_back(*name);
return globInd;
}
static double varName(void* user, const std::string* name) {
return 0.0;
}
static double nsVar(void* user, int index) {
FormulaNamespace* ns = (FormulaNamespace*)user;
if(ns)
return ns->get(index);
else
return 0.0;
}
class ScriptFormula : public AtomicRefCounted {
public:
Formula* formula;
ScriptFormula() : formula(0) {
}
void parse(const std::string& expr) {
if(formula)
delete formula;
try {
formula = Formula::fromInfix(expr.c_str(), &varIndex);
}
catch(FormulaError& err) {
error("Script Formula Error: %s", err.msg.c_str());
scripts::logException();
formula = 0;
}
}
double evaluate(FormulaNamespace* ns = 0) {
if(!formula)
return 0.0;
return formula->evaluate(&varName, ns, &nsVar);
}
~ScriptFormula() {
delete formula;
}
};
static ScriptFormula* makeFormula_e() {
return new ScriptFormula();
}
static ScriptFormula* makeFormula(const std::string& expr) {
ScriptFormula* f = new ScriptFormula();
f->parse(expr);
return f;
}
void RegisterFormulaBinds(bool server) {
nsVariables.clear();
nsVariableNames.clear();
/* FORMULA NAMESPACE */
ClassBind ns("Namespace", asOBJ_REF);
classdoc(ns, "A namespace of variables that can be accessed from formulas attached to it.");
ns.addFactory("Namespace@ f()", asFUNCTION(makeNamespace));
ns.setReferenceFuncs(asMETHOD(FormulaNamespace,grab), asMETHOD(FormulaNamespace,drop));
ns.addMember("ReadWriteMutex mtx", offsetof(FormulaNamespace, mtx))
doc("Mutex that governs reading and writing on this namespace.");
ns.addMethod("int lookup(const string&in name, bool create = true)",
asMETHOD(FormulaNamespace, lookup))
doc("Lookup the index of a variable in the namespace.",
"Name of the variable.",
"If true, create the variable if it does not exist.",
"Index of the variable. -1 if it does not exist and was not created.");
ns.addMethod("double get(const string&in name)",
asMETHODPR(FormulaNamespace, get, (const std::string&), double))
doc("Get or calculate the value of a variable by name.",
"Name of the variable.",
"Value of that variable.");
ns.addMethod("double get(int index)",
asMETHODPR(FormulaNamespace, get, (int), double))
doc("Get or calculate the value of a variable by index.",
"Index of the variable.",
"Value of that variable.");
ns.addMethod("bool has(const string&in name)",
asMETHOD(FormulaNamespace, has))
doc("", "Name of the variable to check for.",
"True if a variable with this name exists.");
ns.addMethod("void setConstant(const string&in name, double value)",
asMETHODPR(FormulaNamespace, setConstant, (const std::string&, double), void))
doc("Set a constant value for a variable.",
"Name of the variable to set.",
"Value to set the variable to.");
ns.addMethod("void setConstant(int index, double value)",
asMETHODPR(FormulaNamespace, setConstant, (int, double), void))
doc("Set a constant value for a variable.",
"Index of the variable to set.",
"Value to set the variable to.");
ns.addMethod("void modConstant(int index, double value)",
asMETHODPR(FormulaNamespace, modConstant, (int, double), void))
doc("Modify a constant value for a variable by adding a new value.",
"Index of the variable to set.",
"Amount to add to the constant value.");
ns.addMethod("void setFormula(const string&in name, const string&in formula)",
asMETHODPR(FormulaNamespace, setFormula, (const std::string&, const std::string&), void))
doc("Set a formula to evaluate for a variable. Formula is evaluated in this namespace.",
"Name of the variable to set.",
"Formula to set the variable to.");
ns.addMethod("void setFormula(int index, const string&in formula)",
asMETHODPR(FormulaNamespace, setFormula, (int, const std::string&), void))
doc("Set a formula to evaluate for a variable. Formula is evaluated in this namespace.",
"Index of the variable to set.",
"Formula to set the variable to.");
ns.addMethod("void write(Message& msg)", asMETHOD(FormulaNamespace, write))
doc("Write the namespace to a message.", "Message to write to.");
ns.addMethod("void read(Message& msg)", asMETHOD(FormulaNamespace, read))
doc("Read the namespace from a message.", "Message to read from.");
ns.addMethod("void save(SaveFile& file)", asMETHOD(FormulaNamespace, write))
doc("Write the namespace to a save file.", "Save file to write to.");
ns.addMethod("void load(SaveFile& file)", asMETHOD(FormulaNamespace, read))
doc("Read the namespace from a save file.", "Save file to read from.");
{
Namespace ns("formula");
bind("int variable(const ::string&in name)", asFUNCTION(varIndex));
}
/* FORMULA */
ClassBind f("Formula", asOBJ_REF);
classdoc(f, "Evaluator for arbitrary formula expressions.");
f.addFactory("Formula@ f()", asFUNCTION(makeFormula_e));
f.addFactory("Formula@ f(const string&in formula)", asFUNCTION(makeFormula))
doc("Construct a new formula.", "Formula expression to use.", "Constructed formula.");
f.setReferenceFuncs(asMETHOD(ScriptFormula,grab), asMETHOD(ScriptFormula,drop));
f.addMethod("void parse(const string&in formula)", asMETHOD(ScriptFormula, parse))
doc("Parse an expression into this formula.", "Formula expression to use.");
f.addMethod("double evaluate(Namespace@ ns = null)", asMETHOD(ScriptFormula, evaluate))
doc("Evaluate the formula expression with the current state of "
"the namespace it was constructed with.",
"Namespace to retrieve variables from.",
"Value of the formula.");
}
void addNamespaceState() {
stateValueTypes["Namespace"].setup(
sizeof(FormulaNamespace*), "Namespace@",
//Copy reference only, or create new if not copying
[](void* m, void* s) {
FormulaNamespace** dest = (FormulaNamespace**)m;
FormulaNamespace** src = (FormulaNamespace**)s;
if(src) {
*dest = *src;
if(*src)
(*src)->grab();
}
else {
*dest = new FormulaNamespace();
}
},
//No initializer
nullptr,
//Release reference on destruct
[](void* mem) {
FormulaNamespace* ns = *(FormulaNamespace**)mem;
if(ns)
ns->drop();
},
//Write to network
[](net::Message& msg, void* mem) {
FormulaNamespace* ns = *(FormulaNamespace**)mem;
if(ns)
ns->write(msg);
},
//Read from network
[](net::Message& msg, void* mem) {
FormulaNamespace* ns = *(FormulaNamespace**)mem;
if(ns)
ns->read(msg);
}
);
}
};
File diff suppressed because it is too large Load Diff
+628
View File
@@ -0,0 +1,628 @@
#include "scripts/binds.h"
#include "main/references.h"
#include "gui/skin.h"
#include "main/tick.h"
#include "render/render_state.h"
#include "render/font.h"
#include "profile/keybinds.h"
#include "../as_addons/include/scriptdictionary.h"
namespace scripts {
using namespace gui;
template<class A, class B>
B* castPtrRef(A* a) {
if(!a)
return 0;
B* b = dynamic_cast<B*>(a);
if(b)
b->grab();
return b;
}
const render::Font* get_font(const std::string& str) {
return &devices.library.getFont(str);
}
int get_fontType(const std::string& str) {
return skin::getFontIndex(str);
}
int get_colorType(const std::string& str) {
return skin::getColorIndex(str);
}
const render::RenderState* get_mat(const std::string& str) {
return &devices.library.getMaterial(str);
}
const skin::Skin* get_skin(const std::string& str) {
return &devices.library.getSkin(str);
}
vec2i screen_size() {
return vec2d(devices.driver->win_width / ui_scale,
devices.driver->win_height / ui_scale);
}
vec2i window_size() {
return vec2i(devices.driver->win_width,
devices.driver->win_height);
}
bool window_focused() {
return devices.driver->isWindowFocused();
}
bool window_minimized() {
return devices.driver->isWindowMinimized();
}
bool window_mouseOver() {
return devices.driver->isMouseOver();
}
void window_flash() {
return devices.driver->flashWindow();
}
bool doClip = false;
recti clipRect;
void setClip(const recti& clip) {
doClip = true;
clipRect = clip;
}
void clearClip() {
doClip = false;
}
recti* getClip() {
if(doClip)
return &clipRect;
else
return 0;
}
void skin_draw(skin::Skin& skin, unsigned style, unsigned flags, const recti& rect) {
auto& element = skin.getElement(style, flags);
element.draw(*devices.render,rect,getClip());
}
vec2i skin_size(skin::Skin& skin, unsigned style, unsigned flags) {
auto& element = skin.getElement(style, flags);
return element.area.getSize();
}
void skin_draw_c(skin::Skin& skin, unsigned style, unsigned flags, const recti& rect, const Color& color) {
auto& element = skin.getElement(style, flags);
element.draw(*devices.render,rect,getClip(),color);
}
void skin_text(skin::Skin& skin, unsigned fontClass, const vec2i& pos, const std::string& text) {
auto& font = skin.getFont(fontClass);
font.draw(devices.render,text.c_str(),pos.x,pos.y,0,getClip());
}
void skin_text_c(skin::Skin& skin, unsigned fontClass, const vec2i& pos, const std::string& text, const Color& color) {
auto& font = skin.getFont(fontClass);
font.draw(devices.render,text.c_str(),pos.x,pos.y,&color,getClip());
}
vec2i skin_char(skin::Skin& skin, unsigned fontClass, const vec2i& pos, int c, int lastC, const Color& color) {
auto& font = skin.getFont(fontClass);
return font.drawChar(devices.render,c,lastC,pos.x,pos.y,&color,getClip());
}
const render::Font* skin_font(skin::Skin& skin, unsigned fontClass) {
return &skin.getFont(fontClass);
}
Color skin_color(skin::Skin& skin, unsigned color) {
return skin.getColor(color);
}
static bool skin_irregular(skin::Skin& skin, unsigned index) {
auto& style = skin.getStyle(index);
return style.irregular;
}
static bool skin_pxactive(skin::Skin& skin, unsigned style, unsigned flags, const recti& box, const vec2i& px) {
auto& element = skin.getElement(style, flags);
return element.isPixelActive(box, px);
}
static unsigned skin_eleCount(skin::Skin& skin, int style) {
if(!skin.hasStyle(style))
return 0;
const skin::Style& st = skin.getStyle(style);
return st.elements.size();
}
static unsigned skin_eleFlags(skin::Skin& skin, int style, unsigned index) {
if(!skin.hasStyle(style))
return 0;
const skin::Style& st = skin.getStyle(style);
if(index >= st.elements.size())
return 0;
return st.elements[index]->flags;
}
vec2i getFontDimension(const render::Font& font, const std::string& text) {
return font.getDimension(text.c_str());
}
vec2i getFontCharDim(const render::Font& font, int c, int lastC) {
return font.getDimension(c, lastC);
}
void enum_skinElements(const std::string& name, unsigned index) {
EnumBind skinElements("SkinFlags",false);
skinElements[std::string("SF_") + name] = index;
}
void enum_skinFonts(const std::string& name, unsigned index) {
EnumBind skinElements("FontType",false);
skinElements[std::string("FT_") + name] = index;
}
void enum_skinColors(const std::string& name, unsigned index) {
EnumBind skinElements("SkinColor",false);
skinElements[std::string("SC_") + name] = index;
}
void enum_skinStyles(const std::string& name, unsigned index) {
EnumBind skinElements("SkinStyle",false);
skinElements[std::string("SS_") + name] = index;
}
void get_skinStyles(CScriptDictionary& map) {
skin::enumerateStyleIndices([&map](const std::string& name, unsigned value) {
asINT64 val = value;
map.Set(name, val);
});
}
vec2i getMousePos() {
vec2i pos;
devices.driver->getMousePos(pos.x,pos.y);
return vec2d(pos) / ui_scale;
}
void setMousePos(const vec2i& pos) {
vec2i scaledPos = vec2d(pos) * ui_scale;
devices.driver->setMousePos(scaledPos.x, scaledPos.y);
}
void setMouseLock(bool lock) {
devices.driver->setCursorShouldLock(lock);
}
void font_text(render::Font& font, const vec2i& pos, const std::string& text) {
font.draw(devices.render,text.c_str(),pos.x,pos.y,0,getClip());
}
void font_text_c(render::Font& font, const vec2i& pos, const std::string& text, const Color& color) {
font.draw(devices.render,text.c_str(),pos.x,pos.y,&color,getClip());
}
vec2i font_char(render::Font& font, const vec2i& pos, int c, int lastC, const Color& color) {
return font.drawChar(devices.render,c,lastC,pos.x,pos.y,&color,getClip());
}
static void font_trunc_c(render::Font& font, const recti& pos, const std::string& text, const std::string& ellipsis, const Color& color, double horizAlign, double vertAlign) {
vec2i dpos = pos.topLeft;
int width = pos.getWidth();
vec2i sz = font.getDimension(text.c_str());
if(vertAlign != 0) {
dpos.y += (int)(vertAlign * double(pos.getHeight() - sz.height));
dpos.y += (font.getLineHeight() - font.getBaseline()) / 2;
}
if(sz.width <= width) {
if(horizAlign != 0)
dpos.x += (int)(horizAlign * double(width - sz.width));
font.draw(devices.render, text.c_str(), dpos.x, dpos.y, &color, getClip());
return;
}
vec2i esz = font.getDimension(ellipsis.c_str());
width -= esz.width;
int relX = 0;
u8it it(text);
int lastC = 0;
while(int c = it++) {
vec2i dim = font.getDimension(c, lastC);
if(relX + dim.x > width) {
font.draw(devices.render, ellipsis.c_str(), dpos.x + relX, dpos.y, &color, getClip());
break;
}
font.drawChar(devices.render, c, lastC, dpos.x + relX, dpos.y, &color, getClip());
relX += dim.x;
}
}
static void font_trunc_cs(render::Font& font, const recti& pos, const std::string& text, const Color& stroke, const std::string& ellipsis, const Color& color, double horizAlign, double vertAlign, int strokeWidth) {
if(stroke.a != 0) {
font_trunc_c(font, pos+vec2i(-strokeWidth,-strokeWidth), text, ellipsis, stroke, horizAlign, vertAlign);
font_trunc_c(font, pos+vec2i(strokeWidth,-strokeWidth), text, ellipsis, stroke, horizAlign, vertAlign);
font_trunc_c(font, pos+vec2i(-strokeWidth,strokeWidth), text, ellipsis, stroke, horizAlign, vertAlign);
font_trunc_c(font, pos+vec2i(strokeWidth,strokeWidth), text, ellipsis, stroke, horizAlign, vertAlign);
}
font_trunc_c(font, pos, text, ellipsis, color, horizAlign, vertAlign);
}
static vec2i font_wrap(render::Font& font, const recti& startPos, const vec2i& offset, int lineHeight, const std::string& text, const Color& color, bool draw = true, bool preserve = false) {
if(lineHeight < 0)
lineHeight = font.getLineHeight();
int yOff = (font.getLineHeight() - font.getBaseline()) / 2;
const char* str = text.c_str();
const char* word = str;
vec2i pos = startPos.topLeft;
pos += offset;
vec2i wordPos = pos;
auto handleWord = [&](const char* from, const char* to) {
if(pos.x > startPos.botRight.x) {
pos.y += lineHeight;
pos.x = startPos.topLeft.x + (pos.x - wordPos.x);
wordPos.y = pos.y;
wordPos.x = startPos.topLeft.x;
if(!preserve)
lineHeight = font.getLineHeight();
}
if(draw) {
u8it it(from);
int lastC = 0;
while(it.str != to) {
int c = it++;
wordPos += font.drawChar(devices.render, c, lastC, wordPos.x, wordPos.y + yOff, &color, getClip());
lastC = c;
++word;
}
}
else {
wordPos.x = pos.x;
}
};
u8it it(str);
int lastC = 0;
while(int c = it++) {
//Skip over letters
if(c != ' ') {
if(c == '\n' || c == '\r') {
if(word != it.str)
handleWord(word, it.str);
pos.x = startPos.topLeft.x + (pos.x - wordPos.x);
pos.y += c == '\n' ? lineHeight : lineHeight+10;
wordPos.x = startPos.topLeft.x;
wordPos.y = pos.y;
if(!preserve)
lineHeight = font.getLineHeight();
}
else {
pos.x += font.getDimension(c, lastC).x;
}
lastC = c;
continue;
}
//Draw the previous word
if(word != it.str)
handleWord(word, it.str);
//Set up for next word
pos.x += font.getDimension(c, lastC).x;
word = it.str;
wordPos = pos;
lastC = c;
}
//Draw the last word
if(word != it.str)
handleWord(word, it.str);
return pos;
}
static vec2i font_wrap_dim(render::Font& font, const recti& startPos, const vec2i& offset, int lineHeight, const std::string& text, bool preserve) {
Color col(0xffffffff);
return font_wrap(font, startPos, offset, lineHeight, text, col, false, preserve);
}
static vec2i font_wrap_draw(render::Font& font, const recti& startPos, const vec2i& offset, int lineHeight, const std::string& text, const Color& color, bool preserve, const Color& stroke) {
if(stroke.a != 0) {
font_wrap(font, startPos+vec2i(-1,0), offset, lineHeight, text, stroke, true, preserve);
font_wrap(font, startPos+vec2i(0,-1), offset, lineHeight, text, stroke, true, preserve);
font_wrap(font, startPos+vec2i(0,1), offset, lineHeight, text, stroke, true, preserve);
font_wrap(font, startPos+vec2i(1,0), offset, lineHeight, text, stroke, true, preserve);
}
return font_wrap(font, startPos, offset, lineHeight, text, color, true, preserve);
}
static double getUIScale() {
return ui_scale;
}
static void setUIScale(double value) {
if(value < 0.1 || value > 10) {
scripts::throwException("UI scale value limited to range [0.1, 10].");
return;
}
ui_scale = value;
}
void RegisterGuiBinds() {
//-- SKIN ELEMENTS
{
ClassBind fnt("Font", asOBJ_REF | asOBJ_NOCOUNT, 0);
classdoc(fnt, "Describes a font as defined in a data file, can be used to draw text on the screen.");
fnt.addMember("const Font@ bold", offsetof(render::Font, bold))
doc("The bold variant for this font, if specified. Null otherwise.");
fnt.addMember("const Font@ italic", offsetof(render::Font, italic))
doc("The italic variant for this font, if specified. Null otherwise.");
fnt.addExternMethod("vec2i getDimension(const string &in text) const", asFUNCTION(getFontDimension))
doc("Calculate the amount of space needed to draw a string with this font.",
"Text to calculate for.", "Amount of space needed.");
fnt.addExternMethod("vec2i getDimension(int c, int lastC) const", asFUNCTION(getFontCharDim))
doc("Calculate the amount of space needed to draw a character with kerning.",
"Character to calculate for.", "Previous character to kern with.",
"Amount of space needed.");
fnt.addMethod("uint getBaseline() const", asMETHOD(render::Font, getBaseline))
doc("", "Baseline height for this font.");
fnt.addMethod("uint getLineHeight() const", asMETHOD(render::Font, getLineHeight))
doc("", "Line height for this font.");
fnt.addExternMethod("void draw(const vec2i &in pos, const string &in text) const", asFUNCTION(font_text))
doc("Draw text on screen with this font.",
"Position to draw text at.", "Text to draw");
fnt.addExternMethod("void draw(const vec2i &in pos, const string &in text, const Color&in color) const", asFUNCTION(font_text_c))
doc("Draw text on screen with this font.",
"Position to draw text at.", "Text to draw", "Color to draw the text in.");
fnt.addExternMethod("vec2i draw(const vec2i &in pos, int c, int lastC, const Color&in color) const", asFUNCTION(font_char))
doc("Draw a character on screen using kerning with the previous character.",
"Position to draw the character at.", "Character to draw.",
"Character previously drawn before it. (Character will be kerned with respect to it)", "Color to draw the character in.",
"The size of the area the character was drawn in.");
fnt.addExternMethod("void draw(const recti &in pos, const string &in text, const string &in ellipsis = locale::ELLIPSIS,"
"const Color&in color = colors::White, double horizAlign = 0, double vertAlign = 0.5) const", asFUNCTION(font_trunc_c))
doc("Draw text on screen with this font, appending an ellipsis if the text is too long.",
"Box to draw the text inside.", "Text to draw.", "Ellipsis to append to text.",
"Color to draw the text in.", "Horizontal alignment within the box.", "Vertical alignment within the box.");
fnt.addExternMethod("void draw(const recti &in pos, const string &in text, const Color& stroke, const string &in ellipsis = locale::ELLIPSIS,"
"const Color&in color = colors::White, double horizAlign = 0, double vertAlign = 0.5, int strokeWidth = 1) const", asFUNCTION(font_trunc_cs))
doc("Draw text on screen with this font, appending an ellipsis if the text is too long.",
"Box to draw the text inside.", "Text to draw.", "Color of the stroke.", "Ellipsis to append to text.",
"Color to draw the text in.", "Horizontal alignment within the box.", "Vertical alignment within the box.",
"Width of the stroke.");
fnt.addExternMethod("vec2i draw(const recti &in pos, const vec2i& offset, int lineHeight, const string &in text,"
" const Color&in color, bool preserveLineHeight = false, const Color& stroke = colors::Invisible) const", asFUNCTION(font_wrap_draw))
doc("Draw text on screen with this font, word wrapping within the area.",
"Box to draw the text inside.", "Offset of the first line of text in the box.",
"Height of each drawn line.", "Text to draw.", "Color to draw the text in.",
"Whether lines after the first line should not reset the lineheight.",
"Color to draw a stroke with.", "Dimensions of the text that was drawn.");
fnt.addExternMethod("vec2i getEndPosition(const recti &in pos, const vec2i& offset, int lineHeight,"
" const string &in text, bool preserveLineHeight = false) const",
asFUNCTION(font_wrap_dim))
doc("Get the position the cursor would end up at after drawing this text with word wrap.",
"Box to draw the text inside.", "Offset of the first line of text in the box.",
"Height of each drawn line.", "Text to draw.", "Whether lines after the first line should not reset the lineheight.",
"Dimension of the text that would be drawn.");
{
Namespace ns("font");
foreach(it, devices.library.fonts)
bindGlobal(format("const ::Font $1", it->first).c_str(), it->second);
}
}
{
EnumBind styleEnum("SkinStyle");
classdoc(styleEnum, "Holds the identifiers for all styles defined in skin definitions.");
skin::enumerateStyleIndices(enum_skinStyles);
styleEnum["SS_NULL"] = -1;
bind("void getSkinStyles(dictionary& result)", asFUNCTION(get_skinStyles))
doc("Fill the passed dictionary with a mapping of all skin style names to their enum identifiers.",
"Dictionary to fill.");
bind("uint getSkinStyleCount()", asFUNCTION(skin::getStyleCount))
doc("", "The amount of skin styles currently defined.");
bind("string getElementFlagName(uint flags)", asFUNCTION(skin::getElementFlagName))
doc("Retrieve the string name of a combination of skin flags.",
"Flag mask to retrieve the name for.", "Name(s) of flags.");
}
{
EnumBind colorEnum("SkinColor");
classdoc(colorEnum, "Identifiers for all color classes in skin definitions.");
skin::enumerateColorIndices(enum_skinColors);
}
{
EnumBind fontEnum("FontType");
classdoc(fontEnum, "Identifiers for all font classes in skin definitions.");
skin::enumerateFontIndices(enum_skinFonts);
}
{
EnumBind skinFlags("SkinFlags");
classdoc(skinFlags, "Masks for skin style flags to alter how a style is presented.");
skin::enumerateElementFlags(enum_skinElements);
}
ClassBind skin("Skin", asOBJ_REF | asOBJ_NOCOUNT, 0);
classdoc(skin,
"Represents a collection of styles that can be swapped out as a skin. "
"Styles are individual elements that can be drawn in arbitrarily-sized screen areas (button backgrounds, hud bars, etc). "
"Each style can be drawn with flags that alter its drawn style (hovered, active, etc).");
skin.addExternMethod("void draw(SkinStyle style, SkinFlags flags, const recti &in box) const", asFUNCTION(skin_draw))
doc("Draw a skin style on a screen area.",
"Style to draw.", "Mask of skin flags for draw state.", "Coordinates on screen to draw at.");
skin.addExternMethod("void draw(SkinStyle style, SkinFlags flags, const recti &in box, const Color&in color) const", asFUNCTION(skin_draw_c))
doc("Draw a skin style on a screen area.",
"Style to draw.", "Mask of skin flags for draw state.", "Coordinates on screen to draw at.", "Color to colorize the style as.");
skin.addExternMethod("void draw(SkinStyle style, uint flags, const recti &in box) const", asFUNCTION(skin_draw))
doc("Draw a skin style on a screen area.",
"Style to draw.", "Mask of skin flags for draw state.", "Coordinates on screen to draw at.");
skin.addExternMethod("void draw(SkinStyle style, uint flags, const recti &in box, const Color&in color) const", asFUNCTION(skin_draw_c))
doc("Draw a skin style on a screen area.",
"Style to draw.", "Mask of skin flags for draw state.", "Coordinates on screen to draw at.", "Color to colorize the style as.");
skin.addExternMethod("void draw(FontType font, const vec2i &in pos, const string &in text) const", asFUNCTION(skin_text))
doc("Draw text on screen with a specific font.",
"Font class to draw with.", "Position to draw the text at.", "Text to draw.");
skin.addExternMethod("void draw(FontType font, const vec2i &in pos, const string &in text, const Color&in color) const", asFUNCTION(skin_text_c))
doc("Draw text on screen with a specific font.",
"Font class to draw with.", "Position to draw the text at.", "Text to draw.", "Color to draw the text in.");
skin.addExternMethod("vec2i draw(FontType font, const vec2i &in pos, int c, int lastC, const Color&in color) const", asFUNCTION(skin_char))
doc("Draw a character on screen using kerning with the previous character.",
"Font class to draw with.", "Position to draw the character at.", "Character to draw.",
"Character previously drawn before it. (Character will be kerned with respect to it)", "Color to draw the character in.",
"Size of the area the character was drawn in.");
skin.addExternMethod("const Font@ getFont(FontType font) const", asFUNCTION(skin_font))
doc("Retrieve the font associated with a font class by this skin.",
"Font class to lookup.", "Associated font.");
skin.addExternMethod("Color getColor(SkinColor color) const", asFUNCTION(skin_color))
doc("Retrieve the color associated with a color class by this skin.",
"Color class to lookup.", "Associated color.");
skin.addExternMethod("vec2i getSize(SkinStyle style, SkinFlags flags) const", asFUNCTION(skin_size))
doc("Retrieve the definition size of a skin style."
"(Note: Non-uniform skin styles can be drawn at any size, not just the definition size.)",
"Skin style definition to lookup.", "Skin flags for the style to lookup.",
"Definition size of the style.");
skin.addExternMethod("bool isIrregular(SkinStyle style) const", asFUNCTION(skin_irregular))
doc("Whether a skin style was defined as irregular. Irregular styles require pixel-checking for focus and"
" can not rely on bounding boxes.", "Skin style to lookup for.",
"Whether the style was defined as irregular.");
skin.addExternMethod("bool isPixelActive(SkinStyle style, SkinFlags flags, const recti &in box, const vec2i &in px) const", asFUNCTION(skin_pxactive))
doc("Check whether a particular pixel would be active in an irregular style as it would be drawn.",
"Skin style to check for.", "Skin flags for the style to check for.", "Box that the style would be drawn in.",
"Pixel relative to the start of the box to check.", "Whether the specified pixel would be active.");
skin.addExternMethod("bool isPixelActive(SkinStyle style, uint flags, const recti &in box, const vec2i &in px) const", asFUNCTION(skin_pxactive))
doc("Check whether a particular pixel would be active in an irregular style as it would be drawn.",
"Skin style to check for.", "Skin flags for the style to check for.", "Box that the style would be drawn in.",
"Pixel relative to the start of the box to check.", "Whether the specified pixel would be active.");
skin.addExternMethod("uint getStyleElementCount(SkinStyle style) const", asFUNCTION(skin_eleCount))
doc("Get the amount of style elements are in a particular skin style. A style element",
" directs the appearance of the style under particular flags.",
"Skin style to check for.", "Amount of style elements.");
skin.addExternMethod("uint getStyleElementFlags(SkinStyle style, uint index) const", asFUNCTION(skin_eleFlags))
doc("Retrieve the flag mask a particular style element indicates the appearance for.",
"Skin style to retrieve from.", "Index of the style element to retrieve from.",
"Mask of skin style flags that the element indicates.");
//-- Library access
bind("const Font@ getFont(const string &in name)", asFUNCTION(get_font))
doc("Get a font by name.", "", "");
bind("FontType getFontType(const string &in name)", asFUNCTION(get_fontType))
doc("Get a font type by name.", "", "");
bind("SkinColor getColorType(const string &in name)", asFUNCTION(get_colorType))
doc("Get a color type by name.", "", "");
bind("const Skin@ getSkin(const string &in name)", asFUNCTION(get_skin))
doc("Get a skin by name.", "", "");
//-- GLOBAL
bind("void setClip(const recti& in clip)", asFUNCTION(setClip))
doc("Set the currently active clipping rectangle.",
"Rectangle to clip all subsequent 2D draws in.");
bind("void clearClip()", asFUNCTION(clearClip))
doc("Clear the current clipping rectangle.");
bind("vec2i get_screenSize()", asFUNCTION(screen_size))
doc("Get the current size of the game screen.", "");
bind("void flashWindow()", asFUNCTION(window_flash))
doc("Flash the window to bring attention.");
bind("bool get_windowFocused()", asFUNCTION(window_focused))
doc("Whether the game window is currently focused.", "");
bind("bool get_windowMinimized()", asFUNCTION(window_minimized))
doc("Whether the game window is currently minimized.", "");
bind("vec2i get_windowSize()", asFUNCTION(window_size))
doc("Get the actual window size (independent of ui scaling).", "");
bind("bool get_mouseOverWindow()", asFUNCTION(window_mouseOver))
doc("Whether the mouse is currently over the window.", "");
bind("vec2i get_mousePos()", asFUNCTION(getMousePos))
doc("Get the current mouse position.", "");
bind("void set_mousePos(const vec2i &in)", asFUNCTION(setMousePos))
doc("Set the mouse position.", "New mouse position");
bind("void set_mouseLock(bool lock)", asFUNCTION(setMouseLock))
doc("Sets whether the cursor should be locked to the screen, if the user has enabled the feature.", "");
bindGlobal("bool shiftKey", &devices.driver->shiftKey)
doc("Whether the shift key is currently pressed.");
bindGlobal("bool altKey", &devices.driver->altKey)
doc("Whether the alt key is currently pressed.");
bindGlobal("bool ctrlKey", &devices.driver->ctrlKey)
doc("Whether the control key is currently pressed.");
bindGlobal("bool mouseLeft", &devices.driver->leftButton)
doc("Whether the left mouse button is currently pressed.");
bindGlobal("bool mouseMiddle", &devices.driver->middleButton)
doc("Whether the middle mouse button is currently pressed.");
bindGlobal("bool mouseRight", &devices.driver->rightButton)
doc("Whether the right mouse button is currently pressed.");
bind("double get_uiScale()", asFUNCTION(getUIScale))
doc("", "Global zoom factor on the entire UI.");
bind("void set_uiScale(double value)", asFUNCTION(setUIScale))
doc("", "New global zoom factor on the entire UI. Must be between 0.1 and 10.");
bindGlobal("bool hide_ui", &hide_ui)
doc("Whether to hide all 2D UI interface elements");
}
};
+144
View File
@@ -0,0 +1,144 @@
#include "binds.h"
#include <stdlib.h>
#include <string>
#include "rect.h"
#include "main/logging.h"
#include "design/hull.h"
namespace scripts {
void inspect(asIScriptContext* ctx, const char* name, void* pData, int typeID) {
asIScriptEngine* engine = ctx->GetEngine();
union {
double asDouble;
asINT64 asInt;
};
char buffer[256];
sprintf(buffer,"<?> at 0x%p",pData);
const char* value = buffer;
int objType = typeID & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR);
void* handle = 0;
if(typeID & asTYPEID_OBJHANDLE) {
handle = pData;
pData = *(void**)pData;
}
switch(objType) {
case asTYPEID_VOID:
value = "<void>";
break;
case asTYPEID_BOOL:
asInt = *(bool*)pData;
value = asInt ? "true" : "false";
break;
case asTYPEID_INT8:
asInt = *(char*)pData;
sprintf(buffer,"%d",(int)asInt);
value = buffer;
break;
case asTYPEID_INT16:
asInt = *(short*)pData;
sprintf(buffer,"%d",(int)asInt);
value = buffer;
break;
case asTYPEID_INT32:
asInt = *(int*)pData;
sprintf(buffer,"%d",(int)asInt);
value = buffer;
break;
case asTYPEID_INT64:
asInt = *(asINT64*)pData;
sprintf(buffer,"%ld",asInt);
value = buffer;
break;
case asTYPEID_UINT8:
asInt = *(unsigned char*)pData;
sprintf(buffer,"%d",(unsigned)asInt);
value = buffer;
break;
case asTYPEID_UINT16:
asInt = *(unsigned short*)pData;
sprintf(buffer,"%d",(unsigned)asInt);
value = buffer;
break;
case asTYPEID_UINT32:
asInt = *(unsigned*)pData;
sprintf(buffer,"%d",(unsigned)asInt);
value = buffer;
break;
case asTYPEID_UINT64:
asInt = *(asINT64*)pData;
sprintf(buffer,"%lu",asInt);
value = buffer;
break;
case asTYPEID_FLOAT:
asDouble = *(double*)pData;
sprintf(buffer,"%f",asDouble);
value = buffer;
break;
case asTYPEID_DOUBLE:
asDouble = *(double*)pData;
sprintf(buffer,"%f",asDouble);
value = buffer;
break;
default:
if( pData ) {
if(objType == engine->GetTypeIdByDecl("recti")) {
recti* r = (recti*)pData;
sprintf(buffer,"<%d,%d,%d,%d>",r->topLeft.x,r->topLeft.y,r->botRight.x,r->botRight.y);
value = buffer;
}
else if(objType == engine->GetTypeIdByDecl("vec2i")) {
vec2i* v = (vec2i*)pData;
sprintf(buffer,"<%d,%d>",v->x,v->y);
value = buffer;
}
else if(objType == engine->GetTypeIdByDecl("vec2f")) {
vec2f* v = (vec2f*)pData;
sprintf(buffer,"<%f,%f>",v->x,v->y);
value = buffer;
}
else if(objType == engine->GetTypeIdByDecl("Hull")) {
HullDef* hull = (HullDef*)pData;
sprintf(buffer,"{name:%64s; id:%d}",hull->name.c_str(),hull->id);
}
else if(handle != 0) {
sprintf(buffer,"0x%p",handle);
}
}
else {
value = "null";
}
}
error("%s %s = %s",engine->GetTypeDeclaration(typeID),name,value);
}
void inspect(const std::string& name, void* pData, int typeID) {
inspect(asGetActiveContext(), name.c_str(), pData, typeID);
}
void printContext() {
asIScriptContext* ctx = asGetActiveContext();
for(unsigned d = 0; d < ctx->GetCallstackSize(); ++d) {
asIScriptFunction* func = ctx->GetFunction(d);
func->GetName();
error("%u: %s\n %s(%i)", d, func->GetDeclaration(), func->GetScriptSectionName(), ctx->GetLineNumber(d));
for(unsigned v = 0; v < (unsigned)ctx->GetVarCount(d); ++v) {
if(!ctx->IsVarInScope(v,d))
continue;
inspect(ctx, ctx->GetVarName(v,d), ctx->GetAddressOfVar(v,d), ctx->GetVarTypeId(v,d));
}
}
}
void RegisterInspectionBinds() {
bind("void inspect(const string &in name, const ?&in)", asFUNCTIONPR(inspect,(const std::string&, void*, int),void));
bind("void debug()", asFUNCTION(printContext));
}
};
File diff suppressed because it is too large Load Diff
+405
View File
@@ -0,0 +1,405 @@
#ifdef _WIN32
#include <Windows.h>
#include <XInput.h>
#endif
#include "GLFW/glfw3.h"
#include "binds.h"
#define JOY_AXIS_MAX 16
#define JOY_BUTTON_MAX 64
#define GLFW_DEADZONE 0.1
#ifdef _WIN32
//As XInput is not available on XP, we need to load it manually
struct XInputLibrary {
HMODULE dll;
decltype(XInputGetState)* xInputGetState;
decltype(XInputSetState)* xInputSetState;
XInputLibrary() : dll(0), xInputGetState(0) {
}
~XInputLibrary() {
if(dll != NULL)
FreeLibrary(dll);
}
bool load() {
//There are three current variants of the library, we try to get the most recent one
//XInput1_4.dll from Window 8
//XInput1_3.dll from DirectX
//XInput9_1_0.dll on Vista
dll = LoadLibrary("XInput1_4.dll");
if(dll == NULL)
dll = LoadLibrary("XInput1_3.dll");
if(dll == NULL)
dll = LoadLibrary("XInput9_1_0.dll");
if(dll == NULL)
return false;
xInputGetState = (decltype(xInputGetState))GetProcAddress(dll, "XInputGetState");
xInputSetState = (decltype(xInputSetState))GetProcAddress(dll, "XInputSetState");
return xInputGetState != 0;
}
bool getState(DWORD index, XINPUT_STATE& state) {
if(!dll || !xInputGetState)
return false;
return xInputGetState(index, &state) == ERROR_SUCCESS;
}
void setVibration(DWORD index, float lowFreq, float hiFreq) {
if(xInputSetState) {
XINPUT_VIBRATION vibration;
ZeroMemory(&vibration, sizeof(vibration));
if(lowFreq >= 1.f)
vibration.wLeftMotorSpeed = 65535;
else if(lowFreq <= 0.f)
vibration.wLeftMotorSpeed = 0;
else
vibration.wLeftMotorSpeed = (WORD)(lowFreq * 65535.f);
if(hiFreq >= 1.f)
vibration.wRightMotorSpeed = 65535;
else if(hiFreq <= 0.f)
vibration.wRightMotorSpeed = 0;
else
vibration.wRightMotorSpeed = (WORD)(hiFreq * 65535.f);
xInputSetState(index, &vibration);
}
}
} xinput;
#endif
namespace scripts {
enum JoystickButtonState {
JBS_Off = GLFW_RELEASE,
JBS_On = GLFW_PRESS,
JBS_Released,
JBS_Pressed
};
//TODO: This should go through a driver, not directly glfw
class Joystick {
public:
bool isXInput;
int index;
unsigned axisCount;
float axes[JOY_AXIS_MAX];
unsigned buttonCount;
unsigned char buttons[JOY_BUTTON_MAX];
Joystick(unsigned Index) : axisCount(0), axes(), buttonCount(0), buttons(), isXInput(false) {
#ifdef _WIN32
XINPUT_STATE state;
if(xinput.load() && xinput.getState(Index - 1, state)) {
index = (int)Index - 1;
isXInput = true;
poll();
return;
}
#endif
if(Index >= 1 && Index <= 16)
index = GLFW_JOYSTICK_1 + (int)Index - 1;
poll();
}
bool poll() {
if(isXInput) {
#ifdef _WIN32
XINPUT_STATE state;
auto buttonUpdate = [this,&state](unsigned btn, bool pressed) {
auto& button = buttons[btn];
if(pressed) {
if(button == JBS_Pressed)
button = JBS_On;
else if(button != JBS_On)
button = JBS_Pressed;
}
else {
if(button == JBS_Released)
button = JBS_Off;
else if(button != JBS_Off)
button = JBS_Released;
}
};
auto deadZone = [](int value, int deadzone, int scale) -> float {
if(value >= -deadzone && value <= deadzone)
return 0;
bool neg = value < 0;
if(neg)
value = -value;
float result = (float)(value - deadzone) / (float)(scale - deadzone);
return neg ? -result : result;
};
if(xinput.getState(index, state)) {
buttonCount = 16;
buttonUpdate(0, state.Gamepad.wButtons & XINPUT_GAMEPAD_A);
buttonUpdate(1, state.Gamepad.wButtons & XINPUT_GAMEPAD_B);
buttonUpdate(2, state.Gamepad.wButtons & XINPUT_GAMEPAD_X);
buttonUpdate(3, state.Gamepad.wButtons & XINPUT_GAMEPAD_Y);
buttonUpdate(4, state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP);
buttonUpdate(5, state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT);
buttonUpdate(6, state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN);
buttonUpdate(7, state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT);
buttonUpdate(8, state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
buttonUpdate(9, state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD);
buttonUpdate(10, state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
buttonUpdate(11, state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
buttonUpdate(12, state.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD);
buttonUpdate(13, state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
buttonUpdate(14, state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK);
buttonUpdate(15, state.Gamepad.wButtons & XINPUT_GAMEPAD_START);
axisCount = 6;
axes[0] = deadZone(state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, 32768);
axes[1] = deadZone(state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, 32768);
axes[2] = deadZone(state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, 32768);
axes[3] = deadZone(state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, 32768);
axes[4] = deadZone(state.Gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255);
axes[5] = deadZone(state.Gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255);
return true;
}
#endif
return false;
}
#ifdef LIN_MODE
else if(true) {
//Statically figured out button/axes numbers from the xboxdrv linux driver.
int btnCnt;
auto* newState = glfwGetJoystickButtons(index, &btnCnt);
int axCnt;
const float* glfwAxes = glfwGetJoystickAxes(index, &axCnt);
auto setButton = [this](unsigned btn, bool pressed) {
auto& button = buttons[btn];
if(pressed) {
if(button == JBS_Pressed)
button = JBS_On;
else if(button != JBS_On)
button = JBS_Pressed;
}
else {
if(button == JBS_Released)
button = JBS_Off;
else if(button != JBS_Off)
button = JBS_Released;
}
};
auto buttonUpdate = [&](unsigned btn, unsigned index) {
if((int)index >= btnCnt) {
buttons[btn] = JBS_Off;
return;
}
setButton(btn, newState[index]);
};
auto buttonFromAxis = [&](unsigned btn, unsigned index, float thresMin, float thresMax) {
if((int)index >= axCnt) {
buttons[btn] = JBS_Off;
return;
}
setButton(btn, glfwAxes[index] >= thresMin && glfwAxes[index] <= thresMax);
};
auto axisUpdate = [&](unsigned ax, unsigned index) {
if((int)index >= axCnt) {
axes[ax] = 0.f;
return;
}
float val = glfwAxes[index];
if(val >= -GLFW_DEADZONE && val <= GLFW_DEADZONE)
axes[ax] = 0.f;
else if(val < 0)
axes[ax] = -((-val - GLFW_DEADZONE) / (1.f - GLFW_DEADZONE));
else
axes[ax] = (val - GLFW_DEADZONE) / (1.f - GLFW_DEADZONE);
};
auto triggerAxis = [&](unsigned ax, unsigned index, float mult) {
if((int)index >= axCnt) {
axes[ax] = 0.f;
return;
}
float val = glfwAxes[index] * mult;
if(val <= -0.9f)
axes[ax] = 0.f;
else
axes[ax] = (val + 0.9f) / 1.9f;
};
buttonUpdate(0, 0);
buttonUpdate(1, 1);
buttonUpdate(2, 2);
buttonUpdate(3, 3);
buttonFromAxis(4, 7, 0.1f, 1.f);
buttonFromAxis(5, 6, -1.f, -0.1f);
buttonFromAxis(6, 7, -1.f, -0.1f);
buttonFromAxis(7, 6, 0.1f, 1.f);
buttonUpdate(8, 4);
buttonFromAxis(9, 5, -1.f, 0.f);
buttonUpdate(10, 9);
buttonUpdate(11, 5);
buttonFromAxis(12, 4, 0.f, 1.f);
buttonUpdate(13, 10);
buttonUpdate(14, 6);
buttonUpdate(15, 7);
buttonCount = 16;
axisUpdate(0, 0);
axisUpdate(1, 1);
axisUpdate(2, 2);
axisUpdate(3, 3);
triggerAxis(4, 5, -1.f);
triggerAxis(5, 4, 1.f);
axisCount = 6;
return true;
}
#endif
else {
int cnt;
auto* newState = glfwGetJoystickButtons(index, &cnt);
buttonCount = cnt;
if(buttonCount == 0)
return false;
for(unsigned i = 0; i < JOY_BUTTON_MAX; ++i) {
if(buttons[i] == newState[i])
continue;
if(newState[i] == GLFW_PRESS) {
if(buttons[i] == JBS_Pressed)
buttons[i] = JBS_On;
else
buttons[i] = JBS_Pressed;
}
else {
if(buttons[i] == JBS_Released)
buttons[i] = JBS_Off;
else
buttons[i] = JBS_Released;
}
}
const float* glfwAxes = glfwGetJoystickAxes(index, &cnt);
axisCount = cnt;
for(int i = 0; i < cnt && i < JOY_AXIS_MAX; ++i) {
//Static deadzone, because glfw
float val = glfwAxes[i];
if(val >= -GLFW_DEADZONE && val <= GLFW_DEADZONE)
axes[i] = 0.f;
else if(val < 0)
axes[i] = -((-val - GLFW_DEADZONE) / (1.f - GLFW_DEADZONE));
else
axes[i] = (val - GLFW_DEADZONE) / (1.f - GLFW_DEADZONE);
}
return buttonCount > 0;
}
}
float getAxis(unsigned axis) const {
if(axis < JOY_AXIS_MAX)
return axes[axis];
else
return 0;
}
unsigned char getButton(unsigned id) const {
if(id < JOY_BUTTON_MAX)
return buttons[id];
else
return JBS_Off;
}
bool getPressed(unsigned id) const {
auto state = getButton(id);
return state == JBS_On || state == JBS_Pressed;
}
bool connected() const {
return glfwJoystickPresent(index) == GL_TRUE;
}
void setVibration(float lowFreq, float hiFreq) {
#ifdef _WIN32
if(isXInput) {
xinput.setVibration(index, lowFreq, hiFreq);
}
#endif
}
static void construct(void* memory, unsigned index) {
new(memory) Joystick(index);
}
};
void RegisterJoystickBinds() {
ClassBind joystick("Joystick", asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C, sizeof(Joystick));
joystick.addConstructor("void f(uint)", asFUNCTION(Joystick::construct));
joystick.addMethod("bool connected() const", asMETHOD(Joystick,connected));
joystick.addMethod("bool poll()", asMETHOD(Joystick,poll));
joystick.addMethod("float get_axis(uint index) const", asMETHOD(Joystick,getAxis));
joystick.addMethod("uint8 get_button(uint index) const", asMETHOD(Joystick,getButton));
joystick.addMethod("bool get_pressed(uint index) const", asMETHOD(Joystick,getPressed));
joystick.addMethod("void setVibration(float low, float high) const", asMETHOD(Joystick,setVibration));
joystick.addMember("uint axisCount", offsetof(Joystick,axisCount));
joystick.addMember("uint buttonCount", offsetof(Joystick,buttonCount));
EnumBind buttonStates("JoystickButtonState");
buttonStates["JBS_On"] = JBS_On;
buttonStates["JBS_Off"] = JBS_Off;
buttonStates["JBS_Pressed"] = JBS_Pressed;
buttonStates["JBS_Released"] = JBS_Released;
EnumBind gamepad("GamepadKeys");
gamepad["GP_A"] = 0;
gamepad["GP_B"] = 1;
gamepad["GP_X"] = 2;
gamepad["GP_Y"] = 3;
gamepad["GP_UP"] = 4;
gamepad["GP_LEFT"] = 5;
gamepad["GP_DOWN"] = 6;
gamepad["GP_RIGHT"] = 7;
gamepad["GP_LB"] = 8;
gamepad["GP_LT"] = 9;
gamepad["GP_L3"] = 10;
gamepad["GP_RB"] = 11;
gamepad["GP_RT"] = 12;
gamepad["GP_R3"] = 13;
gamepad["GP_BACK"] = 14;
gamepad["GP_START"] = 15;
gamepad["GP_AXIS_LX"] = 0;
gamepad["GP_AXIS_LY"] = 1;
gamepad["GP_AXIS_RX"] = 2;
gamepad["GP_AXIS_RY"] = 3;
gamepad["GP_AXIS_LT"] = 4;
gamepad["GP_AXIS_RT"] = 5;
}
};
+491
View File
@@ -0,0 +1,491 @@
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "util/refcount.h"
#include <stdint.h>
#include <string>
#include <fstream>
#include "main/initialization.h"
#include "scripts/binds.h"
#include "files.h"
#include "threads.h"
#include "manager.h"
namespace scripts {
class StrOutputStream {
public:
std::string value;
StrOutputStream() {}
void Put(char c) { value += c; }
void Flush() {}
};
static Threaded(rapidjson::MemoryPoolAllocator<>*) allocator = 0;
rapidjson::MemoryPoolAllocator<>& getAllocator() {
if(!allocator) {
allocator = new rapidjson::MemoryPoolAllocator<>();
addThreadCleanup([]() {
delete allocator;
allocator = 0;
});
}
return *allocator;
}
class JSONTree : public AtomicRefCounted {
public:
rapidjson::Document doc;
void parse(const std::string& str) {
doc.Parse<0>(str.c_str());
}
void readFile(const std::string& fname) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return;
}
doc.Parse<0>(getFileContents(fname).c_str());
}
void writeFile(const std::string& fname, bool pretty) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return;
}
std::fstream file(fname, std::ios_base::out);
file << toString(pretty);
}
std::string toString(bool pretty) {
StrOutputStream stream;
if(pretty) {
rapidjson::PrettyWriter<StrOutputStream> writer(stream);
writer.SetIndent('\t', 1);
doc.Accept(writer);
}
else {
rapidjson::Writer<StrOutputStream> writer(stream);
doc.Accept(writer);
}
return stream.value;
}
rapidjson::Value* root() {
return &doc;
}
};
static JSONTree* makeJSONTree() {
return new JSONTree();
}
static std::string nodeString(rapidjson::Value* node) {
if(!node->IsString()) {
scripts::throwException("Node is not a string value.");
return "";
}
return std::string(node->GetString());
}
static int nodeBool(rapidjson::Value* node) {
if(!node->IsBool()) {
scripts::throwException("Node is not a boolean value.");
return 0;
}
return node->GetBool();
}
static int nodeInt(rapidjson::Value* node) {
if(!node->IsInt()) {
scripts::throwException("Node is not an int value.");
return 0;
}
return node->GetInt();
}
static unsigned nodeUint(rapidjson::Value* node) {
if(!node->IsUint()) {
scripts::throwException("Node is not a uint value.");
return 0;
}
return node->GetUint();
}
static int64_t nodeInt64(rapidjson::Value* node) {
if(!node->IsInt64()) {
scripts::throwException("Node is not an int64 value.");
return 0;
}
return node->GetInt64();
}
static uint64_t nodeUint64(rapidjson::Value* node) {
if(!node->IsUint64()) {
scripts::throwException("Node is not a uint64 value.");
return 0;
}
return node->GetUint64();
}
static double nodeDouble(rapidjson::Value* node) {
if(!node->IsDouble()) {
scripts::throwException("Node is not a double value.");
return 0;
}
return node->GetDouble();
}
static double nodeNumber(rapidjson::Value* node) {
if(node->IsDouble())
return node->GetDouble();
if(node->IsInt())
return (double)node->GetInt();
if(node->IsUint())
return (double)node->GetUint();
if(node->IsInt64())
return (double)node->GetInt64();
if(node->IsUint64())
return (double)node->GetUint64();
scripts::throwException("Node is not a numeric value.");
return 0;
}
static rapidjson::Value* findMember(rapidjson::Value* node, const std::string& name) {
if(!node->IsObject()) {
scripts::throwException("Node is not an object.");
return 0;
}
rapidjson::Value::Member* mem = node->FindMember(name.c_str());
if(!mem)
return 0;
return &mem->value;
}
static void removeMember(rapidjson::Value* node, const std::string& name) {
if(!node->IsObject()) {
scripts::throwException("Node is not an object.");
return;
}
node->RemoveMember(name.c_str());
}
static rapidjson::Value* getMember(rapidjson::Value* node, const std::string& name) {
if(!node->IsObject()) {
scripts::throwException("Node is not an object.");
return 0;
}
rapidjson::Value::Member* mem = node->FindMember(name.c_str());
if(mem)
return &mem->value;
rapidjson::Value nullValue;
node->AddMember(name.c_str(), getAllocator(), nullValue, getAllocator());
return findMember(node, name);
}
static void clearItems(rapidjson::Value* node) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return;
}
node->Clear();
}
static rapidjson::Value* getItem(rapidjson::Value* node, unsigned index) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return 0;
}
if(index >= node->Size()) {
scripts::throwException("Index out of bounds.");
return 0;
}
return &(*node)[index];
}
static unsigned arrSize(rapidjson::Value* node) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return 0;
}
return node->Size();
}
static void arrReserve(rapidjson::Value* node, unsigned amount) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return;
}
node->Reserve(amount, getAllocator());
}
static rapidjson::Value* arrPush(rapidjson::Value* node) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return 0;
}
rapidjson::Value nullValue;
node->PushBack(nullValue, getAllocator());
return &(*node)[node->Size() - 1];
}
static void arrPop(rapidjson::Value* node) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return;
}
node->PopBack();
}
static rapidjson::Value* setString(rapidjson::Value* node, const std::string& value) {
node->SetString(value.c_str(), value.size(), getAllocator());
return node;
}
void RegisterJSONBinds() {
ClassBind node("JSONNode", asOBJ_REF | asOBJ_NOCOUNT);
classdoc(node, "A node within a json tree. Careful when holding references, as"
" changes to the tree can invalidate them.");
//* Tree methods *//
ClassBind tree("JSONTree", asOBJ_REF);
classdoc(tree, "Represents a tree of json nodes that can be altered."
" Please make note that JSONNode@ references are not reference counted,"
" and can be invalidated by changes to the tree. Do not hold references"
" unless you know what you're doing.");
tree.addFactory("JSONTree@ f()", asFUNCTION(makeJSONTree));
tree.setReferenceFuncs(asMETHOD(JSONTree, grab), asMETHOD(JSONTree, drop));
tree.addMethod("void parse(const string& data)", asMETHOD(JSONTree, parse))
doc("Parse a json tree from string data.", "Data string to parse.");
tree.addMethod("void readFile(const string& filename)", asMETHOD(JSONTree, readFile))
doc("Read a json tree from a file.", "Filename of the file to read from.");
tree.addMethod("string toString(bool pretty = false)", asMETHOD(JSONTree, toString))
doc("Dump the json tree to a string.",
"Whether to pretty-print with newlines and indentation.",
"Output data string.");
tree.addMethod("void writeFile(const string& filename, bool pretty = false)",
asMETHOD(JSONTree, writeFile))
doc("Write a json tree to a file.", "Filename of the file to write to.",
"Whether to pretty-print with newlines and indentation.");
tree.addMethod("JSONNode@ get_root()", asMETHOD(JSONTree, root))
doc("", "The root json node in this tree.");
//* Node methods *//
node.addMethod("bool isNull()", asMETHOD(rapidjson::Value, IsNull))
doc("", "Whether the node is a null.");
node.addMethod("bool isFalse()", asMETHOD(rapidjson::Value, IsFalse))
doc("", "Whether the node is a false.");
node.addMethod("bool isTrue()", asMETHOD(rapidjson::Value, IsTrue))
doc("", "Whether the node is a true.");
node.addMethod("bool isBool()", asMETHOD(rapidjson::Value, IsBool))
doc("", "Whether the node is a boolean value (true or false).");
node.addMethod("bool isObject()", asMETHOD(rapidjson::Value, IsObject))
doc("", "Whether the node is an object / mapping.");
node.addMethod("bool isArray()", asMETHOD(rapidjson::Value, IsArray))
doc("", "Whether the node is an array.");
node.addMethod("bool isNumber()", asMETHOD(rapidjson::Value, IsNumber))
doc("", "Whether the node is a numerical type.");
node.addMethod("bool isInt()", asMETHOD(rapidjson::Value, IsInt))
doc("", "Whether the node is an integer.");
node.addMethod("bool isUint()", asMETHOD(rapidjson::Value, IsUint))
doc("", "Whether the node is an unsigned integer.");
node.addMethod("bool isInt64()", asMETHOD(rapidjson::Value, IsInt64))
doc("", "Whether the node is an long integer.");
node.addMethod("bool isUint64()", asMETHOD(rapidjson::Value, IsUint64))
doc("", "Whether the node is an long unsigned integer.");
node.addMethod("bool isDouble()", asMETHOD(rapidjson::Value, IsDouble))
doc("", "Whether the node is a double.");
node.addMethod("bool isString()", asMETHOD(rapidjson::Value, IsString))
doc("", "Whether the node is a string.");
node.addMethod("JSONNode& setNull()", asMETHOD(rapidjson::Value, SetNull))
doc("Set this node to a null value.", "This node.");
node.addMethod("JSONNode& setBool(bool value)",
asMETHOD(rapidjson::Value, SetBool))
doc("Set this node to a boolean value.",
"Boolean value to set to.",
"This node.");
node.addMethod("JSONNode& makeArray()",
asMETHOD(rapidjson::Value, SetArray))
doc("Set this node to be an array.",
"This node.");
node.addMethod("JSONNode& makeObject()",
asMETHOD(rapidjson::Value, SetObject))
doc("Set this node to be an object.",
"This node.");
node.addMethod("JSONNode& setInt(int value)",
asMETHOD(rapidjson::Value, SetInt))
doc("Set this node to an int value.",
"Integer value to set to.",
"This node.");
node.addMethod("JSONNode& setUint(uint value)",
asMETHOD(rapidjson::Value, SetUint))
doc("Set this node to an unsigned int value.",
"Unsigned integer value to set to.",
"This node.");
node.addMethod("JSONNode& setInt64(int64 value)",
asMETHOD(rapidjson::Value, SetInt64))
doc("Set this node to a long int value.",
"Long integer value to set to.",
"This node.");
node.addMethod("JSONNode& setUint64(uint64 value)",
asMETHOD(rapidjson::Value, SetUint64))
doc("Set this node to a long unsigned int value.",
"Long unsigned integer value to set to.",
"This node.");
node.addMethod("JSONNode& setDouble(double value)",
asMETHOD(rapidjson::Value, SetDouble))
doc("Set this node to a double value.",
"Double value to set to.",
"This node.");
node.addExternMethod("JSONNode& setString(const string&in value)",
asFUNCTION(setString))
doc("Set this node to a string value.",
"String value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(int value)",
asMETHOD(rapidjson::Value, SetInt))
doc("Set this node to an int value.",
"Integer value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(uint value)",
asMETHOD(rapidjson::Value, SetUint))
doc("Set this node to an unsigned int value.",
"Unsigned integer value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(int64 value)",
asMETHOD(rapidjson::Value, SetInt64))
doc("Set this node to a long int value.",
"Long integer value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(uint64 value)",
asMETHOD(rapidjson::Value, SetUint64))
doc("Set this node to a long unsigned int value.",
"Long unsigned integer value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(double value)",
asMETHOD(rapidjson::Value, SetDouble))
doc("Set this node to a double value.",
"Double value to set to.",
"This node.");
node.addExternMethod("JSONNode& opAssign(const string&in value)",
asFUNCTION(setString))
doc("Set this node to a string value.",
"String value to set to.",
"This node.");
node.addExternMethod("string getString()", asFUNCTION(nodeString))
doc("", "The string value of the node.");
node.addExternMethod("bool getBool()", asFUNCTION(nodeBool))
doc("", "The boolean value of the node.");
node.addExternMethod("int getInt()", asFUNCTION(nodeInt))
doc("", "The integer value of the node.");
node.addExternMethod("uint getUint()", asFUNCTION(nodeUint))
doc("", "The unsigned integer value of the node.");
node.addExternMethod("int64 getInt64()", asFUNCTION(nodeInt64))
doc("", "The long integer value of the node.");
node.addExternMethod("uint64 getUint64()", asFUNCTION(nodeUint64))
doc("", "The long unsigned integer value of the node.");
node.addExternMethod("double getDouble()", asFUNCTION(nodeDouble))
doc("", "The double value of the node.");
node.addExternMethod("double getNumber()", asFUNCTION(nodeNumber))
doc("", "The numeric value of the node.");
node.addExternMethod("JSONNode@ findMember(const string& name)",
asFUNCTION(findMember))
doc("Retrieve a member node from an object.",
"The name of the member.",
"Reference to the member node. Null if it does not exist.");
node.addExternMethod("JSONNode@ getMember(const string& name)",
asFUNCTION(getMember))
doc("Retrieve a member node from an object, creating it if it does not exist.",
"The name of the member.",
"Reference to the member node, can be a newly created null node.");
node.addExternMethod("JSONNode@ opIndex(const string& name)",
asFUNCTION(getMember))
doc("Retrieve a member node from an object, creating it if it does not exist.",
"The name of the member.",
"Reference to the member node, can be a newly created null node.");
node.addExternMethod("void removeMember(const string& name)", asFUNCTION(removeMember))
doc("Remove a member from an object.", "Name of the member to remove.");
node.addExternMethod("void clearItems()", asFUNCTION(clearItems))
doc("Clear all items from an array node.");
node.addExternMethod("JSONNode@ getItem(uint index)", asFUNCTION(getItem))
doc("Get an item from an array node.", "Index of the item.",
"Node at the specified index in the array.");
node.addExternMethod("JSONNode@ opIndex(uint index)", asFUNCTION(getItem))
doc("Get an item from an array node.", "Index of the item.",
"Node at the specified index in the array.");
node.addExternMethod("uint size()", asFUNCTION(arrSize))
doc("", "The size of the array node.");
node.addExternMethod("void reserve(uint amount)", asFUNCTION(arrReserve))
doc("Reserve an array node to hold an amount of items.",
"Amount of items to reserve for.");
node.addExternMethod("JSONNode@ pushBack()", asFUNCTION(arrPush))
doc("Add a new node to the end of an array node.",
"Newly created null node.");
node.addExternMethod("void popBack()", asFUNCTION(arrPop))
doc("Remove a node from the end of an array node.");
}
};
+294
View File
@@ -0,0 +1,294 @@
#include "binds.h"
#include "main/references.h"
#include "main/initialization.h"
#include "main/tick.h"
#include "network/network_manager.h"
#include "processing.h"
#include "main/save_load.h"
#include "main/logging.h"
#include "util/save_file.h"
#include "files.h"
#include "../../as_addons/include/scriptarray.h"
extern bool watch_resources;
bool queuedModSwitch = false;
std::vector<std::string> modSetup;
void startNewGame(net::Message& msg) {
if(game_running)
destroyGame();
setGameSettings(msg);
game_state = GS_Game;
}
void startNewGame() {
if(game_running)
destroyGame();
clearGameSettings();
game_state = GS_Game;
}
void stopGame() {
if(!game_running)
return;
destroyGame();
clearGameSettings();
game_state = GS_Menu;
}
namespace scripts {
static void switchToMods(CScriptArray* arr) {
if(!arr)
return;
if(game_running)
stopGame();
modSetup.clear();
for(unsigned i = 0, cnt = arr->GetSize(); i < cnt; ++i)
modSetup.push_back(*(std::string*)arr->At(i));
queuedModSwitch = true;
devices.network->disconnect();
}
static void quickstartGame() {
if(game_running)
destroyGame();
clearGameSettings();
game_state = GS_Game;
}
static void toMenu() {
game_state = GS_Menu;
}
static void toGame() {
game_state = GS_Game;
}
static void quitGame() {
game_state = GS_Quit;
}
static void preRenderClient() {
if(devices.scripts.client && game_running)
devices.scripts.client->preRender(realFrameLen);
}
static void renderClient() {
if(devices.scripts.client && game_running)
devices.scripts.client->render(realFrameLen);
}
bool scr_saveGame(const std::string& fname) {
if(!game_running) {
scripts::throwException("No game to save.");
return false;
}
if(devices.network->isClient) {
scripts::throwException("Cannot save game from multiplayer clients.");
return false;
}
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return false;
}
processing::pause();
bool success = saveGame(fname);
processing::resume();
return success;
}
void scr_loadGame(const std::string& fname) {
loadSaveName = fname;
game_state = GS_Load_Prep;
}
struct ThreadedImageData {
Image img;
std::string filename;
ThreadedImageData(const std::string& fname) : filename(fname) {}
};
static threads::threadreturn threadcall SaveImage(void* arg) {
ThreadedImageData& data = *(ThreadedImageData*)arg;
try {
if(!saveImage(&data.img, data.filename.c_str(), true))
throw 0;
}
catch(...) {
error("Could not write image");
}
delete &data;
return 0;
}
static void saveWorldScreen(const std::string& fname) {
auto* data = new ThreadedImageData(fname);
getFrameRender(data->img);
threads::createThread(SaveImage, data);
}
static unsigned saveVersion(const std::string& fname) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return (unsigned)-1;
}
SaveFileInfo info;
getSaveFileInfo(fname, info);
return info.version;
}
static bool saveInfo(const std::string& fname, SaveFileInfo& info) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return (unsigned)-1;
}
return getSaveFileInfo(fname, info);
}
static void makeSaveFileInfo(SaveFileInfo* dat) {
new(dat) SaveFileInfo();
}
static void dtorSaveFileInfo(SaveFileInfo* dat) {
dat->~SaveFileInfo();
}
static void copySaveFileInfo(SaveFileInfo* into, SaveFileInfo* from) {
new(into) SaveFileInfo();
*into = *from;
}
static mods::Mod* getTopMod() {
if(devices.mods.activeMods.empty())
return nullptr;
return devices.mods.activeMods[devices.mods.activeMods.size()-1];
}
static const mods::Mod* getMod(unsigned index) {
if(index < devices.mods.mods.size())
return devices.mods.mods[index];
else
return nullptr;
}
static const mods::Mod* getMod_id(const std::string& name) {
return devices.mods.getMod(name);
}
static unsigned getModCount() {
return devices.mods.mods.size();
}
static void saveModState() {
devices.mods.saveState();
}
static bool createNewMod(const std::string& ident) {
if(!isIdentifier(ident, " ") || ident.empty())
return false;
std::string dirname = path_join("mods", ident);
std::string infoPath = path_join(dirname, "modinfo.txt");
makeDirectory(dirname);
std::ofstream file(infoPath);
file << "Name: " << ident << "\n";
file << "Compatibility: 200\n";
file.close();
devices.mods.registerMod(dirname, ident); //Nothing could possibly go wrong here
return true;
}
void modConflicts(mods::Mod* mine, mods::Mod* other, CScriptArray* arr) {
std::vector<std::string> conflicts;
mine->getConflicts(other, conflicts);
unsigned index = arr->GetSize();
arr->Resize(index + conflicts.size());
foreach(it, conflicts)
*(std::string*)arr->At(index++) = *it;
}
static bool saveFileHasMods(SaveFileInfo* info) {
for(size_t i = 0, cnt = info->mods.size(); i < cnt; ++i) {
auto* mod = devices.mods.getMod(info->mods[i].id);
if(mod)
mod = mod->getFallback(info->mods[i].version);
if(mod == nullptr)
return false;
}
return true;
}
void RegisterMenuBinds(bool ingame) {
//Start a game
bind("void startNewGame(Message&)", asFUNCTIONPR(startNewGame, (net::Message&), void));
bind("void startNewGame()", asFUNCTION(quickstartGame));
bind("void stopGame()", asFUNCTION(stopGame));
bind("void loadGame(const string& fname)", asFUNCTION(scr_loadGame));
bind("bool saveGame(const string& fname)", asFUNCTION(scr_saveGame));
bind("void saveWorldScreen(const string& fname)", asFUNCTION(saveWorldScreen));
bind("void switchToMenu()", asFUNCTION(toMenu));
bind("void switchToGame()", asFUNCTION(toGame));
bind("void quitGame()", asFUNCTION(quitGame));
ClassBind saveData("SaveFileInfo", asOBJ_VALUE | asOBJ_APP_CLASS_CDK, sizeof(SaveFileInfo));
saveData.addConstructor("void f()", asFUNCTION(makeSaveFileInfo));
saveData.addDestructor("void f()", asFUNCTION(dtorSaveFileInfo));
saveData.addConstructor("void f(const SaveFileInfo& other)", asFUNCTION(copySaveFileInfo));
saveData.addExternMethod("bool hasMods()", asFUNCTION(saveFileHasMods));
saveData.addMember("uint version", offsetof(SaveFileInfo, version));
saveData.addMember("uint startVersion", offsetof(SaveFileInfo, startVersion));
bind("void getSaveFileInfo(const string& fname, SaveFileInfo& dat)", asFUNCTION(saveInfo));
bind("uint getSaveVersion(const string& fname)", asFUNCTION(saveVersion));
EnumBind gs("GameState");
gs["GS_Game"] = GS_Game;
gs["GS_Menu"] = GS_Menu;
gs["GS_Quit"] = GS_Quit;
bindGlobal("GameState game_state", &game_state);
//Menu rendering
bind("void preRenderClient()", asFUNCTION(preRenderClient));
bind("void renderClient()", asFUNCTION(renderClient));
//Mod listing
ClassBind mod("Mod", asOBJ_REF | asOBJ_NOCOUNT, 0);
mod.addMember("string ident", offsetof(mods::Mod, ident));
mod.addMember("string name", offsetof(mods::Mod, name));
mod.addMember("string dirname", offsetof(mods::Mod, dirname));
mod.addMember("string abspath", offsetof(mods::Mod, abspath));
mod.addMember("string parentname", offsetof(mods::Mod, parentname));
mod.addMember("string description", offsetof(mods::Mod, description));
mod.addMember("bool listed", offsetof(mods::Mod, listed));
mod.addMember("bool enabled", offsetof(mods::Mod, enabled));
mod.addMember("bool isNew", offsetof(mods::Mod, isNew));
mod.addMember("bool forced", offsetof(mods::Mod, forced));
mod.addMember("bool isBase", offsetof(mods::Mod, isBase));
mod.addMember("uint compatibility", offsetof(mods::Mod, compatibility));
mod.addMember("bool forCurrentVersion", offsetof(mods::Mod, forCurrentVersion));
mod.addMember("Mod@ parent", offsetof(mods::Mod, parent));
mod.addMethod("bool isCompatible(Mod& other)", asMETHOD(mods::Mod, isCompatible));
mod.addExternMethod("void getConflicts(Mod& other, array<string>& files)", asFUNCTION(modConflicts));
bindGlobal("bool watchResources", &watch_resources);
bindGlobal("Mod@ currentMod", &devices.mods.currentMod);
bindGlobal("Mod@ baseMod", &devices.mods.currentMod);
bind("Mod& get_topMod()", asFUNCTION(getTopMod));
bind("Mod@ getMod(uint index)", asFUNCTION(getMod));
bind("Mod@ getMod(const string& id)", asFUNCTION(getMod_id));
bind("uint get_modCount()", asFUNCTION(getModCount));
bind("void saveModState()", asFUNCTION(saveModState));
bind("bool createNewMod(const string& ident)", asFUNCTION(createNewMod));
bind("void switchToMods(const array<string>& mods)", asFUNCTION(switchToMods));
}
};
+848
View File
@@ -0,0 +1,848 @@
#include "binds.h"
#include "scriptarray.h"
#include "main/references.h"
#include "main/logging.h"
#include "design/design.h"
#include "network/message.h"
#include "network/network_manager.h"
#include "util/elevation_map.h"
#include "util/link_container.h"
#include "../as_addons/include/scriptarray.h"
#include "threads.h"
#include "color.h"
#include "empire.h"
Object* readObject(net::Message& msg, bool create, int knownType) {
unsigned typeID = knownType >= 0 ? (unsigned)knownType : msg.readLimited(ObjectTypeCount-1);
unsigned objID = msg.readSmall();
if(objID)
return getObjectByID((typeID << ObjectTypeBitOffset) | objID, create);
else
return 0;
}
void writeObject(net::Message& msg, Object* obj, bool includeType) {
unsigned typeID = 0, objID = 0;
if(obj) {
unsigned id = obj->id;
typeID = id >> ObjectTypeBitOffset;
objID = id & ObjectIDMask;
}
if(includeType)
msg.writeLimited(typeID, ObjectTypeCount-1);
msg.writeSmall(objID);
}
namespace scripts {
net::Message& readObjectScr(net::Message& msg, Object** obj);
static inline asITypeInfo* getSerializable(asIScriptEngine* engine) {
return (asITypeInfo*)engine->GetUserData(EDID_SerializableType);
}
static inline asIScriptFunction* getSerializableWrite(asIScriptEngine* engine) {
return (asIScriptFunction*)engine->GetUserData(EDID_SerializableWrite);
}
static inline asIScriptFunction* getSerializableRead(asIScriptEngine* engine) {
return (asIScriptFunction*)engine->GetUserData(EDID_SerializableRead);
}
static YieldedMessage END_CONTEXT;
static Threaded(YieldedMessage*) yieldLink = 0;
static void setNickname(const std::string& nick) {
devices.network->setNickname(nick);
}
static void yield(asIScriptObject* obj) {
if(yieldLink == 0) {
throwException("No yield context active.");
return;
}
YieldedMessage* msg;
//Make a new message or reuse
if(yieldLink->written) {
msg = new YieldedMessage();
yieldLink->next = msg;
}
else {
msg = yieldLink;
}
//Get correct function to call
Manager* man = getActiveManager();
asIScriptFunction* func = getSerializableWrite(man->engine);
//Call the function
if(obj && func) {
Call cl = man->call(func);
cl.setObject(obj);
cl.push(&msg->msg);
cl.call();
msg->msg.finalize();
}
msg->written = true;
yieldLink = msg;
}
static void yieldObject(Object* obj) {
if(yieldLink == 0) {
throwException("No yield context active.");
return;
}
YieldedMessage* msg;
//Make a new message or reuse
if(yieldLink->written) {
msg = new YieldedMessage();
yieldLink->next = msg;
}
else {
msg = yieldLink;
}
//Call the function
if(obj) {
writeObject(msg->msg, obj);
msg->msg.finalize();
}
msg->written = true;
yieldLink = msg;
}
static net::Message* getYieldMessage() {
if(yieldLink == 0) {
throwException("No yield context active.");
return 0;
}
YieldedMessage* msg;
//Make a new message or reuse
if(yieldLink->written) {
msg = new YieldedMessage();
yieldLink->next = msg;
}
else {
msg = yieldLink;
}
yieldLink = msg;
return &yieldLink->msg;
}
static void finalizeYield() {
if(yieldLink == 0) {
throwException("No yield context active.");
return;
}
if(yieldLink->written) {
throwException("Yielded message already finalized.");
return;
}
yieldLink->msg.finalize();
yieldLink->written = true;
}
static YieldedMessage* waitForMessage(YieldedMessage** ctx) {
//Wait for the message to become ready
YieldedMessage* msg = *ctx;
if(msg == 0 || msg == &END_CONTEXT)
return 0;
while(
//Wait for the next message
(msg->read && msg->next == 0) ||
//Wait for the currrent message to become ready
(!msg->written && msg->next != &END_CONTEXT)
) {
threads::sleep(1);
}
//Skip to the next message if available
if(msg->read) {
*ctx = msg->next;
msg = msg->next;
}
else if(!msg->written && msg->next == &END_CONTEXT)
return 0;
if(msg == &END_CONTEXT)
return 0;
msg->read = true;
return msg;
}
static void readMessage(YieldedMessage* msg, asIScriptObject* obj) {
//Get correct function to call
Manager* man = getActiveManager();
asIScriptFunction* func = getSerializableRead(man->engine);
//Call the function
if(obj && func) {
Call cl = man->call(func);
cl.setObject(obj);
cl.push(&msg->msg);
cl.call();
}
}
static void continueMessage(YieldedMessage* msg, YieldedMessage** ctx) {
//Move to the next message if available
if(msg->next)
*ctx = msg->next;
//Delete old message
delete msg;
}
static bool receive(YieldedMessage** ctx, asIScriptObject* obj) {
YieldedMessage* msg = waitForMessage(ctx);
if(!msg) {
if(obj)
obj->Release();
return false;
}
readMessage(msg, obj);
continueMessage(msg, ctx);
if(obj)
obj->Release();
return true;
}
static bool receiveObject(YieldedMessage** ctx, Object** obj) {
YieldedMessage* msg = waitForMessage(ctx);
if(!msg)
return false;
readObjectScr(msg->msg, obj);
continueMessage(msg, ctx);
return true;
}
static void receiveArray(CScriptArray* arr, YieldedMessage** ctx) {
if(!ctx || !*ctx)
return;
//Get correct interface to use
Manager* man = getActiveManager();
asITypeInfo* srtype = getSerializable(man->engine);
//Check that our subtype is compatible
int subTypeId = arr->GetElementTypeId();
auto* subType = man->engine->GetTypeInfoById(subTypeId);
if(!subType || !subType->Implements(srtype)) {
scripts::throwException("Cannot sync into array of non-serializable objects.");
return;
}
if(subTypeId & asTYPEID_OBJHANDLE) {
scripts::throwException("Cannot sync into array of handles.");
return;
}
//Do all the reading
unsigned index = 0;
for(;;) {
YieldedMessage* msg = waitForMessage(ctx);
if(!msg)
break;
if(arr->GetSize() <= index)
arr->Resize(index+1);
readMessage(msg, (asIScriptObject*)arr->At(index));
continueMessage(msg, ctx);
++index;
}
if(arr->GetSize() > index)
arr->Resize(index);
}
YieldedMessage* StartYieldContext() {
if(yieldLink != 0) {
error("Error: cannot nest yield contexts.");
return 0;
}
yieldLink = new YieldedMessage();
return yieldLink;
}
void EndYieldContext() {
if(yieldLink)
yieldLink->next = &END_CONTEXT;
yieldLink = 0;
}
static void createMessage(void* mem) {
new(mem) net::Message();
}
static void copyMessage(void* mem, net::Message& other) {
new(mem) net::Message(other);
}
static void destroyMessage(net::Message& msg) {
msg.~Message();
}
static net::Message& assignMessage(net::Message& msg, net::Message& other) {
msg = other;
return msg;
}
static bool emptyMessage(net::Message& msg) {
return msg.size() == 0;
}
static net::msize_t reserveInt(net::Message& msg) {
try {
return msg.reserve<int>();
}
catch(...) {
scripts::throwException("Message needs to be aligned before reserving space.");
return 0;
}
}
static void fillInt(net::Message& msg, net::msize_t pos, int value) {
msg.fill<int>(pos, value);
}
static void msgDumpRead(net::Message& msg) {
net::Message::Position pos = msg.getReadPosition();
print("Read position: %d bytes, %d bits", pos.bytes, pos.bits);
}
static void msgDumpWrite(net::Message& msg) {
net::Message::Position pos = msg.getWritePosition();
print("Write position: %d bytes, %d bits", pos.bytes, pos.bits);
}
static void writeSerial(net::Message& msg, asIScriptObject* serial) {
if(serial == 0) {
scripts::throwException("Null Serializable@ when writing");
return;
}
scripts::Manager& manager = scripts::Manager::fromEngine(serial->GetEngine());
Call cl = manager.call(getSerializableWrite(manager.engine));
cl.setObject(serial);
cl.push(&msg);
cl.call();
serial->Release();
}
static void readSerial(net::Message& msg, asIScriptObject* serial) {
if(serial == 0) {
scripts::throwException("Null Serializable@ when reading");
return;
}
scripts::Manager& manager = scripts::Manager::fromEngine(serial->GetEngine());
Call cl = manager.call(getSerializableRead(manager.engine));
cl.setObject(serial);
cl.push(&msg);
cl.call();
serial->Release();
}
static net::Message& readElevation(net::Message& msg, ElevationMap& emap) {
msg >> emap.generated;
msg >> emap.gridStart;
msg >> emap.gridSize;
msg >> emap.gridInterval;
msg >> emap.gridResolution;
unsigned size = emap.gridResolution.x * emap.gridResolution.y;
emap.grid = (float*)calloc(size, sizeof(float));
try {
msg.readBits((uint8_t*)emap.grid, size * sizeof(float) * 8);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return msg;
}
static net::Message& writeElevation(net::Message& msg, ElevationMap& emap) {
msg << emap.generated;
msg << emap.gridStart;
msg << emap.gridSize;
msg << emap.gridInterval;
msg << emap.gridResolution;
unsigned size = emap.gridResolution.x * emap.gridResolution.y;
msg.writeBits((uint8_t*)emap.grid, size * sizeof(float) * 8);
return msg;
}
net::Message& readObjectScr(net::Message& msg, Object** obj) {
Object* newObj = 0;
try {
newObj = readObject(msg);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
return msg;
}
if(Object* prev = *obj)
prev->drop();
*obj = newObj;
return msg;
}
Object* readObject_e(net::Message& msg) {
Object* obj = nullptr;
readObjectScr(msg, &obj);
return obj;
}
net::Message& writeObjectScr(net::Message& msg, Object* obj) {
writeObject(msg, obj);
return msg;
}
net::Message& readEmpire(net::Message& msg, Empire** emp) {
unsigned char empID;
try {
msg >> empID;
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
return msg;
}
*emp = Empire::getEmpireByID(empID);
return msg;
}
net::Message& writeEmpire(net::Message& msg, Empire* emp) {
if(emp)
msg << emp->id;
else
msg << INVALID_EMPIRE;
return msg;
}
static net::Message& readDesign(net::Message& msg, const Design** dsg) {
unsigned char empID;
if(auto* prev = *dsg) {
prev->drop();
prev = 0;
}
try {
msg >> empID;
if(empID == 0) {
*dsg = 0;
return msg;
}
Empire* emp = Empire::getEmpireByID(empID);
if(!emp) {
*dsg = 0;
return msg;
}
unsigned dsgID = msg.readSmall();
*dsg = emp->getDesign(dsgID, true);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
return msg;
}
return msg;
}
static net::Message& writeDesign(net::Message& msg, const Design** dsg) {
if(*dsg) {
if(!(*dsg)->owner) {
unsigned char empID = 0;
msg << empID;
scripts::throwException("Cannot transmit design without owner.");
return msg;
}
msg << (*dsg)->owner->id;
msg.writeSmall((*dsg)->id);
}
else {
unsigned char empID = 0;
msg << empID;
}
return msg;
}
static bool wrapReadBit(net::Message& msg) {
try {
return msg.readBit();
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return false;
}
static void wrapReadAlign(net::Message& msg) {
try {
msg.readAlign();
}
catch(net::MessageReadError) {
scripts::throwException("Error aligning message: end of message.");
}
}
template<class T>
static net::Message& wrapRead(net::Message& msg, T& value) {
try {
msg >> value;
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return msg;
}
template<class T>
static T wrapExplicitRead(net::Message& msg) {
T value = T();
wrapRead<T>(msg, value);
return value;
}
static unsigned readLimited(net::Message& msg, unsigned limit) {
try {
return msg.readLimited(limit);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return 0;
}
static void writeLimited(net::Message& msg, unsigned value, unsigned limit) {
msg.writeLimited(value, limit);
}
static unsigned readSmall(net::Message& msg) {
try {
return msg.readSmall();
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return 0;
}
static void writeSmall(net::Message& msg, unsigned value) {
msg.writeSmall(value);
}
static int readSignedSmall(net::Message& msg) {
try {
return msg.readSignedSmall();
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return 0;
}
static void writeSignedSmall(net::Message& msg, int value) {
msg.writeSignedSmall(value);
}
static double readFixed(net::Message& msg, double minimum, double maximum, unsigned bits) {
try {
return msg.readFixed(minimum, maximum, bits);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return 0.0;
}
static void writeFixed(net::Message& msg, double value, double minimum, double maximum, unsigned bits) {
msg.writeFixed(value, minimum, maximum, bits);
}
static unsigned readBitValue(net::Message& msg, unsigned bits) {
try {
return msg.readBitValue(bits);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return 0;
}
static void writeBitValue(net::Message& msg, unsigned value, unsigned bits) {
msg.writeBitValue(value, bits);
}
static void writeSmallVec3(net::Message& msg, const vec3d& v) {
msg.writeSmallVec3(v.x, v.y, v.z);
}
static vec3d readSmallVec3(net::Message& msg) {
vec3d v;
msg.readSmallVec3(v.x, v.y, v.z);
return v;
}
static void writeVec3(net::Message& msg, const vec3d& v) {
msg.writeMedVec3(v.x, v.y, v.z);
}
static vec3d readVec3(net::Message& msg) {
vec3d v;
msg.readMedVec3(v.x, v.y, v.z);
return v;
}
static void writeDir(net::Message& msg, const vec3d& v) {
msg.writeDirection(v.x, v.y, v.z);
}
static vec3d readDir(net::Message& msg) {
vec3d v;
msg.readDirection(v.x, v.y, v.z);
return v;
}
static void writeRot(net::Message& msg, const quaterniond& v) {
msg.writeRotation(v.xyz.x, v.xyz.y, v.xyz.z, v.w);
}
static quaterniond readRot(net::Message& msg) {
quaterniond v;
msg.readRotation(v.xyz.x, v.xyz.y, v.xyz.z, v.w);
return v;
}
static void writeLinkMapAll(LinkMap& map, net::Message& msg) {
auto numPos = msg.reserve<int>();
int count = 0;
map.iterateAll([&msg,&count](int64_t key, int64_t value) {
msg << key;
msg << value;
count += 1;
});
msg.fill<int>(numPos, count);
}
static void readLinkMap(LinkMap& map, net::Message& msg) {
int count = 0;;
msg >> count;
for(int i = 0; i < count; ++i)
{
int64_t key, value;
msg >> key >> value;
map.set(key, value);
}
}
static void writeLinkMapDelta(LinkMap& map, net::Message& msg) {
auto numPos = msg.reserve<int>();
int count = 0;
map.handleDirty([&msg,&count](int64_t key, int64_t value) -> bool {
msg << key;
msg << value;
count += 1;
return true;
});
msg.fill<int>(numPos, count);
}
void RegisterEarlyNetworkBinds(asIScriptEngine* engine) {
engine->RegisterObjectType("DataList", 0, asOBJ_REF | asOBJ_NOCOUNT);
engine->RegisterObjectMethod("array<T>", "void syncFrom(DataList@& list)", asFUNCTION(receiveArray), asCALL_CDECL_OBJFIRST);
}
void RegisterNetworkBinds(bool server) {
InterfaceBind inf("Serializable", false);
//Message manipulation
ClassBind msg("Message", asOBJ_VALUE | asOBJ_APP_CLASS_CDA, sizeof(net::Message));
msg.addConstructor("void f()", asFUNCTION(createMessage));
msg.addConstructor("void f(const Message&)", asFUNCTION(copyMessage));
msg.addDestructor("void f()", asFUNCTION(destroyMessage));
msg.addExternMethod("Message& opAssign(const Message&in)", asFUNCTION(assignMessage));
msg.addExternMethod("bool get_empty() const", asFUNCTION(emptyMessage));
msg.addMethod("void dump() const", asMETHOD(net::Message, dump));
msg.addMethod("uint get_size() const", asMETHOD(net::Message, size));
msg.addMethod("bool get_error() const", asMETHOD(net::Message, hasError))
doc("Returns true if there was an error during a read operation.", "");
msg.addExternMethod("uint reserve()", asFUNCTION(reserveInt));
msg.addExternMethod("void fill(uint, int)", asFUNCTION(fillInt));
msg.addMethod("void writeBit(bool)", asMETHOD(net::Message, writeBit));
msg.addMethod("void write1()", asMETHOD(net::Message, write1));
msg.addMethod("void write0()", asMETHOD(net::Message, write0));
msg.addExternMethod("bool readBit()", asFUNCTION(wrapReadBit));
msg.addMethod("void writeAlign()", asMETHOD(net::Message, writeAlign));
msg.addExternMethod("void readAlign()", asFUNCTION(wrapReadAlign));
msg.addExternMethod("void dumpWritePosition()", asFUNCTION(msgDumpWrite));
msg.addExternMethod("void dumpReadPosition()", asFUNCTION(msgDumpRead));
msg.addExternMethod("void writeSmall(uint value)", asFUNCTION(writeSmall));
msg.addExternMethod("uint readSmall()", asFUNCTION(readSmall));
msg.addExternMethod("void writeSignedSmall(int value)", asFUNCTION(writeSignedSmall));
msg.addExternMethod("int readSignedSmall()", asFUNCTION(readSignedSmall));
msg.addExternMethod("void writeLimited(uint value, uint limit)", asFUNCTION(writeLimited));
msg.addExternMethod("uint readLimited(uint limit)", asFUNCTION(readLimited));
msg.addExternMethod("void writeBitValue(uint value, uint bits)", asFUNCTION(writeBitValue));
msg.addExternMethod("uint readBitValue(uint bits)", asFUNCTION(readBitValue));
msg.addExternMethod("void writeFixed(double value, double minimum = 0.0, double maximum = 1.0, uint bits = 16)", asFUNCTION(writeFixed))
doc("Writes a double that is guaranteed to fall into the rage [minimum,maximum) using a specified number of bits.", "", "", "", "", "");
msg.addExternMethod("double readFixed(double minimum = 0.0, double maximum = 1.0, uint bits = 16)", asFUNCTION(readFixed));
msg.addExternMethod("void writeSmallVec3(const vec3d& v)", asFUNCTION(writeSmallVec3))
doc("Writes a compressed vec3d into a very small space, sacrificing minimal quality.", "");
msg.addExternMethod("vec3d readSmallVec3()", asFUNCTION(readSmallVec3));
msg.addExternMethod("void writeMedVec3(const vec3d& v)", asFUNCTION(writeVec3))
doc("Writes a compressed vec3d into a smaller space, sacrificing very little quality.", "");
msg.addExternMethod("vec3d readMedVec3()", asFUNCTION(readVec3));
msg.addExternMethod("void writeDirection(const vec3d& v)", asFUNCTION(writeDir))
doc("Writes a unit vector in a much smaller size.", "");
msg.addExternMethod("vec3d readDirection()", asFUNCTION(readDir));
msg.addExternMethod("void writeRotation(const quaterniond& v)", asFUNCTION(writeRot))
doc("Writes a rotation (unit quaternion) in a much smaller size.", "");
msg.addExternMethod("quaterniond readRotation()", asFUNCTION(readRot));
msg.addExternMethod("Message& opShl(Serializable@)", asFUNCTION(writeSerial));
msg.addExternMethod("Message& opShr(Serializable@)", asFUNCTION(readSerial));
msg.addMethod("Message& opShl(bool)",
asMETHODPR(net::Message, operator<<, (bool), net::Message&));
msg.addExternMethod("Message& opShr(bool&)",
asFUNCTION(wrapRead<bool>));
#define BIND_TYPE_SHONLY(stype, rtype)\
msg.addMethod("Message& opShl(" #stype "&)",\
asMETHODPR(net::Message, operator<<, (const rtype&), net::Message&));\
\
msg.addExternMethod("Message& opShr(" #stype "&)",\
asFUNCTION(wrapRead<rtype>));
#define BIND_TYPE(stype, rtype)\
BIND_TYPE_SHONLY(stype, rtype)\
\
msg.addExternMethod(#stype " read_" #stype "()",\
asFUNCTION(wrapExplicitRead<rtype>));
BIND_TYPE(uint8, unsigned char);
BIND_TYPE(uint16, unsigned short);
BIND_TYPE(int8, char);
BIND_TYPE(int16, short);
BIND_TYPE(int64, int64_t);
BIND_TYPE(int, int);
BIND_TYPE(uint, unsigned);
BIND_TYPE(float, float);
BIND_TYPE(double, double);
BIND_TYPE(string, std::string);
BIND_TYPE(Color, Color);
BIND_TYPE(vec3d, vec3d);
BIND_TYPE(vec3f, vec3f);
BIND_TYPE(vec3i, vec3i);
BIND_TYPE(vec2d, vec2d);
BIND_TYPE(vec2u, vec2u);
BIND_TYPE(vec2i, vec2i);
BIND_TYPE(vec2f, vec2f);
BIND_TYPE(quaterniond, quaterniond);
BIND_TYPE_SHONLY(locked_int, int);
BIND_TYPE_SHONLY(locked_double, double);
msg.addExternMethod("Message& opShl(ElevationMap&)", asFUNCTION(writeElevation));
msg.addExternMethod("Message& opShr(ElevationMap&)", asFUNCTION(readElevation));
msg.addExternMethod("Message& opShl(Object@+)", asFUNCTION(writeObjectScr));
msg.addExternMethod("Message& opShr(Object@&)", asFUNCTION(readObjectScr));
msg.addExternMethod("Object@ readObject()", asFUNCTION(readObject_e));
msg.addExternMethod("Message& opShl(Empire@)", asFUNCTION(writeEmpire));
msg.addExternMethod("Message& opShr(Empire@&)", asFUNCTION(readEmpire));
msg.addExternMethod("Message& opShl(const Design@&)", asFUNCTION(writeDesign));
msg.addExternMethod("Message& opShr(const Design@&)", asFUNCTION(readDesign));
//Serializable interface
inf.addMethod("void read(Message& msg)");
inf.addMethod("void write(Message& msg)");
//DataList and yield stuff
bind("bool receive(DataList@& list, Serializable@ obj)", asFUNCTION(receive));
bind("bool receive(DataList@& list, Object@& obj)", asFUNCTION(receiveObject));
bind("void yield(const Serializable& obj)", asFUNCTION(yield));
bind("void yield(const Object& obj)", asFUNCTION(yieldObject));
bind("Message& startYield()", asFUNCTION(getYieldMessage));
bind("void finishYield()", asFUNCTION(finalizeYield));
bind("void setNickname(const string& nick)", asFUNCTION(setNickname));
//Keep function pointers
asITypeInfo* type = getEngine()->GetTypeInfoByName("Serializable");
if(type) {
asIScriptEngine* engine = getEngine();
engine->SetUserData(type, EDID_SerializableType);
engine->SetUserData(type->GetMethodByDecl("void write(Message& msg)"), EDID_SerializableWrite);
engine->SetUserData(type->GetMethodByDecl("void read(Message& msg)"), EDID_SerializableRead);
}
else {
error("ERROR: Problem registering Serializable type interface.");
}
{
ClassBind linkMap("LinkMap");
linkMap.addExternMethod("void writeAll(Message& msg)", asFUNCTION(writeLinkMapAll));
linkMap.addExternMethod("void writeDirty(Message& msg)", asFUNCTION(writeLinkMapDelta));
linkMap.addExternMethod("void readAll(Message& msg)", asFUNCTION(readLinkMap));
linkMap.addExternMethod("void readDirty(Message& msg)", asFUNCTION(readLinkMap));
}
}
};
File diff suppressed because it is too large Load Diff
+364
View File
@@ -0,0 +1,364 @@
#include "binds.h"
#include "constants.h"
#include "main/references.h"
#include "main/initialization.h"
#include "scripts/manager.h"
#include "files.h"
#include <string>
#include "main/logging.h"
namespace scripts {
struct ScriptedKeybind : profile::Keybind {
scripts::Manager* manager;
asIScriptFunction* func;
ScriptedKeybind(scripts::Manager* man, asIScriptFunction* Func)
: manager(man), func(Func) {
}
void call(bool pressed) {
if(func) {
Call cl = manager->call(func);
cl.push(pressed);
cl.call();
}
}
};
static void cb_keybind(profile::BindGroup* grp, const std::string& name, const std::string& str) {
std::string decl("void ");
decl += str;
decl += "(bool)";
asIScriptContext* ctx = asGetActiveContext();
asIScriptFunction* func = ctx->GetFunction();
const char* module = func->GetModuleName();
Manager* manager = scripts::getActiveManager();
asIScriptFunction* scrFunc = manager->getFunction(module, decl.c_str());
if(scrFunc) {
grp->addBind(name, new ScriptedKeybind(manager, scrFunc));
}
else {
error("Could not locate function '%s::%s'", module, decl.c_str());
}
}
static void cb_keybind_id(profile::BindGroup* grp, unsigned id, const std::string& str) {
std::string decl("void ");
decl += str;
decl += "(bool)";
asIScriptContext* ctx = asGetActiveContext();
asIScriptFunction* func = ctx->GetFunction();
const char* module = func->GetModuleName();
Manager* manager = scripts::getActiveManager();
asIScriptFunction* scrFunc = manager->getFunction(module, decl.c_str());
if(scrFunc) {
grp->addBind(id, new ScriptedKeybind(manager, scrFunc));
}
else {
error("Could not locate function '%s::%s'", module, decl.c_str());
}
}
static void cb_save_kb() {
std::string bindFile = path_join(devices.mods.getProfile("settings"), "keybinds.txt");
devices.keybinds.saveBinds(bindFile);
}
static void cb_set_kb(profile::BindGroup* grp, int key, unsigned id) {
grp->setBind(key, id);
}
static void cb_clear_kb(profile::BindGroup* grp, int key) {
grp->clearBind(key);
}
static void cb_clear_kb_bind(profile::BindGroup* grp, unsigned id) {
grp->clearBinds(id);
}
static void cb_setdef_kb(profile::BindGroup* grp, unsigned id) {
grp->setDefaultBinds(id);
}
static void cb_setdef_kb_all(profile::BindGroup* grp) {
grp->setDefaultBinds();
}
static unsigned cb_defcount(profile::BindGroup* grp, unsigned id) {
return grp->getDefaultCount(id);
}
static int cb_defkey(profile::BindGroup* grp, unsigned id, unsigned index) {
return grp->getDefaultKey(id, index);
}
static unsigned cb_curcount(profile::BindGroup* grp, unsigned id) {
return grp->getBindCount(id);
}
static int cb_curkey(profile::BindGroup* grp, unsigned id, unsigned index) {
return grp->getBindKey(id, index);
}
static unsigned cb_count(profile::BindGroup* grp) {
return grp->descriptors.size();
}
static std::string cb_name(profile::BindGroup* grp, unsigned id) {
if(id >= grp->descriptors.size()) {
scripts::throwException("Keybind id out of bounds.");
return "";
}
return grp->descriptors[id].name;
}
static unsigned cb_group_cnt() {
return devices.keybinds.groups.size();
}
static int cb_getbind(profile::BindGroup* grp, int key) {
return grp->getBindID(key);
}
static profile::BindGroup* cb_group(unsigned i) {
if(i >= devices.keybinds.groups.size()) {
scripts::throwException("Group index out of bounds.");
return 0;
}
return devices.keybinds.groups[i];
}
static int mod_key(int key) {
int mod_key = key;
if(mod_key >= 'A' && mod_key <= 'Z')
mod_key += 'a'-'A';
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;
return mod_key;
}
#define stAccess(type) \
void setSetting_ ## type (const std::string& name, type value) { \
auto* v = devices.settings.mod.getSetting(name);\
if(!v)\
v = devices.settings.engine.getSetting(name);\
if(!v) {\
scripts::throwException("Invalid setting name.");\
return;\
}\
*v = value;\
}\
type getSetting_ ## type (const std::string& name) { \
auto* v = devices.settings.mod.getSetting(name);\
if(!v)\
v = devices.settings.engine.getSetting(name);\
if(!v) {\
scripts::throwException("Invalid setting name.");\
return 0;\
}\
return *v;\
}\
stAccess(int);
stAccess(double);
stAccess(bool);
#define stMinMax(type, v_min, v_max) \
type getSetting_min_ ##type (const std::string& name) { \
auto* v = devices.settings.mod.getSetting(name);\
if(!v)\
v = devices.settings.engine.getSetting(name);\
if(!v) {\
scripts::throwException("Invalid setting name.");\
return 0;\
}\
return v->v_min;\
}\
type getSetting_max_ ##type (const std::string& name) { \
auto* v = devices.settings.mod.getSetting(name);\
if(!v)\
v = devices.settings.engine.getSetting(name);\
if(!v) {\
scripts::throwException("Invalid setting name.");\
return 0;\
}\
return v->v_max;\
}\
stMinMax(int, num_min, num_max);
stMinMax(double, flt_min, flt_max);
void setSetting_str(const std::string& name, const std::string& value) {
auto* v = devices.settings.mod.getSetting(name);
if(!v)
v = devices.settings.engine.getSetting(name);
if(!v) {
scripts::throwException("Invalid setting name.");
return;
}
v->setString(value);
}
const std::string errstr("ERR");
const std::string& getSetting_str(const std::string& name) {
auto* v = devices.settings.mod.getSetting(name);
if(!v)
v = devices.settings.engine.getSetting(name);
if(!v) {
scripts::throwException("Invalid setting name.");
return errstr;
}
std::string* str = v->getString();
if(!str) {
scripts::throwException("Setting is not a string setting.");
return errstr;
}
return *str;
}
void applySettings() {
if(game_running && devices.scripts.client) {
scripts::MultiCall cl = devices.scripts.client->call(SC_settings_changed);
cl.call();
}
else if(devices.scripts.menu) {
scripts::MultiCall cl = devices.scripts.menu->call(SC_settings_changed);
cl.call();
}
}
void saveSettings() {
devices.settings.engine.saveSettings(path_join(getProfileRoot(), "settings.txt"));
devices.settings.mod.saveSettings(path_join(devices.mods.getProfile("settings"), "settings.txt"));
}
void RegisterProfileBinds() {
//Bind dynamic enum for registered keybinds
EnumBind kb("Keybind");
kb["KB_NONE"] = -1;
foreach(it, devices.keybinds.groups) {
profile::BindGroup* grp = *it;
for(unsigned i = 0; i < grp->descriptors.size(); ++i) {
auto& bind = grp->descriptors[i];
kb[std::string("KB_")+bind.name] = i;
}
}
kb["MASK_CTRL"] = profile::Mod_Ctrl;
kb["MASK_ALT"] = profile::Mod_Alt;
kb["MASK_SHIFT"] = profile::Mod_Shift;
ClassBind grp("KeybindGroup", asOBJ_REF | asOBJ_NOCOUNT, 0);
grp.addMember("string name", offsetof(profile::BindGroup, name));
grp.addExternMethod("void addBind(const string &in, const string &in)", asFUNCTION(cb_keybind));
grp.addExternMethod("void addBind(Keybind, const string &in)", asFUNCTION(cb_keybind_id));
grp.addExternMethod("uint getBindCount()", asFUNCTION(cb_count));
grp.addExternMethod("string getBindName(uint index)", asFUNCTION(cb_name));
grp.addExternMethod("void setBind(int key, Keybind bind)", asFUNCTION(cb_set_kb));
grp.addExternMethod("void clearBin(int key)", asFUNCTION(cb_clear_kb));
grp.addExternMethod("void clearBinds(Keybind bind)", asFUNCTION(cb_clear_kb_bind));
grp.addExternMethod("void setDefaultBinds(Keybind bind)", asFUNCTION(cb_setdef_kb));
grp.addExternMethod("void setDefaultBinds()", asFUNCTION(cb_setdef_kb_all));
grp.addExternMethod("uint getDefaultKeyCount(Keybind bind)", asFUNCTION(cb_defcount));
grp.addExternMethod("int getDefaultKey(Keybind bind, uint index)", asFUNCTION(cb_defkey));
grp.addExternMethod("uint getCurrentKeyCount(Keybind bind)", asFUNCTION(cb_curcount));
grp.addExternMethod("int getCurrentKey(Keybind bind, uint index)", asFUNCTION(cb_curkey));
grp.addExternMethod("Keybind getBind(int key)", asFUNCTION(cb_getbind));
bind("int getKey(string key)", asFUNCTION(profile::getKey));
bind("string getKeyName(int key)", asFUNCTION(profile::getKeyName));
bind("int getKeyFromDisplayName(string key)", asFUNCTION(profile::getKeyFromDisplayName));
bind("string getKeyDisplayName(int key)", asFUNCTION(profile::getKeyDisplayName));
bind("void saveKeybinds()", asFUNCTION(cb_save_kb));
bind("KeybindGroup@ get_keybindGroup(uint index)", asFUNCTION(cb_group));
bind("uint get_keybindGroupCount()", asFUNCTION(cb_group_cnt));
bind("int modifyKey(int key)", asFUNCTION(mod_key));
{
Namespace ns("keybinds");
foreach(it, devices.keybinds.groups)
bindGlobal(format("::KeybindGroup $1", (*it)->name).c_str(), *it);
}
//Settings
bind("void setSettingInt(const string &in, int)", asFUNCTION(setSetting_int));
bind("void setSettingBool(const string &in, bool)", asFUNCTION(setSetting_bool));
bind("void setSettingDouble(const string &in, double)", asFUNCTION(setSetting_double));
bind("void setSettingStr(const string &in, const string &in)", asFUNCTION(setSetting_str));
bind("int getSettingInt(const string &in)", asFUNCTION(getSetting_int));
bind("bool getSettingBool(const string &in)", asFUNCTION(getSetting_bool));
bind("double getSettingDouble(const string &in)", asFUNCTION(getSetting_double));
bind("const string& getSettingStr(const string &in)", asFUNCTION(getSetting_str));
bind("int getSettingMaxInt(const string &in)", asFUNCTION(getSetting_max_int));
bind("int getSettingMinInt(const string &in)", asFUNCTION(getSetting_min_int));
bind("double getSettingMaxDouble(const string &in)", asFUNCTION(getSetting_max_double));
bind("double getSettingMinDouble(const string &in)", asFUNCTION(getSetting_min_double));
bind("void saveSettings()", asFUNCTION(saveSettings));
bind("void applySettings()", asFUNCTION(applySettings));
auto bindSetting = [](NamedGeneric& set) {
switch(set.type) {
case GT_Bool:
bindGlobal((std::string("bool ")+set.name).c_str(), &set.check);
break;
case GT_Integer:
bindGlobal((std::string("int ")+set.name).c_str(), &set.num);
break;
case GT_Double:
bindGlobal((std::string("double ")+set.name).c_str(), &set.flt);
break;
case GT_String:
bindGlobal((std::string("string ")+set.name).c_str(), set.str);
break;
case GT_Enum: {
std::string ename = set.name;
ename += "_OPTION";
EnumBind bnd(ename.c_str());
for(unsigned i = 0; i < set.values->size(); ++i)
bnd[(*set.values)[i]] = i;
bindGlobal(("::"+ename+" "+set.name).c_str(), &set.value);
} break;
}
};
{ Namespace ns("settings");
//Bind settings to global variables
foreach(it, devices.settings.engine.settings)
bindSetting((*it->second));
foreach(it, devices.settings.mod.settings)
bindSetting((*it->second));
}
}
};
File diff suppressed because it is too large Load Diff
+429
View File
@@ -0,0 +1,429 @@
#include "binds.h"
#include "scriptarray.h"
#include "main/references.h"
#include "main/logging.h"
#include "design/design.h"
#include "network/message.h"
#include "util/elevation_map.h"
#include "util/save_file.h"
#include "threads.h"
#include "color.h"
#include "empire.h"
namespace scripts {
static inline asITypeInfo* getSavable(asIScriptEngine* engine) {
return (asITypeInfo*)engine->GetUserData(EDID_SavableType);
}
static inline asIScriptFunction* getSavableWrite(asIScriptEngine* engine) {
return (asIScriptFunction*)engine->GetUserData(EDID_SavableWrite);
}
static inline asIScriptFunction* getSavableRead(asIScriptEngine* engine) {
return (asIScriptFunction*)engine->GetUserData(EDID_SavableRead);
}
static void createSaveMessage(void* mem) {
scripts::throwException("Don't instantiate savefiles manually.");
}
static void destroyMessage(SaveMessage& msg) {
msg.~SaveMessage();
}
static net::msize_t reserveInt(SaveMessage& msg) {
try {
return msg.reserve<int>();
}
catch(...) {
scripts::throwException("Message needs to be aligned before reserving space.");
return 0;
}
}
static void fillInt(SaveMessage& msg, net::msize_t pos, int value) {
msg.fill<int>(pos, value);
}
static void msgDumpRead(SaveMessage& msg) {
SaveMessage::Position pos = msg.getReadPosition();
print("Read position: %d bytes, %d bits", pos.bytes, pos.bits);
}
static void msgDumpWrite(SaveMessage& msg) {
SaveMessage::Position pos = msg.getWritePosition();
print("Write position: %d bytes, %d bits", pos.bytes, pos.bits);
}
static void writeSerial(SaveMessage& msg, asIScriptObject* serial) {
if(serial == 0) {
scripts::throwException("Null Savable@ when writing");
return;
}
scripts::Manager& manager = scripts::Manager::fromEngine(serial->GetEngine());
Call cl = manager.call(getSavableWrite(manager.engine));
cl.setObject(serial);
cl.push(&msg);
cl.call();
serial->Release();
}
static void readSerial(SaveMessage& msg, asIScriptObject* serial) {
if(serial == 0) {
scripts::throwException("Null Savable@ when reading");
return;
}
scripts::Manager& manager = scripts::Manager::fromEngine(serial->GetEngine());
Call cl = manager.call(getSavableRead(manager.engine));
cl.setObject(serial);
cl.push(&msg);
cl.call();
serial->Release();
}
static SaveMessage& readElevation(SaveMessage& msg, ElevationMap& emap) {
msg >> emap.generated;
msg >> emap.gridStart;
msg >> emap.gridSize;
msg >> emap.gridInterval;
msg >> emap.gridResolution;
unsigned size = emap.gridResolution.x * emap.gridResolution.y;
emap.grid = (float*)calloc(size, sizeof(float));
try {
msg.readBits((uint8_t*)emap.grid, size * sizeof(float) * 8);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return msg;
}
static SaveMessage& writeElevation(SaveMessage& msg, ElevationMap& emap) {
msg << emap.generated;
msg << emap.gridStart;
msg << emap.gridSize;
msg << emap.gridInterval;
msg << emap.gridResolution;
unsigned size = emap.gridResolution.x * emap.gridResolution.y;
msg.writeBits((uint8_t*)emap.grid, size * sizeof(float) * 8);
return msg;
}
SaveMessage& loadObject(SaveMessage& msg, Object** obj) {
int id;
try {
msg >> id;
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
return msg;
}
if(Object* prev = *obj)
prev->drop();
*obj = getObjectByID(id, true);
return msg;
}
Object* readObject_e(SaveMessage& msg) {
Object* obj = nullptr;
loadObject(msg, &obj);
return obj;
}
SaveMessage& saveObject(SaveMessage& msg, Object* obj) {
if(obj) {
msg << obj->id;
}
else {
int id = 0;
msg << id;
}
return msg;
}
SaveMessage& readEmpire(SaveMessage& msg, Empire** emp) {
unsigned char empID;
try {
msg >> empID;
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
return msg;
}
*emp = Empire::getEmpireByID(empID);
return msg;
}
SaveMessage& writeEmpire(SaveMessage& msg, Empire* emp) {
if(emp)
msg << emp->id;
else
msg << INVALID_EMPIRE;
return msg;
}
static SaveMessage& readDesign(SaveMessage& msg, const Design** dsg) {
unsigned char empID;
if(auto* prev = *dsg) {
prev->drop();
prev = 0;
}
try {
msg >> empID;
if(empID == 0) {
*dsg = 0;
return msg;
}
Empire* emp = Empire::getEmpireByID(empID);
if(!emp) {
*dsg = 0;
return msg;
}
int dsgID;
msg >> dsgID;
*dsg = emp->getDesign(dsgID, true);
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
return msg;
}
return msg;
}
static SaveMessage& writeDesign(SaveMessage& msg, const Design** dsg) {
if(*dsg) {
if(!(*dsg)->owner) {
unsigned char empID = 0;
msg << empID;
scripts::throwException("Cannot transmit design without owner.");
return msg;
}
msg << (*dsg)->owner->id;
msg << (*dsg)->id;
}
else {
unsigned char empID = 0;
msg << empID;
}
return msg;
}
static bool wrapReadBit(SaveMessage& msg) {
try {
return msg.readBit();
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return false;
}
static void wrapReadAlign(SaveMessage& msg) {
try {
msg.readAlign();
}
catch(net::MessageReadError) {
scripts::throwException("Error aligning message: end of message.");
}
}
template<class T>
static SaveMessage& wrapRead(SaveMessage& msg, T& value) {
try {
msg >> value;
}
catch(net::MessageReadError) {
scripts::throwException("Error reading from message: end of message.");
}
return msg;
}
static unsigned getScriptVersion(SaveMessage& msg) {
return msg.file.scriptVersion;
}
static void setScriptVersion(SaveMessage& msg, unsigned version) {
msg.file.scriptVersion = version;
}
static unsigned getStartVersion(SaveMessage& msg) {
return msg.file.startVersion;
}
static void setStartVersion(SaveMessage& msg, unsigned version) {
msg.file.startVersion = version;
}
static void addIdentifier(SaveMessage& msg, unsigned type, int id, const std::string& ident) {
if(!msg.file.addIdentifier(type, id, ident))
scripts::throwException(format("Duplicate identifier: '$1'.", ident.c_str()).c_str());
}
static int readIdentifier(SaveMessage& msg, unsigned type) {
int id;
msg >> id;
return msg.file.getIdentifier(type, id);
}
static int getIdentifier(SaveMessage& msg, unsigned type, unsigned id) {
return msg.file.getIdentifier(type, id);
}
static void writeIdentifier(SaveMessage& msg, unsigned type, int id) {
msg << id;
}
static unsigned getIdentifierCount(SaveMessage& msg, unsigned type) {
return msg.file.getIdentifierCount(type);
}
static unsigned getPrevIdentifierCount(SaveMessage& msg, unsigned type) {
return msg.file.getPrevIdentifierCount(type);
}
static int saveCmp(SaveMessage& msg, unsigned version) {
if(version > msg.file.scriptVersion)
return -1;
else if(version < msg.file.scriptVersion)
return 1;
else
return 0;
}
void RegisterSaveFileBinds(bool server, bool decl) {
if(decl) {
ClassBind msg("SaveFile", asOBJ_VALUE | asOBJ_APP_CLASS_CDA, sizeof(SaveMessage));
return;
}
InterfaceBind inf("Savable");
//Message manipulation
ClassBind msg("SaveFile");
msg.addConstructor("void f()", asFUNCTION(createSaveMessage));
msg.addDestructor("void f()", asFUNCTION(destroyMessage));
msg.addMethod("void dump() const", asMETHOD(SaveMessage, dump));
msg.addMethod("uint get_size() const", asMETHOD(SaveMessage, size));
msg.addExternMethod("void set_scriptVersion(uint version)", asFUNCTION(setScriptVersion));
msg.addExternMethod("uint get_scriptVersion()", asFUNCTION(getScriptVersion));
msg.addExternMethod("void set_startVersion(uint version)", asFUNCTION(setStartVersion));
msg.addExternMethod("uint get_startVersion()", asFUNCTION(getStartVersion));
msg.addExternMethod("void addIdentifier(uint type, int id, const string&in ident)", asFUNCTION(addIdentifier));
msg.addExternMethod("uint readIdentifier(uint type)", asFUNCTION(readIdentifier));
msg.addExternMethod("int getIdentifier(uint ident, int id)", asFUNCTION(getIdentifier));
msg.addExternMethod("void writeIdentifier(uint type, int id)", asFUNCTION(writeIdentifier));
msg.addExternMethod("uint getIdentifierCount(uint type)", asFUNCTION(getIdentifierCount));
msg.addExternMethod("uint getPrevIdentifierCount(uint type)", asFUNCTION(getPrevIdentifierCount));
msg.addExternMethod("int opCmp(uint version)", asFUNCTION(saveCmp));
msg.addExternMethod("uint reserve()", asFUNCTION(reserveInt));
msg.addExternMethod("void fill(uint, int)", asFUNCTION(fillInt));
msg.addMethod("void writeBit(bool)", asMETHOD(SaveMessage, writeBit));
msg.addMethod("void write1()", asMETHOD(SaveMessage, write1));
msg.addMethod("void write0()", asMETHOD(SaveMessage, write0));
msg.addExternMethod("bool readBit()", asFUNCTION(wrapReadBit));
msg.addExternMethod("void readAlign()", asFUNCTION(wrapReadAlign));
msg.addMethod("void writeAlign()", asMETHOD(SaveMessage, writeAlign));
msg.addExternMethod("void dumpReadPosition()", asFUNCTION(msgDumpRead));
msg.addExternMethod("void dumpWritePosition()", asFUNCTION(msgDumpWrite));
msg.addExternMethod("Message& opShl(Savable@)", asFUNCTION(writeSerial));
msg.addExternMethod("Message& opShr(Savable@)", asFUNCTION(readSerial));
msg.addMethod("SaveFile& opShl(bool)",
asMETHODPR(net::Message, operator<<, (bool), net::Message&));
msg.addExternMethod("SaveFile& opShr(bool&)",
asFUNCTION(wrapRead<bool>));
#define BIND_TYPE(stype, rtype)\
msg.addMethod("SaveFile& opShl(" #stype "&)",\
asMETHODPR(net::Message, operator<<, (const rtype&), net::Message&));\
\
msg.addExternMethod("SaveFile& opShr(" #stype "&)",\
asFUNCTION(wrapRead<rtype>));
BIND_TYPE(uint8, unsigned char);
BIND_TYPE(uint16, unsigned short);
BIND_TYPE(int8, char);
BIND_TYPE(int16, short);
BIND_TYPE(int64, int64_t);
BIND_TYPE(int, int);
BIND_TYPE(uint, unsigned);
BIND_TYPE(float, float);
BIND_TYPE(double, double);
BIND_TYPE(string, std::string);
BIND_TYPE(Color, Color);
BIND_TYPE(vec3d, vec3d);
BIND_TYPE(vec3f, vec3f);
BIND_TYPE(vec3i, vec3i);
BIND_TYPE(vec2d, vec2d);
BIND_TYPE(vec2u, vec2u);
BIND_TYPE(vec2i, vec2i);
BIND_TYPE(vec2f, vec2f);
BIND_TYPE(quaterniond, quaterniond);
BIND_TYPE(locked_int, int);
BIND_TYPE(locked_double, double);
msg.addExternMethod("SaveFile& opShl(ElevationMap&)", asFUNCTION(writeElevation));
msg.addExternMethod("SaveFile& opShr(ElevationMap&)", asFUNCTION(readElevation));
msg.addExternMethod("SaveFile& opShl(Object@+)", asFUNCTION(saveObject));
msg.addExternMethod("SaveFile& opShr(Object@&)", asFUNCTION(loadObject));
msg.addExternMethod("Object@ readObject()", asFUNCTION(readObject_e));
msg.addExternMethod("SaveFile& opShl(Empire@)", asFUNCTION(writeEmpire));
msg.addExternMethod("SaveFile& opShr(Empire@&)", asFUNCTION(readEmpire));
msg.addExternMethod("SaveFile& opShl(const Design@&)", asFUNCTION(writeDesign));
msg.addExternMethod("SaveFile& opShr(const Design@&)", asFUNCTION(readDesign));
//Savable interface
inf.addMethod("void load(SaveFile& file)");
inf.addMethod("void save(SaveFile& file)");
EnumBind glob("SaveGlobals");
glob["SAVE_IDENTIFIER_START"] = SI_SCRIPT_START;
//Keep function pointers
asITypeInfo* type = getEngine()->GetTypeInfoByName("Savable");
if(type) {
asIScriptEngine* engine = getEngine();
engine->SetUserData(type, EDID_SavableType);
engine->SetUserData(type->GetMethodByDecl("void save(SaveFile& file)"), EDID_SavableWrite);
engine->SetUserData(type->GetMethodByDecl("void load(SaveFile& file)"), EDID_SavableRead);
}
else {
error("ERROR: Problem registering Savable type interface.");
}
}
};
+151
View File
@@ -0,0 +1,151 @@
#include "binds.h"
#include "main/references.h"
#include "main/initialization.h"
#include "main/logging.h"
#include "ISoundSource.h"
#include "ISoundDevice.h"
#include "ISound.h"
#include "SLoadError.h"
#include <string>
namespace scripts {
static bool soundEnabled() {
return use_sound;
}
static float soundVolume() {
return devices.sound->getVolume();
}
static void setSoundVolume(float vol) {
devices.sound->setVolume(vol);
}
static void setSoundScale(float scale) {
devices.sound->setRolloffFactor(1.f / scale);
}
static const resource::Sound* getSound(const std::string& name) {
return devices.library.getSound(name);
}
static audio::ISound* playTrack(const std::string& file, bool loop, bool paused) {
try {
auto* source = devices.sound->loadStreamingSound(devices.mods.resolve(file));
if(source) {
audio::ISound* sound = devices.sound->play2D(source, loop, paused, true);
if(sound)
sound->grab();
source->drop();
return sound;
}
else {
return 0;
}
}
catch(audio::SLoadError) {
return 0;
}
}
static audio::ISound* playTrack3D(const std::string& file, const vec3d& pos, bool loop, bool paused) {
try {
auto* source = devices.sound->loadStreamingSound(devices.mods.resolve(file));
if(source) {
audio::ISound* sound = devices.sound->play3D(source, snd_vec(pos), loop, paused, true);
if(sound)
sound->grab();
source->drop();
return sound;
}
else {
return 0;
}
}
catch(audio::SLoadError) {
return 0;
}
}
static void setSoundPos(audio::ISound* snd, const vec3d& pos) {
snd->setPosition((float)pos.x, (float)pos.y, (float)pos.z);
}
static void setSoundVel(audio::ISound* snd, const vec3d& vel) {
snd->setVelocity((float)vel.x, (float)vel.y, (float)vel.z);
}
static float defaultVolume(resource::Sound* snd) {
if(!snd->loaded)
return 0.f;
return snd->source->getDefaultVolume();
}
void RegisterSoundBinds() {
//Sound progress
ClassBind sp("Sound", asOBJ_REF, 0);
sp.addBehaviour(asBEHAVE_ADDREF, "void f()", asMETHOD(audio::ISound, grab));
sp.addBehaviour(asBEHAVE_RELEASE, "void f()", asMETHOD(audio::ISound, drop));
sp.addMethod("bool get_stopped()", asMETHOD(audio::ISound, isStopped));
sp.addMethod("bool get_playing()", asMETHOD(audio::ISound, isPlaying));
sp.addMethod("bool get_loop()", asMETHOD(audio::ISound, isLooped));
sp.addMethod("void set_paused(bool)", asMETHOD(audio::ISound, setIsPaused));
sp.addMethod("void set_loop(bool)", asMETHOD(audio::ISound, setLooped));
sp.addMethod("void set_volume(float factor)", asMETHOD(audio::ISound, setVolume));
sp.addMethod("void set_pitch(float factor)", asMETHOD(audio::ISound, setPitch));
sp.addMethod("int get_playLength()", asMETHOD(audio::ISound, getPlayLength));
sp.addMethod("int get_playPosition()", asMETHOD(audio::ISound, getPlayPosition));
sp.addMethod("void set_playPosition(int pos)", asMETHOD(audio::ISound, setPlayPosition));
sp.addMethod("void seek(int pos)", asMETHOD(audio::ISound, seek));
sp.addMethod("void set_minDistance(float dist)", asMETHOD(audio::ISound, setMinDistance));
sp.addMethod("void set_maxDistance(float dist)", asMETHOD(audio::ISound, setMaxDistance));
sp.addMethod("void pause()", asMETHOD(audio::ISound, pause));
sp.addMethod("void resume()", asMETHOD(audio::ISound, resume));
sp.addMethod("void stop()", asMETHOD(audio::ISound, stop));
sp.addExternMethod("void set_position(const vec3d &in pos)", asFUNCTION(setSoundPos));
sp.addExternMethod("void set_velocity(const vec3d &in pos)", asFUNCTION(setSoundVel));
sp.addMethod("void set_rolloff(float)", asMETHOD(audio::ISound, setRolloff))
doc("Sets how much distance attentuates the volume of 3D sounds.", "Rolloff factor. Higher values increase attenuation. Default for 3D sounds is 1.");
//Sound source
ClassBind ss("SoundSource", asOBJ_REF | asOBJ_NOCOUNT, 0);
ss.addMember("bool loaded", offsetof(resource::Sound, loaded));
ss.addMethod("Sound@ play(bool loop = false, bool pause = false, bool priority = false) const", asMETHOD(resource::Sound,play2D));
ss.addMethod("Sound@ play(const vec3d &in position, bool loop = false, bool pause = false, bool priority = false) const", asMETHOD(resource::Sound,play3D));
ss.addExternMethod("float get_defaultVolume() const", asFUNCTION(defaultVolume));
//Global access
bind("const SoundSource@ getSound(const string &in id)", asFUNCTION(getSound))
doc("Returns the sound source associated with the id.", "id as specified in the data file.", "Can be null if not present.");
bind("Sound@ playTrack(const string &in filename, bool loop = false, bool pause = false)", asFUNCTION(playTrack))
doc("Plays a streaming sound source from the beginning.", "Filename to play.", "Whether to loop the sound. Defaults to false.", "Whether to start the sound paused. Defaults to false.", "Handle to the sound.");
bind("Sound@ playTrack(const string &in filename, const vec3d &in pos, bool loop = false, bool pause = false)", asFUNCTION(playTrack3D))
doc("Plays a streaming sound source from the beginning.", "Filename to play.", "Position to play from.", "Whether to loop the sound. Defaults to false.", "Whether to start the sound paused. Defaults to false.", "Handle to the sound.");
bind("bool get_soundEnabled()", asFUNCTION(soundEnabled))
doc("Returns true if the sound system is present (even at 0 volume).", "");
bind("float get_soundVolume()", asFUNCTION(soundVolume))
doc("Returns the current volume of the sound system.", "Multiplier to output volume between 0 and 1.");
bind("void set_soundVolume(float)", asFUNCTION(setSoundVolume))
doc("Sets the sound system's volume.", "Multiplier to output volume between 0 and 1.");
bind("void set_soundScale(float)", asFUNCTION(setSoundScale))
doc("Sets the overall scale of the sound system's world.", "Value of the scale.");
bindGlobal("bool soundDisableSFX", &audio::disableSFX)
doc("Whether to disable creation of SFX sounds.");
//Bind sound globals
{
Namespace ns("sound");
foreach(it, devices.library.sounds)
bindGlobal(format("const ::SoundSource $1", it->first).c_str(), it->second);
}
}
};
+668
View File
@@ -0,0 +1,668 @@
#include "scripts/binds.h"
#include "threads.h"
#include "util/refcount.h"
#include "context_cache.h"
#include "main/references.h"
#include "../source/as_scriptengine.h"
#include "../source/as_scriptobject.h"
extern void initNewThread();
extern void cleanupThread();
namespace scripts {
using namespace threads;
class refAtomic : public atomic_int {
mutable atomic_int refcount;
public:
refAtomic(int val) : atomic_int(val), refcount(1) {}
void grab() const {
++refcount;
}
void drop() const {
if(--refcount == 0)
delete this;
}
refAtomic& operator=(int value) {
atomic_int::operator=(value);
return *this;
}
int postInc() {
return (*this)++;
}
int postDec() {
return (*this)--;
}
};
refAtomic* makeAtomic(int val) {
return new refAtomic(val);
}
class refMutex : public Mutex {
mutable atomic_int refcount;
public:
refMutex() : refcount(1) {}
void grab() const {
++refcount;
}
void drop() const {
if(--refcount == 0)
delete this;
}
};
refMutex* makeMutex() {
return new refMutex();
}
refMutex* lockMutex(refMutex* mutex) {
mutex->lock();
return mutex;
}
void unlockMutex(refMutex* mutex) {
mutex->release();
}
class refRWMutex : public ReadWriteMutex {
mutable atomic_int refcount;
public:
refRWMutex() : refcount(1) {}
void grab() const {
++refcount;
}
void drop() const {
if(--refcount == 0)
delete this;
}
};
refRWMutex* makeRWMutex() {
return new refRWMutex();
}
refRWMutex* lockReadMutex(refRWMutex* mutex) {
mutex->readLock();
return mutex;
}
refRWMutex* lockWriteMutex(refRWMutex* mutex) {
mutex->writeLock();
return mutex;
}
void unlockRWMutex(refRWMutex* mutex) {
mutex->release();
}
class refSignal : public Signal {
mutable atomic_int refcount;
public:
refSignal() : refcount(1) {}
void grab() const {
++refcount;
}
void drop() const {
if(--refcount == 0)
delete this;
}
};
refSignal* makeSignal() {
return new refSignal();
}
bool validateThreadLocal(asITypeInfo* ot, bool& dontGarbageCollect) {
int typeId = ot->GetSubTypeId();
if(typeId == asTYPEID_VOID)
return false;
if((typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE)) {
asITypeInfo *st = ot->GetEngine()->GetTypeInfoById(typeId);
int flags = st->GetFlags();
if(flags & asOBJ_REF) {
if( !(flags & asOBJ_GC) )
dontGarbageCollect = true;
for(unsigned i = 0; i < st->GetFactoryCount(); ++i) {
asIScriptFunction *func = st->GetFactoryByIndex(i);
if(func->GetParamCount() == 0)
return true;
}
return false;
}
else if((flags & asOBJ_VALUE) && !(flags & asOBJ_POD))
{
dontGarbageCollect = true;
for(unsigned i = 0; i < st->GetBehaviourCount(); ++i) {
asEBehaviours beh;
asIScriptFunction *func = st->GetBehaviourByIndex(i, &beh);
if(beh != asBEHAVE_CONSTRUCT) continue;
if(func->GetParamCount() == 0)
return true;
}
return false;
}
}
if( !(typeId & asTYPEID_OBJHANDLE) )
dontGarbageCollect = true;
return true;
}
static void* zeroPtr = 0;
class refThreadLocal : public AtomicRefCounted {
threadlocalPointer<void*> ptr;
asITypeInfo* objType;
public:
refThreadLocal(asITypeInfo* ot)
: objType(ot) {
}
~refThreadLocal() {
int typeId = objType->GetSubTypeId();
void* p = ptr.get();
if(!p)
return;
//Free the object
if(typeId & asTYPEID_OBJHANDLE) {
//Release the object we have a handle to
void* cur = *(void**)p;
if(cur)
objType->GetEngine()->ReleaseScriptObject(cur, objType->GetSubType());
//Free the actual pointer
free(p);
}
else if(typeId & ~asTYPEID_MASK_SEQNBR) {
//Free the object being pointed to
objType->GetEngine()->ReleaseScriptObject(p, objType->GetSubType());
}
else {
//Free the actual pointer
free(p);
}
}
void* get() {
void* p = ptr.get();
int typeId = objType->GetSubTypeId();
if(typeId & asTYPEID_OBJHANDLE) {
//Return the pointer to the pointer,
//which is the handle
if(p)
return p;
else
return set(&zeroPtr);
}
else {
if(p)
return p;
else
return set(zeroPtr);
}
}
void* set(void* v) {
void* p = ptr.get();
int typeId = objType->GetSubTypeId();
if(typeId & asTYPEID_OBJHANDLE) {
if(!p) {
p = malloc(sizeof(void*));
*(void**)p = 0;
ptr.set(p);
}
void* cur = *(void**)p;
void* next = *(void**)v;
*(void**)p = next;
if(next)
objType->GetEngine()->AddRefScriptObject(next, objType->GetSubType());
if(cur)
objType->GetEngine()->ReleaseScriptObject(cur, objType->GetSubType());
return p;
}
else if(typeId & ~asTYPEID_MASK_SEQNBR) {
if(!p) {
p = (void*)objType->GetEngine()->CreateScriptObject(objType);
ptr.set(p);
}
if(v)
objType->GetEngine()->AssignScriptObject(p, v, objType);
return p;
}
else if(typeId == asTYPEID_BOOL ||
typeId == asTYPEID_INT8 ||
typeId == asTYPEID_UINT8) {
if(!p) {
p = malloc(sizeof(char));
*(char*)p = 0;
ptr.set(p);
}
if(v)
*(char*)p = *(char*)v;
return p;
}
else if(typeId == asTYPEID_INT16 ||
typeId == asTYPEID_UINT16) {
if(!p) {
p = malloc(sizeof(short));
*(short*)p = 0;
ptr.set(p);
}
if(v)
*(short*)p = *(short*)v;
return p;
}
else if(typeId == asTYPEID_INT32 ||
typeId == asTYPEID_UINT32 ||
typeId == asTYPEID_FLOAT ||
typeId > asTYPEID_DOUBLE) /*enums*/ {
if(!p) {
p = malloc(sizeof(int));
*(int*)p = 0;
ptr.set(p);
}
if(v)
*(int*)p = *(int*)v;
return p;
}
else if(typeId == asTYPEID_INT64 ||
typeId == asTYPEID_UINT64 ||
typeId == asTYPEID_DOUBLE) {
if(!p) {
p = malloc(sizeof(double));
*(double*)p = 0;
ptr.set(p);
}
if(v)
*(double*)p = *(double*)v;
return p;
}
return 0;
}
};
refThreadLocal* createThreadLocal(asITypeInfo* ot) {
return new refThreadLocal(ot);
}
threads::threadreturn threadcall runScriptThread(void* arg);
bool IsHandleCompatibleWithObject(asCScriptEngine* engine, void *obj, int objTypeId, int handleTypeId)
{
// if equal, then it is obvious they are compatible
if( objTypeId == handleTypeId )
return true;
// Get the actual data types from the type ids
asCDataType objDt = engine->GetDataTypeFromTypeId(objTypeId);
asCDataType hdlDt = engine->GetDataTypeFromTypeId(handleTypeId);
// A handle to const cannot be passed to a handle that is not referencing a const object
if( objDt.IsHandleToConst() && !hdlDt.IsHandleToConst() )
return false;
if( objDt.GetTypeInfo() == hdlDt.GetTypeInfo() )
{
// The object type is equal
return true;
}
else if( objDt.IsScriptObject() && obj )
{
// Get the true type from the object instance
asITypeInfo *objType = ((asCScriptObject*)obj)->GetObjectType();
// Check if the object implements the interface, or derives from the base class
// This will also return true, if the requested handle type is an exact match for the object type
if( objType->Implements(hdlDt.GetTypeInfo()) ||
objType->DerivesFrom(hdlDt.GetTypeInfo()) )
return true;
}
return false;
}
struct ScriptThread {
//Two reference counters, one for the resource, one for the scripts
// When the script counter reaches 0, one system reference is lost, and the thread is shut down at the next opportunity
// When the system counter reaches 0, this struct is deleted
threads::atomic_int systemRefs, scriptRefs;
bool gcFlag;
threads::atomic_int runThread;
bool wasError;
Manager* manager;
asIScriptEngine* engine;
asIScriptFunction* function;
threads::Mutex objLock;
void* object;
asITypeInfo* objType;
bool setScriptObject(void* pointer, int typeID) {
threads::Lock lock(objLock);
if(typeID == 0 || pointer == 0 || *(void**)pointer == 0) {
if(object != 0) {
engine->ReleaseScriptObject(object, objType);
object = 0;
}
return true;
}
else if(typeID & asTYPEID_OBJHANDLE) {
object = *(void**)pointer;
objType = engine->GetTypeInfoById(typeID);
engine->AddRefScriptObject(object, objType);
return true;
}
return false;
}
bool getScriptObject(void* pointer, int typeID) {
if(pointer == 0)
return false;
threads::Lock lock(objLock);
if(typeID & asTYPEID_OBJHANDLE) {
if(typeID & asTYPEID_MASK_OBJECT && IsHandleCompatibleWithObject((asCScriptEngine*)engine, object, objType->GetTypeId(), typeID)) {
engine->AddRefScriptObject(object, objType);
*(void**)pointer = object;
return true;
}
}
return false;
}
void stop() {
runThread.compare_exchange_strong(1, 2);
}
bool start(const std::string& func) {
asIScriptContext* ctx = asGetActiveContext();
if(engine != ctx->GetEngine())
return false;
Manager* localMan = &Manager::fromEngine(engine);
if(!localMan)
return false;
asIScriptFunction* localFunc = localMan->getFunction(func, "(double, ScriptThread&)", "double");
if(localFunc) {
if(runThread.compare_exchange_strong(2, 0) == 0) {
function = localFunc;
manager = localMan;
++systemRefs;
wasError = false;
threads::createThread(runScriptThread, this);
return true;
}
}
return false;
}
bool isRunning() const {
return runThread != 0;
}
ScriptThread() : runThread(false), systemRefs(1), scriptRefs(1), gcFlag(false), engine(asGetActiveContext()->GetEngine()), function(0), object(0), wasError(false) {
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("ScriptThread"));
}
ScriptThread(const std::string& func) : runThread(false), systemRefs(1), scriptRefs(1), gcFlag(false), engine(asGetActiveContext()->GetEngine()), function(0), object(0), wasError(false) {
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("ScriptThread"));
start(func);
}
ScriptThread(const std::string& func, void* pointer, int typeID) : runThread(false), systemRefs(1), scriptRefs(1), gcFlag(false), engine(asGetActiveContext()->GetEngine()), function(0), object(0), wasError(false) {
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("ScriptThread"));
setScriptObject(pointer, typeID);
start(func);
}
static ScriptThread* create() {
return new ScriptThread();
}
static ScriptThread* create_f(const std::string& func) {
return new ScriptThread(func);
}
static ScriptThread* create_fo(const std::string& func, void* pointer, int typeID) {
return new ScriptThread(func, pointer, typeID);
}
void systemDrop() {
if(--systemRefs == 0)
delete this;
}
void scriptGrab() {
gcFlag = false;
++scriptRefs;
}
void scriptDrop() {
gcFlag = false;
if(--scriptRefs == 0) {
runThread = 0;
systemDrop();
}
}
void setGCFlag() {
gcFlag = true;
}
bool getGCFlag() const {
return gcFlag;
}
int getRefcount() const {
return scriptRefs;
}
void enumRefs(asIScriptEngine* eng) {
objLock.lock();
if(object)
eng->GCEnumCallback(object);
objLock.release();
}
void releaseRefs(asIScriptEngine* eng) {
setScriptObject(0,0);
}
};
threads::threadreturn threadcall runScriptThread(void* arg) {
ScriptThread& thread = *(ScriptThread*)arg;
initNewThread();
//Server uses game time, client and menu use frame time
bool useGameTime = thread.engine != devices.scripts.client->engine && thread.engine != devices.scripts.menu->engine;
double curTime = useGameTime ? devices.driver->getGameTime() : devices.driver->getFrameTime();
double lastTime = curTime;
double nextTime = lastTime;
thread.manager->scriptThreadCreate();
while(thread.runThread == 2) {
if(!thread.manager->scriptThreadStart())
break;
curTime = useGameTime ? devices.driver->getGameTime() : devices.driver->getFrameTime();
if(curTime >= nextTime) {
double delta = curTime - lastTime;
lastTime = curTime;
#ifdef TRACE_GC_LOCK
thread.manager->markGCImpossible();
#endif
auto call = thread.manager->call(thread.function);
call.push(delta);
call.push(arg);
double delay = 1.0;
if(!call.call(delay)) {
#ifdef TRACE_GC_LOCK
thread.manager->markGCPossible();
#endif
thread.wasError = true;
thread.runThread = 0;
thread.manager->scriptThreadEnd();
break;
}
#ifdef TRACE_GC_LOCK
thread.manager->markGCPossible();
#endif
nextTime = curTime + delay;
}
thread.manager->scriptThreadEnd();
//Sleep until 2ms before the next timer
if(devices.driver->getGameSpeed() < 0.01)
threads::idle();
else {
int sleepTime = (1000.0 * (nextTime - curTime)) - 2.0;
if(sleepTime < 0)
sleepTime = 0;
threads::sleep((unsigned)sleepTime);
}
}
cleanupThread();
thread.manager->scriptThreadDestroy();
thread.systemDrop();
thread.runThread = 0;
return 0;
}
void RegisterThreadingBinds() {
//Register atomic integer
ClassBind aint("atomic_int", asOBJ_REF, 0);
aint.addFactory("atomic_int@ f(int)", asFUNCTION(makeAtomic));
aint.setReferenceFuncs(asMETHOD(refAtomic, grab), asMETHOD(refAtomic, drop));
aint.addMethod("int opPreInc()", asMETHODPR(refAtomic, operator++, (), int));
aint.addMethod("int opPostInc()", asMETHOD(refAtomic, postInc));
aint.addMethod("int opPreDec()", asMETHODPR(refAtomic, operator--, (), int));
aint.addMethod("int opPostDec()", asMETHOD(refAtomic, postDec));
aint.addMethod("atomic_int& opAssign(int)", asMETHODPR(refAtomic, operator=, (int), refAtomic&));
aint.addMethod("void opAddAssign(int)", asMETHODPR(refAtomic, operator+=, (int), int));
aint.addMethod("void opDecAssign(int)", asMETHODPR(refAtomic, operator-=, (int), int));
aint.addMethod("atomic_int& set(int)", asMETHODPR(refAtomic, operator=, (int), refAtomic&));
aint.addMethod("int get()", asMETHODPR(refAtomic, get, () const, int));
aint.addMethod("int get_value()", asMETHODPR(refAtomic, get_basic, (), int));
aint.addMethod("void set_value(int)", asMETHODPR(refAtomic, set_basic, (int), void));
//Register regular mutex and lock
ClassBind mutex("Mutex", asOBJ_REF, 0);
mutex.addFactory("Mutex@ f()", asFUNCTION(makeMutex));
mutex.setReferenceFuncs(asMETHOD(refMutex, grab), asMETHOD(refMutex, drop));
ClassBind lock("Lock", asOBJ_REF | asOBJ_SCOPED, 0);
lock.addFactory("Lock@ f(Mutex&)", asFUNCTION(lockMutex));
lock.addExternBehaviour(asBEHAVE_RELEASE, "void f()", asFUNCTION(unlockMutex));
//Register read-write mutex and lock
ClassBind rw_mutex("ReadWriteMutex", asOBJ_REF, 0);
rw_mutex.addFactory("ReadWriteMutex@ f()", asFUNCTION(makeRWMutex));
rw_mutex.setReferenceFuncs(asMETHOD(refRWMutex, grab), asMETHOD(refRWMutex, drop));
ClassBind readLock("ReadLock", asOBJ_REF | asOBJ_SCOPED, 0);
readLock.addFactory("ReadLock@ f(ReadWriteMutex&)", asFUNCTION(lockReadMutex));
readLock.addExternBehaviour(asBEHAVE_RELEASE, "void f()", asFUNCTION(unlockRWMutex));
ClassBind writeLock("WriteLock", asOBJ_REF | asOBJ_SCOPED, 0);
writeLock.addFactory("WriteLock@ f(ReadWriteMutex&)", asFUNCTION(lockWriteMutex));
writeLock.addExternBehaviour(asBEHAVE_RELEASE, "void f()", asFUNCTION(unlockRWMutex));
//Register signal
ClassBind signal("Signal", asOBJ_REF, 0);
signal.addFactory("Signal@ f()", asFUNCTION(makeSignal));
signal.setReferenceFuncs(asMETHOD(refSignal, grab), asMETHOD(refSignal, drop));
signal.addMethod("void signal(int)", asMETHOD(refSignal, signal));
signal.addMethod("void signalDown()", asMETHOD(refSignal, signalDown));
signal.addMethod("void signalUp()", asMETHOD(refSignal, signalUp));
signal.addMethod("bool check(int)", asMETHOD(refSignal, check));
signal.addMethod("void wait(int)", asMETHOD(refSignal, wait));
signal.addMethod("void waitNot(int)", asMETHOD(refSignal, waitNot));
signal.addMethod("void waitAndSignal(int, int)", asMETHOD(refSignal, waitAndSignal));
//General threading functions
bind("void sleep(uint ms)", asFUNCTION(threads::sleep))
doc("Stops thread processing for some number of milliseconds.",
"Number of milliseconds to sleep (approximately)");
bind("int get_threadID()", asFUNCTION(getThreadID));
//Thread local storage
ClassBind tl("ThreadLocal", asOBJ_REF | asOBJ_TEMPLATE);
tl.addLooseBehaviour(asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in,bool&out)", asFUNCTION(validateThreadLocal));
tl.setReferenceFuncs(asMETHOD(refThreadLocal, grab), asMETHOD(refThreadLocal, drop));
tl.addFactory("ThreadLocal<T>@ f(int&in)", asFUNCTION(createThreadLocal));
tl.addMethod("T& get()", asMETHOD(refThreadLocal, get));
tl.addMethod("T& set(const T&in)", asMETHOD(refThreadLocal, set));
//Thread management
ClassBind thread("ScriptThread", asOBJ_REF | asOBJ_GC);
classdoc(thread, "Creates and controls script threads.");
thread.setReferenceFuncs(asMETHOD(ScriptThread,scriptGrab), asMETHOD(ScriptThread,scriptDrop));
thread.addGarbageCollection(
asMETHOD(ScriptThread,setGCFlag),
asMETHOD(ScriptThread,getGCFlag),
asMETHOD(ScriptThread,getRefcount),
asMETHOD(ScriptThread,enumRefs),
asMETHOD(ScriptThread,releaseRefs) );
thread.addFactory("ScriptThread@ f()", asFUNCTION(ScriptThread::create));
thread.addFactory("ScriptThread@ f(const string &in entry)", asFUNCTION(ScriptThread::create_f))
doc("Starts a thread using the specified function.",
"Entry point of type 'double f(double, ScriptThread&)'. Receives time since last call and a reference to the associated ScripThread. Returns delay till next call.", "");
thread.addFactory("ScriptThread@ f(const string &in entry, ?&in)", asFUNCTION(ScriptThread::create_fo))
doc("Starts a thread using the specified function and script object.",
"Entry point of type 'double f(double, ScriptThread&)'. Receives time since last call and a reference to the associated ScripThread. Returns delay till next call.", "Script object to assign to thread.", "");
thread.addMethod("bool start(const string &in entry)", asMETHOD(ScriptThread,start))
doc("Starts a thread using the specified function.",
"Entry point of type 'double f(double, ScriptThread&)'. Receives time since last call and a reference to the associated ScripThread. Returns delay till next call.", "True if the thread was started");
thread.addMethod("void stop()", asMETHOD(ScriptThread,stop))
doc("Stops the active thread.");
thread.addMethod("bool get_running()", asMETHOD(ScriptThread,isRunning))
doc("", "Whether there is an active thread.");
thread.addMethod("bool setObject(?&)", asMETHOD(ScriptThread,setScriptObject))
doc("Sets the thread's object. Can only receive script object handles.", "", "True if the reference could be taken");
thread.addMethod("bool getObject(?&out)", asMETHOD(ScriptThread,getScriptObject))
doc("Gets the thread's object. Can only receive script object handles.", "", "True if the reference could be stored");
thread.addMember("bool wasError", offsetof(ScriptThread,wasError))
doc("Whether the thread was ended due to an error.");
}
};
+642
View File
@@ -0,0 +1,642 @@
#include "rapidjson/document.h"
#include "scripts/binds.h"
#include "threads.h"
#include "main/references.h"
#include "util/refcount.h"
#include <curl/curl.h>
#include <sstream>
#include "str_util.h"
#include "manager.h"
#include "files.h"
#undef min
extern bool launchPatcher;
namespace scripts {
static const std::string WikiURI("http://wiki.starruler2.com/api.php?format=json&action=query&titles=$1&prop=revisions&rvprop=content");
static const std::string APIURI("http://api.starruler2.com/$1");
static threads::threadreturn threadcall CurlThread(void*);
static const std::string EMPTY_STRING("");
struct WebData : AtomicRefCounted {
CURL* curl;
std::string uri;
std::string result;
volatile bool completed;
std::string errorStr;
std::function<void(WebData&)> onPerform;
std::vector<std::pair<std::string,std::string>> postData;
std::vector<std::pair<std::string,std::string>> headers;
WebData() : completed(true) {
curl = curl_easy_init();
}
~WebData() {
while(!completed)
threads::sleep(10);
curl_easy_cleanup(curl);
}
void request(const std::string& URI, std::function<void(WebData&)> perf = nullptr) {
if(!completed) {
scripts::throwException("Attempting to request WebData result before request completed.");
return;
}
completed = false;
onPerform = perf;
uri = URI;
threads::createThread(CurlThread, this);
}
bool isCompleted() {
return completed;
}
bool wasError() {
return errorStr.size() != 0;
}
void addPost(const std::string& name, const std::string& value) {
if(!completed)
return;
postData.push_back(std::pair<std::string,std::string>(name, value));
}
void addHeader(const std::string& name, const std::string& value) {
if(!completed)
return;
headers.push_back(std::pair<std::string,std::string>(name, value));
}
const std::string& getResult() {
if(!completed) {
scripts::throwException("Attempting to get WebData result before request completed.");
return EMPTY_STRING;
}
return result;
}
const std::string& getError() {
if(!completed) {
scripts::throwException("Attempting to get WebData error before request completed.");
return EMPTY_STRING;
}
return errorStr;
}
};
WebData* makeWebData() {
return new WebData();
}
static size_t curlOutput(void* data, size_t size, size_t nmemb, void* ptr) {
if(!ptr)
return size * nmemb;
WebData& dat = *(WebData*)ptr;
int sz = size * nmemb;
dat.result.append((char*)data, sz);
return sz;
}
static threads::threadreturn threadcall CurlThread(void* ptr) {
WebData& dat = *(WebData*)ptr;
if(dat.result.size() != 0)
dat.result = "";
curl_httppost* formpost = nullptr;
curl_httppost* lastptr = nullptr;
struct curl_slist* headers = nullptr;
if(!dat.postData.empty()) {
for(size_t i = 0, cnt = dat.postData.size(); i < cnt; ++i) {
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, dat.postData[i].first.c_str(),
CURLFORM_COPYCONTENTS, dat.postData[i].second.c_str(),
CURLFORM_END);
}
dat.postData.clear();
curl_easy_setopt(dat.curl, CURLOPT_HTTPPOST, formpost);
}
if(!dat.headers.empty()) {
for(size_t i = 0, cnt = dat.headers.size(); i < cnt; ++i) {
std::string str = dat.headers[i].first+": "+dat.headers[i].second;
headers = curl_slist_append(headers, str.c_str());
}
dat.headers.clear();
curl_easy_setopt(dat.curl, CURLOPT_HTTPHEADER, headers);
}
curl_easy_setopt(dat.curl, CURLOPT_URL, dat.uri.c_str());
curl_easy_setopt(dat.curl, CURLOPT_WRITEFUNCTION, curlOutput);
curl_easy_setopt(dat.curl, CURLOPT_WRITEHEADER, nullptr);
curl_easy_setopt(dat.curl, CURLOPT_WRITEDATA, ptr);
curl_easy_setopt(dat.curl, CURLOPT_FAILONERROR, 1);
CURLcode err = curl_easy_perform(dat.curl);
if(err == CURLE_OK) {
if(dat.errorStr.size() != 0)
dat.errorStr = "";
}
else
dat.errorStr = curl_easy_strerror(err);
if(dat.onPerform)
dat.onPerform(dat);
if(formpost)
curl_formfree(formpost);
if(headers)
curl_slist_free_all(headers);
dat.completed = true;
return 0;
}
threads::Mutex updateLock;
threads::Signal isUpdating;
double updateProgress = 0;
int updateStatus = 0;
bool getIsUpdating() {
return !isUpdating.check(0);
}
struct MD5 {
unsigned bytes[4];
MD5() : bytes() {
}
static unsigned endianFlip(unsigned x) {
return (x << 24) | (x >> 24) | ((x >> 8) & 0xff00) | ((x & 0xff00) << 8);
}
void fromString(const std::string& str) {
for(unsigned i = 0; i < 4; ++i) {
std::stringstream s(str.substr(i*8, 8));
s >> std::hex >> bytes[i];
bytes[i] = endianFlip(bytes[i]);
}
}
std::string toString() const {
std::stringstream s;
s.fill('0');
s << std::hex;
for(unsigned i = 0; i < 4; ++i) {
s.width(8);
s << endianFlip(bytes[i]);
}
return s.str();
}
bool zero() {
return !bytes[0] && !bytes[1] && !bytes[2] && !bytes[3];
}
bool operator==(const MD5& other) const {
return bytes[0] == other.bytes[0] && bytes[1] == other.bytes[1] && bytes[2] == other.bytes[2] && bytes[3] == other.bytes[3];
}
};
unsigned rotLeft(unsigned value, unsigned shifts) {
return (value << shifts) | (value >> (32-shifts));
}
MD5 hash(const std::string& filename) {
//Note: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
const unsigned s[64] = { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };
const unsigned K[64] = { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 };
MD5 md5;
unsigned& a0 = md5.bytes[0];
unsigned& b0 = md5.bytes[1];
unsigned& c0 = md5.bytes[2];
unsigned& d0 = md5.bytes[3];
FILE* file = fopen(filename.c_str(), "rb");
if(!file)
return md5;
a0 = 0x67452301;
b0 = 0xefcdab89;
c0 = 0x98badcfe;
d0 = 0x10325476;
unsigned long long length = 0, pos = 0;
unsigned chunk[16];
int end = 0;
while(end != 2) {
auto bytes = fread(chunk, 1, 16*4, file);
length += bytes;
if(bytes < 16 * 4) {
if(end == 0) {
memset((char*)chunk + bytes, 0, 16*4 - bytes);
*((unsigned char*)chunk + bytes) = 0x80;
end = 1;
if(bytes < 14*4) {
length *= 8;
memcpy(chunk + 14, &length, sizeof(length));
end = 2;
}
}
else {
memset(chunk, 0, sizeof(chunk));
length *= 8;
memcpy(chunk + 14, &length, sizeof(length));
end = 2;
}
}
unsigned A = a0, B = b0, C = c0, D = d0;
for(unsigned i = 0; i < 64; ++i) {
unsigned F, g;
if(i < 16) {
F = (B & C) | (~B & D);
g = i;
}
else if(i < 32) {
F = (D & B) | (~D & C);
g = (i*5 + 1) % 16;
}
else if(i < 48) {
F = B ^ C ^ D;
g = (i * 3 + 5) % 16;
}
else {
F = C ^ (B | ~D);
g = (i * 7) % 16;
}
unsigned newA = D;
D = C;
C = B;
B = B + rotLeft(A + F + K[i] + chunk[g], s[i]);
A = newA;
}
a0 += A;
b0 += B;
c0 += C;
d0 += D;
}
fclose(file);
return md5;
}
static int GameUpdate() {
unsigned totalFiles = 0, downloaded = 0;
const std::string profile = path_join(getProfileRoot(), "patch/");
makeDirectory(profile);
updateProgress = 0;
WebData fileList;
for(unsigned i = 0; i < 3; ++i) {
fileList.request(format(APIURI.c_str(), "updates/latest"));
while(!fileList.completed)
threads::sleep(1);
if(!fileList.wasError())
break;
}
if(fileList.wasError()) {
updateProgress = 100.0;
updateStatus = -1;
isUpdating.signalDown();
return 0;
}
updateProgress = 1.0;
std::unordered_map<std::string,MD5> files;
auto anyHash = hash("Star Ruler 2.exe");
int failures = 0;
std::unordered_map<std::string,std::string> queuedDownloads;
std::unordered_map<std::string,WebData*> activeDownloads;
std::unordered_set<std::string> queuedDeletions;
auto checkDownloads = [&](bool beginDownloads) {
for(auto dl = activeDownloads.begin(), dlend = activeDownloads.end(); dl != dlend; ++dl) {
auto* web = dl->second;
if(web->completed) {
if(web->wasError()) {
failures += 1;
queuedDownloads[dl->first] = web->uri;
delete dl->second;
activeDownloads.erase(dl);
return;
}
else {
auto patchName = profile + dl->first;
if(FILE* file = fopen(patchName.c_str(), "wb")) {
fwrite(dl->second->result.c_str(), 1, dl->second->result.size(), file);
fclose(file);
}
downloaded += 1;
delete dl->second;
activeDownloads.erase(dl);
return;
}
}
}
if(beginDownloads && activeDownloads.size() < 3 && !queuedDownloads.empty()) {
auto f = queuedDownloads.begin();
auto* web = new WebData();
web->request(f->second);
activeDownloads[f->first] = web;
queuedDownloads.erase(f);
}
};
auto queueDownload = [&](const std::string& relpath, MD5 current, MD5 updated) {
if(updated.zero()) {
queuedDeletions.insert(relpath);
return;
}
if(fileExists(profile + relpath)) {
auto md5 = hash(profile + relpath);
if(md5 == updated)
return;
remove((profile + relpath).c_str());
}
{
std::vector<std::string> paths;
path_split(relpath, paths);
std::string path = profile;
for(unsigned i = 0, cnt = paths.size(); i + 1 < cnt; ++i) {
path += paths[i] + "/";
makeDirectory(path);
}
}
totalFiles += 1;
auto uri = format("http://api.starruler2.com/updates/$1/$2", current.toString(), updated.toString());
queuedDownloads[relpath] = uri;
checkDownloads(true);
};
std::stringstream list(fileList.result);
std::string line, key, value;
//Line Format: relative_path<tab>md5_hash
while(!list.eof()) {
std::getline(list, line);
if(!splitKeyValue(line, key, value, "\t"))
continue;
if(value.size() == 32) {
MD5 md5;
md5.fromString(value);
files[key] = md5;
}
}
//Clear out any unnecessary files from the patch folder
std::function<void(const std::string&)> clearFolder;
clearFolder = [&](const std::string& relPath) {
std::vector<std::string> listing;
if(listDirectory(profile + relPath, listing)) {
for(auto f = listing.begin(), fend = listing.end(); f != fend; ++f) {
auto path = path_join(profile, *f);
if(isDirectory(path)) {
clearFolder(relPath + *f + "/");
}
else {
auto fExists = files.find(relPath + *f);
if(fExists == files.end() || fExists->second.zero())
remove(path.c_str());
}
}
}
};
clearFolder("");
unsigned hashedFiles = 0;
updateProgress = 5.0;
//Calculate the hashes of all listed files, and queue the download if they need to be updated
for(auto f = files.begin(), fend = files.end(); f != fend; ++f) {
auto md5 = hash(f->first);
++hashedFiles;
if(md5.zero()) {
if(f->second.zero())
continue;
md5 = anyHash;
}
updateProgress = 5.0 + 45.0 * std::min(1.0, (double)hashedFiles / 2000.0) + 45.0 * (double)downloaded / (double)totalFiles;
if(md5 == f->second)
continue;
queueDownload(f->first, md5, f->second);
}
while((!queuedDownloads.empty() || !activeDownloads.empty()) && failures < 25) {
checkDownloads(true);
if(totalFiles > 0)
updateProgress = 50.0 + 45.0 * (double)downloaded / (double)totalFiles;
threads::sleep(1);
}
while(!activeDownloads.empty()) {
checkDownloads(false);
if(totalFiles > 0)
updateProgress = 50.0 + 45.0 * (double)downloaded / (double)totalFiles;
threads::sleep(1);
}
updateProgress = 95.0;
if(!queuedDownloads.empty()) {
updateStatus = -1;
isUpdating.signalDown();
return 0;
}
//Delete queued files
if(!queuedDeletions.empty()) {
std::ofstream deletions(profile + ".delete.txt", std::ios_base::out);
for(auto f = queuedDeletions.begin(), fend = queuedDeletions.end(); f != fend; ++f)
deletions << *f << std::endl;
deletions.flush();
deletions.close();
}
//Queue patcher execution and shut down the game
launchPatcher = true;
updateProgress = 100.0;
updateStatus = 1;
isUpdating.signalDown();
return 0;
}
void updateGame() {
threads::Lock lock(updateLock);
if(isUpdating.check(0)) {
isUpdating.signalUp();
updateStatus = 0;
threads::async(GameUpdate);
}
}
std::string formatURI(const std::string& uri, std::string param) {
return replaced(format(uri.c_str(),
replaced(param, "&", "&amp;")),
" ", "%20");
}
bool intoMember(WebData& dat, rapidjson::Value*& node, const char* name) {
if(!node->IsObject()) {
dat.errorStr = "Invalid json output. Not an object. "+toString(node->GetType());
return false;
}
rapidjson::Value::Member* mem = node->FindMember(name);
if(!mem) {
dat.errorStr = "Invalid json output. No member "+std::string(name);
return false;
}
node = &mem->value;
return true;
}
bool intoMemberNum(WebData& dat, rapidjson::Value*& node, unsigned ind) {
if(!node->IsObject()) {
dat.errorStr = "Invalid json output. Not an object.";
return false;
}
auto it = node->MemberBegin();
while(ind-- && it != node->MemberEnd())
++it;
if(it == node->MemberEnd()) {
dat.errorStr = "Invalid json output.";
return false;
}
node = &it->value;
return true;
}
bool intoArray(WebData& dat, rapidjson::Value*& node, unsigned ind) {
if(!node->IsArray()) {
dat.errorStr = "Invalid json output. Not an array.";
return false;
}
if(ind >= node->Size()) {
dat.errorStr = "Invalid json output.";
return false;
}
node = &(*node)[ind];
return true;
}
void WikiJSON(WebData& dat) {
if(dat.wasError())
return;
rapidjson::Document doc;
doc.Parse<0>(dat.result.c_str());
rapidjson::Value* cursor = &doc;
if(!intoMember(dat, cursor, "query"))
return;
if(!intoMember(dat, cursor, "pages"))
return;
if(!intoMemberNum(dat, cursor, 0))
return;
if(!intoMember(dat, cursor, "revisions"))
return;
if(!intoArray(dat, cursor, 0))
return;
if(!intoMember(dat, cursor, "*"))
return;
if(!cursor->IsString()) {
dat.errorStr = "Invalid json output.";
return;
}
dat.result = cursor->GetString();
}
static void getWikiPage(const std::string& title, WebData& dat) {
std::string uri = formatURI(WikiURI, title);
dat.request(uri, WikiJSON);
}
static void webAPICall(const std::string& page, WebData& dat) {
std::string uri = format(APIURI.c_str(), replaced(page, " ", "%20"));
auto* token = devices.settings.engine.getSetting("sAPIToken");
if(token)
dat.addHeader("APIToken", token->toString());
dat.request(uri);
}
void RegisterWebBinds() {
ClassBind wd("WebData", asOBJ_REF);
classdoc(wd, "Can be used to do requests to data located on the web.");
wd.addFactory("WebData@ f()", asFUNCTION(makeWebData));
wd.setReferenceFuncs(asMETHOD(WebData, grab), asMETHOD(WebData, drop));
wd.addMethod("bool get_completed()", asMETHOD(WebData, isCompleted))
doc("", "Whether the request that was given has completed yet.");
wd.addMethod("bool get_error()", asMETHOD(WebData, wasError))
doc("", "Whether the request ended in an error.");
wd.addMethod("const string& get_result()", asMETHOD(WebData, getResult))
doc("", "The result data from the request.");
wd.addMethod("const string& get_errorStr()", asMETHOD(WebData, getError))
doc("", "The error that was encountered.");
wd.addMethod("void addPost(const string& name, const string& data)", asMETHOD(WebData, addPost))
doc("Add a post parameter to the request.", "Parameter name.", "Parameter data.");
bind("void getWikiPage(const string&in title, WebData& dat)", asFUNCTION(getWikiPage))
doc("Load the contents of a page from the wiki.", "Wiki page title to load.",
"WebData to use for the loading.");
bind("void webAPICall(const string& page, WebData& dat)", asFUNCTION(webAPICall))
doc("Make a call to the web API to get data.", "API page to call.", "WebData to use for the loading.");
bind("void updateGame()", asFUNCTION(updateGame));
bind("bool get_updating()", asFUNCTION(getIsUpdating));
bindGlobal("int updateStatus", &updateStatus);
bindGlobal("double updateProgress", &updateProgress);
}
};
+108
View File
@@ -0,0 +1,108 @@
#include "binds.h"
#include "script_components.h"
#include "main/references.h"
namespace scripts {
void RegisterServerSideBinds(bool shadow) {
InterfaceBind inf("Serializable");
//Declarations
RegisterObjectBinds(true, true, shadow);
RegisterObjectDefinitions();
RegisterEmpireBinds(true, true);
RegisterDesignBinds(!shadow, true);
RegisterObjectCreation(true);
RegisterSaveFileBinds(true, true);
RegisterRenderBinds(true, false, true);
RegisterDataBinds();
RegisterThreadingBinds();
RegisterInspectionBinds();
RegisterRenderBinds(false, false, true);
RegisterObjectCreation(false);
RegisterDesignBinds(!shadow, false);
RegisterNetworkBinds(true);
RegisterSaveFileBinds(true, false);
RegisterEventBinds(true, shadow, false);
RegisterComponentInterfaces(true);
RegisterEmpireBinds(false, true);
RegisterObjectBinds(false, true, shadow);
RegisterSoundBinds();
RegisterFormulaBinds(true);
RegisterDynamicTypes(true);
RegisterDatafiles();
RegisterScriptHooks();
RegisterJSONBinds();
RegisterProfileBinds();
}
void RegisterClientSideBinds(bool menu) {
InterfaceBind inf("Serializable");
RegisterObjectBinds(true, false, false);
RegisterObjectDefinitions();
RegisterEmpireBinds(true, false);
RegisterDesignBinds(false, true);
RegisterSaveFileBinds(false, true);
RegisterRenderBinds(true, false, false);
RegisterDataBinds();
RegisterThreadingBinds();
RegisterGeneralBinds(false, false);
RegisterProfileBinds();
RegisterGuiBinds();
RegisterParticleSystemBinds();
RegisterRenderBinds(false, false, false);
RegisterInspectionBinds();
RegisterDesignBinds(false, false);
RegisterJoystickBinds();
RegisterNetworkBinds(false);
RegisterSaveFileBinds(false, false);
RegisterEventBinds(false, false, menu);
RegisterComponentInterfaces(false);
RegisterObjectBinds(false, false, false);
RegisterEmpireBinds(false, false);
RegisterSoundBinds();
RegisterFormulaBinds(false);
RegisterDynamicTypes(false);
RegisterDatafiles();
RegisterScriptHooks();
RegisterJSONBinds();
RegisterWebBinds();
RegisterIRCBinds();
}
void RegisterServerBinds(asIScriptEngine* engine) {
if(!engine)
return;
setEngine(engine);
RegisterServerSideBinds(false);
RegisterGeneralBinds(true, false);
}
void RegisterShadowBinds(asIScriptEngine* engine) {
if(!engine)
return;
setEngine(engine);
RegisterServerSideBinds(true);
RegisterGeneralBinds(true, true);
}
void RegisterClientBinds(asIScriptEngine* engine) {
if(!engine)
return;
setEngine(engine);
RegisterClientSideBinds(false);
RegisterMenuBinds(true);
}
void RegisterMenuBinds(asIScriptEngine* engine) {
if(!engine)
return;
setEngine(engine);
RegisterClientSideBinds(true);
RegisterMenuBinds(false);
}
};
+128
View File
@@ -0,0 +1,128 @@
#pragma once
#include "angelscript.h"
#include "util/refcount.h"
#include "rect.h"
#include "scripts/script_bind.h"
#include "network/message.h"
#include <map>
struct Player;
struct Image;
class Object;
class SaveMessage;
namespace net { struct Message; };
namespace scripts {
class Manager;
void RegisterServerBinds(asIScriptEngine* engine);
void RegisterClientBinds(asIScriptEngine* engine);
void RegisterMenuBinds(asIScriptEngine* engine);
void RegisterShadowBinds(asIScriptEngine* engine);
void RegisterObjectDefinitions();
void RegisterObjectBinds(bool declarations, bool server, bool shadow);
void RegisterEmpireBinds(bool declarations, bool server);
void RegisterDesignBinds(bool server, bool declarations);
void RegisterGeneralBinds(bool server, bool shadow);
void RegisterGuiBinds();
void RegisterDataBinds();
void RegisterThreadingBinds();
void RegisterRenderBinds(bool decl, bool isMenu, bool server);
void RegisterParticleSystemBinds();
void RegisterProfileBinds();
void RegisterInspectionBinds();
void RegisterMenuBinds(bool ingame);
void RegisterJoystickBinds();
void RegisterObjectCreation(bool declarations);
void RegisterEventBinds(bool server, bool shadow, bool menu);
void RegisterSoundBinds();
void RegisterDynamicTypes(bool server);
void RegisterNetworkBinds(bool server);
void RegisterDatafiles();
void RegisterScriptHooks();
void RegisterFormulaBinds(bool server);
void RegisterSaveFileBinds(bool server, bool decl);
void RegisterJSONBinds();
void RegisterWebBinds();
void RegisterIRCBinds();
void buildEmpAttribIndices();
void SetObjectTypeOffsets();
void LoadScriptHooks(const std::string& filename);
void ClearEvents();
void ReadEvents(const std::string& filename);
void BindEventBinds(bool menu = false);
scripts::Manager* handleEventMessage(Player* from, net::Message& msg, bool interceptMenu = false);
void handleObjectComponentMessage(Player* from, net::Message& msg);
void handleEmpireComponentMessage(Player* from, net::Message& msg);
bool isAccessible(const std::string& filename);
void addNamespaceState();
void setClip(const recti& clip);
void resetClip();
recti* getClip();
struct ScriptImage;
ScriptImage* makeScriptImage(Image* img);
struct YieldedMessage {
net::Message msg;
YieldedMessage* next;
bool written;
bool read;
YieldedMessage()
: next(0), written(false), read(false) {
}
};
SaveMessage& saveObject(SaveMessage& msg, Object* obj);
SaveMessage& loadObject(SaveMessage& msg, Object** obj);
struct ObjectDesc;
Object* makeObject(ObjectDesc& desc);
YieldedMessage* StartYieldContext();
void EndYieldContext();
void scr_loadGame(const std::string& fname);
//Specialized array used to send objects to scripts
struct ObjArray : public AtomicRefCounted {
std::vector<Object*> objs;
ObjArray();
ObjArray(unsigned count);
static ObjArray* create();
static ObjArray* create_n(unsigned count);
void operator=(const ObjArray& other);
bool empty() const;
unsigned size() const;
void reserve(unsigned size);
void resize(unsigned size);
void clear();
Object*& operator[](unsigned index);
Object*& last();
Object* index_value(unsigned index);
Object* last_value();
void erase(unsigned index);
void insert(unsigned index, Object* obj);
void push_back(Object* obj);
void pop_back();
void sortAsc_bound(unsigned lower, unsigned upper);
void sortAsc();
void sortDesc_bound(unsigned lower, unsigned upper);
void sortDesc();
int find(const Object* obj) const;
void remove(const Object* obj);
int findSorted(const Object* obj) const;
void removeSorted(const Object* obj);
};
};
void writeObject(net::Message& msg, Object* obj, bool includeType = true);
Object* readObject(net::Message& msg, bool create = true, int knownType = -1);
+479
View File
@@ -0,0 +1,479 @@
#include "context_cache.h"
#include "main/logging.h"
#include "compat/misc.h"
#include "str_util.h"
#include "files.h"
#include "util/format.h"
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <stack>
#include "main/references.h"
#ifndef MAX_CACHED_CALLS
#define MAX_CACHED_CALLS 512
#endif
#ifndef MAX_AS_CALLSTACK_PRINT_DEPTH
#define MAX_AS_CALLSTACK_PRINT_DEPTH 64
#endif
#ifdef PROFILE_EXECUTION
#include "../main/references.h"
struct ScriptStep {
asIScriptFunction* func;
int line;
mutable unsigned long long count;
bool operator<(const ScriptStep& other) const {
if(count > other.count)
return true;
else if(count == other.count) {
if(func > other.func)
return true;
else if(func == other.func && line > other.line)
return true;
}
return false;
}
bool operator==(const ScriptStep& other) const {
return func == other.func && line == other.line;
}
ScriptStep() : func(0), line(0), count(0) {}
ScriptStep(asIScriptFunction* f, int l) : func(f), line(l), count(0) {}
};
namespace std {
template<>
struct hash<ScriptStep> {
size_t operator()(const ScriptStep& step) const {
return (size_t)step.func ^ (size_t)step.line;
};
};
};
#endif
bool LOG_CHATTY = false;
bool LOG_ERRORLOG = true;
namespace scripts {
struct ContextCache {
asIScriptContext* menuCtx;
asIScriptContext* clientCtx;
asIScriptContext* serverCtx;
ContextCache() : menuCtx(0), clientCtx(0), serverCtx(0) {
}
void reset(bool resetMenu = false) {
if(resetMenu && menuCtx) {
menuCtx->Release();
menuCtx = 0;
}
if(clientCtx) {
clientCtx->Release();
clientCtx = 0;
}
if(serverCtx) {
serverCtx->Release();
serverCtx = 0;
}
}
};
Threaded(ContextCache*) ctxCache = 0;
//Special logging/profiling systems via line callback
#ifdef DO_LINE_CALLBACK
#ifdef PROFILE_EXECUTION
threads::atomic_int profile_step;
threads::Mutex profile_lock;
typedef std::unordered_set<ScriptStep> scriptSteps;
std::unordered_map<asIScriptEngine*,scriptSteps*> scriptSets;
void logScriptProfile(asIScriptEngine* engine) {
profile_lock.lock();
auto iSet = scriptSets.find(engine);
if(iSet != scriptSets.end()) {
//Order steps by number of hits
std::set<ScriptStep> orderedSteps;
for(auto i = iSet->second->begin(), end = iSet->second->end(); i != end; ++i)
orderedSteps.insert(*i);
//We don't need the shared data now
profile_lock.release();
//Print line counts that registered more than 10 counts
for(auto i = orderedSteps.begin(), end = orderedSteps.end(); i != end && i->count > 10; ++i)
print("\t%8lld: %s line %d in %s", i->count, i->func->GetScriptSectionName(), i->line, i->func->GetName());
}
else {
profile_lock.release();
}
}
Threaded(double*) lastStep = 0;
#endif
void TraceExec(asIScriptContext *ctx, void *arg)
{
#ifdef LOG_EXECUTION
std::string msg;
for( asUINT n = 0; n < ctx->GetCallstackSize(); n++ )
msg += " ";
msg += ctx->GetFunction()->GetDeclaration();
if(void* ptr = ctx->GetThisPointer()) {
msg += "@";
msg += toString((unsigned int)ptr);
}
msg += " Line: ";
msg += toString(ctx->GetLineNumber());
print(msg);
#endif
#ifdef PROFILE_EXECUTION
if(++profile_step % PROFILE_EXECUTION == 0) {
double*& pLastTime = lastStep;
if(pLastTime == 0)
pLastTime = new double(0);
double time = devices.driver->getAccurateTime();
if(time - *pLastTime > 0.000001) {
*pLastTime = time;
ScriptStep step(ctx->GetFunction(),ctx->GetLineNumber());
profile_lock.lock();
auto iSet = scriptSets.find(ctx->GetEngine());
scriptSteps* set;
if(iSet != scriptSets.end()) {
set = iSet->second;
}
else {
set = new scriptSteps;
scriptSets[ctx->GetEngine()] = set;
}
auto iStep = set->find(step);
if(iStep != set->end()) {
++iStep->count;
}
else {
step.count = 1;
set->insert(step);
}
profile_lock.release();
}
}
#endif
}
#endif
void logException(asIScriptContext* context) {
error("Script Exception: %s", context->GetExceptionString());
error(getStackTrace(context, true));
}
threads::Mutex errMtx;
std::unordered_set<std::string> printedErrors;
void excCallback(asIScriptContext* context) {
std::string errMsg;
errMsg += "Script Exception: ";
errMsg += context->GetExceptionString();
errMsg += "\n";
errMsg += getStackTrace(context);
if(!LOG_CHATTY) {
threads::Lock lck(errMtx);
if(printedErrors.find(errMsg) != printedErrors.end())
return;
printedErrors.insert(errMsg);
}
//Add to normal log
error(errMsg);
flushLog();
//Add to error log
if(LOG_ERRORLOG)
appendToErrorLog(errMsg);
}
void logException() {
auto* ctx = asGetActiveContext();
if(ctx)
logException(ctx);
else
error("No active script context to log.");
flushLog();
}
std::string getStackVariables(asIScriptContext* context, unsigned frame) {
std::string trace, line;
for(unsigned n = 0, ncnt = context->GetVarCount(frame); n < ncnt; ++n) {
if(!context->IsVarInScope(n, frame))
continue;
line += context->GetVarDeclaration(n, frame, false);
line += " = ";
line += getScriptVariable(
context->GetAddressOfVar(n, frame),
context->GetVarTypeId(n, frame),
true,
context->GetEngine());
line += "; ";
if(line.size() > 100) {
trace += line+"\n";
line.clear();
}
}
if(!line.empty())
trace += line+"\n";
void* ptr = context->GetThisPointer(frame);
if(ptr != nullptr) {
int thisType = context->GetThisTypeId(frame);
if(thisType >= 0) {
trace += context->GetEngine()->GetTypeDeclaration(thisType);
trace += " this = ";
trace += getScriptVariable(ptr, thisType, true, context->GetEngine());
trace += ";\n";
}
}
return trace;
}
std::string getStackTrace(asIScriptContext* context, bool verbose) {
std::string trace;
int cnt = std::min((int)context->GetCallstackSize(), MAX_AS_CALLSTACK_PRINT_DEPTH);
for(int i = 0; i < cnt; ++i) {
asIScriptFunction* func = context->GetFunction(i);
if(func) {
if(i == 0) {
const char* section = func->GetScriptSectionName();
if(section)
trace += std::string(" ") + section + "\n";
else
trace += " <Unknown>\n";
}
int line, column;
line = context->GetLineNumber(i, &column);
trace += format(" $1::$2 | Line $3 | Col $4\n", func->GetModuleName(), func->GetDeclaration(), line, column);
}
else {
trace += " <Unknown function>\n";
}
}
/*if(cnt != 0 && (verbose || getLogLevel() >= LL_Info)) {
trace += "########################################\n";
trace += getStackVariables(context);
trace += "########################################\n";
}*/
return trace;
}
asIScriptContext* makeContext(asIScriptEngine* engine) {
asIScriptContext* ctx = engine->CreateContext();
ctx->SetExceptionCallback(asFUNCTIONPR(excCallback, (asIScriptContext*), void), 0, asCALL_CDECL);
#ifdef DO_LINE_CALLBACK
ctx->SetLineCallback(asFUNCTION(TraceExec), 0, asCALL_CDECL);
#endif
return ctx;
}
std::vector<ContextCache*> caches;
threads::Mutex cacheMtx;
void initContextCache() {
ctxCache = new ContextCache();
{
threads::Lock lock(cacheMtx);
caches.push_back(ctxCache);
}
}
void freeContextCache() {
{
threads::Lock lock(cacheMtx);
auto it = std::find(caches.begin(), caches.end(), ctxCache);
if(it != caches.end())
caches.erase(it);
}
delete ctxCache;
}
void resetContextCache(bool resetMenu) {
//Only call this if no scripts will run for sure
threads::Lock lock(cacheMtx);
foreach(it, caches)
(*it)->reset(resetMenu);
}
asIScriptContext* fetchContext(asIScriptEngine* engine) {
ContextCache* cache = ctxCache;
if(engine == devices.engines.server) {
auto* ctx = cache->serverCtx;
if(!ctx) {
ctx = makeContext(engine);
cache->serverCtx = ctx;
}
return ctx;
}
else if(engine == devices.engines.client) {
auto* ctx = cache->clientCtx;
if(!ctx) {
ctx = makeContext(engine);
cache->clientCtx = ctx;
}
return ctx;
}
else { //menu
auto* ctx = cache->menuCtx;
if(!ctx) {
ctx = makeContext(engine);
cache->menuCtx = ctx;
}
return ctx;
}
}
std::string getScriptVariable(void *value, asUINT typeId, bool expandMembers, asIScriptEngine *engine) {
//From the AS debugger addon
std::stringstream s;
if( typeId == asTYPEID_VOID )
return "<void>";
else if( typeId == asTYPEID_BOOL )
return *(bool*)value ? "true" : "false";
else if( typeId == asTYPEID_INT8 )
s << (int)*(signed char*)value;
else if( typeId == asTYPEID_INT16 )
s << (int)*(signed short*)value;
else if( typeId == asTYPEID_INT32 )
s << *(signed int*)value;
else if( typeId == asTYPEID_INT64 )
#if defined(_MSC_VER) && _MSC_VER <= 1200
s << "{...}"; // MSVC6 doesn't like the << operator for 64bit integer
#else
s << *(asINT64*)value;
#endif
else if( typeId == asTYPEID_UINT8 )
s << (unsigned int)*(unsigned char*)value;
else if( typeId == asTYPEID_UINT16 )
s << (unsigned int)*(unsigned short*)value;
else if( typeId == asTYPEID_UINT32 )
s << *(unsigned int*)value;
else if( typeId == asTYPEID_UINT64 )
#if defined(_MSC_VER) && _MSC_VER <= 1200
s << "{...}"; // MSVC6 doesn't like the << operator for 64bit integer
#else
s << *(asQWORD*)value;
#endif
else if( typeId == asTYPEID_FLOAT )
s << *(float*)value;
else if( typeId == asTYPEID_DOUBLE )
s << *(double*)value;
else if( (typeId & asTYPEID_MASK_OBJECT) == 0 )
{
// The type is an enum
s << *(asUINT*)value;
// Check if the value matches one of the defined enums
/*for( int n = engine->GetEnumValueCount(typeId); n-- > 0; )
{
int enumVal;
const char *enumName = engine->GetEnumValueByIndex(typeId, n, &enumVal);
if( enumVal == *(int*)value )
{
s << ", " << enumName;
break;
}
}*/
}
else if( typeId & asTYPEID_SCRIPTOBJECT )
{
// Dereference handles, so we can see what it points to
if( typeId & asTYPEID_OBJHANDLE )
value = *(void**)value;
asIScriptObject *obj = (asIScriptObject *)value;
s << "{ " << obj << "";
if( obj && expandMembers )
{
asITypeInfo *type = obj->GetObjectType();
for( unsigned n = 0; n < obj->GetPropertyCount(); n++ )
{
s << std::endl << " " << type->GetPropertyDeclaration(n) << " = " << getScriptVariable(obj->GetAddressOfProperty(n), obj->GetPropertyTypeId(n), false, engine);
}
s << "\n}";
}
else {
s << " }";
}
}
else
{
// Dereference handles, so we can see what it points to
if( typeId & asTYPEID_OBJHANDLE )
value = *(void**)value;
auto* type = engine->GetTypeInfoById(typeId);
if(type && value) {
auto* func = type->GetMethodByDecl("string opAdd(const string&in) const");
if(!func)
func = type->GetMethodByDecl("string opAdd(const string&) const");
if(!func)
func = type->GetMethodByDecl("string opAdd(string&) const");
if(!func)
func = type->GetMethodByDecl("string opAdd_r(string&) const");
if(func) {
std::string val;
std::string* ret;
auto* ctx = engine->CreateContext();
ctx->Prepare(func);
ctx->SetObject(value);
ctx->SetArgAddress(0, &val);
auto status = ctx->Execute();
if(status == asSUCCESS) {
ret = (std::string*)ctx->GetReturnObject();
s << *ret;
}
else {
s << "{ " << value << " }?";
}
}
else {
// Just print the address
s << "{ " << value << " }";
}
}
else {
// Just print the address
s << "{ " << value << " }";
}
}
return s.str();
}
};
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include "angelscript.h"
#include "threads.h"
#include <string>
#include <vector>
#include <stack>
#include <map>
//Logs every line that triggers a line callback
//#define LOG_EXECUTION
//Samples all execution randomly, recording results
// The define also specifies how often to check for an executed line (higher is less often)
//#define PROFILE_EXECUTION 25
#if defined(LOG_EXECUTION) || defined(PROFILE_EXECUTION)
#define DO_LINE_CALLBACK
#endif
namespace scripts {
void initContextCache();
void resetContextCache(bool resetMenu = false);
void freeContextCache();
asIScriptContext* makeContext(asIScriptEngine* engine);
asIScriptContext* fetchContext(asIScriptEngine* engine);
std::string getStackVariables(asIScriptContext* context, unsigned frame = 0);
std::string getStackTrace(asIScriptContext* context, bool verbose = false);
std::string getScriptVariable(void *value, asUINT typeId, bool expandMembers, asIScriptEngine *engine);
void logException();
};
File diff suppressed because it is too large Load Diff
+284
View File
@@ -0,0 +1,284 @@
#pragma once
#include "scripts/manager.h"
#include "script_bind.h"
#include "vec3.h"
#include "vec2.h"
#include "quaternion.h"
#include <string>
#include <stdint.h>
#include <vector>
#include <functional>
#ifndef MAX_GENERIC_ARGUMENTS
#define MAX_GENERIC_ARGUMENTS 16
#endif
class Object;
class Empire;
class Design;
struct Player;
struct ScriptObjectType;
namespace net {
struct Message;
};
namespace scene {
class Node;
};
namespace scripts {
struct ArgumentDesc;
struct GenericValue;
typedef std::function<void(net::Message&,ArgumentDesc&,GenericValue&)> customRW;
typedef std::function<asIScriptObject*(ArgumentDesc&,asIScriptObject*)> customHandle;
struct GenericValue {
union {
int integer;
unsigned uint;
float sfloat;
double dfloat;
bool boolean;
int64_t longint;
std::string* str;
Object* obj;
Empire* emp;
scene::Node* node;
Design* design;
Player* player;
asIScriptObject* script;
net::Message* msg;
vec3d* v3;
vec2i* v2;
quaterniond* quat;
void* ptr;
};
bool managed;
volatile bool asyncReturned;
template<class T>
T& get() {
return *(T*)this;
}
GenericValue() :
ptr(nullptr), managed(false), asyncReturned(false) {}
};
void trivialCopy(GenericValue& to, GenericValue& from);
struct GenericType {
std::string name;
bool wouldConst;
std::function<void(GenericValue&,unsigned,asIScriptGeneric*)> get;
std::function<void(GenericValue&,GenericValue&)> copy;
std::function<void(GenericValue&,asIScriptGeneric*)> ret;
std::function<void(Call&,GenericValue&)> call;
std::function<void(Call&,ArgumentDesc&,GenericValue&,customHandle custom)> push;
std::function<void(net::Message&,ArgumentDesc&,GenericValue&,customRW custom)> write;
std::function<bool(net::Message&,ArgumentDesc&,GenericValue&,customRW custom)> read;
std::function<void(GenericValue&)> destruct;
GenericType() : wouldConst(false) {}
template<class T>
GenericType* setup(std::string Name, decltype(get) Get, decltype(ret) Ret, decltype(copy) Copy = trivialCopy,
decltype(call) Cl = nullptr, decltype(push) Push = nullptr,
decltype(write) Write = nullptr, decltype(read) Read = nullptr,
decltype(destruct) Destruct = nullptr, bool WouldConst = false) {
name = Name;
get = Get;
ret = Ret;
copy = Copy;
if(Cl == nullptr) {
call = [](Call& cl, GenericValue& val) {
cl.call(val.get<T>());
};
}
else {
call = Cl;
}
if(Push == nullptr) {
push = [](Call& cl, ArgumentDesc& desc, GenericValue& val, customHandle custom) {
cl.push(val.get<T>());
};
}
else {
push = Push;
}
if(Write == nullptr) {
write = [](net::Message& msg, ArgumentDesc& desc, GenericValue& val, customRW custom) {
msg << val.get<T>();
};
}
else {
write = Write;
}
if(Read == nullptr) {
read = [](net::Message& msg, ArgumentDesc& desc, GenericValue& val, customRW custom) -> bool {
msg >> val.get<T>();
return true;
};
}
else {
read = Read;
}
destruct = Destruct;
wouldConst = WouldConst;
return this;
}
};
extern GenericType* GT_uint;
extern GenericType* GT_Custom_Handle;
extern GenericType* GT_Player_Ref;
extern GenericType* GT_Object_Ref;
extern GenericType* GT_Empire_Ref;
extern GenericType* GT_Node_Ref;
const std::string& getTypeString(GenericType* type);
GenericType* getStringType(const std::string& type);
struct ArgumentDesc {
GenericType* type;
std::string argName;
std::string defaultValue;
std::string customName;
void* customType;
void* customRead;
void* customWrite;
bool isConst;
ArgumentDesc()
: type(nullptr), customType(nullptr), customRead(nullptr), customWrite(nullptr), isConst(false) {
}
ArgumentDesc(GenericType* Type)
: type(Type), customType(nullptr), customRead(nullptr), customWrite(nullptr), isConst(false) {
}
ArgumentDesc(GenericType* Type, bool IsConst)
: type(Type), customType(nullptr), customRead(nullptr), customWrite(nullptr), isConst(IsConst) {
}
ArgumentDesc(std::string CustomName, bool IsConst)
: type(GT_Custom_Handle), customName(CustomName), customType(nullptr), customRead(nullptr),
customWrite(nullptr), isConst(IsConst) {
}
void operator=(const ArgumentDesc& other) {
type = other.type;
defaultValue = other.defaultValue;
argName = other.argName;
customName = other.customName;
customType = other.customType;
customRead = other.customRead;
customWrite = other.customWrite;
isConst = other.isConst;
}
void operator=(GenericType* Type) {
type = Type;
}
operator GenericType*() {
return type;
}
std::string toString() {
if(!type)
return "void";
std::string out;
if(isConst)
out += "const ";
if(type == GT_Custom_Handle)
out += customName+"@";
else
out += getTypeString(type);
return out;
}
};
//Generic calls follow descriptors that state
//which arguments they have and what their return value is
class GenericCallDesc {
public:
std::string name;
ArgumentDesc returnType;
ArgumentDesc arguments[MAX_GENERIC_ARGUMENTS];
bool returnsArray;
bool constFunction;
unsigned argCount;
GenericCallDesc();
GenericCallDesc(std::string declaration, bool hasReturnType = true, bool customRefs = false);
void parse(std::string declaration, bool hasReturnType = true, bool customRefs = false);
void append(GenericType* argument);
void append(GenericCallDesc& desc);
void append(std::vector<std::string>& arguments, bool customRefs = false);
void prepend(ArgumentDesc argument);
GenericValue call(Call& cl);
std::string declaration(bool addReturnType = true, bool forceConst = false, unsigned start = 0);
};
//Call data holds the values of all the arguments for easy passing around
class GenericCallData {
public:
GenericCallDesc& desc;
GenericValue values[MAX_GENERIC_ARGUMENTS];
void* object;
GenericCallData(GenericCallDesc& desc);
GenericCallData(GenericCallData& data);
~GenericCallData();
//Generic call data can be sent over the network,
//the function descriptor should be sent manually though,
//depending on what system is sending it.
void write(net::Message& msg, unsigned startAt = 0, customRW custom = nullptr);
bool read(net::Message& msg, unsigned startAt = 0, customRW custom = nullptr);
//Read all the arguments from an angelscript generic
//call, and stash them here
GenericCallData(GenericCallDesc& desc, asIScriptGeneric* gen);
//Push all the arguments onto a script call to angelscript
void pushTo(Call& call, int startAt = 0, customHandle custom = nullptr);
GenericValue call(Call& cl);
};
//Retrieve the function matching the call desc from this manager
asIScriptFunction* getFunction(Manager* manager, const char* module, GenericCallDesc& desc, bool forceConst = false);
//Bind a generically specified function to a callback,
//the callback receives the argument data and the bind id
//returned by the call to bindGeneric so it can identify which
//generic call was received
typedef GenericValue (*GenericHandler)(void* arg, GenericCallData& data);
int bindGeneric(GenericCallDesc& desc, GenericHandler handler, void* arg = nullptr, bool acceptConst = false);
int bindGeneric(ClassBind& cls, GenericCallDesc& desc, GenericHandler handler, void* arg = nullptr, bool forceConst = false);
int bindGeneric(ClassBind& cls, const std::string& decl, GenericCallDesc& desc, GenericHandler handler, void* arg = 0);
void clearGenericBinds();
void initGenericTypes();
void bindGenericObjectType(ScriptObjectType* type, std::string name);
};
File diff suppressed because it is too large Load Diff
+280
View File
@@ -0,0 +1,280 @@
#pragma once
#include "angelscript.h"
#include "threads.h"
#include <string>
#include <vector>
#include <stack>
#include <map>
#include <set>
//#define PROFILE_SCRIPT_CALLBACKS
//#define TRACE_GC_LOCK
class SaveFile;
class asCObjectType;
class asCScriptFunction;
class asCGlobalProperty;
namespace scripts {
enum EngineDataIDs {
EDID_Manager,
EDID_SerializableType,
EDID_SerializableWrite,
EDID_SerializableRead,
EDID_objectArray,
EDID_playerArray,
EDID_nodeArray,
EDID_stringArray,
EDID_consoleCommand,
EDID_SavableType,
EDID_SavableWrite,
EDID_SavableRead,
};
enum ScriptCallback {
SC_preInit,
SC_init,
SC_postInit,
SC_tick,
SC_deinit,
SC_preRender,
SC_render,
SC_draw,
SC_settings_changed,
SC_game_settings,
SC_sync_initial,
SC_sync_periodic,
SC_recv_periodic,
SC_save,
SC_load,
SC_saveIdentifiers,
SC_stateChange,
SC_preReload,
SC_postReload,
SC_COUNT
};
class Manager;
struct Call {
Manager* manager;
asIScriptContext* ctx;
asUINT arg;
bool nested;
int status;
Call();
Call(Manager* man, asIScriptContext* Ctx, bool nested = false);
~Call();
inline bool valid() { return ctx != 0; }
void setObject(void* obj);
void* getReturnObject();
void push(int value);
void push(unsigned value);
void push(long long value);
void push(float value);
void push(double value);
void push(bool value);
bool call();
bool call(int& value);
bool call(unsigned& value);
bool call(float& value);
bool call(double& value);
bool call(bool& value);
bool call(long long& value);
template<class T>
void push(T* value) {
if(ctx)
ctx->SetArgAddress(arg++, (void*)value);
}
template<class T>
void pushObj(T* value) {
if(ctx)
ctx->SetArgObject(arg++, (void*)value);
}
template<class T>
bool call(T*& value) {
if (!call()) {
value = 0;
return false;
}
value = (T*)ctx->GetReturnAddress();
return true;
}
template<class T>
bool callObjRet(T*& value) {
if (!call()) {
value = 0;
return false;
}
value = (T*)ctx->GetReturnObject();
return true;
}
};
struct MultiCall {
std::vector<Call> calls;
MultiCall();
void setObject(void* obj);
void push(int value);
void push(float value);
void push(double value);
void push(bool value);
void push(void* value);
void call();
};
typedef std::pair<std::string,std::string> ImportSpec;
struct File {
std::string module, path, contents;
std::map<std::string, File*> includes;
std::set<ImportSpec> imports;
std::set<std::string> exports;
std::set<std::string> exportsFrom;
int priority_init, priority_render;
int priority_draw, priority_tick;
int priority_sync;
File() : priority_init(0), priority_render(0),
priority_draw(0), priority_tick(0), priority_sync(0) {}
};
struct Module {
std::string name;
std::string compiledBy;
File* file;
Manager* manager;
asIScriptModule* module;
asIScriptFunction* callbacks[SC_COUNT];
unsigned tickFails;
unsigned drawFails;
unsigned renderFails;
bool compiled;
bool compiling;
std::set<ImportSpec> imports;
std::set<Module*> dependencies;
std::set<std::string> exports;
std::set<std::string> exportsFrom;
std::vector<asITypeInfo*> typeExports;
std::vector<asCScriptFunction*> funcExports;
std::vector<unsigned> globalExports;
#ifdef PROFILE_SCRIPT_CALLBACKS
double tickTime;
double drawTime;
double renderTime;
Module() : tickFails(0), drawFails(0), renderFails(0),
tickTime(0), drawTime(0), renderTime(0), compiled(false), compiling(false) {}
#else
Module() : tickFails(0), drawFails(0), renderFails(0), compiled(false), compiling(false) {}
#endif
Call call(ScriptCallback cb);
asIScriptFunction* getFunction(const char* decl);
asITypeInfo* getClass(const char* decl);
};
class Manager {
public:
threads::ReadWriteMutex threadedCallMutex;
asIScriptEngine* engine;
std::map<std::string, Module*> modules;
std::map<std::string, File*> files;
threads::atomic_int scriptThreadsActive;
threads::atomic_int scriptThreadsExistent;
volatile bool pauseScripts;
volatile bool clearScripts;
asUINT prevGCSize;
std::multimap<int, Module*> priority_init;
std::multimap<int, Module*> priority_render;
std::multimap<int, Module*> priority_draw;
std::multimap<int, Module*> priority_tick;
std::multimap<int, Module*> priority_sync;
Manager(asIJITCompiler* jit = 0);
~Manager();
Module* getModule(const char* module);
asIScriptFunction* getFunction(int fid);
asIScriptFunction* getFunction(const char* module, const char* decl);
asIScriptFunction* getFunction(const std::string& def, const char* args, const char* ret);
asITypeInfo* getClass(const char* module, const char* decl);
std::vector<std::string> includePaths;
void addIncludePath(std::string path);
void loadDirectory(const std::string& dirname);
void load(const std::string& modulename, const std::string& filename);
Call call(int funcID);
Call call(asIScriptFunction* func);
Call call(const char* module, const char* decle);
MultiCall call(ScriptCallback cb);
void compile(const std::string& cache_root = "", unsigned cache_version = 0);
void reload(const std::string& module);
void init();
void deinit();
void tick(double time);
void draw();
void preRender(double frameTime);
void render(double frameTime);
void save(SaveFile& file);
void load(SaveFile& file);
void saveIdentifiers(SaveFile& file);
void stateChange();
void pauseScriptThreads();
void resumeScriptThreads();
bool scriptThreadStart();
void scriptThreadEnd();
void clearScriptThreads();
void scriptThreadCreate();
void scriptThreadDestroy();
#ifdef PROFILE_SCRIPT_CALLBACKS
void printProfile();
#endif
#ifdef TRACE_GC_LOCK
threads::threadlocalPointer<bool> gcPossible;
void markGCImpossible();
void markGCPossible();
#endif
void clear();
int garbageCollect(bool full = false);
static Manager& fromEngine(asIScriptEngine* engine);
};
Manager* getActiveManager();
void throwException(const char* msg);
void clearCachedScripts();
void preloadScript(const std::string& filename);
};
+626
View File
@@ -0,0 +1,626 @@
#include "script_bind.h"
#include "threads.h"
#include "str_util.h"
#include "main/references.h"
#include <map>
#include <set>
#include <stdarg.h>
namespace scripts {
#ifdef _DEBUG
int check(int id) {
if(id < 0) {
if(id == asALREADY_REGISTERED)
throw "Already bound";
else
throw "Binding error";
}
return id;
}
#else
#define check(x) x
#endif
#ifdef DOCUMENT_API
#define check_doc(x) return *new Documentor(check(x), declaration);
#else
#define check_doc(x) check(x)
#endif
Threaded(asIScriptEngine*) engine;
void setEngine(asIScriptEngine* _engine) {
engine = _engine;
}
asIScriptEngine* getEngine() {
return engine;
}
_DOC bind(const char* declaration, asSFuncPtr func, asDWORD callType) {
check_doc( engine->RegisterGlobalFunction(declaration, func, callType) );
}
_DOC bind(const char* declaration, asSFuncPtr func, void* obj) {
check_doc( engine->RegisterGlobalFunction(declaration, func, asCALL_THISCALL_ASGLOBAL, obj) );
}
void bindStringFactory(const char* stringClass, asSFuncPtr func, asDWORD callType) {
check( engine->RegisterStringFactory(stringClass, func, callType) );
}
_GLOBAL_DOC bindGlobal(const char* declaration, void* ptr) {
check( engine->RegisterGlobalProperty(declaration, ptr) );
#ifdef DOCUMENT_API
return *new GlobalDoc(declaration);
#endif
}
void bindFuncdef(const char* declaration) {
check( engine->RegisterFuncdef( declaration ) );
}
ClassBind::ClassBind(const char* Name) : name(Name) {}
ClassBind::ClassBind(const char* Name, asDWORD flags, int size) : name(Name) {
if(flags & asOBJ_TEMPLATE) {
std::string regName = name;
regName += "<class T>";
name += "<T>";
check( engine->RegisterObjectType(regName.c_str(), size, flags) );
}
else {
check( engine->RegisterObjectType(Name, size, flags) );
}
}
asITypeInfo* ClassBind::getType() {
return engine->GetTypeInfoByName(name.c_str());
}
_DOC ClassBind::addFactory(const char* declaration, asSFuncPtr func) {
check_doc( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_FACTORY, declaration, func, asCALL_CDECL) );
}
_DOC ClassBind::addGenericFactory(const char* declaration, asSFuncPtr func, void* userdata) {
int fid = engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_FACTORY, declaration, func, asCALL_GENERIC);
#ifdef _DEBUG
check(fid);
#endif
if(userdata)
engine->GetFunctionById(fid)->SetUserData(userdata);
#ifdef DOCUMENT_API
return *new Documentor(fid, declaration);
#endif
}
_DOC ClassBind::addConstructor(const char* declaration, asSFuncPtr func) {
check_doc( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_CONSTRUCT, declaration, func, asCALL_CDECL_OBJFIRST) );
}
_DOC ClassBind::addDestructor(const char* declaration, asSFuncPtr func) {
check_doc( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_DESTRUCT, declaration, func, asCALL_CDECL_OBJFIRST) );
}
_DOC ClassBind::addMethod(const char* declaration, asSFuncPtr func) {
check_doc( engine->RegisterObjectMethod(name.c_str(), declaration, func, asCALL_THISCALL) );
}
_DOC ClassBind::addBehaviour(asEBehaviours behav, const char* declaration, asSFuncPtr func) {
check_doc( engine->RegisterObjectBehaviour(name.c_str(), behav, declaration, func, asCALL_THISCALL) );
}
_DOC ClassBind::addExternBehaviour(asEBehaviours behav, const char* declaration, asSFuncPtr func, bool ptrFirst) {
check_doc( engine->RegisterObjectBehaviour(name.c_str(), behav, declaration, func, ptrFirst ? asCALL_CDECL_OBJFIRST : asCALL_CDECL_OBJLAST) );
}
_DOC ClassBind::addGenericBehaviour(asEBehaviours behav, const char* declaration, asSFuncPtr func, void* userdata) {
int fid = engine->RegisterObjectBehaviour(name.c_str(), behav, declaration, func, asCALL_GENERIC);
#ifdef _DEBUG
check(fid);
#endif
if(userdata)
engine->GetFunctionById(fid)->SetUserData(userdata);
#ifdef DOCUMENT_API
return *new Documentor(fid, declaration);
#endif
}
_DOC ClassBind::addLooseBehaviour(asEBehaviours behav, const char* declaration, asSFuncPtr func) {
check_doc( engine->RegisterObjectBehaviour(name.c_str(), behav, declaration, func, asCALL_CDECL) );
}
void ClassBind::addGarbageCollection(asSFuncPtr setflag, asSFuncPtr getflag, asSFuncPtr getrefcount, asSFuncPtr enumrefs, asSFuncPtr releaserefs) {
check( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_SETGCFLAG, "void f()", setflag, asCALL_THISCALL) );
check( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_GETGCFLAG, "bool f()", getflag, asCALL_THISCALL) );
check( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_GETREFCOUNT, "int f()", getrefcount, asCALL_THISCALL) );
check( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_ENUMREFS, "void f(int&in)", enumrefs, asCALL_THISCALL) );
check( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_RELEASEREFS, "void f(int&in)", releaserefs, asCALL_THISCALL) );
}
_DOC ClassBind::addExternMethod(const char* declaration, asSFuncPtr func, bool ptrFirst) {
check_doc( engine->RegisterObjectMethod(name.c_str(), declaration, func, ptrFirst ? asCALL_CDECL_OBJFIRST : asCALL_CDECL_OBJLAST) );
}
_DOC ClassBind::addGenericMethod(const char* declaration, asSFuncPtr func, void* userdata) {
int fid = engine->RegisterObjectMethod(name.c_str(), declaration, func, asCALL_GENERIC);
#ifdef _DEBUG
check(fid);
#endif
if(userdata)
engine->GetFunctionById(fid)->SetUserData(userdata);
#ifdef DOCUMENT_API
return *new Documentor(fid, declaration);
#endif
}
_MEMBER_DOC ClassBind::addMember(const char* declaration, size_t offset) {
check( engine->RegisterObjectProperty(name.c_str(), declaration, (int)offset) );
#ifdef DOCUMENT_API
return *new MemberDoc(name, declaration);
#endif
}
void ClassBind::setReferenceFuncs(asSFuncPtr grab, asSFuncPtr drop) {
check( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_ADDREF, "void f()", grab, asCALL_THISCALL) );
check( engine->RegisterObjectBehaviour(name.c_str(), asBEHAVE_RELEASE, "void f()", drop, asCALL_THISCALL) );
}
InterfaceBind::InterfaceBind(const char* Name, bool Register) : name(Name) {
if(Register)
check( engine->RegisterInterface(Name) );
}
_DOC InterfaceBind::addMethod(const char* declaration, asIScriptFunction** ptr) {
int fid = engine->RegisterInterfaceMethod(name, declaration);
#ifdef _DEBUG
check(fid);
#endif
if(ptr)
*ptr = engine->GetFunctionById(fid);
#ifdef DOCUMENT_API
return *new Documentor(fid, declaration);
#endif
}
EnumBind::EnumBind(const char* Name, bool Register) : name(Name) {
if(Register)
check( engine->RegisterEnum(Name) );
}
void EnumBind::setter::operator=(int val) {
check( engine->RegisterEnumValue( enumName, itemName, val) );
}
EnumBind::setter::setter(const char* E, const char* I) : enumName(E), itemName(I) {}
EnumBind::setter EnumBind::operator[](const char* item) {
return setter(name, item);
}
EnumBind::setter EnumBind::operator[](const std::string& item) {
return setter(name, item.c_str());
}
#ifdef DOCUMENT_API
static std::map<std::pair<asIScriptEngine*, int>, Documentor*> documentation;
static std::map<std::pair<asIScriptEngine*, std::string>, std::string> class_documentation;
static std::map<std::pair<asIScriptEngine*, std::string>, GlobalDoc*> global_documentation;
static std::map<std::tuple<asIScriptEngine*, std::string, std::string>, MemberDoc*> member_documentation;
static threads::Mutex docMtx;
std::set<std::string> INIT_VAR(ignoredWords) {
ignoredWords.insert("&");
ignoredWords.insert("&in");
ignoredWords.insert("&out");
ignoredWords.insert("&inout");
ignoredWords.insert("in");
ignoredWords.insert("out");
ignoredWords.insert("inout");
} INIT_VAR_END;
Documentor::Documentor() : fid(-1), documented(false) {
}
Documentor::Documentor(int fid, std::string decl)
: fid(fid), decl(decl), documented(false) {
funcSplit(decl, funcName, argNames);
argDefaults.resize(argNames.size());
//Only use the variable name
for(unsigned i = 0, cnt = argNames.size(); i < cnt; ++i) {
std::vector<std::string> parts;
split(argNames[i], parts, ' ', true);
//Find default argument and variable name
argNames[i] = "";
for(int j = parts.size() - 1; j >= 1; --j) {
if(ignoredWords.find(parts[j]) != ignoredWords.end())
continue;
if(j == 1 && parts[0] == "const")
break;
if(j > 0 && parts[j-1] == "=") {
argDefaults[i] = parts[j];
--j;
}
else {
argNames[i] = parts[j];
break;
}
}
}
//Add to global list
{
threads::Lock lock(docMtx);
documentation[std::pair<asIScriptEngine*,int>(engine, fid)] = this;
}
}
void Documentor::operator()(const char* func_doc, ...) {
if(fid == -1)
return;
va_list args;
va_start(args, func_doc);
//Function documentation
funcDoc = func_doc;
documented = true;
//Arguments
argDoc.resize(argNames.size());
for(unsigned i = 0, cnt = argNames.size(); i < cnt; ++i)
argDoc[i] = va_arg(args, const char*);
//Return value
auto* func = engine->GetFunctionById(fid);
if(func && func->GetReturnTypeId() != asTYPEID_VOID)
retDoc = va_arg(args, const char*);
va_end(args);
}
Documentor* getDocumentation(asIScriptEngine* eng, int fid) {
std::pair<asIScriptEngine*, int> key(eng, fid);
auto it = documentation.find(key);
if(it != documentation.end())
return it->second;
return 0;
}
Documentor* getDocumentation(asIScriptFunction* func) {
std::pair<asIScriptEngine*, int> key(func->GetEngine(), func->GetId());
auto it = documentation.find(key);
if(it != documentation.end())
return it->second;
return 0;
}
GlobalDoc* getGlobalDocumentation(asIScriptEngine* eng, std::string varname) {
std::pair<asIScriptEngine*, std::string> key(eng, varname);
auto it = global_documentation.find(key);
if(it != global_documentation.end())
return it->second;
return 0;
}
MemberDoc* getMemberDocumentation(asIScriptEngine* eng, std::string clsname, std::string varname) {
std::tuple<asIScriptEngine*, std::string, std::string> key(eng, clsname, varname);
auto it = member_documentation.find(key);
if(it != member_documentation.end())
return it->second;
return 0;
}
std::string getClassDocumentation(asIScriptEngine* eng, std::string clsname) {
std::pair<asIScriptEngine*, std::string> key(eng, clsname);
auto it = class_documentation.find(key);
if(it != class_documentation.end())
return it->second;
return "";
}
GlobalDoc::GlobalDoc(std::string decl) {
std::vector<std::string> parts;
split(decl, parts, ' ', true);
varname = parts[parts.size() - 1];
}
void GlobalDoc::operator()(const char* _doc) {
doc = _doc;
threads::Lock lock(docMtx);
global_documentation[std::pair<asIScriptEngine*,std::string>(engine, varname)] = this;
}
MemberDoc::MemberDoc(std::string clsname, std::string decl)
: clsname(clsname) {
std::vector<std::string> parts;
split(decl, parts, ' ', true);
varname = parts[parts.size() - 1];
}
void MemberDoc::operator()(const char* _doc) {
doc = _doc;
threads::Lock lock(docMtx);
member_documentation[std::tuple<asIScriptEngine*,std::string,std::string>(engine, clsname, varname)] = this;
}
void ClassBind::document(std::string str) {
threads::Lock lock(docMtx);
class_documentation[std::pair<asIScriptEngine*,std::string>(engine, name)] = str;
}
void InterfaceBind::document(std::string str) {
threads::Lock lock(docMtx);
class_documentation[std::pair<asIScriptEngine*,std::string>(engine, name)] = str;
}
void EnumBind::document(std::string str) {
threads::Lock lock(docMtx);
class_documentation[std::pair<asIScriptEngine*,std::string>(engine, name)] = str;
}
void documentBinds() {
std::ofstream file("api_documentation.json");
file << "{\n";
auto docType = [&](asIScriptEngine* eng, int tid, asDWORD flags) {
file << eng->GetTypeDeclaration(tid);
if(flags != 0)
file << "&";
};
auto docFunction = [&](asIScriptFunction* f) {
Documentor* doc = getDocumentation(f);
file << "{ \"name\": \"";
file << f->GetName();
//Function documentation
if(doc && doc->documented) {
file << "\",\n\"doc\": \"";
file << doc->funcDoc;
}
//Arguments
file << "\",\n\"arguments\": [";
unsigned argCnt = f->GetParamCount();
for(unsigned a = 0; a < argCnt; ++a) {
asDWORD flags;
int tid;
f->GetParam(a, &tid, &flags);
if(a != 0)
file << ",";
file << "{ \"type\": \"";
docType(f->GetEngine(), tid, flags);
file << "\", \"name\": \"";
if(doc && a < doc->argNames.size() && !doc->argNames[a].empty())
file << doc->argNames[a];
else
file << "arg" + toString(a);
if(doc && a < doc->argDefaults.size() && !doc->argDefaults[a].empty()) {
file << "\", \"default\": \"";
file << doc->argDefaults[a];
}
if(doc && a < doc->argDoc.size() && !doc->argDoc[a].empty()) {
file << "\", \"doc\": \"";
file << doc->argDoc[a];
}
file << "\"}";
}
file << "],\n";
//Return value
int tid = f->GetReturnTypeId();
file << "\"return\": {\n";
file << "\"type\": \"";
docType(f->GetEngine(), tid, 0);
if(doc && !doc->retDoc.empty()) {
file << "\",\n\"doc\": \"";
file << doc->retDoc;
}
file << "\"},\n";
//Constness of function
if(f->IsReadOnly())
file << "\"const\": true";
else
file << "\"const\": false";
file << "}";
};
auto docEngine = [&](asIScriptEngine* eng) {
//Document classes
file << "\"classes\": [\n";
unsigned clsCnt = eng->GetObjectTypeCount();
for(unsigned i = 0; i < clsCnt; ++i) {
auto* cls = eng->GetObjectTypeByIndex(i);
if(i != 0)
file << ",\n";
file << "{ \"name\": \"";
file << cls->GetName();
std::string clsdoc = getClassDocumentation(eng, cls->GetName());
if(!clsdoc.empty()) {
file << "\",\n\"doc\": \"";
file << clsdoc;
}
if(cls->GetFlags() & asOBJ_SCRIPT_OBJECT && cls->GetSize() == 0)
file << "\",\n\"interface\": true";
else
file << "\",\n\"interface\": false";
//Document members
file << ",\n\"members\": [";
unsigned memCnt = cls->GetPropertyCount();
for(unsigned m = 0; m < memCnt; ++m) {
const char* name;
int typeId;
cls->GetProperty(m, &name, &typeId);
if(m != 0)
file << ",";
file << "{\n";
file << "\"type\": \"";
docType(eng, typeId, 0);
file << "\",\n\"name\": \"";
file << name;
auto* mdoc = getMemberDocumentation(eng, cls->GetName(), name);
if(mdoc && !mdoc->doc.empty()) {
file << "\",\n\"doc\": \"";
file << mdoc->doc;
}
file << "\"}";
}
file << "],\n";
//Document methods
file << "\"methods\": [";
unsigned mthCnt = cls->GetMethodCount();
for(unsigned m = 0; m < mthCnt; ++m) {
if(m != 0)
file << ",\n";
docFunction(cls->GetMethodByIndex(m));
}
file << "]\n";
file << "}";
}
file << "],\n";
//Document enums
file << "\"enums\": [";
unsigned enumCnt = eng->GetEnumCount();
for(unsigned i = 0; i < enumCnt; ++i) {
int enumid;
const char* name;
name = eng->GetEnumByIndex(i, &enumid);
if(i != 0)
file << ",";
file << "\n{";
file << "\"name\": \"";
file << name;
std::string clsdoc = getClassDocumentation(eng, name);
if(!clsdoc.empty()) {
file << "\",\n\"doc\": \"";
file << clsdoc;
}
file << "\",\n\"values\": {";
unsigned vCnt = eng->GetEnumValueCount(enumid);
for(unsigned j = 0; j < vCnt; ++j) {
const char* vname;
int val;
vname = eng->GetEnumValueByIndex(enumid, j, &val);
if(j != 0)
file << ",\n";
file << "\"" << vname << "\": " << val;
}
file << "}}";
}
file << "],\n";
//Document global functions
file << "\"functions\": [\n";
unsigned funcCnt = eng->GetGlobalFunctionCount();
for(unsigned i = 0; i < funcCnt; ++i) {
auto* f = eng->GetGlobalFunctionByIndex(i);
if(i != 0)
file << ",\n";
docFunction(f);
}
file << "],\n";
//Document global variables
file << "\"globals\": [";
unsigned propCnt = eng->GetGlobalPropertyCount();
for(unsigned p = 0; p < propCnt; ++p) {
const char* name, *ns;
int typeId;
eng->GetGlobalPropertyByIndex(p, &name, &ns, &typeId);
if(p != 0)
file << ",";
file << "{\n";
file << "\"type\": \"";
docType(eng, typeId, 0);
file << "\",\n\"ns\": \"";
file << ns;
file << "\",\n\"name\": \"";
file << name;
auto* pdoc = getGlobalDocumentation(eng, name);
if(pdoc) {
file << "\",\n\"doc\": \"";
file << pdoc->doc;
}
file << "\"}";
}
file << "]\n";
};
file << "\"server\": {\n";
docEngine(devices.scripts.cache_server->engine);
file << "},\n\n";
file << "\"client\": {\n";
docEngine(devices.scripts.client->engine);
file << "}\n";
file << "}\n";
file.close();
}
#endif
};
+171
View File
@@ -0,0 +1,171 @@
#pragma once
#include "angelscript.h"
#include "util/format.h"
#include <string>
#include <vector>
namespace scripts {
//#define DOCUMENT_API
#ifdef DOCUMENT_API
#define doc(...) ( __VA_ARGS__ )
#define classdoc(cls, str) cls.document(str)
#define _DOC Documentor&
#define _GLOBAL_DOC GlobalDoc&
#define _MEMBER_DOC MemberDoc&
struct Documentor {
int fid;
std::string decl;
std::string funcName;
bool documented;
std::string funcDoc;
std::string retDoc;
std::vector<std::string> argNames;
std::vector<std::string> argDefaults;
std::vector<std::string> argDoc;
Documentor();
Documentor(int fid, std::string decl);
void operator()(const char* funcDoc, ...);
};
struct GlobalDoc {
std::string doc;
std::string varname;
GlobalDoc(std::string decl);
void operator()(const char* doc);
};
struct MemberDoc {
std::string clsname;
std::string varname;
std::string doc;
MemberDoc(std::string clsname, std::string decl);
void operator()(const char* doc);
};
Documentor* getDocumentation(asIScriptEngine* eng, int fid);
Documentor* getDocumentation(asIScriptFunction* func);
GlobalDoc* getGlobalDocumentation(asIScriptEngine* eng, std::string varname);
std::string getClassDocumentation(asIScriptEngine* eng, std::string clsname);
MemberDoc* getMemberDocumentation(asIScriptEngine* eng, std::string clsname, std::string varname);
void documentBinds();
#else
#define doc(...)
#define classdoc(cls, str)
#define _DOC void
#define _GLOBAL_DOC void
#define _MEMBER_DOC void
#endif
void setEngine(asIScriptEngine* engine);
asIScriptEngine* getEngine();
_DOC bind(const char* declaration, asSFuncPtr func, asDWORD callType = asCALL_CDECL);
_DOC bind(const char* declaration, asSFuncPtr func, void* delegateObject);
void bindStringFactory(const char* stringClass, asSFuncPtr func, asDWORD callType = asCALL_CDECL);
_GLOBAL_DOC bindGlobal(const char* declaration, void* ptr);
void bindFuncdef(const char* declaration);
struct Namespace {
Namespace(const char* name) {
getEngine()->SetDefaultNamespace(name);
}
~Namespace() {
getEngine()->SetDefaultNamespace("");
}
};
struct ClassBind {
std::string name;
//Does not register the class, but allows registration of members of the class
ClassBind(const char* Name);
//Registers the class, and allows registration of members of the class
ClassBind(const char* Name, asDWORD flags, int size = 0);
asITypeInfo* getType();
//Registers a factory function
_DOC addFactory(const char* declaration, asSFuncPtr func);
//Registers a generic factory function
_DOC addGenericFactory(const char* declaration, asSFuncPtr func, void* userdata = 0);
//Registers a initializer function
_DOC addConstructor(const char* declaration, asSFuncPtr func);
//Registers a destructor function
_DOC addDestructor(const char* declaration, asSFuncPtr func);
//Registers a native method of the class
_DOC addMethod(const char* declaration, asSFuncPtr func);
//Registers a psuedo-method of the class - a global function that accepts a pointer to the class (either first or last argument)
_DOC addExternMethod(const char* declaration, asSFuncPtr func, bool ptrFirst = true);
//Registers a psuedo-method of the class, implemented as a generic call
_DOC addGenericMethod(const char* declaration, asSFuncPtr func, void* userdata = 0);
//Registers a behaviour
_DOC addBehaviour(asEBehaviours behav, const char* declaration, asSFuncPtr func);
//Registers a pseudo-method behaviour
_DOC addExternBehaviour(asEBehaviours behav, const char* declaration, asSFuncPtr func, bool ptrFirst = true);
//Registers a generic behaviour
_DOC addGenericBehaviour(asEBehaviours behav, const char* declaration, asSFuncPtr func, void* userdata = 0);
//Registers a behaviour that doesn't take an object
_DOC addLooseBehaviour(asEBehaviours behav, const char* declaration, asSFuncPtr func);
//Register garbage collection functions to the object
void addGarbageCollection(asSFuncPtr setflag, asSFuncPtr getflag, asSFuncPtr getrefcount, asSFuncPtr enumrefs, asSFuncPtr releaserefs);
//Registers a member of the class
_MEMBER_DOC addMember(const char* declaration, size_t offset);
//Register reference count methods
void setReferenceFuncs(asSFuncPtr grab, asSFuncPtr drop);
#ifdef DOCUMENT_API
void document(std::string str);
#endif
private:
ClassBind() {}
};
struct InterfaceBind {
const char* name;
//Allows registering methods on interfaces
InterfaceBind(const char* Name, bool Register = true);
//Add a method
_DOC addMethod(const char* declaration, asIScriptFunction** funcptr = 0);
#ifdef DOCUMENT_API
void document(std::string str);
#endif
};
struct EnumBind {
const char* name;
//Allows registering constants to the enum, and optionally registers it
EnumBind(const char* Name, bool Register = true);
//To set a value, use EnumBind["ValueName"] = value;
struct setter {
const char* enumName, *itemName;
void operator=(int val);
setter(const char* E, const char* I);
};
setter operator[](const char* item);
setter operator[](const std::string& item);
#ifdef DOCUMENT_API
void document(std::string str);
#endif
private:
EnumBind() {}
};
};
File diff suppressed because it is too large Load Diff
+119
View File
@@ -0,0 +1,119 @@
#pragma once
#include "generic_call.h"
#include <vector>
#include <string>
namespace scene {
struct scriptNodeType;
};
struct ScriptObjectType;
class StateValueDefinition;
namespace scripts {
struct MethodFlags {
bool local;
bool server;
bool shadow;
bool restricted;
bool async;
bool safe;
bool relocking;
bool visible;
MethodFlags() {
reset();
}
void parse(std::string& flags);
void reset() {
local = false;
server = false;
shadow = false;
restricted = false;
async = false;
safe = false;
relocking = false;
visible = false;
}
};
struct WrappedMethod {
GenericCallDesc desc;
GenericCallDesc origDesc;
GenericCallDesc wrapped;
asIScriptFunction* func;
std::string fullDeclaration;
union {
unsigned compId;
unsigned objTypeId;
};
unsigned index;
bool isComponentMethod;
bool local;
bool server;
bool shadow;
bool onlyPrimitive;
bool restricted;
bool async;
bool relocking;
bool safe;
bool passPlayer;
bool passContaining;
std::string doc_desc;
std::string doc_return;
std::vector<std::string> doc_args;
WrappedMethod() : compId(0), index(0), func(0), local(false),
server(false), shadow(false), onlyPrimitive(true),
restricted(false), async(false), relocking(false), safe(false),
passPlayer(false), passContaining(false) {
}
};
struct Component {
unsigned id;
std::string name;
std::vector<WrappedMethod*> methods;
std::string typeDecl;
asITypeInfo* type;
asIScriptFunction *save, *load;
std::string containing;
GenericType* containingType;
const StateValueDefinition* def;
const StateValueDefinition* optDef;
Component() : id(0), type(0), save(0), load(0) {}
~Component();
};
void clearComponents();
void loadComponents(const std::string& filename);
void addComponentStateValueTypes();
unsigned getComponentCount();
Component* getComponent(unsigned index);
Component* getComponent(const std::string& name);
Component* getComponent(const StateValueDefinition* def, bool* optional = 0);
void BindEmpireComponentOffsets();
void RegisterObjectComponentWrappers(ClassBind& cls, bool server, ScriptObjectType* type = 0);
void RegisterEmpireComponentWrappers(ClassBind& cls, bool server);
void RegisterNodeMethodWrappers(ClassBind& cls, bool server, scene::scriptNodeType* type);
void RegisterComponentInterfaces(bool server);
void bindComponentClasses();
void objectWait(Object* obj);
bool getSafeCallWait();
void setSafeCallWait(bool wait);
};
+393
View File
@@ -0,0 +1,393 @@
#include "binds.h"
#include "generic_call.h"
#include "compat/misc.h"
#include <fstream>
#include "main/logging.h"
#include "render/spritesheet.h"
#include "str_util.h"
#include "../source/as_objecttype.h"
#include "../as_addons/include/scriptarray.h"
void dummy() {
}
namespace scripts {
static std::vector<GenericCallDesc*> hooks;
void LoadScriptHooks(const std::string& filename) {
//Clear existing hooks
foreach(it, hooks)
delete *it;
hooks.clear();
//Load new hooks
std::ifstream file(filename);
if(!file.is_open())
return;
skipBOM(file);
while(true) {
std::string line;
std::getline(file, line);
if(file.fail())
break;
std::string parse = line.substr(0, line.find("//"));
if(parse.find_first_not_of(" \t\n\r") == std::string::npos)
continue;
parse = trim(parse);
hooks.push_back(new GenericCallDesc(parse, true, true));
}
}
static void getHook(asIScriptGeneric* f) {
const std::string& func = *(std::string*)f->GetArgAddress(0);
GenericCallDesc* desc = (GenericCallDesc*)f->GetFunction()->GetUserData();
std::vector<std::string> parts;
split(func, parts, "::");
if(parts.size() != 2) {
throwException(format("Invalid hook specifier '$1'.", func).c_str());
return;
}
//First part is the module
const std::string& module = parts[0];
//Create a call description with the new function name
GenericCallDesc lookup = *desc;
lookup.name = parts[1];
std::string decl = lookup.declaration();
//Find the function
Manager* man = getActiveManager();
asIScriptFunction* ptr = man->getFunction(module.c_str(), decl.c_str());
if(!ptr) {
throwException(format("Could not find script function '$1'", decl).c_str());
return;
}
//Copy the pointer into the target position
*(asIScriptFunction**)f->GetObject() = ptr;
}
static void emptyHook(asIScriptFunction** f) {
*f = 0;
}
static GenericValue callHook(void* arg, GenericCallData& data) {
Manager* man = getActiveManager();
asIScriptFunction* func = *(asIScriptFunction**)data.object;
if(!func) {
throwException("Unbound script hook called.");
return GenericValue();
}
Call cl = man->call(func);
data.pushTo(cl);
return data.call(cl);
}
static bool validHook(asIScriptFunction** f) {
return *f != 0;
}
static asIScriptFunction** assignHook(asIScriptFunction** dest, asIScriptFunction** src) {
*dest = *src;
return dest;
}
static asITypeInfo* getClass(const std::string& func) {
std::vector<std::string> parts;
split(func, parts, "::");
asITypeInfo* type = nullptr;
if(parts.size() == 1) {
auto* ctx = asGetActiveContext();
if(ctx) {
auto* func = ctx->GetFunction();
type = getActiveManager()->getClass(func->GetModuleName(), parts[0].c_str());
}
}
else if(parts.size() == 2) {
type = getActiveManager()->getClass(parts[0].c_str(), parts[1].c_str());
}
else {
throwException(format("Invalid class specifier '$1'.", func).c_str());
return 0;
}
if(type)
type->AddRef();
return type;
}
static asIScriptObject* createClass(asITypeInfo* scriptType) {
asIScriptObject* ptr = 0;
asIScriptFunction* func = scriptType->GetFactoryByIndex(0);
if(!func)
return nullptr;
scripts::Call cl = getActiveManager()->call(func);
cl.call(ptr);
if(ptr)
ptr->AddRef();
return ptr;
}
static asITypeInfo* thisClass(void* ptr, int typeId) {
if(ptr != nullptr && (typeId & asTYPEID_SCRIPTOBJECT)) {
auto* p = ((asIScriptObject*)ptr)->GetObjectType();
if(p)
p->AddRef();
return p;
}
else {
return nullptr;
}
}
static bool implClass(asCObjectType* type, asCObjectType* impl) {
if(!impl)
return false;
if(impl->IsInterface())
return type->Implements(impl);
else
return type->DerivesFrom(impl);
}
static std::string modName(asIScriptModule* mod) {
return mod->GetName();
}
static std::string className(asITypeInfo* mod) {
return mod->GetName();
}
static unsigned modClassCount(asIScriptModule* mod) {
return mod->GetObjectTypeCount();
}
static asITypeInfo* modClass(asIScriptModule* mod, unsigned index) {
return mod->GetObjectTypeByIndex(index);
}
static asITypeInfo* modClassName(asIScriptModule* mod, const std::string& name) {
return mod->GetTypeInfoByName(name.c_str());
}
static asIScriptModule* getModule(const std::string& str) {
auto* man = getActiveManager();
if(!man)
return nullptr;
auto* mod = man->getModule(str.c_str());
if(!mod)
return nullptr;
return mod->module;
}
static asIScriptModule* thisModule() {
auto* ctx = asGetActiveContext();
if(ctx) {
auto* func = ctx->GetFunction();
if(func)
return func->GetModule();
}
return nullptr;
}
static asIScriptModule* clsModule(asCObjectType* type) {
return type->GetModule();
}
static int clsId(asCObjectType* type) {
return type->GetTypeId();
}
static unsigned clsMemberCount(asCObjectType* type) {
return type->GetPropertyCount();
}
static asITypeInfo* clsMember(asITypeInfo* type, unsigned index) {
if(index >= type->GetPropertyCount())
return nullptr;
int typeId = 0;
type->GetProperty(index, nullptr, &typeId);
auto* memType = type->GetEngine()->GetTypeInfoById(typeId);
if(memType)
memType->AddRef();
return memType;
}
static std::string clsMemberName(asITypeInfo* type, unsigned index) {
if(index >= type->GetPropertyCount())
return nullptr;
const char* name;
type->GetProperty(index, &name);
return std::string(name);
}
static bool objGC(asCObjectType* type) {
return type->GetFlags() & asOBJ_GC;
}
static asIScriptObject* objMember(asCObjectType* type, void* ptr, int typeId, unsigned index) {
if(ptr != nullptr && (typeId & asTYPEID_SCRIPTOBJECT)) {
auto* p = ((asIScriptObject*)ptr);
if(index >= p->GetPropertyCount())
return nullptr;
int memType = p->GetPropertyTypeId(index);
if(!(memType & asTYPEID_SCRIPTOBJECT))
return nullptr;
asIScriptObject* obj = (asIScriptObject*)p->GetAddressOfProperty(index);
if(memType & asTYPEID_OBJHANDLE)
obj = *(asIScriptObject**)obj;
if(obj)
obj->AddRef();
return obj;
}
else {
return nullptr;
}
}
static void getImplementing(CScriptArray* arr, asCObjectType* type) {
if(type == nullptr)
return;
auto* engine = type->GetEngine();
asUINT modCnt = engine->GetModuleCount();
for(asUINT n = 0; n < modCnt; ++n) {
auto* module = engine->GetModuleByIndex(n);
asUINT cnt = module->GetObjectTypeCount();
for(asUINT i = 0; i < cnt; ++i) {
auto* cls = module->GetObjectTypeByIndex(i);
if(type->IsInterface() ? cls->Implements(type) : cls->DerivesFrom(type))
arr->InsertLast(&cls);
}
}
}
static render::Sprite getGlobalSprite(const std::string& module, const std::string& name) {
render::Sprite sprt;
auto* man = getActiveManager();
if(man != nullptr) {
auto* type = man->engine->GetTypeInfoByName("Sprite");
auto* mod = man->getModule(module.c_str());
if(mod != nullptr && type != nullptr) {
int index = mod->module->GetGlobalVarIndexByDecl((std::string("const Sprite ")+name).c_str());
if(index < 0)
index = mod->module->GetGlobalVarIndexByDecl((std::string("Sprite ")+name).c_str());
if(index >= 0) {
int typeId = 0;
if(mod->module->GetGlobalVar(index, nullptr, nullptr, &typeId, nullptr) == asSUCCESS) {
if(typeId == type->GetTypeId()) {
sprt = *(render::Sprite*)mod->module->GetAddressOfGlobalVar(index);
}
}
}
}
}
return sprt;
}
static Color getGlobalColor(const std::string& module, const std::string& name) {
Color col;
auto* man = getActiveManager();
if(man != nullptr) {
auto* type = man->engine->GetTypeInfoByName("Color");
auto* mod = man->getModule(module.c_str());
if(mod != nullptr && type != nullptr) {
int index = mod->module->GetGlobalVarIndexByDecl((std::string("const Color ")+name).c_str());
if(index < 0)
index = mod->module->GetGlobalVarIndexByDecl((std::string("Color ")+name).c_str());
if(index >= 0) {
int typeId = 0;
if(mod->module->GetGlobalVar(index, nullptr, nullptr, &typeId, nullptr) == asSUCCESS) {
if(typeId == type->GetTypeId()) {
col = *(Color*)mod->module->GetAddressOfGlobalVar(index);
}
}
}
}
}
return col;
}
void RegisterScriptHooks() {
foreach(it, hooks) {
GenericCallDesc* type = *it;
std::string name = type->name + "Hook";
ClassBind cls(name.c_str(), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK, sizeof(void*));
cls.addConstructor("void f()", asFUNCTION(emptyHook));
cls.addConstructor(format("void f(const $1&)", name).c_str(), asFUNCTION(assignHook));
cls.addDestructor("void f()", asFUNCTION(dummy));
cls.addExternMethod("bool get_valid() const", asFUNCTION(validHook));
cls.addExternMethod(format("$1& opAssign(const $1&in)", name, name).c_str(), asFUNCTION(assignHook));
//Constructor for the class
int fid = getEngine()->RegisterObjectBehaviour(
cls.name.c_str(), asBEHAVE_CONSTRUCT, "void f(const string &in)",
asFUNCTION(getHook), asCALL_GENERIC);
if(fid > 0)
getEngine()->GetFunctionById(fid)->SetUserData((void*)type);
//Secondary bind
fid = getEngine()->RegisterObjectMethod(
cls.name.c_str(), "void bind(const string &in)",
asFUNCTION(getHook), asCALL_GENERIC);
if(fid > 0)
getEngine()->GetFunctionById(fid)->SetUserData((void*)type);
//Call bind
GenericCallDesc call = *type;
call.name = "call";
call.constFunction = true;
bindGeneric(cls, call, callHook, (void*)type);
}
//Class creation
InterfaceBind anyif("IAnyObject");
ClassBind classptr("AnyClass", asOBJ_REF);
classptr.setReferenceFuncs(asMETHOD(asITypeInfo, AddRef),
asMETHOD(asITypeInfo, Release));
classptr.addExternMethod("int get_id() const", asFUNCTION(clsId));
classptr.addExternMethod("string get_name() const", asFUNCTION(className));
classptr.addExternMethod("IAnyObject@ create() const", asFUNCTION(createClass));
classptr.addExternMethod("bool implements(const AnyClass@ check) const", asFUNCTION(implClass));
classptr.addExternMethod("uint get_memberCount() const", asFUNCTION(clsMemberCount));
classptr.addExternMethod("AnyClass@ get_members(uint index) const", asFUNCTION(clsMember));
classptr.addExternMethod("string get_memberName(uint index) const", asFUNCTION(clsMemberName));
classptr.addExternMethod("IAnyObject@ getMember(const ?&, uint index) const", asFUNCTION(objMember));
classptr.addExternMethod("bool get_isGC()", asFUNCTION(objGC));
bind("AnyClass@ getClass(const string&in spec)", asFUNCTION(getClass));
bind("AnyClass@ getClass(const ?&)", asFUNCTION(thisClass));
ClassBind modptr("ScriptModule", asOBJ_REF | asOBJ_NOCOUNT);
modptr.addExternMethod("string get_name() const", asFUNCTION(modName));
modptr.addExternMethod("uint get_classCount() const", asFUNCTION(modClassCount));
modptr.addExternMethod("AnyClass& get_classes(uint index) const", asFUNCTION(modClass));
modptr.addExternMethod("AnyClass& getClass(const string& name) const", asFUNCTION(modClassName));
classptr.addExternMethod("ScriptModule@ get_module() const", asFUNCTION(clsModule));
bind("ScriptModule@ getScriptModule(const string& name)", asFUNCTION(getModule));
bind("ScriptModule@ get_THIS_MODULE()", asFUNCTION(thisModule));
bind("void getClassesImplementing(array<AnyClass@>& results, AnyClass& base)", asFUNCTION(getImplementing));
bind("Sprite getGlobalSprite(const string& module, const string& name)", asFUNCTION(getGlobalSprite));
bind("Color getGlobalColor(const string& module, const string& name)", asFUNCTION(getGlobalColor));
}
};
+104
View File
@@ -0,0 +1,104 @@
#include "script_type.h"
#include "script_bind.h"
#include "main/logging.h"
#include "str_util.h"
namespace scripts {
ScriptType::ScriptType(const std::string& Name, int ID)
: id(ID), name(Name), manager(0), type(0) {
}
void ScriptType::bind(Manager* mng, const std::vector<std::string>& decls) {
manager = mng;
//Split into module and class name
std::vector<std::string> parts;
split(scriptClass, parts, "::");
if(parts.size() != 2) {
error("ERROR: Invalid type script type declaration '%s'.\n", scriptClass.c_str());
return;
}
//Find the object type
type = manager->getClass(parts[0].c_str(), parts[1].c_str());
if(!type) {
error("ERROR: Could not find script type '%s'.\n", scriptClass.c_str());
return;
}
//Find all the functions
functions.reserve(decls.size());
auto it = decls.begin(), end = decls.end();
for(; it != end; ++it) {
asIScriptFunction* func = type->GetMethodByDecl(it->c_str());
functions.push_back(func);
}
}
bool ScriptType::has(unsigned index) {
if(index >= functions.size())
return false;
return functions[index] != 0;
}
Call ScriptType::call(unsigned index) {
if(index >= functions.size() || functions[index] == 0)
return Call();
return manager->call(functions[index]);
}
void* ScriptType::create() {
if(!type)
return 0;
return manager->engine->CreateScriptObject(type);
}
ScriptTypes::~ScriptTypes() {
clear();
}
void ScriptTypes::clear() {
auto it = types.begin(), end = types.end();
for(; it != end; ++it)
delete *it;
types.clear();
}
int ScriptTypes::add(ScriptType* type) {
type->id = types.size();
types.push_back(type);
return type->id;
}
ScriptType* ScriptTypes::get(int id) {
if(id < 0 || (unsigned)id >= types.size())
return 0;
return types[id];
}
void ScriptTypes::registerEnum(std::string enumName, std::string prefix) {
EnumBind enm(enumName.c_str());
auto it = types.begin(), end = types.end();
for(; it != end; ++it) {
ScriptType& type = **it;
enm[prefix + type.name] = type.id;
}
}
void ScriptTypes::bind(Manager* manager, const std::vector<std::string>& decls) {
auto it = types.begin(), end = types.end();
for(; it != end; ++it)
(*it)->bind(manager, decls);
}
void ScriptTypes::each(std::function<void(ScriptType&)> func) {
auto it = types.begin(), end = types.end();
for(; it != end; ++it)
func(**it);
}
};
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "manager.h"
#include <string>
#include <unordered_map>
#include <functional>
namespace scripts {
//Generic type that can pre-cache function pointers for a type.
struct ScriptType {
Manager* manager;
asITypeInfo* type;
std::vector<asIScriptFunction*> functions;
int id;
std::string name;
std::string scriptClass;
ScriptType(const std::string& name, int id = -1);
void bind(Manager* manager, const std::vector<std::string>& functionDeclarations);
bool has(unsigned index);
Call call(unsigned index);
void* create();
};
//Container for script types that manages them
class ScriptTypes {
std::vector<ScriptType*> types;
public:
~ScriptTypes();
//Clear and delete all script types
void clear();
//Add a new script type to the database
// Returns the reserved id of the type
int add(ScriptType* type);
//Get the script type corresponding to the id
ScriptType* get(int id);
//Bind all added script types
void bind(Manager* manager, const std::vector<std::string>& functionDeclarations);
//Register the type enum to the active script engine
void registerEnum(std::string enumName, std::string prefix);
//Call a function on all types
void each(std::function<void(ScriptType&)> func);
};
};