Files
2018-07-17 14:15:37 +02:00

849 lines
22 KiB
C++

#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));
}
}
};