BVB Source Codes

CRYENGINE Show ScriptRMI.cpp Source code

Return Download CRYENGINE: download ScriptRMI.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. /*************************************************************************
  4.    -------------------------------------------------------------------------
  5.    $Id$
  6.    $DateTime$
  7.    Description: Provides remote method invocation to script
  8.  
  9.    -------------------------------------------------------------------------
  10.    History:
  11.    - 30:11:2004   11:30 : Created by Craig Tiller
  12.  
  13. *************************************************************************/
  14. #include "StdAfx.h"
  15. #include "ScriptRMI.h"
  16. #include "ScriptSerialize.h"
  17. #include "GameContext.h"
  18. #include "GameClientNub.h"
  19. #include "GameServerNub.h"
  20. #include "GameClientChannel.h"
  21. #include "GameServerChannel.h"
  22. #include "CryAction.h"
  23. #include <CrySystem/IConsole.h>
  24.  
  25. #define FLAGS_FIELD           "__sendto"
  26. #define ID_FIELD              "__id"
  27. #define CLIENT_DISPATCH_FIELD "__client_dispatch"
  28. #define SERVER_DISPATCH_FIELD "__server_dispatch"
  29. #define VALIDATED_FIELD       "__validated"
  30. #define SERIALIZE_FUNCTION    "__serialize"
  31. #define SERVER_SYNCHED_FIELD  "__synched"
  32. #define HIDDEN_FIELD          "__hidden"
  33.  
  34. static ICVar* pLogRMIEvents = NULL;
  35. static ICVar* pDisconnectOnError = NULL;
  36.  
  37. CScriptRMI* CScriptRMI::m_pThis = NULL;
  38.  
  39. // MUST be plain-old-data (no destructor called)
  40. struct CScriptRMI::SSynchedPropertyInfo
  41. {
  42.         char                 name[MaxSynchedPropertyNameLength + 1];
  43.         EScriptSerializeType type;
  44. };
  45.  
  46. // MUST be plain-old-data (see CScriptTable::AddFunction)
  47. struct CScriptRMI::SFunctionInfo
  48. {
  49.         char                format[MaxRMIParameters + 1];
  50.         ENetReliabilityType reliability;
  51.         ERMIAttachmentType  attachment;
  52.         uint8               funcID;
  53. };
  54.  
  55. struct SSerializeFunctionParams
  56. {
  57.         SSerializeFunctionParams(TSerialize s, IEntity* p) : ser(s), pEntity(p) {}
  58.         TSerialize ser;
  59.         IEntity*   pEntity;
  60. };
  61.  
  62. // this class implements the body of a remote method invocation message
  63. class CScriptRMI::CScriptMessage : public IRMIMessageBody, public CScriptRMISerialize
  64. {
  65. public:
  66.         CScriptMessage(
  67.           ENetReliabilityType reliability,
  68.           ERMIAttachmentType attachment,
  69.           EntityId objId,
  70.           uint8 funcId,
  71.           const char* fmt) :
  72.                 IRMIMessageBody(reliability, attachment, objId, funcId, NULL, 0, 0),
  73.                 CScriptRMISerialize(fmt),
  74.                 m_refs(0)
  75.         {
  76.         }
  77.  
  78.         void SerializeWith(TSerialize pSerialize)
  79.         {
  80.                 CScriptRMISerialize::SerializeWith(pSerialize);
  81.         }
  82.  
  83.         int8 GetRefs()
  84.         {
  85.                 return m_refs;
  86.         }
  87.  
  88.         size_t GetSize()
  89.         {
  90.                 return sizeof(*this);
  91.         }
  92.  
  93. #if ENABLE_RMI_BENCHMARK
  94.         virtual const SRMIBenchmarkParams* GetRMIBenchmarkParams()
  95.         {
  96.                 return NULL;
  97.         }
  98. #endif
  99.  
  100. private:
  101.         int8 m_refs;
  102. };
  103.  
  104. CScriptRMI::CScriptRMI() :
  105.         m_pParent(0)
  106. {
  107.         m_pThis = this;
  108. }
  109.  
  110. bool CScriptRMI::Init()
  111. {
  112.         /*
  113.            CryAutoCriticalSection lkDispatch(m_dispatchMutex);
  114.  
  115.            IScriptSystem * pSS = gEnv->pScriptSystem;
  116.            IEntityClassRegistry * pReg = gEnv->pEntitySystem->GetClassRegistry();
  117.            IEntityClass * pClass = NULL;
  118.            for (pReg->IteratorMoveFirst(); pClass = pReg->IteratorNext();)
  119.            {
  120.            pClass->LoadScript(false);
  121.            if (IEntityScript * pEntityScript = pClass->GetIEntityScript())
  122.            {
  123.             if (m_entityClassToEntityTypeID.find(pClass->GetName()) != m_entityClassToEntityTypeID.end())
  124.             {
  125.               SmartScriptTable entityTable;
  126.               pSS->GetGlobalValue( pClass->GetName(), entityTable );
  127.               if (!!entityTable)
  128.               {
  129.                 //ValidateDispatchTable( pClass->GetName(), , , false );
  130.                 //ValidateDispatchTable( pClass->GetName(), , , true );
  131.               }
  132.             }
  133.            }
  134.            }
  135.          */
  136.         return true;
  137. }
  138.  
  139. void CScriptRMI::UnloadLevel()
  140. {
  141.         CryAutoLock<CryCriticalSection> lock(m_dispatchMutex);
  142.         m_entities.clear();
  143.         m_entityClassToEntityTypeID.clear();
  144.  
  145.         const size_t numDispatches = m_dispatch.size();
  146.         for (size_t i = 0; i < numDispatches; ++i)
  147.         {
  148.                 SDispatch& dispatch = m_dispatch[i];
  149.                 if (dispatch.m_serverDispatchScriptTable)
  150.                 {
  151.                         dispatch.m_serverDispatchScriptTable->SetValue(VALIDATED_FIELD, false);
  152.                 }
  153.                 if (dispatch.m_clientDispatchScriptTable)
  154.                 {
  155.                         dispatch.m_clientDispatchScriptTable->SetValue(VALIDATED_FIELD, false);
  156.                 }
  157.         }
  158.  
  159.         stl::free_container(m_dispatch);
  160. }
  161.  
  162. void CScriptRMI::SetContext(CGameContext* pContext)
  163. {
  164.         m_pParent = pContext;
  165. }
  166.  
  167. void CScriptRMI::RegisterCVars()
  168. {
  169.         pLogRMIEvents = REGISTER_INT(
  170.           "net_log_remote_methods", 0, VF_DUMPTODISK,
  171.           "Log remote method invocations");
  172.         pDisconnectOnError = REGISTER_INT(
  173.           "net_disconnect_on_rmi_error", 0, VF_DUMPTODISK,
  174.           "Disconnect remote connections on script exceptions during RMI calls");
  175. }
  176.  
  177. void CScriptRMI::UnregisterCVars()
  178. {
  179.         SAFE_RELEASE(pLogRMIEvents);
  180.         SAFE_RELEASE(pDisconnectOnError);
  181. }
  182.  
  183. // implementation of Net.Expose() - exposes a class
  184. int CScriptRMI::ExposeClass(IFunctionHandler* pFH)
  185. {
  186.         SmartScriptTable params, cls, clientMethods, serverMethods;
  187.         SmartScriptTable clientTable, serverTable;
  188.         SmartScriptTable serverProperties;
  189.  
  190.         IScriptSystem* pSS = pFH->GetIScriptSystem();
  191.  
  192.         if (pFH->GetParamCount() != 1)
  193.         {
  194.                 pSS->RaiseError("Net.Expose takes only one parameter - a table");
  195.                 return pFH->EndFunction(false);
  196.         }
  197.  
  198.         if (!pFH->GetParam(1, params))
  199.         {
  200.                 pSS->RaiseError("Net.Expose takes only one parameter - a table");
  201.                 return pFH->EndFunction(false);
  202.         }
  203.         if (!params->GetValue("Class", cls))
  204.         {
  205.                 pSS->RaiseError("No 'Class' parameter to Net.Expose");
  206.                 return pFH->EndFunction(false);
  207.         }
  208.  
  209.         if (!params->GetValue("ClientMethods", clientMethods))
  210.         {
  211.                 GameWarning("No 'ClientMethods' parameter to Net.Expose");
  212.         }
  213.         else if (!cls->GetValue("Client", clientTable))
  214.         {
  215.                 pSS->RaiseError("'ClientMethods' exposed, but no 'Client' table in class");
  216.                 return pFH->EndFunction(false);
  217.         }
  218.         if (!params->GetValue("ServerMethods", serverMethods))
  219.         {
  220.                 GameWarning("No 'ServerMethods' parameter to Net.Expose");
  221.         }
  222.         else if (!cls->GetValue("Server", serverTable))
  223.         {
  224.                 pSS->RaiseError("'ServerMethods' exposed, but no 'Server' table in class");
  225.                 return pFH->EndFunction(false);
  226.         }
  227.         params->GetValue("ServerProperties", serverProperties);
  228.  
  229.         if (clientMethods.GetPtr())
  230.         {
  231.                 CRY_ASSERT(clientTable.GetPtr());
  232.                 if (!BuildDispatchTable(clientMethods, clientTable, cls, CLIENT_DISPATCH_FIELD))
  233.                         return pFH->EndFunction(false);
  234.         }
  235.  
  236.         if (serverMethods.GetPtr())
  237.         {
  238.                 CRY_ASSERT(serverTable.GetPtr());
  239.                 if (!BuildDispatchTable(serverMethods, serverTable, cls, SERVER_DISPATCH_FIELD))
  240.                         return pFH->EndFunction(false);
  241.         }
  242.  
  243.         if (serverProperties.GetPtr())
  244.         {
  245.                 if (!BuildSynchTable(serverProperties, cls, SERVER_SYNCHED_FIELD))
  246.                         return pFH->EndFunction(false);
  247.         }
  248.  
  249.         return pFH->EndFunction(true);
  250. }
  251.  
  252. // build a script dispatch table - this table is the metatable for all
  253. // dispatch proxy tables used (onClient, allClients, otherClients)
  254. bool CScriptRMI::BuildDispatchTable(
  255.   SmartScriptTable methods,
  256.   SmartScriptTable classMethodTable,
  257.   SmartScriptTable cls,
  258.   const char* name)
  259. {
  260.         IScriptSystem* pSS = methods->GetScriptSystem();
  261.         SmartScriptTable dispatch(pSS);
  262.  
  263.         uint8 funcID = 0;
  264.  
  265.         IScriptTable::SUserFunctionDesc fd;
  266.         SFunctionInfo info;
  267.  
  268.         IScriptTable::Iterator iter = methods->BeginIteration();
  269.         while (methods->MoveNext(iter))
  270.         {
  271.                 if (iter.sKey)
  272.                 {
  273.                         const char* function = iter.sKey;
  274.  
  275.                         if (strlen(function) >= 2 && function[0] == '_' && function[1] == '_')
  276.                         {
  277.                                 methods->EndIteration(iter);
  278.                                 pSS->RaiseError("In Net.Expose: can't expose functions beginning with '__' (function was %s)",
  279.                                                 function);
  280.                                 return false;
  281.                         }
  282.  
  283.                         SmartScriptTable specTable;
  284.                         if (!methods->GetValue(function, specTable))
  285.                         {
  286.                                 methods->EndIteration(iter);
  287.                                 pSS->RaiseError("In Net.Expose: function %s entry is not a table (in %s)",
  288.                                                 function, name);
  289.                                 return false;
  290.                         }
  291.  
  292.                         // fetch format
  293.                         int count = specTable->Count();
  294.                         if (count < 1)
  295.                         {
  296.                                 methods->EndIteration(iter);
  297.                                 pSS->RaiseError("In Net.Expose: function %s entry is an empty table (in %s)",
  298.                                                 function, name);
  299.                                 return false;
  300.                         }
  301.                         else if (count - 1 > MaxRMIParameters)
  302.                         {
  303.                                 methods->EndIteration(iter);
  304.                                 pSS->RaiseError("In Net.Expose: function %s has too many parameters (%d) (in %s)",
  305.                                                 function, count - 1, name);
  306.                                 return false;
  307.                         }
  308.                         int tempReliability;
  309.                         if (!specTable->GetAt(1, tempReliability) || tempReliability < 0 || tempReliability >= eNRT_NumReliabilityTypes)
  310.                         {
  311.                                 methods->EndIteration(iter);
  312.                                 pSS->RaiseError("In Net.Expose: function %s has invalid reliability type %d (in %s)",
  313.                                                 function, tempReliability, name);
  314.                                 return false;
  315.                         }
  316.                         ENetReliabilityType reliability = (ENetReliabilityType) tempReliability;
  317.                         if (!specTable->GetAt(2, tempReliability) || tempReliability < 0 || tempReliability >= eRAT_NumAttachmentTypes)
  318.                         {
  319.                                 methods->EndIteration(iter);
  320.                                 pSS->RaiseError("In Net.Expose: function %s has invalid attachment type %d (in %s)",
  321.                                                 function, tempReliability, name);
  322.                         }
  323.                         ERMIAttachmentType attachment = (ERMIAttachmentType) tempReliability;
  324.                         string format;
  325.                         format.reserve(count - 1);
  326.                         for (int i = 3; i <= count; i++)
  327.                         {
  328.                                 int type = 666;
  329.                                 if (!specTable->GetAt(i, type) || type < -128 || type > 127)
  330.                                 {
  331.                                         methods->EndIteration(iter);
  332.                                         pSS->RaiseError("In Net.Expose: function %s has invalid serialization policy %d at %d (in %s)",
  333.                                                         function, type, i, name);
  334.                                         return false;
  335.                                 }
  336.                                 format.push_back((char) type);
  337.                         }
  338.  
  339.                         CRY_ASSERT(format.length() <= MaxRMIParameters);
  340.                         cry_strcpy(info.format, format.c_str());
  341.                         info.funcID = funcID;
  342.                         info.reliability = reliability;
  343.                         info.attachment = attachment;
  344.  
  345.                         fd.pUserDataFunc = ProxyFunction;
  346.                         fd.sFunctionName = function;
  347.                         fd.nDataSize = sizeof(SFunctionInfo);
  348.                         fd.pDataBuffer = &info;
  349.                         fd.sGlobalName = "<net-dispatch>";
  350.                         fd.sFunctionParams = "(...)";
  351.  
  352.                         dispatch->AddFunction(fd);
  353.  
  354.                         string lookupData = function;
  355.                         lookupData += ":";
  356.                         lookupData += format;
  357.                         dispatch->SetAt(funcID + 1, lookupData.c_str());
  358.  
  359.                         funcID++;
  360.                         if (funcID == 0)
  361.                         {
  362.                                 funcID--;
  363.                                 methods->EndIteration(iter);
  364.                                 pSS->RaiseError("Too many functions... max is %d", funcID);
  365.                                 return false;
  366.                         }
  367.                 }
  368.                 else
  369.                 {
  370.                         GameWarning("In Net.Expose: non-string key ignored");
  371.                 }
  372.         }
  373.         methods->EndIteration(iter);
  374.  
  375.         dispatch->SetValue(VALIDATED_FIELD, false);
  376.         cls->SetValue(name, dispatch);
  377.  
  378.         return true;
  379. }
  380.  
  381. // setup the meta-table for synched variables
  382. bool CScriptRMI::BuildSynchTable(SmartScriptTable vars, SmartScriptTable cls, const char* name)
  383. {
  384.         IScriptSystem* pSS = vars->GetScriptSystem();
  385.         SmartScriptTable synched(pSS);
  386.         SmartScriptTable defaultValues(pSS);
  387.  
  388.         // TODO: Improve
  389.         IScriptTable::SUserFunctionDesc fd;
  390.         fd.pFunctor = functor_ret(SynchedNewIndexFunction);
  391.         fd.sFunctionName = "__newindex";
  392.         fd.sGlobalName = "<net-dispatch>";
  393.         fd.sFunctionParams = "(...)";
  394.         synched->AddFunction(fd);
  395.  
  396.         std::vector<SSynchedPropertyInfo> properties;
  397.  
  398.         IScriptTable::Iterator iter = vars->BeginIteration();
  399.         while (vars->MoveNext(iter))
  400.         {
  401.                 if (iter.sKey)
  402.                 {
  403.                         int type;
  404.                         if (!vars->GetValue(iter.sKey, type))
  405.                         {
  406.                                 vars->EndIteration(iter);
  407.                                 pSS->RaiseError("No type for %s", iter.sKey);
  408.                                 return false;
  409.                         }
  410.                         size_t len = strlen(iter.sKey);
  411.                         if (len > MaxSynchedPropertyNameLength)
  412.                         {
  413.                                 vars->EndIteration(iter);
  414.                                 pSS->RaiseError("Synched var name '%s' too long (max is %d)",
  415.                                                 iter.sKey, (int)MaxSynchedPropertyNameLength);
  416.                                 return false;
  417.                         }
  418.                         SSynchedPropertyInfo info;
  419.                         cry_strcpy(info.name, iter.sKey);
  420.                         info.type = (EScriptSerializeType) type;
  421.                         properties.push_back(info);
  422.  
  423.                         if (info.type == eSST_String)
  424.                                 defaultValues->SetValue(iter.sKey, "");
  425.                         else
  426.                                 defaultValues->SetValue(iter.sKey, 0);
  427.                 }
  428.         }
  429.         vars->EndIteration(iter);
  430.  
  431.         if (properties.empty())
  432.                 return true;
  433.  
  434.         fd.pFunctor = NULL;
  435.         fd.pUserDataFunc = SerializeFunction;
  436.         fd.nDataSize = sizeof(SSynchedPropertyInfo) * properties.size();
  437.         fd.pDataBuffer = &properties[0];
  438.         fd.sFunctionName = SERIALIZE_FUNCTION;
  439.         fd.sFunctionParams = "(...)";
  440.         fd.sGlobalName = "<net-dispatch>";
  441.         synched->AddFunction(fd);
  442.  
  443.         cls->SetValue(SERVER_SYNCHED_FIELD, synched);
  444.         cls->SetValue("synched", defaultValues);
  445.  
  446.         return true;
  447. }
  448.  
  449. // initialize an entity for networked methods
  450. void CScriptRMI::SetupEntity(EntityId eid, IEntity* pEntity, bool client, bool server)
  451. {
  452.         if (!m_pParent)
  453.         {
  454.                 GameWarning("Trying to setup an entity for network with no game started... failing");
  455.                 return;
  456.         }
  457.  
  458.         IEntityClass* pClass = pEntity->GetClass();
  459.         stack_string className = pClass->GetName();
  460.  
  461.         IScriptTable* pEntityTable = pEntity->GetScriptTable();
  462.         IScriptSystem* pSS = pEntityTable->GetScriptSystem();
  463.  
  464.         SmartScriptTable clientDispatchTable, serverDispatchTable, serverSynchedTable;
  465.         pEntityTable->GetValue(CLIENT_DISPATCH_FIELD, clientDispatchTable);
  466.         pEntityTable->GetValue(SERVER_DISPATCH_FIELD, serverDispatchTable);
  467.         pEntityTable->GetValue(SERVER_SYNCHED_FIELD, serverSynchedTable);
  468.  
  469.         bool validated;
  470.         if (clientDispatchTable.GetPtr())
  471.         {
  472.                 if (!clientDispatchTable->GetValue(VALIDATED_FIELD, validated))
  473.                         return;
  474.                 if (!validated)
  475.                 {
  476.                         SmartScriptTable methods;
  477.                         if (!pEntityTable->GetValue("Client", methods))
  478.                         {
  479.                                 GameWarning("No Client table, but has a client dispatch on class %s",
  480.                                             pEntity->GetClass()->GetName());
  481.                                 return;
  482.                         }
  483.                         if (!ValidateDispatchTable(pEntity->GetClass()->GetName(), clientDispatchTable, methods, false))
  484.                                 return;
  485.                 }
  486.         }
  487.         if (serverDispatchTable.GetPtr())
  488.         {
  489.                 if (!serverDispatchTable->GetValue(VALIDATED_FIELD, validated))
  490.                         return;
  491.                 if (!validated)
  492.                 {
  493.                         SmartScriptTable methods;
  494.                         if (!pEntityTable->GetValue("Server", methods))
  495.                         {
  496.                                 GameWarning("No Server table, but has a server dispatch on class %s",
  497.                                             pEntity->GetClass()->GetName());
  498.                                 return;
  499.                         }
  500.                         if (!ValidateDispatchTable(pEntity->GetClass()->GetName(), serverDispatchTable, methods, true))
  501.                                 return;
  502.                 }
  503.         }
  504.  
  505.         ScriptHandle id;
  506.         id.n = eid;
  507.  
  508.         ScriptHandle flags;
  509.  
  510.         if (client && serverDispatchTable.GetPtr())
  511.         {
  512.                 flags.n = eDF_ToServer;
  513.                 AddProxyTable(pEntityTable, id, flags, "server", serverDispatchTable);
  514.         }
  515.  
  516.         if (server && clientDispatchTable.GetPtr())
  517.         {
  518.                 // only expose ownClient, otherClients for actors with a channelId
  519.                 flags.n = eDF_ToClientOnChannel;
  520.                 AddProxyTable(pEntityTable, id, flags, "onClient", clientDispatchTable);
  521.                 flags.n = eDF_ToClientOnOtherChannels;
  522.                 AddProxyTable(pEntityTable, id, flags, "otherClients", clientDispatchTable);
  523.                 flags.n = eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels;
  524.                 AddProxyTable(pEntityTable, id, flags, "allClients", clientDispatchTable);
  525.         }
  526.  
  527.         if (serverSynchedTable.GetPtr())
  528.         {
  529.                 AddSynchedTable(pEntityTable, id, "synched", serverSynchedTable);
  530.         }
  531.  
  532.         CryAutoCriticalSection lkDispatch(m_dispatchMutex);
  533.         std::map<string, size_t>::iterator iter = m_entityClassToEntityTypeID.find(CONST_TEMP_STRING(pEntity->GetClass()->GetName()));
  534.         if (iter == m_entityClassToEntityTypeID.end())
  535.         {
  536.                 //[Timur] commented out as spam.
  537.                 //GameWarning("[scriptrmi] unable to find class %s", pEntity->GetClass()->GetName());
  538.         }
  539.         else
  540.                 m_entities[eid] = iter->second;
  541. }
  542.  
  543. void CScriptRMI::RemoveEntity(EntityId id)
  544. {
  545.         CryAutoCriticalSection lkDispatch(m_dispatchMutex);
  546.         m_entities.erase(id);
  547. }
  548.  
  549. bool CScriptRMI::SerializeScript(TSerialize ser, IEntity* pEntity)
  550. {
  551.         SSerializeFunctionParams p(ser, pEntity);
  552.         ScriptHandle hdl(&p);
  553.         IScriptTable* pTable = pEntity->GetScriptTable();
  554.         if (pTable)
  555.         {
  556.                 SmartScriptTable serTable;
  557.                 SmartScriptTable synchedTable;
  558.                 pTable->GetValue("synched", synchedTable);
  559.                 if (synchedTable.GetPtr())
  560.                 {
  561.                         synchedTable->GetValue(HIDDEN_FIELD, serTable);
  562.                         if (serTable.GetPtr())
  563.                         {
  564.                                 IScriptSystem* pScriptSystem = pTable->GetScriptSystem();
  565.                                 pScriptSystem->BeginCall(serTable.GetPtr(), SERIALIZE_FUNCTION);
  566.                                 pScriptSystem->PushFuncParam(serTable);
  567.                                 pScriptSystem->PushFuncParam(hdl);
  568.                                 return pScriptSystem->EndCall();
  569.                         }
  570.                 }
  571.         }
  572.         return true;
  573. }
  574.  
  575. int CScriptRMI::SerializeFunction(IFunctionHandler* pH, void* pBuffer, int nSize)
  576. {
  577.         SmartScriptTable serTable;
  578.         ScriptHandle hdl;
  579.         pH->GetParam(1, serTable);
  580.         pH->GetParam(2, hdl);
  581.         SSerializeFunctionParams* p = (SSerializeFunctionParams*) hdl.ptr;
  582.  
  583.         SSynchedPropertyInfo* pInfo = (SSynchedPropertyInfo*) pBuffer;
  584.         int nProperties = nSize / sizeof(SSynchedPropertyInfo);
  585.  
  586.         if (p->ser.IsReading())
  587.         {
  588.                 for (int i = 0; i < nProperties; i++)
  589.                         if (!m_pThis->ReadValue(pInfo[i].name, pInfo[i].type, p->ser, serTable.GetPtr()))
  590.                                 return pH->EndFunction(false);
  591.         }
  592.         else
  593.         {
  594.                 for (int i = 0; i < nProperties; i++)
  595.                         if (!m_pThis->WriteValue(pInfo[i].name, pInfo[i].type, p->ser, serTable.GetPtr()))
  596.                                 return pH->EndFunction(false);
  597.         }
  598.         return pH->EndFunction(true);
  599. }
  600.  
  601. // one-time validation of entity tables
  602. bool CScriptRMI::ValidateDispatchTable(const char* clazz, SmartScriptTable dispatch, SmartScriptTable methods, bool bServerTable)
  603. {
  604.         CryAutoCriticalSection lkDispatch(m_dispatchMutex);
  605.  
  606.         std::map<string, size_t>::iterator iterN = m_entityClassToEntityTypeID.lower_bound(clazz);
  607.         if (iterN == m_entityClassToEntityTypeID.end() || iterN->first != clazz)
  608.         {
  609.                 iterN = m_entityClassToEntityTypeID.insert(iterN, std::make_pair(clazz, m_entityClassToEntityTypeID.size()));
  610.                 CRY_ASSERT(iterN->second == m_dispatch.size());
  611.                 m_dispatch.push_back(SDispatch());
  612.         }
  613.         SDispatch& dispatchTblCont = m_dispatch[iterN->second];
  614.         SFunctionDispatchTable& dispatchTbl = bServerTable ? dispatchTblCont.server : dispatchTblCont.client;
  615.  
  616.         IScriptTable::Iterator iter = dispatch->BeginIteration();
  617.         while (dispatch->MoveNext(iter))
  618.         {
  619.                 if (iter.sKey)
  620.                 {
  621.                         if (iter.sKey[0] == '_' && iter.sKey[1] == '_')
  622.                                 continue;
  623.  
  624.                         ScriptVarType type = methods->GetValueType(iter.sKey);
  625.                         if (type != svtFunction)
  626.                         {
  627.                                 GameWarning("In class %s: function %s is exposed but not defined",
  628.                                             clazz, iter.sKey);
  629.                                 dispatch->EndIteration(iter);
  630.                                 return false;
  631.                         }
  632.                 }
  633.                 else
  634.                 {
  635.                         int id = iter.nKey;
  636.                         CRY_ASSERT(id > 0);
  637.                         id--;
  638.  
  639.                         if (id >= dispatchTbl.size())
  640.                                 dispatchTbl.resize(id + 1);
  641.                         SFunctionDispatch& dt = dispatchTbl[id];
  642.                         if (iter.value.GetVarType() != svtString)
  643.                         {
  644.                                 GameWarning("Expected a string in dispatch table, got type %d", iter.value.GetVarType());
  645.                                 dispatch->EndIteration(iter);
  646.                                 return false;
  647.                         }
  648.  
  649.                         const char* funcData = iter.value.str;
  650.                         const char* colon = strchr(funcData, ':');
  651.                         if (colon == NULL)
  652.                         {
  653.                                 dispatch->EndIteration(iter);
  654.                                 return false;
  655.                         }
  656.                         if (colon - funcData > MaxSynchedPropertyNameLength)
  657.                         {
  658.                                 dispatch->EndIteration(iter);
  659.                                 return false;
  660.                         }
  661.                         memcpy(dt.name, funcData, colon - funcData);
  662.                         dt.name[colon - funcData] = 0;
  663.                         cry_strcpy(dt.format, colon + 1);
  664.                 }
  665.         }
  666.         dispatch->EndIteration(iter);
  667.         dispatch->SetValue(VALIDATED_FIELD, true);
  668.  
  669.         if (bServerTable)
  670.         {
  671.                 dispatchTblCont.m_serverDispatchScriptTable = dispatch;
  672.         }
  673.         else
  674.         {
  675.                 dispatchTblCont.m_clientDispatchScriptTable = dispatch;
  676.         }
  677.  
  678.         return true;
  679. }
  680.  
  681. class CScriptRMI::CCallHelper
  682. {
  683. public:
  684.         CCallHelper()
  685.                 : m_pScriptSystem(nullptr)
  686.                 , m_pEntitySystem(nullptr)
  687.                 , m_pEntity(nullptr)
  688.                 , m_format(nullptr)
  689.         {
  690.                 m_scriptTable = 0;
  691.                 m_function[0] = 0;
  692.  
  693.         }
  694.  
  695.         bool Prologue(EntityId objID, bool bClient, uint8 funcID)
  696.         {
  697.                 if (!InitGameMembers(objID))
  698.                         return false;
  699.  
  700.                 SmartScriptTable dispatchTable;
  701.                 if (!m_scriptTable->GetValue(bClient ? CLIENT_DISPATCH_FIELD : SERVER_DISPATCH_FIELD, dispatchTable))
  702.                         return false;
  703.  
  704.                 const char* funcData;
  705.                 if (!dispatchTable->GetAt(funcID + 1, funcData))
  706.                 {
  707.                         GameWarning("No such function dispatch index %d on entity %s (class %s)", funcID,
  708.                                     m_pEntity->GetName(), m_pEntity->GetClass()->GetName());
  709.                         return false;
  710.                 }
  711.  
  712.                 const char* colon = strchr(funcData, ':');
  713.                 if (colon == NULL)
  714.                         return false;
  715.                 if (colon - funcData > BUFFER)
  716.                         return false;
  717.                 memcpy(m_function, funcData, colon - funcData);
  718.                 m_function[colon - funcData] = 0;
  719.                 m_format = colon + 1;
  720.                 return true;
  721.         }
  722.  
  723.         void Prologue(const char* function, const char* format)
  724.         {
  725.                 cry_strcpy(m_function, function);
  726.                 m_format = format;
  727.         }
  728.  
  729.         const char* GetFormat() const { return m_format; }
  730.  
  731.         bool        PerformCall(EntityId objID, CScriptRMISerialize* pSer, bool bClient, INetContext* pNetContext, INetChannel* pChannel)
  732.         {
  733.                 if (!InitGameMembers(objID))
  734.                         return false;
  735.  
  736.                 SmartScriptTable callTable;
  737.                 if (!m_scriptTable->GetValue(bClient ? "Client" : "Server", callTable))
  738.                         return false;
  739.  
  740.                 if (!bClient)
  741.                         pNetContext->LogRMI(m_function, pSer);
  742.  
  743.                 m_pScriptSystem->BeginCall(callTable, m_function);
  744.                 m_pScriptSystem->PushFuncParam(m_scriptTable);
  745.                 pSer->PushFunctionParams(m_pScriptSystem);
  746.                 // channel
  747.                 IGameChannel* pIGameChannel = pChannel->GetGameChannel();
  748.                 CGameServerChannel* pGameServerChannel = (CGameServerChannel*) pIGameChannel;
  749.                 uint16 channelId = pGameServerChannel->GetChannelId();
  750.                 m_pScriptSystem->PushFuncParam(channelId);
  751.                 //
  752.                 return m_pScriptSystem->EndCall();
  753.         }
  754.  
  755. private:
  756.         bool InitGameMembers(EntityId id)
  757.         {
  758.                 if (!m_pScriptSystem)
  759.                 {
  760.                         ISystem* pSystem = GetISystem();
  761.                         m_pScriptSystem = pSystem->GetIScriptSystem();
  762.                         m_pEntitySystem = gEnv->pEntitySystem;
  763.  
  764.                         IEntity* pEntity = m_pEntitySystem->GetEntity(id);
  765.                         if (!pEntity)
  766.                                 return false;
  767.  
  768.                         m_scriptTable = pEntity->GetScriptTable();
  769.                         if (!m_scriptTable.GetPtr())
  770.                                 return false;
  771.                 }
  772.                 return true;
  773.         }
  774.  
  775.         IScriptSystem*      m_pScriptSystem;
  776.         IEntitySystem*      m_pEntitySystem;
  777.         IEntity*            m_pEntity;
  778.         SmartScriptTable    m_scriptTable;
  779.  
  780.         static const size_t BUFFER = 256;
  781.         char                m_function[BUFFER];
  782.         const char*         m_format;
  783. };
  784.  
  785. // handle a call
  786. INetAtSyncItem* CScriptRMI::HandleRMI(bool bClient, EntityId objID, uint8 funcID, TSerialize ser, INetChannel* pChannel)
  787. {
  788.         if (!m_pParent)
  789.         {
  790.                 GameWarning("Trying to handle a remote method invocation with no game started... failing");
  791.                 return NULL;
  792.         }
  793.  
  794.         CryAutoCriticalSection lkDispatch(m_dispatchMutex);
  795.  
  796.         class CHandleAnRMI : public INetAtSyncItem, private CCallHelper, private CScriptRMISerialize
  797.         {
  798.         public:
  799.                 CHandleAnRMI(bool bClient, EntityId objID, const SFunctionDispatch& info, TSerialize ser, CGameContext* pContext, INetChannel* pChannel) : CScriptRMISerialize(info.format), m_bClient(bClient), m_pContext(pContext), m_objID(objID), m_pChannel(pChannel)
  800.                 {
  801.                         Prologue(info.name, info.format);
  802.                         SerializeWith(ser);
  803.                 }
  804.  
  805.                 bool Sync()
  806.                 {
  807.                         bool bDisconnect = PerformCall(m_objID, this, m_bClient, m_pContext->GetNetContext(), m_pChannel);
  808.                         if (bDisconnect && pDisconnectOnError && pDisconnectOnError->GetIVal() == 0)
  809.                                 bDisconnect = false;
  810.                         return !bDisconnect;
  811.                 }
  812.  
  813.                 bool SyncWithError(EDisconnectionCause& disconnectCause, string& disconnectMessage)
  814.                 {
  815.                         return Sync();
  816.                 }
  817.  
  818.                 void DeleteThis()
  819.                 {
  820.                         delete this;
  821.                 }
  822.  
  823.         private:
  824.                 std::unique_ptr<CScriptRMISerialize> m_pSer;
  825.                 bool m_bClient;
  826.                 CGameContext*                        m_pContext;
  827.                 EntityId                             m_objID;
  828.                 INetChannel*                         m_pChannel;
  829.         };
  830.  
  831.         // fetch the class of entity
  832.         std::map<EntityId, size_t>::const_iterator iterEnt = m_entities.find(objID);
  833.         if (iterEnt == m_entities.end())
  834.                 return 0;
  835.         std::vector<SFunctionDispatch>& dispatchTable = bClient ? m_dispatch[iterEnt->second].client : m_dispatch[iterEnt->second].server;
  836.  
  837.         if (dispatchTable.size() <= funcID)
  838.                 return 0;
  839.  
  840.         return new CHandleAnRMI(bClient, objID, dispatchTable[funcID], ser, m_pParent, pChannel);
  841. }
  842.  
  843. void CScriptRMI::OnInitClient(uint16 channelId, IEntity* pEntity)
  844. {
  845.         SmartScriptTable server;
  846.         SmartScriptTable entity = pEntity->GetScriptTable();
  847.  
  848.         if (!entity.GetPtr())
  849.                 return;
  850.  
  851.         if (!entity->GetValue("Server", server) || !server.GetPtr())
  852.                 return;
  853.  
  854.         IScriptSystem* pSystem = entity->GetScriptSystem();
  855.         if ((server->GetValueType("OnInitClient") == svtFunction) &&
  856.             pSystem->BeginCall(server, "OnInitClient"))
  857.         {
  858.                 pSystem->PushFuncParam(entity);
  859.                 pSystem->PushFuncParam(channelId);
  860.                 pSystem->EndCall();
  861.         }
  862. }
  863.  
  864. void CScriptRMI::OnPostInitClient(uint16 channelId, IEntity* pEntity)
  865. {
  866.         SmartScriptTable server;
  867.         SmartScriptTable entity = pEntity->GetScriptTable();
  868.  
  869.         if (!entity.GetPtr())
  870.                 return;
  871.  
  872.         if (!entity->GetValue("Server", server) || !server.GetPtr())
  873.                 return;
  874.  
  875.         IScriptSystem* pSystem = entity->GetScriptSystem();
  876.         if ((server->GetValueType("OnPostInitClient") == svtFunction) &&
  877.             pSystem->BeginCall(server, "OnPostInitClient"))
  878.         {
  879.                 pSystem->PushFuncParam(entity);
  880.                 pSystem->PushFuncParam(channelId);
  881.                 pSystem->EndCall();
  882.         }
  883. }
  884.  
  885. // add a dispatch proxy table to an entity
  886. void CScriptRMI::AddProxyTable(IScriptTable* pEntityTable,
  887.                                ScriptHandle id, ScriptHandle flags,
  888.                                const char* name, SmartScriptTable dispatchTable)
  889. {
  890.         SmartScriptTable proxyTable(pEntityTable->GetScriptSystem());
  891.         proxyTable->Delegate(dispatchTable.GetPtr());
  892.         proxyTable->SetValue(FLAGS_FIELD, flags);
  893.         proxyTable->SetValue(ID_FIELD, id);
  894.         proxyTable->SetValue("__nopersist", true);
  895.         pEntityTable->SetValue(name, proxyTable);
  896. }
  897.  
  898. // add a synch proxy table to an entity
  899. void CScriptRMI::AddSynchedTable(IScriptTable* pEntityTable,
  900.                                  ScriptHandle id,
  901.                                  const char* name, SmartScriptTable dispatchTable)
  902. {
  903.         SmartScriptTable synchedTable(pEntityTable->GetScriptSystem());
  904.         SmartScriptTable hiddenTable(pEntityTable->GetScriptSystem());
  905.         SmartScriptTable origTable;
  906.         pEntityTable->GetValue(name, origTable);
  907.  
  908.         hiddenTable->Clone(dispatchTable);
  909.         hiddenTable->SetValue("__index", hiddenTable);
  910.  
  911.         IScriptTable::Iterator iter = origTable->BeginIteration();
  912.         while (origTable->MoveNext(iter))
  913.         {
  914.                 if (iter.sKey)
  915.                 {
  916.                         if (hiddenTable->GetValueType(iter.sKey) != svtNull)
  917.                                 GameWarning("Replacing non-null value %s", iter.sKey);
  918.  
  919.                         ScriptAnyValue value;
  920.                         origTable->GetValueAny(iter.sKey, value);
  921.                         hiddenTable->SetValueAny(iter.sKey, value);
  922.                 }
  923.         }
  924.         origTable->EndIteration(iter);
  925.  
  926.         synchedTable->Delegate(hiddenTable);
  927.         synchedTable->SetValue(ID_FIELD, id);
  928.         synchedTable->SetValue(HIDDEN_FIELD, hiddenTable);
  929.         pEntityTable->SetValue(name, synchedTable);
  930. }
  931.  
  932. // send a single call to a channel - used for implementation of ProxyFunction
  933. void CScriptRMI::DispatchRMI(INetChannel* pChannel, _smart_ptr<CScriptMessage> pMsg, bool bClient)
  934. {
  935.         if (!m_pThis->m_pParent)
  936.         {
  937.                 GameWarning("Trying to dispatch a remote method invocation with no game started... failing");
  938.                 return;
  939.         }
  940.  
  941.         if (!pChannel->IsLocal())
  942.         {
  943.                 NET_PROFILE_SCOPE_RMI("RMI::CScriptRMI", false);
  944.                 pChannel->DispatchRMI(&*pMsg);
  945.         }
  946.         else
  947.         {
  948.                 CCallHelper helper;
  949.                 if (!helper.Prologue(pMsg->objId, bClient, pMsg->funcId))
  950.                 {
  951.                         CRY_ASSERT(!"This should never happen");
  952.                         return;
  953.                 }
  954.                 helper.PerformCall(pMsg->objId, pMsg, bClient, m_pThis->m_pParent->GetNetContext(), pChannel);
  955.         }
  956. }
  957.  
  958. // send a call
  959. int CScriptRMI::ProxyFunction(IFunctionHandler* pH, void* pBuffer, int nSize)
  960. {
  961.         if (!m_pThis->m_pParent)
  962.         {
  963.                 pH->GetIScriptSystem()->RaiseError("Trying to proxy a remote method invocation with no game started... failing");
  964.                 return pH->EndFunction();
  965.         }
  966.  
  967.         string funcInfo;
  968.         string dispatchInfo;
  969.         bool gatherDebugInfo = pLogRMIEvents && pLogRMIEvents->GetIVal() != 0;
  970.  
  971.         IScriptSystem* pSS = pH->GetIScriptSystem();
  972.         const SFunctionInfo* pFunctionInfo = static_cast<const SFunctionInfo*>(pBuffer);
  973.  
  974.         SmartScriptTable proxyTable;
  975.         if (!pH->GetParam(1, proxyTable))
  976.                 return pH->EndFunction(false);
  977.  
  978.         ScriptHandle flags;
  979.         ScriptHandle id;
  980.         if (!proxyTable->GetValue(FLAGS_FIELD, flags))
  981.                 return pH->EndFunction(false);
  982.         if (!proxyTable->GetValue(ID_FIELD, id))
  983.                 return pH->EndFunction(false);
  984.  
  985.         int formatStart = 2;
  986.         if (uint32 flagsMask = flags.n & (eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels))
  987.         {
  988.                 if (flagsMask != (eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels))
  989.                         formatStart++;
  990.         }
  991.  
  992.         _smart_ptr<CScriptMessage> pMsg = new CScriptMessage(
  993.           pFunctionInfo->reliability,
  994.           pFunctionInfo->attachment,
  995.           (EntityId)id.n,
  996.           pFunctionInfo->funcID,
  997.           pFunctionInfo->format);
  998.         if (!pMsg->SetFromFunction(pH, formatStart))
  999.         {
  1000.                 return pH->EndFunction(false);
  1001.         }
  1002.  
  1003.         if (gatherDebugInfo)
  1004.                 funcInfo = pMsg->DebugInfo();
  1005.  
  1006.         INetContext* pNetContext = m_pThis->m_pParent->GetNetContext();
  1007.         CCryAction* pFramework = m_pThis->m_pParent->GetFramework();
  1008.  
  1009.         if (flags.n & eDF_ToServer)
  1010.         {
  1011.                 CGameClientNub* pClientNub = pFramework->GetGameClientNub();
  1012.                 bool called = false;
  1013.                 if (pClientNub)
  1014.                 {
  1015.                         CGameClientChannel* pChannel = pClientNub->GetGameClientChannel();
  1016.                         if (pChannel)
  1017.                         {
  1018.                                 DispatchRMI(pChannel->GetNetChannel(), pMsg, false);
  1019.                                 called = true;
  1020.                         }
  1021.                 }
  1022.                 if (!called)
  1023.                 {
  1024.                         pSS->RaiseError("RMI via client (to server) requested but we are not a client");
  1025.                 }
  1026.  
  1027.                 if (gatherDebugInfo)
  1028.                         dispatchInfo = "server";
  1029.         }
  1030.         else if (flags.n & (eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels))
  1031.         {
  1032.                 CGameServerNub* pServerNub = pFramework->GetGameServerNub();
  1033.                 if (pServerNub)
  1034.                 {
  1035.                         int myChannelId = 0;
  1036.                         bool ok = true;
  1037.                         if (flags.n != (eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels))
  1038.                         {
  1039.                                 //Allow the local channel id to be null for otherClients RMIs, because the local channel id is set to null on dedicated servers
  1040.                                 if (!((flags.n & eDF_ToClientOnOtherChannels) != 0 && pH->GetParamType(2) == svtNull))
  1041.                                 {
  1042.                                         if (!pH->GetParam(2, myChannelId))
  1043.                                         {
  1044.                                                 pSS->RaiseError("RMI onClient or otherClients must have a valid channel id for its first argument");
  1045.                                                 ok = false;
  1046.                                         }
  1047.                                         else if (pServerNub->GetServerChannelMap()->find(myChannelId) == pServerNub->GetServerChannelMap()->end())
  1048.                                         {
  1049.                                                 pSS->RaiseError("RMI No such server channel %d", myChannelId);
  1050.                                                 ok = false;
  1051.                                         }
  1052.                                 }
  1053.                         }
  1054.                         if (ok)
  1055.                         {
  1056.                                 TServerChannelMap* pChannelMap = pServerNub->GetServerChannelMap();
  1057.                                 for (TServerChannelMap::iterator iter = pChannelMap->begin(); iter != pChannelMap->end(); ++iter)
  1058.                                 {
  1059.                                         bool isOwn = iter->first == myChannelId;
  1060.                                         if (isOwn && !(flags.n & eDF_ToClientOnChannel))
  1061.                                                 continue;
  1062.                                         if (!isOwn && !(flags.n & eDF_ToClientOnOtherChannels))
  1063.                                                 continue;
  1064.  
  1065.                                         if (iter->second->GetNetChannel())
  1066.                                                 DispatchRMI(iter->second->GetNetChannel(), pMsg, true);
  1067.                                 }
  1068.                                 if (gatherDebugInfo)
  1069.                                 {
  1070.                                         dispatchInfo = "client: ";
  1071.                                         bool appendChannel = false;
  1072.                                         switch (flags.n)
  1073.                                         {
  1074.                                         case eDF_ToClientOnChannel:
  1075.                                                 dispatchInfo += "specificChannel";
  1076.                                                 appendChannel = true;
  1077.                                                 break;
  1078.                                         case eDF_ToClientOnOtherChannels:
  1079.                                                 dispatchInfo += "otherChannels";
  1080.                                                 appendChannel = true;
  1081.                                                 break;
  1082.                                         case eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels:
  1083.                                                 dispatchInfo += "allChannels";
  1084.                                                 break;
  1085.                                         default:
  1086.                                                 dispatchInfo += "UNKNOWN(BUG)";
  1087.                                                 appendChannel = true;
  1088.                                                 CRY_ASSERT(false);
  1089.                                         }
  1090.                                         if (appendChannel)
  1091.                                         {
  1092.                                                 string temp;
  1093.                                                 temp.Format(" %d", myChannelId);
  1094.                                                 dispatchInfo += temp;
  1095.                                         }
  1096.                                 }
  1097.                         }
  1098.                 }
  1099.                 else
  1100.                 {
  1101.                         pSS->RaiseError("RMI via server (to client) requested but we are not a server");
  1102.                 }
  1103.         }
  1104.  
  1105.         if (gatherDebugInfo)
  1106.         {
  1107.                 IEntity* pEntity = gEnv->pEntitySystem->GetEntity((EntityId)id.n);
  1108.                 const char* name = "<invalid>";
  1109.                 if (pEntity) name = pEntity->GetName();
  1110.                 CryLogAlways("[rmi] %s(%s) [%s] on entity[%d] %s",
  1111.                              pH->GetFuncName(), funcInfo.c_str(), dispatchInfo.c_str(), (int)id.n, name);
  1112.         }
  1113.  
  1114.         return pH->EndFunction(true);
  1115. }
  1116.  
  1117. int CScriptRMI::SynchedNewIndexFunction(IFunctionHandler* pH)
  1118. {
  1119.         if (!m_pThis->m_pParent)
  1120.         {
  1121.                 pH->GetIScriptSystem()->RaiseError("Trying to set a synchronized variable with no game started... failing");
  1122.                 return pH->EndFunction();
  1123.         }
  1124.  
  1125.         SmartScriptTable table;
  1126.         SmartScriptTable hidden;
  1127.         const char* key;
  1128.         ScriptAnyValue value;
  1129.  
  1130.         if (!pH->GetParam(1, table))
  1131.                 return pH->EndFunction(false);
  1132.  
  1133.         ScriptHandle id;
  1134.         if (!table->GetValue(ID_FIELD, id))
  1135.                 return pH->EndFunction(false);
  1136.         if (!table->GetValue(HIDDEN_FIELD, hidden))
  1137.                 return pH->EndFunction(false);
  1138.  
  1139.         if (!pH->GetParam(2, key))
  1140.                 return pH->EndFunction(false);
  1141.         if (!pH->GetParamAny(3, value))
  1142.                 return pH->EndFunction(false);
  1143.  
  1144.         hidden->SetValueAny(key, value);
  1145.  
  1146.         m_pThis->m_pParent->ChangedScript((EntityId) id.n);
  1147.  
  1148.         return pH->EndFunction(true);
  1149. }
  1150.  
  1151. void CScriptRMI::GetMemoryStatistics(ICrySizer* s)
  1152. {
  1153.         CryAutoCriticalSection lk(m_dispatchMutex);
  1154.  
  1155.         s->AddObject(this, sizeof(*this));
  1156.         s->AddObject(m_entityClassToEntityTypeID);
  1157.         s->AddObject(m_entities);
  1158.         s->AddObject(m_dispatch);
  1159. }
  1160.  
downloadScriptRMI.cpp Source code - Download CRYENGINE Source code
Related Source Codes/Software:
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top