#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(); } 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(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 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 static T wrapExplicitRead(net::Message& msg) { T value = T(); wrapRead(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 count = 0; map.iterateAll([&msg,&count](int64_t key, int64_t value) { msg << key; msg << value; count += 1; }); msg.fill(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 count = 0; map.handleDirty([&msg,&count](int64_t key, int64_t value) -> bool { msg << key; msg << value; count += 1; return true; }); msg.fill(numPos, count); } void RegisterEarlyNetworkBinds(asIScriptEngine* engine) { engine->RegisterObjectType("DataList", 0, asOBJ_REF | asOBJ_NOCOUNT); engine->RegisterObjectMethod("array", "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)); #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)); #define BIND_TYPE(stype, rtype)\ BIND_TYPE_SHONLY(stype, rtype)\ \ msg.addExternMethod(#stype " read_" #stype "()",\ asFUNCTION(wrapExplicitRead)); 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)); } } };