PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Extension.cpp

https://gitlab.com/N3X15/sm-ext-socket
C++ | 464 lines | 360 code | 86 blank | 18 comment | 92 complexity | 864e2023acdef1547a1c24cd0f3ce3c3 MD5 | raw file
Possible License(s): GPL-2.0
  1. #include "Extension.h"
  2. #include <string>
  3. #include <boost/asio.hpp>
  4. #include "CallbackHandler.h"
  5. #include "Callback.h"
  6. #include "Socket.h"
  7. using namespace boost::asio::ip;
  8. Extension extension;
  9. SMEXT_LINK(&extension);
  10. void GameFrame(bool simulating) {
  11. callbackHandler.ExecuteQueuedCallbacks();
  12. }
  13. extern const sp_nativeinfo_t smsock_natives[];
  14. bool Extension::SDK_OnLoad(char *error, size_t err_max, bool late) {
  15. smutils->AddGameFrameHook(&GameFrame);
  16. sharesys->AddNatives(myself, smsock_natives);
  17. socketHandleType = handlesys->CreateType("Socket", this, 0, NULL, NULL, myself->GetIdentity(), NULL);
  18. //if (_debug) smutils->LogError(myself, "[Debug] Extension loaded");
  19. socketHandler.StartProcessing();
  20. return true;
  21. }
  22. void Extension::SDK_OnUnload() {
  23. smutils->RemoveGameFrameHook(&GameFrame);
  24. handlesys->RemoveType(socketHandleType, NULL);
  25. socketHandler.Shutdown();
  26. }
  27. void Extension::OnHandleDestroy(HandleType_t type, void *object) {
  28. if (type == socketHandleType && object != NULL) {
  29. socketHandler.DestroySocket((SocketWrapper*) object);
  30. }
  31. }
  32. SocketWrapper* Extension::GetSocketWrapperByHandle(Handle_t handle) {
  33. HandleSecurity sec;
  34. sec.pOwner = NULL;
  35. sec.pIdentity = myself->GetIdentity();
  36. SocketWrapper* sw;
  37. if (handlesys->ReadHandle(handle, socketHandleType, &sec, (void**)&sw) != HandleError_None) return NULL;
  38. return sw;
  39. }
  40. // native bool:SocketIsConnected(Handle:socket);
  41. cell_t SocketIsConnected(IPluginContext *pContext, const cell_t *params) {
  42. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  43. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  44. switch (sw->socketType) {
  45. case SM_SocketType_Tcp:
  46. return ((Socket<tcp>*) sw->socket)->IsOpen();
  47. case SM_SocketType_Udp:
  48. return ((Socket<udp>*) sw->socket)->IsOpen();
  49. default:
  50. return false;
  51. }
  52. }
  53. // native Handle:SocketCreate(SocketType:protocol=SOCKET_TCP, SocketErrorCB:efunc);
  54. cell_t SocketCreate(IPluginContext *pContext, const cell_t *params) {
  55. if (params[1] != SM_SocketType_Tcp && params[1] != SM_SocketType_Udp) return pContext->ThrowNativeError("Invalid protocol specified");
  56. if (!pContext->GetFunctionById(params[2])) return pContext->ThrowNativeError("Invalid error callback specified");
  57. cell_t handle = -1;
  58. switch (params[1]) {
  59. case SM_SocketType_Tcp: {
  60. Socket<tcp>* socket = socketHandler.CreateSocket<tcp>(SM_SocketType_Tcp);
  61. SocketWrapper* sw = socketHandler.GetSocketWrapper(socket);
  62. handle = handlesys->CreateHandle(extension.socketHandleType, sw, pContext->GetIdentity(), myself->GetIdentity(), NULL);
  63. socket->smHandle = handle;
  64. socket->errorCallback = pContext->GetFunctionById(params[2]);
  65. break;
  66. }
  67. case SM_SocketType_Udp: {
  68. Socket<udp>* socket = socketHandler.CreateSocket<udp>(SM_SocketType_Udp);
  69. SocketWrapper* sw = socketHandler.GetSocketWrapper(socket);
  70. handle = handlesys->CreateHandle(extension.socketHandleType, sw, pContext->GetIdentity(), myself->GetIdentity(), NULL);
  71. socket->smHandle = handle;
  72. socket->errorCallback = pContext->GetFunctionById(params[2]);
  73. break;
  74. }
  75. }
  76. return handle;
  77. }
  78. // native SocketBind(Handle:socket, String:hostname[], port);
  79. cell_t SocketBind(IPluginContext *pContext, const cell_t *params) {
  80. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  81. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  82. if (params[3] < 0 || params[3] > 65535) return pContext->ThrowNativeError("Invalid port specified");
  83. char *hostname = NULL;
  84. pContext->LocalToString(params[2], &hostname);
  85. switch (sw->socketType) {
  86. case SM_SocketType_Tcp:
  87. return ((Socket<tcp>*) sw->socket)->Bind(hostname, params[3], false);
  88. case SM_SocketType_Udp:
  89. return ((Socket<udp>*) sw->socket)->Bind(hostname, params[3], false);
  90. default:
  91. return false;
  92. }
  93. }
  94. // native SocketConnect(Handle:socket, SocketConnectCB:cfunc, SocketReceiveCB:rfunc, SocketDisconnectCB:dfunc, String:hostname[], port);
  95. cell_t SocketConnect(IPluginContext *pContext, const cell_t *params) {
  96. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  97. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  98. //if (socket->shouldListen()) return pContext->ThrowNativeError("You can't connect a listening socket");
  99. if (!pContext->GetFunctionById(params[2])) return pContext->ThrowNativeError("Invalid connect callback specified");
  100. if (!pContext->GetFunctionById(params[3])) return pContext->ThrowNativeError("Invalid receive callback specified");
  101. if (!pContext->GetFunctionById(params[4])) return pContext->ThrowNativeError("Invalid disconnect callback specified");
  102. if (params[6] < 0 || params[6] > 65535) return pContext->ThrowNativeError("Invalid port specified");
  103. char *hostname = NULL;
  104. pContext->LocalToString(params[5], &hostname);
  105. switch (sw->socketType) {
  106. case SM_SocketType_Tcp: {
  107. Socket<tcp>* socket = (Socket<tcp>*) sw->socket;
  108. if (socket->IsOpen()) return pContext->ThrowNativeError("Socket is already connected");
  109. socket->connectCallback = pContext->GetFunctionById(params[2]);
  110. socket->receiveCallback = pContext->GetFunctionById(params[3]);
  111. socket->disconnectCallback = pContext->GetFunctionById(params[4]);
  112. return socket->Connect(hostname, params[6]);
  113. }
  114. case SM_SocketType_Udp: {
  115. Socket<udp>* socket = (Socket<udp>*) sw->socket;
  116. if (socket->IsOpen()) return pContext->ThrowNativeError("Socket is already connected");
  117. socket->connectCallback = pContext->GetFunctionById(params[2]);
  118. socket->receiveCallback = pContext->GetFunctionById(params[3]);
  119. socket->disconnectCallback = pContext->GetFunctionById(params[4]);
  120. return socket->Connect(hostname, params[6]);
  121. }
  122. default:
  123. return false;
  124. }
  125. }
  126. // native SocketDisconnect(Handle:socket);
  127. cell_t SocketDisconnect(IPluginContext *pContext, const cell_t *params) {
  128. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  129. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  130. switch (sw->socketType) {
  131. case SM_SocketType_Tcp: {
  132. Socket<tcp>* socket = (Socket<tcp>*) sw->socket;
  133. if (!socket->IsOpen()) return pContext->ThrowNativeError("Socket is not connected/listening");
  134. return socket->Disconnect();
  135. }
  136. case SM_SocketType_Udp: {
  137. Socket<udp>* socket = (Socket<udp>*) sw->socket;
  138. if (!socket->IsOpen()) return pContext->ThrowNativeError("Socket is not connected/listening");
  139. return socket->Disconnect();
  140. }
  141. default:
  142. return false;
  143. }
  144. }
  145. // native SocketListen(Handle:socket, SocketIncomingCB:ifunc);
  146. cell_t SocketListen(IPluginContext *pContext, const cell_t *params) {
  147. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  148. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  149. if (sw->socketType != SM_SocketType_Tcp) return pContext->ThrowNativeError("The socket must use the TCP/SOCK_STREAM protocol");
  150. if (!pContext->GetFunctionById(params[2])) return pContext->ThrowNativeError("Invalid incoming callback specified");
  151. switch (sw->socketType) {
  152. case SM_SocketType_Tcp: {
  153. Socket<tcp>* socket = (Socket<tcp>*) sw->socket;
  154. if (socket->IsOpen()) return pContext->ThrowNativeError("Socket is already open");
  155. socket->incomingCallback = pContext->GetFunctionById(params[2]);
  156. return socket->Listen();
  157. }
  158. case SM_SocketType_Udp: {
  159. Socket<udp>* socket = (Socket<udp>*) sw->socket;
  160. if (socket->IsOpen()) return pContext->ThrowNativeError("Socket is already open");
  161. socket->incomingCallback = pContext->GetFunctionById(params[2]);
  162. return socket->Listen();
  163. }
  164. default:
  165. return false;
  166. }
  167. }
  168. // native SocketSend(Handle:socket, String:command[], size);
  169. cell_t SocketSend(IPluginContext *pContext, const cell_t *params) {
  170. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  171. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  172. char* dataTmp = NULL;
  173. pContext->LocalToString(params[2], &dataTmp);
  174. std::string data;
  175. if (params[3] == -1) {
  176. data.assign(dataTmp);
  177. } else {
  178. data.assign(dataTmp, params[3]);
  179. }
  180. switch (sw->socketType) {
  181. case SM_SocketType_Tcp: {
  182. Socket<tcp>* socket = (Socket<tcp>*) sw->socket;
  183. if (!socket->IsOpen()) return pContext->ThrowNativeError("Can't send, socket is not connected");
  184. return socket->Send(data);
  185. }
  186. case SM_SocketType_Udp: {
  187. Socket<udp>* socket = (Socket<udp>*) sw->socket;
  188. if (!socket->IsOpen()) return pContext->ThrowNativeError("Can't send, socket is not connected");
  189. socket->incomingCallback = pContext->GetFunctionById(params[2]);
  190. return socket->Send(data);
  191. }
  192. default:
  193. return false;
  194. }
  195. }
  196. // native SocketSendTo(Handle:socket, const String:data[], size=-1, const String:hostname[], port);
  197. cell_t SocketSendTo(IPluginContext *pContext, const cell_t *params) {
  198. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  199. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  200. if (sw->socketType == SM_SocketType_Tcp) return pContext->ThrowNativeError("This native doesn't support connection orientated protocols");
  201. char* dataTmp = NULL;
  202. pContext->LocalToString(params[2], &dataTmp);
  203. std::string data;
  204. if (params[3] == -1) {
  205. data.assign(dataTmp);
  206. } else {
  207. data.assign(dataTmp, params[3]);
  208. }
  209. char* hostname = NULL;
  210. pContext->LocalToString(params[4], &hostname);
  211. switch (sw->socketType) {
  212. case SM_SocketType_Udp: {
  213. Socket<udp>* socket = (Socket<udp>*) sw->socket;
  214. //if (!socket->IsOpen()) return pContext->ThrowNativeError("Can't send, socket is not connected");
  215. socket->incomingCallback = pContext->GetFunctionById(params[2]);
  216. return socket->SendTo(data, hostname, params[5]);
  217. }
  218. default:
  219. return false;
  220. }
  221. }
  222. // native SocketSetOption(Handle:socket, SocketOption:option, value)
  223. cell_t SocketSetOption(IPluginContext *pContext, const cell_t *params) {
  224. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  225. if (params[2] != SM_SO_ConcatenateCallbacks &&
  226. params[2] != SM_SO_ForceFrameLock &&
  227. params[2] != SM_SO_CallbacksPerFrame &&
  228. params[2] != SM_SO_DebugMode) {
  229. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  230. switch (sw->socketType) {
  231. case SM_SocketType_Tcp: {
  232. return ((Socket<tcp>*) sw->socket)->SetOption((SM_SocketOption) params[2], params[3]);
  233. }
  234. case SM_SocketType_Udp: {
  235. return ((Socket<udp>*) sw->socket)->SetOption((SM_SocketOption) params[2], params[3]);
  236. }
  237. default:
  238. return false;
  239. }
  240. } else {
  241. return false;
  242. }
  243. #if 0
  244. switch (params[2]) {
  245. case ConcatenateCallbacks:
  246. socket->setOption(ConcatenateCallbacks, value);
  247. return 1;
  248. case ForceFrameLock:
  249. callbacks->setOption(ForceFrameLock, value);
  250. return 1;
  251. case CallbacksPerFrame:
  252. if (value > 0) {
  253. callbacks->setOption(CallbacksPerFrame, value);
  254. return 1;
  255. } else {
  256. return 0;
  257. }
  258. case DebugMode:
  259. sockets._debug = value != 0;
  260. return 1;
  261. ...
  262. }
  263. #endif
  264. }
  265. // native SocketSetReceiveCallback(Handle:socket, SocketReceiveCB:rfunc);
  266. cell_t SocketSetReceiveCallback(IPluginContext *pContext, const cell_t *params) {
  267. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  268. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  269. switch (sw->socketType) {
  270. case SM_SocketType_Tcp:
  271. ((Socket<tcp>*) sw->socket)->receiveCallback = pContext->GetFunctionById((params[2]));
  272. break;
  273. case SM_SocketType_Udp:
  274. ((Socket<udp>*) sw->socket)->receiveCallback = pContext->GetFunctionById((params[2]));
  275. default:
  276. return false;
  277. }
  278. return true;
  279. }
  280. // native SocketSetSendqueueEmptyCallback(Handle:socket, SocketSendqueueEmptyCB:sfunc);
  281. cell_t SocketSetSendqueueEmptyCallback(IPluginContext *pContext, const cell_t *params) {
  282. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  283. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  284. bool forceSendqueueEmptyCallback = false;
  285. switch (sw->socketType) {
  286. case SM_SocketType_Tcp:
  287. ((Socket<tcp>*) sw->socket)->sendqueueEmptyCallback = pContext->GetFunctionById((params[2]));
  288. if (!((Socket<tcp>*) sw->socket)->sendQueueLength) forceSendqueueEmptyCallback = true;
  289. break;
  290. case SM_SocketType_Udp:
  291. ((Socket<udp>*) sw->socket)->sendqueueEmptyCallback = pContext->GetFunctionById((params[2]));
  292. if (!((Socket<tcp>*) sw->socket)->sendQueueLength) forceSendqueueEmptyCallback = true;
  293. default:
  294. return false;
  295. }
  296. if (forceSendqueueEmptyCallback) {
  297. callbackHandler.AddCallback(new Callback(CallbackEvent_SendQueueEmpty, sw->socket));
  298. }
  299. return true;
  300. }
  301. // native SocketSetDisconnectCallback(Handle:socket, SocketDisconnectCB:dfunc);
  302. cell_t SocketSetDisconnectCallback(IPluginContext *pContext, const cell_t *params) {
  303. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  304. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  305. switch (sw->socketType) {
  306. case SM_SocketType_Tcp:
  307. ((Socket<tcp>*) sw->socket)->disconnectCallback = pContext->GetFunctionById((params[2]));
  308. break;
  309. case SM_SocketType_Udp:
  310. ((Socket<udp>*) sw->socket)->disconnectCallback = pContext->GetFunctionById((params[2]));
  311. default:
  312. return false;
  313. }
  314. return true;
  315. }
  316. // native SocketSetErrorCallback(Handle:socket, SocketErrorCB:efunc);
  317. cell_t SocketSetErrorCallback(IPluginContext *pContext, const cell_t *params) {
  318. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  319. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  320. switch (sw->socketType) {
  321. case SM_SocketType_Tcp:
  322. ((Socket<tcp>*) sw->socket)->errorCallback = pContext->GetFunctionById((params[2]));
  323. break;
  324. case SM_SocketType_Udp:
  325. ((Socket<udp>*) sw->socket)->errorCallback = pContext->GetFunctionById((params[2]));
  326. default:
  327. return false;
  328. }
  329. return true;
  330. }
  331. // native SocketSetArg(Handle:socket, any:arg);
  332. cell_t SocketSetArg(IPluginContext *pContext, const cell_t *params) {
  333. SocketWrapper* sw = extension.GetSocketWrapperByHandle(static_cast<Handle_t>(params[1]));
  334. if (sw == NULL) return pContext->ThrowNativeError("Invalid handle: %i", params[1]);
  335. switch (sw->socketType) {
  336. case SM_SocketType_Tcp:
  337. ((Socket<tcp>*) sw->socket)->smCallbackArg = params[2];
  338. break;
  339. case SM_SocketType_Udp:
  340. ((Socket<udp>*) sw->socket)->smCallbackArg = params[2];
  341. default:
  342. return false;
  343. }
  344. return true;
  345. }
  346. // native SocketGetHostName(String:dest[], destLen);
  347. cell_t SocketGetHostName(IPluginContext *pContext, const cell_t *params) {
  348. char* dest = NULL;
  349. pContext->LocalToString(params[1], &dest);
  350. boost::system::error_code errorCode;
  351. std::string hostName = host_name(errorCode);
  352. if (!errorCode) {
  353. size_t len = hostName.copy(dest, params[2]-1);
  354. dest[len] = '\0';
  355. return true;
  356. } else {
  357. dest[0] = '\0';
  358. return false;
  359. }
  360. }
  361. const sp_nativeinfo_t smsock_natives[] = {
  362. {"SocketIsConnected", SocketIsConnected},
  363. {"SocketCreate", SocketCreate},
  364. {"SocketBind", SocketBind},
  365. {"SocketConnect", SocketConnect},
  366. {"SocketDisconnect", SocketDisconnect},
  367. {"SocketListen", SocketListen},
  368. {"SocketSend", SocketSend},
  369. {"SocketSendTo", SocketSendTo},
  370. {"SocketSetOption", SocketSetOption},
  371. {"SocketSetReceiveCallback", SocketSetReceiveCallback},
  372. {"SocketSetSendqueueEmptyCallback", SocketSetSendqueueEmptyCallback},
  373. {"SocketSetDisconnectCallback", SocketSetDisconnectCallback},
  374. {"SocketSetErrorCallback", SocketSetErrorCallback},
  375. {"SocketSetArg", SocketSetArg},
  376. {"SocketGetHostName", SocketGetHostName},
  377. {NULL, NULL},
  378. };