├── .gitmodules ├── test ├── .gitignore └── pawn.yaml ├── lib ├── cmake-modules │ ├── .gitattributes │ ├── FindPawnCC.cmake │ ├── FindSAMPGDK.cmake │ ├── LICENSE.txt │ ├── AMXConfig.cmake │ ├── FindSAMPSDK.cmake │ ├── FindSAMPServerCLI.cmake │ ├── FindSAMPServer.cmake │ ├── README.md │ ├── AddSAMPPlugin.cmake │ └── AddSAMPPluginTest.cmake ├── samp-plugin-sdk │ ├── amx │ │ ├── getch.h │ │ ├── amx2.h │ │ ├── sclinux.h │ │ ├── getch.c │ │ └── amx.h │ ├── amxplugin2.cpp │ ├── plugincommon.h │ └── amxplugin.cpp └── cmake-conan │ └── conan.cmake ├── .devcontainer ├── docker-compose.yml ├── devcontainer.json └── Dockerfile ├── .gitattributes ├── src ├── plugin.def ├── common.hpp ├── CMakeLists.txt ├── natives.hpp ├── requests.cpp ├── impl.hpp ├── impl.cpp └── natives.cpp ├── .vscode ├── .cmaketools.json ├── tasks.json ├── settings.json └── c_cpp_properties.json ├── pawn_requests_version.inc ├── .gitignore ├── http_methods.inc ├── Taskfile.yml ├── LICENSE ├── pawn.json ├── CMakeLists.txt ├── Earthfile ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── status_codes.inc ├── requests.inc └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | gamemodes/ 2 | !pawn.yaml -------------------------------------------------------------------------------- /lib/cmake-modules/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | vscode: 3 | build: . 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pwn linguist-language=Pawn 2 | *.inc linguist-language=Pawn 3 | -------------------------------------------------------------------------------- /src/plugin.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | Supports 3 | Load 4 | Unload 5 | AmxLoad 6 | AmxUnload 7 | ProcessTick 8 | -------------------------------------------------------------------------------- /.vscode/.cmaketools.json: -------------------------------------------------------------------------------- 1 | { 2 | "variant": null, 3 | "activeEnvironments": [], 4 | "codeModel": null 5 | } -------------------------------------------------------------------------------- /src/common.hpp: -------------------------------------------------------------------------------- 1 | extern void **ppPluginData; 2 | extern void *pAMXFunctions; 3 | typedef void (*logprintf_t)(const char *szFormat, ...); 4 | extern logprintf_t logprintf; 5 | -------------------------------------------------------------------------------- /lib/samp-plugin-sdk/amx/getch.h: -------------------------------------------------------------------------------- 1 | /* Extremely inefficient but portable POSIX getch(), see getch.c */ 2 | #ifndef GETCH_H 3 | #define GETCH_H 4 | 5 | #if defined __cplusplus 6 | extern "C" { 7 | #endif 8 | int getch(void); 9 | int kbhit(void); 10 | 11 | #if defined __cplusplus 12 | } 13 | #endif 14 | 15 | #endif /* GETCH_H */ 16 | -------------------------------------------------------------------------------- /pawn_requests_version.inc: -------------------------------------------------------------------------------- 1 | // This file was generated by "sampctl package release" 2 | // DO NOT EDIT THIS FILE MANUALLY! 3 | // To update the version number for a new release, run "sampctl package release" 4 | 5 | #define PAWN_REQUESTS_VERSION_MAJOR (0) 6 | #define PAWN_REQUESTS_VERSION_MINOR (8) 7 | #define PAWN_REQUESTS_VERSION_PATCH (7) 8 | -------------------------------------------------------------------------------- /test/pawn.yaml: -------------------------------------------------------------------------------- 1 | site: "Southclaws/pawn-requests" 2 | user: southclaws 3 | repo: pawn-requests 4 | tag: 0.9.0 5 | entry: ../test.pwn 6 | output: gamemodes/test.amx 7 | dependencies: 8 | - pawn-lang/samp-stdlib 9 | - pawn-lang/YSI-Includes@5.x 10 | local: true 11 | runtime: 12 | mode: "y_testing" 13 | plugins: 14 | - requests 15 | -------------------------------------------------------------------------------- /lib/cmake-modules/FindPawnCC.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | if(WIN32) 4 | set(_PawnCC_EXECUTABLE_NAME pawncc.exe) 5 | else() 6 | set(_PawnCC_EXECUTABLE_NAME pawncc) 7 | endif() 8 | 9 | find_file(PawnCC_EXECUTABLE ${_PawnCC_EXECUTABLE_NAME}) 10 | 11 | mark_as_advanced(PawnCC_EXECUTABLE) 12 | 13 | find_package_handle_standard_args(PawnCC 14 | FOUND_VAR PawnCC_FOUND 15 | REQUIRED_VARS PawnCC_EXECUTABLE 16 | ) 17 | -------------------------------------------------------------------------------- /lib/cmake-modules/FindSAMPGDK.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | find_package(SAMPGDK QUIET CONFIG NAMES SAMPGDK) 4 | 5 | set(SAMPGDK_INCLUDE_DIRS ${SAMPGDK_INCLUDE_DIR}) 6 | set(SAMPGDK_LIBRARY_DIRS ${SAMPGDK_LIBRARY_DIR}) 7 | 8 | mark_as_advanced( 9 | SAMPGDK_VERSION 10 | SAMPGDK_INCLUDE_DIR 11 | SAMPGDK_LIBRARY_DIR 12 | ) 13 | 14 | find_package_handle_standard_args(SAMPGDK 15 | REQUIRED_VARS SAMPGDK_INCLUDE_DIRS SAMPGDK_LIBRARY_DIRS 16 | VERSION_VAR SAMPGDK_VERSION 17 | ) 18 | -------------------------------------------------------------------------------- /lib/cmake-modules/LICENSE.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2014-2015 Zeex 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /lib/cmake-modules/AMXConfig.cmake: -------------------------------------------------------------------------------- 1 | include(CheckIncludeFile) 2 | 3 | check_include_file(alloca.h HAVE_ALLOCA_H) 4 | if(HAVE_ALLOCA_H) 5 | add_definitions(-DHAVE_ALLOCA_H) 6 | endif() 7 | check_include_file(inttypes.h HAVE_INTTYPES_H) 8 | if(HAVE_INTTYPES_H) 9 | add_definitions(-DHAVE_INTTYPES_H) 10 | endif() 11 | check_include_file(malloc.h HAVE_MALLOC_H) 12 | if(HAVE_MALLOC_H) 13 | add_definitions(-DHAVE_MALLOC_H) 14 | endif() 15 | check_include_file(stdint.h HAVE_STDINT_H) 16 | if(HAVE_STDINT_H) 17 | add_definitions(-DHAVE_STDINT_H) 18 | endif() 19 | -------------------------------------------------------------------------------- /lib/cmake-modules/FindSAMPSDK.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | find_path(SAMPSDK_DIR 4 | NAMES plugin.h 5 | plugincommon.h 6 | HINTS ${SAMP_SDK_ROOT} 7 | ENV SAMP_SDK_ROOT 8 | PATH_SUFFIXES sdk SDK 9 | DOC "Path to SA-MP plugin SDK" 10 | NO_SYSTEM_ENVIRONMENT_PATH 11 | NO_CMAKE_SYSTEM_PATH 12 | ) 13 | 14 | set(SAMPSDK_INCLUDE_DIR ${SAMPSDK_DIR}) 15 | 16 | mark_as_advanced( 17 | SAMPSDK_DIR 18 | SAMPSDK_INCLUDE_DIR 19 | ) 20 | 21 | find_package_handle_standard_args(SAMPSDK 22 | REQUIRED_VARS SAMPSDK_DIR 23 | SAMPSDK_INCLUDE_DIR 24 | ) 25 | -------------------------------------------------------------------------------- /lib/cmake-modules/FindSAMPServerCLI.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | if(WIN32) 4 | set(_SAMPServerCLI_EXECUTABLE_NAME samp-server-cli.bat 5 | samp-server-cli.exe) 6 | else() 7 | set(_SAMPServerCLI_EXECUTABLE_NAME samp-server-cli) 8 | endif() 9 | 10 | find_file(SAMPServerCLI_EXECUTABLE 11 | NAMES ${_SAMPServerCLI_EXECUTABLE_NAME} 12 | HINTS ENV SAMP_SERVER_ROOT 13 | ) 14 | find_path(SAMPServerCLI_DIR 15 | NAMES ${_SAMPServerCLI_EXECUTABLE_NAME} 16 | HINTS ENV SAMP_SERVER_ROOT 17 | ) 18 | 19 | mark_as_advanced( 20 | SAMPServerCLI_EXECUTABLE 21 | SAMPServerCLI_DIR 22 | ) 23 | 24 | find_package_handle_standard_args(SAMPServerCLI 25 | FOUND_VAR SAMPServerCLI_FOUND 26 | REQUIRED_VARS SAMPServerCLI_EXECUTABLE 27 | SAMPServerCLI_DIR 28 | ) 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Package only files 3 | # 4 | 5 | # Compiled Bytecode 6 | *.amx 7 | 8 | # Vendor directory for dependencies 9 | dependencies/ 10 | 11 | # Dependency versions lockfile 12 | pawn.lock 13 | 14 | 15 | # 16 | # Server/gamemode related files 17 | # 18 | 19 | # compiled settings file 20 | # keep `samp.json` file on version control 21 | # but make sure the `rcon_password` field is set externally 22 | # you can use the environment variable `SAMP_RCON_PASSWORD` to do this. 23 | server.cfg 24 | 25 | # Plugins directory 26 | plugins/ 27 | 28 | # binaries 29 | *.exe 30 | *.dll 31 | *.so 32 | announce 33 | samp03svr 34 | samp-npc 35 | 36 | # logs 37 | logs/ 38 | server_log.txt 39 | 40 | # 41 | # Common files 42 | # 43 | 44 | *.sublime-workspace 45 | *.sublime-project 46 | 47 | build*/ 48 | .vs/ 49 | *.so 50 | *.o 51 | 52 | releases 53 | releases/* -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pawn-requests", 3 | "dockerComposeFile": "docker-compose.yml", 4 | "service": "vscode", 5 | "workspaceFolder": "/workspace", 6 | "remoteUser": "root", 7 | "overrideCommand": true, 8 | "mounts": [ 9 | "source=../,target=/workspace,type=bind", 10 | "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" 11 | ], 12 | "containerEnv": { 13 | "EARTHLY_DISABLE_ANALYTICS": "1" 14 | }, 15 | "postCreateCommand": "~/.ssh.sh && conan profile new default --detect --force || true", 16 | "customizations": { 17 | "vscode": { 18 | "extensions": [ 19 | "ms-vscode.cpptools", 20 | "ms-vscode.cmake-tools", 21 | "ms-vscode.cpptools-extension-pack", 22 | "ms-azuretools.vscode-docker", 23 | "earthly.earthfile-syntax-highlighting", 24 | "github.copilot" 25 | ] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/cmake-modules/FindSAMPServer.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | if(WIN32) 4 | set(_SAMPServer_EXECUTABLE_NAME samp-server.exe) 5 | else() 6 | set(_SAMPServer_EXECUTABLE_NAME samp03svr) 7 | endif() 8 | 9 | find_file(SAMPServer_EXECUTABLE 10 | NAMES ${_SAMPServer_EXECUTABLE_NAME} 11 | HINTS ENV SAMP_SERVER_ROOT 12 | ) 13 | find_path(SAMPServer_DIR 14 | NAMES ${_SAMPServer_EXECUTABLE_NAME} 15 | HINTS ENV SAMP_SERVER_ROOT 16 | ) 17 | find_path(SAMPServer_INCLUDE_DIR 18 | NAMES a_samp.inc a_npc.inc 19 | HINTS ${SAMPServer_DIR}/include 20 | ${SAMPServer_DIR}/pawno/include 21 | ) 22 | 23 | mark_as_advanced( 24 | SAMPServer_EXECUTABLE 25 | SAMPServer_DIR 26 | SAMPServer_INCLUDE_DIR 27 | ) 28 | 29 | find_package_handle_standard_args(SAMPServer 30 | FOUND_VAR SAMPServer_FOUND 31 | REQUIRED_VARS SAMPServer_EXECUTABLE 32 | SAMPServer_DIR 33 | SAMPServer_INCLUDE_DIR 34 | ) 35 | -------------------------------------------------------------------------------- /http_methods.inc: -------------------------------------------------------------------------------- 1 | // http_methods.inc contains an enumerated set of HTTP request methods 2 | 3 | enum E_HTTP_METHOD { 4 | HTTP_METHOD_GET, // Requests a representation of the specified resource. Requests using GET should only retrieve data. 5 | HTTP_METHOD_HEAD, // Asks for a response identical to that of a GET request, but without the response body. 6 | HTTP_METHOD_POST, // Is used to submit an entity to the specified resource, often causing a change in state or side effects on the server 7 | HTTP_METHOD_PUT, // Replaces all current representations of the target resource with the request payload. 8 | HTTP_METHOD_DELETE, // Deletes the specified resource. 9 | HTTP_METHOD_CONNECT, // Establishes a tunnel to the server identified by the target resource. 10 | HTTP_METHOD_OPTIONS, // Is used to describe the communication options for the target resource. 11 | HTTP_METHOD_TRACE, // Performs a message loop-back test along the path to the target resource. 12 | HTTP_METHOD_PATCH, // Is used to apply partial modifications to a resource. 13 | } 14 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: 3 2 | tasks: 3 | default: 4 | dir: build 5 | cmds: 6 | - cmake .. -DCMAKE_BUILD_TYPE=Debug 7 | - make 8 | 9 | build:release: 10 | dir: build 11 | cmds: 12 | - cmake .. -DCMAKE_BUILD_TYPE=Release 13 | - cmake --build . --config Release 14 | 15 | pkg:linux: 16 | dir: releases/linux 17 | cmds: 18 | - mkdir -p {plugins,includes} 19 | - cp ../../test/plugins/requests.so ./plugins/requests.so 20 | - cp ../../*.inc ./includes 21 | - 7z a -r ../pawn-requests-linux.zip * 22 | 23 | pkg:windows: 24 | dir: releases/windows 25 | cmds: 26 | - mkdir -p {plugins,includes} 27 | - cp ../../test/plugins/requests.dll ./plugins/requests.dll 28 | - cp ../../*.inc ./includes 29 | - 7z a -r ../pawn-requests-windows.zip * 30 | 31 | docker:linux: 32 | cmds: 33 | - rm -rf build 34 | - docker build --no-cache -t southclaws/requests-build . 35 | - docker run \ 36 | -v $(shell pwd):/build \ 37 | southclaws/requests-build 38 | env: 39 | DOCKER_BUILDKIT: 1 -------------------------------------------------------------------------------- /lib/cmake-modules/README.md: -------------------------------------------------------------------------------- 1 | Name | Description 2 | :--------------------------------------------|:-------------------------------------------------------- 3 | [AddSAMPPlugin](AddSAMPPlugin.cmake) | Provides `add_samp_plugin()` function 4 | [AddSAMPPluginTest](AddSAMPPluginTest.cmake) | Provides `add_samp_plugin_test()` function 5 | [AMXConfig](AMXConfig.cmake) | Defines platform-specific macros expected by AMX headers 6 | [FindPawnCC](FindPawnCC.cmake) | Finds Pawn compiler executable 7 | [FindSAMPSDK](FindSAMPSDK.cmake) | Finds SA-MP plugin SDK 8 | [FindSAMPGDK](FindSAMPGDK.cmake) | Finds [GDK][sampgdk] library and headers 9 | [FindSAMPServer](FindSAMPServer.cmake) | Finds SA-MP server executable 10 | [FindSAMPServerCLI](FindSAMPServerCLI.cmake) | Finds [samp-server-cli][samp-server-cli] 11 | ------------------------------------------------------------------------------------------------------- 12 | 13 | [sampgdk]: https://github.com/Zeex/sampgdk 14 | [samp-server-cli]: https://github.com/Zeex/samp-server-cli 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 Barnaby "Southclaws" Keene 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /pawn.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": "Southclaws", 3 | "repo": "pawn-requests", 4 | "entry": "test.pwn", 5 | "output": "test/gamemodes/test.amx", 6 | "dependencies": ["Southclaws/samp-stdlib"], 7 | "dev_dependencies": ["pawn-lang/YSI-Includes"], 8 | "runtime": { 9 | "plugins": ["Southclaws/pawn-requests"] 10 | }, 11 | "resources": [ 12 | { 13 | "name": "pawn-requests-linux.zip", 14 | "archive": true, 15 | "platform": "linux", 16 | "includes": ["includes"], 17 | "plugins": ["plugins/requests.so"] 18 | }, 19 | { 20 | "name": "pawn-requests-windows.zip", 21 | "archive": true, 22 | "platform": "windows", 23 | "includes": ["includes"], 24 | "plugins": ["plugins/requests.dll"], 25 | "files": { 26 | "boost_date_time-vc141-mt-x32-1_68.dll": "boost_date_time-vc141-mt-x32-1_68.dll", 27 | "boost_system-vc141-mt-x32-1_68.dll": "boost_system-vc141-mt-x32-1_68.dll", 28 | "cpprest_2_10.dll": "cpprest_2_10.dll", 29 | "LIBEAY32.dll": "LIBEAY32.dll", 30 | "SSLEAY32.dll": "SSLEAY32.dll", 31 | "zlib1.dll": "zlib1.dll" 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 2 | conan_basic_setup(NO_OUTPUT_DIRS) 3 | 4 | include(AMXConfig) 5 | include(AddSAMPPlugin) 6 | 7 | set(SAMP_SDK_ROOT "${CMAKE_SOURCE_DIR}/lib/samp-plugin-sdk") 8 | find_package(SAMPSDK REQUIRED) 9 | 10 | include_directories( 11 | ${SAMPSDK_INCLUDE_DIR} 12 | ) 13 | 14 | add_samp_plugin(requests 15 | ${SAMPSDK_DIR}/amxplugin.cpp 16 | ${SAMPSDK_DIR}/amxplugin2.cpp 17 | ${SAMPSDK_DIR}/amx/getch.c 18 | common.hpp 19 | requests.cpp 20 | impl.cpp 21 | impl.hpp 22 | natives.cpp 23 | natives.hpp 24 | plugin.def 25 | ) 26 | 27 | if(MSVC) 28 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") 29 | target_compile_definitions(requests PRIVATE 30 | _CRT_SECURE_NO_WARNINGS 31 | NOMINMAX 32 | _WIN32_WINNT=0x0600 33 | _SCL_SECURE_NO_WARNINGS 34 | ) 35 | 36 | target_compile_options(requests PRIVATE 37 | /W4 38 | /permissive- 39 | ) 40 | 41 | # also enable multi-processor compilation 42 | target_compile_options(requests PRIVATE 43 | /MP 44 | ) 45 | endif() 46 | 47 | if (MSVC) 48 | add_custom_command( 49 | TARGET requests POST_BUILD 50 | COMMAND ${CMAKE_COMMAND} -E copy 51 | ${CMAKE_BINARY_DIR}/lib/requests.dll 52 | ${CMAKE_SOURCE_DIR}/test/plugins/requests.dll) 53 | endif() 54 | 55 | conan_target_link_libraries(requests) 56 | -------------------------------------------------------------------------------- /lib/cmake-modules/AddSAMPPlugin.cmake: -------------------------------------------------------------------------------- 1 | function(add_samp_plugin name) 2 | add_library(${name} MODULE ${ARGN}) 3 | 4 | set_target_properties(${name} PROPERTIES PREFIX "") 5 | 6 | if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 7 | set_property(TARGET ${name} APPEND_STRING PROPERTY COMPILE_FLAGS " -m32") 8 | set_property(TARGET ${name} APPEND_STRING PROPERTY LINK_FLAGS " -m32") 9 | endif() 10 | 11 | if(CMAKE_COMPILER_IS_GNUCXX) 12 | set_property(TARGET ${name} APPEND_STRING PROPERTY 13 | COMPILE_FLAGS " -Wno-attributes") 14 | endif() 15 | 16 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 17 | set_property(TARGET ${name} APPEND_STRING PROPERTY 18 | COMPILE_FLAGS " -Wno-ignored-attributes") 19 | endif() 20 | 21 | if(WIN32 AND CMAKE_COMPILER_IS_GNUCC) 22 | set_property(TARGET ${name} APPEND_STRING PROPERTY 23 | LINK_FLAGS " -Wl,--enable-stdcall-fixup") 24 | endif() 25 | 26 | if(CYGWIN) 27 | set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS "WIN32") 28 | set_property(TARGET ${name} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--kill-at") 29 | elseif(UNIX AND NOT WIN32 AND NOT APPLE) 30 | set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS "LINUX") 31 | endif() 32 | 33 | if(MINGW) 34 | # Work around missing #include in /amx/amx.h. 35 | set_property(TARGET ${name} APPEND_STRING PROPERTY COMPILE_FLAGS " -include stddef.h") 36 | endif() 37 | endfunction() 38 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build only", 6 | "type": "shell", 7 | "command": "sampctl package build", 8 | "group": { 9 | "kind": "build", 10 | "isDefault": true 11 | }, 12 | "isBackground": false, 13 | "presentation": { 14 | "reveal": "silent", 15 | "panel": "dedicated" 16 | }, 17 | "problemMatcher": "$sampctl" 18 | }, 19 | { 20 | "label": "build watcher", 21 | "type": "shell", 22 | "command": "sampctl package build --watch", 23 | "group": "build", 24 | "isBackground": true, 25 | "presentation": { 26 | "reveal": "silent", 27 | "panel": "dedicated" 28 | }, 29 | "problemMatcher": "$sampctl" 30 | }, 31 | { 32 | "label": "run tests", 33 | "type": "shell", 34 | "command": "sampctl package run", 35 | "group": { 36 | "kind": "test", 37 | "isDefault": true 38 | }, 39 | "isBackground": true, 40 | "presentation": { 41 | "reveal": "silent", 42 | "panel": "dedicated" 43 | }, 44 | "problemMatcher": "$sampctl" 45 | }, 46 | { 47 | "label": "run tests watcher", 48 | "type": "shell", 49 | "command": "sampctl package run --watch", 50 | "group": "test", 51 | "isBackground": true, 52 | "presentation": { 53 | "reveal": "silent", 54 | "panel": "dedicated" 55 | }, 56 | "problemMatcher": "$sampctl" 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /lib/cmake-modules/AddSAMPPluginTest.cmake: -------------------------------------------------------------------------------- 1 | # AddSAMPPluginTest - add tests for SA-MP plugins. 2 | # 3 | # This module reuires the samp-server-cli script to be present in PATH in 4 | # order to be able to run the tests. The script can be downloaded here: 5 | # 6 | # https://github.com/Zeex/samp-server-cli 7 | # 8 | # Additionally the SAMP_SERVER_ROOT environment variable must be defined and 9 | # must point to the SA-MP server's root directory. 10 | 11 | include(CMakeParseArguments) 12 | 13 | function(add_samp_plugin_test) 14 | set(name "${ARGV0}") 15 | 16 | set(options TARGET OUTPUT_FILE SCRIPT TIMEOUT CONFIG WORKING_DIRECTORY) 17 | cmake_parse_arguments(ARG "" "${options}" "" ${ARGN}) 18 | 19 | find_package(SAMPServerCLI REQUIRED) 20 | set(command ${SAMPServerCLI_EXECUTABLE}) 21 | 22 | if(ARG_SCRIPT) 23 | list(APPEND args --gamemode ${ARG_SCRIPT}) 24 | endif() 25 | 26 | if(ARG_TIMEOUT) 27 | list(APPEND args --timeout ${ARG_TIMEOUT}) 28 | endif() 29 | 30 | if(ARG_WORKING_DIRECTORY) 31 | list(APPEND args --workdir ${ARG_WORKING_DIRECTORY}) 32 | endif() 33 | 34 | add_test(NAME ${name} COMMAND ${command} ${args} --output 35 | --plugin $) 36 | 37 | if(ARG_SCRIPT) 38 | get_filename_component(AMX_PATH ${ARG_SCRIPT} DIRECTORY) 39 | set_tests_properties(${name} PROPERTIES ENVIRONMENT AMX_PATH=${AMX_PATH}) 40 | endif() 41 | 42 | if(ARG_OUTPUT_FILE) 43 | file(READ ${ARG_OUTPUT_FILE} output) 44 | set_tests_properties(${name} PROPERTIES PASS_REGULAR_EXPRESSION ${output}) 45 | endif() 46 | endfunction() 47 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(requests) 3 | 4 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR};${CMAKE_SOURCE_DIR}/lib/cmake-modules") 5 | list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}) 6 | 7 | if(WIN32) 8 | if(NOT DEFINED ENV{CONAN_VCVARS_VER}) 9 | set(ENV{CONAN_VCVARS_VER} "14.4") 10 | endif() 11 | if(NOT DEFINED ENV{VCVARS_VER}) 12 | set(ENV{VCVARS_VER} "14.4") 13 | endif() 14 | if(NOT DEFINED ENV{VSCMD_ARG_VCVARS_VER}) 15 | set(ENV{VSCMD_ARG_VCVARS_VER} "14.4") 16 | endif() 17 | endif() 18 | 19 | set(CONAN_CMAKE_FILE "${CMAKE_SOURCE_DIR}/lib/cmake-conan/conan.cmake") 20 | if(NOT EXISTS "${CONAN_CMAKE_FILE}") 21 | message(FATAL_ERROR "Missing ${CONAN_CMAKE_FILE}; ensure the vendored conan.cmake is present in the repository.") 22 | endif() 23 | 24 | include("${CONAN_CMAKE_FILE}") 25 | 26 | conan_cmake_run(REQUIRES cpprestsdk/2.10.18 27 | BASIC_SETUP BUILD missing 28 | SETTINGS arch=x86) 29 | 30 | set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/test/plugins) 31 | 32 | if (MSVC) 33 | # link runtime statically 34 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") 35 | elseif(UNIX) 36 | # hide non-exported symbols 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -fvisibility=hidden") 38 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 -fvisibility=hidden") 39 | set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS OFF) 40 | 41 | # link runtime statically 42 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") 43 | add_link_options( 44 | "-static-libgcc" 45 | "-static-libstdc++" 46 | ) 47 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'") 48 | endif() 49 | 50 | add_subdirectory(src) 51 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM ghcr.io/qdm12/basedevcontainer:debian 3 | 4 | USER root 5 | 6 | ENV DEBIAN_FRONTEND=noninteractive \ 7 | CMAKE_VERSION=3.25.1 8 | 9 | RUN apt-get update && \ 10 | apt-get install -y --no-install-recommends \ 11 | build-essential \ 12 | g++-multilib \ 13 | git \ 14 | ca-certificates \ 15 | curl \ 16 | wget \ 17 | make \ 18 | python3 \ 19 | python3-pip \ 20 | python3-setuptools \ 21 | pkg-config \ 22 | unzip \ 23 | p7zip-full \ 24 | ninja-build \ 25 | docker.io \ 26 | xz-utils && \ 27 | rm -rf /var/lib/apt/lists/* 28 | 29 | RUN wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh -O /tmp/cmake.sh && \ 30 | chmod +x /tmp/cmake.sh && \ 31 | /tmp/cmake.sh --skip-license --prefix=/usr/local --exclude-subdir && \ 32 | rm /tmp/cmake.sh 33 | 34 | RUN python3 -m pip install --no-cache-dir "conan<2" --break-system-packages 35 | 36 | RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin && \ 37 | chmod +x /usr/local/bin/task 38 | 39 | RUN wget https://github.com/earthly/earthly/releases/latest/download/earthly-linux-amd64 -O /usr/local/bin/earthly && \ 40 | chmod +x /usr/local/bin/earthly && \ 41 | earthly --version && \ 42 | earthly bootstrap --no-buildkit --with-autocomplete || true 43 | 44 | RUN curl -sSL https://github.com/Southclaws/sampctl/releases/latest/download/sampctl_1.12.0_linux_amd64.tar.gz | \ 45 | tar -xz -C /usr/local/bin sampctl && \ 46 | chmod +x /usr/local/bin/sampctl 47 | 48 | ENV PATH="/root/.local/bin:${PATH}" 49 | 50 | WORKDIR /workspaces/pawn-requests 51 | -------------------------------------------------------------------------------- /lib/samp-plugin-sdk/amx/amx2.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2014 SA-MP Team, Dan, maddinat0r 5 | // 6 | //---------------------------------------------------------- 7 | 8 | #pragma once 9 | 10 | //---------------------------------------------------------- 11 | 12 | #if defined __cplusplus 13 | #include 14 | #endif 15 | 16 | //---------------------------------------------------------- 17 | 18 | #include "amx.h" 19 | 20 | //---------------------------------------------------------- 21 | 22 | #define USENAMETABLE(hdr) \ 23 | ((hdr)->defsize==sizeof(AMX_FUNCSTUBNT)) 24 | 25 | #define NUMENTRIES(hdr,field,nextfield) \ 26 | (unsigned)(((hdr)->nextfield - (hdr)->field) / (hdr)->defsize) 27 | 28 | #define GETENTRY(hdr,table,index) \ 29 | (AMX_FUNCSTUB *)((unsigned char*)(hdr) + (unsigned)(hdr)->table + (unsigned)index*(hdr)->defsize) 30 | 31 | #define GETENTRYNAME(hdr,entry) \ 32 | (USENAMETABLE(hdr) ? \ 33 | (char *)((unsigned char*)(hdr) + (unsigned)((AMX_FUNCSTUBNT*)(entry))->nameofs) : \ 34 | ((AMX_FUNCSTUB*)(entry))->name) 35 | 36 | //---------------------------------------------------------- 37 | 38 | extern int AMXAPI amx_PushAddress(AMX *amx, cell *address); 39 | extern void AMXAPI amx_Redirect(AMX *amx, char *from, ucell to, AMX_NATIVE *store); 40 | extern int AMXAPI amx_GetCString(AMX *amx, cell param, char *&dest); 41 | extern int AMXAPI amx_SetCString(AMX *amx, cell param, const char *str, int len); 42 | 43 | #if defined __cplusplus 44 | extern std::string AMXAPI amx_GetCppString(AMX *amx, cell param); 45 | extern int AMXAPI amx_SetCppString(AMX *amx, cell param, const std::string &str, size_t maxlen); 46 | #endif 47 | 48 | //---------------------------------------------------------- 49 | // EOF 50 | -------------------------------------------------------------------------------- /lib/samp-plugin-sdk/amx/sclinux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Things needed to compile under linux. 3 | * 4 | * Should be reworked totally to use GNU's 'configure' 5 | */ 6 | #ifndef SCLINUX_H 7 | #define SCLINUX_H 8 | 9 | /* getchar() is not a 'cool' replacement for MSDOS getch: Linux/unix depends on the features activated or not about the 10 | * controlling terminal's tty. This means that ioctl(2) calls must be performed, for instance to have the controlling 11 | * terminal tty's in 'raw' mode, if we want to be able to fetch a single character. This also means that everything must 12 | * be put back correctly when the function ends. See GETCH.C for an implementation. 13 | * 14 | * For interactive use of SRUN/SDBG if would be much better to use GNU's readline package: the user would be able to 15 | * have a complete emacs/vi like line editing system. 16 | */ 17 | #include "getch.h" 18 | 19 | #define stricmp(a,b) strcasecmp(a,b) 20 | #define strnicmp(a,b,c) strncasecmp(a,b,c) 21 | 22 | /* 23 | * WinWorld wants '\'. Unices do not. 24 | */ 25 | #define DIRECTORY_SEP_CHAR '/' 26 | #define DIRECTORY_SEP_STR "/" 27 | 28 | /* 29 | * SC assumes that a computer is Little Endian unless told otherwise. It uses 30 | * (and defines) the macros BYTE_ORDER and BIG_ENDIAN. 31 | * For Linux, we must overrule these settings with those defined in glibc. 32 | */ 33 | #if !defined __BYTE_ORDER 34 | # include 35 | #endif 36 | 37 | #if defined __APPLE__ 38 | # include 39 | #endif 40 | 41 | #if defined __OpenBSD__ || defined __FreeBSD__ || defined __APPLE__ 42 | # define __BYTE_ORDER BYTE_ORDER 43 | # define __LITTLE_ENDIAN LITTLE_ENDIAN 44 | # define __BIG_ENDIAN BIG_ENDIAN 45 | #endif 46 | 47 | #if !defined __BYTE_ORDER 48 | # error "Can't figure computer byte order (__BYTE_ORDER macro not found)" 49 | #endif 50 | 51 | #endif /* SCLINUX_H */ 52 | -------------------------------------------------------------------------------- /lib/samp-plugin-sdk/amx/getch.c: -------------------------------------------------------------------------------- 1 | /* Extremely inefficient but portable POSIX getch() */ 2 | #ifndef WIN32 3 | 4 | #include 5 | #include 6 | #include /* for tcgetattr() and tcsetattr() */ 7 | #include /* for read() */ 8 | #include 9 | #include 10 | #include 11 | #include "getch.h" 12 | 13 | #ifndef STDIN_FILENO 14 | # define STDIN_FILENO 0 15 | #endif 16 | 17 | int 18 | getch (void) 19 | { 20 | struct termios save_termios; 21 | struct termios ios; 22 | int c = 0; 23 | 24 | if (!isatty (STDIN_FILENO)) 25 | return EOF; 26 | 27 | if (tcgetattr (STDIN_FILENO, &save_termios) < 0) 28 | return EOF; 29 | 30 | ios = save_termios; 31 | ios.c_lflag &= ~(ICANON | ECHO | ISIG); 32 | ios.c_cc[VMIN] = 1; /* read() will return with one char */ 33 | ios.c_cc[VTIME] = 0; /* read() blocks forever */ 34 | 35 | if (tcsetattr (STDIN_FILENO, TCSANOW, &ios) < 0) 36 | return EOF; 37 | 38 | if (read (STDIN_FILENO, &c, 1) != 1) 39 | c = EOF; 40 | 41 | tcsetattr (STDIN_FILENO, TCSANOW, &save_termios); 42 | 43 | return c; 44 | } 45 | 46 | int 47 | kbhit (void) 48 | { 49 | struct termios save_termios; 50 | struct termios ios; 51 | fd_set inp; 52 | struct timeval timeout = {0, 0}; 53 | int result; 54 | 55 | if (!isatty (STDIN_FILENO)) 56 | return 0; 57 | 58 | if (tcgetattr (STDIN_FILENO, &save_termios) < 0) 59 | return 0; 60 | 61 | ios = save_termios; 62 | ios.c_lflag &= ~(ICANON | ECHO | ISIG); 63 | ios.c_cc[VMIN] = 1; /* read() will return with one char */ 64 | ios.c_cc[VTIME] = 0; /* read() blocks forever */ 65 | 66 | if (tcsetattr (STDIN_FILENO, TCSANOW, &ios) < 0) 67 | return 0; 68 | 69 | /* set up select() args */ 70 | FD_ZERO(&inp); 71 | FD_SET(STDIN_FILENO, &inp); 72 | 73 | result = select (STDIN_FILENO+1, &inp, NULL, NULL, &timeout) == 1; 74 | 75 | tcsetattr (STDIN_FILENO, TCSANOW, &save_termios); 76 | 77 | return result; 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /Earthfile: -------------------------------------------------------------------------------- 1 | VERSION 0.6 2 | ARG CMAKE_VERSION=3.25.1 3 | 4 | build-base: 5 | FROM debian:bookworm 6 | ENV DEBIAN_FRONTEND=noninteractive 7 | RUN dpkg --add-architecture i386 && \ 8 | apt-get update && \ 9 | apt-get install -yq --no-install-recommends \ 10 | g++-multilib git ca-certificates make wget curl python3 python3-setuptools python3-pip python3-venv \ 11 | p7zip-full ninja-build pkg-config && \ 12 | rm -rf /var/lib/apt/lists/* 13 | RUN python3 -m venv /opt/conan && \ 14 | /opt/conan/bin/pip install --no-cache-dir --upgrade pip && \ 15 | /opt/conan/bin/pip install --no-cache-dir "conan<2" && \ 16 | ln -s /opt/conan/bin/conan /usr/local/bin/conan 17 | RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin && chmod +x /usr/local/bin/task 18 | RUN wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh && \ 19 | chmod +x ./cmake-${CMAKE_VERSION}-linux-x86_64.sh && \ 20 | ./cmake-${CMAKE_VERSION}-linux-x86_64.sh --skip-license --prefix=/usr/local/ --exclude-subdir && \ 21 | rm ./cmake-${CMAKE_VERSION}-linux-x86_64.sh 22 | WORKDIR /src 23 | COPY . . 24 | 25 | build: 26 | FROM +build-base 27 | RUN --mount=type=cache,target=/root/.conan \ 28 | rm -rf build && \ 29 | conan profile new default --detect --force 30 | RUN --mount=type=cache,target=/root/.conan \ 31 | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release 32 | RUN --mount=type=cache,target=/root/.conan \ 33 | cmake --build build --config Release -j$(nproc) 34 | SAVE ARTIFACT build/lib/requests.so AS LOCAL build/lib/requests.so 35 | 36 | package: 37 | FROM +build 38 | RUN rm -rf releases && mkdir -p releases/linux_package/plugins releases/linux_package/includes 39 | RUN cp build/lib/requests.so releases/linux_package/plugins/requests.so 40 | RUN cp *.inc releases/linux_package/includes/ 41 | WORKDIR /src/releases/linux_package 42 | RUN 7z a ../pawn-requests-linux.zip ./includes ./plugins 43 | WORKDIR /src 44 | SAVE ARTIFACT releases/pawn-requests-linux.zip 45 | SAVE ARTIFACT releases/pawn-requests-linux.zip AS LOCAL releases/pawn-requests-linux.zip 46 | 47 | release: 48 | BUILD +package -------------------------------------------------------------------------------- /src/natives.hpp: -------------------------------------------------------------------------------- 1 | #ifndef REQUESTS_NATIVES_H 2 | #define REQUESTS_NATIVES_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "common.hpp" 12 | 13 | namespace Natives 14 | { 15 | int RequestsClient(AMX *amx, cell *params); 16 | int RequestHeaders(AMX *amx, cell *params); 17 | int Request(AMX *amx, cell *params); 18 | int RequestJSON(AMX *amx, cell *params); 19 | 20 | int WebSocketClient(AMX *amx, cell *params); 21 | int WebSocketSend(AMX *amx, cell *params); 22 | int JsonWebSocketClient(AMX *amx, cell *params); 23 | int JsonWebSocketSend(AMX *amx, cell *params); 24 | 25 | void processTick(std::set amx_List); 26 | 27 | namespace JSON 28 | { 29 | int Parse(AMX *amx, cell *params); 30 | int Stringify(AMX *amx, cell *params); 31 | int NodeType(AMX *amx, cell *params); 32 | 33 | int Object(AMX *amx, cell *params); 34 | int Bool(AMX *amx, cell *params); 35 | int Int(AMX *amx, cell *params); 36 | int Float(AMX *amx, cell *params); 37 | int String(AMX *amx, cell *params); 38 | int Array(AMX *amx, cell *params); 39 | int Append(AMX *amx, cell *params); 40 | int SetObject(AMX *amx, cell *params); 41 | int SetInt(AMX *amx, cell *params); 42 | int SetFloat(AMX *amx, cell *params); 43 | int SetBool(AMX *amx, cell *params); 44 | int SetString(AMX *amx, cell *params); 45 | int GetObjectAlt(AMX *amx, cell *params); 46 | int GetInt(AMX *amx, cell *params); 47 | int GetFloat(AMX *amx, cell *params); 48 | int GetBool(AMX *amx, cell *params); 49 | int GetString(AMX *amx, cell *params); 50 | int GetArray(AMX *amx, cell *params); 51 | int ArrayLength(AMX *amx, cell *params); 52 | int ArrayObject(AMX *amx, cell *params); 53 | 54 | int GetNodeInt(AMX *amx, cell *params); 55 | int GetNodeFloat(AMX *amx, cell *params); 56 | int GetNodeBool(AMX *amx, cell *params); 57 | int GetNodeString(AMX *amx, cell *params); 58 | 59 | int ToggleGC(AMX *amx, cell *params); 60 | int Cleanup(AMX *amx, cell *params); 61 | 62 | struct node 63 | { 64 | web::json::value *value; 65 | bool gc; 66 | }; 67 | extern std::unordered_map nodeTable; 68 | extern int jsonPoolCounter; 69 | int Alloc(web::json::value *item); 70 | web::json::value Get(int id, bool gc = true); 71 | web::json::value *GetPointer(int id); 72 | void Erase(int id); 73 | } 74 | } 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "__config": "cpp", 4 | "__nullptr": "cpp", 5 | "exception": "cpp", 6 | "initializer_list": "cpp", 7 | "new": "cpp", 8 | "stdexcept": "cpp", 9 | "typeinfo": "cpp", 10 | "iosfwd": "cpp", 11 | "cstdlib": "cpp", 12 | "__locale": "cpp", 13 | "string": "cpp", 14 | "fstream": "cpp", 15 | "ios": "cpp", 16 | "locale": "cpp", 17 | "filesystem": "cpp", 18 | "xstring": "cpp", 19 | "system_error": "cpp", 20 | "algorithm": "cpp", 21 | "array": "cpp", 22 | "atomic": "cpp", 23 | "bitset": "cpp", 24 | "cctype": "cpp", 25 | "chrono": "cpp", 26 | "clocale": "cpp", 27 | "cmath": "cpp", 28 | "codecvt": "cpp", 29 | "condition_variable": "cpp", 30 | "cstdarg": "cpp", 31 | "cstddef": "cpp", 32 | "cstdint": "cpp", 33 | "cstdio": "cpp", 34 | "cstring": "cpp", 35 | "ctime": "cpp", 36 | "cwchar": "cpp", 37 | "deque": "cpp", 38 | "forward_list": "cpp", 39 | "functional": "cpp", 40 | "iomanip": "cpp", 41 | "iostream": "cpp", 42 | "istream": "cpp", 43 | "iterator": "cpp", 44 | "limits": "cpp", 45 | "list": "cpp", 46 | "map": "cpp", 47 | "memory": "cpp", 48 | "mutex": "cpp", 49 | "numeric": "cpp", 50 | "ostream": "cpp", 51 | "random": "cpp", 52 | "ratio": "cpp", 53 | "regex": "cpp", 54 | "set": "cpp", 55 | "sstream": "cpp", 56 | "streambuf": "cpp", 57 | "thread": "cpp", 58 | "tuple": "cpp", 59 | "type_traits": "cpp", 60 | "unordered_map": "cpp", 61 | "unordered_set": "cpp", 62 | "utility": "cpp", 63 | "valarray": "cpp", 64 | "vector": "cpp", 65 | "xfacet": "cpp", 66 | "xfunctional": "cpp", 67 | "xhash": "cpp", 68 | "xiosbase": "cpp", 69 | "xlocale": "cpp", 70 | "xlocbuf": "cpp", 71 | "xlocinfo": "cpp", 72 | "xlocmes": "cpp", 73 | "xlocmon": "cpp", 74 | "xlocnum": "cpp", 75 | "xloctime": "cpp", 76 | "xmemory": "cpp", 77 | "xmemory0": "cpp", 78 | "xstddef": "cpp", 79 | "xtr1common": "cpp", 80 | "xtree": "cpp", 81 | "xutility": "cpp", 82 | "stack": "cpp", 83 | "string_view": "cpp", 84 | "*.ipp": "cpp", 85 | "__functional_base": "cpp", 86 | "__functional_base_03": "cpp", 87 | "__hash_table": "cpp", 88 | "__tree": "cpp", 89 | "__tuple": "cpp", 90 | "*.dat": "cpp", 91 | "__split_buffer": "cpp" 92 | } 93 | } -------------------------------------------------------------------------------- /src/requests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "common.hpp" 7 | #include "natives.hpp" 8 | 9 | logprintf_t logprintf; 10 | 11 | extern "C" AMX_NATIVE_INFO amx_Natives[] = { 12 | {"RequestsClient", Natives::RequestsClient}, 13 | {"RequestHeaders", Natives::RequestHeaders}, 14 | {"Request", Natives::Request}, 15 | {"RequestJSON", Natives::RequestJSON}, 16 | 17 | {"WebSocketClient", Natives::WebSocketClient}, 18 | {"WebSocketSend", Natives::WebSocketSend}, 19 | {"JsonWebSocketClient", Natives::JsonWebSocketClient}, 20 | {"JsonWebSocketSend", Natives::JsonWebSocketSend}, 21 | 22 | {"JsonParse", Natives::JSON::Parse}, 23 | {"JsonStringify", Natives::JSON::Stringify}, 24 | {"JsonNodeType", Natives::JSON::NodeType}, 25 | {"JsonObject", Natives::JSON::Object}, 26 | {"JsonInt", Natives::JSON::Int}, 27 | {"JsonFloat", Natives::JSON::Float}, 28 | {"JsonBool", Natives::JSON::Bool}, 29 | {"JsonString", Natives::JSON::String}, 30 | {"JsonArray", Natives::JSON::Array}, 31 | {"JsonAppend", Natives::JSON::Append}, 32 | {"JsonSetObject", Natives::JSON::SetObject}, 33 | {"JsonSetInt", Natives::JSON::SetInt}, 34 | {"JsonSetFloat", Natives::JSON::SetFloat}, 35 | {"JsonSetBool", Natives::JSON::SetBool}, 36 | {"JsonSetString", Natives::JSON::SetString}, 37 | {"JsonGetObject", Natives::JSON::GetObjectAlt}, // renamed due to a msvc macro interfering 38 | {"JsonGetInt", Natives::JSON::GetInt}, 39 | {"JsonGetFloat", Natives::JSON::GetFloat}, 40 | {"JsonGetBool", Natives::JSON::GetBool}, 41 | {"JsonGetString", Natives::JSON::GetString}, 42 | {"JsonGetArray", Natives::JSON::GetArray}, 43 | {"JsonArrayLength", Natives::JSON::ArrayLength}, 44 | {"JsonArrayObject", Natives::JSON::ArrayObject}, 45 | 46 | {"JsonGetNodeInt", Natives::JSON::GetNodeInt}, 47 | {"JsonGetNodeFloat", Natives::JSON::GetNodeFloat}, 48 | {"JsonGetNodeBool", Natives::JSON::GetNodeBool}, 49 | {"JsonGetNodeString", Natives::JSON::GetNodeString}, 50 | 51 | {"JsonToggleGC", Natives::JSON::ToggleGC}, 52 | {"JsonCleanup", Natives::JSON::Cleanup}, 53 | 54 | {0, 0}}; 55 | 56 | std::set amx_List; 57 | 58 | PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() 59 | { 60 | return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES | SUPPORTS_PROCESS_TICK; 61 | } 62 | 63 | PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) 64 | { 65 | pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS]; 66 | logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF]; 67 | 68 | return true; 69 | } 70 | 71 | PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) 72 | { 73 | amx_List.insert(amx); 74 | return amx_Register(amx, amx_Natives, -1); 75 | } 76 | 77 | PLUGIN_EXPORT void PLUGIN_CALL ProcessTick() 78 | { 79 | Natives::processTick(amx_List); 80 | } 81 | 82 | PLUGIN_EXPORT int PLUGIN_CALL Unload() 83 | { 84 | return 1; 85 | } 86 | 87 | PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) 88 | { 89 | amx_List.erase(amx); 90 | return 1; 91 | } 92 | -------------------------------------------------------------------------------- /lib/samp-plugin-sdk/amxplugin2.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2014 SA-MP Team, Dan, maddinat0r 5 | // 6 | //---------------------------------------------------------- 7 | 8 | #include 9 | #include 10 | 11 | //---------------------------------------------------------- 12 | 13 | #include 14 | 15 | //---------------------------------------------------------- 16 | 17 | #include "amx/amx2.h" 18 | 19 | //---------------------------------------------------------- 20 | 21 | int AMXAPI amx_PushAddress(AMX *amx, cell *address) 22 | { 23 | AMX_HEADER *hdr; 24 | unsigned char *data; 25 | cell xaddr; 26 | /* reverse relocate the address */ 27 | assert(amx != NULL); 28 | hdr = (AMX_HEADER *) amx->base; 29 | assert(hdr != NULL); 30 | assert(hdr->magic == AMX_MAGIC); 31 | data = (amx->data != NULL) ? amx->data : amx->base + (int) hdr->dat; 32 | xaddr = (cell) ((unsigned char*) address-data); 33 | if ((ucell) xaddr >= (ucell) amx->stp) 34 | { 35 | return AMX_ERR_MEMACCESS; 36 | } 37 | return amx_Push(amx,xaddr); 38 | } 39 | 40 | void AMXAPI amx_Redirect(AMX *amx, char *from, ucell to, AMX_NATIVE *store) 41 | { 42 | AMX_HEADER *hdr = (AMX_HEADER*) amx->base; 43 | AMX_FUNCSTUB *func; 44 | for (int idx = 0, num = NUMENTRIES(hdr, natives, libraries); idx != num; ++idx) 45 | { 46 | func = GETENTRY(hdr, natives, idx); 47 | if (!strcmp(from, GETENTRYNAME(hdr, func))) 48 | { 49 | if (store) 50 | { 51 | *store = (AMX_NATIVE) func->address; 52 | } 53 | func->address = to; 54 | return; 55 | } 56 | } 57 | } 58 | 59 | int AMXAPI amx_GetCString(AMX *amx, cell param, char *&dest) 60 | { 61 | cell *ptr; 62 | amx_GetAddr(amx, param, &ptr); 63 | int len; 64 | amx_StrLen(ptr, &len); 65 | dest = (char*) malloc((len + 1) * sizeof(char)); 66 | if (dest != NULL) 67 | { 68 | amx_GetString(dest, ptr, 0, UNLIMITED); 69 | dest[len] = 0; 70 | return len; 71 | } 72 | return 0; 73 | } 74 | 75 | int AMXAPI amx_SetCString(AMX *amx, cell param, const char *str, int len) 76 | { 77 | cell *dest; 78 | int error; 79 | if ((error = amx_GetAddr(amx, param, &dest)) != AMX_ERR_NONE) 80 | return error; 81 | 82 | return amx_SetString(dest, str, 0, 0, len); 83 | } 84 | 85 | #if defined __cplusplus 86 | 87 | std::string AMXAPI amx_GetCppString(AMX *amx, cell param) 88 | { 89 | cell *addr = nullptr; 90 | amx_GetAddr(amx, param, &addr); 91 | 92 | int len = 0; 93 | amx_StrLen(addr, &len); 94 | 95 | std::string string(len, ' '); 96 | amx_GetString(&string[0], addr, 0, len + 1); 97 | 98 | return string; 99 | } 100 | 101 | int AMXAPI amx_SetCppString(AMX *amx, cell param, const std::string &str, size_t maxlen) 102 | { 103 | cell *dest = nullptr; 104 | int error; 105 | if ((error = amx_GetAddr(amx, param, &dest)) != AMX_ERR_NONE) 106 | return error; 107 | 108 | return amx_SetString(dest, str.c_str(), 0, 0, maxlen); 109 | } 110 | 111 | #endif // __cplusplus 112 | 113 | //---------------------------------------------------------- 114 | // EOF 115 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1", 7 | "/usr/local/include", 8 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include", 9 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", 10 | "/usr/include", 11 | "${workspaceRoot}", 12 | "${workspaceRoot}/lib/samp-plugin-sdk" 13 | ], 14 | "defines": [], 15 | "intelliSenseMode": "clang-x64", 16 | "browse": { 17 | "path": [ 18 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1", 19 | "/usr/local/include", 20 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include", 21 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", 22 | "/usr/include", 23 | "${workspaceRoot}", 24 | "${workspaceRoot}/lib/samp-plugin-sdk" 25 | ], 26 | "limitSymbolsToIncludedHeaders": true, 27 | "databaseFilename": "" 28 | }, 29 | "macFrameworkPath": [ 30 | "/System/Library/Frameworks", 31 | "/Library/Frameworks" 32 | ], 33 | "compilerPath": "/usr/bin/clang", 34 | "cStandard": "c11", 35 | "cppStandard": "c++17" 36 | }, 37 | { 38 | "name": "Linux", 39 | "includePath": [ 40 | "/usr/include", 41 | "/usr/local/include", 42 | "${workspaceRoot}" 43 | ], 44 | "defines": [], 45 | "intelliSenseMode": "clang-x64", 46 | "browse": { 47 | "path": [ 48 | "/usr/include", 49 | "/usr/local/include", 50 | "${workspaceRoot}" 51 | ], 52 | "limitSymbolsToIncludedHeaders": true, 53 | "databaseFilename": "" 54 | } 55 | }, 56 | { 57 | "name": "Win32", 58 | "includePath": [ 59 | "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include", 60 | "${workspaceRoot}" 61 | ], 62 | "defines": [ 63 | "_DEBUG", 64 | "UNICODE" 65 | ], 66 | "intelliSenseMode": "msvc-x64", 67 | "browse": { 68 | "path": [ 69 | "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*", 70 | "${workspaceRoot}" 71 | ], 72 | "limitSymbolsToIncludedHeaders": true, 73 | "databaseFilename": "" 74 | }, 75 | "cStandard": "c11", 76 | "cppStandard": "c++17" 77 | } 78 | ], 79 | "version": 4 80 | } -------------------------------------------------------------------------------- /src/impl.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "common.hpp" 17 | #include 18 | 19 | using namespace utility; // Common utilities like string conversions 20 | using namespace web; // Common features like URIs. 21 | using namespace web::http; // Common HTTP functionality 22 | using namespace web::http::client; // HTTP client features 23 | using namespace web::websockets::client; // Websocket client features 24 | using namespace concurrency::streams; // Asynchronous streams 25 | 26 | #ifndef REQUESTS_IMPL_H 27 | #define REQUESTS_IMPL_H 28 | 29 | namespace Impl 30 | { 31 | enum E_HTTP_METHOD 32 | { 33 | HTTP_METHOD_GET = 0, 34 | HTTP_METHOD_HEAD, 35 | HTTP_METHOD_POST, 36 | HTTP_METHOD_PUT, 37 | HTTP_METHOD_DELETE, 38 | HTTP_METHOD_CONNECT, 39 | HTTP_METHOD_OPTIONS, 40 | HTTP_METHOD_TRACE, 41 | HTTP_METHOD_PATCH 42 | }; 43 | enum E_CONTENT_TYPE 44 | { 45 | empty, 46 | string, 47 | json 48 | }; 49 | struct RequestData 50 | { 51 | AMX *amx; 52 | int id; 53 | std::string callback; 54 | std::string path; 55 | E_HTTP_METHOD method; 56 | E_CONTENT_TYPE requestType; 57 | std::vector> extraHeaders; 58 | std::string bodyString; 59 | web::json::value bodyJson; 60 | }; 61 | struct ResponseData 62 | { 63 | AMX *amx; 64 | int id; 65 | std::string callback; 66 | int status; 67 | E_CONTENT_TYPE responseType; 68 | std::string rawBody; 69 | bool isWebSocket = false; 70 | }; 71 | 72 | int RequestsClient(std::string endpoint, int headers); 73 | int RequestHeaders(std::vector> headers); 74 | int Request(AMX *amx, int id, std::string path, E_HTTP_METHOD method, std::string callback, const std::string &data, int headers); 75 | int RequestJSON(AMX *amx, int id, std::string path, E_HTTP_METHOD method, std::string callback, web::json::value json, int headers); 76 | 77 | int WebSocketClient(AMX *amx, std::string address, std::string callback); 78 | int WebSocketSend(int id, std::string data); 79 | int JsonWebSocketClient(AMX *amx, std::string address, std::string callback); 80 | int JsonWebSocketSend(int id, web::json::value json); 81 | 82 | struct ClientData 83 | { 84 | http_client *client; 85 | std::vector> headers; 86 | }; 87 | struct WebSocketClientData 88 | { 89 | int id; 90 | websocket_callback_client *client; 91 | std::string address; 92 | std::string callback; 93 | bool isJson; 94 | AMX *amx; 95 | }; 96 | int headersCleanup(int id); 97 | int doRequest(int id, RequestData data); 98 | void doRequestWithClient(ClientData cd, RequestData requestData); 99 | void doRequestSync(ClientData cd, RequestData requestData, ResponseData &responseData); 100 | web::http::method methodName(E_HTTP_METHOD id); 101 | void startWebSocketListener(WebSocketClientData &wsc); 102 | 103 | extern int requestCounter; 104 | extern std::stack responseQueue; 105 | extern std::mutex responseQueueLock; 106 | std::vector gatherResponses(); 107 | 108 | extern std::unordered_map clientsTable; 109 | extern int clientsTableCounter; 110 | 111 | extern std::unordered_map>> headersTable; 112 | extern int headersTableCounter; 113 | 114 | extern std::unordered_map websocketClientsTable; 115 | extern int websocketClientsTableCounter; 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | linux: 13 | name: Build (Linux) 14 | runs-on: ubuntu-22.04 15 | permissions: 16 | contents: read 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | with: 21 | submodules: recursive 22 | - name: Set up Earthly 23 | uses: earthly/actions/setup-earthly@v1 24 | with: 25 | version: latest 26 | - name: Build and package 27 | run: earthly --ci --artifact +package/pawn-requests-linux.zip releases/pawn-requests-linux.zip 28 | - name: Upload Linux artifact 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: pawn-requests-linux 32 | path: releases/pawn-requests-linux.zip 33 | 34 | windows: 35 | name: Build (Windows) 36 | runs-on: windows-latest 37 | env: 38 | CONAN_VCVARS_VER: "14.4" 39 | VCVARS_VER: "14.4" 40 | VSCMD_ARG_VCVARS_VER: "14.4" 41 | CONAN_REQUEST_TIMEOUT: "600" 42 | CONAN_RETRY: "5" 43 | permissions: 44 | contents: read 45 | steps: 46 | - name: Checkout 47 | uses: actions/checkout@v4 48 | with: 49 | submodules: recursive 50 | - name: Cache Conan 51 | uses: actions/cache@v4 52 | with: 53 | path: | 54 | C:\Users\runneradmin\.conan 55 | key: windows-conan-${{ hashFiles('CMakeLists.txt', 'src/CMakeLists.txt', 'lib/cmake-conan/conan.cmake') }} 56 | restore-keys: | 57 | windows-conan- 58 | - name: Set up Python 59 | uses: actions/setup-python@v5 60 | with: 61 | python-version: '3.x' 62 | - name: Install Conan 63 | run: pip install "conan<2" 64 | - name: Configure Conan profile 65 | run: conan profile new default --detect --force 66 | - name: Force MSVC toolset 67 | run: conan profile update settings.compiler.version=194 default 68 | - name: Ensure MSVC v14.3 alias 69 | shell: pwsh 70 | run: | 71 | $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" 72 | if (-not (Test-Path $vswhere)) { throw "vswhere.exe not found at $vswhere" } 73 | $vsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 74 | if (-not $vsPath) { 75 | $vsPath = & $vswhere -latest -products * -property installationPath 76 | } 77 | if (-not $vsPath) { throw "Unable to locate Visual Studio installation via vswhere" } 78 | $toolsRoot = Join-Path $vsPath "VC\Tools\MSVC" 79 | $target = Get-ChildItem $toolsRoot | Sort-Object Name -Descending | Select-Object -First 1 80 | if (-not $target) { throw "Could not locate MSVC toolset under $toolsRoot" } 81 | $alias = Join-Path $toolsRoot "14.3" 82 | if (-not (Test-Path $alias)) { 83 | New-Item -ItemType Junction -Path $alias -Target $target.FullName | Out-Null 84 | } 85 | - name: Configure CMake 86 | run: cmake -S . -B build -A Win32 -DCMAKE_BUILD_TYPE=Release -T v143 -DCONAN_DISABLE_CHECK_COMPILER=ON 87 | - name: Build 88 | run: cmake --build build --config Release 89 | - name: Package Windows artifact 90 | shell: pwsh 91 | run: | 92 | Remove-Item -Recurse -Force releases -ErrorAction SilentlyContinue 93 | New-Item -ItemType Directory -Path releases/windows/plugins -Force | Out-Null 94 | New-Item -ItemType Directory -Path releases/windows/includes -Force | Out-Null 95 | Copy-Item test/plugins/requests.dll releases/windows/plugins/requests.dll 96 | Copy-Item *.inc releases/windows/includes/ 97 | Push-Location releases/windows 98 | Compress-Archive -Path plugins,includes -DestinationPath ../pawn-requests-windows.zip -Force 99 | Pop-Location 100 | - name: Upload Windows artifact 101 | uses: actions/upload-artifact@v4 102 | with: 103 | name: pawn-requests-windows 104 | path: releases/pawn-requests-windows.zip 105 | -------------------------------------------------------------------------------- /lib/samp-plugin-sdk/plugincommon.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2004-2009 SA-MP Team 5 | // 6 | //---------------------------------------------------------- 7 | 8 | #pragma once 9 | 10 | //---------------------------------------------------------- 11 | 12 | #define SAMP_PLUGIN_VERSION 0x0200 13 | 14 | //---------------------------------------------------------- 15 | 16 | #ifdef __cplusplus 17 | #define PLUGIN_EXTERN_C extern "C" 18 | #else 19 | #define PLUGIN_EXTERN_C 20 | #endif 21 | 22 | #if defined(LINUX) || defined(FREEBSD) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) 23 | #ifndef __GNUC__ 24 | #pragma message "Warning: Not using a GNU compiler." 25 | #endif 26 | #define PLUGIN_CALL 27 | #ifndef SAMPSVR 28 | // Compile code with -fvisibility=hidden to hide non-exported functions. 29 | #define PLUGIN_EXPORT PLUGIN_EXTERN_C __attribute__((visibility("default"))) 30 | #else 31 | #define PLUGIN_EXPORT PLUGIN_EXTERN_C 32 | #endif 33 | #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) 34 | #ifndef _MSC_VER 35 | #pragma message "Warning: Not using a VC++ compiler." 36 | #endif 37 | #define PLUGIN_CALL __stdcall 38 | #define PLUGIN_EXPORT PLUGIN_EXTERN_C 39 | #else 40 | #error "You must define one of WIN32, LINUX or FREEBSD" 41 | #endif 42 | 43 | //---------------------------------------------------------- 44 | 45 | enum SUPPORTS_FLAGS 46 | { 47 | SUPPORTS_VERSION = SAMP_PLUGIN_VERSION, 48 | SUPPORTS_VERSION_MASK = 0xffff, 49 | SUPPORTS_AMX_NATIVES = 0x10000, 50 | SUPPORTS_PROCESS_TICK = 0x20000 51 | }; 52 | 53 | //---------------------------------------------------------- 54 | 55 | enum PLUGIN_DATA_TYPE 56 | { 57 | // For some debugging 58 | PLUGIN_DATA_LOGPRINTF = 0x00, // void (*logprintf)(char* format, ...) 59 | PLUGIN_DATA_NETGAME = 0xE1, // CNetGame* GetNetGame(); 60 | PLUGIN_DATA_RAKSERVER = 0xE2, // RakServerInterface* PluginGetRakServer() 61 | PLUGIN_DATA_LOADFSCRIPT = 0xE3, // bool LoadFilterscriptFromMemory(char* pFileName, char* pFileData) 62 | PLUGIN_DATA_UNLOADFSCRIPT = 0xE5, // bool UnloadFilterScript(char* pFileName) 63 | PLUGIN_DATA_CONSOLE = 0xE4, // CConsole* GetConsole(); 64 | 65 | // AMX 66 | PLUGIN_DATA_AMX_EXPORTS = 0x10, // void* AmxFunctionTable[] (see PLUGIN_AMX_EXPORT) 67 | PLUGIN_DATA_CALLPUBLIC_FS = 0x11, // int (*AmxCallPublicFilterScript)(char *szFunctionName) 68 | PLUGIN_DATA_CALLPUBLIC_GM = 0x12, // int (*AmxCallPublicGameMode)(char *szFunctionName) 69 | 70 | }; 71 | 72 | //---------------------------------------------------------- 73 | 74 | enum PLUGIN_AMX_EXPORT 75 | { 76 | PLUGIN_AMX_EXPORT_Align16 = 0, 77 | PLUGIN_AMX_EXPORT_Align32 = 1, 78 | PLUGIN_AMX_EXPORT_Align64 = 2, 79 | PLUGIN_AMX_EXPORT_Allot = 3, 80 | PLUGIN_AMX_EXPORT_Callback = 4, 81 | PLUGIN_AMX_EXPORT_Cleanup = 5, 82 | PLUGIN_AMX_EXPORT_Clone = 6, 83 | PLUGIN_AMX_EXPORT_Exec = 7, 84 | PLUGIN_AMX_EXPORT_FindNative = 8, 85 | PLUGIN_AMX_EXPORT_FindPublic = 9, 86 | PLUGIN_AMX_EXPORT_FindPubVar = 10, 87 | PLUGIN_AMX_EXPORT_FindTagId = 11, 88 | PLUGIN_AMX_EXPORT_Flags = 12, 89 | PLUGIN_AMX_EXPORT_GetAddr = 13, 90 | PLUGIN_AMX_EXPORT_GetNative = 14, 91 | PLUGIN_AMX_EXPORT_GetPublic = 15, 92 | PLUGIN_AMX_EXPORT_GetPubVar = 16, 93 | PLUGIN_AMX_EXPORT_GetString = 17, 94 | PLUGIN_AMX_EXPORT_GetTag = 18, 95 | PLUGIN_AMX_EXPORT_GetUserData = 19, 96 | PLUGIN_AMX_EXPORT_Init = 20, 97 | PLUGIN_AMX_EXPORT_InitJIT = 21, 98 | PLUGIN_AMX_EXPORT_MemInfo = 22, 99 | PLUGIN_AMX_EXPORT_NameLength = 23, 100 | PLUGIN_AMX_EXPORT_NativeInfo = 24, 101 | PLUGIN_AMX_EXPORT_NumNatives = 25, 102 | PLUGIN_AMX_EXPORT_NumPublics = 26, 103 | PLUGIN_AMX_EXPORT_NumPubVars = 27, 104 | PLUGIN_AMX_EXPORT_NumTags = 28, 105 | PLUGIN_AMX_EXPORT_Push = 29, 106 | PLUGIN_AMX_EXPORT_PushArray = 30, 107 | PLUGIN_AMX_EXPORT_PushString = 31, 108 | PLUGIN_AMX_EXPORT_RaiseError = 32, 109 | PLUGIN_AMX_EXPORT_Register = 33, 110 | PLUGIN_AMX_EXPORT_Release = 34, 111 | PLUGIN_AMX_EXPORT_SetCallback = 35, 112 | PLUGIN_AMX_EXPORT_SetDebugHook = 36, 113 | PLUGIN_AMX_EXPORT_SetString = 37, 114 | PLUGIN_AMX_EXPORT_SetUserData = 38, 115 | PLUGIN_AMX_EXPORT_StrLen = 39, 116 | PLUGIN_AMX_EXPORT_UTF8Check = 40, 117 | PLUGIN_AMX_EXPORT_UTF8Get = 41, 118 | PLUGIN_AMX_EXPORT_UTF8Len = 42, 119 | PLUGIN_AMX_EXPORT_UTF8Put = 43, 120 | }; 121 | 122 | //---------------------------------------------------------- 123 | // EOF 124 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | linux-build: 10 | runs-on: ubuntu-22.04 11 | permissions: 12 | contents: read 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | with: 17 | submodules: recursive 18 | - name: Set up Earthly 19 | uses: earthly/actions/setup-earthly@v1 20 | with: 21 | version: latest 22 | - name: Build Linux release 23 | run: earthly --ci --artifact +package/pawn-requests-linux.zip releases/pawn-requests-linux.zip 24 | - name: Upload Linux artifact 25 | uses: actions/upload-artifact@v4 26 | with: 27 | name: pawn-requests-linux 28 | path: releases/pawn-requests-linux.zip 29 | 30 | windows-build: 31 | runs-on: windows-latest 32 | env: 33 | CONAN_VCVARS_VER: "14.4" 34 | VCVARS_VER: "14.4" 35 | VSCMD_ARG_VCVARS_VER: "14.4" 36 | CONAN_REQUEST_TIMEOUT: "600" 37 | CONAN_RETRY: "5" 38 | permissions: 39 | contents: read 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v4 43 | with: 44 | submodules: recursive 45 | - name: Cache Conan 46 | uses: actions/cache@v4 47 | with: 48 | path: | 49 | C:\Users\runneradmin\.conan 50 | key: windows-conan-${{ hashFiles('CMakeLists.txt', 'src/CMakeLists.txt', 'lib/cmake-conan/conan.cmake') }} 51 | restore-keys: | 52 | windows-conan- 53 | - name: Set up Python 54 | uses: actions/setup-python@v5 55 | with: 56 | python-version: '3.x' 57 | - name: Install Conan 58 | run: pip install "conan<2" 59 | - name: Configure Conan profile 60 | run: conan profile new default --detect --force 61 | - name: Force MSVC toolset 62 | run: conan profile update settings.compiler.version=194 default 63 | - name: Ensure MSVC v14.3 alias 64 | shell: pwsh 65 | run: | 66 | $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" 67 | if (-not (Test-Path $vswhere)) { throw "vswhere.exe not found at $vswhere" } 68 | $vsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 69 | if (-not $vsPath) { 70 | $vsPath = & $vswhere -latest -products * -property installationPath 71 | } 72 | if (-not $vsPath) { throw "Unable to locate Visual Studio installation via vswhere" } 73 | $toolsRoot = Join-Path $vsPath "VC\Tools\MSVC" 74 | $target = Get-ChildItem $toolsRoot | Sort-Object Name -Descending | Select-Object -First 1 75 | if (-not $target) { throw "Could not locate MSVC toolset under $toolsRoot" } 76 | $alias = Join-Path $toolsRoot "14.3" 77 | if (-not (Test-Path $alias)) { 78 | New-Item -ItemType Junction -Path $alias -Target $target.FullName | Out-Null 79 | } 80 | - name: Configure CMake 81 | run: cmake -S . -B build -A Win32 -DCMAKE_BUILD_TYPE=Release -T v143 -DCONAN_DISABLE_CHECK_COMPILER=ON 82 | - name: Build 83 | run: cmake --build build --config Release 84 | - name: Package Windows release 85 | shell: pwsh 86 | run: | 87 | Remove-Item -Recurse -Force releases -ErrorAction SilentlyContinue 88 | New-Item -ItemType Directory -Path releases/windows/plugins -Force | Out-Null 89 | New-Item -ItemType Directory -Path releases/windows/includes -Force | Out-Null 90 | Copy-Item test/plugins/requests.dll releases/windows/plugins/requests.dll 91 | Copy-Item *.inc releases/windows/includes/ 92 | Push-Location releases/windows 93 | Compress-Archive -Path plugins,includes -DestinationPath ../pawn-requests-windows.zip -Force 94 | Pop-Location 95 | - name: Upload Windows artifact 96 | uses: actions/upload-artifact@v4 97 | with: 98 | name: pawn-requests-windows 99 | path: releases/pawn-requests-windows.zip 100 | 101 | publish: 102 | runs-on: ubuntu-22.04 103 | needs: 104 | - linux-build 105 | - windows-build 106 | permissions: 107 | contents: write 108 | steps: 109 | - name: Checkout 110 | uses: actions/checkout@v4 111 | with: 112 | fetch-depth: 0 113 | - name: Download Linux artifact 114 | uses: actions/download-artifact@v4 115 | with: 116 | name: pawn-requests-linux 117 | path: releases 118 | - name: Download Windows artifact 119 | uses: actions/download-artifact@v4 120 | with: 121 | name: pawn-requests-windows 122 | path: releases 123 | - name: Generate changelog 124 | run: | 125 | PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || true) 126 | if [ -z "$PREVIOUS_TAG" ]; then 127 | git log --pretty=format:'* %s (%h)' > CHANGELOG.md 128 | else 129 | git log "${PREVIOUS_TAG}..HEAD" --pretty=format:'* %s (%h)' > CHANGELOG.md 130 | fi 131 | - name: Publish GitHub release 132 | uses: softprops/action-gh-release@v2 133 | with: 134 | files: | 135 | releases/pawn-requests-linux.zip 136 | releases/pawn-requests-windows.zip 137 | body_path: CHANGELOG.md 138 | env: 139 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 140 | -------------------------------------------------------------------------------- /status_codes.inc: -------------------------------------------------------------------------------- 1 | // status_codes.inc contains constant definitions for status codes and strings. 2 | 3 | // E_HTTP_STATUS contains all standard HTTP status codes 4 | enum E_HTTP_STATUS { 5 | HTTP_STATUS_CONTINUE = 100, // RFC 7231, 6.2.1 6 | HTTP_STATUS_SWITCHING_PROTOCOLS = 101, // RFC 7231, 6.2.2 7 | HTTP_STATUS_PROCESSING = 102, // RFC 2518, 10.1 8 | 9 | HTTP_STATUS_OK = 200, // RFC 7231, 6.3.1 10 | HTTP_STATUS_CREATED = 201, // RFC 7231, 6.3.2 11 | HTTP_STATUS_ACCEPTED = 202, // RFC 7231, 6.3.3 12 | HTTP_STATUS_NON_AUTH_INFO = 203, // RFC 7231, 6.3.4 13 | HTTP_STATUS_NO_CONTENT = 204, // RFC 7231, 6.3.5 14 | HTTP_STATUS_RESET_CONTENT = 205, // RFC 7231, 6.3.6 15 | HTTP_STATUS_PARTIAL_CONTENT = 206, // RFC 7233, 4.1 16 | HTTP_STATUS_MULTI_STATUS = 207, // RFC 4918, 11.1 17 | HTTP_STATUS_ALREADY_REPORTED = 208, // RFC 5842, 7.1 18 | HTTP_STATUS_IMUSED = 226, // RFC 3229, 10.4.1 19 | 20 | HTTP_STATUS_MULTIPLE_CHOICES = 300, // RFC 7231, 6.4.1 21 | HTTP_STATUS_MOVED_PERMANENTLY = 301, // RFC 7231, 6.4.2 22 | HTTP_STATUS_FOUND = 302, // RFC 7231, 6.4.3 23 | HTTP_STATUS_SEE_OTHER = 303, // RFC 7231, 6.4.4 24 | HTTP_STATUS_NOT_MODIFIED = 304, // RFC 7232, 4.1 25 | HTTP_STATUS_USE_PROXY = 305, // RFC 7231, 6.4.5 26 | HTTP_STATUS_UNUSED = 306, // RFC 7231, 6.4.6 (Unused) 27 | HTTP_STATUS_TEMPORARY_REDIRECT = 307, // RFC 7231, 6.4.7 28 | HTTP_STATUS_PERMANENT_REDIRECT = 308, // RFC 7538, 3 29 | 30 | HTTP_STATUS_BAD_REQUEST = 400, // RFC 7231, 6.5.1 31 | HTTP_STATUS_UNAUTHORIZED = 401, // RFC 7235, 3.1 32 | HTTP_STATUS_PAYMENT_REQUIRED = 402, // RFC 7231, 6.5.2 33 | HTTP_STATUS_FORBIDDEN = 403, // RFC 7231, 6.5.3 34 | HTTP_STATUS_NOT_FOUND = 404, // RFC 7231, 6.5.4 35 | HTTP_STATUS_METHOD_NOT_ALLOWED = 405, // RFC 7231, 6.5.5 36 | HTTP_STATUS_NOT_ACCEPTABLE = 406, // RFC 7231, 6.5.6 37 | HTTP_STATUS_PROXY_AUTH_REQUIRED = 407, // RFC 7235, 3.2 38 | HTTP_STATUS_REQUEST_TIMEOUT = 408, // RFC 7231, 6.5.7 39 | HTTP_STATUS_CONFLICT = 409, // RFC 7231, 6.5.8 40 | HTTP_STATUS_GONE = 410, // RFC 7231, 6.5.9 41 | HTTP_STATUS_LENGTH_REQUIRED = 411, // RFC 7231, 6.5.10 42 | HTTP_STATUS_PRECONDITION_FAILED = 412, // RFC 7232, 4.2 43 | HTTP_STATUS_ENTITY_TOO_LARGE = 413, // RFC 7231, 6.5.11 44 | HTTP_STATUS_REQUEST_URITOO_LONG = 414, // RFC 7231, 6.5.12 45 | HTTP_STATUS_INVALID_MEDIA_TYPE = 415, // RFC 7231, 6.5.13 46 | HTTP_STATUS_RANGE_NOT_SATISFIED = 416, // RFC 7233, 4.4 47 | HTTP_STATUS_EXPECTATION_FAILED = 417, // RFC 7231, 6.5.14 48 | HTTP_STATUS_TEAPOT = 418, // RFC 7168, 2.3.3 49 | HTTP_STATUS_INVALID_ENTITY = 422, // RFC 4918, 11.2 50 | HTTP_STATUS_LOCKED = 423, // RFC 4918, 11.3 51 | HTTP_STATUS_FAILED_DEPENDENCY = 424, // RFC 4918, 11.4 52 | HTTP_STATUS_UPGRADE_REQUIRED = 426, // RFC 7231, 6.5.15 53 | HTTP_STATUS_PRECOND_REQUIRED = 428, // RFC 6585, 3 54 | HTTP_STATUS_TOO_MANY_REQUESTS = 429, // RFC 6585, 4 55 | HTTP_STATUS_HEADER_TOO_LARGE = 431, // RFC 6585, 5 56 | HTTP_STATUS_LEGAL_FAILURE = 451, // RFC 7725, 3 57 | 58 | HTTP_STATUS_SERVER_ERROR = 500, // RFC 7231, 6.6.1 59 | HTTP_STATUS_NOT_IMPLEMENTED = 501, // RFC 7231, 6.6.2 60 | HTTP_STATUS_BAD_GATEWAY = 502, // RFC 7231, 6.6.3 61 | HTTP_STATUS_SERVICE_UNAVAILABLE = 503, // RFC 7231, 6.6.4 62 | HTTP_STATUS_GATEWAY_TIMEOUT = 504, // RFC 7231, 6.6.5 63 | HTTP_STATUS_HTTPV_NOT_SUPPORTED = 505, // RFC 7231, 6.6.6 64 | HTTP_STATUS_NEGOTIATION_LOOP = 506, // RFC 2295, 8.1 65 | HTTP_STATUS_NO_STORAGE = 507, // RFC 4918, 11.5 66 | HTTP_STATUS_LOOP_DETECTED = 508, // RFC 5842, 7.2 67 | HTTP_STATUS_NOT_EXTENDED = 510, // RFC 2774, 7 68 | HTTP_STATUS_AUTH_REQUIRED = 511, // RFC 6585, 6 69 | } 70 | 71 | stock const Continue = "Continue"; 72 | stock const SwitchingProtocols = "Switching Protocols"; 73 | stock const Processing = "Processing"; 74 | 75 | stock const Ok = "OK"; 76 | stock const Created = "Created"; 77 | stock const Accepted = "Accepted"; 78 | stock const NonAuthInfo = "Non-Authoritative Information"; 79 | stock const NoContent = "No Content"; 80 | stock const ResetContent = "Reset Content"; 81 | stock const PartialContent = "Partial Content"; 82 | stock const MultiStatus = "Multi-Status"; 83 | stock const AlreadyReported = "Already Reported"; 84 | stock const Imused = "IM Used"; 85 | 86 | stock const MultipleChoices = "Multiple Choices"; 87 | stock const MovedPermanently = "Moved Permanently"; 88 | stock const Found = "Found"; 89 | stock const SeeOther = "See Other"; 90 | stock const NotModified = "Not Modified"; 91 | stock const UseProxy = "Use Proxy"; 92 | stock const Unused = ""; 93 | stock const TemporaryRedirect = "Temporary Redirect"; 94 | stock const PermanentRedirect = "Permanent Redirect"; 95 | 96 | stock const BadRequest = "Bad Request"; 97 | stock const Unauthorized = "Unauthorized"; 98 | stock const PaymentRequired = "Payment Required"; 99 | stock const Forbidden = "Forbidden"; 100 | stock const NotFound = "Not Found"; 101 | stock const MethodNotAllowed = "Method Not Allowed"; 102 | stock const NotAcceptable = "Not Acceptable"; 103 | stock const ProxyAuthRequired = "Proxy Authentication Required"; 104 | stock const RequestTimeout = "Request Timeout"; 105 | stock const Conflict = "Conflict"; 106 | stock const Gone = "Gone"; 107 | stock const LengthRequired = "Length Required"; 108 | stock const PreconditionFailed = "Precondition Failed"; 109 | stock const EntityTooLarge = "Request Entity Too Large"; 110 | stock const RequestUritooLong = "Request URI Too Long"; 111 | stock const InvalidMediaType = "Unsupported Media Type"; 112 | stock const RangeNotSatisfied = "Requested Range Not Satisfiable"; 113 | stock const ExpectationFailed = "Expectation Failed"; 114 | stock const Teapot = "I'm a teapot"; 115 | stock const InvalidEntity = "Unprocessable Entity"; 116 | stock const Locked = "Locked"; 117 | stock const FailedDependency = "Failed Dependency"; 118 | stock const UpgradeRequired = "Upgrade Required"; 119 | stock const PrecondRequired = "Precondition Required"; 120 | stock const TooManyRequests = "Too Many Requests"; 121 | stock const HeaderTooLarge = "Request Header Fields Too Large"; 122 | stock const LegalFailure = "Unavailable For Legal Reasons"; 123 | 124 | stock const ServerError = "Internal Server Error"; 125 | stock const NotImplemented = "Not Implemented"; 126 | stock const BadGateway = "Bad Gateway"; 127 | stock const ServiceUnavailable = "Service Unavailable"; 128 | stock const GatewayTimeout = "Gateway Timeout"; 129 | stock const HttpvNotSupported = "HTTP Version Not Supported"; 130 | stock const NegotiationLoop = "Variant Also Negotiates"; 131 | stock const NoStorage = "Insufficient Storage"; 132 | stock const LoopDetected = "Loop Detected"; 133 | stock const NotExtended = "Not Extended"; 134 | stock const AuthRequired = "Network Authentication Required"; 135 | -------------------------------------------------------------------------------- /requests.inc: -------------------------------------------------------------------------------- 1 | // built-in include guard removal 2 | // just in case the user has a local dependency with the same file name 3 | #if defined _inc_requests 4 | #undef _inc_requests 5 | #endif 6 | // custom include-guard to ensure we don't duplicate 7 | #if defined _requests_included 8 | #endinput 9 | #endif 10 | #define _requests_included 11 | 12 | #include "status_codes" 13 | #include "http_methods" 14 | 15 | 16 | // - 17 | // Request API 18 | // - 19 | 20 | // OnRequestFailure is only called when there is some internal failure/exception 21 | forward OnRequestFailure(Request:id, errorCode, errorMessage[], len); 22 | 23 | // RequestsClient initialises a new Requests client with an endpoint. The endpoint 24 | // must include a scheme (http or https) and must not contain a path. Any 25 | // headers set on the client are sent with every request from the client but may 26 | // be overwritten on a per-request basis. 27 | native RequestsClient:RequestsClient(const endpoint[], Headers:headers = Headers:-1); 28 | 29 | // RequestHeaders constructs a headers object and returns a handle ID. This 30 | // function is designed to only ever be used as an argument to functions that 31 | // take `Headers:` values as those functions handle the garbage collection of 32 | // resources allocated on the heap. 33 | native Headers:RequestHeaders(...); 34 | 35 | // Request performs a request to send or receive text data 36 | native Request:Request( 37 | RequestsClient:id, 38 | const path[], 39 | E_HTTP_METHOD:method, 40 | const callback[], 41 | body[] = "", 42 | Headers:headers = Headers:-1 43 | ); 44 | 45 | // RequestJSON performs a request to send or receive JSON data 46 | native Request:RequestJSON( 47 | RequestsClient:id, 48 | const path[], 49 | E_HTTP_METHOD:method, 50 | const callback[], 51 | Node:json = Node:-1, 52 | Headers:headers = Headers:-1 53 | ); 54 | 55 | // WebSocketClient creates a websocket client and establishes a connection at 56 | // specified address. Received messages are sent to the callback which must have 57 | // the signature: (WebSocket:ws, const data[], dataLen) 58 | native WebSocket:WebSocketClient(const address[], const callback[]); 59 | native WebSocketSend(WebSocket:ws, const data[]); 60 | 61 | // JsonWebSockets are the same as above but the callback signature is: 62 | // (JsonWebSocket:jws, Node:node) 63 | native JsonWebSocket:JsonWebSocketClient(const address[], const callback[]); 64 | native JsonWebSocketSend(JsonWebSocket:ws, Node:node); 65 | 66 | // - 67 | // JSON API 68 | // - 69 | 70 | enum JSON_NODE { 71 | JSON_NODE_NUMBER, 72 | JSON_NODE_BOOLEAN, 73 | JSON_NODE_STRING, 74 | JSON_NODE_OBJECT, 75 | JSON_NODE_ARRAY, 76 | JSON_NODE_NULL, 77 | } 78 | 79 | // JsonParse decodes JSON and stores the root node into `output`. 80 | native JsonParse(const string[], &Node:output); 81 | 82 | // JsonStringify encodes a JSON node into `buf`. 83 | native JsonStringify(Node:node, buf[], len = sizeof(buf)); 84 | 85 | // JsonNodeType returns the type of a node from the above enumerator. 86 | native JSON_NODE:JsonNodeType(Node:node); 87 | 88 | // JsonObject allocates a node from a set of key-value pairs where each key must 89 | // be a string and each value must be a `Node:` value. For example: 90 | // 91 | // JsonObject("key", JsonString("value")); 92 | // 93 | // output: {"key": "value"} 94 | // 95 | // Returns a `Node:` ID which can be passed as an argument to another JsonObject 96 | // function in order to build nested objects. For example: 97 | // 98 | // JsonObject("key", JsonObject("nestedKey", JsonString("value"))); 99 | // 100 | // output: {"key": {"nestedKey": "value"}} 101 | // 102 | native Node:JsonObject({_, Node}:...); 103 | 104 | // JsonInt, JsonBool, JsonFloat, JsonString each allocate a JSON node. 105 | native Node:JsonInt(value); 106 | native Node:JsonBool(bool:value); 107 | native Node:JsonFloat(Float:value); 108 | native Node:JsonString(const value[]); 109 | 110 | // JsonArray simply takes an argument list of `Node:` IDs. 111 | // 112 | // JsonArray(JsonString("value"), JsonInt(1), JsonObject("k", JsonString("v"))) 113 | // 114 | // output: ["value", 1, {"k": "v"}] 115 | // 116 | native Node:JsonArray(Node:...); 117 | 118 | // JsonAppend returns a new `Node:` which is the result of appending b to a. 119 | // This works on both objects and arrays and the two input nodes will be deleted 120 | // from the global node store. For example: 121 | // 122 | // new Node:a = JsonObject("key1", JsonString("value")); 123 | // new Node:b = JsonObject("key2", JsonString("value")); 124 | // new Node:c = JsonAppend(a, b); 125 | // 126 | // output: {"key1": "value", "key2": "value"} 127 | // 128 | // new Node:a = JsonArray(JsonInt(1), JsonInt(2)); 129 | // new Node:a = JsonArray(JsonInt(3)); 130 | // new Node:c = JsonAppend(a, b); 131 | // 132 | // output: [1, 2, 3] 133 | // 134 | native Node:JsonAppend(Node:a, Node:b); 135 | native Node:operator+(Node:a, Node:b) = JsonAppend; 136 | 137 | // JsonSet* functions directly modify nodes by inserting or modifying keys. 138 | native JsonSetObject(Node:node, const key[], Node:object); 139 | native JsonSetInt(Node:node, const key[], output); 140 | native JsonSetFloat(Node:node, const key[], Float:output); 141 | native JsonSetBool(Node:node, const key[], bool:output); 142 | native JsonSetString(Node:node, const key[], output[], len = sizeof(output)); 143 | 144 | // JsonGetObject returns the `Node:` stored at `key` in the given `node`. 145 | // For example: 146 | // 147 | // input: {"key": {"inner": 1}} 148 | // 149 | // new Node:output; 150 | // JsonGetObject(node, "key", output); 151 | // 152 | // `output` now contains a JSON object containing {"inner": 1}, this node can be 153 | // treated like any other node: 154 | // 155 | // new outputValue; 156 | // JsonGetInt(output, outputValue); 157 | // outputValue == 1 158 | // 159 | native JsonGetObject(Node:node, const key[], &Node:output); 160 | 161 | // JsonGet* functions extract a native type from an object these functions are 162 | // shorthand for: 163 | // 164 | // new Node:output; 165 | // JsonGetObject(node, "key", output); 166 | // new string[128]; 167 | // JsonGetString(output, string); 168 | // 169 | // 99% of the time, you only need these functions to get values out of objects. 170 | // 171 | native JsonGetInt(Node:node, const key[], &output); 172 | native JsonGetFloat(Node:node, const key[], &Float:output); 173 | native JsonGetBool(Node:node, const key[], &bool:output); 174 | native JsonGetString(Node:node, const key[], output[], len = sizeof(output)); 175 | 176 | // JsonGetArray returns the `Node:` stored at `index` in the given `node`. The 177 | // `Node:` returned could be an Object or a primitive, such as an int, float, 178 | // bool or string. Use functions below to convert `Node:` into a native type. 179 | // For example: 180 | // 181 | // input: {"key": [1, 2, 3]} 182 | // 183 | // new Node:output; 184 | // JsonGetArray(node, key, output); 185 | // 186 | // `output` now contains a JSON array and can be accessed with: 187 | // 188 | // new Node:element; 189 | // JsonArrayObject(node, 1, element); 190 | // 191 | // `element` now contains a JSON integer type node and can be converted to a 192 | // native integer type using `JsonGetNodeInt`. 193 | // 194 | native JsonGetArray(Node:node, const key[], &Node:output); 195 | native JsonArrayLength(Node:node, &length); 196 | native JsonArrayObject(Node:node, index, &Node:output); 197 | 198 | // JsonGetNode* functions extract a JSON object `Node:` to `output`. 199 | // These are useful for when you get a `Node:` that represents a primitive type 200 | // such as from JsonGetArray. 201 | native JsonGetNodeInt(Node:node, &output); 202 | native JsonGetNodeFloat(Node:node, &Float:output); 203 | native JsonGetNodeBool(Node:node, &bool:output); 204 | native JsonGetNodeString(Node:node, output[], len = sizeof(output)); 205 | 206 | // JsonToggleGC toggles garbage collection for a node. This prevents 207 | // `JsonCleanup` from deleting nodes if `auto` is true. In other words, 208 | // disabling garbage collection for a node will prevent it from being deleted 209 | // automatically when it leaves scope. This is useful for when you want to pass 210 | // a node through function calls or store it for a longer period of time. 211 | // Be very careful with this function as losing a node pointer will result in a 212 | // classic memory leak. For example: 213 | // 214 | // new Node:n = JsonObject(); 215 | // JsonToggleGC(n, false); 216 | // CallLocalFunction("FillJsonObject", "d", _:n); 217 | // JsonToggleGC(n, true); 218 | // 219 | // This will ensure that each hook of `FillJsonObject` does not delete `n` when 220 | // it leaves scope. 221 | // 222 | native JsonToggleGC(Node:node, bool:toggle); 223 | 224 | // - 225 | // Internal 226 | // - 227 | 228 | // JsonCleanup is an internal function for cleaning up `Node:` objects. This is 229 | // necessary because each of the object builder functions above allocate nodes 230 | // in a pool to be passed between internal function calls. If called manually, 231 | // leave `auto` as the default value of false which will ignore a garbage 232 | // collection disable done with `JsonToggleGC`. 233 | native JsonCleanup(Node:node, auto = false); 234 | 235 | // cleans up nodes once they go out of scope. 236 | stock operator~(const Node:nodes[], len) { 237 | for(new i; i < len; ++i) { 238 | JsonCleanup(nodes[i], true); 239 | } 240 | } 241 | 242 | // - 243 | // Helpers 244 | // - 245 | 246 | // IsValidRequestsClient is for checking if the client ID returned by 247 | // `RequestsClient` is valid and safe for use. 248 | stock bool:IsValidRequestsClient(RequestsClient:id) { 249 | return (id >= RequestsClient:0); 250 | } 251 | 252 | // IsValidRequest is for checking if the ID returned by `Request` or 253 | // `RequestJSON` is valid. If it is invalid, no request was sent. 254 | stock bool:IsValidRequest(Request:id) { 255 | return (id >= Request:0); 256 | } 257 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pawn-requests 2 | 3 | [![GitHub](https://shields.southcla.ws/badge/sampctl-pawn--requests-2f2f2f.svg?style=for-the-badge)](https://github.com/Southclaws/pawn-requests) 4 | 5 | This package provides an API for interacting with HTTP(S) APIs with support for 6 | text and JSON data types. 7 | 8 | ## Installation 9 | 10 | Simply install to your project: 11 | 12 | ```bash 13 | sampctl package install Southclaws/pawn-requests 14 | ``` 15 | 16 | Include in your code and begin using the library: 17 | 18 | ```pawn 19 | #include 20 | ``` 21 | 22 | ## Usage 23 | 24 | ### Requests 25 | 26 | The Requests API is based on common implementations of similar libraries in 27 | languages such as Go, Python and JS (Node.js). 28 | 29 | There is an example of a basic gamemode that uses requests to store player data 30 | as JSON [here](https://github.com/Southclaws/pawn-requests-example). 31 | 32 | #### Requests Client 33 | 34 | First you create a `RequestsClient`, you should store this globally: 35 | 36 | ```pawn 37 | new RequestsClient:client; 38 | 39 | main() { 40 | client = RequestsClient("http://httpbin.org/"); 41 | } 42 | ``` 43 | 44 | When you create a RequestsClient, you specify the **endpoint** you want to send 45 | requests to with that client. This means you don't specify the endpoint for each 46 | individual request. 47 | 48 | You can also set headers for the client, these headers will be sent with every 49 | request. This is useful for setting authentication headers for a private 50 | endpoint: 51 | 52 | ```pawn 53 | new RequestsClient:client; 54 | 55 | main() { 56 | client = RequestsClient("http://httpbin.org/", RequestHeaders( 57 | "Authorization", "Bearer xyz" 58 | )); 59 | } 60 | ``` 61 | 62 | The `RequestHeaders` function expects an even number of string arguments. It's 63 | good practice to lay out your headers in a key-value style, like: 64 | 65 | ```pawn 66 | RequestHeaders( 67 | "Authorization", "Bearer xyz", 68 | "Connection", "keep-alive", 69 | "Cache-Control", "no-cache" 70 | ) 71 | ``` 72 | 73 | But don't forget these are just normal arguments to a function so watch out for 74 | trailing commas! 75 | 76 | #### Making Basic Requests 77 | 78 | Now you have a client, you can start making requests. If you want to work with 79 | plain text or any data other than JSON, you use the `Request` function: 80 | 81 | ```pawn 82 | Request( 83 | client, 84 | "robots.txt", 85 | HTTP_METHOD_GET, 86 | "OnGetData", 87 | .headers = RequestHeaders() 88 | ); 89 | 90 | public OnGetData(Request:id, E_HTTP_STATUS:status, data[], dataLen) { 91 | printf("status: %d, data: '%s'", _:status, data); 92 | } 93 | ``` 94 | 95 | Using the client constructed earlier, this would hit 96 | `http://httpbin.org/robots.txt` with a GET request and when the request has 97 | finished, `OnGetData` would be called and print: 98 | 99 | ```text 100 | status: 200, data: 'User-agent: * 101 | Disallow: /deny 102 | ' 103 | ``` 104 | 105 | The behaviour is similar to the existing SA:MP `HTTP()` function except this 106 | supports headers, a larger body, more methods, HTTPS and is generally safer in 107 | terms of error handling. 108 | 109 | #### Making JSON Requests 110 | 111 | JSON requests allow you to inline construct JSON at the request side as well as 112 | access JSON objects in the response. 113 | 114 | For example, the endpoint `http://httpbin.org/anything` returns JSON data so we 115 | can access that directly as a `Node:` object in the response callback: 116 | 117 | ```pawn 118 | RequestJSON( 119 | client, 120 | "anything", 121 | HTTP_METHOD_GET, 122 | "OnGetJson", 123 | .headers = RequestHeaders() 124 | ); 125 | 126 | public OnGetJson(Request:id, E_HTTP_STATUS:status, Node:node) { 127 | new output[128]; 128 | JsonGetString(node, "method", output); 129 | printf("anything response: '%s'", output); 130 | } 131 | ``` 132 | 133 | The `anything` endpoint at httpbin responds with a bunch of related data in JSON 134 | format. The `method` field contains the method used to perform the request and 135 | in this case, the method is `GET` so `OnGetJson` will output 136 | `anything response: 'GET'`. 137 | 138 | And you can also send JSON data with a POST method: 139 | 140 | ```pawn 141 | RequestJSON( 142 | client, 143 | "post", 144 | HTTP_METHOD_POST, 145 | "OnPostJson", 146 | JsonObject( 147 | "playerName", JsonString("Southclaws"), 148 | "kills", JsonInt(5), 149 | "topThreeWeapons", JsonArray( 150 | JsonString("M4"), 151 | JsonString("MP5"), 152 | JsonString("Desert Eagle") 153 | ) 154 | ), 155 | .headers = RequestHeaders() 156 | ); 157 | 158 | public OnPostJson(Request:id, E_HTTP_STATUS:status, Node:node) { 159 | if(status == HTTP_STATUS_CREATED) { 160 | printf("successfully posted JSON!"); 161 | } 162 | } 163 | ``` 164 | 165 | You could quite easily build a JSON-driven storage server backed by MongoDB. 166 | 167 | See the JSON section below for examples of manipulating JSON `Node:` objects. 168 | 169 | See the 170 | [pawn-requests-example](https://github.com/Southclaws/pawn-requests-example) 171 | repository for a more full example of using requests and JSON together. 172 | 173 | #### Request Failures 174 | 175 | If a request fails for any reason, `OnRequestFailure` is called with the 176 | following signature: `(Request:id, errorCode, errorMessage[], len)` where 177 | `errorCode` and `errorMessage` contain information to help you debug the 178 | request. 179 | 180 | #### Keeping Track of Request IDs 181 | 182 | Both `Request` and `RequestJSON` return a `Request:` tagged value. This value is 183 | the request identifier and is unique during server runtime, same as how 184 | `SetTimer` returns a unique ID. 185 | 186 | Because responses are asynchronous and the data comes back in a callback at a 187 | later time, most of the time you will have to store this ID so you know which 188 | request triggered which response. 189 | 190 | You cannot simply use the ID as an index to an array because it's an 191 | automatically incrementing value and thus is unbounded. You should instead use 192 | BigETI's pawn-map plugin to map request IDs to some other data - such as the 193 | player/vehicle/house/etc that triggered the request. See the 194 | [pawn-requests-example](https://github.com/Southclaws/pawn-requests-example) for 195 | an example of this. 196 | 197 | ### JSON 198 | 199 | If you don't already know what JSON is, a good place to start is 200 | [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON). 201 | It's pretty much a web API standard nowadays (Twitter, Discord, GitHub and just 202 | about every other API uses it to represent data). I'll briefly go over it before 203 | getting into the API. 204 | 205 | This plugin stores JSON values as "Nodes". Each node represents a value of one 206 | type. Here are some examples of the representations of different node types: 207 | 208 | * `{}` - Object that is empty 209 | * `{"key": "value"}` - Object with one key that points to a String node 210 | * `"hello"` - String 211 | * `1` - Number (integer) 212 | * `1.5` - Number (floating point) 213 | * `[1, 2, 3]` - Array, of Number nodes 214 | * `[{}, {}]` - Array of empty Object nodes 215 | * `true` - Boolean 216 | 217 | The main point here is that everything is a node, even Objects and Arrays that 218 | contain other nodes. 219 | 220 | #### Building an Object 221 | 222 | To build a JSON object to be sent in a request, you most likely want to start 223 | with `JsonObject` however you can use any node as the root node, it depends on 224 | where you're sending the data but for this example I'll use an Object as the 225 | root node. 226 | 227 | ```pawn 228 | new Node:node = JsonObject(); 229 | ``` 230 | 231 | This just constructs an empty object and if you "stringify" it (stringify simply 232 | means to turn into a string) you get: 233 | 234 | ```json 235 | {} 236 | ``` 237 | 238 | So to add more nodes to this object, simply add parameters, as key-value pairs: 239 | 240 | ```pawn 241 | new Node:node = JsonObject( 242 | "key", JsonString("value") 243 | ); 244 | ``` 245 | 246 | This would stringify as: 247 | 248 | ```json 249 | { 250 | "key": "value" 251 | } 252 | ``` 253 | 254 | You can nest objects within objects too: 255 | 256 | ```pawn 257 | new Node:node = JsonObject( 258 | "key", JsonObject( 259 | "key", JsonString("value") 260 | ) 261 | ); 262 | ``` 263 | 264 | ```json 265 | { 266 | "key": { 267 | "key": "value" 268 | } 269 | } 270 | ``` 271 | 272 | And do arrays of any node: 273 | 274 | ```pawn 275 | new Node:node = JsonObject( 276 | "key", JsonArray( 277 | JsonString("one"), 278 | JsonString("two"), 279 | JsonString("three"), 280 | JsonObject( 281 | "more_stuff1", JsonString("uno"), 282 | "more_stuff2", JsonString("dos"), 283 | "more_stuff3", JsonString("tres") 284 | ) 285 | ) 286 | ); 287 | ``` 288 | 289 | See the 290 | [unit tests](https://github.com/Southclaws/pawn-requests/blob/master/test.pwn) 291 | for more examples of JSON builders. 292 | 293 | #### Accessing Data 294 | 295 | When you request JSON data, it's provided as a `Node:` in the callback. Most of 296 | the time, you'll get an object back but depending on the application that 297 | responded this could differ. 298 | 299 | Lets assume this request responds with the following data: 300 | 301 | ```json 302 | { 303 | "name": "Southclaws", 304 | "score": 45, 305 | "vip": true, 306 | "inventory": [ 307 | { 308 | "name": "M4", 309 | "ammo": 341 310 | }, 311 | { 312 | "name": "Desert Eagle", 313 | "ammo": 32 314 | } 315 | ] 316 | } 317 | ``` 318 | 319 | ```pawn 320 | public OnSomeResponse(Request:id, E_HTTP_STATUS:status, Node:json) { 321 | new ret; 322 | 323 | new name[MAX_PLAYER_NAME]; 324 | ret = JsonGetString(node, "name", name); 325 | if(ret) { 326 | err("failed to get name, error: %d", ret); 327 | return 1; 328 | } 329 | 330 | new score; 331 | ret = JsonGetInt(node, "score", score); 332 | if(ret) { 333 | err("failed to get score, error: %d", ret); 334 | return 1; 335 | } 336 | 337 | new bool:vip; 338 | ret = JsonGetBool(node, "vip", vip); 339 | if(ret) { 340 | err("failed to get vip, error: %d", ret); 341 | return 1; 342 | } 343 | 344 | new Node:inventory; 345 | ret = JsonGetArray(node, "inventory", inventory); 346 | if(ret) { 347 | err("failed to get inventory, error: %d", ret); 348 | return 1; 349 | } 350 | 351 | new length; 352 | ret = JsonArrayLength(inventory, length); 353 | if(ret) { 354 | err("failed to get inventory array length, error: %d", ret); 355 | return 1; 356 | } 357 | 358 | for(new i; i < length; ++i) { 359 | new Node:item; 360 | ret = JsonArrayObject(inventory, i, item); 361 | if(ret) { 362 | err("failed to get inventory item %d, error: %d", i, ret); 363 | return 1; 364 | } 365 | 366 | new itemName[32]; 367 | ret = JsonGetString(item, "name", itemName); 368 | if(ret) { 369 | err("failed to get inventory item %d, error: %d", i, ret); 370 | return 1; 371 | } 372 | 373 | new itemAmmo; 374 | ret = JsonGetInt(item, "name", itemAmmo); 375 | if(ret) { 376 | err("failed to get inventory item %d, error: %d", i, ret); 377 | return 1; 378 | } 379 | 380 | printf("item %d name: %s ammo: %d", itemName, itemAmmo); 381 | } 382 | 383 | return 0; 384 | } 385 | ``` 386 | 387 | In this example, we extract each field from the JSON object with full error 388 | checking. This example shows usage of object and array access as well as 389 | primitives such as strings, integers and a boolean. 390 | 391 | If you're not a fan of the overly terse and explicit error checking, you can 392 | alternatively just check your errors at the end but this will mean you won't 393 | know exactly _where_ an error occurred, just that it did. 394 | 395 | ```pawn 396 | new ret; 397 | ret += JsonGetString(node, "key1", value1); 398 | ret += JsonGetString(node, "key2", value2); 399 | ret += JsonGetString(node, "key3", value3); 400 | if(ret) { 401 | err("some error occurred: %d", ret); 402 | } 403 | ``` 404 | 405 | ## Testing 406 | 407 | To run unit tests for the plugin on Windows, first build the plugin with Visual 408 | Studio by opening the `CMakeLists.txt` via the `File > Open > CMake` menu and 409 | then building the project. You will need to pull the dependencies too so make 410 | sure you've done `git submodule init && git submodule update` or cloned the 411 | repository recursively. 412 | 413 | Once you've done that, the .dll files will be in `./test/plugins/Debug`. There 414 | is also a `-release` suffixed version of this make command for testing the 415 | release binaries. 416 | 417 | ```powershell 418 | make test-windows-debug 419 | ``` 420 | 421 | If you want to build and test the Linux version from a Windows machine, make 422 | sure Docker is installed and run: 423 | 424 | ```powershell 425 | make build-linux 426 | ``` 427 | 428 | Which will output `requests.so` to `./test/plugins`. To run unit tests on Linux, 429 | run: 430 | 431 | ```powershell 432 | make test-linux 433 | ``` 434 | 435 | Which will run the tests via sampctl with the `--container` flag set. 436 | 437 | ## Development 438 | 439 | To set up the development environment, first install 440 | [`vcpkg`](https://github.com/Microsoft/vcpkg) then 441 | [cpprestsdk](https://github.com/Microsoft/cpprestsdk). 442 | 443 | Open Visual Studio (A recent version with CMake support) and File > Open the 444 | project `CMakeLists.txt`. VS will fail on the first attempt as it won't be able 445 | to find cpprestsdk. To resolve this, edit `.vs/CMakeSettings.json` to contain 446 | the necessary environment variables for a Debug and Release configuration: 447 | 448 | ```json 449 | { 450 | "configurations": [ 451 | { 452 | "name": "x86-Release", 453 | "generator": "Visual Studio 15 2017", 454 | "configurationType": "Release", 455 | "buildRoot": 456 | "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", 457 | "cmakeCommandArgs": "", 458 | "buildCommandArgs": "-m -v:minimal", 459 | "variables": [ 460 | { 461 | "name": "CMAKE_TOOLCHAIN_FILE", 462 | "value": "C:/Users/Southclaws/vcpkg/scripts/buildsystems/vcpkg.cmake" 463 | } 464 | ] 465 | }, 466 | { 467 | "name": "x86-Debug", 468 | "generator": "Visual Studio 15 2017", 469 | "configurationType": "Debug", 470 | "buildRoot": 471 | "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", 472 | "cmakeCommandArgs": "", 473 | "buildCommandArgs": "-m -v:minimal", 474 | "variables": [ 475 | { 476 | "name": "CMAKE_TOOLCHAIN_FILE", 477 | "value": "C:/Users/Southclaws/vcpkg/scripts/buildsystems/vcpkg.cmake" 478 | } 479 | ] 480 | } 481 | ] 482 | } 483 | ``` 484 | 485 | The configuration file may change depending on VS version or other things, as 486 | long as the `CMAKE_TOOLCHAIN_FILE` variables are passed to CMake properly, the 487 | build should succeed. 488 | 489 | Once this is done, VS should start indexing all the dependencies. Once it has 490 | finihed, in the menu bar, hit CMake > Build All and it should spit out a `.dll`. 491 | -------------------------------------------------------------------------------- /src/impl.cpp: -------------------------------------------------------------------------------- 1 | #include "impl.hpp" 2 | 3 | #include 4 | #include 5 | 6 | int Impl::requestCounter = 0; 7 | 8 | std::stack Impl::responseQueue; 9 | std::mutex Impl::responseQueueLock; 10 | 11 | std::unordered_map Impl::clientsTable; 12 | int Impl::clientsTableCounter = 0; 13 | 14 | std::unordered_map>> Impl::headersTable; 15 | int Impl::headersTableCounter = 0; 16 | 17 | std::unordered_map Impl::websocketClientsTable; 18 | int Impl::websocketClientsTableCounter = 0; 19 | 20 | namespace 21 | { 22 | bool isSslLoggingEnabled() 23 | { 24 | static const bool enabled = []() { 25 | const char *env = std::getenv("PAWN_REQUESTS_LOG_SSL"); 26 | if (env == nullptr || env[0] == '\0') 27 | { 28 | return false; 29 | } 30 | const char first = env[0]; 31 | return !(first == '0' || first == 'f' || first == 'F' || first == 'n' || first == 'N'); 32 | }(); 33 | return enabled; 34 | } 35 | 36 | std::vector> copyHeadersForId(int headersId) 37 | { 38 | if (headersId < 0) 39 | { 40 | return {}; 41 | } 42 | 43 | auto it = Impl::headersTable.find(headersId); 44 | if (it == Impl::headersTable.end()) 45 | { 46 | logprintf("WARN: headers handle %d does not exist", headersId); 47 | return {}; 48 | } 49 | 50 | return it->second; 51 | } 52 | 53 | void logOpenSslErrors(const char *context) 54 | { 55 | if (!isSslLoggingEnabled()) 56 | { 57 | while (ERR_get_error() != 0) 58 | { 59 | } 60 | return; 61 | } 62 | 63 | unsigned long err = ERR_get_error(); 64 | while (err != 0) 65 | { 66 | char buffer[256]; 67 | ERR_error_string_n(err, buffer, sizeof(buffer)); 68 | logprintf("ERROR: %s: OpenSSL error: %s", context, buffer); 69 | err = ERR_get_error(); 70 | } 71 | } 72 | 73 | } 74 | 75 | int Impl::RequestsClient(std::string endpoint, int headers) 76 | { 77 | int id = clientsTableCounter++; 78 | auto clientHeaders = copyHeadersForId(headers); 79 | try 80 | { 81 | http_client_config config; 82 | #ifndef _WIN32 83 | // TODO: in the future allow the user to specify options to configure 84 | // TODO: the client with 85 | config.set_ssl_context_callback([](boost::asio::ssl::context &ctx) 86 | { 87 | ctx.set_default_verify_paths(); 88 | ctx.load_verify_file("/etc/ssl/certs/ca-certificates.crt"); 89 | }); 90 | #endif 91 | http_client *client = new http_client(utility::conversions::to_string_t(endpoint), config); 92 | clientsTable[id] = {client, clientHeaders}; 93 | } 94 | catch (std::exception &e) 95 | { 96 | logprintf("ERROR: Failed to create new HTTP client: %s", e.what()); 97 | id = -1; 98 | } 99 | return id; 100 | } 101 | 102 | int Impl::RequestHeaders(std::vector> headers) 103 | { 104 | int id = headersTableCounter++; 105 | headersTable[id] = headers; 106 | return id; 107 | } 108 | 109 | int Impl::Request(AMX *amx, int id, std::string path, E_HTTP_METHOD method, std::string callback, const std::string &data, int headers) 110 | { 111 | RequestData requestData; 112 | requestData.amx = amx; 113 | requestData.id = requestCounter; 114 | requestData.callback = callback; 115 | requestData.path = path; 116 | requestData.method = method; 117 | requestData.requestType = E_CONTENT_TYPE::string; 118 | requestData.extraHeaders = copyHeadersForId(headers); 119 | requestData.bodyString = data; 120 | 121 | int ret = doRequest(id, requestData); 122 | if (ret < 0) 123 | { 124 | return ret; 125 | } 126 | return requestCounter++; 127 | } 128 | 129 | int Impl::RequestJSON(AMX *amx, int id, std::string path, E_HTTP_METHOD method, std::string callback, web::json::value json, int headers) 130 | { 131 | RequestData requestData; 132 | requestData.amx = amx; 133 | requestData.id = requestCounter; 134 | requestData.callback = callback; 135 | requestData.path = path; 136 | requestData.method = method; 137 | requestData.requestType = E_CONTENT_TYPE::json; 138 | requestData.extraHeaders = copyHeadersForId(headers); 139 | requestData.bodyJson = json; 140 | 141 | int ret = doRequest(id, requestData); 142 | if (ret < 0) 143 | { 144 | return ret; 145 | } 146 | return requestCounter++; 147 | } 148 | 149 | int Impl::headersCleanup(int id) 150 | { 151 | headersTable.erase(id); 152 | return 0; 153 | } 154 | 155 | int Impl::doRequest(int id, RequestData requestData) 156 | { 157 | if (clientsTable.find(id) == clientsTable.end()) 158 | { 159 | logprintf("ERROR: invalid client ID %d used", id); 160 | return -1; 161 | } 162 | ClientData cd = clientsTable[id]; 163 | 164 | try 165 | { 166 | std::thread t(doRequestWithClient, cd, requestData); 167 | t.detach(); 168 | } 169 | catch (std::exception e) 170 | { 171 | logprintf("ERROR: failed to dispatch request thread: '%s'", e.what()); 172 | return -2; 173 | } 174 | 175 | return 0; 176 | } 177 | 178 | void Impl::doRequestWithClient(ClientData cd, RequestData requestData) 179 | { 180 | ResponseData responseData; 181 | responseData.amx = requestData.amx; 182 | responseData.id = requestData.id; 183 | responseData.callback = requestData.callback; 184 | responseData.responseType = E_CONTENT_TYPE::empty; 185 | 186 | try 187 | { 188 | doRequestSync(cd, requestData, responseData); 189 | } 190 | catch (http::http_exception e) 191 | { 192 | logprintf("ERROR: HTTP error %s", e.what()); 193 | responseData.callback = "OnRequestFailure"; 194 | responseData.rawBody = e.what(); 195 | responseData.status = 1; 196 | } 197 | catch (std::exception e) 198 | { 199 | logprintf("ERROR: General error %s", e.what()); 200 | responseData.callback = "OnRequestFailure"; 201 | responseData.rawBody = e.what(); 202 | responseData.status = 2; 203 | } 204 | catch (...) 205 | { 206 | try 207 | { 208 | auto eptr = std::current_exception(); 209 | if (eptr) 210 | { 211 | std::rethrow_exception(eptr); 212 | } 213 | } 214 | catch (const std::exception &e) 215 | { 216 | logprintf("ERROR: Unknown error %s", e.what()); 217 | responseData.callback = "OnRequestFailure"; 218 | responseData.rawBody = e.what(); 219 | responseData.status = 3; 220 | } 221 | } 222 | 223 | { 224 | std::lock_guard lock(responseQueueLock); 225 | responseQueue.push(responseData); 226 | } 227 | } 228 | 229 | void Impl::doRequestSync(ClientData cd, RequestData requestData, ResponseData &responseData) 230 | { 231 | http_request request(methodName(requestData.method)); 232 | for (auto h : cd.headers) 233 | { 234 | request.headers().add( 235 | utility::conversions::to_string_t(h.first), 236 | utility::conversions::to_string_t(h.second)); 237 | } 238 | for (auto h : requestData.extraHeaders) 239 | { 240 | request.headers().add( 241 | utility::conversions::to_string_t(h.first), 242 | utility::conversions::to_string_t(h.second)); 243 | } 244 | request.set_request_uri(utility::conversions::to_string_t(requestData.path)); 245 | 246 | switch (requestData.requestType) 247 | { 248 | case E_CONTENT_TYPE::json: 249 | { 250 | if (!requestData.bodyJson.is_null()) 251 | { 252 | request.set_body(requestData.bodyJson); 253 | } 254 | request.headers().set_content_type(U("application/json")); 255 | break; 256 | } 257 | case E_CONTENT_TYPE::string: 258 | { 259 | request.set_body(requestData.bodyString); 260 | break; 261 | } 262 | } 263 | 264 | http_response response = cd.client->request(request).get(); 265 | std::string body = response.extract_utf8string().get(); 266 | 267 | responseData.status = response.status_code(); 268 | responseData.rawBody = body; 269 | responseData.responseType = requestData.requestType; 270 | } 271 | 272 | web::http::method Impl::methodName(E_HTTP_METHOD id) 273 | { 274 | switch (id) 275 | { 276 | case E_HTTP_METHOD::HTTP_METHOD_GET: 277 | return utility::conversions::to_string_t("GET"); 278 | case E_HTTP_METHOD::HTTP_METHOD_HEAD: 279 | return utility::conversions::to_string_t("HEAD"); 280 | case E_HTTP_METHOD::HTTP_METHOD_POST: 281 | return utility::conversions::to_string_t("POST"); 282 | case E_HTTP_METHOD::HTTP_METHOD_PUT: 283 | return utility::conversions::to_string_t("PUT"); 284 | case E_HTTP_METHOD::HTTP_METHOD_DELETE: 285 | return utility::conversions::to_string_t("DELETE"); 286 | case E_HTTP_METHOD::HTTP_METHOD_CONNECT: 287 | return utility::conversions::to_string_t("CONNECT"); 288 | case E_HTTP_METHOD::HTTP_METHOD_OPTIONS: 289 | return utility::conversions::to_string_t("OPTIONS"); 290 | case E_HTTP_METHOD::HTTP_METHOD_TRACE: 291 | return utility::conversions::to_string_t("TRACE"); 292 | case E_HTTP_METHOD::HTTP_METHOD_PATCH: 293 | return utility::conversions::to_string_t("PATCH"); 294 | } 295 | throw std::invalid_argument("HTTP method not found in enumerator"); 296 | } 297 | 298 | std::vector Impl::gatherResponses() 299 | { 300 | std::vector tasks; 301 | 302 | // if we can't lock the mutex, don't block, just return and try next tick 303 | if (responseQueueLock.try_lock()) 304 | { 305 | ResponseData response; 306 | while (!responseQueue.empty()) 307 | { 308 | response = responseQueue.top(); 309 | tasks.push_back(response); 310 | responseQueue.pop(); 311 | } 312 | responseQueueLock.unlock(); 313 | } 314 | 315 | return tasks; 316 | } 317 | 318 | int Impl::WebSocketClient(AMX *amx, std::string address, std::string callback) 319 | { 320 | int id = websocketClientsTableCounter++; 321 | 322 | websocket_client_config wcc; 323 | #ifndef _WIN32 324 | wcc.set_ssl_context_callback([](boost::asio::ssl::context &ctx) 325 | { 326 | ctx.set_default_verify_paths(); 327 | ctx.load_verify_file("/etc/ssl/certs/ca-certificates.crt"); 328 | }); 329 | #endif 330 | websocket_callback_client *client = new websocket_callback_client(wcc); 331 | if (client == nullptr) 332 | { 333 | return -1; 334 | } 335 | 336 | WebSocketClientData wsc = {id, client, address, callback, false, amx}; 337 | websocketClientsTable[id] = wsc; 338 | auto &entry = websocketClientsTable[id]; 339 | try 340 | { 341 | startWebSocketListener(entry); 342 | } 343 | catch (std::exception &e) 344 | { 345 | logprintf("ERROR: WebSocketClient failed: %s", e.what()); 346 | logOpenSslErrors("WebSocketClient"); 347 | return -1; 348 | } 349 | 350 | return id; 351 | } 352 | 353 | int Impl::WebSocketSend(int id, std::string data) 354 | { 355 | auto it = websocketClientsTable.find(id); 356 | if (it == websocketClientsTable.end() || it->second.client == nullptr) 357 | { 358 | logprintf("ERROR: WebSocketSend failed, invalid client id %d", id); 359 | return -1; 360 | } 361 | 362 | try 363 | { 364 | websocket_outgoing_message msg; 365 | msg.set_utf8_message(data); 366 | it->second.client->send(msg); 367 | } 368 | catch (const std::exception &e) 369 | { 370 | logprintf("ERROR: WebSocketSend failed: %s", e.what()); 371 | return -1; 372 | } 373 | 374 | return 0; 375 | } 376 | 377 | int Impl::JsonWebSocketClient(AMX *amx, std::string address, std::string callback) 378 | { 379 | int id = websocketClientsTableCounter++; 380 | 381 | websocket_client_config wcc; 382 | #ifndef _WIN32 383 | wcc.set_ssl_context_callback([](boost::asio::ssl::context &ctx) 384 | { 385 | ctx.set_default_verify_paths(); 386 | ctx.load_verify_file("/etc/ssl/certs/ca-certificates.crt"); 387 | }); 388 | #endif 389 | websocket_callback_client *client = new websocket_callback_client(wcc); 390 | if (client == nullptr) 391 | { 392 | return -1; 393 | } 394 | 395 | WebSocketClientData wsc = {id, client, address, callback, true, amx}; 396 | websocketClientsTable[id] = wsc; 397 | auto &entry = websocketClientsTable[id]; 398 | try 399 | { 400 | startWebSocketListener(entry); 401 | } 402 | catch (std::exception &e) 403 | { 404 | logprintf("ERROR: JsonWebSocketClient failed: %s", e.what()); 405 | logOpenSslErrors("JsonWebSocketClient"); 406 | return -1; 407 | } 408 | 409 | return id; 410 | } 411 | 412 | int Impl::JsonWebSocketSend(int id, web::json::value json) 413 | { 414 | auto it = websocketClientsTable.find(id); 415 | if (it == websocketClientsTable.end() || it->second.client == nullptr) 416 | { 417 | logprintf("ERROR: JsonWebSocketSend failed, invalid client id %d", id); 418 | return -1; 419 | } 420 | 421 | try 422 | { 423 | websocket_outgoing_message msg; 424 | msg.set_utf8_message(utility::conversions::to_utf8string(json.serialize())); 425 | it->second.client->send(msg); 426 | } 427 | catch (const std::exception &e) 428 | { 429 | logprintf("ERROR: JsonWebSocketSend failed: %s", e.what()); 430 | return -1; 431 | } 432 | return 0; 433 | } 434 | 435 | void Impl::startWebSocketListener(WebSocketClientData &wsc) 436 | { 437 | wsc.client->set_message_handler([wsc](const websocket_incoming_message &msg) -> void 438 | { 439 | std::string raw = msg.extract_string().get(); 440 | 441 | ResponseData responseData; 442 | responseData.amx = wsc.amx; 443 | responseData.id = wsc.id; 444 | responseData.callback = wsc.callback; 445 | responseData.rawBody = raw; 446 | responseData.responseType = wsc.isJson ? E_CONTENT_TYPE::json : E_CONTENT_TYPE::string; 447 | responseData.isWebSocket = true; 448 | 449 | { 450 | std::lock_guard lock(responseQueueLock); 451 | responseQueue.push(responseData); 452 | } }); 453 | 454 | wsc.client->set_close_handler([id = wsc.id](websocket_close_status status, const utility::string_t &reason, const std::error_code &error) 455 | { 456 | if (!error) 457 | { 458 | return; 459 | } 460 | auto reasonUtf8 = utility::conversions::to_utf8string(reason); 461 | logprintf("WARN: WebSocket %d closed during status %d, reason '%s', error %d (%s)", 462 | id, 463 | static_cast(status), 464 | reasonUtf8.c_str(), 465 | error.value(), 466 | error.message().c_str()); 467 | }); 468 | 469 | wsc.client->connect(utility::conversions::to_string_t(wsc.address)).wait(); 470 | } 471 | -------------------------------------------------------------------------------- /lib/samp-plugin-sdk/amx/amx.h: -------------------------------------------------------------------------------- 1 | /* Pawn Abstract Machine (for the Pawn language) 2 | * 3 | * Copyright (c) ITB CompuPhase, 1997-2005 4 | * 5 | * This software is provided "as-is", without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in 15 | * a product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 2. Altered source versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. 19 | * 3. This notice may not be removed or altered from any source distribution. 20 | * 21 | * Version: $Id: amx.h,v 1.5 2006/03/26 16:56:15 spookie Exp $ 22 | */ 23 | 24 | #if defined FREEBSD && !defined __FreeBSD__ 25 | #define __FreeBSD__ 26 | #endif 27 | #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ 28 | #include "sclinux.h" 29 | #endif 30 | 31 | #ifndef AMX_H_INCLUDED 32 | #define AMX_H_INCLUDED 33 | 34 | #if defined HAVE_STDINT_H 35 | #include 36 | #else 37 | #if defined __LCC__ || defined __DMC__ || defined LINUX 38 | #if defined HAVE_INTTYPES_H 39 | #include 40 | #else 41 | #include 42 | #endif 43 | #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L 44 | /* The ISO C99 defines the int16_t and int_32t types. If the compiler got 45 | * here, these types are probably undefined. 46 | */ 47 | #if defined __MACH__ 48 | #include 49 | typedef unsigned short int uint16_t; 50 | typedef unsigned long int uint32_t; 51 | #elif defined __FreeBSD__ 52 | #include 53 | #else 54 | typedef short int int16_t; 55 | typedef unsigned short int uint16_t; 56 | #if defined SN_TARGET_PS2 57 | typedef int int32_t; 58 | typedef unsigned int uint32_t; 59 | #else 60 | typedef long int int32_t; 61 | typedef unsigned long int uint32_t; 62 | #endif 63 | #if defined __WIN32__ || defined _WIN32 || defined WIN32 64 | typedef __int64 int64_t; 65 | typedef unsigned __int64 uint64_t; 66 | #define HAVE_I64 67 | #elif defined __GNUC__ 68 | typedef long long int64_t; 69 | typedef unsigned long long uint64_t; 70 | #define HAVE_I64 71 | #endif 72 | #endif 73 | #endif 74 | #define HAVE_STDINT_H 75 | #endif 76 | #if defined _LP64 || defined WIN64 || defined _WIN64 77 | #if !defined __64BIT__ 78 | #define __64BIT__ 79 | #endif 80 | #endif 81 | 82 | #if HAVE_ALLOCA_H 83 | #include 84 | #elif HAVE_MALLOC_H //alloca could be also defined here 85 | #include 86 | #endif 87 | #if defined __WIN32__ || defined _WIN32 || defined WIN32 /* || defined __MSDOS__ */ 88 | #if !defined alloca 89 | #define alloca(n) _alloca(n) 90 | #endif 91 | #endif 92 | 93 | #if !defined arraysize 94 | #define arraysize(array) (sizeof(array) / sizeof((array)[0])) 95 | #endif 96 | 97 | #ifdef __cplusplus 98 | extern "C" { 99 | #endif 100 | 101 | #if defined PAWN_DLL 102 | #if !defined AMX_NATIVE_CALL 103 | #define AMX_NATIVE_CALL __stdcall 104 | #endif 105 | #if !defined AMXAPI 106 | #define AMXAPI __stdcall 107 | #endif 108 | #endif 109 | 110 | /* calling convention for native functions */ 111 | #if !defined AMX_NATIVE_CALL 112 | #define AMX_NATIVE_CALL 113 | #endif 114 | /* calling convention for all interface functions and callback functions */ 115 | #if !defined AMXAPI 116 | #if defined STDECL 117 | #define AMXAPI __stdcall 118 | #elif defined CDECL 119 | #define AMXAPI __cdecl 120 | #elif defined GCC_HASCLASSVISIBILITY 121 | #define AMXAPI __attribute__ ((visibility("default"))) 122 | #else 123 | #define AMXAPI 124 | #endif 125 | #endif 126 | #if !defined AMXEXPORT 127 | #define AMXEXPORT 128 | #endif 129 | 130 | /* File format version Required AMX version 131 | * 0 (original version) 0 132 | * 1 (opcodes JUMP.pri, SWITCH and CASETBL) 1 133 | * 2 (compressed files) 2 134 | * 3 (public variables) 2 135 | * 4 (opcodes SWAP.pri/alt and PUSHADDR) 4 136 | * 5 (tagnames table) 4 137 | * 6 (reformatted header) 6 138 | * 7 (name table, opcodes SYMTAG & SYSREQ.D) 7 139 | * 8 (opcode STMT, renewed debug interface) 8 140 | */ 141 | #define CUR_FILE_VERSION 8 /* current file version; also the current AMX version */ 142 | #define MIN_FILE_VERSION 6 /* lowest supported file format version for the current AMX version */ 143 | #define MIN_AMX_VERSION 8 /* minimum AMX version needed to support the current file format */ 144 | 145 | #if !defined PAWN_CELL_SIZE 146 | #define PAWN_CELL_SIZE 32 /* by default, use 32-bit cells */ 147 | #endif 148 | #if PAWN_CELL_SIZE==16 149 | typedef uint16_t ucell; 150 | typedef int16_t cell; 151 | #elif PAWN_CELL_SIZE==32 152 | typedef uint32_t ucell; 153 | typedef int32_t cell; 154 | #elif PAWN_CELL_SIZE==64 155 | typedef uint64_t ucell; 156 | typedef int64_t cell; 157 | #else 158 | #error Unsupported cell size (PAWN_CELL_SIZE) 159 | #endif 160 | 161 | #define UNPACKEDMAX ((1L << (sizeof(cell)-1)*8) - 1) 162 | #define UNLIMITED (~1u >> 1) 163 | 164 | struct tagAMX; 165 | typedef cell (AMX_NATIVE_CALL *AMX_NATIVE)(struct tagAMX *amx, cell *params); 166 | typedef int (AMXAPI *AMX_CALLBACK)(struct tagAMX *amx, cell index, 167 | cell *result, cell *params); 168 | typedef int (AMXAPI *AMX_DEBUG)(struct tagAMX *amx); 169 | #if !defined _FAR 170 | #define _FAR 171 | #endif 172 | 173 | #if defined _MSC_VER 174 | #pragma warning(disable:4103) /* disable warning message 4103 that complains 175 | * about pragma pack in a header file */ 176 | #pragma warning(disable:4100) /* "'%$S' : unreferenced formal parameter" */ 177 | #endif 178 | 179 | /* Some compilers do not support the #pragma align, which should be fine. Some 180 | * compilers give a warning on unknown #pragmas, which is not so fine... 181 | */ 182 | #if (defined SN_TARGET_PS2 || defined __GNUC__) && !defined AMX_NO_ALIGN 183 | #define AMX_NO_ALIGN 184 | #endif 185 | 186 | #if defined __GNUC__ 187 | #define PACKED __attribute__((packed)) 188 | #else 189 | #define PACKED 190 | #endif 191 | 192 | #if !defined AMX_NO_ALIGN 193 | #if defined LINUX || defined __FreeBSD__ 194 | #pragma pack(1) /* structures must be packed (byte-aligned) */ 195 | #elif defined MACOS && defined __MWERKS__ 196 | #pragma options align=mac68k 197 | #else 198 | #pragma pack(push) 199 | #pragma pack(1) /* structures must be packed (byte-aligned) */ 200 | #if defined __TURBOC__ 201 | #pragma option -a- /* "pack" pragma for older Borland compilers */ 202 | #endif 203 | #endif 204 | #endif 205 | 206 | typedef struct tagAMX_NATIVE_INFO { 207 | const char _FAR *name PACKED; 208 | AMX_NATIVE func PACKED; 209 | } PACKED AMX_NATIVE_INFO; 210 | 211 | #define AMX_USERNUM 4 212 | #define sEXPMAX 19 /* maximum name length for file version <= 6 */ 213 | #define sNAMEMAX 31 /* maximum name length of symbol name */ 214 | 215 | typedef struct tagAMX_FUNCSTUB { 216 | ucell address PACKED; 217 | char name[sEXPMAX+1] PACKED; 218 | } PACKED AMX_FUNCSTUB; 219 | 220 | typedef struct tagFUNCSTUBNT { 221 | ucell address PACKED; 222 | uint32_t nameofs PACKED; 223 | } PACKED AMX_FUNCSTUBNT; 224 | 225 | /* The AMX structure is the internal structure for many functions. Not all 226 | * fields are valid at all times; many fields are cached in local variables. 227 | */ 228 | typedef struct tagAMX { 229 | unsigned char _FAR *base PACKED; /* points to the AMX header plus the code, optionally also the data */ 230 | unsigned char _FAR *data PACKED; /* points to separate data+stack+heap, may be NULL */ 231 | AMX_CALLBACK callback PACKED; 232 | AMX_DEBUG debug PACKED; /* debug callback */ 233 | /* for external functions a few registers must be accessible from the outside */ 234 | cell cip PACKED; /* instruction pointer: relative to base + amxhdr->cod */ 235 | cell frm PACKED; /* stack frame base: relative to base + amxhdr->dat */ 236 | cell hea PACKED; /* top of the heap: relative to base + amxhdr->dat */ 237 | cell hlw PACKED; /* bottom of the heap: relative to base + amxhdr->dat */ 238 | cell stk PACKED; /* stack pointer: relative to base + amxhdr->dat */ 239 | cell stp PACKED; /* top of the stack: relative to base + amxhdr->dat */ 240 | int flags PACKED; /* current status, see amx_Flags() */ 241 | /* user data */ 242 | long usertags[AMX_USERNUM] PACKED; 243 | void _FAR *userdata[AMX_USERNUM] PACKED; 244 | /* native functions can raise an error */ 245 | int error PACKED; 246 | /* passing parameters requires a "count" field */ 247 | int paramcount; 248 | /* the sleep opcode needs to store the full AMX status */ 249 | cell pri PACKED; 250 | cell alt PACKED; 251 | cell reset_stk PACKED; 252 | cell reset_hea PACKED; 253 | cell sysreq_d PACKED; /* relocated address/value for the SYSREQ.D opcode */ 254 | #if defined JIT 255 | /* support variables for the JIT */ 256 | int reloc_size PACKED; /* required temporary buffer for relocations */ 257 | long code_size PACKED; /* estimated memory footprint of the native code */ 258 | #endif 259 | } PACKED AMX; 260 | 261 | /* The AMX_HEADER structure is both the memory format as the file format. The 262 | * structure is used internaly. 263 | */ 264 | typedef struct tagAMX_HEADER { 265 | int32_t size PACKED; /* size of the "file" */ 266 | uint16_t magic PACKED; /* signature */ 267 | char file_version PACKED; /* file format version */ 268 | char amx_version PACKED; /* required version of the AMX */ 269 | int16_t flags PACKED; 270 | int16_t defsize PACKED; /* size of a definition record */ 271 | int32_t cod PACKED; /* initial value of COD - code block */ 272 | int32_t dat PACKED; /* initial value of DAT - data block */ 273 | int32_t hea PACKED; /* initial value of HEA - start of the heap */ 274 | int32_t stp PACKED; /* initial value of STP - stack top */ 275 | int32_t cip PACKED; /* initial value of CIP - the instruction pointer */ 276 | int32_t publics PACKED; /* offset to the "public functions" table */ 277 | int32_t natives PACKED; /* offset to the "native functions" table */ 278 | int32_t libraries PACKED; /* offset to the table of libraries */ 279 | int32_t pubvars PACKED; /* the "public variables" table */ 280 | int32_t tags PACKED; /* the "public tagnames" table */ 281 | int32_t nametable PACKED; /* name table */ 282 | } PACKED AMX_HEADER; 283 | 284 | #if PAWN_CELL_SIZE==16 285 | #define AMX_MAGIC 0xf1e2 286 | #elif PAWN_CELL_SIZE==32 287 | #define AMX_MAGIC 0xf1e0 288 | #elif PAWN_CELL_SIZE==64 289 | #define AMX_MAGIC 0xf1e1 290 | #endif 291 | 292 | enum { 293 | AMX_ERR_NONE, 294 | /* reserve the first 15 error codes for exit codes of the abstract machine */ 295 | AMX_ERR_EXIT, /* forced exit */ 296 | AMX_ERR_ASSERT, /* assertion failed */ 297 | AMX_ERR_STACKERR, /* stack/heap collision */ 298 | AMX_ERR_BOUNDS, /* index out of bounds */ 299 | AMX_ERR_MEMACCESS, /* invalid memory access */ 300 | AMX_ERR_INVINSTR, /* invalid instruction */ 301 | AMX_ERR_STACKLOW, /* stack underflow */ 302 | AMX_ERR_HEAPLOW, /* heap underflow */ 303 | AMX_ERR_CALLBACK, /* no callback, or invalid callback */ 304 | AMX_ERR_NATIVE, /* native function failed */ 305 | AMX_ERR_DIVIDE, /* divide by zero */ 306 | AMX_ERR_SLEEP, /* go into sleepmode - code can be restarted */ 307 | AMX_ERR_INVSTATE, /* invalid state for this access */ 308 | 309 | AMX_ERR_MEMORY = 16, /* out of memory */ 310 | AMX_ERR_FORMAT, /* invalid file format */ 311 | AMX_ERR_VERSION, /* file is for a newer version of the AMX */ 312 | AMX_ERR_NOTFOUND, /* function not found */ 313 | AMX_ERR_INDEX, /* invalid index parameter (bad entry point) */ 314 | AMX_ERR_DEBUG, /* debugger cannot run */ 315 | AMX_ERR_INIT, /* AMX not initialized (or doubly initialized) */ 316 | AMX_ERR_USERDATA, /* unable to set user data field (table full) */ 317 | AMX_ERR_INIT_JIT, /* cannot initialize the JIT */ 318 | AMX_ERR_PARAMS, /* parameter error */ 319 | AMX_ERR_DOMAIN, /* domain error, expression result does not fit in range */ 320 | AMX_ERR_GENERAL, /* general error (unknown or unspecific error) */ 321 | }; 322 | 323 | /* AMX_FLAG_CHAR16 0x01 no longer used */ 324 | #define AMX_FLAG_DEBUG 0x02 /* symbolic info. available */ 325 | #define AMX_FLAG_COMPACT 0x04 /* compact encoding */ 326 | #define AMX_FLAG_BYTEOPC 0x08 /* opcode is a byte (not a cell) */ 327 | #define AMX_FLAG_NOCHECKS 0x10 /* no array bounds checking; no STMT opcode */ 328 | #define AMX_FLAG_NTVREG 0x1000 /* all native functions are registered */ 329 | #define AMX_FLAG_JITC 0x2000 /* abstract machine is JIT compiled */ 330 | #define AMX_FLAG_BROWSE 0x4000 /* busy browsing */ 331 | #define AMX_FLAG_RELOC 0x8000 /* jump/call addresses relocated */ 332 | 333 | #define AMX_EXEC_MAIN -1 /* start at program entry point */ 334 | #define AMX_EXEC_CONT -2 /* continue from last address */ 335 | 336 | #define AMX_USERTAG(a,b,c,d) ((a) | ((b)<<8) | ((long)(c)<<16) | ((long)(d)<<24)) 337 | 338 | #if !defined AMX_COMPACTMARGIN 339 | #define AMX_COMPACTMARGIN 64 340 | #endif 341 | 342 | /* for native functions that use floating point parameters, the following 343 | * two macros are convenient for casting a "cell" into a "float" type _without_ 344 | * changing the bit pattern 345 | */ 346 | #if PAWN_CELL_SIZE==32 347 | #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ 348 | #define amx_ctof(c) ( * ((float*)&c) ) /* cell to float */ 349 | #elif PAWN_CELL_SIZE==64 350 | #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ 351 | #define amx_ctof(c) ( * ((double*)&c) ) /* cell to float */ 352 | #else 353 | #error Unsupported cell size 354 | #endif 355 | 356 | #define amx_StrParam(amx,param,result) \ 357 | do { \ 358 | cell *amx_cstr_; int amx_length_; \ 359 | amx_GetAddr((amx), (param), &amx_cstr_); \ 360 | amx_StrLen(amx_cstr_, &amx_length_); \ 361 | if (amx_length_ > 0 && \ 362 | ((result) = (char*)alloca((amx_length_ + 1) * sizeof(*(result)))) != NULL) \ 363 | amx_GetString((char*)(result), amx_cstr_, sizeof(*(result))>1, amx_length_ + 1); \ 364 | else (result) = NULL; \ 365 | } while (0) 366 | 367 | uint16_t * AMXAPI amx_Align16(uint16_t *v); 368 | uint32_t * AMXAPI amx_Align32(uint32_t *v); 369 | #if defined _I64_MAX || defined HAVE_I64 370 | uint64_t * AMXAPI amx_Align64(uint64_t *v); 371 | #endif 372 | int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr); 373 | int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params); 374 | int AMXAPI amx_Cleanup(AMX *amx); 375 | int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data); 376 | int AMXAPI amx_Exec(AMX *amx, cell *retval, int index); 377 | int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index); 378 | int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index); 379 | int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr); 380 | int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname); 381 | int AMXAPI amx_Flags(AMX *amx,uint16_t *flags); 382 | int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr); 383 | int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname); 384 | int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname); 385 | int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr); 386 | int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size); 387 | int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id); 388 | int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr); 389 | int AMXAPI amx_Init(AMX *amx, void *program); 390 | int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code); 391 | int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap); 392 | int AMXAPI amx_NameLength(AMX *amx, int *length); 393 | AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func); 394 | int AMXAPI amx_NumNatives(AMX *amx, int *number); 395 | int AMXAPI amx_NumPublics(AMX *amx, int *number); 396 | int AMXAPI amx_NumPubVars(AMX *amx, int *number); 397 | int AMXAPI amx_NumTags(AMX *amx, int *number); 398 | int AMXAPI amx_Push(AMX *amx, cell value); 399 | int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells); 400 | int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar); 401 | int AMXAPI amx_RaiseError(AMX *amx, int error); 402 | int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number); 403 | int AMXAPI amx_Release(AMX *amx, cell amx_addr); 404 | int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback); 405 | int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug); 406 | int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size); 407 | int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr); 408 | int AMXAPI amx_StrLen(const cell *cstring, int *length); 409 | int AMXAPI amx_UTF8Check(const char *string, int *length); 410 | int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value); 411 | int AMXAPI amx_UTF8Len(const cell *cstr, int *length); 412 | int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); 413 | 414 | #if PAWN_CELL_SIZE==16 415 | #define amx_AlignCell(v) amx_Align16(v) 416 | #elif PAWN_CELL_SIZE==32 417 | #define amx_AlignCell(v) amx_Align32(v) 418 | #elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) 419 | #define amx_AlignCell(v) amx_Align64(v) 420 | #else 421 | #error Unsupported cell size 422 | #endif 423 | 424 | #define amx_RegisterFunc(amx, name, func) \ 425 | amx_Register((amx), amx_NativeInfo((name),(func)), 1); 426 | 427 | #if !defined AMX_NO_ALIGN 428 | #if defined LINUX || defined __FreeBSD__ 429 | #pragma pack() /* reset default packing */ 430 | #elif defined MACOS && defined __MWERKS__ 431 | #pragma options align=reset 432 | #else 433 | #pragma pack(pop) /* reset previous packing */ 434 | #endif 435 | #endif 436 | 437 | #ifdef __cplusplus 438 | } 439 | #endif 440 | 441 | #endif /* AMX_H_INCLUDED */ 442 | -------------------------------------------------------------------------------- /lib/samp-plugin-sdk/amxplugin.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2004-2009 SA-MP Team 5 | // 6 | //---------------------------------------------------------- 7 | // 8 | // This provides an interface to call amx library functions 9 | // within samp-server. 10 | // 11 | // To use: 12 | // Define the extern in your main source file: 13 | // extern void *pAMXFunctions; 14 | // And, in your Initialize function, assign: 15 | // pAMXFunctions = data[PLUGIN_DATA_AMX]; 16 | // 17 | // 18 | // WIN32: To regenerate thunks for calls from prototypes in amx.h 19 | // Run a regex with: 20 | // S: ^(.*)(AMXAPI amx_)([^(]*)([^\;]*);$ 21 | // R: NUDE \1\2\3\4\n{\n\t_asm mov eax, pAMXFunctions;\n\t_asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_\3*4];\n}\n 22 | // 23 | // LINUX/BSD: To regenerate thunks for calls from prototypes in amx.h 24 | // Run a regex with: 25 | // S: ^(.*)(AMXAPI amx_)([^(]*)([^\;]*);$ 26 | // R: typedef \1 AMXAPI (*amx_\3_t)\4;\n\1\2\3\4\n{\n\tamx_\3_t fn = ((amx_\3_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_\3];\n\treturn fn\4;\n}\n 27 | // Modify internal function calls as nessesary 28 | // 29 | 30 | //---------------------------------------------------------- 31 | 32 | #include "amx/amx.h" 33 | #include "plugincommon.h" 34 | 35 | //---------------------------------------------------------- 36 | 37 | void *pAMXFunctions; 38 | 39 | //---------------------------------------------------------- 40 | 41 | #if (defined(WIN32) || defined(_WIN32)) && defined(_MSC_VER) 42 | 43 | // Optimized Inline Assembly Thunks for MS VC++ 44 | 45 | #define NUDE _declspec(naked) 46 | 47 | NUDE uint16_t * AMXAPI amx_Align16(uint16_t *v) 48 | { 49 | _asm mov eax, pAMXFunctions; 50 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Align16*4]; 51 | } 52 | 53 | NUDE uint32_t * AMXAPI amx_Align32(uint32_t *v) 54 | { 55 | _asm mov eax, pAMXFunctions; 56 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Align32*4]; 57 | } 58 | 59 | #if defined _I64_MAX || defined HAVE_I64 60 | NUDE uint64_t * AMXAPI amx_Align64(uint64_t *v) 61 | { 62 | _asm mov eax, pAMXFunctions; 63 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Align64*4]; 64 | } 65 | 66 | #endif 67 | NUDE int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr) 68 | { 69 | _asm mov eax, pAMXFunctions; 70 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Allot*4]; 71 | } 72 | 73 | NUDE int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) 74 | { 75 | _asm mov eax, pAMXFunctions; 76 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Callback*4]; 77 | } 78 | 79 | NUDE int AMXAPI amx_Cleanup(AMX *amx) 80 | { 81 | _asm mov eax, pAMXFunctions; 82 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Cleanup*4]; 83 | } 84 | 85 | NUDE int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data) 86 | { 87 | _asm mov eax, pAMXFunctions; 88 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Clone*4]; 89 | } 90 | 91 | NUDE int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) 92 | { 93 | _asm mov eax, pAMXFunctions; 94 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Exec*4]; 95 | } 96 | 97 | NUDE int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index) 98 | { 99 | _asm mov eax, pAMXFunctions; 100 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_FindNative*4]; 101 | } 102 | 103 | NUDE int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index) 104 | { 105 | _asm mov eax, pAMXFunctions; 106 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_FindPublic*4]; 107 | } 108 | 109 | NUDE int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr) 110 | { 111 | _asm mov eax, pAMXFunctions; 112 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_FindPubVar*4]; 113 | } 114 | 115 | NUDE int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname) 116 | { 117 | _asm mov eax, pAMXFunctions; 118 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_FindTagId*4]; 119 | } 120 | 121 | NUDE int AMXAPI amx_Flags(AMX *amx,uint16_t *flags) 122 | { 123 | _asm mov eax, pAMXFunctions; 124 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Flags*4]; 125 | } 126 | 127 | NUDE int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr) 128 | { 129 | _asm mov eax, pAMXFunctions; 130 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_GetAddr*4]; 131 | } 132 | 133 | NUDE int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname) 134 | { 135 | _asm mov eax, pAMXFunctions; 136 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_GetNative*4]; 137 | } 138 | 139 | NUDE int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname) 140 | { 141 | _asm mov eax, pAMXFunctions; 142 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_GetPublic*4]; 143 | } 144 | 145 | NUDE int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr) 146 | { 147 | _asm mov eax, pAMXFunctions; 148 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_GetPubVar*4]; 149 | } 150 | 151 | NUDE int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size) 152 | { 153 | _asm mov eax, pAMXFunctions; 154 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_GetString*4]; 155 | } 156 | 157 | NUDE int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id) 158 | { 159 | _asm mov eax, pAMXFunctions; 160 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_GetTag*4]; 161 | } 162 | 163 | NUDE int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr) 164 | { 165 | _asm mov eax, pAMXFunctions; 166 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_GetUserData*4]; 167 | } 168 | 169 | NUDE int AMXAPI amx_Init(AMX *amx, void *program) 170 | { 171 | _asm mov eax, pAMXFunctions; 172 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Init*4]; 173 | } 174 | 175 | NUDE int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code) 176 | { 177 | _asm mov eax, pAMXFunctions; 178 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_InitJIT*4]; 179 | } 180 | 181 | NUDE int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap) 182 | { 183 | _asm mov eax, pAMXFunctions; 184 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_MemInfo*4]; 185 | } 186 | 187 | NUDE int AMXAPI amx_NameLength(AMX *amx, int *length) 188 | { 189 | _asm mov eax, pAMXFunctions; 190 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_NameLength*4]; 191 | } 192 | 193 | NUDE AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func) 194 | { 195 | _asm mov eax, pAMXFunctions; 196 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_NativeInfo*4]; 197 | } 198 | 199 | NUDE int AMXAPI amx_NumNatives(AMX *amx, int *number) 200 | { 201 | _asm mov eax, pAMXFunctions; 202 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_NumNatives*4]; 203 | } 204 | 205 | NUDE int AMXAPI amx_NumPublics(AMX *amx, int *number) 206 | { 207 | _asm mov eax, pAMXFunctions; 208 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_NumPublics*4]; 209 | } 210 | 211 | NUDE int AMXAPI amx_NumPubVars(AMX *amx, int *number) 212 | { 213 | _asm mov eax, pAMXFunctions; 214 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_NumPubVars*4]; 215 | } 216 | 217 | NUDE int AMXAPI amx_NumTags(AMX *amx, int *number) 218 | { 219 | _asm mov eax, pAMXFunctions; 220 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_NumTags*4]; 221 | } 222 | 223 | NUDE int AMXAPI amx_Push(AMX *amx, cell value) 224 | { 225 | _asm mov eax, pAMXFunctions; 226 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Push*4]; 227 | } 228 | 229 | NUDE int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells) 230 | { 231 | _asm mov eax, pAMXFunctions; 232 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_PushArray*4]; 233 | } 234 | 235 | NUDE int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar) 236 | { 237 | _asm mov eax, pAMXFunctions; 238 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_PushString*4]; 239 | } 240 | 241 | NUDE int AMXAPI amx_RaiseError(AMX *amx, int error) 242 | { 243 | _asm mov eax, pAMXFunctions; 244 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_RaiseError*4]; 245 | } 246 | 247 | NUDE int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number) 248 | { 249 | _asm mov eax, pAMXFunctions; 250 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Register*4]; 251 | } 252 | 253 | NUDE int AMXAPI amx_Release(AMX *amx, cell amx_addr) 254 | { 255 | _asm mov eax, pAMXFunctions; 256 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_Release*4]; 257 | } 258 | 259 | NUDE int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback) 260 | { 261 | _asm mov eax, pAMXFunctions; 262 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_SetCallback*4]; 263 | } 264 | 265 | NUDE int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug) 266 | { 267 | _asm mov eax, pAMXFunctions; 268 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_SetDebugHook*4]; 269 | } 270 | 271 | NUDE int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size) 272 | { 273 | _asm mov eax, pAMXFunctions; 274 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_SetString*4]; 275 | } 276 | 277 | NUDE int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr) 278 | { 279 | _asm mov eax, pAMXFunctions; 280 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_SetUserData*4]; 281 | } 282 | 283 | NUDE int AMXAPI amx_StrLen(const cell *cstring, int *length) 284 | { 285 | _asm mov eax, pAMXFunctions; 286 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_StrLen*4]; 287 | } 288 | 289 | NUDE int AMXAPI amx_UTF8Check(const char *string, int *length) 290 | { 291 | _asm mov eax, pAMXFunctions; 292 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_UTF8Check*4]; 293 | } 294 | 295 | NUDE int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value) 296 | { 297 | _asm mov eax, pAMXFunctions; 298 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_UTF8Get*4]; 299 | } 300 | 301 | NUDE int AMXAPI amx_UTF8Len(const cell *cstr, int *length) 302 | { 303 | _asm mov eax, pAMXFunctions; 304 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_UTF8Len*4]; 305 | } 306 | 307 | NUDE int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value) 308 | { 309 | _asm mov eax, pAMXFunctions; 310 | _asm jmp dword ptr [eax+PLUGIN_AMX_EXPORT_UTF8Put*4]; 311 | } 312 | 313 | #else 314 | 315 | // Unoptimized Thunks (Linux/BSD/non MSVC++) 316 | 317 | typedef uint16_t * AMXAPI (*amx_Align16_t)(uint16_t *v); 318 | uint16_t * AMXAPI amx_Align16(uint16_t *v) 319 | { 320 | amx_Align16_t fn = ((amx_Align16_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align16]; 321 | return fn(v); 322 | } 323 | 324 | typedef uint32_t * AMXAPI (*amx_Align32_t)(uint32_t *v); 325 | uint32_t * AMXAPI amx_Align32(uint32_t *v) 326 | { 327 | amx_Align32_t fn = ((amx_Align32_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align32]; 328 | return fn(v); 329 | } 330 | 331 | #if defined _I64_MAX || defined HAVE_I64 332 | typedef uint64_t * AMXAPI (*amx_Align64_t)(uint64_t *v); 333 | uint64_t * AMXAPI amx_Align64(uint64_t *v) 334 | { 335 | amx_Align64_t fn = ((amx_Align64_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align64]; 336 | return fn(v); 337 | } 338 | #endif 339 | 340 | typedef int AMXAPI (*amx_Allot_t)(AMX *amx, int cells, cell *amx_addr, cell **phys_addr); 341 | int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr) 342 | { 343 | amx_Allot_t fn = ((amx_Allot_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Allot]; 344 | return fn(amx, cells, amx_addr, phys_addr); 345 | } 346 | 347 | typedef int AMXAPI (*amx_Callback_t)(AMX *amx, cell index, cell *result, cell *params); 348 | int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) 349 | { 350 | amx_Callback_t fn = ((amx_Callback_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Callback]; 351 | return fn(amx, index, result, params); 352 | } 353 | 354 | typedef int AMXAPI (*amx_Cleanup_t)(AMX *amx); 355 | int AMXAPI amx_Cleanup(AMX *amx) 356 | { 357 | amx_Cleanup_t fn = ((amx_Cleanup_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Cleanup]; 358 | return fn(amx); 359 | } 360 | 361 | typedef int AMXAPI (*amx_Clone_t)(AMX *amxClone, AMX *amxSource, void *data); 362 | int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data) 363 | { 364 | amx_Clone_t fn = ((amx_Clone_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Clone]; 365 | return fn(amxClone, amxSource, data); 366 | } 367 | 368 | typedef int AMXAPI (*amx_Exec_t)(AMX *amx, cell *retval, int index); 369 | int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) 370 | { 371 | amx_Exec_t fn = ((amx_Exec_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Exec]; 372 | return fn(amx, retval, index); 373 | } 374 | 375 | typedef int AMXAPI (*amx_FindNative_t)(AMX *amx, const char *name, int *index); 376 | int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index) 377 | { 378 | amx_FindNative_t fn = ((amx_FindNative_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_FindNative]; 379 | return fn(amx, name, index); 380 | } 381 | 382 | typedef int AMXAPI (*amx_FindPublic_t)(AMX *amx, const char *funcname, int *index); 383 | int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index) 384 | { 385 | amx_FindPublic_t fn = ((amx_FindPublic_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_FindPublic]; 386 | return fn(amx, funcname, index); 387 | } 388 | 389 | typedef int AMXAPI (*amx_FindPubVar_t)(AMX *amx, const char *varname, cell *amx_addr); 390 | int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr) 391 | { 392 | amx_FindPubVar_t fn = ((amx_FindPubVar_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_FindPubVar]; 393 | return fn(amx, varname, amx_addr); 394 | } 395 | 396 | typedef int AMXAPI (*amx_FindTagId_t)(AMX *amx, cell tag_id, char *tagname); 397 | int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname) 398 | { 399 | amx_FindTagId_t fn = ((amx_FindTagId_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_FindTagId]; 400 | return fn(amx, tag_id, tagname); 401 | } 402 | 403 | typedef int AMXAPI (*amx_Flags_t)(AMX *amx,uint16_t *flags); 404 | int AMXAPI amx_Flags(AMX *amx,uint16_t *flags) 405 | { 406 | amx_Flags_t fn = ((amx_Flags_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Flags]; 407 | return fn(amx,flags); 408 | } 409 | 410 | typedef int AMXAPI (*amx_GetAddr_t)(AMX *amx,cell amx_addr,cell **phys_addr); 411 | int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr) 412 | { 413 | amx_GetAddr_t fn = ((amx_GetAddr_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetAddr]; 414 | return fn(amx,amx_addr,phys_addr); 415 | } 416 | 417 | typedef int AMXAPI (*amx_GetNative_t)(AMX *amx, int index, char *funcname); 418 | int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname) 419 | { 420 | amx_GetNative_t fn = ((amx_GetNative_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetNative]; 421 | return fn(amx, index, funcname); 422 | } 423 | 424 | typedef int AMXAPI (*amx_GetPublic_t)(AMX *amx, int index, char *funcname); 425 | int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname) 426 | { 427 | amx_GetPublic_t fn = ((amx_GetPublic_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetPublic]; 428 | return fn(amx, index, funcname); 429 | } 430 | 431 | typedef int AMXAPI (*amx_GetPubVar_t)(AMX *amx, int index, char *varname, cell *amx_addr); 432 | int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr) 433 | { 434 | amx_GetPubVar_t fn = ((amx_GetPubVar_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetPubVar]; 435 | return fn(amx, index, varname, amx_addr); 436 | } 437 | 438 | typedef int AMXAPI (*amx_GetString_t)(char *dest,const cell *source, int use_wchar, size_t size); 439 | int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size) 440 | { 441 | amx_GetString_t fn = ((amx_GetString_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetString]; 442 | return fn(dest,source, use_wchar, size); 443 | } 444 | 445 | typedef int AMXAPI (*amx_GetTag_t)(AMX *amx, int index, char *tagname, cell *tag_id); 446 | int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id) 447 | { 448 | amx_GetTag_t fn = ((amx_GetTag_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetTag]; 449 | return fn(amx, index, tagname, tag_id); 450 | } 451 | 452 | typedef int AMXAPI (*amx_GetUserData_t)(AMX *amx, long tag, void **ptr); 453 | int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr) 454 | { 455 | amx_GetUserData_t fn = ((amx_GetUserData_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetUserData]; 456 | return fn(amx, tag, ptr); 457 | } 458 | 459 | typedef int AMXAPI (*amx_Init_t)(AMX *amx, void *program); 460 | int AMXAPI amx_Init(AMX *amx, void *program) 461 | { 462 | amx_Init_t fn = ((amx_Init_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Init]; 463 | return fn(amx, program); 464 | } 465 | 466 | typedef int AMXAPI (*amx_InitJIT_t)(AMX *amx, void *reloc_table, void *native_code); 467 | int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code) 468 | { 469 | amx_InitJIT_t fn = ((amx_InitJIT_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_InitJIT]; 470 | return fn(amx, reloc_table, native_code); 471 | } 472 | 473 | typedef int AMXAPI (*amx_MemInfo_t)(AMX *amx, long *codesize, long *datasize, long *stackheap); 474 | int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap) 475 | { 476 | amx_MemInfo_t fn = ((amx_MemInfo_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_MemInfo]; 477 | return fn(amx, codesize, datasize, stackheap); 478 | } 479 | 480 | typedef int AMXAPI (*amx_NameLength_t)(AMX *amx, int *length); 481 | int AMXAPI amx_NameLength(AMX *amx, int *length) 482 | { 483 | amx_NameLength_t fn = ((amx_NameLength_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NameLength]; 484 | return fn(amx, length); 485 | } 486 | 487 | typedef AMX_NATIVE_INFO * AMXAPI (*amx_NativeInfo_t)(const char *name, AMX_NATIVE func); 488 | AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func) 489 | { 490 | amx_NativeInfo_t fn = ((amx_NativeInfo_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NativeInfo]; 491 | return fn(name, func); 492 | } 493 | 494 | typedef int AMXAPI (*amx_NumNatives_t)(AMX *amx, int *number); 495 | int AMXAPI amx_NumNatives(AMX *amx, int *number) 496 | { 497 | amx_NumNatives_t fn = ((amx_NumNatives_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NumNatives]; 498 | return fn(amx, number); 499 | } 500 | 501 | typedef int AMXAPI (*amx_NumPublics_t)(AMX *amx, int *number); 502 | int AMXAPI amx_NumPublics(AMX *amx, int *number) 503 | { 504 | amx_NumPublics_t fn = ((amx_NumPublics_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NumPublics]; 505 | return fn(amx, number); 506 | } 507 | 508 | typedef int AMXAPI (*amx_NumPubVars_t)(AMX *amx, int *number); 509 | int AMXAPI amx_NumPubVars(AMX *amx, int *number) 510 | { 511 | amx_NumPubVars_t fn = ((amx_NumPubVars_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NumPubVars]; 512 | return fn(amx, number); 513 | } 514 | 515 | typedef int AMXAPI (*amx_NumTags_t)(AMX *amx, int *number); 516 | int AMXAPI amx_NumTags(AMX *amx, int *number) 517 | { 518 | amx_NumTags_t fn = ((amx_NumTags_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NumTags]; 519 | return fn(amx, number); 520 | } 521 | 522 | typedef int AMXAPI (*amx_Push_t)(AMX *amx, cell value); 523 | int AMXAPI amx_Push(AMX *amx, cell value) 524 | { 525 | amx_Push_t fn = ((amx_Push_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Push]; 526 | return fn(amx, value); 527 | } 528 | 529 | typedef int AMXAPI (*amx_PushArray_t)(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells); 530 | int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells) 531 | { 532 | amx_PushArray_t fn = ((amx_PushArray_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_PushArray]; 533 | return fn(amx, amx_addr, phys_addr, array, numcells); 534 | } 535 | 536 | typedef int AMXAPI (*amx_PushString_t)(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar); 537 | int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar) 538 | { 539 | amx_PushString_t fn = ((amx_PushString_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_PushString]; 540 | return fn(amx, amx_addr, phys_addr, string, pack, use_wchar); 541 | } 542 | 543 | typedef int AMXAPI (*amx_RaiseError_t)(AMX *amx, int error); 544 | int AMXAPI amx_RaiseError(AMX *amx, int error) 545 | { 546 | amx_RaiseError_t fn = ((amx_RaiseError_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_RaiseError]; 547 | return fn(amx, error); 548 | } 549 | 550 | typedef int AMXAPI (*amx_Register_t)(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number); 551 | int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number) 552 | { 553 | amx_Register_t fn = ((amx_Register_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Register]; 554 | return fn(amx, nativelist, number); 555 | } 556 | 557 | typedef int AMXAPI (*amx_Release_t)(AMX *amx, cell amx_addr); 558 | int AMXAPI amx_Release(AMX *amx, cell amx_addr) 559 | { 560 | amx_Release_t fn = ((amx_Release_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Release]; 561 | return fn(amx, amx_addr); 562 | } 563 | 564 | typedef int AMXAPI (*amx_SetCallback_t)(AMX *amx, AMX_CALLBACK callback); 565 | int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback) 566 | { 567 | amx_SetCallback_t fn = ((amx_SetCallback_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_SetCallback]; 568 | return fn(amx, callback); 569 | } 570 | 571 | typedef int AMXAPI (*amx_SetDebugHook_t)(AMX *amx, AMX_DEBUG debug); 572 | int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug) 573 | { 574 | amx_SetDebugHook_t fn = ((amx_SetDebugHook_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_SetDebugHook]; 575 | return fn(amx, debug); 576 | } 577 | 578 | typedef int AMXAPI (*amx_SetString_t)(cell *dest, const char *source, int pack, int use_wchar, size_t size); 579 | int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size) 580 | { 581 | amx_SetString_t fn = ((amx_SetString_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_SetString]; 582 | return fn(dest, source, pack, use_wchar, size); 583 | } 584 | 585 | typedef int AMXAPI (*amx_SetUserData_t)(AMX *amx, long tag, void *ptr); 586 | int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr) 587 | { 588 | amx_SetUserData_t fn = ((amx_SetUserData_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_SetUserData]; 589 | return fn(amx, tag, ptr); 590 | } 591 | 592 | typedef int AMXAPI (*amx_StrLen_t)(const cell *cstring, int *length); 593 | int AMXAPI amx_StrLen(const cell *cstring, int *length) 594 | { 595 | amx_StrLen_t fn = ((amx_StrLen_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_StrLen]; 596 | return fn(cstring, length); 597 | } 598 | 599 | typedef int AMXAPI (*amx_UTF8Check_t)(const char *string, int *length); 600 | int AMXAPI amx_UTF8Check(const char *string, int *length) 601 | { 602 | amx_UTF8Check_t fn = ((amx_UTF8Check_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_UTF8Check]; 603 | return fn(string, length); 604 | } 605 | 606 | typedef int AMXAPI (*amx_UTF8Get_t)(const char *string, const char **endptr, cell *value); 607 | int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value) 608 | { 609 | amx_UTF8Get_t fn = ((amx_UTF8Get_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_UTF8Get]; 610 | return fn(string, endptr, value); 611 | } 612 | 613 | typedef int AMXAPI (*amx_UTF8Len_t)(const cell *cstr, int *length); 614 | int AMXAPI amx_UTF8Len(const cell *cstr, int *length) 615 | { 616 | amx_UTF8Len_t fn = ((amx_UTF8Len_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_UTF8Len]; 617 | return fn(cstr, length); 618 | } 619 | 620 | typedef int AMXAPI (*amx_UTF8Put_t)(char *string, char **endptr, int maxchars, cell value); 621 | int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value) 622 | { 623 | amx_UTF8Put_t fn = ((amx_UTF8Put_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_UTF8Put]; 624 | return fn(string, endptr, maxchars, value); 625 | } 626 | 627 | #endif 628 | 629 | //---------------------------------------------------------- 630 | // EOF 631 | -------------------------------------------------------------------------------- /src/natives.cpp: -------------------------------------------------------------------------------- 1 | #include "natives.hpp" 2 | #include "impl.hpp" 3 | // #include "plugin-natives\NativeFunc.hpp" 4 | 5 | // nodeTable maps numeric identifiers to JSON node pointers. 6 | std::unordered_map Natives::JSON::nodeTable; 7 | int Natives::JSON::jsonPoolCounter = 0; 8 | 9 | int Natives::RequestsClient(AMX *amx, cell *params) 10 | { 11 | std::string endpoint = amx_GetCppString(amx, params[1]); 12 | return Impl::RequestsClient(endpoint, params[2]); 13 | } 14 | 15 | int Natives::RequestHeaders(AMX *amx, cell *params) 16 | { 17 | std::vector> headers; 18 | std::string key; 19 | for (size_t i = 1; i <= params[0] / sizeof(cell); i++) 20 | { 21 | std::string header = amx_GetCppString(amx, params[i]); 22 | if (i & 1) 23 | { 24 | key = header; 25 | } 26 | else 27 | { 28 | headers.push_back(std::make_pair(key, header)); 29 | } 30 | } 31 | return Impl::RequestHeaders(headers); 32 | } 33 | 34 | int Natives::Request(AMX *amx, cell *params) 35 | { 36 | int id = params[1]; 37 | std::string path = amx_GetCppString(amx, params[2]); 38 | Impl::E_HTTP_METHOD method = static_cast(params[3]); 39 | std::string callback = amx_GetCppString(amx, params[4]); 40 | std::string data = amx_GetCppString(amx, params[5]); 41 | int headers = params[6]; 42 | 43 | return Impl::Request(amx, id, path, method, callback, data, headers); 44 | } 45 | 46 | int Natives::RequestJSON(AMX *amx, cell *params) 47 | { 48 | int id = params[1]; 49 | std::string path = amx_GetCppString(amx, params[2]); 50 | Impl::E_HTTP_METHOD method = static_cast(params[3]); 51 | std::string callback = amx_GetCppString(amx, params[4]); 52 | auto obj = JSON::Get(params[5]); 53 | int headers = params[6]; 54 | 55 | return Impl::RequestJSON(amx, id, path, method, callback, obj, headers); 56 | } 57 | 58 | void Natives::processTick(std::set amx_List) 59 | { 60 | std::vector responses = Impl::gatherResponses(); 61 | for (auto response : responses) 62 | { 63 | AMX *currentAmx = nullptr; 64 | for (AMX *amx : amx_List) 65 | { 66 | if (amx != response.amx) 67 | { 68 | continue; 69 | } 70 | 71 | currentAmx = amx; 72 | break; 73 | } 74 | 75 | if (currentAmx == nullptr) 76 | { 77 | logprintf("WARN: received response for unknown AMX instance, dropping"); 78 | continue; 79 | } 80 | 81 | int error; 82 | int amx_idx; 83 | cell amx_addr; 84 | cell amx_ret; 85 | cell *phys_addr; 86 | 87 | error = amx_FindPublic(currentAmx, response.callback.c_str(), &amx_idx); 88 | if (error != AMX_ERR_NONE) 89 | { 90 | logprintf("ERROR: failed to locate public function '%s' in amx, error: %d", response.callback.c_str(), error); 91 | continue; 92 | } 93 | 94 | switch (response.responseType) 95 | { 96 | default: 97 | { 98 | logprintf("ERROR: Invalid response object type: %d", response.responseType); 99 | break; 100 | } 101 | case Impl::E_CONTENT_TYPE::empty: 102 | { 103 | // (Request:id, errorCode, errorMessage[], len) 104 | amx_Push(currentAmx, response.rawBody.length()); 105 | amx_PushString(currentAmx, &amx_addr, &phys_addr, response.rawBody.c_str(), 0, 0); 106 | amx_Push(currentAmx, response.status); 107 | amx_Push(currentAmx, response.id); 108 | 109 | amx_Exec(currentAmx, &amx_ret, amx_idx); 110 | amx_Release(currentAmx, amx_addr); 111 | 112 | break; 113 | } 114 | 115 | case Impl::E_CONTENT_TYPE::string: 116 | { 117 | amx_Push(currentAmx, response.rawBody.length()); 118 | amx_PushString(currentAmx, &amx_addr, &phys_addr, response.rawBody.c_str(), 0, 0); 119 | 120 | // signature is either 121 | // (Request:id, E_HTTP_STATUS:status, data[], dataLen) 122 | // or: 123 | // (WebSocket:id, data[], dataLen) 124 | // depending on whether the response is from a websocket 125 | if (!response.isWebSocket) 126 | { 127 | amx_Push(currentAmx, response.status); 128 | } 129 | 130 | amx_Push(currentAmx, response.id); 131 | 132 | amx_Exec(currentAmx, &amx_ret, amx_idx); 133 | amx_Release(currentAmx, amx_addr); 134 | 135 | break; 136 | } 137 | 138 | case Impl::E_CONTENT_TYPE::json: 139 | { 140 | cell id = -1; 141 | try 142 | { 143 | json::value *obj = new json::value; 144 | *obj = json::value::parse(utility::conversions::to_string_t(response.rawBody)); 145 | id = JSON::Alloc(obj); 146 | } 147 | catch (std::exception e) 148 | { 149 | logprintf("ERROR: failed to parse response as JSON: '%s'", response.rawBody.c_str()); 150 | } 151 | 152 | amx_Push(currentAmx, id); 153 | // signature is either 154 | // (Request:id, E_HTTP_STATUS:status, Node:node) 155 | // or: 156 | // (WebSocket:id, Node:node) 157 | // depending on whether the response is from a websocket 158 | if (!response.isWebSocket) 159 | { 160 | amx_Push(currentAmx, response.status); 161 | } 162 | amx_Push(currentAmx, response.id); 163 | 164 | amx_Exec(currentAmx, &amx_ret, amx_idx); 165 | 166 | JSON::Erase(id); 167 | break; 168 | } 169 | } 170 | } 171 | } 172 | 173 | int Natives::WebSocketClient(AMX *amx, cell *params) 174 | { 175 | std::string address = amx_GetCppString(amx, params[1]); 176 | std::string callback = amx_GetCppString(amx, params[2]); 177 | return Impl::WebSocketClient(amx, address, callback); 178 | } 179 | 180 | int Natives::WebSocketSend(AMX *amx, cell *params) 181 | { 182 | int id = params[1]; 183 | std::string data = amx_GetCppString(amx, params[2]); 184 | return Impl::WebSocketSend(id, data); 185 | } 186 | 187 | int Natives::JsonWebSocketClient(AMX *amx, cell *params) 188 | { 189 | std::string address = amx_GetCppString(amx, params[1]); 190 | std::string callback = amx_GetCppString(amx, params[2]); 191 | return Impl::JsonWebSocketClient(amx, address, callback); 192 | } 193 | 194 | int Natives::JsonWebSocketSend(AMX *amx, cell *params) 195 | { 196 | int id = params[1]; 197 | auto obj = JSON::Get(params[2]); 198 | return Impl::JsonWebSocketSend(id, obj); 199 | } 200 | 201 | // JSON implementation is directly in the Natives section unlike other API. 202 | // this is purely to simplify things while working with JSON value types. 203 | 204 | int Natives::JSON::Parse(AMX *amx, cell *params) 205 | { 206 | std::string input = amx_GetCppString(amx, params[1]); 207 | cell *output; 208 | amx_GetAddr(amx, params[2], &output); 209 | 210 | web::json::value *obj = new web::json::value; 211 | 212 | try 213 | { 214 | *obj = web::json::value::parse(utility::conversions::to_string_t(input)); 215 | } 216 | catch (std::exception &e) 217 | { 218 | delete obj; 219 | logprintf("ERROR: JsonParse failed with: %s", e.what()); 220 | return 1; 221 | } 222 | 223 | *output = Alloc(obj); 224 | 225 | return 0; 226 | } 227 | 228 | int Natives::JSON::Stringify(AMX *amx, cell *params) 229 | { 230 | auto obj = Get(params[1], false); 231 | std::string s = utility::conversions::to_utf8string(obj.serialize()); 232 | 233 | amx_SetCppString(amx, params[2], s, params[3]); 234 | 235 | return 0; 236 | } 237 | 238 | int Natives::JSON::NodeType(AMX *amx, cell *params) 239 | { 240 | auto obj = Get(params[1], false); 241 | if (obj.is_null()) 242 | { 243 | return web::json::value::Null; 244 | } 245 | return obj.type(); 246 | } 247 | 248 | int Natives::JSON::Object(AMX *amx, cell *params) 249 | { 250 | std::string key; 251 | std::vector> fields; 252 | 253 | for (size_t i = 1; i <= params[0] / sizeof(cell); i++) 254 | { 255 | cell *addr = nullptr; 256 | amx_GetAddr(amx, params[i], &addr); 257 | 258 | if (addr == nullptr) 259 | { 260 | break; 261 | } 262 | 263 | if (i & 1) 264 | { 265 | int len = 0; 266 | amx_StrLen(addr, &len); 267 | if (len <= 0 || len > 512) 268 | { 269 | logprintf("ERROR: string length in Object out of bounds (%d)", len); 270 | return -1; 271 | } 272 | 273 | key = std::string(len, ' '); 274 | amx_GetString(&key[0], addr, 0, len + 1); 275 | } 276 | else 277 | { 278 | web::json::value obj = Get(*addr); 279 | if (obj == web::json::value::null()) 280 | { 281 | logprintf("ERROR: value node %d was invalid", *addr); 282 | return -2; 283 | } 284 | fields.push_back(std::make_pair(utility::conversions::to_string_t(key), obj)); 285 | } 286 | } 287 | 288 | web::json::value *obj = new web::json::value; 289 | *obj = web::json::value::object(fields); 290 | return Alloc(obj); 291 | } 292 | 293 | int Natives::JSON::Int(AMX *amx, cell *params) 294 | { 295 | web::json::value *obj = new web::json::value; 296 | *obj = web::json::value::number(params[1]); 297 | return Alloc(obj); 298 | } 299 | 300 | int Natives::JSON::Float(AMX *amx, cell *params) 301 | { 302 | web::json::value *obj = new web::json::value; 303 | *obj = web::json::value::number(amx_ctof(params[1])); 304 | return Alloc(obj); 305 | } 306 | 307 | int Natives::JSON::Bool(AMX *amx, cell *params) 308 | { 309 | web::json::value *obj = new web::json::value; 310 | *obj = web::json::value::boolean(params[1]); 311 | return Alloc(obj); 312 | } 313 | 314 | int Natives::JSON::String(AMX *amx, cell *params) 315 | { 316 | web::json::value *obj = new web::json::value; 317 | *obj = web::json::value::string(utility::conversions::to_string_t(amx_GetCppString(amx, params[1]))); 318 | return Alloc(obj); 319 | } 320 | 321 | int Natives::JSON::Array(AMX *amx, cell *params) 322 | { 323 | std::vector fields; 324 | 325 | for (size_t i = 1; i <= params[0] / sizeof(cell); i++) 326 | { 327 | cell *addr = nullptr; 328 | amx_GetAddr(amx, params[i], &addr); 329 | 330 | if (addr == nullptr) 331 | { 332 | break; 333 | } 334 | 335 | auto obj = Get(*addr); 336 | if (obj == web::json::value::null()) 337 | { 338 | logprintf("ERROR: value node %d was invalid", *addr); 339 | return -2; 340 | } 341 | fields.push_back(obj); 342 | } 343 | 344 | web::json::value *obj = new web::json::value; 345 | *obj = web::json::value::array(fields); 346 | return Alloc(obj); 347 | } 348 | 349 | int Natives::JSON::Append(AMX *amx, cell *params) 350 | { 351 | web::json::value a = Get(params[1], false); 352 | web::json::value b = Get(params[2]); 353 | int result; 354 | 355 | if (a.is_object() && b.is_object()) 356 | { 357 | web::json::value *c = new web::json::value; 358 | std::vector> newObject; 359 | for (auto entry : a.as_object()) 360 | { 361 | newObject.push_back(std::make_pair(entry.first, entry.second)); 362 | } 363 | for (auto entry : b.as_object()) 364 | { 365 | newObject.push_back(std::make_pair(entry.first, entry.second)); 366 | } 367 | *c = web::json::value::object(newObject); 368 | result = Alloc(c); 369 | } 370 | else if (a.is_array() && b.is_array()) 371 | { 372 | web::json::value *c = new web::json::value; 373 | std::vector newArray; 374 | for (auto entry : a.as_array()) 375 | { 376 | newArray.push_back(entry); 377 | } 378 | for (auto entry : b.as_array()) 379 | { 380 | newArray.push_back(entry); 381 | } 382 | *c = web::json::value::array(newArray); 383 | result = Alloc(c); 384 | } 385 | else 386 | { 387 | return -1; 388 | } 389 | 390 | return result; 391 | } 392 | 393 | int Natives::JSON::SetObject(AMX *amx, cell *params) 394 | { 395 | web::json::value *obj = GetPointer(params[1]); 396 | if (obj == nullptr) 397 | { 398 | return 1; 399 | } 400 | if (!obj->is_object()) 401 | { 402 | return 2; 403 | } 404 | 405 | web::json::value value = Get(params[3]); 406 | if (value == web::json::value::null()) 407 | { 408 | return 3; 409 | } 410 | 411 | utility::string_t key = utility::conversions::to_string_t(amx_GetCppString(amx, params[2])); 412 | obj->as_object()[key] = value; 413 | 414 | return 0; 415 | } 416 | 417 | int Natives::JSON::SetInt(AMX *amx, cell *params) 418 | { 419 | web::json::value *obj = GetPointer(params[1]); 420 | if (obj == nullptr) 421 | { 422 | return 1; 423 | } 424 | if (!obj->is_object()) 425 | { 426 | return 2; 427 | } 428 | 429 | utility::string_t key = utility::conversions::to_string_t(amx_GetCppString(amx, params[2])); 430 | obj->as_object()[key] = json::value::number(params[3]); 431 | 432 | return 0; 433 | } 434 | 435 | int Natives::JSON::SetFloat(AMX *amx, cell *params) 436 | { 437 | web::json::value *obj = GetPointer(params[1]); 438 | if (obj == nullptr) 439 | { 440 | return 1; 441 | } 442 | if (!obj->is_object()) 443 | { 444 | return 2; 445 | } 446 | 447 | utility::string_t key = utility::conversions::to_string_t(amx_GetCppString(amx, params[2])); 448 | obj->as_object()[key] = json::value::number(amx_ctof(params[3])); 449 | 450 | return 0; 451 | } 452 | 453 | int Natives::JSON::SetBool(AMX *amx, cell *params) 454 | { 455 | web::json::value *obj = GetPointer(params[1]); 456 | if (obj == nullptr) 457 | { 458 | return 1; 459 | } 460 | if (!obj->is_object()) 461 | { 462 | return 2; 463 | } 464 | 465 | utility::string_t key = utility::conversions::to_string_t(amx_GetCppString(amx, params[2])); 466 | obj->as_object()[key] = json::value::boolean(params[3]); 467 | 468 | return 0; 469 | } 470 | 471 | int Natives::JSON::SetString(AMX *amx, cell *params) 472 | { 473 | web::json::value *obj = GetPointer(params[1]); 474 | if (obj == nullptr) 475 | { 476 | return 1; 477 | } 478 | if (!obj->is_object()) 479 | { 480 | return 2; 481 | } 482 | 483 | utility::string_t key = utility::conversions::to_string_t(amx_GetCppString(amx, params[2])); 484 | obj->as_object()[key] = json::value::string(utility::conversions::to_string_t(amx_GetCppString(amx, params[3]))); 485 | 486 | return 0; 487 | } 488 | 489 | int Natives::JSON::GetObjectAlt(AMX *amx, cell *params) 490 | { 491 | web::json::value obj = Get(params[1]); 492 | if (obj == web::json::value::null()) 493 | { 494 | return 1; 495 | } 496 | 497 | std::string key = amx_GetCppString(amx, params[2]); 498 | 499 | web::json::value *result = new web::json::value(); 500 | try 501 | { 502 | auto &object = obj.as_object(); 503 | utility::string_t nativeKey = utility::conversions::to_string_t(key); 504 | auto it = object.find(nativeKey); 505 | if (it == object.end()) 506 | { 507 | delete result; 508 | return 2; 509 | } 510 | *result = it->second; 511 | } 512 | catch (...) 513 | { 514 | delete result; 515 | return 2; 516 | } 517 | cell id = Alloc(result); 518 | 519 | cell *addr = nullptr; 520 | amx_GetAddr(amx, params[3], &addr); 521 | *addr = id; 522 | 523 | return 0; 524 | } 525 | 526 | int Natives::JSON::GetInt(AMX *amx, cell *params) 527 | { 528 | web::json::value obj = Get(params[1], false); 529 | if (obj == web::json::value::null()) 530 | { 531 | return 1; 532 | } 533 | std::string key = amx_GetCppString(amx, params[2]); 534 | 535 | web::json::value target; 536 | try 537 | { 538 | target = obj.as_object().at(utility::conversions::to_string_t(key)); 539 | } 540 | catch (...) 541 | { 542 | return 2; 543 | } 544 | if (!target.is_integer()) 545 | { 546 | return 3; 547 | } 548 | 549 | cell *addr = nullptr; 550 | amx_GetAddr(amx, params[3], &addr); 551 | *addr = target.as_integer(); 552 | 553 | return 0; 554 | } 555 | 556 | int Natives::JSON::GetFloat(AMX *amx, cell *params) 557 | { 558 | web::json::value obj = Get(params[1], false); 559 | if (obj == web::json::value::null()) 560 | { 561 | return 1; 562 | } 563 | std::string key = amx_GetCppString(amx, params[2]); 564 | 565 | web::json::value target; 566 | try 567 | { 568 | target = obj.as_object().at(utility::conversions::to_string_t(key)); 569 | } 570 | catch (...) 571 | { 572 | return 2; 573 | } 574 | if (!target.is_double()) 575 | { 576 | return 3; 577 | } 578 | 579 | cell *addr = nullptr; 580 | amx_GetAddr(amx, params[3], &addr); 581 | float d = static_cast(target.as_double()); 582 | *addr = amx_ftoc(d); 583 | 584 | return 0; 585 | } 586 | 587 | int Natives::JSON::GetBool(AMX *amx, cell *params) 588 | { 589 | web::json::value obj = Get(params[1], false); 590 | if (obj == web::json::value::null()) 591 | { 592 | return 1; 593 | } 594 | std::string key = amx_GetCppString(amx, params[2]); 595 | 596 | web::json::value target; 597 | try 598 | { 599 | target = obj.as_object().at(utility::conversions::to_string_t(key)); 600 | } 601 | catch (...) 602 | { 603 | return 2; 604 | } 605 | if (!target.is_boolean()) 606 | { 607 | return 3; 608 | } 609 | 610 | cell *addr = nullptr; 611 | amx_GetAddr(amx, params[3], &addr); 612 | *addr = target.as_bool(); 613 | 614 | return 0; 615 | } 616 | 617 | int Natives::JSON::GetString(AMX *amx, cell *params) 618 | { 619 | web::json::value obj = Get(params[1], false); 620 | if (obj == web::json::value::null()) 621 | { 622 | return 1; 623 | } 624 | std::string key = amx_GetCppString(amx, params[2]); 625 | 626 | web::json::value target; 627 | try 628 | { 629 | target = obj.as_object().at(utility::conversions::to_string_t(key)); 630 | } 631 | catch (...) 632 | { 633 | return 2; 634 | } 635 | if (!target.is_string()) 636 | { 637 | return 3; 638 | } 639 | 640 | return amx_SetCppString(amx, params[3], utility::conversions::to_utf8string(target.as_string()).c_str(), params[4]); 641 | } 642 | 643 | int Natives::JSON::GetArray(AMX *amx, cell *params) 644 | { 645 | web::json::value obj = Get(params[1], false); 646 | if (obj == web::json::value::null()) 647 | { 648 | return 1; 649 | } 650 | std::string key = amx_GetCppString(amx, params[2]); 651 | 652 | web::json::value *target = new web::json::value; 653 | try 654 | { 655 | *target = obj.as_object().at(utility::conversions::to_string_t(key)); 656 | } 657 | catch (...) 658 | { 659 | return 2; 660 | } 661 | if (!target->is_array()) 662 | { 663 | return 3; 664 | } 665 | 666 | cell *addr = nullptr; 667 | amx_GetAddr(amx, params[3], &addr); 668 | *addr = Alloc(target); 669 | 670 | return 0; 671 | } 672 | 673 | int Natives::JSON::ArrayLength(AMX *amx, cell *params) 674 | { 675 | web::json::value obj = Get(params[1], false); 676 | if (!obj.is_array() || obj == web::json::value::null()) 677 | { 678 | return 1; 679 | } 680 | 681 | cell *addr = nullptr; 682 | amx_GetAddr(amx, params[2], &addr); 683 | *addr = obj.as_array().size(); 684 | 685 | return 0; 686 | } 687 | 688 | int Natives::JSON::ArrayObject(AMX *amx, cell *params) 689 | { 690 | web::json::value obj = Get(params[1], false); 691 | if (!obj.is_array() || obj == web::json::value::null()) 692 | { 693 | return 1; 694 | } 695 | 696 | web::json::value *result = new web::json::value(); 697 | try 698 | { 699 | *result = obj.as_array().at(params[2]); 700 | } 701 | catch (...) 702 | { 703 | return 2; 704 | } 705 | cell id = Alloc(result); 706 | 707 | cell *addr = nullptr; 708 | amx_GetAddr(amx, params[3], &addr); 709 | *addr = id; 710 | 711 | return 0; 712 | } 713 | 714 | int Natives::JSON::GetNodeInt(AMX *amx, cell *params) 715 | { 716 | web::json::value obj = Get(params[1]); 717 | if (!obj.is_integer() || obj == web::json::value::null()) 718 | { 719 | return 1; 720 | } 721 | 722 | cell *addr = nullptr; 723 | amx_GetAddr(amx, params[2], &addr); 724 | *addr = obj.as_integer(); 725 | 726 | return 0; 727 | } 728 | 729 | int Natives::JSON::GetNodeFloat(AMX *amx, cell *params) 730 | { 731 | web::json::value obj = Get(params[1]); 732 | if (!obj.is_double() || obj == web::json::value::null()) 733 | { 734 | return 1; 735 | } 736 | 737 | cell *addr = nullptr; 738 | amx_GetAddr(amx, params[2], &addr); 739 | float d = static_cast(obj.as_double()); 740 | *addr = amx_ftoc(d); 741 | 742 | return 0; 743 | } 744 | 745 | int Natives::JSON::GetNodeBool(AMX *amx, cell *params) 746 | { 747 | web::json::value obj = Get(params[1]); 748 | if (!obj.is_boolean() || obj == web::json::value::null()) 749 | { 750 | return 1; 751 | } 752 | 753 | cell *addr = nullptr; 754 | amx_GetAddr(amx, params[2], &addr); 755 | *addr = obj.as_bool(); 756 | 757 | return 0; 758 | } 759 | 760 | int Natives::JSON::GetNodeString(AMX *amx, cell *params) 761 | { 762 | web::json::value obj = Get(params[1]); 763 | if (!obj.is_string() || obj == web::json::value::null()) 764 | { 765 | return 1; 766 | } 767 | 768 | return amx_SetCppString(amx, params[2], utility::conversions::to_utf8string(obj.as_string()).c_str(), params[3]); 769 | } 770 | 771 | int Natives::JSON::ToggleGC(AMX *amx, cell *params) 772 | { 773 | auto n = nodeTable.find(params[1]); 774 | if (n == nodeTable.end()) 775 | { 776 | return 1; 777 | } 778 | 779 | n->second.gc = params[2]; 780 | 781 | return 0; 782 | } 783 | 784 | int Natives::JSON::Cleanup(AMX *amx, cell *params) 785 | { 786 | auto n = nodeTable.find(params[1]); 787 | if (n == nodeTable.end()) 788 | { 789 | if (!params[2]) 790 | { 791 | logprintf("ERROR: attempt to cleanup node from invalid ID %d", params[1]); 792 | } 793 | return 1; 794 | } 795 | 796 | if (!n->second.gc && params[2]) 797 | { 798 | return 2; 799 | } 800 | 801 | Erase(params[1]); 802 | 803 | return 0; 804 | } 805 | 806 | cell Natives::JSON::Alloc(web::json::value *item) 807 | { 808 | int id = jsonPoolCounter++; 809 | nodeTable[id] = {item, true}; 810 | return id; 811 | } 812 | 813 | web::json::value Natives::JSON::Get(int id, bool gc) 814 | { 815 | auto n = nodeTable.find(id); 816 | if (n == nodeTable.end()) 817 | { 818 | return web::json::value::null(); 819 | } 820 | 821 | // deref the node into a local copy for returning 822 | web::json::value copy = *(n->second.value); 823 | if (gc && n->second.gc) 824 | { 825 | // if gc, then delete the heap copy 826 | Erase(id); 827 | } 828 | // and return the copy 829 | return copy; 830 | } 831 | 832 | web::json::value *Natives::JSON::GetPointer(int id) 833 | { 834 | auto n = nodeTable.find(id); 835 | if (n == nodeTable.end()) 836 | { 837 | return nullptr; 838 | } 839 | return n->second.value; 840 | } 841 | 842 | void Natives::JSON::Erase(int id) 843 | { 844 | auto n = nodeTable.find(id); 845 | if (n == nodeTable.end()) 846 | { 847 | return; 848 | } 849 | delete n->second.value; 850 | nodeTable.erase(id); 851 | } 852 | -------------------------------------------------------------------------------- /lib/cmake-conan/conan.cmake: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | # Copyright (c) 2018 JFrog 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | 24 | 25 | # This file comes from: https://github.com/conan-io/cmake-conan. Please refer 26 | # to this repository for issues and documentation. 27 | 28 | # Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called. 29 | # It will take CMake current settings (os, compiler, compiler version, architecture) 30 | # and translate them to conan settings for installing and retrieving dependencies. 31 | 32 | # It is intended to facilitate developers building projects that have conan dependencies, 33 | # but it is only necessary on the end-user side. It is not necessary to create conan 34 | # packages, in fact it shouldn't be use for that. Check the project documentation. 35 | 36 | 37 | include(CMakeParseArguments) 38 | 39 | function(_get_msvc_ide_version result) 40 | set(${result} "" PARENT_SCOPE) 41 | if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500) 42 | set(${result} 8 PARENT_SCOPE) 43 | elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600) 44 | set(${result} 9 PARENT_SCOPE) 45 | elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700) 46 | set(${result} 10 PARENT_SCOPE) 47 | elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800) 48 | set(${result} 11 PARENT_SCOPE) 49 | elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900) 50 | set(${result} 12 PARENT_SCOPE) 51 | elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910) 52 | set(${result} 14 PARENT_SCOPE) 53 | elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920) 54 | set(${result} 15 PARENT_SCOPE) 55 | elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930) 56 | set(${result} 16 PARENT_SCOPE) 57 | elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1940) 58 | set(${result} 17 PARENT_SCOPE) 59 | elseif(NOT MSVC_VERSION VERSION_LESS 1940 AND MSVC_VERSION VERSION_LESS 1950) 60 | set(${result} 17 PARENT_SCOPE) 61 | else() 62 | message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]") 63 | endif() 64 | endfunction() 65 | 66 | function(conan_cmake_settings result) 67 | #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER}) 68 | #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID}) 69 | #message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION}) 70 | #message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS}) 71 | #message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE}) 72 | #message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE}) 73 | #message(STATUS "GENERATOR " ${CMAKE_GENERATOR}) 74 | #message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64}) 75 | 76 | message(STATUS "Conan: Automatic detection of conan settings from cmake") 77 | 78 | parse_arguments(${ARGV}) 79 | 80 | if(ARGUMENTS_BUILD_TYPE) 81 | set(_CONAN_SETTING_BUILD_TYPE ${ARGUMENTS_BUILD_TYPE}) 82 | elseif(CMAKE_BUILD_TYPE) 83 | set(_CONAN_SETTING_BUILD_TYPE ${CMAKE_BUILD_TYPE}) 84 | else() 85 | message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") 86 | endif() 87 | 88 | string(TOUPPER ${_CONAN_SETTING_BUILD_TYPE} _CONAN_SETTING_BUILD_TYPE_UPPER) 89 | if (_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "DEBUG") 90 | set(_CONAN_SETTING_BUILD_TYPE "Debug") 91 | elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELEASE") 92 | set(_CONAN_SETTING_BUILD_TYPE "Release") 93 | elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELWITHDEBINFO") 94 | set(_CONAN_SETTING_BUILD_TYPE "RelWithDebInfo") 95 | elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "MINSIZEREL") 96 | set(_CONAN_SETTING_BUILD_TYPE "MinSizeRel") 97 | endif() 98 | 99 | if(ARGUMENTS_ARCH) 100 | set(_CONAN_SETTING_ARCH ${ARGUMENTS_ARCH}) 101 | endif() 102 | #handle -s os setting 103 | if(CMAKE_SYSTEM_NAME) 104 | #use default conan os setting if CMAKE_SYSTEM_NAME is not defined 105 | set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) 106 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") 107 | set(CONAN_SYSTEM_NAME Macos) 108 | endif() 109 | set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD WindowsStore) 110 | list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index) 111 | if (${_index} GREATER -1) 112 | #check if the cmake system is a conan supported one 113 | set(_CONAN_SETTING_OS ${CONAN_SYSTEM_NAME}) 114 | else() 115 | message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}") 116 | endif() 117 | endif() 118 | 119 | get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES) 120 | if (";${_languages};" MATCHES ";CXX;") 121 | set(LANGUAGE CXX) 122 | set(USING_CXX 1) 123 | elseif (";${_languages};" MATCHES ";C;") 124 | set(LANGUAGE C) 125 | set(USING_CXX 0) 126 | else () 127 | message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unabled to detect compiler version.") 128 | endif() 129 | 130 | if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU) 131 | # using GCC 132 | # TODO: Handle other params 133 | string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) 134 | list(GET VERSION_LIST 0 MAJOR) 135 | list(GET VERSION_LIST 1 MINOR) 136 | set(COMPILER_VERSION ${MAJOR}.${MINOR}) 137 | if(${MAJOR} GREATER 4) 138 | set(COMPILER_VERSION ${MAJOR}) 139 | endif() 140 | set(_CONAN_SETTING_COMPILER gcc) 141 | set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) 142 | if (USING_CXX) 143 | conan_cmake_detect_unix_libcxx(_LIBCXX) 144 | set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) 145 | endif () 146 | elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang) 147 | # using AppleClang 148 | string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) 149 | list(GET VERSION_LIST 0 MAJOR) 150 | list(GET VERSION_LIST 1 MINOR) 151 | set(_CONAN_SETTING_COMPILER apple-clang) 152 | set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR}) 153 | if (USING_CXX) 154 | conan_cmake_detect_unix_libcxx(_LIBCXX) 155 | set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) 156 | endif () 157 | elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang) 158 | string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) 159 | list(GET VERSION_LIST 0 MAJOR) 160 | list(GET VERSION_LIST 1 MINOR) 161 | set(_CONAN_SETTING_COMPILER clang) 162 | set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR}) 163 | if(APPLE) 164 | cmake_policy(GET CMP0025 APPLE_CLANG_POLICY_ENABLED) 165 | if(NOT APPLE_CLANG_POLICY_ENABLED) 166 | message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it") 167 | set(_CONAN_SETTING_COMPILER apple-clang) 168 | endif() 169 | endif() 170 | if(${_CONAN_SETTING_COMPILER} STREQUAL clang AND ${MAJOR} GREATER 7) 171 | set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}) 172 | endif() 173 | if (USING_CXX) 174 | conan_cmake_detect_unix_libcxx(_LIBCXX) 175 | set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) 176 | endif () 177 | elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC) 178 | set(_VISUAL "Visual Studio") 179 | _get_msvc_ide_version(_VISUAL_VERSION) 180 | if("${_VISUAL_VERSION}" STREQUAL "") 181 | message(FATAL_ERROR "Conan: Visual Studio not recognized") 182 | else() 183 | set(_CONAN_SETTING_COMPILER ${_VISUAL}) 184 | set(_CONAN_SETTING_COMPILER_VERSION ${_VISUAL_VERSION}) 185 | endif() 186 | 187 | if(NOT _CONAN_SETTING_ARCH) 188 | if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64") 189 | set(_CONAN_SETTING_ARCH x86_64) 190 | elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM") 191 | message(STATUS "Conan: Using default ARM architecture from MSVC") 192 | set(_CONAN_SETTING_ARCH armv6) 193 | elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86") 194 | set(_CONAN_SETTING_ARCH x86) 195 | else () 196 | message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]") 197 | endif() 198 | endif() 199 | 200 | conan_cmake_detect_vs_runtime(_vs_runtime) 201 | message(STATUS "Conan: Detected VS runtime: ${_vs_runtime}") 202 | set(_CONAN_SETTING_COMPILER_RUNTIME ${_vs_runtime}) 203 | 204 | if (CMAKE_GENERATOR_TOOLSET) 205 | set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) 206 | elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja")) 207 | set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) 208 | endif() 209 | else() 210 | message(FATAL_ERROR "Conan: compiler setup not recognized") 211 | endif() 212 | 213 | # If profile is defined it is used 214 | if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE) 215 | set(_APPLIED_PROFILES ${ARGUMENTS_DEBUG_PROFILE}) 216 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE) 217 | set(_APPLIED_PROFILES ${ARGUMENTS_RELEASE_PROFILE}) 218 | elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE) 219 | set(_APPLIED_PROFILES ${ARGUMENTS_RELWITHDEBINFO_PROFILE}) 220 | elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE) 221 | set(_APPLIED_PROFILES ${ARGUMENTS_MINSIZEREL_PROFILE}) 222 | elseif(ARGUMENTS_PROFILE) 223 | set(_APPLIED_PROFILES ${ARGUMENTS_PROFILE}) 224 | endif() 225 | 226 | foreach(ARG ${_APPLIED_PROFILES}) 227 | set(_SETTINGS ${_SETTINGS} -pr ${ARG}) 228 | endforeach() 229 | 230 | if(NOT _SETTINGS OR ARGUMENTS_PROFILE_AUTO STREQUAL "ALL") 231 | set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version 232 | compiler.runtime compiler.libcxx compiler.toolset) 233 | endif() 234 | 235 | # Automatic from CMake 236 | foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) 237 | string(TOUPPER ${ARG} _arg_name) 238 | string(REPLACE "." "_" _arg_name ${_arg_name}) 239 | if(_CONAN_SETTING_${_arg_name}) 240 | set(_SETTINGS ${_SETTINGS} -s ${ARG}=${_CONAN_SETTING_${_arg_name}}) 241 | endif() 242 | endforeach() 243 | 244 | foreach(ARG ${ARGUMENTS_SETTINGS}) 245 | set(_SETTINGS ${_SETTINGS} -s ${ARG}) 246 | endforeach() 247 | 248 | message(STATUS "Conan: Settings= ${_SETTINGS}") 249 | 250 | set(${result} ${_SETTINGS} PARENT_SCOPE) 251 | endfunction() 252 | 253 | 254 | function(conan_cmake_detect_unix_libcxx result) 255 | # Take into account any -stdlib in compile options 256 | get_directory_property(compile_options DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS) 257 | 258 | # Take into account any _GLIBCXX_USE_CXX11_ABI in compile definitions 259 | get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS) 260 | foreach(define ${defines}) 261 | if(define MATCHES "_GLIBCXX_USE_CXX11_ABI") 262 | if(define MATCHES "^-D") 263 | set(compile_options ${compile_options} "${define}") 264 | else() 265 | set(compile_options ${compile_options} "-D${define}") 266 | endif() 267 | endif() 268 | endforeach() 269 | 270 | execute_process( 271 | COMMAND ${CMAKE_COMMAND} -E echo "#include " 272 | COMMAND ${CMAKE_CXX_COMPILER} -x c++ ${compile_options} -E -dM - 273 | OUTPUT_VARIABLE string_defines 274 | ) 275 | 276 | if(string_defines MATCHES "#define __GLIBCXX__") 277 | # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake 278 | if(DEFINED _GLIBCXX_USE_CXX11_ABI) 279 | if(_GLIBCXX_USE_CXX11_ABI) 280 | set(${result} libstdc++11 PARENT_SCOPE) 281 | return() 282 | else() 283 | set(${result} libstdc++ PARENT_SCOPE) 284 | return() 285 | endif() 286 | endif() 287 | 288 | if(string_defines MATCHES "#define _GLIBCXX_USE_CXX11_ABI 1\n") 289 | set(${result} libstdc++11 PARENT_SCOPE) 290 | else() 291 | # Either the compiler is missing the define because it is old, and so 292 | # it can't use the new abi, or the compiler was configured to use the 293 | # old abi by the user or distro (e.g. devtoolset on RHEL/CentOS) 294 | set(${result} libstdc++ PARENT_SCOPE) 295 | endif() 296 | else() 297 | set(${result} libc++ PARENT_SCOPE) 298 | endif() 299 | endfunction() 300 | 301 | function(conan_cmake_detect_vs_runtime result) 302 | string(TOUPPER ${CMAKE_BUILD_TYPE} build_type) 303 | set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS) 304 | foreach(variable ${variables}) 305 | string(REPLACE " " ";" flags ${${variable}}) 306 | foreach (flag ${flags}) 307 | if(${flag} STREQUAL "/MD" OR ${flag} STREQUAL "/MDd" OR ${flag} STREQUAL "/MT" OR ${flag} STREQUAL "/MTd") 308 | string(SUBSTRING ${flag} 1 -1 runtime) 309 | set(${result} ${runtime} PARENT_SCOPE) 310 | return() 311 | endif() 312 | endforeach() 313 | endforeach() 314 | if(${build_type} STREQUAL "DEBUG") 315 | set(${result} "MDd" PARENT_SCOPE) 316 | else() 317 | set(${result} "MD" PARENT_SCOPE) 318 | endif() 319 | endfunction() 320 | 321 | 322 | macro(parse_arguments) 323 | set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_LOAD NO_OUTPUT_DIRS OUTPUT_QUIET NO_IMPORTS) 324 | set(oneValueArgs CONANFILE ARCH BUILD_TYPE INSTALL_FOLDER CONAN_COMMAND) 325 | set(multiValueArgs DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE 326 | PROFILE REQUIRES OPTIONS IMPORTS SETTINGS BUILD ENV GENERATORS PROFILE_AUTO 327 | INSTALL_ARGS) 328 | cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 329 | endmacro() 330 | 331 | function(conan_cmake_install) 332 | # Calls "conan install" 333 | # Argument BUILD is equivalant to --build={missing, PkgName,...} or 334 | # --build when argument is 'BUILD all' (which builds all packages from source) 335 | # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source 336 | # cmake does not identify conan as command, even if it is +x and it is in the path 337 | parse_arguments(${ARGV}) 338 | 339 | if(CONAN_CMAKE_MULTI) 340 | set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake_multi) 341 | else() 342 | set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake) 343 | endif() 344 | 345 | set(CONAN_BUILD_POLICY "") 346 | foreach(ARG ${ARGUMENTS_BUILD}) 347 | if(${ARG} STREQUAL "all") 348 | set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build) 349 | break() 350 | else() 351 | set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG}) 352 | endif() 353 | endforeach() 354 | if(ARGUMENTS_CONAN_COMMAND) 355 | set(conan_command ${ARGUMENTS_CONAN_COMMAND}) 356 | else() 357 | set(conan_command conan) 358 | endif() 359 | set(CONAN_OPTIONS "") 360 | if(ARGUMENTS_CONANFILE) 361 | set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE}) 362 | # A conan file has been specified - apply specified options as well if provided 363 | foreach(ARG ${ARGUMENTS_OPTIONS}) 364 | set(CONAN_OPTIONS ${CONAN_OPTIONS} -o=${ARG}) 365 | endforeach() 366 | else() 367 | set(CONANFILE ".") 368 | endif() 369 | if(ARGUMENTS_UPDATE) 370 | set(CONAN_INSTALL_UPDATE --update) 371 | endif() 372 | if(ARGUMENTS_NO_IMPORTS) 373 | set(CONAN_INSTALL_NO_IMPORTS --no-imports) 374 | endif() 375 | set(CONAN_INSTALL_FOLDER "") 376 | if(ARGUMENTS_INSTALL_FOLDER) 377 | set(CONAN_INSTALL_FOLDER -if=${ARGUMENTS_INSTALL_FOLDER}) 378 | endif() 379 | foreach(ARG ${ARGUMENTS_GENERATORS}) 380 | set(CONAN_GENERATORS ${CONAN_GENERATORS} -g=${ARG}) 381 | endforeach() 382 | foreach(ARG ${ARGUMENTS_ENV}) 383 | set(CONAN_ENV_VARS ${CONAN_ENV_VARS} -e=${ARG}) 384 | endforeach() 385 | set(conan_args install ${CONANFILE} ${settings} ${CONAN_ENV_VARS} ${CONAN_GENERATORS} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_INSTALL_NO_IMPORTS} ${CONAN_OPTIONS} ${CONAN_INSTALL_FOLDER} ${ARGUMENTS_INSTALL_ARGS}) 386 | 387 | string (REPLACE ";" " " _conan_args "${conan_args}") 388 | message(STATUS "Conan executing: ${conan_command} ${_conan_args}") 389 | 390 | if(ARGUMENTS_OUTPUT_QUIET) 391 | execute_process(COMMAND ${conan_command} ${conan_args} 392 | RESULT_VARIABLE return_code 393 | OUTPUT_VARIABLE conan_output 394 | ERROR_VARIABLE conan_output 395 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 396 | else() 397 | execute_process(COMMAND ${conan_command} ${conan_args} 398 | RESULT_VARIABLE return_code 399 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 400 | endif() 401 | 402 | if(NOT "${return_code}" STREQUAL "0") 403 | message(FATAL_ERROR "Conan install failed='${return_code}'") 404 | endif() 405 | 406 | endfunction() 407 | 408 | 409 | function(conan_cmake_setup_conanfile) 410 | parse_arguments(${ARGV}) 411 | if(ARGUMENTS_CONANFILE) 412 | get_filename_component(_CONANFILE_NAME ${ARGUMENTS_CONANFILE} NAME) 413 | # configure_file will make sure cmake re-runs when conanfile is updated 414 | configure_file(${ARGUMENTS_CONANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk) 415 | file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk) 416 | else() 417 | conan_cmake_generate_conanfile(${ARGV}) 418 | endif() 419 | endfunction() 420 | 421 | function(conan_cmake_generate_conanfile) 422 | # Generate, writing in disk a conanfile.txt with the requires, options, and imports 423 | # specified as arguments 424 | # This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR) 425 | parse_arguments(${ARGV}) 426 | set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt") 427 | 428 | file(WRITE ${_FN} "[generators]\ncmake\n\n[requires]\n") 429 | foreach(ARG ${ARGUMENTS_REQUIRES}) 430 | file(APPEND ${_FN} ${ARG} "\n") 431 | endforeach() 432 | 433 | file(APPEND ${_FN} ${ARG} "\n[options]\n") 434 | foreach(ARG ${ARGUMENTS_OPTIONS}) 435 | file(APPEND ${_FN} ${ARG} "\n") 436 | endforeach() 437 | 438 | file(APPEND ${_FN} ${ARG} "\n[imports]\n") 439 | foreach(ARG ${ARGUMENTS_IMPORTS}) 440 | file(APPEND ${_FN} ${ARG} "\n") 441 | endforeach() 442 | endfunction() 443 | 444 | 445 | macro(conan_load_buildinfo) 446 | if(CONAN_CMAKE_MULTI) 447 | set(_CONANBUILDINFO conanbuildinfo_multi.cmake) 448 | else() 449 | set(_CONANBUILDINFO conanbuildinfo.cmake) 450 | endif() 451 | if(ARGUMENTS_INSTALL_FOLDER) 452 | set(_CONANBUILDINFOFOLDER ${ARGUMENTS_INSTALL_FOLDER}) 453 | else() 454 | set(_CONANBUILDINFOFOLDER ${CMAKE_CURRENT_BINARY_DIR}) 455 | endif() 456 | # Checks for the existence of conanbuildinfo.cmake, and loads it 457 | # important that it is macro, so variables defined at parent scope 458 | if(EXISTS "${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}") 459 | message(STATUS "Conan: Loading ${_CONANBUILDINFO}") 460 | include(${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}) 461 | else() 462 | message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}") 463 | endif() 464 | endmacro() 465 | 466 | 467 | macro(conan_cmake_run) 468 | parse_arguments(${ARGV}) 469 | 470 | if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED 471 | AND NOT ARGUMENTS_BUILD_TYPE) 472 | set(CONAN_CMAKE_MULTI ON) 473 | message(STATUS "Conan: Using cmake-multi generator") 474 | else() 475 | set(CONAN_CMAKE_MULTI OFF) 476 | endif() 477 | 478 | if(NOT CONAN_EXPORTED) 479 | conan_cmake_setup_conanfile(${ARGV}) 480 | if(CONAN_CMAKE_MULTI) 481 | foreach(CMAKE_BUILD_TYPE "Release" "Debug") 482 | set(ENV{CONAN_IMPORT_PATH} ${CMAKE_BUILD_TYPE}) 483 | conan_cmake_settings(settings ${ARGV}) 484 | conan_cmake_install(SETTINGS ${settings} ${ARGV}) 485 | endforeach() 486 | set(CMAKE_BUILD_TYPE) 487 | else() 488 | conan_cmake_settings(settings ${ARGV}) 489 | conan_cmake_install(SETTINGS ${settings} ${ARGV}) 490 | endif() 491 | endif() 492 | 493 | if (NOT ARGUMENTS_NO_LOAD) 494 | conan_load_buildinfo() 495 | endif() 496 | 497 | if(ARGUMENTS_BASIC_SETUP) 498 | foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS) 499 | if(ARGUMENTS_${_option}) 500 | if(${_option} STREQUAL "CMAKE_TARGETS") 501 | list(APPEND _setup_options "TARGETS") 502 | else() 503 | list(APPEND _setup_options ${_option}) 504 | endif() 505 | endif() 506 | endforeach() 507 | conan_basic_setup(${_setup_options}) 508 | endif() 509 | endmacro() 510 | 511 | macro(conan_check) 512 | # Checks conan availability in PATH 513 | # Arguments REQUIRED and VERSION are optional 514 | # Example usage: 515 | # conan_check(VERSION 1.0.0 REQUIRED) 516 | message(STATUS "Conan: checking conan executable in path") 517 | set(options REQUIRED) 518 | set(oneValueArgs VERSION) 519 | cmake_parse_arguments(CONAN "${options}" "${oneValueArgs}" "" ${ARGN}) 520 | 521 | find_program(CONAN_CMD conan) 522 | if(NOT CONAN_CMD AND CONAN_REQUIRED) 523 | message(FATAL_ERROR "Conan executable not found!") 524 | endif() 525 | message(STATUS "Conan: Found program ${CONAN_CMD}") 526 | execute_process(COMMAND ${CONAN_CMD} --version 527 | OUTPUT_VARIABLE CONAN_VERSION_OUTPUT 528 | ERROR_VARIABLE CONAN_VERSION_OUTPUT) 529 | message(STATUS "Conan: Version found ${CONAN_VERSION_OUTPUT}") 530 | 531 | if(DEFINED CONAN_VERSION) 532 | string(REGEX MATCH ".*Conan version ([0-9]+\.[0-9]+\.[0-9]+)" FOO 533 | "${CONAN_VERSION_OUTPUT}") 534 | if(${CMAKE_MATCH_1} VERSION_LESS ${CONAN_VERSION}) 535 | message(FATAL_ERROR "Conan outdated. Installed: ${CMAKE_MATCH_1}, \ 536 | required: ${CONAN_VERSION}. Consider updating via 'pip \ 537 | install conan==${CONAN_VERSION}'.") 538 | endif() 539 | endif() 540 | endmacro() 541 | 542 | macro(conan_add_remote) 543 | # Adds a remote 544 | # Arguments URL and NAME are required, INDEX is optional 545 | # Example usage: 546 | # conan_add_remote(NAME bincrafters INDEX 1 547 | # URL https://api.bintray.com/conan/bincrafters/public-conan) 548 | set(oneValueArgs URL NAME INDEX) 549 | cmake_parse_arguments(CONAN "" "${oneValueArgs}" "" ${ARGN}) 550 | 551 | if(DEFINED CONAN_INDEX) 552 | set(CONAN_INDEX_ARG "-i ${CONAN_INDEX}") 553 | endif() 554 | 555 | message(STATUS "Conan: Adding ${CONAN_NAME} remote repositoy (${CONAN_URL})") 556 | execute_process(COMMAND ${CONAN_CMD} remote add ${CONAN_NAME} ${CONAN_URL} 557 | ${CONAN_INDEX_ARG} -f) 558 | endmacro() 559 | --------------------------------------------------------------------------------