Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
#include "scripted_node.h"
|
||||
#include "angelscript.h"
|
||||
#include "scripts/manager.h"
|
||||
#include "scripts/script_components.h"
|
||||
#include "scripts/generic_call.h"
|
||||
#include "main/references.h"
|
||||
#include "main/logging.h"
|
||||
#include "util/format.h"
|
||||
#include "str_util.h"
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
#include "threads.h"
|
||||
#include "frustum.h"
|
||||
|
||||
std::unordered_map<std::string, scene::scriptNodeType*> nodeTypes;
|
||||
static unsigned nextScriptNodeID = 0;
|
||||
|
||||
namespace scene {
|
||||
scriptNodeType::scriptNodeType(const std::string& Name, const std::string& ident)
|
||||
: id(nextScriptNodeID++), name(Name), identifier(ident), factory(0), preRender(0), render(0) {}
|
||||
|
||||
void scriptNodeType::bind() {
|
||||
asITypeInfo* type = 0;
|
||||
|
||||
std::vector<std::string> parts;
|
||||
split(identifier, parts, "::");
|
||||
if(parts.size() == 2)
|
||||
type = devices.scripts.client->getClass(parts[0].c_str(),parts[1].c_str());
|
||||
|
||||
if(type) {
|
||||
factory = type->GetFactoryByDecl(format("$1@ $1(Node&)", type->GetName()).c_str());
|
||||
preRender = type->GetMethodByDecl("bool preRender(Node&)",false);
|
||||
render = type->GetMethodByDecl("void render(Node&)",false);
|
||||
|
||||
for(unsigned i = 0; i < methods.size(); ++i) {
|
||||
auto* method = methods[i];
|
||||
|
||||
auto descContaining = method->desc;
|
||||
descContaining.prepend(scripts::GT_Node_Ref);
|
||||
|
||||
method->func = type->GetMethodByDecl(descContaining.declaration().c_str(),false);
|
||||
if(method->func) {
|
||||
method->desc = descContaining;
|
||||
method->passContaining = true;
|
||||
}
|
||||
else {
|
||||
method->func = type->GetMethodByDecl(method->desc.declaration().c_str(),false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
error("'%s' was not a valid script class identifier", identifier.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ScriptedNode::ScriptedNode(scriptNodeType* nodeType) : scriptObject(0), errors(0), type(*nodeType) {
|
||||
if(asIScriptFunction* func = type.factory) {
|
||||
auto call = devices.scripts.client->call(func);
|
||||
call.push(this);
|
||||
call.call(scriptObject);
|
||||
if(scriptObject)
|
||||
scriptObject->AddRef();
|
||||
else
|
||||
throw "Failed to create script node";
|
||||
}
|
||||
else {
|
||||
throw "Script object has no acceptable factory";
|
||||
}
|
||||
}
|
||||
|
||||
ScriptedNode* ScriptedNode::create(const std::string& type) {
|
||||
try {
|
||||
auto entry = nodeTypes.find(type);
|
||||
if(entry != nodeTypes.end())
|
||||
return new ScriptedNode(entry->second);
|
||||
else
|
||||
error("Error creating script node '%s': No such node type", type.c_str());
|
||||
}
|
||||
catch(const char* err) {
|
||||
error("Error creating script node '%s': %s", type.c_str(), err);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScriptedNode::destroy() {
|
||||
if(scriptObject) {
|
||||
scriptObject->Release();
|
||||
scriptObject = 0;
|
||||
}
|
||||
|
||||
Node::destroy();
|
||||
}
|
||||
|
||||
ScriptedNode::~ScriptedNode() {
|
||||
if(scriptObject)
|
||||
scriptObject->Release();
|
||||
}
|
||||
|
||||
NodeType ScriptedNode::getType() const {
|
||||
return (NodeType)(NT_ScriptBase + type.id);
|
||||
}
|
||||
|
||||
bool ScriptedNode::preRender(render::RenderDriver& driver) {
|
||||
if(!scriptObject)
|
||||
return false;
|
||||
|
||||
sortDistance = abs_position.distanceTo(driver.cam_pos);
|
||||
|
||||
//Call scripted pre-render if available
|
||||
if(asIScriptFunction* func = type.preRender) {
|
||||
auto call = devices.scripts.client->call(func);
|
||||
call.setObject(scriptObject);
|
||||
call.push(this);
|
||||
|
||||
bool ret = false;
|
||||
if(!call.call(ret)) {
|
||||
if(++errors >= 3) {
|
||||
markForDeletion();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!visible)
|
||||
return false;
|
||||
if(!getFlag(NF_NoCulling) && !driver.getViewFrustum().overlaps(abs_position, abs_scale))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptedNode::render(render::RenderDriver& driver) {
|
||||
if(!scriptObject)
|
||||
return;
|
||||
if(asIScriptFunction* func = type.render) {
|
||||
auto call = devices.scripts.client->call(func);
|
||||
call.setObject(scriptObject);
|
||||
call.push(this);
|
||||
|
||||
if(!call.call()) {
|
||||
if(++errors >= 3) {
|
||||
markForDeletion();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadScriptNodeTypes(const std::string& filename) {
|
||||
std::ifstream file(filename);
|
||||
skipBOM(file);
|
||||
char name[80], script[80], bracket;
|
||||
|
||||
bool inType = false;
|
||||
scriptNodeType* type = 0;
|
||||
|
||||
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;
|
||||
|
||||
if(!inType) {
|
||||
int args = sscanf(parse.c_str(), "Node %79s from %79s {", name, script);
|
||||
if(args != 2) {
|
||||
error("Unrecognized line '%s'", line.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string typeName = name;
|
||||
|
||||
if(nodeTypes.find(typeName) != nodeTypes.end()) {
|
||||
error("Duplicate node type '%s'", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
type = new scriptNodeType(name, script);
|
||||
nodeTypes[name] = type;
|
||||
|
||||
inType = true;
|
||||
}
|
||||
else if(type) {
|
||||
int args = sscanf(parse.c_str(), " %c", &bracket);
|
||||
if(args == 1 && bracket == '}') {
|
||||
inType = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
parse = trim(parse);
|
||||
|
||||
if(parse[parse.size() - 1] == ':') {
|
||||
parse[parse.size() - 1] = ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
//Parse declaration
|
||||
scripts::GenericCallDesc desc;
|
||||
desc.parse(parse, true, false);
|
||||
|
||||
if(desc.returnType.type) {
|
||||
error("Error: '%s': node functions may not return a value.", parse.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
//Create method desc
|
||||
scripts::WrappedMethod* m = new scripts::WrappedMethod();
|
||||
m->index = (unsigned)type->methods.size();
|
||||
m->compId = 0;
|
||||
m->local = true;
|
||||
m->server = true;
|
||||
m->shadow = true;
|
||||
m->wrapped = desc;
|
||||
m->restricted = false;
|
||||
|
||||
m->desc.append(desc);
|
||||
m->desc.name = desc.name;
|
||||
m->desc.constFunction = false;
|
||||
m->desc.returnType = desc.returnType;
|
||||
m->desc.returnsArray = m->wrapped.returnsArray;
|
||||
|
||||
type->methods.push_back(m);
|
||||
}
|
||||
else {
|
||||
if(parse.find('}') != std::string::npos)
|
||||
inType = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bindScriptNodeTypes() {
|
||||
foreach(it, nodeTypes)
|
||||
it->second->bind();
|
||||
}
|
||||
|
||||
void clearScriptNodeTypes() {
|
||||
foreach(it, nodeTypes)
|
||||
delete it->second;
|
||||
nodeTypes.clear();
|
||||
nextScriptNodeID = 0;
|
||||
}
|
||||
|
||||
const char* getScriptNodeName(unsigned id) {
|
||||
foreach(it, nodeTypes)
|
||||
if(it->second->id == id)
|
||||
return it->first.c_str();
|
||||
return "";
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user