diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index 92d7aaab..1d49cf18 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -1,4 +1,4 @@ -name: Build & deploy +name: Build & deploy server on: push: branches-ignore: @@ -30,6 +30,7 @@ jobs: - name: Build shell: cmd run: | + cd server mkdir build pushd build cmake -G"Visual Studio 16" -A x64 -DJS_MODULE_VERSION=%VERSION% .. @@ -44,7 +45,7 @@ jobs: - uses: actions/upload-artifact@v2 with: name: js-module-windows - path: ./dist/ + path: ./server/dist/ build-linux: name: Build linux release @@ -66,6 +67,7 @@ jobs: - name: Build run: | + cd server sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-8 g++-8 @@ -82,7 +84,7 @@ jobs: - uses: actions/upload-artifact@v2 with: name: js-module-linux - path: ./dist/ + path: ./server/dist/ deploy-cdn: name: Deploy release to alt:V CDN diff --git a/.github/workflows/test-client.yml b/.github/workflows/test-client.yml new file mode 100644 index 00000000..03f8ddd4 --- /dev/null +++ b/.github/workflows/test-client.yml @@ -0,0 +1,37 @@ +name: Test client changes +on: + push: + paths: + - "client/**" + pull_request: + paths: + - "client/**" + +env: + BUILD_TYPE: Release + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + + - name: Fetch Submodules + shell: bash + run: | + git submodule init + git submodule update + + - name: Generate + shell: bash + run: | + cd client + cmake . -BBUILD + + - name: Build + shell: bash + run: | + cd client + cmake --build BUILD --config $BUILD_TYPE + diff --git a/.github/workflows/test-changes.yml b/.github/workflows/test-server.yml similarity index 86% rename from .github/workflows/test-changes.yml rename to .github/workflows/test-server.yml index 983ad237..9a8349f1 100644 --- a/.github/workflows/test-changes.yml +++ b/.github/workflows/test-server.yml @@ -1,5 +1,11 @@ -name: Test changes -on: [pull_request] +name: Test server changes +on: + push: + paths: + - "server/**" + pull_request: + paths: + - "server/**" jobs: test-windows: @@ -15,6 +21,7 @@ jobs: - name: Build shell: cmd run: | + cd server mkdir build pushd build cmake -G"Visual Studio 16" -A x64 .. @@ -35,6 +42,7 @@ jobs: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-8 g++-8 + cd server mkdir build-linux && cd build-linux cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-8 .. cmake --build . --config Release diff --git a/.gitmodules b/.gitmodules index 0b7b9532..332d23c7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "src/helpers"] - path = src/helpers - url = https://github.com/altmp/v8-helpers.git -[submodule "src/cpp-sdk"] - path = src/cpp-sdk +[submodule "shared/deps/cpp-sdk"] + path = shared/deps/cpp-sdk url = https://github.com/altmp/cpp-sdk.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..3cd1c7bf --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,33 @@ +{ + "configurations": [ + { + "name": "Win32 Static", + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "ALT_CLIENT_API", + "ALT_CLIENT", + "ALT_SERVER_API", + "ALT_SERVER", + "JS_MODULE_VERSION" + ], + "windowsSdkVersion": "10.0.19041.0", + "cppStandard": "c++17", + "intelliSenseMode": "msvc-x64", + "configurationProvider": "ms-vscode.cmake-tools", + "includePath": [ + "client/src", + "client/BUILD/_deps/altv-js-deps-src/include", + "server/src", + "server/deps", + "server/deps/nodejs/include", + "server/deps/nodejs/deps/v8/include", + "server/deps/nodejs/deps/uv/include", + "shared/", + "shared/deps" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..e1078fd0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(Windows) Attach", + "type": "cppvsdbg", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..a9f84797 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,81 @@ +{ + "files.associations": { + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "filesystem": "cpp", + "fstream": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "utility": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xmemory0": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "deque": "cpp", + "queue": "cpp", + "sstream": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "set": "cpp", + "bit": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "format": "cpp", + "forward_list": "cpp", + "optional": "cpp", + "stop_token": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..6f5f1fa8 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build client", + "type": "shell", + "command": "cd client && build-debug.bat", + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + "isDefault": true + }, + }, + { + "label": "Build server", + "type": "shell", + "command": "cd server && ./build", + "windows": { + "command": "cd server && build.bat" + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + "isDefault": true + }, + } + ] +} diff --git a/README.md b/README.md index 36d01f20..ca0f21b8 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,18 @@ -# altv-js-module -JS module for alt:V Multiplayer. Powered by NodeJS & v8 +# alt:V JS module -## Troubleshooting -**Problem**: I want to build a native Node.js addon with -[node-gyp](https://github.com/nodejs/node-gyp) but the alt:V server can not load -it and responds with following error: -``` -./altv-server: symbol lookup error: /usr/share/addon/binding.node: undefined symbol: node_module_register -``` -**Solution**: Add a C++ file to your project with following content and include -it the ```sources``` in your ```binding.gyp``` file. -```cpp -#include -#include +Repository containing the JS module for [alt:V multiplayer](https://altv.mp/). -extern "C" NODE_EXTERN void node_module_register(void* mod) { - auto base_ptr = dlopen("libnode.so.72", RTLD_NOW | RTLD_GLOBAL); - if (base_ptr == nullptr) { - return; - } - auto register_func = reinterpret_cast(dlsym(base_ptr, "node_module_register")); - if (register_func == nullptr) { - return; - } - register_func(mod); -} +## Structure + +| Directory | Description | +| ------------------ | ------------------------------------------------------- | +| [/client](/client) | Clientside JS module powered by V8 | +| [/server](/server) | Serverside JS module powered by Node.js | +| [/shared](/shared) | Shared code for the clientside & serverside module | +| [/docs](/docs) | Documentation for the internal workings of the module | + +## Contributions + +All contributions are greatly appreciated. +If there are any questions or you would like to discuss a feature, +contact the *module maintainer*. diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 00000000..4ddf8e9d --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,11 @@ +# Build files +BUILD/ +deps/v8/ +dist/ + +# Editors +.vs/ +.vscode/settings.json + +# Extra +tools/extra.bat diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 00000000..25949532 --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,126 @@ +cmake_minimum_required (VERSION 3.10) +include(cmake/GitUtils.cmake) +include(FetchContent) + +project(altv-client-js) + +# set(ALTV_JS_DEPS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps) + +# Fetch deps +# v8 +message("alt:V JS - Fetching v8 deps, can take a while") +FetchContent_Declare(altv-js-deps + URL https://github.com/altmp/altv-client-js/releases/download/deps3/deps.zip +) +FetchContent_Populate(altv-js-deps) +set(ALTV_JS_DL_DEPS ${altv-js-deps_SOURCE_DIR}) + +# cpp-sdk +if(NOT ALTV_JS_CPP_SDK) + set(ALTV_JS_CPP_SDK ../shared/deps) +else() + set(ALTV_JS_DEINIT_CPPSDK 1) +endif() +# Fetch deps + +file(GLOB_RECURSE PROJECT_SOURCE_FILES "src/*.h" "src/*.hpp" "src/*.cpp" "src/*.c") +file(GLOB_RECURSE PROJECT_SHARED_FILES "../shared/*.h" "../shared/*.cpp") + +macro(GroupSources curdir groupindex) + file(GLOB children RELATIVE ${curdir} ${curdir}/*) + + foreach(child ${children}) + if(IS_DIRECTORY ${curdir}/${child}) + GroupSources(${curdir}/${child} ${groupindex}/${child}) + else() + + string(REPLACE "/" "\\" groupname ${groupindex}) + + source_group(${groupname} FILES ${curdir}/${child}) + endif() + endforeach() +endmacro() + +GroupSources(${PROJECT_SOURCE_DIR}/src "Source Files") +GroupSources("../shared" "Shared Files") + +include_directories( + ${ALTV_JS_CPP_SDK} + ${ALTV_JS_DL_DEPS}/include + ../shared +) + +set(ALTV_JS_LINKS + # Platform binaries + Winmm.lib + DbgHelp.lib + shlwapi.lib + + # V8 + ${ALTV_JS_DL_DEPS}/lib/$,Debug,Release>/v8_monolith.lib +) + +set(ALTV_JS_DEFS + # Compliation + -DCXX_COMPILER_ID="${CMAKE_CXX_COMPILER_ID}" + + # Platform + -DUNICODE + -D_UNICODE + + # alt:V + -DALT_CLIENT + -DALT_CLIENT_API + + # v8 + -DV8_COMPRESS_POINTERS + -DV8_31BIT_SMIS_ON_64BIT_ARCH + -DV8_IMMINENT_DEPRECATION_WARNINGS +) + +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD /Zi /bigobj") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd /bigobj") +set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /OPT:REF /OPT:ICF") + +if(DYNAMIC_BUILD) + ## SHARED + add_library( + ${PROJECT_NAME} SHARED + ${PROJECT_SOURCE_FILES} + ${PROJECT_SHARED_FILES} + ) + set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 17 + ) + target_compile_definitions(${PROJECT_NAME} PRIVATE + ${ALTV_JS_DEFS} + -DALTV_JS_SHARED + ) + target_link_libraries(${PROJECT_NAME} PRIVATE + ${ALTV_JS_LINKS} + ) +else() + ## STATIC + add_library( + ${PROJECT_NAME}-static STATIC + ${PROJECT_SOURCE_FILES} + ${PROJECT_SHARED_FILES} + ) + set_target_properties(${PROJECT_NAME}-static PROPERTIES + CXX_STANDARD 17 + ) + target_compile_definitions(${PROJECT_NAME}-static PRIVATE + ${ALTV_JS_DEFS} + ) + target_link_libraries(${PROJECT_NAME}-static PRIVATE + ${ALTV_JS_LINKS} + ) +endif() + +if(ALTV_JS_DEINIT_CPPSDK) + add_custom_command(TARGET ${PROJECT_NAME}-static + PRE_BUILD + COMMAND cmd /C "tools\\deinit-cppsdk.bat" + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) +endif() \ No newline at end of file diff --git a/client/README.md b/client/README.md new file mode 100644 index 00000000..6e4e6bfd --- /dev/null +++ b/client/README.md @@ -0,0 +1,17 @@ +# Official clientside JavaScript runtime for alt:V + +## To build: +Run `.\tools\build.bat` (Make sure to run this script from the main directory of the repo) + +Or use the VSCode task + +Built `altv-client-js.dll` is now inside `dist` directory (if build was successful) + +## To test: +Get dev branch of alt:V client (download from https://altv.mp or change branch in `altv.cfg`) + +Place `altv-client-js.dll` into the `modules/altv-client-js` directory in the alt:V client folder. (You need to create these directories yourself) + +Set `debug: true` in `altv.cfg` + +### All contributions are appreciated. diff --git a/client/build-debug.bat b/client/build-debug.bat new file mode 100644 index 00000000..306d30fe --- /dev/null +++ b/client/build-debug.bat @@ -0,0 +1,12 @@ +@echo off + +:: Build the project +cmake . -BBUILD -DDYNAMIC_BUILD=1 +cmake --build BUILD --config Release + +:: Copy built binary to dist folder +IF NOT EXIST dist ( + mkdir dist +) +copy BUILD\Release\altv-client-js.dll dist +copy BUILD\Release\altv-client-js.pdb dist diff --git a/client/build.bat b/client/build.bat new file mode 100644 index 00000000..47b3109a --- /dev/null +++ b/client/build.bat @@ -0,0 +1,12 @@ +@echo off + +:: Build the project +cmake . -BBUILD +cmake --build BUILD --config Release + +:: Copy built binary to dist folder +IF NOT EXIST dist ( + mkdir dist +) +copy BUILD\Release\altv-client-js-static.lib dist +copy BUILD\Release\altv-client-js-static.pdb dist diff --git a/client/cmake/GitUtils.cmake b/client/cmake/GitUtils.cmake new file mode 100644 index 00000000..25ba372c --- /dev/null +++ b/client/cmake/GitUtils.cmake @@ -0,0 +1,228 @@ +cmake_minimum_required(VERSION 2.8.7) + +include(${CMAKE_CURRENT_LIST_DIR}/Utils.cmake) +include(CMakeParseArguments) + +find_package(Git) +if(NOT GIT_FOUND) + message(FATAL_ERROR "git not found!") +endif() + + +# clone a git repo into a directory at configure time +# this can be useful for including cmake-library projects that contain *.cmake files +# the function will automatically init git submodules too +# +# ATTENTION: CMakeLists-files in the cloned repo will NOT be build automatically +# +# why not use ExternalProject_Add you ask? because we need to run this at configure time +# +# USAGE: +# git_clone( +# PROJECT_NAME +# GIT_URL +# [GIT_TAG|GIT_BRANCH|GIT_COMMIT ] +# [DIRECTORY ] +# [QUIET] +# ) +# +# +# ARGUMENTS: +# PROJECT_NAME +# name of the project that will be used in output variables. +# must be the same as the git directory/repo name +# +# GIT_URL +# url to the git repo +# +# GIT_TAG|GIT_BRANCH|GIT_COMMIT +# optional +# the tag/branch/commit to checkout +# default is master +# +# DIRECTORY +# optional +# the directory the project will be cloned into +# default is the build directory, similar to ExternalProject (${CMAKE_BINARY_DIR}) +# +# QUIET +# optional +# don't print status messages +# +# SOURCE_DIR_VARIABLE +# optional +# the variable will be set to contain the path to clonned directory. +# if not set path will be set in _SOURCE_DIR +# +# CLONE_RESULT_VARIABLE +# optional +# the variable will be set to contain the clone result. TRUE - success, FALSE - error +# if not set result will be set in _CLONE_RESULT +# +# +# +# OUTPUT VARIABLES: +# _SOURCE_DIR +# optional, exists when SOURCE_DIR_VARIABLE not set +# top level source directory of the cloned project +# +# _CLONE_RESULT +# optional, exists when CLONE_RESULT_VARIABLE not set +# Result of git_clone function. TRUE - success, FALSE - error +# +# +# EXAMPLE: +# git_clone( +# PROJECT_NAME testProj +# GIT_URL https://github.com/test/test.git +# GIT_COMMIT a1b2c3 +# DIRECTORY ${CMAKE_BINARY_DIR} +# QUIET +# ) +# +# include(${testProj_SOURCE_DIR}/cmake/myFancyLib.cmake) + +function(git_clone) + + cmake_parse_arguments( + PARGS # prefix of output variables + "QUIET" # list of names of the boolean arguments (only defined ones will be true) + "PROJECT_NAME;GIT_URL;GIT_TAG;GIT_BRANCH;GIT_COMMIT;DIRECTORY;SOURCE_DIR_VARIABLE;CLONE_RESULT_VARIABLE" # list of names of mono-valued arguments + "" # list of names of multi-valued arguments (output variables are lists) + ${ARGN} # arguments of the function to parse, here we take the all original ones + ) # remaining unparsed arguments can be found in PARGS_UNPARSED_ARGUMENTS + if(NOT PARGS_PROJECT_NAME) + message(FATAL_ERROR "You must provide a project name") + endif() + + if(NOT PARGS_GIT_URL) + message(FATAL_ERROR "You must provide a git url") + endif() + + if(NOT PARGS_DIRECTORY) + set(PARGS_DIRECTORY ${CMAKE_BINARY_DIR}) + endif() + + if(NOT PARGS_SOURCE_DIR_VARIABLE) + set(${PARGS_PROJECT_NAME}_SOURCE_DIR + ${PARGS_DIRECTORY}/${PARGS_PROJECT_NAME} + CACHE INTERNAL "" FORCE) # makes var visible everywhere because PARENT_SCOPE wouldn't include this scope + + set(SOURCE_DIR ${PARGS_PROJECT_NAME}_SOURCE_DIR) + else() + set(${PARGS_SOURCE_DIR_VARIABLE} + ${PARGS_DIRECTORY}/${PARGS_PROJECT_NAME} + CACHE INTERNAL "" FORCE) # makes var visible everywhere because PARENT_SCOPE wouldn't include this scope + + set(SOURCE_DIR ${PARGS_SOURCE_DIR_VARIABLE}) + endif() + + if(NOT PARGS_CLONE_RESULT_VARIABLE) + set(CLONE_RESULT ${PARGS_PROJECT_NAME}_CLONE_RESULT) + else() + set(CLONE_RESULT ${PARGS_CLONE_RESULT_VARIABLE}) + endif() + + # check that only one of GIT_TAG xor GIT_BRANCH xor GIT_COMMIT was passed + at_most_one(at_most_one_tag ${PARGS_GIT_TAG} ${PARGS_GIT_BRANCH} ${PARGS_GIT_COMMIT}) + + if(NOT at_most_one_tag) + message(FATAL_ERROR "you can only provide one of GIT_TAG, GIT_BRANCH or GIT_COMMIT") + endif() + + if(NOT PARGS_QUIET) + message(STATUS "downloading/updating ${PARGS_PROJECT_NAME}") + endif() + + # first clone the repo + if(EXISTS ${${SOURCE_DIR}}) + if(NOT PARGS_QUIET) + message(STATUS "${PARGS_PROJECT_NAME} directory found, pulling...") + endif() + + execute_process( + COMMAND ${GIT_EXECUTABLE} pull origin master + WORKING_DIRECTORY ${${SOURCE_DIR}} + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_output) + if(git_result EQUAL "0") + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --remote + WORKING_DIRECTORY ${${SOURCE_DIR}} + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_output) + if(NOT git_result EQUAL "0") + set(${CLONE_RESULT} FALSE CACHE INTERNAL "" FORCE) + if(NOT PARGS_QUIET) + message(WARNING "${PARGS_PROJECT_NAME} submodule update error") #ToDo: maybe FATAL_ERROR? + endif() + return() + endif() + else() + set(${CLONE_RESULT} FALSE CACHE INTERNAL "" FORCE) + if(NOT PARGS_QUIET) + message(WARNING "${PARGS_PROJECT_NAME} pull error") #ToDo: maybe FATAL_ERROR? + endif() + return() + endif() + else() + if(NOT PARGS_QUIET) + message(STATUS "${PARGS_PROJECT_NAME} directory not found, cloning...") + endif() + + execute_process( + COMMAND ${GIT_EXECUTABLE} clone ${PARGS_GIT_URL} --recursive ${${SOURCE_DIR}} + WORKING_DIRECTORY ${PARGS_DIRECTORY} + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_output) + if(NOT git_result EQUAL "0") + set(${CLONE_RESULT} FALSE CACHE INTERNAL "" FORCE) + if(NOT PARGS_QUIET) + message(WARNING "${PARGS_PROJECT_NAME} clone error") #ToDo: maybe FATAL_ERROR? + endif() + return() + endif() + endif() + + + if(NOT PARGS_QUIET) + message(STATUS "${git_output}") + endif() + + # now checkout the right commit + if(PARGS_GIT_TAG) + execute_process( + COMMAND ${GIT_EXECUTABLE} fetch --all --tags --prune + COMMAND ${GIT_EXECUTABLE} checkout tags/${PARGS_GIT_TAG} -b tag_${PARGS_GIT_TAG} + WORKING_DIRECTORY ${${SOURCE_DIR}} + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_output) + elseif(PARGS_GIT_BRANCH OR PARGS_GIT_COMMIT) + execute_process( + COMMAND ${GIT_EXECUTABLE} checkout ${PARGS_GIT_BRANCH} ${PARGS_GIT_COMMIT} + WORKING_DIRECTORY ${${SOURCE_DIR}} + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_output) + else() + if(NOT PARGS_QUIET) + message(STATUS "no tag specified, defaulting to master") + endif() + execute_process( + COMMAND ${GIT_EXECUTABLE} checkout master + WORKING_DIRECTORY ${${SOURCE_DIR}} + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_output) + endif() + if(NOT git_result EQUAL "0") + set(${CLONE_RESULT} FALSE CACHE INTERNAL "" FORCE) + if(NOT PARGS_QUIET) + message(WARNING "${PARGS_PROJECT_NAME} some error happens. ${git_output}") #ToDo: maybe FATAL_ERROR? + endif() + return() + else() + set(${CLONE_RESULT} TRUE CACHE INTERNAL "" FORCE) + endif() + if(NOT PARGS_QUIET) + message(STATUS "${git_output}") + endif() +endfunction() diff --git a/client/cmake/Utils.cmake b/client/cmake/Utils.cmake new file mode 100644 index 00000000..a76708ca --- /dev/null +++ b/client/cmake/Utils.cmake @@ -0,0 +1,32 @@ +# returns true if only a single one of its arguments is true +function(xor result) + set(true_args_count 0) + + foreach(foo ${ARGN}) + if(foo) + math(EXPR true_args_count "${true_args_count}+1") + endif() + endforeach() + + if(NOT (${true_args_count} EQUAL 1)) + set(${result} FALSE PARENT_SCOPE) + else() + set(${result} TRUE PARENT_SCOPE) + endif() +endfunction() + +function(at_most_one result) + set(true_args_count 0) + + foreach(foo ${ARGN}) + if(foo) + math(EXPR true_args_count "${true_args_count}+1") + endif() + endforeach() + + if(${true_args_count} GREATER 1) + set(${result} FALSE PARENT_SCOPE) + else() + set(${result} TRUE PARENT_SCOPE) + endif() +endfunction() diff --git a/client/src/CV8Resource.cpp b/client/src/CV8Resource.cpp new file mode 100644 index 00000000..b99aed9d --- /dev/null +++ b/client/src/CV8Resource.cpp @@ -0,0 +1,722 @@ +#include "cpp-sdk/objects/IEntity.h" +#include "cpp-sdk/objects/IPlayer.h" + +#include "cpp-sdk/events/CEvent.h" +#include "cpp-sdk/events/CClientScriptEvent.h" +#include "cpp-sdk/events/CServerScriptEvent.h" +#include "cpp-sdk/events/CWebViewEvent.h" +#include "cpp-sdk/events/CWebSocketClientEvent.h" +#include "cpp-sdk/events/CKeyboardEvent.h" +#include "cpp-sdk/events/CConnectionComplete.h" +#include "cpp-sdk/events/CDisconnectEvent.h" +#include "cpp-sdk/events/CGameEntityCreateEvent.h" +#include "cpp-sdk/events/CGameEntityDestroyEvent.h" +#include "cpp-sdk/events/CSyncedMetaDataChangeEvent.h" +#include "cpp-sdk/events/CStreamSyncedMetaDataChangeEvent.h" +#include "cpp-sdk/events/CGlobalSyncedMetaDataChangeEvent.h" +#include "cpp-sdk/events/CRemoveEntityEvent.h" +#include "cpp-sdk/events/CResourceStartEvent.h" +#include "cpp-sdk/events/CResourceStopEvent.h" +#include "cpp-sdk/events/CResourceErrorEvent.h" +#include "cpp-sdk/events/CRenderEvent.h" +#include "cpp-sdk/SDK.h" + +#include "CV8ScriptRuntime.h" +#include "CV8Resource.h" +#include "V8Helpers.h" +#include "V8Module.h" + +static void StaticRequire(const v8::FunctionCallbackInfo &info) +{ + v8::Isolate *isolate = info.GetIsolate(); + + V8_CHECK(info.Length() == 1, "1 arg expected"); + V8_CHECK(info[0]->IsString(), "moduleName must be a string"); + + V8ResourceImpl *resource = V8ResourceImpl::Get(isolate->GetEnteredOrMicrotaskContext()); + V8_CHECK(resource, "invalid resource"); + + std::string name{*v8::String::Utf8Value{isolate, info[0]}}; + + v8::MaybeLocal _exports = static_cast(resource)->Require(name); + + if (!_exports.IsEmpty()) + info.GetReturnValue().Set(_exports.ToLocalChecked()); + else + V8Helpers::Throw(isolate, "No such module " + name); +} + +void CV8ResourceImpl::ProcessDynamicImports() +{ + for(auto import : dynamicImports) + { + import(); + } + dynamicImports.clear(); +} + +extern V8Module altModule; +bool CV8ResourceImpl::Start() +{ + if (resource->GetMain().IsEmpty()) + return false; + + resource->EnableNatives(); + auto nscope = resource->PushNativesScope(); + + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + microtaskQueue = v8::MicrotaskQueue::New(isolate, v8::MicrotasksPolicy::kExplicit); + v8::Local ctx = v8::Context::New( + isolate, + nullptr, + v8::MaybeLocal(), + v8::MaybeLocal(), + v8::DeserializeInternalFieldsCallback(), + microtaskQueue.get() + ); + + context.Reset(isolate, ctx); + ctx->SetAlignedPointerInEmbedderData(1, resource); + + V8ResourceImpl::Start(); + + v8::Context::Scope context_scope(ctx); + + //Log::Debug(V8ResourceImpl::GetResource(ctx)); + //Log::Debug(V8ResourceImpl::GetResource(isolate->GetEnteredOrMicrotaskContext())); + + /*runtime->GetInspector()->contextCreated({ + ctx, + 1, + v8_inspector::StringView{ (uint8_t*)name.CStr(), name.GetSize() } + });*/ + + std::string path = resource->GetMain().ToString(); + + alt::IPackage *pkg = resource->GetPackage(); + alt::IPackage::File *file = pkg->OpenFile(path); + + alt::String src{pkg->GetFileSize(file)}; + + pkg->ReadFile(file, src.GetData(), src.GetSize()); + pkg->CloseFile(file); + + Log::Info << "[V8] Starting script " << path << Log::Endl; + + v8::Local sourceCode = v8::String::NewFromUtf8(isolate, src.GetData(), v8::NewStringType::kNormal, src.GetSize()).ToLocalChecked(); + + v8::ScriptOrigin scriptOrigin( + isolate, + V8_NEW_STRING(path.c_str()), + 0, + 0, false, + -1, + v8::Local(), + false, + false, + true, + v8::Local() + ); + + bool result = V8Helpers::TryCatch([&]() { + v8::ScriptCompiler::Source source{sourceCode, scriptOrigin}; + v8::MaybeLocal maybeModule = v8::ScriptCompiler::CompileModule(isolate, &source); + + if (maybeModule.IsEmpty()) + return false; + + v8::Local curModule = maybeModule.ToLocalChecked(); + + modules.emplace(path, v8::UniquePersistent{isolate, curModule}); + + auto exports = altModule.GetExports(isolate, ctx); + // Overwrite global console object + auto console = ctx->Global()->Get(ctx, V8_NEW_STRING("console")).ToLocalChecked().As(); + if (!console.IsEmpty()) + { + console->Set(ctx, V8_NEW_STRING("log"), exports->Get(ctx, V8_NEW_STRING("log")).ToLocalChecked()); + console->Set(ctx, V8_NEW_STRING("warn"), exports->Get(ctx, V8_NEW_STRING("logWarning")).ToLocalChecked()); + console->Set(ctx, V8_NEW_STRING("error"), exports->Get(ctx, V8_NEW_STRING("logError")).ToLocalChecked()); + } + + // Add global timer funcs + ctx->Global()->Set(ctx, V8_NEW_STRING("setInterval"), exports->Get(ctx, V8_NEW_STRING("setInterval")).ToLocalChecked()); + ctx->Global()->Set(ctx, V8_NEW_STRING("setTimeout"), exports->Get(ctx, V8_NEW_STRING("setTimeout")).ToLocalChecked()); + ctx->Global()->Set(ctx, V8_NEW_STRING("clearInterval"), exports->Get(ctx, V8_NEW_STRING("clearInterval")).ToLocalChecked()); + ctx->Global()->Set(ctx, V8_NEW_STRING("clearTimeout"), exports->Get(ctx, V8_NEW_STRING("clearTimeout")).ToLocalChecked()); + + ctx->Global()->Set(ctx, v8::String::NewFromUtf8(isolate, "__internal_get_exports").ToLocalChecked(), v8::Function::New(ctx, &StaticRequire).ToLocalChecked()); + + bool res = curModule->InstantiateModule(ctx, CV8ScriptRuntime::ResolveModule).IsJust(); + + if (!res) + return false; + + v8::MaybeLocal v = curModule->Evaluate(ctx); + + if (v.IsEmpty()) + return false; + + alt::MValue _exports = V8Helpers::V8ToMValue(curModule->GetModuleNamespace()); + resource->SetExports(_exports.As()); + + Log::Info << "[V8] Started script " << path << Log::Endl; + return true; + }); + + if (!result) + { + modules.erase(path); + } + + DispatchStartEvent(!result); + + // if all resources are already loaded + if(CV8ScriptRuntime::Instance().resourcesLoaded) { + ProcessDynamicImports(); + } + + return result; +} + +bool CV8ResourceImpl::Stop() +{ + std::vector> objects(ownedObjects.size()); + + for (auto handle : ownedObjects) + objects.push_back(handle); + + ownedObjects.clear(); + + // ????? + // for (auto handle : objects) + // { + // CGame::Instance().DestroyBaseObject(handle); + // } + + //runtime->GetInspector()->contextDestroyed(context.Get(isolate)); + + for(auto pair : timers) + { + delete pair.second; + } + timers.clear(); + oldTimers.clear(); + + if (!context.IsEmpty()) + { + auto nscope = resource->PushNativesScope(); + + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + auto ctx = GetContext(); + v8::Context::Scope scope(ctx); + + auto resources = static_cast(resource->GetRuntime())->GetResources(); + auto name = this->resource->GetName().ToString(); + for(auto res : resources) + { + if(res == this) continue; + auto it = res->modules.find(name); + if(it != res->modules.end()) { + res->modules.erase(it); + } + auto found = res->requires.find(name); + if(found != res->requires.end()) { + res->requires.erase(found); + } + } + + DispatchStopEvent(); + } + + return true; +} + +bool CV8ResourceImpl::OnEvent(const alt::CEvent* e) +{ + auto nscope = resource->PushNativesScope(); + + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + v8::Context::Scope scope(GetContext()); + + V8::EventHandler* handler = V8::EventHandler::Get(e); + if (!handler) + return true; + + // Generic event handler + { + auto evType = e->GetType(); + if(evType == alt::CEvent::Type::CLIENT_SCRIPT_EVENT || evType == alt::CEvent::Type::SERVER_SCRIPT_EVENT) + { + std::vector callbacks; + const char* eventName; + + if(evType == alt::CEvent::Type::CLIENT_SCRIPT_EVENT) + { + callbacks = std::move(GetGenericHandlers(true)); + eventName = static_cast(e)->GetName().CStr(); + } + else if(evType == alt::CEvent::Type::SERVER_SCRIPT_EVENT) + { + callbacks = std::move(GetGenericHandlers(false)); + eventName = static_cast(e)->GetName().CStr(); + } + + if(callbacks.size() != 0) + { + auto evArgs = handler->GetArgs(this, e); + evArgs.insert(evArgs.begin(), V8_NEW_STRING(eventName)); + + InvokeEventHandlers(e, callbacks, evArgs); + } + } + } + + std::vector callbacks = handler->GetCallbacks(this, e); + if (callbacks.size() > 0) + { + std::vector> args = handler->GetArgs(this, e); + + InvokeEventHandlers(e, callbacks, args); + } + + // Dynamic imports + { + if(e->GetType() == alt::CEvent::Type::CONNECTION_COMPLETE) + { + CV8ScriptRuntime& runtime = CV8ScriptRuntime::Instance(); + if(!runtime.resourcesLoaded) + { + runtime.resourcesLoaded = true; + ProcessDynamicImports(); + } + } + else if(e->GetType() == alt::CEvent::Type::DISCONNECT_EVENT) + { + CV8ScriptRuntime::Instance().resourcesLoaded = false; + } + } + + return true; +} + +std::vector CV8ResourceImpl::GetWebViewHandlers(alt::Ref view, const std::string& name) +{ + std::vector handlers; + auto it = webViewHandlers.find(view.Get()); + + if (it != webViewHandlers.end()) + { + auto range = it->second.equal_range(name); + + for (auto it = range.first; it != range.second; ++it) + handlers.push_back(&it->second); + } + + return handlers; +} + +std::vector CV8ResourceImpl::GetWebSocketClientHandlers(alt::Ref webSocket, const std::string& name) +{ + std::vector handlers; + auto it = webSocketClientHandlers.find(webSocket.Get()); + + if (it != webSocketClientHandlers.end()) + { + auto range = it->second.equal_range(name); + + for (auto it = range.first; it != range.second; ++it) + handlers.push_back(&it->second); + } + + return handlers; +} + +std::vector CV8ResourceImpl::GetAudioHandlers(alt::Ref audio, const std::string& name) +{ + std::vector handlers; + auto it = audioHandlers.find(audio.Get()); + + if (it != audioHandlers.end()) + { + auto range = it->second.equal_range(name); + + for (auto it = range.first; it != range.second; ++it) + handlers.push_back(&it->second); + } + + return handlers; +} + +void CV8ResourceImpl::OnTick() +{ + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope scope(GetContext()); + + microtaskQueue->PerformCheckpoint(isolate); + + int64_t time = GetTime(); + + V8ResourceImpl::OnTick(); + + if (GetTime() - time > 10) + { + Log::Warning << "Resource " << resource->GetName() << " tick was too long " << GetTime() - time << " ms" << Log::Endl; + } + + for (auto& view : webViewHandlers) + { + for (auto it = view.second.begin(); it != view.second.end();) + { + if (it->second.removed) + it = view.second.erase(it); + else + ++it; + } + } + + for (auto& webSocket : webSocketClientHandlers) + { + for (auto it = webSocket.second.begin(); it != webSocket.second.end();) + { + if (it->second.removed) + it = webSocket.second.erase(it); + else + ++it; + } + } +} + +void CV8ResourceImpl::OnPromiseRejectedWithNoHandler(v8::PromiseRejectMessage &data) +{ + promiseRejections.RejectedWithNoHandler(this, data); +} + +void CV8ResourceImpl::OnPromiseHandlerAdded(v8::PromiseRejectMessage &data) +{ + promiseRejections.HandlerAdded(this, data); +} + +static v8::MaybeLocal CompileESM(v8::Isolate *isolate, const std::string &name, const std::string &src) +{ + v8::Local sourceCode = v8::String::NewFromUtf8(isolate, src.data(), v8::NewStringType::kNormal, src.size()).ToLocalChecked(); + + v8::ScriptOrigin scriptOrigin( + isolate, + V8_NEW_STRING(name.c_str()), + 0, + 0, false, + -1, + v8::Local(), + false, + false, + true, + v8::Local() + ); + + v8::ScriptCompiler::Source source{sourceCode, scriptOrigin}; + return v8::ScriptCompiler::CompileModule(isolate, &source); +} + +static v8::MaybeLocal WrapModule(v8::Isolate *isolate, const std::deque &_exportKeys, const std::string &name, bool exportAsDefault = false) +{ + bool hasDefault = false; + std::stringstream src; + + src << "const _exports = __internal_get_exports('" << name << "');\n"; + + for (auto &key : _exportKeys) + { + if (key == "default") + { + src << "export default _exports['" << key << "'];\n"; + hasDefault = true; + } + else + { + src << "export let " << key << " = _exports['" << key << "'];\n"; + } + } + + if (!hasDefault && exportAsDefault) + src << "export default _exports;"; + + return CompileESM(isolate, name, src.str()); +} + +bool CV8ResourceImpl::IsValidModule(const std::string &name) +{ + if (V8Module::Exists(name)) + return true; + + alt::IResource *resource = alt::ICore::Instance().GetResource(name); + if (resource) + return true; + + return false; +} + +std::deque CV8ResourceImpl::GetModuleKeys(const std::string &name) +{ + auto &v8module = V8Module::All().find(name); + if (v8module != V8Module::All().end()) + { + auto _exports = v8module->second->GetExports(isolate, GetContext()); + v8::Local v8Keys = _exports->GetOwnPropertyNames(GetContext()).ToLocalChecked(); + + std::deque keys; + + for (uint32_t i = 0; i < v8Keys->Length(); ++i) + { + v8::Local v8Key = v8Keys->Get(GetContext(), i).ToLocalChecked().As(); + keys.push_back(*v8::String::Utf8Value(isolate, v8Key)); + } + + return keys; + } + + alt::IResource *resource = alt::ICore::Instance().GetResource(name); + if (resource) + { + std::deque keys; + + alt::MValueDict _exports = resource->GetExports(); + for (auto it = _exports->Begin(); it; it = _exports->Next()) + keys.push_back(it->GetKey().ToString()); + + return keys; + } + + return {}; +} + +std::string CV8ResourceImpl::GetModulePath(v8::Local moduleHandle) +{ + for (auto &md : modules) + { + if (md.second == moduleHandle) + return md.first; + } + + return std::string{}; +} + +v8::Local CV8ResourceImpl::GetModuleFromPath(std::string modulePath) +{ + for (auto& md : modules) + { + if (md.first == modulePath) + return md.second.Get(isolate); + } + + return v8::Local{}; +} + +static bool IsSystemModule(const std::string &name) +{ + return V8Module::Exists(name); +} + +v8::MaybeLocal CV8ResourceImpl::Require(const std::string &name) +{ + auto it = requires.find(name); + + if (it != requires.end()) + return it->second.Get(isolate); + + auto &v8module = V8Module::All().find(name); + if (v8module != V8Module::All().end()) + { + auto _exports = v8module->second->GetExports(isolate, GetContext()); + requires.insert({name, v8::UniquePersistent{isolate, _exports}}); + + return _exports; + } + + alt::IResource *resource = alt::ICore::Instance().GetResource(name); + if (resource) + { + v8::Local _exports = V8Helpers::MValueToV8(resource->GetExports()); + + requires.insert({name, v8::UniquePersistent{isolate, _exports}}); + + return _exports; + } + + return v8::MaybeLocal(); +} + +v8::MaybeLocal CV8ResourceImpl::ResolveFile(const std::string &name, v8::Local referrer) +{ + auto path = alt::ICore::Instance().Resolve(resource, name, GetModulePath(referrer)); + + if (!path.pkg) + return v8::MaybeLocal(); + + auto fileName = path.fileName.ToString(); + + if (fileName.size() == 0) + { + if (path.pkg->FileExists("index.js")) + fileName = "index.js"; + else if (path.pkg->FileExists("index.mjs")) + fileName = "index.mjs"; + else + return v8::MaybeLocal(); + } + else + { + if (path.pkg->FileExists(fileName + ".js")) + fileName += ".js"; + else if (path.pkg->FileExists(fileName + ".mjs")) + fileName += ".mjs"; + else if (path.pkg->FileExists(fileName + "/index.js")) + fileName += "/index.js"; + else if (path.pkg->FileExists(fileName + "/index.mjs")) + fileName += "/index.mjs"; + else if (!path.pkg->FileExists(fileName)) + return v8::MaybeLocal(); + } + + std::string fullName = path.prefix.ToString() + fileName; + + auto it = modules.find(fullName); + + if (it != modules.end()) + return it->second.Get(isolate); + + v8::MaybeLocal maybeModule; + + V8Helpers::TryCatch([&] { + alt::IPackage::File *file = path.pkg->OpenFile(fileName); + + std::string src(path.pkg->GetFileSize(file), '\0'); + path.pkg->ReadFile(file, src.data(), src.size()); + path.pkg->CloseFile(file); + + maybeModule = CompileESM(isolate, fullName, src); + + if (maybeModule.IsEmpty()) + return false; + + v8::Local _module = maybeModule.ToLocalChecked(); + + modules.emplace(fullName, v8::UniquePersistent{isolate, _module}); + /*v8::Maybe res = _module->InstantiateModule(GetContext(), CV8ScriptRuntime::ResolveModule); + if (res.IsNothing()) + return false; + + v8::MaybeLocal res2 = _module->Evaluate(GetContext()); + if (res2.IsEmpty()) + return false;*/ + + return true; + }); + + if (maybeModule.IsEmpty()) + { + modules.erase(fullName); + } + + return maybeModule; +} + +v8::MaybeLocal CV8ResourceImpl::ResolveModule(const std::string &_name, v8::Local referrer) +{ + v8::MaybeLocal maybeModule; + + std::string name = _name; + + if (name == "alt-client") + name = "alt"; + + auto it = modules.find(name); + if (it != modules.end()) + { + maybeModule = it->second.Get(isolate); + } + + if (maybeModule.IsEmpty()) + { + if (IsValidModule(name)) + { + V8Helpers::TryCatch([&] { + maybeModule = WrapModule(isolate, GetModuleKeys(name), name, IsSystemModule(name)); + + if (!maybeModule.IsEmpty()) + { + v8::Local _module = maybeModule.ToLocalChecked(); + modules.emplace(name, v8::UniquePersistent{isolate, _module}); + + /*v8::Maybe res = _module->InstantiateModule(GetContext(), CV8ScriptRuntime::ResolveModule); + if (res.IsNothing()) + { + Log::Info(__LINE__, "res.IsNothing()"); + return false; + } + + v8::MaybeLocal res2 = _module->Evaluate(GetContext()); + if (res2.IsEmpty()) + { + Log::Info(__LINE__, "res2.IsEmpty()"); + return false; + }*/ + + return true; + } + + Log::Info << __LINE__ << "maybeModule.IsEmpty()"; + return false; + }); + + if (maybeModule.IsEmpty()) + { + modules.erase(name); + isolate->ThrowException(v8::Exception::ReferenceError(v8::String::NewFromUtf8(isolate, ("Failed to load module: " + name).c_str()).ToLocalChecked())); + return v8::MaybeLocal{}; + } + } + } + + if (maybeModule.IsEmpty()) + { + maybeModule = ResolveFile(name, referrer); + } + + if (maybeModule.IsEmpty()) + { + modules.erase(name); + isolate->ThrowException(v8::Exception::ReferenceError(v8::String::NewFromUtf8(isolate, ("No such module: " + name).c_str()).ToLocalChecked())); + return v8::MaybeLocal{}; + } + + /*v8::Local _module = maybeModule.ToLocalChecked(); + if (_module->GetStatus() != v8::Module::kEvaluated) + { + modules.erase(name); + isolate->ThrowException(v8::Exception::ReferenceError(v8::String::NewFromUtf8(isolate, ("Failed to import: " + name).c_str()))); + return v8::MaybeLocal{ }; + }*/ + + return maybeModule; +} + +v8::MaybeLocal CV8ResourceImpl::ResolveCode(const std::string& code, const V8::SourceLocation& location) +{ + v8::MaybeLocal maybeModule; + std::stringstream name; + name << "[module " << location.GetFileName() << ":" << location.GetLineNumber() << "]"; + maybeModule = CompileESM(isolate, name.str(), code); + + return maybeModule; +} diff --git a/client/src/CV8Resource.h b/client/src/CV8Resource.h new file mode 100644 index 00000000..c2380f2d --- /dev/null +++ b/client/src/CV8Resource.h @@ -0,0 +1,165 @@ +#pragma once + +#include "cpp-sdk/IResource.h" +#include "cpp-sdk/objects/IEntity.h" + +#include "V8ResourceImpl.h" + +#include + +#include "Log.h" + +class CV8ScriptRuntime; + +class CV8ResourceImpl : public V8ResourceImpl +{ +public: + std::list> dynamicImports; + + CV8ResourceImpl(alt::IResource *resource, v8::Isolate *isolate) : V8ResourceImpl(isolate, resource) + { + } + + ~CV8ResourceImpl() + { + Log::Debug << __FUNCTION__ << Log::Endl; + } + + void ProcessDynamicImports(); + + bool Start() override; + + bool Stop() override; + + bool OnEvent(const alt::CEvent *ev) override; + + void OnTick() override; + + void OnPromiseRejectedWithNoHandler(v8::PromiseRejectMessage &data); + void OnPromiseHandlerAdded(v8::PromiseRejectMessage &data); + + void SubscribeWebView(alt::Ref view, const std::string &evName, v8::Local cb, V8::SourceLocation &&location, bool once = false) + { + webViewHandlers[view].insert({evName, V8::EventCallback{isolate, cb, std::move(location), once}}); + } + + void UnsubscribeWebView(alt::Ref view, const std::string &evName, v8::Local cb) + { + auto it = webViewHandlers.find(view); + if (it != webViewHandlers.end()) + { + auto &viewEvents = it->second; + auto range = viewEvents.equal_range(evName); + + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.fn.Get(isolate)->StrictEquals(cb)) + it->second.removed = true; + } + } + } + + std::vector GetWebViewHandlers(alt::Ref view, const std::string& name); + + void SubscribeWebSocketClient(alt::Ref webSocket, const std::string& evName, v8::Local cb, V8::SourceLocation&& location) + { + webSocketClientHandlers[webSocket].insert({ evName, V8::EventCallback{isolate, cb, std::move(location)} }); + } + + void UnsubscribeWebSocketClient(alt::Ref webSocket, const std::string& evName, v8::Local cb) + { + auto it = webSocketClientHandlers.find(webSocket); + if (it != webSocketClientHandlers.end()) + { + auto& webSocketEvents = it->second; + auto range = webSocketEvents.equal_range(evName); + + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.fn.Get(isolate)->StrictEquals(cb)) + it->second.removed = true; + } + } + } + + std::vector GetWebSocketClientHandlers(alt::Ref webSocket, const std::string& name); + + void SubscribeAudio(alt::Ref audio, const std::string& evName, v8::Local cb, V8::SourceLocation&& location) + { + audioHandlers[audio].insert({ evName, V8::EventCallback{isolate, cb, std::move(location)} }); + } + + void UnsubscribeAudio(alt::Ref audio, const std::string& evName, v8::Local cb) + { + auto it = audioHandlers.find(audio); + if (it != audioHandlers.end()) + { + auto& audioEvents = it->second; + auto range = audioEvents.equal_range(evName); + + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.fn.Get(isolate)->StrictEquals(cb)) + it->second.removed = true; + } + } + } + + std::vector GetAudioHandlers(alt::Ref audio, const std::string& name); + + void AddOwned(alt::Ref handle) + { + ownedObjects.insert(handle); + } + + void OnRemoveBaseObject(alt::Ref handle) + { + ownedObjects.erase(handle); + + if (handle->GetType() == alt::IBaseObject::Type::WEBVIEW) + webViewHandlers.erase(handle.As()); + + if (handle->GetType() == alt::IBaseObject::Type::WEBSOCKET_CLIENT) + webSocketClientHandlers.erase(handle.As()); + + V8ResourceImpl::OnRemoveBaseObject(handle); + } + + v8::Local GetLocalStorage() + { + if (localStorage.IsEmpty()) + { + std::vector> args; + extern V8Class v8LocalStorage; + localStorage.Reset(isolate, v8LocalStorage.New(GetContext(), args).As()); + } + + return localStorage.Get(isolate); + } + + bool IsValidModule(const std::string &name); + std::deque GetModuleKeys(const std::string &name); + std::string GetModulePath(v8::Local moduleHandle); + v8::Local GetModuleFromPath(std::string modulePath); + + v8::MaybeLocal Require(const std::string &name); + v8::MaybeLocal ResolveFile(const std::string &name, v8::Local referrer); + v8::MaybeLocal ResolveModule(const std::string &name, v8::Local referrer); + v8::MaybeLocal ResolveCode(const std::string& code, const V8::SourceLocation& location); + +private: + using WebViewEvents = std::unordered_multimap; + + std::unordered_map> requires; + std::unordered_map> modules; + + std::unordered_map, WebViewEvents> webViewHandlers; + std::unordered_map, WebViewEvents> webSocketClientHandlers; + std::unordered_map, WebViewEvents> audioHandlers; + + std::unordered_set> ownedObjects; + + v8::Persistent localStorage; + + std::unique_ptr microtaskQueue; +}; diff --git a/client/src/CV8ScriptRuntime.cpp b/client/src/CV8ScriptRuntime.cpp new file mode 100644 index 00000000..e6c2193c --- /dev/null +++ b/client/src/CV8ScriptRuntime.cpp @@ -0,0 +1,208 @@ + +#include "CV8ScriptRuntime.h" +#include "inspector/CV8InspectorClient.h" +#include "inspector/CV8InspectorChannel.h" +#include "V8Module.h" +#include "events/Events.h" + +CV8ScriptRuntime::CV8ScriptRuntime() +{ + platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(platform.get()); + v8::V8::Initialize(); + + create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + + isolate = v8::Isolate::New(create_params); + isolate->SetFatalErrorHandler([](const char *location, const char *message) { + Log::Error << "[V8] " << location << ": " << message << Log::Endl; + }); + + isolate->SetOOMErrorHandler([](const char* location, bool isHeap) { + if(!isHeap) return; + Log::Error << "[V8] " << location << ": Heap out of memory. Forward this to the server developers." << Log::Endl; + Log::Error << "[V8] The current heap limit can be shown with the 'heap' console command. Consider increasing your system RAM." << Log::Endl; + }); + + isolate->AddNearHeapLimitCallback([](void*, size_t current, size_t initial) { + Log::Warning << "[V8] The remaining V8 heap space is approaching critical levels. Forward this to the server developers." << Log::Endl; + Log::Warning << "[V8] Initial heap limit: " << CV8ScriptRuntime::FormatBytes(initial) << " | Current heap limit: " << CV8ScriptRuntime::FormatBytes(current) << Log::Endl; + + // Increase the heap limit by 100MB if the heap limit has not exceeded 4GB + uint64_t currentLimitMb = (current / 1024) / 1024; + if(currentLimitMb < 4096) return current + (100 * 1024 * 1024); + else return current; + }, nullptr); + + isolate->SetPromiseRejectCallback([](v8::PromiseRejectMessage message) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + v8::Local value = message.GetValue(); + v8::Local ctx = isolate->GetEnteredOrMicrotaskContext(); + + CV8ResourceImpl *resource = static_cast(V8ResourceImpl::Get(ctx)); + if (resource) + { + if (message.GetEvent() == v8::PromiseRejectEvent::kPromiseRejectWithNoHandler) + { + resource->OnPromiseRejectedWithNoHandler(message); + } + else if (message.GetEvent() == v8::PromiseRejectEvent::kPromiseHandlerAddedAfterReject) + { + resource->OnPromiseHandlerAdded(message); + } + else if (message.GetEvent() == v8::PromiseRejectEvent::kPromiseRejectAfterResolved) + { + Log::Warning << "[V8] Promise rejected after being resolved in resource " + << resource->GetResource()->GetName() << " (" + << *v8::String::Utf8Value(isolate, value->ToString(ctx).ToLocalChecked()) + << ")" << Log::Endl; + } + else if (message.GetEvent() == v8::PromiseRejectEvent::kPromiseResolveAfterResolved) + { + Log::Warning << "[V8] Promise resolved after being resolved in resource " + << resource->GetResource()->GetName() << " (" + << *v8::String::Utf8Value(isolate, value->ToString(ctx).ToLocalChecked()) + << ")" << Log::Endl; + } + } + else + { + Log::Error << "You're not supposed to ever see this" << Log::Endl; + } + }); + + isolate->SetHostImportModuleDynamicallyCallback([](v8::Local context, v8::Local referrer, v8::Local specifier, v8::Local) { + v8::Isolate* isolate = context->GetIsolate(); + + auto referrerVal = referrer->GetResourceName(); + if(referrerVal->IsUndefined()) + { + return v8::MaybeLocal(); + } + + std::string referrerUrl = *v8::String::Utf8Value(isolate, referrer->GetResourceName()); + auto resource = static_cast(V8ResourceImpl::Get(context)); + + auto resolver = v8::Promise::Resolver::New(context).ToLocalChecked(); + + V8::CPersistent presolver(isolate, resolver); + V8::CPersistent pspecifier(isolate, specifier); + V8::CPersistent preferrerModule(isolate, resource->GetModuleFromPath(referrerUrl)); + + // careful what we take in by value in the lambda + // it is possible pass v8::Local but should not be done + // make a V8::CPersistent out of it and pass that + auto domodule = [isolate, presolver, pspecifier, preferrerModule]{ + auto referrerModule = preferrerModule.Get(isolate); + auto resolver = presolver.Get(isolate); + auto specifier = pspecifier.Get(isolate); + + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + v8::Context::Scope ctxs(ctx); + + auto mmodule = ResolveModule(ctx, specifier, v8::Local(), referrerModule); + if(mmodule.IsEmpty()) + { + resolver->Reject(ctx, v8::Exception::ReferenceError(V8_NEW_STRING("Could not resolve module"))); + return; + } + + auto module = mmodule.ToLocalChecked(); + V8Helpers::TryCatch([&]{ + if(module->GetStatus() == v8::Module::Status::kUninstantiated && !module->InstantiateModule(ctx, ResolveModule).ToChecked()) + { + resolver->Reject(ctx, v8::Exception::ReferenceError(V8_NEW_STRING("Error instantiating module"))); + return false; + } + + if(module->GetStatus() != v8::Module::Status::kEvaluated && module->Evaluate(ctx).IsEmpty()) + { + resolver->Reject(ctx, v8::Exception::ReferenceError(V8_NEW_STRING("Error evaluating module"))); + return false; + } + + resolver->Resolve(ctx, module->GetModuleNamespace()); + return true; + }); + }; + + if(Instance().resourcesLoaded && resource->GetResource()->IsStarted()) + { + // instantly resolve the module + domodule(); + } else { + // put it in the queue to resolve after all resource are loaded + resource->dynamicImports.emplace_back(domodule); + } + + return v8::MaybeLocal(resolver->GetPromise()); + }); + + isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); + + /*{ + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + inspectorClient.reset(new alt::CV8InspectorClient); + inspectorChannel.reset(new alt::CV8InspectorChannel); + + inspector = v8_inspector::V8Inspector::create(isolate, inspectorClient.get()); + + v8_inspector::StringView inspectorView{ (uint8_t*)inspectorViewStr, strlen(inspectorViewStr) }; + inspectorSession = inspector->connect(1, inspectorChannel.get(), inspectorView); + }*/ + + profiler = v8::CpuProfiler::New(isolate); + profiler->SetUsePreciseSampling(true); + profiler->SetSamplingInterval(profilerSamplingInterval); + v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate); + + { + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + V8Class::LoadAll(isolate); + + extern V8Module altModule, nativesModule, sharedModule; + V8Module::Add({altModule, nativesModule, sharedModule}); + } + + RegisterEvents(); +} + +void CV8ScriptRuntime::OnEntityStreamIn(alt::Ref entity) +{ + switch(entity->GetType()) + { + case alt::IEntity::Type::PLAYER: + { + streamedInPlayers.insert({ entity->GetID(), entity.As() }); + break; + } + case alt::IEntity::Type::VEHICLE: + { + streamedInVehicles.insert({ entity->GetID(), entity.As() }); + break; + } + } +} + +void CV8ScriptRuntime::OnEntityStreamOut(alt::Ref entity) +{ + switch(entity->GetType()) + { + case alt::IEntity::Type::PLAYER: + { + streamedInPlayers.erase(entity->GetID()); + break; + } + case alt::IEntity::Type::VEHICLE: + { + streamedInVehicles.erase(entity->GetID()); + break; + } + } +} diff --git a/client/src/CV8ScriptRuntime.h b/client/src/CV8ScriptRuntime.h new file mode 100644 index 00000000..b546e2bc --- /dev/null +++ b/client/src/CV8ScriptRuntime.h @@ -0,0 +1,208 @@ +#pragma once + +#include "v8.h" +#include "v8-inspector.h" +#include "libplatform/libplatform.h" + +#include "cpp-sdk/IScriptRuntime.h" +#include "cpp-sdk/objects/IPlayer.h" +#include "cpp-sdk/objects/IVehicle.h" + +#include "CV8Resource.h" + +#include "v8-profiler.h" + +class CV8ScriptRuntime : public alt::IScriptRuntime +{ + static constexpr char inspectorViewStr[] = "alt:V Multiplayer"; + + std::unordered_set resources; + std::unique_ptr platform; + v8::Isolate::CreateParams create_params; + v8::Isolate *isolate; + std::unique_ptr inspectorClient; + std::unique_ptr inspectorChannel; + std::unique_ptr inspector; + std::unique_ptr inspectorSession; + v8::CpuProfiler* profiler; + uint32_t profilerSamplingInterval = 100; + + std::unordered_map> streamedInPlayers; + std::unordered_map> streamedInVehicles; + +public: + + CV8ScriptRuntime(); + + static CV8ScriptRuntime& Instance() + { + static CV8ScriptRuntime instance; + return instance; + } + + v8::Isolate *GetIsolate() const { return isolate; } + + v8_inspector::V8Inspector *GetInspector() const { return inspector.get(); } + + v8::CpuProfiler* GetProfiler() { return profiler; } + uint32_t GetProfilerSamplingInterval() { return profilerSamplingInterval; } + void SetProfilerSamplingInterval(uint32_t interval) + { + profilerSamplingInterval = interval; + profiler->SetSamplingInterval(interval); + } + + alt::IResource::Impl *CreateImpl(alt::IResource *resource) override + { + auto res = new CV8ResourceImpl(resource, isolate); + resources.insert(res); + return res; + } + + void DestroyImpl(alt::IResource::Impl *impl) override + { + auto res = static_cast(impl); + resources.erase(res); + delete res; + } + + void OnTick() override + { + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + v8::platform::PumpMessageLoop(platform.get(), isolate); + } + + std::unordered_set GetResources() + { + return resources; + } + + void HeapBenchmark() + { + v8::HeapStatistics heapStats; + isolate->GetHeapStatistics(&heapStats); + Log::Info << "================ Heap benchmark info =================" << Log::Endl; + Log::Info << "total_heap_size = " << FormatBytes(heapStats.total_heap_size()) << Log::Endl; + Log::Info << "total_heap_size_executable = " << FormatBytes(heapStats.total_heap_size_executable()) << Log::Endl; + Log::Info << "total_physical_size = " << FormatBytes(heapStats.total_physical_size()) << Log::Endl; + Log::Info << "total_available_size = " << FormatBytes(heapStats.total_available_size()) << Log::Endl; + Log::Info << "used_heap_size = " << FormatBytes(heapStats.used_heap_size()) << Log::Endl; + Log::Info << "heap_size_limit = " << FormatBytes(heapStats.heap_size_limit()) << Log::Endl; + Log::Info << "malloced_memory = " << FormatBytes(heapStats.malloced_memory()) << Log::Endl; + Log::Info << "external_memory = " << FormatBytes(heapStats.external_memory()) << Log::Endl; + Log::Info << "peak_malloced_memory = " << FormatBytes(heapStats.peak_malloced_memory()) << Log::Endl; + Log::Info << "number_of_native_contexts = " << heapStats.number_of_native_contexts() << Log::Endl; + Log::Info << "number_of_detached_contexts = " << heapStats.number_of_detached_contexts() << Log::Endl; + Log::Info << "======================================================" << Log::Endl; + } + + ~CV8ScriptRuntime() + { + isolate->Dispose(); + v8::V8::Dispose(); + v8::V8::ShutdownPlatform(); + delete create_params.array_buffer_allocator; + } + + static v8::MaybeLocal ResolveModule(v8::Local ctx, v8::Local specifier, v8::Local, v8::Local referrer) + { + auto isolate = ctx->GetIsolate(); + V8ResourceImpl *resource = V8ResourceImpl::Get(ctx); + if (!resource) + { + isolate->ThrowException(v8::Exception::ReferenceError(v8::String::NewFromUtf8(ctx->GetIsolate(), "Invalid resource").ToLocalChecked())); + return v8::MaybeLocal{}; + } + + std::string _specifier = *v8::String::Utf8Value{isolate, specifier}; + if(_specifier == resource->GetResource()->GetName().ToString()) + { + isolate->ThrowException(v8::Exception::ReferenceError(v8::String::NewFromUtf8(ctx->GetIsolate(), "Cannot import the resource itself (self-importing)").ToLocalChecked())); + return v8::MaybeLocal{}; + } + + return static_cast(resource)->ResolveModule(_specifier, referrer); + } + + static std::string FormatBytes(uint64_t bytes) + { + static std::string result = ""; + const char *sizes[5] = {"bytes", "KB", "MB", "GB", "TB"}; + + if (bytes == 0) + { + result = "0 bytes"; + return result; + } + else if (bytes == 1) + { + result = "1 byte"; + return result; + } + + uint64_t left = std::floor(std::log(bytes) / std::log(1024)); + + if (left == 0) + { + std::stringstream ss; + ss << bytes + " bytes"; + result = ss.str(); + return result; + } + else + { + std::stringstream ss; + ss << std::setprecision(2) << std::fixed << (bytes / std::pow(1024, left)) << " " << sizes[left]; + result = ss.str(); + return result; + } + }; + + // using DynamicImportReadyCallback = void (*)(v8::Local referrer, v8::Local specifier, const void* promise); + + // class DynamicImportReadyResult + // { + // public: + // DynamicImportReadyResult(v8::Local referrer, v8::Local specifier, const void* promise, DynamicImportReadyCallback cb) + // { + // _referrer = referrer; + // _specifier = specifier; + // _promise = promise; + // _callback = cb; + // } + + // v8::Local _referrer; + // v8::Local _specifier; + // DynamicImportReadyCallback _callback; + // const void* _promise; + + // void call() + // { + // _callback(_referrer, _specifier, _promise); + // } + // }; + + // std::list onDynamicImportReadyCallbacks; + // void OnDynamicImportReady(DynamicImportReadyResult result) + // { + // if (_allResourcesLoaded) result.call(); + // else onDynamicImportReadyCallbacks.emplace_back(result); + // }; + + bool resourcesLoaded = false; + + void OnEntityStreamIn(alt::Ref entity); + void OnEntityStreamOut(alt::Ref entity); + + auto GetStreamedInPlayers() + { + return streamedInPlayers; + } + auto GetStreamedInVehicles() + { + return streamedInVehicles; + } +}; \ No newline at end of file diff --git a/client/src/bindings/Audio.cpp b/client/src/bindings/Audio.cpp new file mode 100644 index 00000000..ae31f805 --- /dev/null +++ b/client/src/bindings/Audio.cpp @@ -0,0 +1,209 @@ +#include "V8Helpers.h" +#include "V8BindHelpers.h" +#include "V8ResourceImpl.h" +#include "V8Class.h" +#include "../CV8ScriptRuntime.h" +#include "cpp-sdk/script-objects/IAudio.h" + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN_MIN_MAX(2, 4); + + V8_ARG_TO_STRING(1, source); + V8_ARG_TO_NUMBER(2, volume); + + uint32_t category = 0; + bool frontend = false; + if(info.Length() == 3 || info.Length() == 4) + { + if(info[2]->IsNumber()) + { + V8_ARG_TO_UINT32(3, categ); + category = categ; + } + else + { + V8_ARG_TO_STRING(3, categ); + category = alt::ICore::Instance().Hash(categ); + } + if (info.Length() == 4) + { + V8_ARG_TO_BOOLEAN(4, frntnd); + frontend = frntnd; + } + } + + auto audio = alt::ICore::Instance().CreateAudio(source, volume, category, frontend, resource->GetResource()); + V8_BIND_BASE_OBJECT(audio, "Failed to create Audio"); +} + +static void On(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, fun); + + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + + static_cast(resource)->SubscribeAudio(audio, evName.ToString(), fun, V8::SourceLocation::GetCurrent(isolate)); +} + +static void Off(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, fun); + + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + + static_cast(resource)->UnsubscribeAudio(audio, evName.ToString(), fun); +} + +static void GetEventListeners(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + + V8_ARG_TO_STRING(1, eventName); + + std::vector handlers = static_cast(resource)->GetAudioHandlers(audio, eventName.ToString()); + + auto array = v8::Array::New(isolate, handlers.size()); + for (int i = 0; i < handlers.size(); i++) + { + array->Set(ctx, i, handlers[i]->fn.Get(isolate)); + } + + V8_RETURN(array); +} + +static void CategoryGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + + V8_RETURN_UINT(audio->GetCategory()); +} + +static void CategorySetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + + int64_t category; + if(val->IsNumber()) + { + V8_TO_INTEGER(val, categ); + category = categ; + } + else if(val->IsString()) + { + V8_TO_STRING(val, categ); + category = alt::ICore::Instance().Hash(categ); + } + audio->SetCategory(category); +} + +static void AddOutput(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + V8_CHECK_ARGS_LEN(1); + + if(info[0]->IsInt32() || info[0]->IsUint32()) + { + V8_ARG_TO_INT(1, scriptId); + audio->AddOutput(scriptId); + } + else + { + V8_ARG_TO_BASE_OBJECT(1, entity, alt::IEntity, "Entity"); + audio->AddOutput(entity); + } +} + +static void RemoveOutput(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + V8_CHECK_ARGS_LEN(1); + + if(info[0]->IsInt32() || info[0]->IsUint32()) + { + V8_ARG_TO_INT(1, scriptId); + audio->RemoveOutput(scriptId); + } + else + { + V8_ARG_TO_BASE_OBJECT(1, entity, alt::IEntity, "Entity"); + audio->RemoveOutput(entity); + } +} + +static void GetOutputs(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + + auto list = audio->GetOutputs(); + auto arr = v8::Array::New(isolate, list->GetSize()); + for(int i = 0; i < list->GetSize(); i++) + { + auto val = list->Get(i); + if(val->GetType() == alt::IMValue::Type::BASE_OBJECT) + { + auto baseObj = resource->GetBaseObjectOrNull(val.As()->Value()); + arr->Set(ctx, i, baseObj); + } + else if(val->GetType() == alt::IMValue::Type::UINT) + arr->Set(ctx, i, v8::Integer::NewFromUnsigned(isolate, val.As()->Value())); + } + + V8_RETURN(arr); +} + +static void Seek(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(audio, alt::IAudio); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_NUMBER(1, time); + + audio->Seek(time); +} + +extern V8Class v8BaseObject; +extern V8Class v8Audio("Audio", v8BaseObject, &Constructor, [](v8::Local tpl) { + using namespace alt; + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetMethod(isolate, tpl, "on", &On); + V8::SetMethod(isolate, tpl, "off", &Off); + V8::SetMethod(isolate, tpl, "getEventListeners", GetEventListeners); + + V8::SetAccessor(isolate, tpl, "source"); + V8::SetAccessor(isolate, tpl, "looped"); + V8::SetAccessor(isolate, tpl, "volume"); + V8::SetAccessor(isolate, tpl, "category", &CategoryGetter, &CategorySetter); + V8::SetAccessor(isolate, tpl, "frontendPlay"); + V8::SetAccessor(isolate, tpl, "currentTime"); + V8::SetAccessor(isolate, tpl, "maxTime"); + V8::SetAccessor(isolate, tpl, "playing"); + + V8::SetMethod(isolate, tpl, "addOutput", &AddOutput); + V8::SetMethod(isolate, tpl, "removeOutput", &RemoveOutput); + V8::SetMethod(isolate, tpl, "getOutputs", &GetOutputs); + + V8::SetMethod(isolate, tpl, "play"); + V8::SetMethod(isolate, tpl, "pause"); + V8::SetMethod(isolate, tpl, "reset"); + V8::SetMethod(isolate, tpl, "seek", &Seek); +}); diff --git a/client/src/bindings/Blip.cpp b/client/src/bindings/Blip.cpp new file mode 100644 index 00000000..6070a1a9 --- /dev/null +++ b/client/src/bindings/Blip.cpp @@ -0,0 +1,210 @@ +#include "../CV8Resource.h" +#include "V8Helpers.h" +#include "V8BindHelpers.h" +#include "cpp-sdk/script-objects/IBlip.h" + +static void ToString(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + auto blip = info.This(); + V8_OBJECT_GET_STRING(blip, "name", name); + V8_OBJECT_GET_STRING(blip, "category", category); + + std::ostringstream ss; + ss << "Blip{ name: " << name.CStr() << ", category: " << category.CStr() << " }"; + + V8_RETURN_STRING(ss.str().c_str()); +} + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK(false, "You can't use constructor of abstract class"); +} + +static void ConstructorAreaBlip(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(5); + V8_ARG_TO_NUMBER(1, x); + V8_ARG_TO_NUMBER(2, y); + V8_ARG_TO_NUMBER(3, z); + V8_ARG_TO_NUMBER(4, width); + V8_ARG_TO_NUMBER(5, height); + + alt::Ref blip = alt::ICore::Instance().CreateBlip({ x, y, z }, width, height); + V8_BIND_BASE_OBJECT(blip, "Failed to create AreaBlip"); +} + +static void ConstructorRadiusBlip(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(4); + V8_ARG_TO_NUMBER(1, x); + V8_ARG_TO_NUMBER(2, y); + V8_ARG_TO_NUMBER(3, z); + V8_ARG_TO_NUMBER(4, radius); + + alt::Ref blip = alt::ICore::Instance().CreateBlip({ x, y, z }, radius); + V8_BIND_BASE_OBJECT(blip, "Failed to create RadiusBlip"); +} + +static void ConstructorPointBlip(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(3); + V8_ARG_TO_NUMBER(1, x); + V8_ARG_TO_NUMBER(2, y); + V8_ARG_TO_NUMBER(3, z); + + alt::Ref blip = alt::ICore::Instance().CreateBlip(alt::IBlip::BlipType::DESTINATION, { x, y, z }); + V8_BIND_BASE_OBJECT(blip, "Failed to create PointBlip"); +} + +static void ConstructorPedBlip(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, pedId); + + alt::Ref blip = alt::ICore::Instance().CreateBlip(alt::IBlip::BlipType::PED, pedId); + V8_BIND_BASE_OBJECT(blip, "Failed to create PedBlip"); +} + +static void ConstructorVehicleBlip(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, vehicleId); + + alt::Ref blip = alt::ICore::Instance().CreateBlip(alt::IBlip::BlipType::VEHICLE, vehicleId); + V8_BIND_BASE_OBJECT(blip, "Failed to create VehicleBlip"); +} + +static void RouteColorGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_GET_THIS_BASE_OBJECT(blip, alt::IBlip); + V8_RETURN(resource->CreateRGBA(blip->GetRouteColor())); +} + +static void RouteColorSetter(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(blip, alt::IBlip); + + V8_TO_OBJECT(value, color); + V8_OBJECT_GET_INT(color, "r", r); + V8_OBJECT_GET_INT(color, "g", g); + V8_OBJECT_GET_INT(color, "b", b); + V8_OBJECT_GET_INT(color, "a", a); + + blip->SetRouteColor({ (uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a }); +} + +static void ScaleGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(blip, alt::IBlip); + V8_RETURN_NUMBER(blip->GetScaleXY()[0]); +} + +static void ScaleSetter(v8::Local, v8::Local value, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(blip, alt::IBlip); + V8_TO_NUMBER(value, val); + blip->SetScaleXY({ val, val }); +} + +static void Fade(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_INT(1, opacity); + V8_ARG_TO_INT(2, duration); + V8_GET_THIS_BASE_OBJECT(blip, alt::IBlip); + blip->Fade(opacity, duration); +} + +extern V8Class v8WorldObject; +extern V8Class v8Blip("Blip", v8WorldObject, Constructor, [](v8::Local tpl){ + using namespace alt; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + V8::SetMethod(isolate, tpl, "toString", ToString); + + V8::SetStaticAccessor(isolate, tpl, "routeColor", &RouteColorGetter, &RouteColorSetter); + + V8::SetAccessor(isolate, tpl, "sprite"); + V8::SetAccessor(isolate, tpl, "size"); + V8::SetAccessor(isolate, tpl, "scale", &ScaleGetter, &ScaleSetter); + V8::SetAccessor(isolate, tpl, "color"); + V8::SetAccessor(isolate, tpl, "secondaryColor"); + V8::SetAccessor(isolate, tpl, "alpha"); + V8::SetAccessor(isolate, tpl, "flashTimer"); + V8::SetAccessor(isolate, tpl, "flashInterval"); + V8::SetAccessor(isolate, tpl, "route"); + V8::SetAccessor(isolate, tpl, "bright"); + V8::SetAccessor(isolate, tpl, "number"); + V8::SetAccessor(isolate, tpl, "display"); + V8::SetAccessor(isolate, tpl, "showCone"); + V8::SetAccessor(isolate, tpl, "flashes"); + V8::SetAccessor(isolate, tpl, "flashesAlternate"); + V8::SetAccessor(isolate, tpl, "shortRange"); + V8::SetAccessor(isolate, tpl, "priority"); + V8::SetAccessor(isolate, tpl, "heading"); + V8::SetAccessor(isolate, tpl, "gxtName"); + V8::SetAccessor(isolate, tpl, "name"); + V8::SetAccessor(isolate, tpl, "pulse"); + V8::SetAccessor(isolate, tpl, "asMissionCreator"); + V8::SetAccessor(isolate, tpl, "tickVisible"); + V8::SetAccessor(isolate, tpl, "headingIndicatorVisible"); + V8::SetAccessor(isolate, tpl, "outlineIndicatorVisible"); + V8::SetAccessor(isolate, tpl, "friendIndicatorVisible"); + V8::SetAccessor(isolate, tpl, "crewIndicatorVisible"); + V8::SetAccessor(isolate, tpl, "category"); + V8::SetAccessor(isolate, tpl, "highDetail"); + V8::SetAccessor(isolate, tpl, "shrinked"); + + V8::SetAccessor(isolate, tpl, "scriptID"); + + V8::SetMethod(isolate, tpl, "fade", &Fade); +}); + +extern V8Class v8AreaBlip("AreaBlip", v8Blip, ConstructorAreaBlip, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + v8::Local proto = tpl->PrototypeTemplate(); +}); + +extern V8Class v8RadiusBlip("RadiusBlip", v8Blip, ConstructorRadiusBlip, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + v8::Local proto = tpl->PrototypeTemplate(); +}); + +extern V8Class v8PointBlip("PointBlip", v8Blip, ConstructorPointBlip, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + v8::Local proto = tpl->PrototypeTemplate(); +}); + +extern V8Class v8PedBlip("PedBlip", v8Blip, ConstructorPedBlip, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + v8::Local proto = tpl->PrototypeTemplate(); +}); + +extern V8Class v8VehicleBlip("VehicleBlip", v8Blip, ConstructorVehicleBlip, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + v8::Local proto = tpl->PrototypeTemplate(); +}); diff --git a/client/src/bindings/Checkpoint.cpp b/client/src/bindings/Checkpoint.cpp new file mode 100644 index 00000000..e2b830b9 --- /dev/null +++ b/client/src/bindings/Checkpoint.cpp @@ -0,0 +1,95 @@ +#include "../CV8Resource.h" +#include "V8Helpers.h" +#include "V8BindHelpers.h" +#include "cpp-sdk/script-objects/ICheckpoint.h" + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + if(info.Length() == 6) + { + V8_ARG_TO_INT(1, type); + V8_ARG_TO_OBJECT(2, pos); + V8_ARG_TO_OBJECT(3, nextPos); + V8_ARG_TO_NUMBER(4, radius); + V8_ARG_TO_NUMBER(5, height); + V8_ARG_TO_OBJECT(6, color); + + V8_OBJECT_GET_NUMBER(pos, "x", x); + V8_OBJECT_GET_NUMBER(pos, "y", y); + V8_OBJECT_GET_NUMBER(pos, "z", z); + + V8_OBJECT_GET_NUMBER(nextPos, "x", x2); + V8_OBJECT_GET_NUMBER(nextPos, "y", y2); + V8_OBJECT_GET_NUMBER(nextPos, "z", z2); + + V8_OBJECT_GET_INT(color, "r", r); + V8_OBJECT_GET_INT(color, "g", g); + V8_OBJECT_GET_INT(color, "b", b); + V8_OBJECT_GET_INT(color, "a", a); + + alt::Ref cp = alt::ICore::Instance().CreateCheckpoint(type, { x, y, z }, { x2, y2, z2 }, radius, height, { (uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a }); + V8_BIND_BASE_OBJECT(cp, "Failed to create Checkpoint"); + } + else if(info.Length() == 10) + { + V8_ARG_TO_INT(1, type); + V8_ARG_TO_NUMBER(2, x); + V8_ARG_TO_NUMBER(3, y); + V8_ARG_TO_NUMBER(4, z); + V8_ARG_TO_NUMBER(5, x2); + V8_ARG_TO_NUMBER(6, y2); + V8_ARG_TO_NUMBER(7, z2); + V8_ARG_TO_NUMBER(8, radius); + V8_ARG_TO_NUMBER(9, height); + V8_ARG_TO_OBJECT(10, color); + + V8_OBJECT_GET_INT(color, "r", r); + V8_OBJECT_GET_INT(color, "g", g); + V8_OBJECT_GET_INT(color, "b", b); + V8_OBJECT_GET_INT(color, "a", a); + + alt::Ref cp = alt::ICore::Instance().CreateCheckpoint(type, { x, y, z }, { x2, y2, z2 }, radius, height, { (uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a }); + V8_BIND_BASE_OBJECT(cp, "Failed to create Checkpoint"); + } + else V8Helpers::Throw(isolate, "6 or 10 arguments expected"); +} + +static void IsEntityIn(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_GET_THIS_BASE_OBJECT(cp, alt::ICheckpoint); + + V8_ARG_TO_BASE_OBJECT(1, ent, alt::IEntity, "IEntity"); + + V8_RETURN_BOOLEAN(cp->IsEntityIn(ent)); +} + +static void IsPointIn(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_GET_THIS_BASE_OBJECT(cp, alt::ICheckpoint); + + V8_ARG_TO_OBJECT(1, pos); + V8_OBJECT_GET_NUMBER(pos, "x", x); + V8_OBJECT_GET_NUMBER(pos, "y", y); + V8_OBJECT_GET_NUMBER(pos, "z", z); + + V8_RETURN_BOOLEAN(cp->IsPointIn({ x, y, z})); +} + +extern V8Class v8WorldObject; +extern V8Class v8Checkpoint("Checkpoint", v8WorldObject, Constructor, [](v8::Local tpl) { + using namespace alt; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + V8::SetAccessor(isolate, tpl, "checkpointType"); + V8::SetAccessor(isolate, tpl, "radius"); + V8::SetAccessor(isolate, tpl, "height"); + V8::SetAccessor(isolate, tpl, "color"); + V8::SetAccessor(isolate, tpl, "nextPos"); + + V8::SetMethod(isolate, tpl, "isEntityIn", &IsEntityIn); + V8::SetMethod(isolate, tpl, "isPointIn", &IsPointIn); +}); diff --git a/client/src/bindings/Discord.cpp b/client/src/bindings/Discord.cpp new file mode 100644 index 00000000..5a4dddd3 --- /dev/null +++ b/client/src/bindings/Discord.cpp @@ -0,0 +1,38 @@ + +#include "../CV8Resource.h" +#include "V8Class.h" +#include "V8Helpers.h" + +struct DiscordRequestOAuth2TokenCallbackData +{ + CV8ResourceImpl *resource; + v8::Persistent resolver; +}; + +static void CurrentUserGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + auto discord = alt::ICore::Instance().GetDiscordManager(); + if (discord->IsUserDataReady()) + { + V8_NEW_OBJECT(discordInfo); + + V8_OBJECT_SET_STRING(discordInfo, "id", discord->GetUserID()); + V8_OBJECT_SET_STRING(discordInfo, "name", discord->GetUsername()); + V8_OBJECT_SET_STRING(discordInfo, "discriminator", discord->GetDiscriminator()); + V8_OBJECT_SET_STRING(discordInfo, "avatar", discord->GetAvatar()); + + V8_RETURN(discordInfo); + } + else + { + V8_RETURN_NULL(); + } +} + +extern V8Class v8Discord("Discord", [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetStaticAccessor(isolate, tpl, "currentUser", &CurrentUserGetter); +}); diff --git a/client/src/bindings/Handling.cpp b/client/src/bindings/Handling.cpp new file mode 100644 index 00000000..f8acf890 --- /dev/null +++ b/client/src/bindings/Handling.cpp @@ -0,0 +1,1287 @@ + +#include "../CV8Resource.h" +#include "V8Helpers.h" +#include "V8Class.h" +#include "V8Entity.h" +#include "V8ResourceImpl.h" +#include "cpp-sdk/objects/IVehicle.h" + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_OBJECT(1, vehicle); + + info.This()->SetInternalField(0, vehicle); +} + +static void IsModified(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN_BOOLEAN(vehicle->IsHandlingModified()); +} + +static void Reset(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + vehicle->ResetHandling(); +} + +static void HandlingNameHashGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN_INT(vehicle->GetHandling()->GetHandlingNameHash()); +} + +static void MassGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetMass()); +} + +static void MassSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetMass(fvalue); +} + +static void InitialDragCoeffGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN_NUMBER(vehicle->GetHandling()->GetInitialDragCoeff()); +} + +static void InitialDragCoeffSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetInitialDragCoeff(fvalue); +} + +static void DownforceModifierGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN_NUMBER(vehicle->GetHandling()->GetDownforceModifier()); +} + +static void DownforceModifierSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetDownforceModifier(fvalue); +} + +static void unkFloat1Getter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN_NUMBER(vehicle->GetHandling()->GetunkFloat1()); +} + +static void unkFloat1Setter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetunkFloat1(fvalue); +} + +static void unkFloat2Getter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN_NUMBER(vehicle->GetHandling()->GetunkFloat2()); +} + +static void unkFloat2Setter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetunkFloat2(fvalue); +} + +static void CentreOfMassOffsetGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN(resource->CreateVector3(vehicle->GetHandling()->GetCentreOfMassOffset())); +} + +static void CentreOfMassOffsetSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_OBJECT(val, pos); + V8_OBJECT_GET_NUMBER(pos, "x", x); + V8_OBJECT_GET_NUMBER(pos, "y", y); + V8_OBJECT_GET_NUMBER(pos, "z", z); + + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetCentreOfMassOffset({ x, y, z }); +} + +static void InertiaMultiplierGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN(resource->CreateVector3(vehicle->GetHandling()->GetInertiaMultiplier())); +} + +static void InertiaMultiplierSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_OBJECT(val, pos); + V8_OBJECT_GET_NUMBER(pos, "x", x); + V8_OBJECT_GET_NUMBER(pos, "y", y); + V8_OBJECT_GET_NUMBER(pos, "z", z); + + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetInertiaMultiplier({ x, y, z }); +} + +static void PercentSubmergedGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetPercentSubmerged()); +} + +static void PercentSubmergedSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetPercentSubmerged(fvalue); +} + +static void PercentSubmergedRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetPercentSubmergedRatio()); +} + +static void PercentSubmergedRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetPercentSubmergedRatio(fvalue); +} + +static void DriveBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetDriveBiasFront()); +} + +static void DriveBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetDriveBiasFront(fvalue); +} + +static void AccelerationGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetAcceleration()); +} + +static void AccelerationSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetAcceleration(fvalue); +} + +static void InitialDriveGearsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_INT(vehicle->GetHandling()->GetInitialDriveGears()); +} + +static void InitialDriveGearsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_INTEGER(val, ivalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetInitialDriveGears(ivalue); +} + +static void DriveInertiaGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetDriveInertia()); +} + +static void DriveInertiaSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetDriveInertia(fvalue); +} + +static void ClutchChangeRateScaleUpShiftGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetClutchChangeRateScaleUpShift()); +} + +static void ClutchChangeRateScaleUpShiftSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetClutchChangeRateScaleUpShift(fvalue); +} + +static void ClutchChangeRateScaleDownShiftGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetClutchChangeRateScaleDownShift()); +} + +static void ClutchChangeRateScaleDownShiftSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetClutchChangeRateScaleDownShift(fvalue); +} + +static void InitialDriveForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetInitialDriveForce()); +} + +static void InitialDriveForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetInitialDriveForce(fvalue); +} + +static void DriveMaxFlatVelGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetDriveMaxFlatVel()); +} + +static void DriveMaxFlatVelSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetDriveMaxFlatVel(fvalue); +} + +static void InitialDriveMaxFlatVelGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetInitialDriveMaxFlatVel()); +} + +static void InitialDriveMaxFlatVelSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetInitialDriveMaxFlatVel(fvalue); +} + +static void BrakeForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetBrakeForce()); +} + +static void BrakeForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetBrakeForce(fvalue); +} + +static void unkFloat4Getter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetunkFloat4()); +} + +static void unkFloat4Setter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetunkFloat4(fvalue); +} + +static void BrakeBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetBrakeBiasFront()); +} + +static void BrakeBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetBrakeBiasFront(fvalue); +} + +static void BrakeBiasRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetBrakeBiasRear()); +} + +static void BrakeBiasRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetBrakeBiasRear(fvalue); +} + +static void HandBrakeForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetHandBrakeForce()); +} + +static void HandBrakeForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetHandBrakeForce(fvalue); +} + +static void SteeringLockGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSteeringLock()); +} + +static void SteeringLockSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSteeringLock(fvalue); +} + +static void SteeringLockRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSteeringLockRatio()); +} + +static void SteeringLockRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSteeringLockRatio(fvalue); +} + +static void TractionCurveMaxGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionCurveMax()); +} + +static void TractionCurveMaxSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionCurveMax(fvalue); +} + +static void TractionCurveMaxRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionCurveMaxRatio()); +} + +static void TractionCurveMaxRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionCurveMaxRatio(fvalue); +} + +static void TractionCurveMinGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionCurveMin()); +} + +static void TractionCurveMinSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionCurveMin(fvalue); +} + +static void TractionCurveMinRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionCurveMinRatio()); +} + +static void TractionCurveMinRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionCurveMinRatio(fvalue); +} + +static void TractionCurveLateralGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionCurveLateral()); +} + +static void TractionCurveLateralSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionCurveLateral(fvalue); +} + +static void TractionCurveLateralRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionCurveLateralRatio()); +} + +static void TractionCurveLateralRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionCurveLateralRatio(fvalue); +} + +static void TractionSpringDeltaMaxGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionSpringDeltaMax()); +} + +static void TractionSpringDeltaMaxSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionSpringDeltaMax(fvalue); +} + +static void TractionSpringDeltaMaxRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionSpringDeltaMaxRatio()); +} + +static void TractionSpringDeltaMaxRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionSpringDeltaMaxRatio(fvalue); +} + +static void LowSpeedTractionLossMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetLowSpeedTractionLossMult()); +} + +static void LowSpeedTractionLossMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetLowSpeedTractionLossMult(fvalue); +} + +static void CamberStiffnessGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetCamberStiffness()); +} + +static void CamberStiffnessSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetCamberStiffness(fvalue); +} + +static void TractionBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionBiasFront()); +} + +static void TractionBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionBiasFront(fvalue); +} + +static void TractionBiasRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionBiasRear()); +} + +static void TractionBiasRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionBiasRear(fvalue); +} + +static void TractionLossMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetTractionLossMult()); +} + +static void TractionLossMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetTractionLossMult(fvalue); +} + +static void SuspensionForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSuspensionForce()); +} + +static void SuspensionForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSuspensionForce(fvalue); +} + +static void SuspensionCompDampGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSuspensionCompDamp()); +} + +static void SuspensionCompDampSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSuspensionCompDamp(fvalue); +} + +static void SuspensionReboundDampGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSuspensionReboundDamp()); +} + +static void SuspensionReboundDampSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSuspensionReboundDamp(fvalue); +} + +static void SuspensionUpperLimitGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSuspensionUpperLimit()); +} + +static void SuspensionUpperLimitSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSuspensionUpperLimit(fvalue); +} + +static void SuspensionLowerLimitGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSuspensionLowerLimit()); +} + +static void SuspensionLowerLimitSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSuspensionLowerLimit(fvalue); +} + +static void SuspensionRaiseGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSuspensionRaise()); +} + +static void SuspensionRaiseSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSuspensionRaise(fvalue); +} + +static void SuspensionBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSuspensionBiasFront()); +} + +static void SuspensionBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSuspensionBiasFront(fvalue); +} + +static void SuspensionBiasRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSuspensionBiasRear()); +} + +static void SuspensionBiasRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSuspensionBiasRear(fvalue); +} + +static void AntiRollBarForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetAntiRollBarForce()); +} + +static void AntiRollBarForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetAntiRollBarForce(fvalue); +} + +static void AntiRollBarBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetAntiRollBarBiasFront()); +} + +static void AntiRollBarBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetAntiRollBarBiasFront(fvalue); +} + +static void AntiRollBarBiasRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetAntiRollBarBiasRear()); +} + +static void AntiRollBarBiasRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetAntiRollBarBiasRear(fvalue); +} + +static void RollCentreHeightFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetRollCentreHeightFront()); +} + +static void RollCentreHeightFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetRollCentreHeightFront(fvalue); +} + +static void RollCentreHeightRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetRollCentreHeightRear()); +} + +static void RollCentreHeightRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetRollCentreHeightRear(fvalue); +} + +static void CollisionDamageMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetCollisionDamageMult()); +} + +static void CollisionDamageMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetCollisionDamageMult(fvalue); +} + +static void WeaponDamageMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetWeaponDamageMult()); +} + +static void WeaponDamageMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetWeaponDamageMult(fvalue); +} + +static void DeformationDamageMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetDeformationDamageMult()); +} + +static void DeformationDamageMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetDeformationDamageMult(fvalue); +} + +static void EngineDamageMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetEngineDamageMult()); +} + +static void EngineDamageMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetEngineDamageMult(fvalue); +} + +static void PetrolTankVolumeGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetPetrolTankVolume()); +} + +static void PetrolTankVolumeSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetPetrolTankVolume(fvalue); +} + +static void OilVolumeGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetOilVolume()); +} + +static void OilVolumeSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetOilVolume(fvalue); +} + +static void unkFloat5Getter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetunkFloat5()); +} + +static void unkFloat5Setter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetunkFloat5(fvalue); +} + +static void SeatOffsetDistXGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSeatOffsetDistX()); +} + +static void SeatOffsetDistXSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSeatOffsetDistX(fvalue); +} + +static void SeatOffsetDistYGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSeatOffsetDistY()); +} + +static void SeatOffsetDistYSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSeatOffsetDistY(fvalue); +} + +static void SeatOffsetDistZGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_NUMBER(vehicle->GetHandling()->GetSeatOffsetDistZ()); +} + +static void SeatOffsetDistZSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_NUMBER(val, fvalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetSeatOffsetDistZ(fvalue); +} + +static void MonetaryValueGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_INT(vehicle->GetHandling()->GetMonetaryValue()); +} + +static void MonetaryValueSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_INTEGER(val, ivalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetMonetaryValue(ivalue); +} + +static void ModelFlagsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_RETURN_INT(vehicle->GetHandling()->GetModelFlags()); +} + +static void ModelFlagsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_INTEGER(val, ivalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetModelFlags(ivalue); +} + +static void HandlingFlagsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN_INT(vehicle->GetHandling()->GetHandlingFlags()); +} + +static void HandlingFlagsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_INTEGER(val, ivalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetHandlingFlags(ivalue); +} + +static void DamageFlagsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + V8_RETURN_INT(vehicle->GetHandling()->GetDamageFlags()); +} + +static void DamageFlagsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_ENTITY(1, vehicle, alt::IVehicle); + + V8_TO_INTEGER(val, ivalue); + vehicle->ReplaceHandling(); + vehicle->GetHandling()->SetDamageFlags(ivalue); +} + +extern V8Class v8Handling("Handling", Constructor, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + v8::Local proto = tpl->PrototypeTemplate(); + + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + V8::SetMethod(isolate, tpl, "isModified", &IsModified); + V8::SetMethod(isolate, tpl, "reset", &Reset); + + V8::SetAccessor(isolate, tpl, "handlingNameHash", &HandlingNameHashGetter); + V8::SetAccessor(isolate, tpl, "mass", &MassGetter, &MassSetter); + V8::SetAccessor(isolate, tpl, "initialDragCoeff", &InitialDragCoeffGetter, &InitialDragCoeffSetter); + V8::SetAccessor(isolate, tpl, "downforceModifier", &DownforceModifierGetter, &DownforceModifierSetter); + V8::SetAccessor(isolate, tpl, "unkFloat1", &unkFloat1Getter, &unkFloat1Setter); + V8::SetAccessor(isolate, tpl, "unkFloat2", &unkFloat2Getter, &unkFloat2Setter); + V8::SetAccessor(isolate, tpl, "centreOfMassOffset", &CentreOfMassOffsetGetter, &CentreOfMassOffsetSetter); + V8::SetAccessor(isolate, tpl, "inertiaMultiplier", &InertiaMultiplierGetter, &InertiaMultiplierSetter); + V8::SetAccessor(isolate, tpl, "percentSubmerged", &PercentSubmergedGetter, &PercentSubmergedSetter); + V8::SetAccessor(isolate, tpl, "percentSubmergedRatio", &PercentSubmergedRatioGetter, &PercentSubmergedRatioSetter); + V8::SetAccessor(isolate, tpl, "driveBiasFront", &DriveBiasFrontGetter, &DriveBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "acceleration", &AccelerationGetter, &AccelerationSetter); + V8::SetAccessor(isolate, tpl, "initialDriveGears", &InitialDriveGearsGetter, &InitialDriveGearsSetter); + V8::SetAccessor(isolate, tpl, "driveInertia", &DriveInertiaGetter, &DriveInertiaSetter); + V8::SetAccessor(isolate, tpl, "clutchChangeRateScaleUpShift", &ClutchChangeRateScaleUpShiftGetter, &ClutchChangeRateScaleUpShiftSetter); + V8::SetAccessor(isolate, tpl, "clutchChangeRateScaleDownShift", &ClutchChangeRateScaleDownShiftGetter, &ClutchChangeRateScaleDownShiftSetter); + V8::SetAccessor(isolate, tpl, "initialDriveForce", &InitialDriveForceGetter, &InitialDriveForceSetter); + V8::SetAccessor(isolate, tpl, "driveMaxFlatVel", &DriveMaxFlatVelGetter, &DriveMaxFlatVelSetter); + V8::SetAccessor(isolate, tpl, "initialDriveMaxFlatVel", &InitialDriveMaxFlatVelGetter, &InitialDriveMaxFlatVelSetter); + V8::SetAccessor(isolate, tpl, "brakeForce", &BrakeForceGetter, &BrakeForceSetter); + V8::SetAccessor(isolate, tpl, "unkFloat4", &unkFloat4Getter, &unkFloat4Setter); + V8::SetAccessor(isolate, tpl, "brakeBiasFront", &BrakeBiasFrontGetter, &BrakeBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "brakeBiasRear", &BrakeBiasRearGetter, &BrakeBiasRearSetter); + V8::SetAccessor(isolate, tpl, "handBrakeForce", &HandBrakeForceGetter, &HandBrakeForceSetter); + V8::SetAccessor(isolate, tpl, "steeringLock", &SteeringLockGetter, &SteeringLockSetter); + V8::SetAccessor(isolate, tpl, "steeringLockRatio", &SteeringLockRatioGetter, &SteeringLockRatioSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveMax", &TractionCurveMaxGetter, &TractionCurveMaxSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveMaxRatio", &TractionCurveMaxRatioGetter, &TractionCurveMaxRatioSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveMin", &TractionCurveMinGetter, &TractionCurveMinSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveMinRatio", &TractionCurveMinRatioGetter, &TractionCurveMinRatioSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveLateral", &TractionCurveLateralGetter, &TractionCurveLateralSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveLateralRatio", &TractionCurveLateralRatioGetter, &TractionCurveLateralRatioSetter); + V8::SetAccessor(isolate, tpl, "tractionSpringDeltaMax", &TractionSpringDeltaMaxGetter, &TractionSpringDeltaMaxSetter); + V8::SetAccessor(isolate, tpl, "tractionSpringDeltaMaxRatio", &TractionSpringDeltaMaxRatioGetter, &TractionSpringDeltaMaxRatioSetter); + V8::SetAccessor(isolate, tpl, "lowSpeedTractionLossMult", &LowSpeedTractionLossMultGetter, &LowSpeedTractionLossMultSetter); + V8::SetAccessor(isolate, tpl, "camberStiffness", &CamberStiffnessGetter, &CamberStiffnessSetter); + V8::SetAccessor(isolate, tpl, "tractionBiasFront", &TractionBiasFrontGetter, &TractionBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "tractionBiasRear", &TractionBiasRearGetter, &TractionBiasRearSetter); + V8::SetAccessor(isolate, tpl, "tractionLossMult", &TractionLossMultGetter, &TractionLossMultSetter); + V8::SetAccessor(isolate, tpl, "suspensionForce", &SuspensionForceGetter, &SuspensionForceSetter); + V8::SetAccessor(isolate, tpl, "suspensionCompDamp", &SuspensionCompDampGetter, &SuspensionCompDampSetter); + V8::SetAccessor(isolate, tpl, "suspensionReboundDamp", &SuspensionReboundDampGetter, &SuspensionReboundDampSetter); + V8::SetAccessor(isolate, tpl, "suspensionUpperLimit", &SuspensionUpperLimitGetter, &SuspensionUpperLimitSetter); + V8::SetAccessor(isolate, tpl, "suspensionLowerLimit", &SuspensionLowerLimitGetter, &SuspensionLowerLimitSetter); + V8::SetAccessor(isolate, tpl, "suspensionRaise", &SuspensionRaiseGetter, &SuspensionRaiseSetter); + V8::SetAccessor(isolate, tpl, "suspensionBiasFront", &SuspensionBiasFrontGetter, &SuspensionBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "suspensionBiasRear", &SuspensionBiasRearGetter, &SuspensionBiasRearSetter); + V8::SetAccessor(isolate, tpl, "antiRollBarForce", &AntiRollBarForceGetter, &AntiRollBarForceSetter); + V8::SetAccessor(isolate, tpl, "antiRollBarBiasFront", &AntiRollBarBiasFrontGetter, &AntiRollBarBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "antiRollBarBiasRear", &AntiRollBarBiasRearGetter, &AntiRollBarBiasRearSetter); + V8::SetAccessor(isolate, tpl, "rollCentreHeightFront", &RollCentreHeightFrontGetter, &RollCentreHeightFrontSetter); + V8::SetAccessor(isolate, tpl, "rollCentreHeightRear", &RollCentreHeightRearGetter, &RollCentreHeightRearSetter); + V8::SetAccessor(isolate, tpl, "collisionDamageMult", &CollisionDamageMultGetter, &CollisionDamageMultSetter); + V8::SetAccessor(isolate, tpl, "weaponDamageMult", &WeaponDamageMultGetter, &WeaponDamageMultSetter); + V8::SetAccessor(isolate, tpl, "deformationDamageMult", &DeformationDamageMultGetter, &DeformationDamageMultSetter); + V8::SetAccessor(isolate, tpl, "engineDamageMult", &EngineDamageMultGetter, &EngineDamageMultSetter); + V8::SetAccessor(isolate, tpl, "petrolTankVolume", &PetrolTankVolumeGetter, &PetrolTankVolumeSetter); + V8::SetAccessor(isolate, tpl, "oilVolume", &OilVolumeGetter, &OilVolumeSetter); + V8::SetAccessor(isolate, tpl, "unkFloat5", &unkFloat5Getter, &unkFloat5Setter); + V8::SetAccessor(isolate, tpl, "seatOffsetDistX", &SeatOffsetDistXGetter, &SeatOffsetDistXSetter); + V8::SetAccessor(isolate, tpl, "seatOffsetDistY", &SeatOffsetDistYGetter, &SeatOffsetDistYSetter); + V8::SetAccessor(isolate, tpl, "seatOffsetDistZ", &SeatOffsetDistZGetter, &SeatOffsetDistZSetter); + V8::SetAccessor(isolate, tpl, "monetaryValue", &MonetaryValueGetter, &MonetaryValueSetter); + V8::SetAccessor(isolate, tpl, "modelFlags", &ModelFlagsGetter, &ModelFlagsSetter); + V8::SetAccessor(isolate, tpl, "handlingFlags", &HandlingFlagsGetter, &HandlingFlagsSetter); + V8::SetAccessor(isolate, tpl, "damageFlags", &DamageFlagsGetter, &DamageFlagsSetter); +}); \ No newline at end of file diff --git a/client/src/bindings/HandlingData.cpp b/client/src/bindings/HandlingData.cpp new file mode 100644 index 00000000..1ca0d6f8 --- /dev/null +++ b/client/src/bindings/HandlingData.cpp @@ -0,0 +1,1854 @@ + +#include "../CV8Resource.h" +#include "V8Class.h" + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_CONSTRUCTOR(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "model doesn't exist"); + + info.This()->SetInternalField(0, info[0]); +} + +static void GetForHandlingName(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, modelHash); + + std::vector> args{ + v8::Number::New(isolate, modelHash) + }; + + extern V8Class v8HandlingData; + V8_RETURN(v8HandlingData.New(isolate->GetEnteredOrMicrotaskContext(), args)); +} + +static void GetForHandlingNameDeprecated(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, modelHash); + + std::vector> args{ + v8::Number::New(isolate, modelHash) + }; + + extern V8Class v8HandlingData; + V8_RETURN(v8HandlingData.New(isolate->GetEnteredOrMicrotaskContext(), args)); + + Log::Warning << "alt.HandlingData.getForModelName is deprecated and will be removed in the future. Please use alt.HandlingData.getForHandlingName" << Log::Endl; +} + +static void HandlingNameHashGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + uint32_t modelHash2 = info.This()->GetInternalField(0)->IntegerValue(ctx).ToChecked(); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetHandlingNameHash()); +} + +static void MassGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetMass()); +} + +static void MassSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetMass(fvalue); +} + +static void InitialDragCoeffGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetInitialDragCoeff()); +} + +static void InitialDragCoeffSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetInitialDragCoeff(fvalue); +} + +static void DownforceModifierGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetDownforceModifier()); +} + +static void DownforceModifierSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetDownforceModifier(fvalue); +} + +static void unkFloat1Getter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetunkFloat1()); +} + +static void unkFloat1Setter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetunkFloat1(fvalue); +} + +static void unkFloat2Getter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetunkFloat2()); +} + +static void unkFloat2Setter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetunkFloat2(fvalue); +} + +static void CentreOfMassOffsetGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN(resource->CreateVector3(handling->GetCentreOfMassOffset())); +} + +static void CentreOfMassOffsetSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_OBJECT(val, pos); + V8_OBJECT_GET_NUMBER(pos, "x", x); + V8_OBJECT_GET_NUMBER(pos, "y", y); + V8_OBJECT_GET_NUMBER(pos, "z", z); + + handling->SetCentreOfMassOffset({ x, y, z }); +} + +static void InertiaMultiplierGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN(resource->CreateVector3(handling->GetInertiaMultiplier())); +} + +static void InertiaMultiplierSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_OBJECT(val, pos); + V8_OBJECT_GET_NUMBER(pos, "x", x); + V8_OBJECT_GET_NUMBER(pos, "y", y); + V8_OBJECT_GET_NUMBER(pos, "z", z); + + handling->SetInertiaMultiplier({ x, y, z }); +} + +static void PercentSubmergedGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetPercentSubmerged()); +} + +static void PercentSubmergedSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetPercentSubmerged(fvalue); +} + +static void PercentSubmergedRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetPercentSubmergedRatio()); +} + +static void PercentSubmergedRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetPercentSubmergedRatio(fvalue); +} + +static void DriveBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetDriveBiasFront()); +} + +static void DriveBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetDriveBiasFront(fvalue); +} + +static void AccelerationGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetAcceleration()); +} + +static void AccelerationSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetAcceleration(fvalue); +} + +static void InitialDriveGearsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetInitialDriveGears()); +} + +static void InitialDriveGearsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + handling->SetInitialDriveGears(val->ToUint32(isolate->GetEnteredOrMicrotaskContext()).ToLocalChecked()->Value()); +} + +static void DriveInertiaGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetDriveInertia()); +} + +static void DriveInertiaSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetDriveInertia(fvalue); +} + +static void ClutchChangeRateScaleUpShiftGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetClutchChangeRateScaleUpShift()); +} + +static void ClutchChangeRateScaleUpShiftSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetClutchChangeRateScaleUpShift(fvalue); +} + +static void ClutchChangeRateScaleDownShiftGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetClutchChangeRateScaleDownShift()); +} + +static void ClutchChangeRateScaleDownShiftSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetClutchChangeRateScaleDownShift(fvalue); +} + +static void InitialDriveForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetInitialDriveForce()); +} + +static void InitialDriveForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetInitialDriveForce(fvalue); +} + +static void DriveMaxFlatVelGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetDriveMaxFlatVel()); +} + +static void DriveMaxFlatVelSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetDriveMaxFlatVel(fvalue); +} + +static void InitialDriveMaxFlatVelGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetInitialDriveMaxFlatVel()); +} + +static void InitialDriveMaxFlatVelSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetInitialDriveMaxFlatVel(fvalue); +} + +static void BrakeForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetBrakeForce()); +} + +static void BrakeForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetBrakeForce(fvalue); +} + +static void BrakeForceGetterDeprecated(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetBrakeForce()); + Log::Warning << "HandlingData.breakForce is deprecated and will be removed in the future. Please use HandlingData.brakeForce instead." << Log::Endl; +} + +static void BrakeForceSetterDeprecated(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetBrakeForce(fvalue); + Log::Warning << "HandlingData.breakForce is deprecated and will be removed in the future. Please use HandlingData.brakeForce instead." << Log::Endl; +} + +static void unkFloat4Getter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetunkFloat4()); +} + +static void unkFloat4Setter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetunkFloat4(fvalue); +} + +static void BrakeBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetBrakeBiasFront()); +} + +static void BrakeBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetBrakeBiasFront(fvalue); +} + +static void BrakeBiasRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetBrakeBiasRear()); +} + +static void BrakeBiasRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetBrakeBiasRear(fvalue); +} + +static void HandBrakeForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetHandBrakeForce()); +} + +static void HandBrakeForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetHandBrakeForce(fvalue); +} + +static void SteeringLockGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSteeringLock()); +} + +static void SteeringLockSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSteeringLock(fvalue); +} + +static void SteeringLockRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSteeringLockRatio()); +} + +static void SteeringLockRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSteeringLockRatio(fvalue); +} + +static void TractionCurveMaxGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionCurveMax()); +} + +static void TractionCurveMaxSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionCurveMax(fvalue); +} + +static void TractionCurveMaxRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionCurveMaxRatio()); +} + +static void TractionCurveMaxRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionCurveMaxRatio(fvalue); +} + +static void TractionCurveMinGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionCurveMin()); +} + +static void TractionCurveMinSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionCurveMin(fvalue); +} + +static void TractionCurveMinRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionCurveMinRatio()); +} + +static void TractionCurveMinRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionCurveMinRatio(fvalue); +} + +static void TractionCurveLateralGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionCurveLateral()); +} + +static void TractionCurveLateralSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionCurveLateral(fvalue); +} + +static void TractionCurveLateralRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionCurveLateralRatio()); +} + +static void TractionCurveLateralRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionCurveLateralRatio(fvalue); +} + +static void TractionSpringDeltaMaxGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionSpringDeltaMax()); +} + +static void TractionSpringDeltaMaxSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionSpringDeltaMax(fvalue); +} + +static void TractionSpringDeltaMaxRatioGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionSpringDeltaMaxRatio()); +} + +static void TractionSpringDeltaMaxRatioSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionSpringDeltaMaxRatio(fvalue); +} + +static void LowSpeedTractionLossMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetLowSpeedTractionLossMult()); +} + +static void LowSpeedTractionLossMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetLowSpeedTractionLossMult(fvalue); +} + +static void CamberStiffnessGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetCamberStiffness()); +} + +static void CamberStiffnessSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetCamberStiffness(fvalue); +} + +static void TractionBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionBiasFront()); +} + +static void TractionBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionBiasFront(fvalue); +} + +static void TractionBiasRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionBiasRear()); +} + +static void TractionBiasRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionBiasRear(fvalue); +} + +static void TractionLossMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetTractionLossMult()); +} + +static void TractionLossMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetTractionLossMult(fvalue); +} + +static void SuspensionForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSuspensionForce()); +} + +static void SuspensionForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSuspensionForce(fvalue); +} + +static void SuspensionCompDampGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSuspensionCompDamp()); +} + +static void SuspensionCompDampSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSuspensionCompDamp(fvalue); +} + +static void SuspensionReboundDampGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSuspensionReboundDamp()); +} + +static void SuspensionReboundDampSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSuspensionReboundDamp(fvalue); +} + +static void SuspensionUpperLimitGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSuspensionUpperLimit()); +} + +static void SuspensionUpperLimitSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSuspensionUpperLimit(fvalue); +} + +static void SuspensionLowerLimitGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSuspensionLowerLimit()); +} + +static void SuspensionLowerLimitSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSuspensionLowerLimit(fvalue); +} + +static void SuspensionRaiseGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSuspensionRaise()); +} + +static void SuspensionRaiseSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSuspensionRaise(fvalue); +} + +static void SuspensionBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSuspensionBiasFront()); +} + +static void SuspensionBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSuspensionBiasFront(fvalue); +} + +static void SuspensionBiasRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSuspensionBiasRear()); +} + +static void SuspensionBiasRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSuspensionBiasRear(fvalue); +} + +static void AntiRollBarForceGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetAntiRollBarForce()); +} + +static void AntiRollBarForceSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetAntiRollBarForce(fvalue); +} + +static void AntiRollBarBiasFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetAntiRollBarBiasFront()); +} + +static void AntiRollBarBiasFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetAntiRollBarBiasFront(fvalue); +} + +static void AntiRollBarBiasRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetAntiRollBarBiasRear()); +} + +static void AntiRollBarBiasRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetAntiRollBarBiasRear(fvalue); +} + +static void RollCentreHeightFrontGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetRollCentreHeightFront()); +} + +static void RollCentreHeightFrontSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetRollCentreHeightFront(fvalue); +} + +static void RollCentreHeightRearGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetRollCentreHeightRear()); +} + +static void RollCentreHeightRearSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetRollCentreHeightRear(fvalue); +} + +static void CollisionDamageMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetCollisionDamageMult()); +} + +static void CollisionDamageMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetCollisionDamageMult(fvalue); +} + +static void WeaponDamageMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetWeaponDamageMult()); +} + +static void WeaponDamageMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetWeaponDamageMult(fvalue); +} + +static void DeformationDamageMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetDeformationDamageMult()); +} + +static void DeformationDamageMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetDeformationDamageMult(fvalue); +} + +static void EngineDamageMultGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetEngineDamageMult()); +} + +static void EngineDamageMultSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetEngineDamageMult(fvalue); +} + +static void PetrolTankVolumeGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetPetrolTankVolume()); +} + +static void PetrolTankVolumeSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetPetrolTankVolume(fvalue); +} + +static void OilVolumeGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetOilVolume()); +} + +static void OilVolumeSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetOilVolume(fvalue); +} + +static void unkFloat5Getter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetunkFloat5()); +} + +static void unkFloat5Setter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetunkFloat5(fvalue); +} + +static void SeatOffsetDistXGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSeatOffsetDistX()); +} + +static void SeatOffsetDistXSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSeatOffsetDistX(fvalue); +} + +static void SeatOffsetDistYGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSeatOffsetDistY()); +} + +static void SeatOffsetDistYSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSeatOffsetDistY(fvalue); +} + +static void SeatOffsetDistZGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetSeatOffsetDistZ()); +} + +static void SeatOffsetDistZSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_TO_NUMBER(val, fvalue); + + handling->SetSeatOffsetDistZ(fvalue); +} + +static void MonetaryValueGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetMonetaryValue()); +} + +static void MonetaryValueSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + handling->SetMonetaryValue(val->ToUint32(isolate->GetEnteredOrMicrotaskContext()).ToLocalChecked()->Value()); +} + +static void ModelFlagsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetModelFlags()); +} + +static void ModelFlagsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + handling->SetModelFlags(val->ToUint32(isolate->GetEnteredOrMicrotaskContext()).ToLocalChecked()->Value()); +} + +static void HandlingFlagsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetHandlingFlags()); +} + +static void HandlingFlagsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + handling->SetHandlingFlags(val->ToUint32(isolate->GetEnteredOrMicrotaskContext()).ToLocalChecked()->Value()); +} + +static void DamageFlagsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + V8_RETURN_NUMBER(handling->GetDamageFlags()); +} + +static void DamageFlagsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_INTEGER(1, modelHash); + + auto handling = alt::ICore::Instance().GetHandlingData(modelHash); + V8_CHECK(handling, "handling data for vehicle not found"); + + handling->SetDamageFlags(val->ToUint32(isolate->GetEnteredOrMicrotaskContext()).ToLocalChecked()->Value()); +} + +extern V8Class v8HandlingData("HandlingData", Constructor, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + V8::SetStaticMethod(isolate, tpl, "getForHandlingName", &GetForHandlingName); + V8::SetStaticMethod(isolate, tpl, "getForModelName", &GetForHandlingNameDeprecated); + + V8::SetAccessor(isolate, tpl, "handlingNameHash", &HandlingNameHashGetter); + V8::SetAccessor(isolate, tpl, "mass", &MassGetter, &MassSetter); + V8::SetAccessor(isolate, tpl, "initialDragCoeff", &InitialDragCoeffGetter, &InitialDragCoeffSetter); + V8::SetAccessor(isolate, tpl, "downforceModifier", &DownforceModifierGetter, &DownforceModifierSetter); + V8::SetAccessor(isolate, tpl, "unkFloat1", &unkFloat1Getter, &unkFloat1Setter); + V8::SetAccessor(isolate, tpl, "unkFloat2", &unkFloat2Getter, &unkFloat2Setter); + V8::SetAccessor(isolate, tpl, "centreOfMassOffset", &CentreOfMassOffsetGetter, &CentreOfMassOffsetSetter); + V8::SetAccessor(isolate, tpl, "inertiaMultiplier", &InertiaMultiplierGetter, &InertiaMultiplierSetter); + V8::SetAccessor(isolate, tpl, "percentSubmerged", &PercentSubmergedGetter, &PercentSubmergedSetter); + V8::SetAccessor(isolate, tpl, "percentSubmergedRatio", &PercentSubmergedRatioGetter, &PercentSubmergedRatioSetter); + V8::SetAccessor(isolate, tpl, "driveBiasFront", &DriveBiasFrontGetter, &DriveBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "acceleration", &AccelerationGetter, &AccelerationSetter); + V8::SetAccessor(isolate, tpl, "initialDriveGears", &InitialDriveGearsGetter, &InitialDriveGearsSetter); + V8::SetAccessor(isolate, tpl, "driveInertia", &DriveInertiaGetter, &DriveInertiaSetter); + V8::SetAccessor(isolate, tpl, "clutchChangeRateScaleUpShift", &ClutchChangeRateScaleUpShiftGetter, &ClutchChangeRateScaleUpShiftSetter); + V8::SetAccessor(isolate, tpl, "clutchChangeRateScaleDownShift", &ClutchChangeRateScaleDownShiftGetter, &ClutchChangeRateScaleDownShiftSetter); + V8::SetAccessor(isolate, tpl, "initialDriveForce", &InitialDriveForceGetter, &InitialDriveForceSetter); + V8::SetAccessor(isolate, tpl, "driveMaxFlatVel", &DriveMaxFlatVelGetter, &DriveMaxFlatVelSetter); + V8::SetAccessor(isolate, tpl, "initialDriveMaxFlatVel", &InitialDriveMaxFlatVelGetter, &InitialDriveMaxFlatVelSetter); + V8::SetAccessor(isolate, tpl, "brakeForce", &BrakeForceGetter, &BrakeForceSetter); + V8::SetAccessor(isolate, tpl, "breakForce", &BrakeForceGetterDeprecated, &BrakeForceSetterDeprecated); + V8::SetAccessor(isolate, tpl, "unkFloat4", &unkFloat4Getter, &unkFloat4Setter); + V8::SetAccessor(isolate, tpl, "brakeBiasFront", &BrakeBiasFrontGetter, &BrakeBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "brakeBiasRear", &BrakeBiasRearGetter, &BrakeBiasRearSetter); + V8::SetAccessor(isolate, tpl, "handBrakeForce", &HandBrakeForceGetter, &HandBrakeForceSetter); + V8::SetAccessor(isolate, tpl, "steeringLock", &SteeringLockGetter, &SteeringLockSetter); + V8::SetAccessor(isolate, tpl, "steeringLockRatio", &SteeringLockRatioGetter, &SteeringLockRatioSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveMax", &TractionCurveMaxGetter, &TractionCurveMaxSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveMaxRatio", &TractionCurveMaxRatioGetter, &TractionCurveMaxRatioSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveMin", &TractionCurveMinGetter, &TractionCurveMinSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveMinRatio", &TractionCurveMinRatioGetter, &TractionCurveMinRatioSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveLateral", &TractionCurveLateralGetter, &TractionCurveLateralSetter); + V8::SetAccessor(isolate, tpl, "tractionCurveLateralRatio", &TractionCurveLateralRatioGetter, &TractionCurveLateralRatioSetter); + V8::SetAccessor(isolate, tpl, "tractionSpringDeltaMax", &TractionSpringDeltaMaxGetter, &TractionSpringDeltaMaxSetter); + V8::SetAccessor(isolate, tpl, "tractionSpringDeltaMaxRatio", &TractionSpringDeltaMaxRatioGetter, &TractionSpringDeltaMaxRatioSetter); + V8::SetAccessor(isolate, tpl, "lowSpeedTractionLossMult", &LowSpeedTractionLossMultGetter, &LowSpeedTractionLossMultSetter); + V8::SetAccessor(isolate, tpl, "camberStiffness", &CamberStiffnessGetter, &CamberStiffnessSetter); + V8::SetAccessor(isolate, tpl, "tractionBiasFront", &TractionBiasFrontGetter, &TractionBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "tractionBiasRear", &TractionBiasRearGetter, &TractionBiasRearSetter); + V8::SetAccessor(isolate, tpl, "tractionLossMult", &TractionLossMultGetter, &TractionLossMultSetter); + V8::SetAccessor(isolate, tpl, "suspensionForce", &SuspensionForceGetter, &SuspensionForceSetter); + V8::SetAccessor(isolate, tpl, "suspensionCompDamp", &SuspensionCompDampGetter, &SuspensionCompDampSetter); + V8::SetAccessor(isolate, tpl, "suspensionReboundDamp", &SuspensionReboundDampGetter, &SuspensionReboundDampSetter); + V8::SetAccessor(isolate, tpl, "suspensionUpperLimit", &SuspensionUpperLimitGetter, &SuspensionUpperLimitSetter); + V8::SetAccessor(isolate, tpl, "suspensionLowerLimit", &SuspensionLowerLimitGetter, &SuspensionLowerLimitSetter); + V8::SetAccessor(isolate, tpl, "suspensionRaise", &SuspensionRaiseGetter, &SuspensionRaiseSetter); + V8::SetAccessor(isolate, tpl, "suspensionBiasFront", &SuspensionBiasFrontGetter, &SuspensionBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "suspensionBiasRear", &SuspensionBiasRearGetter, &SuspensionBiasRearSetter); + V8::SetAccessor(isolate, tpl, "antiRollBarForce", &AntiRollBarForceGetter, &AntiRollBarForceSetter); + V8::SetAccessor(isolate, tpl, "antiRollBarBiasFront", &AntiRollBarBiasFrontGetter, &AntiRollBarBiasFrontSetter); + V8::SetAccessor(isolate, tpl, "antiRollBarBiasRear", &AntiRollBarBiasRearGetter, &AntiRollBarBiasRearSetter); + V8::SetAccessor(isolate, tpl, "rollCentreHeightFront", &RollCentreHeightFrontGetter, &RollCentreHeightFrontSetter); + V8::SetAccessor(isolate, tpl, "rollCentreHeightRear", &RollCentreHeightRearGetter, &RollCentreHeightRearSetter); + V8::SetAccessor(isolate, tpl, "collisionDamageMult", &CollisionDamageMultGetter, &CollisionDamageMultSetter); + V8::SetAccessor(isolate, tpl, "weaponDamageMult", &WeaponDamageMultGetter, &WeaponDamageMultSetter); + V8::SetAccessor(isolate, tpl, "deformationDamageMult", &DeformationDamageMultGetter, &DeformationDamageMultSetter); + V8::SetAccessor(isolate, tpl, "engineDamageMult", &EngineDamageMultGetter, &EngineDamageMultSetter); + V8::SetAccessor(isolate, tpl, "petrolTankVolume", &PetrolTankVolumeGetter, &PetrolTankVolumeSetter); + V8::SetAccessor(isolate, tpl, "oilVolume", &OilVolumeGetter, &OilVolumeSetter); + V8::SetAccessor(isolate, tpl, "unkFloat5", &unkFloat5Getter, &unkFloat5Setter); + V8::SetAccessor(isolate, tpl, "seatOffsetDistX", &SeatOffsetDistXGetter, &SeatOffsetDistXSetter); + V8::SetAccessor(isolate, tpl, "seatOffsetDistY", &SeatOffsetDistYGetter, &SeatOffsetDistYSetter); + V8::SetAccessor(isolate, tpl, "seatOffsetDistZ", &SeatOffsetDistZGetter, &SeatOffsetDistZSetter); + V8::SetAccessor(isolate, tpl, "monetaryValue", &MonetaryValueGetter, &MonetaryValueSetter); + V8::SetAccessor(isolate, tpl, "modelFlags", &ModelFlagsGetter, &ModelFlagsSetter); + V8::SetAccessor(isolate, tpl, "handlingFlags", &HandlingFlagsGetter, &HandlingFlagsSetter); + V8::SetAccessor(isolate, tpl, "damageFlags", &DamageFlagsGetter, &DamageFlagsSetter); + }); diff --git a/client/src/bindings/HttpClient.cpp b/client/src/bindings/HttpClient.cpp new file mode 100644 index 00000000..f2e6e915 --- /dev/null +++ b/client/src/bindings/HttpClient.cpp @@ -0,0 +1,445 @@ +#include "V8Helpers.h" +#include "V8ResourceImpl.h" +#include "V8Class.h" +#include "../CV8ScriptRuntime.h" + +static void SetExtraHeader(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, name); + V8_ARG_TO_STRING(2, value); + + client->SetExtraHeader(name, value); +} + +static void GetExtraHeaders(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + + auto dict = client->GetExtraHeaders(); + V8_NEW_OBJECT(headers); + for(auto it = dict->Begin(); it; it = dict->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + + V8_RETURN(headers); +} + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + + auto client = alt::ICore::Instance().CreateHttpClient(resource->GetResource()); + V8_BIND_BASE_OBJECT(client, "Failed to create HttpClient"); +} + +static std::list> requestPromises; + +static void Get(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_STRING(1, url); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Get(callback, url, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void Head(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_STRING(1, url); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Head(callback, url, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void Post(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, url); + V8_ARG_TO_STRING(2, body); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Post(callback, url, body, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void Put(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, url); + V8_ARG_TO_STRING(2, body); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Put(callback, url, body, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void Delete(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, url); + V8_ARG_TO_STRING(2, body); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Delete(callback, url, body, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void Connect(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, url); + V8_ARG_TO_STRING(2, body); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Connect(callback, url, body, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void Options(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, url); + V8_ARG_TO_STRING(2, body); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Options(callback, url, body, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void Trace(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, url); + V8_ARG_TO_STRING(2, body); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Trace(callback, url, body, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void Patch(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(client, alt::IHttpClient); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, url); + V8_ARG_TO_STRING(2, body); + + auto& persistent = requestPromises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + auto callback = [](alt::IHttpClient::HttpResponse response, const void* userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate* isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent*)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + V8_NEW_OBJECT(responseObj); + V8_OBJECT_SET_INT(responseObj, "statusCode", response.statusCode); + V8_OBJECT_SET_STRING(responseObj, "body", response.body); + V8_NEW_OBJECT(headers); + for(auto it = response.headers->Begin(); it; it = response.headers->Next()) + { + headers->Set(ctx, V8_NEW_STRING(it->GetKey().CStr()), V8_NEW_STRING(it->GetValue().As()->Value().CStr())); + } + responseObj->Set(ctx, V8_NEW_STRING("headers"), headers); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), responseObj); + } + + requestPromises.remove(*persistent); + }; + + client->Patch(callback, url, body, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +extern V8Class v8BaseObject; +extern V8Class v8HttpClient("HttpClient", v8BaseObject, &Constructor, [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetMethod(isolate, tpl, "setExtraHeader", &SetExtraHeader); + V8::SetMethod(isolate, tpl, "getExtraHeaders", &GetExtraHeaders); + + V8::SetMethod(isolate, tpl, "get", Get); + V8::SetMethod(isolate, tpl, "head", Head); + V8::SetMethod(isolate, tpl, "post", Post); + V8::SetMethod(isolate, tpl, "put", Put); + V8::SetMethod(isolate, tpl, "delete", Delete); + V8::SetMethod(isolate, tpl, "connect", Connect); + V8::SetMethod(isolate, tpl, "options", Options); + V8::SetMethod(isolate, tpl, "trace", Trace); + V8::SetMethod(isolate, tpl, "patch", Patch); +}); diff --git a/client/src/bindings/LocalPlayer.cpp b/client/src/bindings/LocalPlayer.cpp new file mode 100644 index 00000000..82355b0a --- /dev/null +++ b/client/src/bindings/LocalPlayer.cpp @@ -0,0 +1,14 @@ +#include "V8Helpers.h" +#include "V8Class.h" +#include "V8Entity.h" +#include "V8ResourceImpl.h" +#include "V8BindHelpers.h" + +#include "cpp-sdk/objects/ILocalPlayer.h" + +extern V8Class v8Player; +extern V8Class v8LocalPlayer("LocalPlayer", v8Player, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + V8::SetAccessor(isolate, tpl, "currentAmmo"); +}); diff --git a/client/src/bindings/LocalStorage.cpp b/client/src/bindings/LocalStorage.cpp new file mode 100644 index 00000000..6c658455 --- /dev/null +++ b/client/src/bindings/LocalStorage.cpp @@ -0,0 +1,135 @@ + +#include "V8Class.h" +#include "V8Helpers.h" +#include "V8ResourceImpl.h" +#include "../CV8Resource.h" +#include "cpp-sdk/SDK.h" + +static void StaticGet(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN2(0, 1); + + if(info.Length() == 0) + { + V8_RETURN(static_cast(resource)->GetLocalStorage()); + + Log::Warning << "Using the local storage instance methods is deprecated. Use the static methods instead." << Log::Endl; + } + else + { + alt::IResource *iresource = V8ResourceImpl::GetResource(isolate->GetEnteredOrMicrotaskContext()); + V8_ARG_TO_STRING(1, key); + V8_RETURN(V8Helpers::MValueToV8(iresource->GetLocalStorage()->Get(key))); + } +} + +static void StaticSet(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, key); + V8_ARG_TO_MVALUE(2, val); + + resource->GetLocalStorage()->Set(key, val); +} + +static void StaticDelete(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + resource->GetLocalStorage()->Delete(key); +} + +static void StaticClear(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + resource->GetLocalStorage()->Clear(); +} + +static void StaticSave(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK(resource->GetLocalStorage()->Save(), "exceeded max local storage size (4MB)"); +} + +static void Get(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_RETURN(V8Helpers::MValueToV8(resource->GetLocalStorage()->Get(key))); + + Log::Warning << "Using the local storage instance methods is deprecated. Use the static methods instead." << Log::Endl; +} + +static void Set(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, key); + V8_ARG_TO_MVALUE(2, val); + + resource->GetLocalStorage()->Set(key, val); + + Log::Warning << "Using the local storage instance methods is deprecated. Use the static methods instead." << Log::Endl; +} + +static void Delete(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + resource->GetLocalStorage()->Delete(key); + + Log::Warning << "Using the local storage instance methods is deprecated. Use the static methods instead." << Log::Endl; +} + +static void Clear(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + resource->GetLocalStorage()->Clear(); + + Log::Warning << "Using the local storage instance methods is deprecated. Use the static methods instead." << Log::Endl; +} + +static void Save(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK(resource->GetLocalStorage()->Save(), "exceeded max local storage size (4MB)"); + + Log::Warning << "Using the local storage instance methods is deprecated. Use the static methods instead." << Log::Endl; +} + +extern V8Class v8LocalStorage("LocalStorage", nullptr, [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetStaticMethod(isolate, tpl, "get", &StaticGet); + V8::SetStaticMethod(isolate, tpl, "set", &StaticSet); + V8::SetStaticMethod(isolate, tpl, "delete", &StaticDelete); + V8::SetStaticMethod(isolate, tpl, "deleteAll", &StaticClear); + V8::SetStaticMethod(isolate, tpl, "clear", &StaticClear); + V8::SetStaticMethod(isolate, tpl, "save", &StaticSave); + + V8::SetMethod(isolate, tpl, "get", &Get); + V8::SetMethod(isolate, tpl, "set", &Set); + V8::SetMethod(isolate, tpl, "delete", &Delete); + V8::SetMethod(isolate, tpl, "deleteAll", &Clear); + V8::SetMethod(isolate, tpl, "clear", &Clear); + V8::SetMethod(isolate, tpl, "save", &Save); +}); diff --git a/client/src/bindings/Main.cpp b/client/src/bindings/Main.cpp new file mode 100644 index 00000000..7e496165 --- /dev/null +++ b/client/src/bindings/Main.cpp @@ -0,0 +1,986 @@ + +#include "../CV8ScriptRuntime.h" +#include "cpp-sdk/objects/IPlayer.h" +#include "V8Module.h" + +#include "cpp-sdk/SDK.h" + +#include "Log.h" + +using namespace alt; + +static void OnServer(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN2(1, 2); + + if(info.Length() == 1) + { + V8_ARG_TO_FUNCTION(1, callback); + + resource->SubscribeGenericRemote(callback, V8::SourceLocation::GetCurrent(isolate)); + } + else if(info.Length() == 2) + { + V8_ARG_TO_STRING(1, eventName); + V8_ARG_TO_FUNCTION(2, callback); + + resource->SubscribeRemote(eventName.ToString(), callback, V8::SourceLocation::GetCurrent(isolate)); + } +} + +static void OnceServer(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN2(1, 2); + + if(info.Length() == 1) + { + V8_ARG_TO_FUNCTION(1, callback); + + resource->SubscribeGenericRemote(callback, V8::SourceLocation::GetCurrent(isolate), true); + } + else if(info.Length() == 2) + { + V8_ARG_TO_STRING(1, eventName); + V8_ARG_TO_FUNCTION(2, callback); + + resource->SubscribeRemote(eventName.ToString(), callback, V8::SourceLocation::GetCurrent(isolate), true); + } +} + +static void OffServer(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN2(1, 2); + + if(info.Length() == 1) + { + V8_ARG_TO_FUNCTION(1, callback); + + resource->UnsubscribeGenericRemote(callback); + } + else if(info.Length() == 2) + { + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, callback); + + resource->UnsubscribeRemote(evName.ToString(), callback); + } +} + +static void EmitServer(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, eventName); + + alt::MValueArgs args; + + for (int i = 1; i < info.Length(); ++i) + args.Push(V8Helpers::V8ToMValue(info[i])); + + alt::ICore::Instance().TriggerServerEvent(eventName.ToString(), args); +} + +static void GameControlsEnabled(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE(); + V8_RETURN_BOOLEAN(alt::ICore::Instance().AreControlsEnabled()); +} + +static void ToggleGameControls(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_BOOLEAN(1, state); + + resource->ToggleGameControls(state); +} + +static void ToggleVoiceControls(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_BOOLEAN(1, state); + + // TODO: make it resource-bound + //jsResource->ToggleVoiceControls(state); + alt::ICore::Instance().ToggleVoiceControls(state); +} + +static void ShowCursor(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_BOOLEAN(1, state); + + if (!resource->ToggleCursor(state)) + { + if (alt::ICore::Instance().IsDebug()) + { + V8Helpers::Throw(isolate, "Cursor state can't go < 0"); + } + else + { + Log::Warning << "Cursor state can't go < 0"; + } + } +} + +static void GetCursorPos(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + alt::Vector2i cursorPos = alt::ICore::Instance().GetCursorPosition(); + + V8_RETURN(resource->CreateVector2(cursorPos)); +} + +static void SetCursorPos(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_OBJECT(1, pos); + V8_OBJECT_GET_INT(pos, "x", x); + V8_OBJECT_GET_INT(pos, "y", y); + + ICore::Instance().SetCursorPosition({ x, y }); +} + +static void IsTextureExistInArchetype(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_INT(1, modelHash); + V8_ARG_TO_STRING(2, modelName); + + V8_RETURN_BOOLEAN(nullptr != ICore::Instance().GetTextureFromDrawable(modelHash, modelName)); +} + +static void RequestIPL(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, iplName); + + ICore::Instance().RequestIPL(iplName); +} + +static void RemoveIPL(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, iplName); + + ICore::Instance().RemoveIPL(iplName); +} + +static void GetLicenseHash(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_RETURN_STRING(ICore::Instance().GetLicenseHash().CStr()); +} + +static void SetCamFrozen(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_BOOLEAN(1, state); + + ICore::Instance().SetCamFrozen(state); +} + +static void IsInDebug(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_RETURN_BOOLEAN(alt::ICore::Instance().IsDebug()); +} + +static void IsVoiceActivityInputEnabled(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + Log::Warning << "alt.isVoiceActivityInputEnabled is deprecated and will be removed in the future. Please use alt.Voice.activityInputEnabled" << Log::Endl; + + V8_RETURN_BOOLEAN(ICore::Instance().IsVoiceActivationEnabled()); +} + +static void AddGxtText(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + V8_CHECK_ARGS_LEN(2); + + uint32_t gxtHash; + if(info[0]->IsString()) + { + V8_ARG_TO_STRING(1, key); + gxtHash = alt::ICore::Instance().Hash(key); + } + else + { + V8_ARG_TO_UINT32(1, hash); + gxtHash = hash; + } + + V8_ARG_TO_STRING(2, textValue); + + resource->AddGxtText(gxtHash, textValue.ToString()); +} + +static void RemoveGxtText(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + resource->RemoveGxtText(ICore::Instance().Hash(key)); +} + +static void GetGxtText(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_RETURN_STRING(resource->GetGxtText(ICore::Instance().Hash(key)).c_str()); +} + +static void GetMsPerGameMinute(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE(); + + V8_RETURN_INT(ICore::Instance().GetMsPerGameMinute()); +} + +static void SetMsPerGameMinute(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, ms); + + ICore::Instance().SetMsPerGameMinute(ms); +} + +static void BeginScaleformMovieMethodMinimap(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, methodName); + + V8_RETURN_BOOLEAN(ICore::Instance().BeginScaleformMovieMethodMinimap(methodName.CStr())); +} + +static void GetLocale(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_RETURN_STRING(ICore::Instance().GetLocale().CStr()); +} + +static void SetWeatherCycle(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_ARRAY(1, weathers); + V8_ARG_TO_ARRAY(2, multipliers); + + V8_CHECK(weathers->Length() < 256, "Weathers array size must be <= 255"); + V8_CHECK(multipliers->Length() < 256, "Multipliers array size must be <= 255"); + V8_CHECK(weathers->Length() == multipliers->Length(), "Weathers and multipliers array has to be the same size"); + + Array weathersVec; + Array multipliersVec; + + for (int i = 0; i < weathers->Length(); ++i) + { + V8_TO_INTEGER(weathers->Get(ctx, i).ToLocalChecked(), weatherNum); + V8_CHECK(weatherNum >= 0 && weatherNum <= 14, "weather ids must be >= 0 && <= 14"); + weathersVec.Push(weatherNum); + + V8_TO_INTEGER(multipliers->Get(ctx, i).ToLocalChecked(), multiplierNum); + V8_CHECK(multiplierNum > 0 && multiplierNum < 256, "multipliers must be > 0 && <= 255"); + multipliersVec.Push(multiplierNum); + } + + ICore::Instance().SetWeatherCycle(weathersVec, multipliersVec); +} + +static void SetWeatherSyncActive(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_BOOLEAN(1, isActive); + + ICore::Instance().SetWeatherSyncActive(isActive); +} + +static void SetCharStat(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, statName); + + IStatData *targetStat = alt::ICore::Instance().GetStatData(statName); + V8_CHECK(targetStat != nullptr, "stat with this name not found"); + + V8_CHECK(strcmp(targetStat->GetStatType(), "NONE") != 0 && strcmp(targetStat->GetStatType(), "PROFILE_SETTING") != 0, "target stat can't be edited"); + + if (!strcmp(targetStat->GetStatType(), "INT")) + { + V8_ARG_TO_INT(2, value); + targetStat->SetInt32Value(value); + V8_RETURN_BOOLEAN(true); + return; + } + else if (!strcmp(targetStat->GetStatType(), "INT64")) + { + V8_ARG_TO_INT64(2, value); + targetStat->SetInt64Value(value); + V8_RETURN_BOOLEAN(true); + return; + } + else if (!strcmp(targetStat->GetStatType(), "TEXTLABEL")) + { + V8_ARG_TO_INT32(2, value); + targetStat->SetInt32Value(value); + V8_RETURN_BOOLEAN(true); + return; + } + else if (!strcmp(targetStat->GetStatType(), "FLOAT")) + { + V8_ARG_TO_NUMBER(2, value); + targetStat->SetFloatValue(value); + V8_RETURN_BOOLEAN(true); + return; + } + else if (!strcmp(targetStat->GetStatType(), "BOOL")) + { + V8_ARG_TO_BOOLEAN(2, value); + targetStat->SetBoolValue(value); + V8_RETURN_BOOLEAN(true); + return; + } + else if (!strcmp(targetStat->GetStatType(), "STRING")) + { + V8_ARG_TO_STRING(2, value); + targetStat->SetStringValue(value.CStr()); + V8_RETURN_BOOLEAN(true); + return; + } + else if (!strcmp(targetStat->GetStatType(), "UINT8")) + { + V8_ARG_TO_UINT32(2, value); + targetStat->SetUInt8Value(value); + V8_RETURN_BOOLEAN(true); + return; + } + else if (!strcmp(targetStat->GetStatType(), "UINT16")) + { + V8_ARG_TO_UINT32(2, value); + targetStat->SetUInt16Value(value); + V8_RETURN_BOOLEAN(true); + return; + } + else if (!strcmp(targetStat->GetStatType(), "UINT32")) + { + V8_ARG_TO_UINT32(2, value); + targetStat->SetUInt32Value(value); + V8_RETURN_BOOLEAN(true); + return; + } + else if ( + !strcmp(targetStat->GetStatType(), "UINT64") || + !strcmp(targetStat->GetStatType(), "POS") || + !strcmp(targetStat->GetStatType(), "DATE") || + !strcmp(targetStat->GetStatType(), "PACKED") || + !strcmp(targetStat->GetStatType(), "USERID")) + { + V8_ARG_TO_UINT64(2, value); + targetStat->SetUInt64Value(value); + V8_RETURN_BOOLEAN(true); + return; + } + + V8_RETURN_BOOLEAN(false); +} + +static void GetCharStat(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, statName); + + IStatData *targetStat = alt::ICore::Instance().GetStatData(statName); + V8_CHECK(targetStat != nullptr, "stat with this name not found"); + + V8_CHECK(strcmp(targetStat->GetStatType(), "NONE") != 0 && strcmp(targetStat->GetStatType(), "PROFILE_SETTING") != 0, "target stat can't be readed"); + + if (!strcmp(targetStat->GetStatType(), "INT")) + { + V8_RETURN_INT(targetStat->GetInt32Value()); + return; + } + else if (!strcmp(targetStat->GetStatType(), "INT64")) + { + V8_RETURN_INT64(targetStat->GetInt64Value()); + return; + } + else if (!strcmp(targetStat->GetStatType(), "TEXTLABEL")) + { + V8_RETURN_INT(targetStat->GetInt32Value()); + return; + } + else if (!strcmp(targetStat->GetStatType(), "FLOAT")) + { + V8_RETURN_NUMBER(targetStat->GetFloatValue()); + return; + } + else if (!strcmp(targetStat->GetStatType(), "BOOL")) + { + V8_RETURN_BOOLEAN(targetStat->GetBoolValue()); + return; + } + else if (!strcmp(targetStat->GetStatType(), "STRING")) + { + V8_RETURN_STRING(targetStat->GetStringValue()); + return; + } + else if (!strcmp(targetStat->GetStatType(), "UINT8")) + { + V8_RETURN_UINT(targetStat->GetUInt8Value()); + return; + } + else if (!strcmp(targetStat->GetStatType(), "UINT16")) + { + V8_RETURN_UINT(targetStat->GetUInt16Value()); + return; + } + else if (!strcmp(targetStat->GetStatType(), "UINT32")) + { + V8_RETURN_UINT(targetStat->GetUInt32Value()); + return; + } + else if ( + !strcmp(targetStat->GetStatType(), "UINT64") || + !strcmp(targetStat->GetStatType(), "POS") || + !strcmp(targetStat->GetStatType(), "DATE") || + !strcmp(targetStat->GetStatType(), "PACKED") || + !strcmp(targetStat->GetStatType(), "USERID")) + { + V8_RETURN_UINT64(targetStat->GetUInt64Value()); + return; + } + + V8_RETURN_NULL(); +} + +static void ResetCharStat(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, statName); + + IStatData *targetStat = alt::ICore::Instance().GetStatData(statName); + V8_CHECK(targetStat != nullptr, "stat with this name not found"); + + V8_CHECK(strcmp(targetStat->GetStatType(), "NONE") != 0 && strcmp(targetStat->GetStatType(), "PROFILE_SETTING") != 0, "target stat can't be reseted"); + targetStat->Reset(); + V8_RETURN_BOOLEAN(true); +} + +static void IsMenuOpen(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE(); + V8_RETURN_BOOLEAN(ICore::Instance().IsMenuOpen()); +} + +static void IsConsoleOpen(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE(); + V8_RETURN_BOOLEAN(ICore::Instance().IsConsoleOpen()); +} + +static void IsKeyDown(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, keycode); + + V8_RETURN_BOOLEAN(alt::ICore::Instance().GetKeyState(keycode).IsDown()); +} + +static void IsKeyToggled(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, keycode); + + V8_RETURN_BOOLEAN(alt::ICore::Instance().GetKeyState(keycode).IsToggled()); +} + +static void SetConfigFlag(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_STRING(1, flag); + V8_ARG_TO_BOOLEAN(2, state); + + ICore::Instance().SetConfigFlag(flag, state); +} + +static void GetConfigFlag(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_STRING(1, flag); + + V8_RETURN_BOOLEAN(ICore::Instance().GetConfigFlag(flag)); +} + +static void DoesConfigFlagExist(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_STRING(1, flag); + + V8_RETURN_BOOLEAN(ICore::Instance().DoesConfigFlagExist(flag)); +} + +static void LoadYtyp(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_STRING(1, path); + + V8_RETURN_BOOLEAN(ICore::Instance().LoadYtyp(path.ToString())); +} + +static void UnloadYtyp(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_STRING(1, path); + + V8_RETURN_BOOLEAN(ICore::Instance().UnloadYtyp(path.ToString())); +} + +// extern V8Class v8MemoryBuffer; +// static void GetEntityMemoryByID(const v8::FunctionCallbackInfo &info) +// { +// V8_GET_ISOLATE_CONTEXT(); + +// #ifdef NDEBUG +// auto &game = CGame::Instance(); +// V8_CHECK(game.IsDebug() && IsDevOrInternalBranch(), "Must be in debug mode and dev branch"); +// #endif + +// V8_CHECK_ARGS_LEN(1); +// V8_ARG_TO_INT(1, id); + +// ::CEntity *addr = funcs::GetEntityFromScriptID<::CEntity *>(id); + +// auto buf = v8MemoryBuffer.CreateInstance(ctx); +// buf->SetAlignedPointerInInternalField(0, addr); +// buf->SetInternalField(1, v8::Integer::NewFromUnsigned(isolate, UINT32_MAX)); + +// V8_RETURN(buf); +// } + +static void SetAngularVelocity(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(4); + + V8_ARG_TO_INT(1, id); + V8_ARG_TO_NUMBER(2, x); + V8_ARG_TO_NUMBER(3, y); + V8_ARG_TO_NUMBER(4, z); + + alt::ICore::Instance().SetAngularVelocity(id, { x, y, z, 0.0 }); +} + +static void GetPermissionState(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, permnum); + + V8_RETURN_INT((uint8_t)alt::ICore::Instance().GetPermissionState((alt::Permission)permnum)); +} + +static void IsInStreamerMode(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE(); + + V8_RETURN_BOOLEAN(alt::ICore::Instance().IsInStreamerMode()); +} + +static void TakeScreenshot(const v8::FunctionCallbackInfo &info) +{ + static std::list> promises; + + V8_GET_ISOLATE_CONTEXT(); + + auto &api = alt::ICore::Instance(); + auto state = api.GetPermissionState(alt::Permission::ScreenCapture); + V8_CHECK(state != alt::PermissionState::Denied, "No permissions"); + V8_CHECK(state != alt::PermissionState::Unspecified, "Permissions not specified"); + + auto &persistent = promises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + + api.TakeScreenshot([](alt::StringView base64, const void *userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate *isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent *)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), v8::String::NewFromUtf8(isolate, base64.CStr()).ToLocalChecked()); + } + + promises.remove(*persistent); + }, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void TakeScreenshotGameOnly(const v8::FunctionCallbackInfo &info) +{ + static std::list> promises; + + V8_GET_ISOLATE_CONTEXT(); + + auto &api = alt::ICore::Instance(); + auto state = api.GetPermissionState(alt::Permission::ScreenCapture); + V8_CHECK(state != alt::PermissionState::Denied, "No permissions"); + V8_CHECK(state != alt::PermissionState::Unspecified, "Permissions not specified"); + + auto &persistent = promises.emplace_back(v8::UniquePersistent(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked())); + + api.TakeScreenshotGameOnly([](alt::StringView base64, const void *userData) { + // TODO: NOT PERFORMANCE EFFICIENT TO LOCK HERE, RESOLVE IN NEXT TICK INSTEAD + + v8::Isolate *isolate = CV8ScriptRuntime::Instance().GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + auto persistent = (v8::UniquePersistent *)userData; + auto resolver = persistent->Get(isolate); + auto ctx = resolver->GetCreationContext().ToLocalChecked(); + { + v8::Context::Scope ctxscope(ctx); + resolver->Resolve(resolver->GetCreationContext().ToLocalChecked(), v8::String::NewFromUtf8(isolate, base64.CStr()).ToLocalChecked()); + } + + promises.remove(*persistent); + }, &persistent); + + V8_RETURN(persistent.Get(isolate)->GetPromise()); +} + +static void IsGameFocused(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_RETURN_BOOLEAN(alt::ICore::Instance().IsGameFocused()); +} + +static void LoadModel(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_UINT32(1, hash); + + alt::ICore::Instance().LoadModel(hash); + + Log::Warning << "loadModel is deprecated and it will be removed in the future. Please use the native requestModel." << Log::Endl; +} + +static void LoadModelAsync(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_UINT32(1, hash); + + alt::ICore::Instance().LoadModelAsync(hash); + + Log::Warning << "loadModelAsync is deprecated and it will be removed in the future. Please use the native requestModel." << Log::Endl; +} + +static void EvalModule(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, code); + + auto maybeModule = static_cast(resource)->ResolveCode(code.ToString(), V8::SourceLocation::GetCurrent(isolate)); + if(maybeModule.IsEmpty()) + { + V8Helpers::Throw(isolate, "Failed to resolve module"); + return; + } + + auto module = maybeModule.ToLocalChecked(); + v8::Maybe result = module->InstantiateModule(ctx, CV8ScriptRuntime::ResolveModule); + if(result.IsNothing() || result.ToChecked() == false) + { + V8Helpers::Throw(isolate, "Failed to instantiate module"); + return; + } + + auto returnValue = module->Evaluate(ctx); + if(returnValue.IsEmpty()) + { + V8Helpers::Throw(isolate, "Failed to evaluate module"); + return; + } + + V8_RETURN(module->GetModuleNamespace()); +} + +static void GetHeadshotBase64(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_UINT32(1, id); + + V8_RETURN_STRING(alt::ICore::Instance().HeadshotToBase64(id).CStr()); +} + +static void SetPedDlcClothes(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN2(5, 6); + + V8_ARG_TO_INT(1, scriptId); + V8_ARG_TO_UINT32(2, dlc); + V8_ARG_TO_INT(3, component); + V8_ARG_TO_INT(4, drawable); + V8_ARG_TO_INT(5, texture); + + uint8_t palette; + if(info.Length() == 5) + { + palette = 2; + } + else if(info.Length() == 6) + { + V8_ARG_TO_INT(6, paletteArg); + palette = paletteArg; + } + + alt::ICore::Instance().SetDlcClothes(scriptId, component, drawable, texture, palette, dlc); +} + +static void SetPedDlcProps(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(5); + + V8_ARG_TO_INT(1, scriptId); + V8_ARG_TO_UINT32(2, dlc); + V8_ARG_TO_INT(3, component); + V8_ARG_TO_INT(4, drawable); + V8_ARG_TO_INT(5, texture); + + alt::ICore::Instance().SetDlcProps(scriptId, component, drawable, texture, dlc); +} + +static void ClearPedProps(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_INT(1, scriptId); + V8_ARG_TO_INT(2, component); + + alt::ICore::Instance().ClearProps(scriptId, component); +} + +static void SetWatermarkPosition(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_INT(1, pos); + + alt::ICore::Instance().SetWatermarkPosition(pos); +} + +extern V8Module sharedModule; +extern V8Class v8Player, + v8Player, + v8Vehicle, + v8WebView, + v8Blip, + v8AreaBlip, + v8RadiusBlip, + v8PointBlip, + v8HandlingData, + v8LocalStorage, + v8MemoryBuffer, + v8MapZoomData, + v8Discord, + v8Voice, + v8PedBlip, + v8VehicleBlip, + v8WebSocketClient, + v8Checkpoint, + v8HttpClient, + v8Audio, + v8LocalPlayer, + v8Profiler; +extern V8Module altModule( + "alt", + &sharedModule, + {v8Player, + v8Vehicle, + v8WebView, + v8Blip, + v8AreaBlip, + v8RadiusBlip, + v8PointBlip, + v8PedBlip, + v8VehicleBlip, + v8HandlingData, + v8LocalStorage, + v8MemoryBuffer, + v8MapZoomData, + v8Discord, + v8Voice, + v8WebSocketClient, + v8Checkpoint, + v8HttpClient, + v8Audio, + v8LocalPlayer, + v8Profiler}, + [](v8::Local ctx, v8::Local exports) { + V8Helpers::RegisterFunc(exports, "onServer", &OnServer); + V8Helpers::RegisterFunc(exports, "onceServer", &OnceServer); + V8Helpers::RegisterFunc(exports, "offServer", &OffServer); + V8Helpers::RegisterFunc(exports, "emitServer", &EmitServer); + V8Helpers::RegisterFunc(exports, "gameControlsEnabled", &GameControlsEnabled); + V8Helpers::RegisterFunc(exports, "toggleGameControls", &ToggleGameControls); + V8Helpers::RegisterFunc(exports, "toggleVoiceControls", &ToggleVoiceControls); + V8Helpers::RegisterFunc(exports, "showCursor", &ShowCursor); + + V8Helpers::RegisterFunc(exports, "getCursorPos", &GetCursorPos); + V8Helpers::RegisterFunc(exports, "setCursorPos", &SetCursorPos); + V8Helpers::RegisterFunc(exports, "isMenuOpen", &IsMenuOpen); + V8Helpers::RegisterFunc(exports, "isConsoleOpen", &IsConsoleOpen); + //V8Helpers::RegisterFunc(exports, "drawRect2D", &DrawRect2D); + + V8Helpers::RegisterFunc(exports, "requestIpl", &RequestIPL); + V8Helpers::RegisterFunc(exports, "removeIpl", &RemoveIPL); + //V8Helpers::RegisterFunc(exports, "wait", &ScriptWait); + //V8Helpers::RegisterFunc(exports, "isInSandbox", &IsInSandbox); + V8Helpers::RegisterFunc(exports, "setCamFrozen", &SetCamFrozen); + + V8Helpers::RegisterFunc(exports, "getLicenseHash", &GetLicenseHash); + + //Gxt texts functions + V8Helpers::RegisterFunc(exports, "addGxtText", &AddGxtText); + V8Helpers::RegisterFunc(exports, "removeGxtText", &RemoveGxtText); + V8Helpers::RegisterFunc(exports, "getGxtText", &GetGxtText); + + //Voice functions + V8Helpers::RegisterFunc(exports, "isVoiceActivityInputEnabled", &IsVoiceActivityInputEnabled); + + //Time managements functions + V8Helpers::RegisterFunc(exports, "setMsPerGameMinute", &SetMsPerGameMinute); + V8Helpers::RegisterFunc(exports, "getMsPerGameMinute", &GetMsPerGameMinute); + + //CEF rendering on texture + V8Helpers::RegisterFunc(exports, "isTextureExistInArchetype", &IsTextureExistInArchetype); + + //Scaleform additionals + V8Helpers::RegisterFunc(exports, "beginScaleformMovieMethodMinimap", &BeginScaleformMovieMethodMinimap); + +#ifndef NDEBUG + // V8Helpers::RegisterFunc(exports, "getVehWheels", &GetVehWheels); +#endif + + V8Helpers::RegisterFunc(exports, "getLocale", &GetLocale); + + V8Helpers::RegisterFunc(exports, "setWeatherCycle", &SetWeatherCycle); + V8Helpers::RegisterFunc(exports, "setWeatherSyncActive", &SetWeatherSyncActive); + + V8Helpers::RegisterFunc(exports, "setStat", &SetCharStat); + V8Helpers::RegisterFunc(exports, "getStat", &GetCharStat); + V8Helpers::RegisterFunc(exports, "resetStat", &ResetCharStat); + + V8Helpers::RegisterFunc(exports, "isKeyDown", &IsKeyDown); + V8Helpers::RegisterFunc(exports, "isKeyToggled", &IsKeyToggled); + + V8Helpers::RegisterFunc(exports, "setConfigFlag", &SetConfigFlag); + V8Helpers::RegisterFunc(exports, "getConfigFlag", &GetConfigFlag); + V8Helpers::RegisterFunc(exports, "doesConfigFlagExist", &DoesConfigFlagExist); + + // V8Helpers::RegisterFunc(exports, "getEntityMemoryByID", &GetEntityMemoryByID); + + V8Helpers::RegisterFunc(exports, "setRotationVelocity", &SetAngularVelocity); + // V8Helpers::RegisterFunc(exports, "setAngularVelocity", &SetAngularVelocity); + + V8Helpers::RegisterFunc(exports, "isInStreamerMode", &IsInStreamerMode); + V8Helpers::RegisterFunc(exports, "getPermissionState", &GetPermissionState); + + V8Helpers::RegisterFunc(exports, "takeScreenshot", &TakeScreenshot); + V8Helpers::RegisterFunc(exports, "takeScreenshotGameOnly", &TakeScreenshotGameOnly); + + V8Helpers::RegisterFunc(exports, "isGameFocused", &IsGameFocused); + + V8Helpers::RegisterFunc(exports, "loadModel", &LoadModel); + V8Helpers::RegisterFunc(exports, "loadModelAsync", &LoadModelAsync); + + V8Helpers::RegisterFunc(exports, "loadYtyp", &LoadYtyp); + V8Helpers::RegisterFunc(exports, "unloadYtyp", &UnloadYtyp); + + V8Helpers::RegisterFunc(exports, "evalModule", &EvalModule); + + V8Helpers::RegisterFunc(exports, "getHeadshotBase64", &GetHeadshotBase64); + + V8Helpers::RegisterFunc(exports, "setPedDlcClothes", &SetPedDlcClothes); + V8Helpers::RegisterFunc(exports, "setPedDlcProp", &SetPedDlcProps); + V8Helpers::RegisterFunc(exports, "clearPedProp", &ClearPedProps); + + V8Helpers::RegisterFunc(exports, "setWatermarkPosition", &SetWatermarkPosition); + }); diff --git a/client/src/bindings/MapZoomData.cpp b/client/src/bindings/MapZoomData.cpp new file mode 100644 index 00000000..b4075809 --- /dev/null +++ b/client/src/bindings/MapZoomData.cpp @@ -0,0 +1,194 @@ +#include "../CV8Resource.h" +#include "V8Class.h" + +extern V8Class v8MapZoomData; + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(1); + + V8_CHECK(info[0]->IsNumber() || info[0]->IsString(), "zoomDataId must be a number or string"); + + if (info[0]->IsNumber()) + { + V8_ARG_TO_UINT32(1, zoomDataId); + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoomData with this id not found"); + + info.This()->SetInternalField(0, info[0]); + } + else + { + V8_ARG_TO_STRING(1, zoomDataAlias); + + auto data = alt::ICore::Instance().GetMapData(zoomDataAlias); + V8_CHECK(data, "zoomData with this id not found"); + + uint8_t id = alt::ICore::Instance().GetMapDataIDFromAlias(zoomDataAlias); + info.This()->SetInternalField(0, v8::Integer::NewFromUnsigned(isolate, id)); + } +} + +static void Get(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE(); + V8_CHECK_ARGS_LEN(1); + V8_CHECK(info[0]->IsNumber() || info[0]->IsString(), "zoomDataId must be a number or string"); + + std::vector> args{ info[0] }; + V8_RETURN(v8MapZoomData.New(isolate->GetEnteredOrMicrotaskContext(), args)); +} + +static void ResetAll(const v8::FunctionCallbackInfo &info) +{ + alt::ICore::Instance().ResetAllMapData(); +} + +static void fZoomScaleGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + V8_RETURN_NUMBER(data->GetZoomScale()); +} + +static void fZoomScaleSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + V8_TO_NUMBER(val, fvalue); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + data->SetZoomScale((float)fvalue); +} + +static void fZoomSpeedGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + info.GetReturnValue().Set(v8::Number::New(isolate, data->GetZoomSpeed())); + V8_RETURN_NUMBER(data->GetZoomSpeed()); +} + +static void fZoomSpeedSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + V8_TO_NUMBER(val, fvalue); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + data->SetZoomSpeed((float)fvalue); +} + +static void fScrollSpeedGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + info.GetReturnValue().Set(v8::Number::New(isolate, data->GetScrollSpeed())); + V8_RETURN_NUMBER(data->GetScrollSpeed()); +} + +static void fScrollSpeedSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + V8_TO_NUMBER(val, fvalue); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + data->SetScrollSpeed((float)fvalue); +} + +static void vTilesXGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + info.GetReturnValue().Set(v8::Number::New(isolate, data->GetTilesCountX())); + V8_RETURN_NUMBER(data->GetTilesCountX()); +} + +static void vTilesXSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + V8_TO_NUMBER(val, fvalue); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + data->SetTilesCountX((float)fvalue); +} + +static void vTilesYGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + info.GetReturnValue().Set(v8::Number::New(isolate, data->GetTilesCountY())); + V8_RETURN_NUMBER(data->GetTilesCountY()); +} + +static void vTilesYSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + V8_TO_NUMBER(val, fvalue); + + auto data = alt::ICore::Instance().GetMapData(zoomDataId); + V8_CHECK(data, "zoom data not found"); + + data->SetTilesCountY((float)fvalue); +} + +static void Reset(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, zoomDataId); + + alt::ICore::Instance().ResetMapData(zoomDataId); +} + +// Perhaps rename or something +extern V8Class v8MapZoomData("MapZoomData", Constructor, [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + v8::Local proto = tpl->PrototypeTemplate(); + + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + V8::SetStaticMethod(isolate, tpl, "get", &Get); + V8::SetStaticMethod(isolate, tpl, "resetAll", &ResetAll); + + V8::SetAccessor(isolate, tpl, "fZoomScale", &fZoomScaleGetter, &fZoomScaleSetter); + V8::SetAccessor(isolate, tpl, "fZoomSpeed", &fZoomSpeedGetter, &fZoomSpeedSetter); + V8::SetAccessor(isolate, tpl, "fScrollSpeed", &fScrollSpeedGetter, &fScrollSpeedSetter); + V8::SetAccessor(isolate, tpl, "vTilesX", &vTilesXGetter, &vTilesXSetter); + V8::SetAccessor(isolate, tpl, "vTilesY", &vTilesYGetter, &vTilesYSetter); + V8::SetMethod(isolate, tpl, "reset", &Reset); +}); diff --git a/client/src/bindings/MemoryBuffer.cpp b/client/src/bindings/MemoryBuffer.cpp new file mode 100644 index 00000000..9bb4e9ca --- /dev/null +++ b/client/src/bindings/MemoryBuffer.cpp @@ -0,0 +1,190 @@ + +#include "../CV8Resource.h" +#include "V8Class.h" + +//static void weakCallbackForObjectHolder(const v8::WeakCallbackInfo& data) { +// uint8_t* memory = (uint8_t*)data.GetInternalField(0); +// delete data.GetParameter(); +//} + +//static void WeakCallback(v8::WeakCallbackData data) +//{ +// Local val = data.GetValue(); +// int* ptr = retinterpret_cast(val->GetAlignedPointerFromINternalField(0)); +// delete ptr; +// fprintf(stdout, "Deleted internal object!\n"); +//} + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(1); + + // Ask alt:V to add pattern searching to C++ SDK if you want this available +// if(info[0]->IsString()) +// { +// #ifdef NDEBUG +// V8_CHECK(CGame::Instance().IsDebug() && IsDevOrInternalBranch(), "must be in debug mode and dev branch to use memory patterns"); +// #endif +// V8_ARG_TO_STRING(0, str); +// auto mem = CMemory::Pattern(str.ToString()).Search(true); +// V8_CHECK(mem.IsValid(), "Pattern not found"); +// info.This()->SetAlignedPointerInInternalField(0, mem.GetAddress()); +// info.This()->SetInternalField(1, v8::Integer::NewFromUnsigned(isolate, UINT32_MAX)); +// } +// else + { + V8_ARG_TO_UINT32(1, size); + if(size == 0) + { + info.This()->SetAlignedPointerInInternalField(0, nullptr); + info.This()->SetInternalField(1, v8::Integer::NewFromUnsigned(isolate, 0)); + return; + } + V8_CHECK(size <= 1024, "You can't allocate > 1KB"); + + uint8_t* allocatedMemory = new uint8_t[size]; + memset(allocatedMemory, 0, size); + info.This()->SetAlignedPointerInInternalField(0, allocatedMemory); + info.This()->SetInternalField(1, v8::Integer::NewFromUnsigned(isolate, size)); + } + + /*v8::UniquePersistent persistent(isolate, info.This()); + persistent.SetWeak(info.This(), weakCallbackForObjectHolder, v8::WeakCallbackType::kInternalFields);*/ +} + +static void FreeBuffer(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_PTR(1, memory, uint8_t); + if (memory != nullptr) + { + delete memory; + info.This()->SetAlignedPointerInInternalField(0, nullptr); + V8_RETURN_BOOLEAN(true); + return; + } + V8_RETURN_BOOLEAN(false); +} + +static void SizeGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_UINT32(1, size); + V8_RETURN_UINT(size); +} + +static void AddressGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_INTERNAL_FIELD_PTR(1, memory, uint8_t); + V8_RETURN_INT64((uintptr_t)memory); +} + +template +static void GetDataOfType(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + bool isString = false; + if (std::is_same_v) + { + V8_CHECK_ARGS_LEN(2); + isString = true; + } + else + { + V8_CHECK_ARGS_LEN(1); + } + + V8_ARG_TO_UINT32(1, offset); + + uint32_t strLength = 0; + if (isString) + { + V8_ARG_TO_UINT32(2, len); + strLength = len; + } + + V8_GET_THIS_INTERNAL_FIELD_PTR(1, memory, uint8_t); + V8_GET_THIS_INTERNAL_FIELD_UINT32(2, size); + if (memory == nullptr || size == 0) + { + V8_RETURN_NULL(); + return; + } + + { + if (isString) + { + V8_CHECK((offset + strLength) <= size, "Offset is out of bounds"); + } + else + { + V8_CHECK((offset + sizeof(T)) <= size, "Offset is out of bounds"); + } + } + + if (!isString) + { + if (std::is_same_v || std::is_same_v || std::is_same_v) + { + V8_RETURN_UINT(*(uint32_t*)((uintptr_t)memory + offset)); + return; + } + else if (std::is_same_v) + { + V8_RETURN_UINT64(*(uint64_t*)((uintptr_t)memory + offset)); + return; + } + else if (std::is_same_v || std::is_same_v || std::is_same_v) + { + V8_RETURN_INT(*(int32_t*)((uintptr_t)memory + offset)); + return; + } + else if (std::is_same_v) + { + V8_RETURN_INT64(*(int64_t*)((uintptr_t)memory + offset)); + return; + } + else if (std::is_same_v || std::is_same_v) + { + V8_RETURN_NUMBER(*(double*)((uintptr_t)memory + offset)); + return; + } + } + else + { + char* newString = new char[strLength + 1]; + memcpy_s(newString, strLength + 1, (void*)((uintptr_t)memory + offset), strLength); + newString[strLength] = 0; + V8_RETURN_STRING(newString); + delete newString; + } +} + +extern V8Class v8MemoryBuffer("MemoryBuffer", Constructor, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + tpl->InstanceTemplate()->SetInternalFieldCount(2); + + V8::SetAccessor(isolate, tpl, "size", SizeGetter); + V8::SetAccessor(isolate, tpl, "address", AddressGetter); + + V8::SetMethod(isolate, tpl, "free", FreeBuffer); + V8::SetMethod(isolate, tpl, "ubyte", GetDataOfType); + V8::SetMethod(isolate, tpl, "ushort", GetDataOfType); + V8::SetMethod(isolate, tpl, "uint", GetDataOfType); + V8::SetMethod(isolate, tpl, "ulong", GetDataOfType); + V8::SetMethod(isolate, tpl, "byte", GetDataOfType); + V8::SetMethod(isolate, tpl, "short", GetDataOfType); + V8::SetMethod(isolate, tpl, "int", GetDataOfType); + V8::SetMethod(isolate, tpl, "long", GetDataOfType); + V8::SetMethod(isolate, tpl, "float", GetDataOfType); + V8::SetMethod(isolate, tpl, "double", GetDataOfType); + V8::SetMethod(isolate, tpl, "string", GetDataOfType); +}); diff --git a/client/src/bindings/Player.cpp b/client/src/bindings/Player.cpp new file mode 100644 index 00000000..be460ab6 --- /dev/null +++ b/client/src/bindings/Player.cpp @@ -0,0 +1,155 @@ + +#include "V8Helpers.h" +#include "V8BindHelpers.h" +#include "V8Class.h" +#include "V8Entity.h" +#include "V8ResourceImpl.h" + +#include "../CV8ScriptRuntime.h" + +#include "cpp-sdk/objects/IPlayer.h" +#include "cpp-sdk/objects/IVehicle.h" + +using namespace alt; + +static void ToString(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_BASE_OBJECT(player, alt::IPlayer); + + std::ostringstream ss; + ss << "Player{ id: " << std::to_string(player->GetID()) << ", name: " << player->GetName().CStr() << " }"; + + V8_RETURN_STRING(ss.str().c_str()); +} + +static void CurrentWeaponComponentsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(player, alt::IPlayer); + + alt::Array comps = player->GetCurrentWeaponComponents(); + + v8::Local componentsArray = v8::Array::New(isolate, comps.GetSize()); + + for (uint32_t i = 0; i < comps.GetSize(); ++i) + componentsArray->Set(ctx, i, v8::Integer::NewFromUnsigned(isolate, comps[i])); + + V8_RETURN(componentsArray); +} + +static void WeaponHasComponent(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(player, alt::IPlayer); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_INT(1, weaponHash); + V8_ARG_TO_INT(2, componentHash); + + V8_RETURN_BOOLEAN(player->HasWeaponComponent(weaponHash, componentHash)); +} + +static void GetWeaponTintIndex(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(player, alt::IPlayer); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, weaponHash); + + V8_RETURN_INT(player->GetWeaponTintIndex(weaponHash)); +} + +static void AllGetter(v8::Local name, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_RETURN(resource->GetAllPlayers()->Clone()); +} + +static void StreamedInGetter(v8::Local name, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + auto streamedIn = CV8ScriptRuntime::Instance().GetStreamedInPlayers(); + auto arr = v8::Array::New(isolate, streamedIn.size()); + int i = 0; + for(auto kv : streamedIn) + { + arr->Set(ctx, i, resource->GetOrCreateEntity(kv.second.Get(), "Player")->GetJSVal(isolate)); + i++; + } + + V8_RETURN(arr); +} + +static void LocalGetter(v8::Local name, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_RETURN_BASE_OBJECT(alt::ICore::Instance().GetLocalPlayer()); +} + +static void StaticGetByScriptID(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, scriptGuid); + V8_RETURN_BASE_OBJECT(alt::ICore::Instance().GetEntityByScriptGuid(scriptGuid).As()); +} + +static void StaticGetByID(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, id); + V8_RETURN_BASE_OBJECT(alt::ICore::Instance().GetEntityByID(id).As()); +} + +extern V8Class v8Entity; +extern V8Class v8Player("Player", v8Entity, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + V8::SetMethod(isolate, tpl, "toString", ToString); + + V8::SetStaticMethod(isolate, tpl, "getByID", StaticGetByID); + V8::SetStaticMethod(isolate, tpl, "getByScriptID", StaticGetByScriptID); + + V8::SetStaticAccessor(isolate, tpl, "all", &AllGetter); + V8::SetStaticAccessor(isolate, tpl, "streamedIn", &StreamedInGetter); + V8::SetStaticAccessor(isolate, tpl, "local", &LocalGetter); + + // Common getters + V8::SetAccessor(isolate, tpl, "name"); + V8::SetAccessor, &IPlayer::GetVehicle>(isolate, tpl, "vehicle"); + V8::SetAccessor(isolate, tpl, "seat"); + V8::SetAccessor(isolate, tpl, "isTalking"); + V8::SetAccessor(isolate, tpl, "micLevel"); + V8::SetAccessor(isolate, tpl, "health"); + V8::SetAccessor(isolate, tpl, "maxHealth"); + V8::SetAccessor(isolate, tpl, "armour"); + V8::SetAccessor(isolate, tpl, "maxArmour"); + V8::SetAccessor(isolate, tpl, "spatialVolume"); + V8::SetAccessor(isolate, tpl, "nonSpatialVolume"); + + // Weapon getters + V8::SetAccessor(isolate, tpl, "currentWeaponComponents", &CurrentWeaponComponentsGetter); + //V8::SetAccessor(isolate, tpl, "currentWeaponTintIndex", &CurrentWeaponTintIndexGetter); + V8::SetAccessor(isolate, tpl, "currentWeapon"); + V8::SetAccessor, &IPlayer::GetEntityAimingAt>(isolate, tpl, "entityAimingAt"); + V8::SetAccessor(isolate, tpl, "entityAimOffset"); + V8::SetAccessor(isolate, tpl, "flashlightActive"); + V8::SetAccessor(isolate, tpl, "aimPos"); + + // Gamestate getters + //V8::SetAccessor(isolate, tpl, "isJumping", &IsJumpingGetter); + V8::SetAccessor(isolate, tpl, "isInRagdoll"); + V8::SetAccessor(isolate, tpl, "isAiming"); + //V8::SetAccessor(isolate, tpl, "isShooting", &IsShootingGetter); + //V8::SetAccessor(isolate, tpl, "isReloading", &IsReloadingGetter); + V8::SetAccessor(isolate, tpl, "isDead"); + V8::SetAccessor(isolate, tpl, "moveSpeed"); + V8::SetAccessor(isolate, tpl, "headRot"); +}); diff --git a/client/src/bindings/Profiler.cpp b/client/src/bindings/Profiler.cpp new file mode 100644 index 00000000..56a8b20b --- /dev/null +++ b/client/src/bindings/Profiler.cpp @@ -0,0 +1,221 @@ +#include "V8Helpers.h" +#include "V8Class.h" +#include "../CV8ScriptRuntime.h" +#include "v8-profiler.h" +#include +#include +#include + +static void GetHeapStatistics(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + v8::HeapStatistics heapStats; + isolate->GetHeapStatistics(&heapStats); + V8_NEW_OBJECT(stats); + V8_OBJECT_SET_UINT(stats, "heapSizeLimit", heapStats.heap_size_limit()); + V8_OBJECT_SET_UINT(stats, "totalHeapSize", heapStats.total_heap_size()); + V8_OBJECT_SET_UINT(stats, "usedHeapSize", heapStats.used_heap_size()); + V8_OBJECT_SET_UINT(stats, "mallocedMemory", heapStats.malloced_memory()); + V8_OBJECT_SET_UINT(stats, "peakMallocedMemory", heapStats.peak_malloced_memory()); + + V8_RETURN(stats); +} + +// Key = Node ID, Value = Timestamp +// We store a map of the timestamps here, so we can quickly +// access it when setting it while serializing the profiler node +// todo: There is probably some nicer way to do this +static std::unordered_map nodeMap; +static uint32_t profilerRunningCount = 0; +static void GetProfileNodeData(v8::Isolate* isolate, const v8::CpuProfileNode* node, v8::Local result); + +static void StartProfiling(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN2(0, 1); + + v8::Local name; + if(info.Length() == 1) + { + V8_ARG_TO_STRING(1, profileName); + name = V8_NEW_STRING(profileName.CStr()); + } + else name = v8::String::Empty(isolate); + + v8::CpuProfilingStatus status = CV8ScriptRuntime::Instance().GetProfiler()->StartProfiling(name, true); + if(status == v8::CpuProfilingStatus::kStarted) + { + profilerRunningCount++; + return; + } + else if(status == v8::CpuProfilingStatus::kAlreadyStarted) V8Helpers::Throw(isolate, "A profile with the given name is already running"); + else if(status == v8::CpuProfilingStatus::kErrorTooManyProfilers) V8Helpers::Throw(isolate, "There are already too many profilers running"); +} + +static void StopProfiling(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN2(0, 1); + + v8::Local name; + if(info.Length() == 1) + { + V8_ARG_TO_STRING(1, profileName); + name = V8_NEW_STRING(profileName.CStr()); + } + else name = v8::String::Empty(isolate); + + v8::CpuProfile* result = CV8ScriptRuntime::Instance().GetProfiler()->StopProfiling(name); + V8_CHECK(result, "The specified profiler is not running"); + + // Store the node map + int sampleCount = result->GetSamplesCount(); + for(int i = 0; i < sampleCount; i++) + { + unsigned int nodeId = result->GetSample(i)->GetNodeId(); + if(nodeMap.count(nodeId) != 0) continue; + nodeMap.insert({ nodeId, (result->GetSampleTimestamp(i) / 1000) }); + } + + // Set top level info about the profile + V8_NEW_OBJECT(resultObj); + V8_OBJECT_SET_INT(resultObj, "id", std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); + V8_OBJECT_SET_STRING(resultObj, "type", alt::String("cpu")); + V8_OBJECT_SET_INT(resultObj, "start", result->GetStartTime() / 1000); + V8_OBJECT_SET_INT(resultObj, "end", result->GetEndTime() / 1000); + V8_OBJECT_SET_INT(resultObj, "samples", result->GetSamplesCount()); + + V8_NEW_OBJECT(root); + GetProfileNodeData(isolate, result->GetTopDownRoot(), root); + resultObj->Set(ctx, V8_NEW_STRING("root"), root); + + // Clear the nodemap to not cause a memory leak + nodeMap.clear(); + result->Delete(); + + profilerRunningCount--; + + V8_RETURN(resultObj); +} + +static void SamplingIntervalSetter(v8::Local, v8::Local value, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_TO_INT32(value, interval); + + V8_CHECK(profilerRunningCount == 0, "Can't set sampling interval while profiler is running"); + + CV8ScriptRuntime::Instance().SetProfilerSamplingInterval(interval); +} + +static void SamplingIntervalGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_RETURN_INT(CV8ScriptRuntime::Instance().GetProfilerSamplingInterval()); +} + +static void ProfilesRunningGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_RETURN_UINT(profilerRunningCount); +} + +extern V8Class v8Profiler("Profiler", [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetStaticAccessor(isolate, tpl, "heapStats", GetHeapStatistics); + V8::SetStaticAccessor(isolate, tpl, "samplingInterval", SamplingIntervalGetter, SamplingIntervalSetter); + V8::SetStaticAccessor(isolate, tpl, "profilesRunning", ProfilesRunningGetter); + + V8::SetStaticMethod(isolate, tpl, "startProfiling", StartProfiling); + V8::SetStaticMethod(isolate, tpl, "stopProfiling", StopProfiling); +}); + +// *** CPU Profile Serialization + +inline static const char* GetSourceTypeName(v8::CpuProfileNode::SourceType type) +{ + switch(type) + { + case v8::CpuProfileNode::SourceType::kScript: return "script"; + case v8::CpuProfileNode::SourceType::kBuiltin: return "builtins"; + case v8::CpuProfileNode::SourceType::kCallback: return "native-callback"; + case v8::CpuProfileNode::SourceType::kInternal: return "internal"; + } + return "unknown"; +} + +static void GetProfileNodeData(v8::Isolate* isolate, const v8::CpuProfileNode* node, v8::Local result) +{ + auto ctx = isolate->GetEnteredOrMicrotaskContext(); + + // Node info + result->Set(ctx, V8_NEW_STRING("id"), v8::Integer::NewFromUnsigned(isolate, node->GetNodeId())); + + v8::Local functionName; + const char* name = node->GetFunctionNameStr(); + if(name == NULL || strlen(name) == 0) functionName = V8_NEW_STRING("(anonymous function)"); + else functionName = V8_NEW_STRING(name); + result->Set(ctx, V8_NEW_STRING("function"), functionName); + + v8::Local sourceName; + const char* source = node->GetScriptResourceNameStr(); + if(source == NULL || strlen(source) == 0) sourceName = V8_NEW_STRING("(unknown)"); + else sourceName = V8_NEW_STRING(source); + result->Set(ctx, V8_NEW_STRING("source"), sourceName); + + result->Set(ctx, V8_NEW_STRING("sourceType"), V8_NEW_STRING(GetSourceTypeName(node->GetSourceType()))); + result->Set(ctx, V8_NEW_STRING("line"), v8::Integer::New(isolate, node->GetLineNumber())); + + v8::Local bailoutReason; + const char* reason = node->GetBailoutReason(); + if(reason == NULL || strlen(reason) == 0) bailoutReason = v8::Null(isolate); + else bailoutReason = V8_NEW_STRING(reason); + result->Set(ctx, V8_NEW_STRING("bailoutReason"), bailoutReason); + + result->Set(ctx, V8_NEW_STRING("hitCount"), v8::Integer::NewFromUnsigned(isolate, node->GetHitCount())); + + int64_t timestamp; + if(nodeMap.count(node->GetNodeId()) == 0) timestamp = -1; + else timestamp = nodeMap.at(node->GetNodeId()); + result->Set(ctx, V8_NEW_STRING("timestamp"), v8::Integer::New(isolate, timestamp)); + + // Children + { + int childrenCount = node->GetChildrenCount(); + v8::Local children; + if(childrenCount != 0) + { + children = v8::Array::New(isolate, childrenCount); + for(int i = 0; i < childrenCount; i++) + { + V8_NEW_OBJECT(child); + GetProfileNodeData(isolate, node->GetChild(i), child); + children.As()->Set(ctx, i, child); + } + } + else children = v8::Null(isolate); + + result->Set(ctx, V8_NEW_STRING("children"), children); + } + + // Line ticks + { + std::vector ticks(node->GetHitLineCount()); + auto ticksSize = ticks.size(); + v8::Local val; + if(node->GetLineTicks(&ticks[0], ticksSize)) + { + val = v8::Array::New(isolate, ticksSize); + for(size_t i = 0; i < ticksSize; i++) + { + auto tick = ticks[i]; + V8_NEW_OBJECT(tickObj); + tickObj->Set(ctx, V8_NEW_STRING("line"), v8::Integer::New(isolate, tick.line)); + tickObj->Set(ctx, V8_NEW_STRING("hitCount"), v8::Integer::NewFromUnsigned(isolate, tick.hit_count)); + val.As()->Set(ctx, i, tickObj); + } + } + else val = v8::Null(isolate); + result->Set(ctx, V8_NEW_STRING("lineTicks"), val); + } +} diff --git a/client/src/bindings/V8Natives.cpp b/client/src/bindings/V8Natives.cpp new file mode 100644 index 00000000..db050249 --- /dev/null +++ b/client/src/bindings/V8Natives.cpp @@ -0,0 +1,414 @@ + +#include "V8Helpers.h" +#include "V8Module.h" +#include "Log.h" +#include "V8ResourceImpl.h" + +static uint64_t pointers[32]; +static uint32_t pointersCount = 0; + +static uint32_t returnsCount = 1; + +static char *SaveString(const char *str) +{ + static char *stringValues[256] = {0}; + static int nextString = 0; + + if (stringValues[nextString]) + free(stringValues[nextString]); + + char *_str = _strdup(str); + stringValues[nextString] = _str; + nextString = (nextString + 1) % 256; + + return _str; +} + +template +static T *SavePointer(T val) +{ + T *ptr = reinterpret_cast(&pointers[pointersCount++]); + *ptr = val; + return ptr; +} + +template <> +static alt::INative::Vector3 *SavePointer(alt::INative::Vector3 val) +{ + alt::INative::Vector3 *ptr = reinterpret_cast(&pointers[pointersCount]); + pointersCount += 3; + *ptr = val; + return ptr; +} + +static void *ToMemoryBuffer(v8::Local val, v8::Local ctx) +{ + if (val->IsObject()) + { + v8::Local obj = val.As(); + + if (obj->InternalFieldCount() == 2) + { + void *memory = obj->GetAlignedPointerFromInternalField(0); + uint32_t size = obj->GetInternalField(0)->Uint32Value(ctx).ToChecked(); + + if (size > 0) + return memory; + } + } + + return nullptr; +} + +static const char* GetNativeTypeName(alt::INative::Type type) +{ + using Type = alt::INative::Type; + switch(type) + { + case Type::ARG_BOOL: + case Type::ARG_BOOL_PTR: + return "bool"; + case Type::ARG_INT32: + case Type::ARG_INT32_PTR: + return "int"; + case Type::ARG_UINT32: + case Type::ARG_UINT32_PTR: + return "unsigned int"; + case Type::ARG_FLOAT: + case Type::ARG_FLOAT_PTR: + return "float"; + case Type::ARG_VECTOR3: + case Type::ARG_VECTOR3_PTR: + return "vector3"; + case Type::ARG_STRING: + return "string"; + case Type::ARG_STRUCT: + return "struct"; + case Type::ARG_VOID: + return "void"; + } + return "unknown"; +} + +inline void ShowNativeArgParseErrorMsg(v8::Isolate* isolate, v8::Local val, alt::INative* native, alt::INative::Type argType, uint32_t idx) +{ + V8::SourceLocation source = V8::SourceLocation::GetCurrent(isolate); + auto resource = V8ResourceImpl::GetResource(isolate->GetEnteredOrMicrotaskContext()); + Log::Error << "[" << resource->GetName() << ":" << source.GetFileName() << ":" << source.GetLineNumber() << "] " + << "Native argument at index " << idx << " " << "(" << V8::GetJSValueTypeName(val) << ")" << " could not be parsed to type " << GetNativeTypeName(argType) + << " (" << native->GetName() << ")" << Log::Endl; +} + +inline void ShowNativeArgMismatchErrorMsg(v8::Isolate* isolate, alt::INative* native, int expected, int received) +{ + V8::SourceLocation source = V8::SourceLocation::GetCurrent(isolate); + auto resource = V8ResourceImpl::GetResource(isolate->GetEnteredOrMicrotaskContext()); + Log::Error << "[" << resource->GetName() << ":" << source.GetFileName() << ":" << source.GetLineNumber() << "] " + << "Native argument size mismatch. Expected: " << expected << ", Received: " << received + << " (" << native->GetName() << ")" << Log::Endl; + Log::Error << "Check the documentation for the needed arguments of this native." << Log::Endl; +} + +static void PushArg(alt::Ref scrCtx, alt::INative* native, alt::INative::Type argType, v8::Isolate *isolate, V8ResourceImpl* resource, v8::Local val, uint32_t idx) +{ + using ArgType = alt::INative::Type; + + v8::Local v8Ctx = isolate->GetEnteredOrMicrotaskContext(); + + switch (argType) + { + case alt::INative::Type::ARG_BOOL: + scrCtx->Push((int32_t)val->ToBoolean(isolate)->Value()); + break; + case alt::INative::Type::ARG_BOOL_PTR: + ++returnsCount; + scrCtx->Push(SavePointer((int32_t)val->ToBoolean(isolate)->Value())); + break; + case alt::INative::Type::ARG_INT32: + { + if (val->IsNumber()) + { + v8::Local value; + if (val->ToInteger(v8Ctx).ToLocal(&value)) + { + scrCtx->Push((int32_t)value->Value()); + } + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push(0); + } + } + else if (val->IsBigInt()) + { + v8::Local value; + if (val->ToBigInt(v8Ctx).ToLocal(&value)) + { + scrCtx->Push((int32_t)value->Int64Value()); + } + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push(0); + } + } + else if (val->IsObject()) + { + auto ent = V8Entity::Get(val); + if(ent != nullptr) scrCtx->Push(ent->GetHandle().As()->GetScriptGuid()); + else scrCtx->Push(0); + } + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push(0); + } + break; + } + case alt::INative::Type::ARG_INT32_PTR: + ++returnsCount; + scrCtx->Push(SavePointer((int32_t)val->ToInteger(v8Ctx).ToLocalChecked()->Value())); + break; + case alt::INative::Type::ARG_UINT32: + { + if (val->IsNumber()) + { + v8::Local value; + if (val->ToInteger(v8Ctx).ToLocal(&value)) + { + scrCtx->Push((uint32_t)value->Value()); + } + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push(0); + } + } + else if (val->IsBigInt()) + { + v8::Local value; + if (val->ToBigInt(v8Ctx).ToLocal(&value)) + { + scrCtx->Push((uint32_t)value->Int64Value()); + } + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push(0); + } + } + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push(0); + } + break; + } + case alt::INative::Type::ARG_UINT32_PTR: + ++returnsCount; + scrCtx->Push(SavePointer((uint32_t)val->ToInteger(v8Ctx).ToLocalChecked()->Value())); + break; + case alt::INative::Type::ARG_FLOAT: + { + if (val->IsNumber()) + { + v8::Local value; + if (val->ToNumber(v8Ctx).ToLocal(&value)) + { + scrCtx->Push((float)value->Value()); + } + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push(0.f); + } + } + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push(0.f); + } + break; + } + case alt::INative::Type::ARG_FLOAT_PTR: + ++returnsCount; + scrCtx->Push(SavePointer((float)val->ToNumber(v8Ctx).ToLocalChecked()->Value())); + break; + case alt::INative::Type::ARG_VECTOR3_PTR: + ++returnsCount; + scrCtx->Push(SavePointer(alt::INative::Vector3{})); // TODO: Add initializer + break; + case alt::INative::Type::ARG_STRING: + if (val->IsString()) + scrCtx->Push(SaveString(*v8::String::Utf8Value(isolate, val->ToString(v8Ctx).ToLocalChecked()))); + else + scrCtx->Push((char *)nullptr); + break; + case alt::INative::Type::ARG_STRUCT: + { + auto buffer = ToMemoryBuffer(val, v8Ctx); + if(buffer != nullptr) scrCtx->Push(buffer); + else + { + ShowNativeArgParseErrorMsg(isolate, val, native, argType, idx); + scrCtx->Push((void*)nullptr); + } + break; + } + default: + Log::Error << "Unknown native arg type " << (int)argType << " (" << native->GetName() << ")" << Log::Endl; + } +} + +static void PushPointerReturn(alt::INative::Type argType, v8::Local retns, v8::Isolate *isolate, v8::Local ctx) +{ + using ArgType = alt::INative::Type; + + switch (argType) + { + case alt::INative::Type::ARG_BOOL_PTR: + retns->Set(ctx, returnsCount++, v8::Boolean::New(isolate, *reinterpret_cast(&pointers[pointersCount++]))); + break; + case alt::INative::Type::ARG_INT32_PTR: + retns->Set(ctx, returnsCount++, v8::Integer::New(isolate, *reinterpret_cast(&pointers[pointersCount++]))); + break; + case alt::INative::Type::ARG_UINT32_PTR: + retns->Set(ctx, returnsCount++, v8::Integer::NewFromUnsigned(isolate, *reinterpret_cast(&pointers[pointersCount++]))); + break; + case alt::INative::Type::ARG_FLOAT_PTR: + retns->Set(ctx, returnsCount++, v8::Number::New(isolate, *reinterpret_cast(&pointers[pointersCount++]))); + break; + case alt::INative::Type::ARG_VECTOR3_PTR: + { + alt::INative::Vector3 *val = reinterpret_cast(&pointers[pointersCount]); + pointersCount += 3; + + v8::Local v8Ctx = isolate->GetEnteredOrMicrotaskContext(); + V8ResourceImpl* resource = V8ResourceImpl::Get(v8Ctx); + auto vector = resource->CreateVector3({ val->x, val->y, val->z }).As(); + + retns->Set(ctx, returnsCount++, vector); + break; + } + } +} + +static v8::Local GetReturn(alt::Ref scrCtx, alt::INative* native, alt::INative::Type retnType, v8::Isolate *isolate) +{ + using ArgType = alt::INative::Type; + + v8::Local v8Ctx = isolate->GetEnteredOrMicrotaskContext(); + + switch (retnType) + { + case alt::INative::Type::ARG_BOOL: + return v8::Boolean::New(isolate, scrCtx->ResultBool()); + case alt::INative::Type::ARG_INT32: + return v8::Integer::New(isolate, scrCtx->ResultInt()); + case alt::INative::Type::ARG_UINT32: + return v8::Integer::NewFromUnsigned(isolate, scrCtx->ResultUint()); + case alt::INative::Type::ARG_FLOAT: + return v8::Number::New(isolate, scrCtx->ResultFloat()); + case alt::INative::Type::ARG_VECTOR3: + { + alt::INative::Vector3 val = scrCtx->ResultVector3(); + V8ResourceImpl* resource = V8ResourceImpl::Get(v8Ctx); + auto vector = resource->CreateVector3({ val.x, val.y, val.z }).As(); + return vector; + } + case alt::INative::Type::ARG_STRING: + if (!scrCtx->ResultString()) + return v8::Null(isolate); + + return v8::String::NewFromUtf8(isolate, scrCtx->ResultString()).ToLocalChecked(); + case alt::INative::Type::ARG_VOID: + return v8::Undefined(isolate); + default: + Log::Error << "Unknown native return type " << (int)retnType << " (" << native->GetName() << ")" << Log::Endl; + return v8::Undefined(isolate); + } +} + +static inline int GetNativeNeededArgCount(alt::INative* native) +{ + int count = 0; + auto args = native->GetArgTypes(); + for(auto arg : args) + { + if(arg == alt::INative::Type::ARG_BOOL_PTR || arg == alt::INative::Type::ARG_INT32_PTR || arg == alt::INative::Type::ARG_UINT32_PTR || + arg == alt::INative::Type::ARG_FLOAT_PTR || arg == alt::INative::Type::ARG_VOID || arg == alt::INative::Type::ARG_VECTOR3_PTR) + continue; + count++; + } + return count; +} + +static void InvokeNative(const v8::FunctionCallbackInfo &info) +{ + static auto ctx = alt::ICore::Instance().CreateNativesContext(); + + v8::Isolate *isolate = info.GetIsolate(); + v8::Local v8Ctx = isolate->GetCurrentContext(); + + auto native = static_cast(info.Data().As()->Value()); + + if (!native->IsValid()) + { + info.GetReturnValue().Set(v8::Boolean::New(isolate, false)); + return; + } + + auto args = native->GetArgTypes(); + uint32_t argsSize = args.GetSize(); + + auto neededArgs = GetNativeNeededArgCount(native); + if(neededArgs > info.Length()) + { + ShowNativeArgMismatchErrorMsg(isolate, native, neededArgs, info.Length()); + return; + } + + ctx->Reset(); + pointersCount = 0; + returnsCount = 1; + + auto resource = V8ResourceImpl::Get(v8Ctx); + for (uint32_t i = 0; i < argsSize; ++i) + PushArg(ctx, native, args[i], isolate, resource, info[i], i); + + if (!native->Invoke(ctx)) + { + V8Helpers::Throw(isolate, "Native call failed"); + return; + } + + if (returnsCount == 1) + { + info.GetReturnValue().Set(GetReturn(ctx, native, native->GetRetnType(), isolate)); + } + else + { + v8::Local retns = v8::Array::New(isolate, returnsCount); + retns->Set(v8Ctx, 0, GetReturn(ctx, native, native->GetRetnType(), isolate)); + + pointersCount = 0; + returnsCount = 1; + + for (uint32_t i = 0; i < argsSize; ++i) + PushPointerReturn(args[i], retns, isolate, v8Ctx); + + info.GetReturnValue().Set(retns); + } +} + +static void RegisterNatives(v8::Local ctx, v8::Local exports) +{ + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + for (auto native : alt::ICore::Instance().GetAllNatives()) + { + V8::SetFunction(isolate, ctx, exports, native->GetName().CStr(), InvokeNative, native); + } +} + +extern V8Module nativesModule("natives", nullptr, {}, RegisterNatives); diff --git a/client/src/bindings/Vehicle.cpp b/client/src/bindings/Vehicle.cpp new file mode 100644 index 00000000..92d751b1 --- /dev/null +++ b/client/src/bindings/Vehicle.cpp @@ -0,0 +1,175 @@ + +#include "V8Helpers.h" +#include "V8BindHelpers.h" +#include "V8Class.h" +#include "V8Entity.h" +#include "V8ResourceImpl.h" + +#include "../CV8ScriptRuntime.h" + +#include "cpp-sdk/objects/IPlayer.h" +#include "cpp-sdk/objects/IVehicle.h" + +using namespace alt; + +static void ToString(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_BASE_OBJECT(vehicle, alt::IVehicle); + + std::ostringstream ss; + ss << "Vehicle{ id: " << std::to_string(vehicle->GetID()) << ", model: " << std::to_string((uint64_t)vehicle->GetModel()) << " }"; + + V8_RETURN_STRING(ss.str().c_str()); +} + +static void HandlingGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_NEW_ARGS(args); + V8_ADD_ARG(args, info.This()); + + extern V8Class v8Handling; + V8_RETURN(v8Handling.New(ctx, args)); +} + +static void NeonGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(vehicle, alt::IVehicle); + + bool left, right, front, back; + vehicle->GetNeonActive(&left, &right, &front, &back); + + V8_NEW_OBJECT(neonActive); + V8_OBJECT_SET_BOOLEAN(neonActive, "left", left); + V8_OBJECT_SET_BOOLEAN(neonActive, "right", right); + V8_OBJECT_SET_BOOLEAN(neonActive, "front", front); + V8_OBJECT_SET_BOOLEAN(neonActive, "back", back); + + V8_RETURN(neonActive); +} + +static void ToggleExtra(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(vehicle, alt::IVehicle); + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_INT(1, extraID); + V8_ARG_TO_BOOLEAN(2, toggle); + vehicle->ToggleExtra(extraID, toggle); +} + +static void AllGetter(v8::Local name, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_RETURN(resource->GetAllVehicles()->Clone()); +} + +static void StreamedInGetter(v8::Local name, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + auto streamedIn = CV8ScriptRuntime::Instance().GetStreamedInVehicles(); + auto arr = v8::Array::New(isolate, streamedIn.size()); + int i = 0; + for(auto kv : streamedIn) + { + arr->Set(ctx, i, resource->GetOrCreateEntity(kv.second.Get(), "Vehicle")->GetJSVal(isolate)); + i++; + } + + V8_RETURN(arr); +} + +static void StaticGetByScriptID(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, scriptGuid); + V8_RETURN_BASE_OBJECT(alt::ICore::Instance().GetEntityByScriptGuid(scriptGuid).As()); +} + +static void StaticGetByID(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, id); + V8_RETURN_BASE_OBJECT(alt::ICore::Instance().GetEntityByID(id).As()); +} + +static void IndicatorLightsGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_THIS_BASE_OBJECT(vehicle, alt::IVehicle); + + V8_RETURN_INT(vehicle->GetLightsIndicator()); +} + +static void IndicatorLightsSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(vehicle, alt::IVehicle); + + V8_TO_INTEGER(val, indicatorLights); + vehicle->SetLightsIndicator(indicatorLights); +} + +extern V8Class v8Entity; +extern V8Class v8Vehicle("Vehicle", v8Entity, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + V8::SetMethod(isolate, tpl, "toString", ToString); + + V8::SetStaticMethod(isolate, tpl, "getByID", StaticGetByID); + V8::SetStaticMethod(isolate, tpl, "getByScriptID", StaticGetByScriptID); + + V8::SetStaticAccessor(isolate, tpl, "all", &AllGetter); + V8::SetStaticAccessor(isolate, tpl, "streamedIn", &StreamedInGetter); + + // Common getters + V8::SetAccessor(isolate, tpl, "speed"); + V8::SetAccessor(isolate, tpl, "gear"); + V8::SetAccessor(isolate, tpl, "maxGear"); + V8::SetAccessor(isolate, tpl, "rpm"); + V8::SetAccessor(isolate, tpl, "wheelsCount"); + V8::SetAccessor(isolate, tpl, "speedVector"); + V8::SetAccessor(isolate, tpl, "handling", &HandlingGetter); + V8::SetMethod(isolate, tpl, "toggleExtra", ToggleExtra); + V8::SetAccessor(isolate, tpl, "indicatorLights"); + + /*GETTERS BELOW ARE UNIMPLEMENTED + V8::SetAccessor(isolate, tpl, "isDestroyed", &IsDestroyedGetter); + V8::SetAccessor(isolate, tpl, "driver", &DriverGetter); + + // Appearance getters + V8::SetAccessor(isolate, tpl, "modKitsCount", &ModKitsCountGetter); + V8::SetAccessor(isolate, tpl, "modKit", &ModKitGetter); + V8::SetAccessor(isolate, tpl, "hasCustomPrimaryColor", &IsPrimaryColorRGBGetter); + V8::SetAccessor(isolate, tpl, "primaryColor", &PrimaryColorGetter); + V8::SetAccessor(isolate, tpl, "customPrimaryColor", &PrimaryColorRGBGetter); + //V8::SetAccessor(isolate, tpl, "hasCustomSecondaryColor", &IsSecondaryColorRGBGetter); + V8::SetAccessor(isolate, tpl, "secondaryColor", &SecondaryColorGetter); + V8::SetAccessor(isolate, tpl, "customSecondaryColor", &SecondaryColorRGBGetter); + V8::SetAccessor(isolate, tpl, "pearlColor", &PearlColorGetter); + V8::SetAccessor(isolate, tpl, "wheelColor", &WheelColorGetter); + V8::SetAccessor(isolate, tpl, "interiorColor", &InteriorColorGetter); + V8::SetAccessor(isolate, tpl, "dashboardColor", &DashboardColorGetter); + //V8::SetAccessor(isolate, tpl, "hasCustomTireSmokeColor", &IsTireSmokeColorCustomGetter); + V8::SetAccessor(isolate, tpl, "tireSmokeColor", &TireSmokeColorGetter); + V8::SetAccessor(isolate, tpl, "wheelType", &WheelTypeGetter); + V8::SetAccessor(isolate, tpl, "frontWheels", &WheelVariationGetter); + V8::SetAccessor(isolate, tpl, "rearWheels", &RearWheelVariationGetter); + V8::SetAccessor(isolate, tpl, "customTires", &IsCustomTiresGetter); + V8::SetAccessor(isolate, tpl, "darkness", &SpecialDarknessGetter); + V8::SetAccessor(isolate, tpl, "numberPlateIndex", &NumberplateIndexGetter); + V8::SetAccessor(isolate, tpl, "numberPlateText", &NumberplateTextGetter); + V8::SetAccessor(isolate, tpl, "windowTint", &WindowTintGetter); + V8::SetAccessor(isolate, tpl, "dirtLevel", &DirtLevelGetter); + //V8::SetAccessor(isolate, tpl, "neonActive", &IsNeonActiveGetter); + V8::SetAccessor(isolate, tpl, "neon", &NeonGetter); + V8::SetAccessor(isolate, tpl, "neonColor", &NeonColorGetter);*/ +}); diff --git a/client/src/bindings/Voice.cpp b/client/src/bindings/Voice.cpp new file mode 100644 index 00000000..1f3fb443 --- /dev/null +++ b/client/src/bindings/Voice.cpp @@ -0,0 +1,41 @@ + +#include "V8Helpers.h" +#include "V8ResourceImpl.h" +#include "V8Class.h" + +static void StaticGetInputMuted(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_RETURN_BOOLEAN(alt::ICore::Instance().IsVoiceInputMuted()); +} + +static void StaticSetInputMuted(v8::Local, v8::Local value, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_TO_BOOLEAN(value, state); + + alt::ICore::Instance().SetVoiceInputMuted(state); +} + +static void StaticGetVoiceActivityInputEnabled(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_RETURN_BOOLEAN(alt::ICore::Instance().IsVoiceActivationEnabled()); +} + +static void StaticGetVoiceActivationKey(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_RETURN_UINT(alt::ICore::Instance().GetVoiceActivationKey()); +} + +extern V8Class v8Voice("Voice", [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetStaticAccessor(isolate, tpl, "muteInput", StaticGetInputMuted, StaticSetInputMuted); + V8::SetStaticAccessor(isolate, tpl, "activityInputEnabled", StaticGetVoiceActivityInputEnabled); + V8::SetStaticAccessor(isolate, tpl, "activationKey", StaticGetVoiceActivationKey); +}); diff --git a/client/src/bindings/WebSocketClient.cpp b/client/src/bindings/WebSocketClient.cpp new file mode 100644 index 00000000..84763fa2 --- /dev/null +++ b/client/src/bindings/WebSocketClient.cpp @@ -0,0 +1,189 @@ +#include "V8Helpers.h" +#include "V8BindHelpers.h" +#include "V8Class.h" +#include "V8Entity.h" +#include "V8ResourceImpl.h" +#include "../CV8Resource.h" +#include "cpp-sdk/script-objects/IWebView.h" + +using namespace alt; + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_STRING(1, url); + + alt::IResource* altres = V8ResourceImpl::GetResource(isolate->GetEnteredOrMicrotaskContext()); + V8_CHECK(altres, "invalid resource"); + + alt::Ref webSocket = nullptr; + + webSocket = alt::ICore::Instance().CreateWebSocketClient(url, altres); + + V8_BIND_BASE_OBJECT(webSocket, "Failed to create WebSocketClient"); +} + +static void On(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, fun); + + V8_GET_THIS_BASE_OBJECT(webSocket, alt::IWebSocketClient); + + static_cast(resource)->SubscribeWebSocketClient(webSocket, evName.ToString(), fun, V8::SourceLocation::GetCurrent(isolate)); +} + +static void Off(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, fun); + + V8_GET_THIS_BASE_OBJECT(webSocket, alt::IWebSocketClient); + + static_cast(resource)->UnsubscribeWebSocketClient(webSocket, evName.ToString(), fun); +} + +static void Send(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_GET_THIS_BASE_OBJECT(webSocket, alt::IWebSocketClient); + + bool ret = false; + if (info[0]->IsString()) + { + V8_ARG_TO_STRING(1, msg); + ret = webSocket->Send(msg); + } + else if (info[0]->IsArrayBufferView()) + { + V8_ARG_TO_ARRAY_BUFFER_VIEW(1, v8ArrayBufferView); + auto v8Buffer = v8ArrayBufferView->Buffer()->GetBackingStore(); + ret = webSocket->SendBinary(alt::StringView((char*)v8Buffer->Data(), v8Buffer->ByteLength())); + } + else if (info[0]->IsArrayBuffer()) + { + V8_ARG_TO_ARRAY_BUFFER(1, v8ArrayBuffer); + auto v8Buffer = v8ArrayBuffer->GetBackingStore(); + ret = webSocket->SendBinary(alt::StringView((char*)v8Buffer->Data(), v8Buffer->ByteLength())); + } + else + { + V8_CHECK(false, "Unknown parameter type specified for IWebSocketClient::Send"); + } + V8_RETURN_BOOLEAN(ret); +} + +static void AddSubProtocol(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, msg); + + V8_GET_THIS_BASE_OBJECT(webSocket, alt::IWebSocketClient); + + webSocket->AddSubProtocol(msg); +} + +static void GetSubProtocols(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_BASE_OBJECT(webSocket, alt::IWebSocketClient); + + auto protocols = webSocket->GetSubProtocols(); + + v8::Local protocolsArray = v8::Array::New(isolate, protocols.GetSize()); + + int idx = 0; + for (auto& protocol : protocols) + protocolsArray->Set(ctx, idx++, v8::String::NewFromUtf8(isolate, protocol.CStr()).ToLocalChecked()); + + V8_RETURN(protocolsArray); +} + +static void SetExtraHeader(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, name); + V8_ARG_TO_STRING(2, value); + + V8_GET_THIS_BASE_OBJECT(webSocket, alt::IWebSocketClient); + + webSocket->SetExtraHeader(name, value); +} + +static void GetExtraHeaders(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_BASE_OBJECT(webSocket, alt::IWebSocketClient); + + auto extraHeaders = webSocket->GetExtraHeaders(); + V8_NEW_OBJECT(headersObject); + + for (auto it = extraHeaders->Begin(); it; it = extraHeaders->Next()) + { + alt::String key = it->GetKey(); + V8_OBJECT_SET_STRING(headersObject, key.CStr(), extraHeaders->Get(key).As()->Value()); + } + + V8_RETURN(headersObject); +} + +static void GetEventListeners(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_GET_THIS_BASE_OBJECT(webSocket, alt::IWebSocketClient); + + V8_ARG_TO_STRING(1, eventName); + + std::vector handlers = static_cast(resource)->GetWebSocketClientHandlers(webSocket, eventName.ToString()); + + auto array = v8::Array::New(isolate, handlers.size()); + for(int i = 0; i < handlers.size(); i++) + { + array->Set(ctx, i, handlers[i]->fn.Get(isolate)); + } + + V8_RETURN(array); +} + +extern V8Class v8BaseObject; +extern V8Class v8WebSocketClient("WebSocketClient", v8BaseObject, &Constructor, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + V8::SetMethod(isolate, tpl, "on", &On); + V8::SetMethod(isolate, tpl, "off", &Off); + V8::SetMethod(isolate, tpl, "getEventListeners", GetEventListeners); + + V8::SetMethod(isolate, tpl, "start"); + V8::SetMethod(isolate, tpl, "stop"); + V8::SetMethod(isolate, tpl, "send", &Send); + + V8::SetMethod(isolate, tpl, "addSubProtocol", &AddSubProtocol); + V8::SetMethod(isolate, tpl, "getSubProtocols", &GetSubProtocols); + + V8::SetMethod(isolate, tpl, "setExtraHeader", &SetExtraHeader); + V8::SetMethod(isolate, tpl, "getExtraHeaders", &GetExtraHeaders); + + V8::SetAccessor(isolate, tpl, "autoReconnect"); + V8::SetAccessor(isolate, tpl, "perMessageDeflate"); + V8::SetAccessor(isolate, tpl, "pingInterval"); + V8::SetAccessor(isolate, tpl, "url"); + V8::SetAccessor(isolate, tpl, "readyState"); +}); diff --git a/client/src/bindings/WebView.cpp b/client/src/bindings/WebView.cpp new file mode 100644 index 00000000..a89d41b9 --- /dev/null +++ b/client/src/bindings/WebView.cpp @@ -0,0 +1,242 @@ + +#include "V8Helpers.h" +#include "V8BindHelpers.h" +#include "V8Class.h" +#include "V8Entity.h" +#include "V8ResourceImpl.h" +#include "../CV8Resource.h" +#include "cpp-sdk/script-objects/IWebView.h" + +using namespace alt; + +static void ToString(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + auto webview = info.This(); + V8_OBJECT_GET_STRING(webview, "url", url); + + std::ostringstream ss; + ss << "WebView{ url: " << url.CStr() << " }"; + + V8_RETURN_STRING(ss.str().c_str()); +} + +static void On(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, fun); + + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + static_cast(resource)->SubscribeWebView(view, evName.ToString(), fun, V8::SourceLocation::GetCurrent(isolate)); +} + +static void Once(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, fun); + + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + static_cast(resource)->SubscribeWebView(view, evName.ToString(), fun, V8::SourceLocation::GetCurrent(isolate), true); +} + +static void Off(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, fun); + + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + static_cast(resource)->UnsubscribeWebView(view, evName.ToString(), fun); +} + +static void Emit(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, evName); + + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + alt::MValueArgs mvArgs; + + for (int i = 1; i < info.Length(); ++i) + mvArgs.Push(V8Helpers::V8ToMValue(info[i])); + + view->Trigger(evName, mvArgs); +} + +static void GetEventListeners(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + V8_ARG_TO_STRING(1, eventName); + + std::vector handlers = static_cast(resource)->GetWebViewHandlers(view, eventName.ToString()); + + auto array = v8::Array::New(isolate, handlers.size()); + for(int i = 0; i < handlers.size(); i++) + { + array->Set(ctx, i, handlers[i]->fn.Get(isolate)); + } + + V8_RETURN(array); +} + +static void FocusedGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE(); + + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + V8_RETURN_BOOLEAN(view->IsFocused()); +} + +static void FocusedSetter(v8::Local, v8::Local value, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + V8_TO_BOOLEAN(value, shouldBeFocused); + + if(shouldBeFocused) view->Focus(); + else view->Unfocus(); +} + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN_MIN_MAX(1, 4); + + V8_ARG_TO_STRING(1, url); + + alt::IResource* altres = V8ResourceImpl::GetResource(isolate->GetEnteredOrMicrotaskContext()); + V8_CHECK(altres, "invalid resource"); + + alt::Ref view = nullptr; + + if (info.Length() == 4) + { + V8_ARG_TO_BOOLEAN(2, isOverlayBool); + V8_ARG_TO_OBJECT(3, pos); + V8_ARG_TO_OBJECT(4, size); + + V8_OBJECT_GET_INT(pos, "x", posX); + V8_OBJECT_GET_INT(pos, "y", posY); + + V8_OBJECT_GET_INT(size, "x", sizeX); + V8_OBJECT_GET_INT(size, "y", sizeY); + + view = alt::ICore::Instance().CreateWebView(altres, url, { posX, posY }, { sizeX, sizeY }, true, isOverlayBool); + } + else if (info.Length() == 3 && info[2]->IsObject()) + { + V8_ARG_TO_OBJECT(2, pos); + V8_ARG_TO_OBJECT(3, size); + + V8_OBJECT_GET_INT(pos, "x", posX); + V8_OBJECT_GET_INT(pos, "y", posY); + + V8_OBJECT_GET_INT(size, "x", sizeX); + V8_OBJECT_GET_INT(size, "y", sizeY); + + view = alt::ICore::Instance().CreateWebView(altres, url, { posX, posY }, { sizeX, sizeY }, true, false); + } + else if (info.Length() == 3) + { + V8_ARG_TO_INT(2, drawableHash); + V8_ARG_TO_STRING(3, targetTextureStr); + + auto texture = alt::ICore::Instance().GetTextureFromDrawable(drawableHash, targetTextureStr); + V8_CHECK(texture != nullptr, "Texture not found"); + + view = alt::ICore::Instance().CreateWebView(altres, url, (uint32_t)drawableHash, targetTextureStr); + V8_CHECK(!view.IsEmpty(), "Interactive WebView cannot be created"); + } + else if (info.Length() == 2 && info[1]->IsObject()) + { + V8_ARG_TO_OBJECT(2, pos); + + V8_OBJECT_GET_INT(pos, "x", posX); + V8_OBJECT_GET_INT(pos, "y", posY); + + view = alt::ICore::Instance().CreateWebView(altres, url, { posX, posY }, { 0, 0 }, true, false); + } + else if (info.Length() == 2) + { + V8_ARG_TO_BOOLEAN(2, isOverlayBool); + + view = alt::ICore::Instance().CreateWebView(altres, url, { 0, 0 }, { 0, 0 }, true, isOverlayBool); + } + else + { + view = alt::ICore::Instance().CreateWebView(altres, url, { 0, 0 }, { 0, 0 }, true, false); + } + + V8_BIND_BASE_OBJECT(view, "Failed to create WebView"); +} + +static void SetExtraHeader(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, name); + V8_ARG_TO_STRING(2, value); + + view->SetExtraHeader(name, value); +} + +static void SetZoomLevel(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_GET_THIS_BASE_OBJECT(view, alt::IWebView); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_NUMBER(1, zoomLevel); + + view->SetZoomLevel(zoomLevel); +} + +extern V8Class v8BaseObject; +extern V8Class v8WebView("WebView", v8BaseObject, &Constructor, [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetMethod(isolate, tpl, "toString", ToString); + + V8::SetAccessor(isolate, tpl, "isVisible"); + V8::SetAccessor(isolate, tpl, "url"); + V8::SetAccessor(isolate, tpl, "isOverlay"); + V8::SetAccessor(isolate, tpl, "isReady"); + V8::SetAccessor(isolate, tpl, "focused", &FocusedGetter, &FocusedSetter); + + V8::SetMethod(isolate, tpl, "on", &On); + V8::SetMethod(isolate, tpl, "once", &Once); + V8::SetMethod(isolate, tpl, "off", &Off); + V8::SetMethod(isolate, tpl, "getEventListeners", GetEventListeners); + V8::SetMethod(isolate, tpl, "emit", &Emit); + V8::SetMethod(isolate, tpl, "focus"); + V8::SetMethod(isolate, tpl, "unfocus"); + + V8::SetMethod(isolate, tpl, "setExtraHeader", &SetExtraHeader); + V8::SetMethod(isolate, tpl, "setZoomLevel", &SetZoomLevel); +}); diff --git a/client/src/events/Entity.cpp b/client/src/events/Entity.cpp new file mode 100644 index 00000000..66db5dcb --- /dev/null +++ b/client/src/events/Entity.cpp @@ -0,0 +1,63 @@ +#include "V8ResourceImpl.h" +#include "V8Helpers.h" + +#include "../CV8ScriptRuntime.h" + +#include "cpp-sdk/events/CRemoveEntityEvent.h" +#include "cpp-sdk/events/CGameEntityCreateEvent.h" +#include "cpp-sdk/events/CGameEntityDestroyEvent.h" +#include "cpp-sdk/events/CTaskChangeEvent.h" + +#include "cpp-sdk/SDK.h" + +using alt::CEvent; +using EventType = CEvent::Type; + +V8_LOCAL_EVENT_HANDLER removeEntity( + EventType::REMOVE_ENTITY_EVENT, + "removeEntity", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(resource->GetOrCreateEntity(ev->GetEntity().Get())->GetJSVal(isolate)); + }); + +V8_EVENT_HANDLER gameEntityCreate( + EventType::GAME_ENTITY_CREATE, + [](V8ResourceImpl* resource, const alt::CEvent* e) { + CV8ScriptRuntime::Instance().OnEntityStreamIn(static_cast(e)->GetTarget()); + + return resource->GetLocalHandlers("gameEntityCreate"); + }, + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(resource->GetOrCreateEntity(ev->GetTarget().Get())->GetJSVal(isolate)); + }); + +V8_EVENT_HANDLER gameEntityDestroy( + EventType::GAME_ENTITY_DESTROY, + [](V8ResourceImpl* resource, const alt::CEvent* e) { + CV8ScriptRuntime::Instance().OnEntityStreamOut(static_cast(e)->GetTarget()); + + return resource->GetLocalHandlers("gameEntityDestroy"); + }, + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(resource->GetOrCreateEntity(ev->GetTarget().Get())->GetJSVal(isolate)); + }); + +V8_LOCAL_EVENT_HANDLER taskChange( + EventType::TASK_CHANGE, + "taskChange", + [](V8ResourceImpl* resource, const alt::CEvent* e, std::vector>& args) { + auto ev = static_cast(e); + v8::Isolate* isolate = resource->GetIsolate(); + + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetOldTask())); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetNewTask())); + }); \ No newline at end of file diff --git a/client/src/events/Events.h b/client/src/events/Events.h new file mode 100644 index 00000000..066c2076 --- /dev/null +++ b/client/src/events/Events.h @@ -0,0 +1,49 @@ +#pragma once + +#include "V8Helpers.h" + +/** + * Needed cause MSVC requires globals to be references + * or they will be optimized away in static builds + * https://stackoverflow.com/questions/9459980/c-global-variable-not-initialized-when-linked-through-static-libraries-but-ok + * the other solutions didn't work + * + * Once the module is completely a DLL, this will not be necessary + */ +inline void RegisterEvents() +{ + // Shared + V8_REFERENCE_LOCAL_EVENT_HANDLER(consoleCommand); + + // Main + V8_REFERENCE_EVENT_HANDLER(clientScriptEvent); + V8_REFERENCE_EVENT_HANDLER(serverScriptEvent); + V8_REFERENCE_EVENT_HANDLER(webviewEvent); + V8_REFERENCE_EVENT_HANDLER(webSocketEvent); + V8_REFERENCE_EVENT_HANDLER(audioEvent); + V8_REFERENCE_EVENT_HANDLER(keyboardEvent); + V8_REFERENCE_LOCAL_EVENT_HANDLER(render); + V8_REFERENCE_LOCAL_EVENT_HANDLER(connectionComplete); + V8_REFERENCE_LOCAL_EVENT_HANDLER(disconnect); + + // Entity + V8_REFERENCE_LOCAL_EVENT_HANDLER(removeEntity); + V8_REFERENCE_EVENT_HANDLER(gameEntityCreate); + V8_REFERENCE_EVENT_HANDLER(gameEntityDestroy); + + // Meta + V8_REFERENCE_LOCAL_EVENT_HANDLER(syncedMetaChange); + V8_REFERENCE_LOCAL_EVENT_HANDLER(streamSyncedMetaChange); + V8_REFERENCE_LOCAL_EVENT_HANDLER(globalSyncedMetaChange); + V8_REFERENCE_LOCAL_EVENT_HANDLER(globalMetaChange); + + // Resource + V8_REFERENCE_LOCAL_EVENT_HANDLER(anyResourceStart); + V8_REFERENCE_LOCAL_EVENT_HANDLER(anyResourceStop); + V8_REFERENCE_LOCAL_EVENT_HANDLER(anyResourceError); + + // Vehicle + V8_REFERENCE_LOCAL_EVENT_HANDLER(enteredVehicle); + V8_REFERENCE_LOCAL_EVENT_HANDLER(leftVehicle); + V8_REFERENCE_LOCAL_EVENT_HANDLER(changedVehicleSeat); +} diff --git a/client/src/events/Main.cpp b/client/src/events/Main.cpp new file mode 100644 index 00000000..0ba2f383 --- /dev/null +++ b/client/src/events/Main.cpp @@ -0,0 +1,129 @@ +#include "../CV8Resource.h" +#include "../CV8ScriptRuntime.h" + +#include "V8ResourceImpl.h" +#include "V8Helpers.h" + +#include "cpp-sdk/events/CConnectionComplete.h" +#include "cpp-sdk/events/CDisconnectEvent.h" +#include "cpp-sdk/events/CRenderEvent.h" +#include "cpp-sdk/events/CConsoleCommandEvent.h" +#include "cpp-sdk/events/CClientScriptEvent.h" +#include "cpp-sdk/events/CServerScriptEvent.h" +#include "cpp-sdk/events/CKeyboardEvent.h" +#include "cpp-sdk/events/CWebViewEvent.h" +#include "cpp-sdk/events/CWebSocketClientEvent.h" +#include "cpp-sdk/events/CAudioEvent.h" + +#include "cpp-sdk/SDK.h" + +using alt::CEvent; +using EventType = CEvent::Type; + +V8_EVENT_HANDLER clientScriptEvent( + EventType::CLIENT_SCRIPT_EVENT, + [](V8ResourceImpl* resource, const CEvent* e) { + auto ev = static_cast(e); + return resource->GetLocalHandlers(ev->GetName().ToString()); + }, + [](V8ResourceImpl* resource, const CEvent* e, std::vector>& args) { + auto ev = static_cast(e); + + V8Helpers::MValueArgsToV8(ev->GetArgs(), args); + } +); + +V8_EVENT_HANDLER serverScriptEvent( + EventType::SERVER_SCRIPT_EVENT, + [](V8ResourceImpl* resource, const CEvent* e) { + auto ev = static_cast(e); + return resource->GetRemoteHandlers(ev->GetName().ToString()); + }, + [](V8ResourceImpl* resource, const CEvent* e, std::vector>& args) { + auto ev = static_cast(e); + + V8Helpers::MValueArgsToV8(ev->GetArgs(), args); + } +); + +V8_EVENT_HANDLER webviewEvent( + EventType::WEB_VIEW_EVENT, + [](V8ResourceImpl* resource, const CEvent* e) { + auto ev = static_cast(e); + + return static_cast(resource)->GetWebViewHandlers(ev->GetTarget(), ev->GetName().ToString()); + }, + [](V8ResourceImpl* resource, const CEvent* e, std::vector>& args) { + auto ev = static_cast(e); + + V8Helpers::MValueArgsToV8(ev->GetArgs(), args); + } +); + +V8_EVENT_HANDLER webSocketEvent( + EventType::WEB_SOCKET_CLIENT_EVENT, + [](V8ResourceImpl* resource, const CEvent* e) { + auto ev = static_cast(e); + + return static_cast(resource)->GetWebSocketClientHandlers(ev->GetTarget(), ev->GetName().ToString()); + }, + [](V8ResourceImpl* resource, const CEvent* e, std::vector>& args) { + auto ev = static_cast(e); + + V8Helpers::MValueArgsToV8(ev->GetArgs(), args); + } +); + +V8_EVENT_HANDLER audioEvent( + EventType::AUDIO_EVENT, + [](V8ResourceImpl* resource, const CEvent* e) { + auto ev = static_cast(e); + + return static_cast(resource)->GetAudioHandlers(ev->GetTarget(), ev->GetName().ToString()); + }, + [](V8ResourceImpl* resource, const CEvent* e, std::vector>& args) { + auto ev = static_cast(e); + + V8Helpers::MValueArgsToV8(ev->GetArgs(), args); + } +); + +V8_EVENT_HANDLER keyboardEvent( + EventType::KEYBOARD_EVENT, + [](V8ResourceImpl* resource, const CEvent* e) { + auto ev = static_cast(e); + if (ev->GetKeyState() == alt::CKeyboardEvent::KeyState::UP) + return resource->GetLocalHandlers("keyup"); + else if (ev->GetKeyState() == alt::CKeyboardEvent::KeyState::DOWN) + return resource->GetLocalHandlers("keydown"); + else + { + Log::Error << "Unhandled keystate in keyboard event handler: " << (int)ev->GetKeyState() << Log::Endl; + return std::vector(); + } + }, + [](V8ResourceImpl* resource, const CEvent* e, std::vector>& args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetKeyCode())); + } +); + +V8_LOCAL_EVENT_HANDLER render( + EventType::RENDER, + "render", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + }); + +V8_LOCAL_EVENT_HANDLER connectionComplete( + EventType::CONNECTION_COMPLETE, + "connectionComplete", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + }); + +V8_LOCAL_EVENT_HANDLER disconnect( + EventType::DISCONNECT_EVENT, + "disconnect", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + }); diff --git a/client/src/events/Meta.cpp b/client/src/events/Meta.cpp new file mode 100644 index 00000000..e5da1171 --- /dev/null +++ b/client/src/events/Meta.cpp @@ -0,0 +1,62 @@ +#include "V8ResourceImpl.h" +#include "V8Helpers.h" + +#include "cpp-sdk/events/CSyncedMetaDataChangeEvent.h" +#include "cpp-sdk/events/CStreamSyncedMetaDataChangeEvent.h" +#include "cpp-sdk/events/CGlobalSyncedMetaDataChangeEvent.h" +#include "cpp-sdk/events/CGlobalMetaDataChangeEvent.h" + +#include "cpp-sdk/SDK.h" + +using alt::CEvent; +using EventType = CEvent::Type; + +V8_LOCAL_EVENT_HANDLER syncedMetaChange( + EventType::SYNCED_META_CHANGE, + "syncedMetaChange", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(resource->GetOrCreateEntity(ev->GetTarget().Get())->GetJSVal(isolate)); + args.push_back(V8_NEW_STRING(ev->GetKey().CStr())); + args.push_back(V8Helpers::MValueToV8(ev->GetVal())); + args.push_back(V8Helpers::MValueToV8(ev->GetOldVal())); + }); + +V8_LOCAL_EVENT_HANDLER streamSyncedMetaChange( + EventType::STREAM_SYNCED_META_CHANGE, + "streamSyncedMetaChange", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(resource->GetOrCreateEntity(ev->GetTarget().Get())->GetJSVal(isolate)); + args.push_back(V8_NEW_STRING(ev->GetKey().CStr())); + args.push_back(V8Helpers::MValueToV8(ev->GetVal())); + args.push_back(V8Helpers::MValueToV8(ev->GetOldVal())); + }); + +V8_LOCAL_EVENT_HANDLER globalSyncedMetaChange( + EventType::GLOBAL_SYNCED_META_CHANGE, + "globalSyncedMetaChange", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(V8_NEW_STRING(ev->GetKey().CStr())); + args.push_back(V8Helpers::MValueToV8(ev->GetVal())); + args.push_back(V8Helpers::MValueToV8(ev->GetOldVal())); + }); + +V8_LOCAL_EVENT_HANDLER globalMetaChange( + EventType::GLOBAL_META_CHANGE, + "globalMetaChange", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(V8_NEW_STRING(ev->GetKey().CStr())); + args.push_back(V8Helpers::MValueToV8(ev->GetVal())); + args.push_back(V8Helpers::MValueToV8(ev->GetOldVal())); + }); \ No newline at end of file diff --git a/client/src/events/Resource.cpp b/client/src/events/Resource.cpp new file mode 100644 index 00000000..73535a5f --- /dev/null +++ b/client/src/events/Resource.cpp @@ -0,0 +1,41 @@ +#include "V8ResourceImpl.h" +#include "V8Helpers.h" + +#include "cpp-sdk/events/CResourceStartEvent.h" +#include "cpp-sdk/events/CResourceStopEvent.h" +#include "cpp-sdk/events/CResourceErrorEvent.h" + +#include "cpp-sdk/SDK.h" + +using alt::CEvent; +using EventType = CEvent::Type; + +V8_LOCAL_EVENT_HANDLER anyResourceStart( + EventType::RESOURCE_START, + "anyResourceStart", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(V8_NEW_STRING(ev->GetResource()->GetName().CStr())); + }); + +V8_LOCAL_EVENT_HANDLER anyResourceStop( + EventType::RESOURCE_STOP, + "anyResourceStop", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(V8_NEW_STRING(ev->GetResource()->GetName().CStr())); + }); + +V8_LOCAL_EVENT_HANDLER anyResourceError( + EventType::RESOURCE_ERROR, + "anyResourceError", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(V8_NEW_STRING(ev->GetResource()->GetName().CStr())); + }); \ No newline at end of file diff --git a/client/src/events/Vehicle.cpp b/client/src/events/Vehicle.cpp new file mode 100644 index 00000000..ebc43309 --- /dev/null +++ b/client/src/events/Vehicle.cpp @@ -0,0 +1,45 @@ +#include "V8ResourceImpl.h" +#include "V8Helpers.h" + +#include "cpp-sdk/events/CPlayerEnterVehicleEvent.h" +#include "cpp-sdk/events/CPlayerLeaveVehicleEvent.h" +#include "cpp-sdk/events/CPlayerChangeVehicleSeatEvent.h" + +#include "cpp-sdk/SDK.h" + +using alt::CEvent; +using EventType = CEvent::Type; + +V8_LOCAL_EVENT_HANDLER enteredVehicle( + EventType::PLAYER_ENTER_VEHICLE, + "enteredVehicle", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(resource->GetOrCreateEntity(ev->GetTarget().Get())->GetJSVal(isolate)); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetSeat())); + }); + +V8_LOCAL_EVENT_HANDLER leftVehicle( + EventType::PLAYER_LEAVE_VEHICLE, + "leftVehicle", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(resource->GetOrCreateEntity(ev->GetTarget().Get())->GetJSVal(isolate)); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetSeat())); + }); + +V8_LOCAL_EVENT_HANDLER changedVehicleSeat( + EventType::PLAYER_CHANGE_VEHICLE_SEAT, + "changedVehicleSeat", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(resource->GetOrCreateEntity(ev->GetTarget().Get())->GetJSVal(isolate)); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetOldSeat())); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetNewSeat())); + }); diff --git a/client/src/inspector/CV8InspectorChannel.h b/client/src/inspector/CV8InspectorChannel.h new file mode 100644 index 00000000..871e610d --- /dev/null +++ b/client/src/inspector/CV8InspectorChannel.h @@ -0,0 +1,24 @@ +#pragma once + +#include "v8-inspector.h" + +namespace alt +{ + class CV8InspectorChannel : public v8_inspector::V8Inspector::Channel + { + void sendResponse(int callId, std::unique_ptr message) + { + Log::Debug << __FUNCTION__ << callId << message->string().characters8(); + } + + void sendNotification(std::unique_ptr message) + { + Log::Debug << __FUNCTION__ << message->string().characters8(); + } + + void flushProtocolNotifications() + { + Log::Debug << __FUNCTION__; + } + }; +} // namespace alt diff --git a/client/src/inspector/CV8InspectorClient.h b/client/src/inspector/CV8InspectorClient.h new file mode 100644 index 00000000..cbd22c37 --- /dev/null +++ b/client/src/inspector/CV8InspectorClient.h @@ -0,0 +1,11 @@ +#pragma once + +#include "v8-inspector.h" + +namespace alt +{ + class CV8InspectorClient : public v8_inspector::V8InspectorClient + { + + }; +} diff --git a/client/src/main.cpp b/client/src/main.cpp new file mode 100644 index 00000000..6452a299 --- /dev/null +++ b/client/src/main.cpp @@ -0,0 +1,71 @@ +#include "cpp-sdk/SDK.h" +#include "CV8ScriptRuntime.h" +#include "Log.h" + +#ifdef ALTV_JS_SHARED +#define ALTV_JS_EXPORT extern "C" __declspec(dllexport) +#else +#define ALTV_JS_EXPORT extern "C" +#endif + +static void HeapCommand(alt::Array, void* runtime) +{ + static_cast(runtime)->HeapBenchmark(); +} + +static void TimersCommand(alt::Array, void* runtime) +{ + auto resources = static_cast(runtime)->GetResources(); + Log::Info << "================ Timer info =================" << Log::Endl; + for(auto resource : resources) + { + resource->TimerBenchmark(); + } + Log::Info << "======================================================" << Log::Endl; +} + +static void ClientJSCommand(alt::Array args, void*) +{ + if (args.GetSize() > 0 && args[0] == "--version") + { + Log::Colored << "~ly~cpp-sdk: v" << alt::ICore::SDK_VERSION << Log::Endl; + Log::Colored << "~ly~" << u8"Copyright © 2020 altMP team." << Log::Endl; + + Log::Colored << "~ly~v8: v" << V8_MAJOR_VERSION << "." << V8_MINOR_VERSION << Log::Endl; + Log::Colored << "~ly~" << u8"Copyright © 2014 The V8 project authors." << Log::Endl; + } + else if (args.GetSize() > 0 && args[0] == "--help") + { + Log::Colored << "~y~Usage: ~w~js-module [options]" << Log::Endl; + Log::Colored << "~y~Options:" << Log::Endl; + Log::Colored << " ~ly~--help ~w~- this message." << Log::Endl; + Log::Colored << " ~ly~--version ~w~- version info." << Log::Endl; + } + else + { + Log::Colored << "~y~Usage: ~w~js-module [options]" << Log::Endl; + Log::Colored << " Use: ~ly~\"js-module --help\" ~w~for more info" << Log::Endl; + } +} + +ALTV_JS_EXPORT void CreateScriptRuntime(alt::ICore *core) +{ + alt::ICore::SetInstance(core); + auto& runtime = CV8ScriptRuntime::Instance(); + core->RegisterScriptRuntime("js", &runtime); + + // Commands + core->SubscribeCommand("heap", &HeapCommand, &runtime); + core->SubscribeCommand("timers", &TimersCommand, &runtime); + core->SubscribeCommand("js-module", &ClientJSCommand); +} + +ALTV_JS_EXPORT const char* GetType() +{ + return "js"; +} + +ALTV_JS_EXPORT uint32_t GetSDKVersion() +{ + return alt::ICore::SDK_VERSION; +} diff --git a/client/tools/deinit-cppsdk.bat b/client/tools/deinit-cppsdk.bat new file mode 100644 index 00000000..843a061b --- /dev/null +++ b/client/tools/deinit-cppsdk.bat @@ -0,0 +1,6 @@ +@echo off + +if EXIST deps/cpp-sdk/cpp-sdk/SDK.h ( + echo alt:V JS - Using external cpp-sdk, deiniting local cpp-sdk + git submodule deinit -f deps/cpp-sdk/cpp-sdk +) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..1a1aebed --- /dev/null +++ b/docs/README.md @@ -0,0 +1 @@ +# WIP \ No newline at end of file diff --git a/.gitignore b/server/.gitignore similarity index 100% rename from .gitignore rename to server/.gitignore diff --git a/CMakeLists.txt b/server/CMakeLists.txt similarity index 95% rename from CMakeLists.txt rename to server/CMakeLists.txt index 955de70a..00c78ad3 100644 --- a/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -33,6 +33,7 @@ add_definitions(-DHAVE_INSPECTOR=1) add_definitions(-DALT_SERVER_API) file(GLOB_RECURSE PROJECT_SOURCE_FILES "src/*.h" "src/*.hpp" "src/*.cpp") +file(GLOB_RECURSE PROJECT_SHARED_FILES "../shared/*.h" "../shared/*.cpp") macro(GroupSources curdir groupindex) file(GLOB children RELATIVE ${curdir} ${curdir}/*) @@ -50,14 +51,15 @@ macro(GroupSources curdir groupindex) endmacro() GroupSources(${PROJECT_SOURCE_DIR}/src "Source Files") +GroupSources("../shared" "Shared Files") include_directories( src - src/api - src/fivenet-shared/src deps/nodejs/include deps/nodejs/deps/v8/include deps/nodejs/deps/uv/include + ../shared + ../shared/deps ) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -121,6 +123,7 @@ link_directories( add_library( ${PROJECT_NAME} SHARED ${PROJECT_SOURCE_FILES} + ${PROJECT_SHARED_FILES} ) if (WIN32) diff --git a/LICENSE b/server/LICENSE similarity index 100% rename from LICENSE rename to server/LICENSE diff --git a/server/README.md b/server/README.md new file mode 100644 index 00000000..36d01f20 --- /dev/null +++ b/server/README.md @@ -0,0 +1,27 @@ +# altv-js-module +JS module for alt:V Multiplayer. Powered by NodeJS & v8 + +## Troubleshooting +**Problem**: I want to build a native Node.js addon with +[node-gyp](https://github.com/nodejs/node-gyp) but the alt:V server can not load +it and responds with following error: +``` +./altv-server: symbol lookup error: /usr/share/addon/binding.node: undefined symbol: node_module_register +``` +**Solution**: Add a C++ file to your project with following content and include +it the ```sources``` in your ```binding.gyp``` file. +```cpp +#include +#include + +extern "C" NODE_EXTERN void node_module_register(void* mod) { + auto base_ptr = dlopen("libnode.so.72", RTLD_NOW | RTLD_GLOBAL); + if (base_ptr == nullptr) { + return; + } + auto register_func = reinterpret_cast(dlsym(base_ptr, "node_module_register")); + if (register_func == nullptr) { + return; + } + register_func(mod); +} diff --git a/build.bat b/server/build.bat similarity index 73% rename from build.bat rename to server/build.bat index dbef7704..881a036d 100644 --- a/build.bat +++ b/server/build.bat @@ -1,7 +1,7 @@ @echo off mkdir build pushd build -cmake -G"Visual Studio 16" -A x64 .. +cmake -G"Visual Studio 16" -A x64 -DJS_MODULE_VERSION=DEV .. cmake --build . --config Release popd diff --git a/build.sh b/server/build.sh similarity index 54% rename from build.sh rename to server/build.sh index 2c20e915..8cf631fa 100644 --- a/build.sh +++ b/server/build.sh @@ -1,5 +1,5 @@ mkdir build-linux cd build-linux -cmake -DCMAKE_BUILD_TYPE=Release .. +cmake -DCMAKE_BUILD_TYPE=Release -DJS_MODULE_VERSION=DEV .. cmake --build . --config Release cd .. diff --git a/deps/nodejs/deps/uv/include/uv.h b/server/deps/nodejs/deps/uv/include/uv.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv.h rename to server/deps/nodejs/deps/uv/include/uv.h diff --git a/deps/nodejs/deps/uv/include/uv/aix.h b/server/deps/nodejs/deps/uv/include/uv/aix.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/aix.h rename to server/deps/nodejs/deps/uv/include/uv/aix.h diff --git a/deps/nodejs/deps/uv/include/uv/android-ifaddrs.h b/server/deps/nodejs/deps/uv/include/uv/android-ifaddrs.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/android-ifaddrs.h rename to server/deps/nodejs/deps/uv/include/uv/android-ifaddrs.h diff --git a/deps/nodejs/deps/uv/include/uv/bsd.h b/server/deps/nodejs/deps/uv/include/uv/bsd.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/bsd.h rename to server/deps/nodejs/deps/uv/include/uv/bsd.h diff --git a/deps/nodejs/deps/uv/include/uv/darwin.h b/server/deps/nodejs/deps/uv/include/uv/darwin.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/darwin.h rename to server/deps/nodejs/deps/uv/include/uv/darwin.h diff --git a/deps/nodejs/deps/uv/include/uv/errno.h b/server/deps/nodejs/deps/uv/include/uv/errno.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/errno.h rename to server/deps/nodejs/deps/uv/include/uv/errno.h diff --git a/deps/nodejs/deps/uv/include/uv/linux.h b/server/deps/nodejs/deps/uv/include/uv/linux.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/linux.h rename to server/deps/nodejs/deps/uv/include/uv/linux.h diff --git a/deps/nodejs/deps/uv/include/uv/os390.h b/server/deps/nodejs/deps/uv/include/uv/os390.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/os390.h rename to server/deps/nodejs/deps/uv/include/uv/os390.h diff --git a/deps/nodejs/deps/uv/include/uv/posix.h b/server/deps/nodejs/deps/uv/include/uv/posix.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/posix.h rename to server/deps/nodejs/deps/uv/include/uv/posix.h diff --git a/deps/nodejs/deps/uv/include/uv/stdint-msvc2008.h b/server/deps/nodejs/deps/uv/include/uv/stdint-msvc2008.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/stdint-msvc2008.h rename to server/deps/nodejs/deps/uv/include/uv/stdint-msvc2008.h diff --git a/deps/nodejs/deps/uv/include/uv/sunos.h b/server/deps/nodejs/deps/uv/include/uv/sunos.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/sunos.h rename to server/deps/nodejs/deps/uv/include/uv/sunos.h diff --git a/deps/nodejs/deps/uv/include/uv/threadpool.h b/server/deps/nodejs/deps/uv/include/uv/threadpool.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/threadpool.h rename to server/deps/nodejs/deps/uv/include/uv/threadpool.h diff --git a/deps/nodejs/deps/uv/include/uv/tree.h b/server/deps/nodejs/deps/uv/include/uv/tree.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/tree.h rename to server/deps/nodejs/deps/uv/include/uv/tree.h diff --git a/deps/nodejs/deps/uv/include/uv/unix.h b/server/deps/nodejs/deps/uv/include/uv/unix.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/unix.h rename to server/deps/nodejs/deps/uv/include/uv/unix.h diff --git a/deps/nodejs/deps/uv/include/uv/version.h b/server/deps/nodejs/deps/uv/include/uv/version.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/version.h rename to server/deps/nodejs/deps/uv/include/uv/version.h diff --git a/deps/nodejs/deps/uv/include/uv/win.h b/server/deps/nodejs/deps/uv/include/uv/win.h similarity index 100% rename from deps/nodejs/deps/uv/include/uv/win.h rename to server/deps/nodejs/deps/uv/include/uv/win.h diff --git a/deps/nodejs/deps/v8/include/APIDesign.md b/server/deps/nodejs/deps/v8/include/APIDesign.md similarity index 100% rename from deps/nodejs/deps/v8/include/APIDesign.md rename to server/deps/nodejs/deps/v8/include/APIDesign.md diff --git a/deps/nodejs/deps/v8/include/DEPS b/server/deps/nodejs/deps/v8/include/DEPS similarity index 100% rename from deps/nodejs/deps/v8/include/DEPS rename to server/deps/nodejs/deps/v8/include/DEPS diff --git a/deps/nodejs/deps/v8/include/OWNERS b/server/deps/nodejs/deps/v8/include/OWNERS similarity index 100% rename from deps/nodejs/deps/v8/include/OWNERS rename to server/deps/nodejs/deps/v8/include/OWNERS diff --git a/deps/nodejs/deps/v8/include/cppgc/DEPS b/server/deps/nodejs/deps/v8/include/cppgc/DEPS similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/DEPS rename to server/deps/nodejs/deps/v8/include/cppgc/DEPS diff --git a/deps/nodejs/deps/v8/include/cppgc/README.md b/server/deps/nodejs/deps/v8/include/cppgc/README.md similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/README.md rename to server/deps/nodejs/deps/v8/include/cppgc/README.md diff --git a/deps/nodejs/deps/v8/include/cppgc/allocation.h b/server/deps/nodejs/deps/v8/include/cppgc/allocation.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/allocation.h rename to server/deps/nodejs/deps/v8/include/cppgc/allocation.h diff --git a/deps/nodejs/deps/v8/include/cppgc/common.h b/server/deps/nodejs/deps/v8/include/cppgc/common.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/common.h rename to server/deps/nodejs/deps/v8/include/cppgc/common.h diff --git a/deps/nodejs/deps/v8/include/cppgc/custom-space.h b/server/deps/nodejs/deps/v8/include/cppgc/custom-space.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/custom-space.h rename to server/deps/nodejs/deps/v8/include/cppgc/custom-space.h diff --git a/deps/nodejs/deps/v8/include/cppgc/garbage-collected.h b/server/deps/nodejs/deps/v8/include/cppgc/garbage-collected.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/garbage-collected.h rename to server/deps/nodejs/deps/v8/include/cppgc/garbage-collected.h diff --git a/deps/nodejs/deps/v8/include/cppgc/heap.h b/server/deps/nodejs/deps/v8/include/cppgc/heap.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/heap.h rename to server/deps/nodejs/deps/v8/include/cppgc/heap.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/accessors.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/accessors.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/accessors.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/accessors.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/api-constants.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/api-constants.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/api-constants.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/api-constants.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/compiler-specific.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/compiler-specific.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/compiler-specific.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/compiler-specific.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/finalizer-trait.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/finalizer-trait.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/finalizer-trait.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/finalizer-trait.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/gc-info.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/gc-info.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/gc-info.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/gc-info.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/logging.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/logging.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/logging.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/logging.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/persistent-node.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/persistent-node.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/persistent-node.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/persistent-node.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/pointer-policies.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/pointer-policies.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/pointer-policies.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/pointer-policies.h diff --git a/deps/nodejs/deps/v8/include/cppgc/internal/prefinalizer-handler.h b/server/deps/nodejs/deps/v8/include/cppgc/internal/prefinalizer-handler.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/internal/prefinalizer-handler.h rename to server/deps/nodejs/deps/v8/include/cppgc/internal/prefinalizer-handler.h diff --git a/deps/nodejs/deps/v8/include/cppgc/liveness-broker.h b/server/deps/nodejs/deps/v8/include/cppgc/liveness-broker.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/liveness-broker.h rename to server/deps/nodejs/deps/v8/include/cppgc/liveness-broker.h diff --git a/deps/nodejs/deps/v8/include/cppgc/macros.h b/server/deps/nodejs/deps/v8/include/cppgc/macros.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/macros.h rename to server/deps/nodejs/deps/v8/include/cppgc/macros.h diff --git a/deps/nodejs/deps/v8/include/cppgc/member.h b/server/deps/nodejs/deps/v8/include/cppgc/member.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/member.h rename to server/deps/nodejs/deps/v8/include/cppgc/member.h diff --git a/deps/nodejs/deps/v8/include/cppgc/persistent.h b/server/deps/nodejs/deps/v8/include/cppgc/persistent.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/persistent.h rename to server/deps/nodejs/deps/v8/include/cppgc/persistent.h diff --git a/deps/nodejs/deps/v8/include/cppgc/platform.h b/server/deps/nodejs/deps/v8/include/cppgc/platform.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/platform.h rename to server/deps/nodejs/deps/v8/include/cppgc/platform.h diff --git a/deps/nodejs/deps/v8/include/cppgc/prefinalizer.h b/server/deps/nodejs/deps/v8/include/cppgc/prefinalizer.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/prefinalizer.h rename to server/deps/nodejs/deps/v8/include/cppgc/prefinalizer.h diff --git a/deps/nodejs/deps/v8/include/cppgc/source-location.h b/server/deps/nodejs/deps/v8/include/cppgc/source-location.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/source-location.h rename to server/deps/nodejs/deps/v8/include/cppgc/source-location.h diff --git a/deps/nodejs/deps/v8/include/cppgc/trace-trait.h b/server/deps/nodejs/deps/v8/include/cppgc/trace-trait.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/trace-trait.h rename to server/deps/nodejs/deps/v8/include/cppgc/trace-trait.h diff --git a/deps/nodejs/deps/v8/include/cppgc/type-traits.h b/server/deps/nodejs/deps/v8/include/cppgc/type-traits.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/type-traits.h rename to server/deps/nodejs/deps/v8/include/cppgc/type-traits.h diff --git a/deps/nodejs/deps/v8/include/cppgc/visitor.h b/server/deps/nodejs/deps/v8/include/cppgc/visitor.h similarity index 100% rename from deps/nodejs/deps/v8/include/cppgc/visitor.h rename to server/deps/nodejs/deps/v8/include/cppgc/visitor.h diff --git a/deps/nodejs/deps/v8/include/js_protocol-1.2.json b/server/deps/nodejs/deps/v8/include/js_protocol-1.2.json similarity index 100% rename from deps/nodejs/deps/v8/include/js_protocol-1.2.json rename to server/deps/nodejs/deps/v8/include/js_protocol-1.2.json diff --git a/deps/nodejs/deps/v8/include/js_protocol-1.3.json b/server/deps/nodejs/deps/v8/include/js_protocol-1.3.json similarity index 100% rename from deps/nodejs/deps/v8/include/js_protocol-1.3.json rename to server/deps/nodejs/deps/v8/include/js_protocol-1.3.json diff --git a/deps/nodejs/deps/v8/include/js_protocol.pdl b/server/deps/nodejs/deps/v8/include/js_protocol.pdl similarity index 100% rename from deps/nodejs/deps/v8/include/js_protocol.pdl rename to server/deps/nodejs/deps/v8/include/js_protocol.pdl diff --git a/deps/nodejs/deps/v8/include/libplatform/DEPS b/server/deps/nodejs/deps/v8/include/libplatform/DEPS similarity index 100% rename from deps/nodejs/deps/v8/include/libplatform/DEPS rename to server/deps/nodejs/deps/v8/include/libplatform/DEPS diff --git a/deps/nodejs/deps/v8/include/libplatform/libplatform-export.h b/server/deps/nodejs/deps/v8/include/libplatform/libplatform-export.h similarity index 100% rename from deps/nodejs/deps/v8/include/libplatform/libplatform-export.h rename to server/deps/nodejs/deps/v8/include/libplatform/libplatform-export.h diff --git a/deps/nodejs/deps/v8/include/libplatform/libplatform.h b/server/deps/nodejs/deps/v8/include/libplatform/libplatform.h similarity index 100% rename from deps/nodejs/deps/v8/include/libplatform/libplatform.h rename to server/deps/nodejs/deps/v8/include/libplatform/libplatform.h diff --git a/deps/nodejs/deps/v8/include/libplatform/v8-tracing.h b/server/deps/nodejs/deps/v8/include/libplatform/v8-tracing.h similarity index 100% rename from deps/nodejs/deps/v8/include/libplatform/v8-tracing.h rename to server/deps/nodejs/deps/v8/include/libplatform/v8-tracing.h diff --git a/deps/nodejs/deps/v8/include/v8-fast-api-calls.h b/server/deps/nodejs/deps/v8/include/v8-fast-api-calls.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-fast-api-calls.h rename to server/deps/nodejs/deps/v8/include/v8-fast-api-calls.h diff --git a/deps/nodejs/deps/v8/include/v8-inspector-protocol.h b/server/deps/nodejs/deps/v8/include/v8-inspector-protocol.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-inspector-protocol.h rename to server/deps/nodejs/deps/v8/include/v8-inspector-protocol.h diff --git a/deps/nodejs/deps/v8/include/v8-inspector.h b/server/deps/nodejs/deps/v8/include/v8-inspector.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-inspector.h rename to server/deps/nodejs/deps/v8/include/v8-inspector.h diff --git a/deps/nodejs/deps/v8/include/v8-internal.h b/server/deps/nodejs/deps/v8/include/v8-internal.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-internal.h rename to server/deps/nodejs/deps/v8/include/v8-internal.h diff --git a/deps/nodejs/deps/v8/include/v8-platform.h b/server/deps/nodejs/deps/v8/include/v8-platform.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-platform.h rename to server/deps/nodejs/deps/v8/include/v8-platform.h diff --git a/deps/nodejs/deps/v8/include/v8-profiler.h b/server/deps/nodejs/deps/v8/include/v8-profiler.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-profiler.h rename to server/deps/nodejs/deps/v8/include/v8-profiler.h diff --git a/deps/nodejs/deps/v8/include/v8-util.h b/server/deps/nodejs/deps/v8/include/v8-util.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-util.h rename to server/deps/nodejs/deps/v8/include/v8-util.h diff --git a/deps/nodejs/deps/v8/include/v8-value-serializer-version.h b/server/deps/nodejs/deps/v8/include/v8-value-serializer-version.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-value-serializer-version.h rename to server/deps/nodejs/deps/v8/include/v8-value-serializer-version.h diff --git a/deps/nodejs/deps/v8/include/v8-version-string.h b/server/deps/nodejs/deps/v8/include/v8-version-string.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-version-string.h rename to server/deps/nodejs/deps/v8/include/v8-version-string.h diff --git a/deps/nodejs/deps/v8/include/v8-version.h b/server/deps/nodejs/deps/v8/include/v8-version.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-version.h rename to server/deps/nodejs/deps/v8/include/v8-version.h diff --git a/deps/nodejs/deps/v8/include/v8-wasm-trap-handler-posix.h b/server/deps/nodejs/deps/v8/include/v8-wasm-trap-handler-posix.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-wasm-trap-handler-posix.h rename to server/deps/nodejs/deps/v8/include/v8-wasm-trap-handler-posix.h diff --git a/deps/nodejs/deps/v8/include/v8-wasm-trap-handler-win.h b/server/deps/nodejs/deps/v8/include/v8-wasm-trap-handler-win.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8-wasm-trap-handler-win.h rename to server/deps/nodejs/deps/v8/include/v8-wasm-trap-handler-win.h diff --git a/deps/nodejs/deps/v8/include/v8.h b/server/deps/nodejs/deps/v8/include/v8.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8.h rename to server/deps/nodejs/deps/v8/include/v8.h diff --git a/deps/nodejs/deps/v8/include/v8config.h b/server/deps/nodejs/deps/v8/include/v8config.h similarity index 100% rename from deps/nodejs/deps/v8/include/v8config.h rename to server/deps/nodejs/deps/v8/include/v8config.h diff --git a/deps/nodejs/include/aliased_buffer.h b/server/deps/nodejs/include/aliased_buffer.h similarity index 100% rename from deps/nodejs/include/aliased_buffer.h rename to server/deps/nodejs/include/aliased_buffer.h diff --git a/deps/nodejs/include/aliased_struct-inl.h b/server/deps/nodejs/include/aliased_struct-inl.h similarity index 100% rename from deps/nodejs/include/aliased_struct-inl.h rename to server/deps/nodejs/include/aliased_struct-inl.h diff --git a/deps/nodejs/include/aliased_struct.h b/server/deps/nodejs/include/aliased_struct.h similarity index 100% rename from deps/nodejs/include/aliased_struct.h rename to server/deps/nodejs/include/aliased_struct.h diff --git a/deps/nodejs/include/allocated_buffer-inl.h b/server/deps/nodejs/include/allocated_buffer-inl.h similarity index 100% rename from deps/nodejs/include/allocated_buffer-inl.h rename to server/deps/nodejs/include/allocated_buffer-inl.h diff --git a/deps/nodejs/include/allocated_buffer.h b/server/deps/nodejs/include/allocated_buffer.h similarity index 100% rename from deps/nodejs/include/allocated_buffer.h rename to server/deps/nodejs/include/allocated_buffer.h diff --git a/deps/nodejs/include/async_wrap-inl.h b/server/deps/nodejs/include/async_wrap-inl.h similarity index 100% rename from deps/nodejs/include/async_wrap-inl.h rename to server/deps/nodejs/include/async_wrap-inl.h diff --git a/deps/nodejs/include/async_wrap.h b/server/deps/nodejs/include/async_wrap.h similarity index 100% rename from deps/nodejs/include/async_wrap.h rename to server/deps/nodejs/include/async_wrap.h diff --git a/deps/nodejs/include/base64-inl.h b/server/deps/nodejs/include/base64-inl.h similarity index 100% rename from deps/nodejs/include/base64-inl.h rename to server/deps/nodejs/include/base64-inl.h diff --git a/deps/nodejs/include/base64.h b/server/deps/nodejs/include/base64.h similarity index 100% rename from deps/nodejs/include/base64.h rename to server/deps/nodejs/include/base64.h diff --git a/deps/nodejs/include/base_object-inl.h b/server/deps/nodejs/include/base_object-inl.h similarity index 100% rename from deps/nodejs/include/base_object-inl.h rename to server/deps/nodejs/include/base_object-inl.h diff --git a/deps/nodejs/include/base_object.h b/server/deps/nodejs/include/base_object.h similarity index 100% rename from deps/nodejs/include/base_object.h rename to server/deps/nodejs/include/base_object.h diff --git a/deps/nodejs/include/callback_queue-inl.h b/server/deps/nodejs/include/callback_queue-inl.h similarity index 100% rename from deps/nodejs/include/callback_queue-inl.h rename to server/deps/nodejs/include/callback_queue-inl.h diff --git a/deps/nodejs/include/callback_queue.h b/server/deps/nodejs/include/callback_queue.h similarity index 100% rename from deps/nodejs/include/callback_queue.h rename to server/deps/nodejs/include/callback_queue.h diff --git a/deps/nodejs/include/connect_wrap.h b/server/deps/nodejs/include/connect_wrap.h similarity index 100% rename from deps/nodejs/include/connect_wrap.h rename to server/deps/nodejs/include/connect_wrap.h diff --git a/deps/nodejs/include/connection_wrap.h b/server/deps/nodejs/include/connection_wrap.h similarity index 100% rename from deps/nodejs/include/connection_wrap.h rename to server/deps/nodejs/include/connection_wrap.h diff --git a/deps/nodejs/include/debug_utils-inl.h b/server/deps/nodejs/include/debug_utils-inl.h similarity index 100% rename from deps/nodejs/include/debug_utils-inl.h rename to server/deps/nodejs/include/debug_utils-inl.h diff --git a/deps/nodejs/include/debug_utils.h b/server/deps/nodejs/include/debug_utils.h similarity index 100% rename from deps/nodejs/include/debug_utils.h rename to server/deps/nodejs/include/debug_utils.h diff --git a/deps/nodejs/include/diagnosticfilename-inl.h b/server/deps/nodejs/include/diagnosticfilename-inl.h similarity index 100% rename from deps/nodejs/include/diagnosticfilename-inl.h rename to server/deps/nodejs/include/diagnosticfilename-inl.h diff --git a/deps/nodejs/include/env-inl.h b/server/deps/nodejs/include/env-inl.h similarity index 100% rename from deps/nodejs/include/env-inl.h rename to server/deps/nodejs/include/env-inl.h diff --git a/deps/nodejs/include/env.h b/server/deps/nodejs/include/env.h similarity index 100% rename from deps/nodejs/include/env.h rename to server/deps/nodejs/include/env.h diff --git a/deps/nodejs/include/handle_wrap.h b/server/deps/nodejs/include/handle_wrap.h similarity index 100% rename from deps/nodejs/include/handle_wrap.h rename to server/deps/nodejs/include/handle_wrap.h diff --git a/deps/nodejs/include/histogram-inl.h b/server/deps/nodejs/include/histogram-inl.h similarity index 100% rename from deps/nodejs/include/histogram-inl.h rename to server/deps/nodejs/include/histogram-inl.h diff --git a/deps/nodejs/include/histogram.h b/server/deps/nodejs/include/histogram.h similarity index 100% rename from deps/nodejs/include/histogram.h rename to server/deps/nodejs/include/histogram.h diff --git a/deps/nodejs/include/inspector/main_thread_interface.h b/server/deps/nodejs/include/inspector/main_thread_interface.h similarity index 100% rename from deps/nodejs/include/inspector/main_thread_interface.h rename to server/deps/nodejs/include/inspector/main_thread_interface.h diff --git a/deps/nodejs/include/inspector/node_string.h b/server/deps/nodejs/include/inspector/node_string.h similarity index 100% rename from deps/nodejs/include/inspector/node_string.h rename to server/deps/nodejs/include/inspector/node_string.h diff --git a/deps/nodejs/include/inspector/runtime_agent.h b/server/deps/nodejs/include/inspector/runtime_agent.h similarity index 100% rename from deps/nodejs/include/inspector/runtime_agent.h rename to server/deps/nodejs/include/inspector/runtime_agent.h diff --git a/deps/nodejs/include/inspector/tracing_agent.h b/server/deps/nodejs/include/inspector/tracing_agent.h similarity index 100% rename from deps/nodejs/include/inspector/tracing_agent.h rename to server/deps/nodejs/include/inspector/tracing_agent.h diff --git a/deps/nodejs/include/inspector/worker_agent.h b/server/deps/nodejs/include/inspector/worker_agent.h similarity index 100% rename from deps/nodejs/include/inspector/worker_agent.h rename to server/deps/nodejs/include/inspector/worker_agent.h diff --git a/deps/nodejs/include/inspector/worker_inspector.h b/server/deps/nodejs/include/inspector/worker_inspector.h similarity index 100% rename from deps/nodejs/include/inspector/worker_inspector.h rename to server/deps/nodejs/include/inspector/worker_inspector.h diff --git a/deps/nodejs/include/inspector_agent.h b/server/deps/nodejs/include/inspector_agent.h similarity index 100% rename from deps/nodejs/include/inspector_agent.h rename to server/deps/nodejs/include/inspector_agent.h diff --git a/deps/nodejs/include/inspector_io.h b/server/deps/nodejs/include/inspector_io.h similarity index 100% rename from deps/nodejs/include/inspector_io.h rename to server/deps/nodejs/include/inspector_io.h diff --git a/deps/nodejs/include/inspector_profiler.h b/server/deps/nodejs/include/inspector_profiler.h similarity index 100% rename from deps/nodejs/include/inspector_profiler.h rename to server/deps/nodejs/include/inspector_profiler.h diff --git a/deps/nodejs/include/inspector_socket.h b/server/deps/nodejs/include/inspector_socket.h similarity index 100% rename from deps/nodejs/include/inspector_socket.h rename to server/deps/nodejs/include/inspector_socket.h diff --git a/deps/nodejs/include/inspector_socket_server.h b/server/deps/nodejs/include/inspector_socket_server.h similarity index 100% rename from deps/nodejs/include/inspector_socket_server.h rename to server/deps/nodejs/include/inspector_socket_server.h diff --git a/deps/nodejs/include/js_native_api.h b/server/deps/nodejs/include/js_native_api.h similarity index 100% rename from deps/nodejs/include/js_native_api.h rename to server/deps/nodejs/include/js_native_api.h diff --git a/deps/nodejs/include/js_native_api_types.h b/server/deps/nodejs/include/js_native_api_types.h similarity index 100% rename from deps/nodejs/include/js_native_api_types.h rename to server/deps/nodejs/include/js_native_api_types.h diff --git a/deps/nodejs/include/js_native_api_v8.h b/server/deps/nodejs/include/js_native_api_v8.h similarity index 100% rename from deps/nodejs/include/js_native_api_v8.h rename to server/deps/nodejs/include/js_native_api_v8.h diff --git a/deps/nodejs/include/js_native_api_v8_internals.h b/server/deps/nodejs/include/js_native_api_v8_internals.h similarity index 100% rename from deps/nodejs/include/js_native_api_v8_internals.h rename to server/deps/nodejs/include/js_native_api_v8_internals.h diff --git a/deps/nodejs/include/js_stream.h b/server/deps/nodejs/include/js_stream.h similarity index 100% rename from deps/nodejs/include/js_stream.h rename to server/deps/nodejs/include/js_stream.h diff --git a/deps/nodejs/include/json_utils.h b/server/deps/nodejs/include/json_utils.h similarity index 100% rename from deps/nodejs/include/json_utils.h rename to server/deps/nodejs/include/json_utils.h diff --git a/deps/nodejs/include/large_pages/node_large_page.h b/server/deps/nodejs/include/large_pages/node_large_page.h similarity index 100% rename from deps/nodejs/include/large_pages/node_large_page.h rename to server/deps/nodejs/include/large_pages/node_large_page.h diff --git a/deps/nodejs/include/memory_tracker-inl.h b/server/deps/nodejs/include/memory_tracker-inl.h similarity index 100% rename from deps/nodejs/include/memory_tracker-inl.h rename to server/deps/nodejs/include/memory_tracker-inl.h diff --git a/deps/nodejs/include/memory_tracker.h b/server/deps/nodejs/include/memory_tracker.h similarity index 100% rename from deps/nodejs/include/memory_tracker.h rename to server/deps/nodejs/include/memory_tracker.h diff --git a/deps/nodejs/include/module_wrap.h b/server/deps/nodejs/include/module_wrap.h similarity index 100% rename from deps/nodejs/include/module_wrap.h rename to server/deps/nodejs/include/module_wrap.h diff --git a/deps/nodejs/include/node.h b/server/deps/nodejs/include/node.h similarity index 100% rename from deps/nodejs/include/node.h rename to server/deps/nodejs/include/node.h diff --git a/deps/nodejs/include/node_api.h b/server/deps/nodejs/include/node_api.h similarity index 100% rename from deps/nodejs/include/node_api.h rename to server/deps/nodejs/include/node_api.h diff --git a/deps/nodejs/include/node_api_types.h b/server/deps/nodejs/include/node_api_types.h similarity index 100% rename from deps/nodejs/include/node_api_types.h rename to server/deps/nodejs/include/node_api_types.h diff --git a/deps/nodejs/include/node_binding.h b/server/deps/nodejs/include/node_binding.h similarity index 100% rename from deps/nodejs/include/node_binding.h rename to server/deps/nodejs/include/node_binding.h diff --git a/deps/nodejs/include/node_buffer.h b/server/deps/nodejs/include/node_buffer.h similarity index 100% rename from deps/nodejs/include/node_buffer.h rename to server/deps/nodejs/include/node_buffer.h diff --git a/deps/nodejs/include/node_constants.h b/server/deps/nodejs/include/node_constants.h similarity index 100% rename from deps/nodejs/include/node_constants.h rename to server/deps/nodejs/include/node_constants.h diff --git a/deps/nodejs/include/node_context_data.h b/server/deps/nodejs/include/node_context_data.h similarity index 100% rename from deps/nodejs/include/node_context_data.h rename to server/deps/nodejs/include/node_context_data.h diff --git a/deps/nodejs/include/node_contextify.h b/server/deps/nodejs/include/node_contextify.h similarity index 100% rename from deps/nodejs/include/node_contextify.h rename to server/deps/nodejs/include/node_contextify.h diff --git a/deps/nodejs/include/node_crypto.h b/server/deps/nodejs/include/node_crypto.h similarity index 100% rename from deps/nodejs/include/node_crypto.h rename to server/deps/nodejs/include/node_crypto.h diff --git a/deps/nodejs/include/node_crypto_bio.h b/server/deps/nodejs/include/node_crypto_bio.h similarity index 100% rename from deps/nodejs/include/node_crypto_bio.h rename to server/deps/nodejs/include/node_crypto_bio.h diff --git a/deps/nodejs/include/node_crypto_clienthello-inl.h b/server/deps/nodejs/include/node_crypto_clienthello-inl.h similarity index 100% rename from deps/nodejs/include/node_crypto_clienthello-inl.h rename to server/deps/nodejs/include/node_crypto_clienthello-inl.h diff --git a/deps/nodejs/include/node_crypto_clienthello.h b/server/deps/nodejs/include/node_crypto_clienthello.h similarity index 100% rename from deps/nodejs/include/node_crypto_clienthello.h rename to server/deps/nodejs/include/node_crypto_clienthello.h diff --git a/deps/nodejs/include/node_crypto_common.h b/server/deps/nodejs/include/node_crypto_common.h similarity index 100% rename from deps/nodejs/include/node_crypto_common.h rename to server/deps/nodejs/include/node_crypto_common.h diff --git a/deps/nodejs/include/node_crypto_groups.h b/server/deps/nodejs/include/node_crypto_groups.h similarity index 100% rename from deps/nodejs/include/node_crypto_groups.h rename to server/deps/nodejs/include/node_crypto_groups.h diff --git a/deps/nodejs/include/node_dir.h b/server/deps/nodejs/include/node_dir.h similarity index 100% rename from deps/nodejs/include/node_dir.h rename to server/deps/nodejs/include/node_dir.h diff --git a/deps/nodejs/include/node_dtrace.h b/server/deps/nodejs/include/node_dtrace.h similarity index 100% rename from deps/nodejs/include/node_dtrace.h rename to server/deps/nodejs/include/node_dtrace.h diff --git a/deps/nodejs/include/node_errors.h b/server/deps/nodejs/include/node_errors.h similarity index 100% rename from deps/nodejs/include/node_errors.h rename to server/deps/nodejs/include/node_errors.h diff --git a/deps/nodejs/include/node_file-inl.h b/server/deps/nodejs/include/node_file-inl.h similarity index 100% rename from deps/nodejs/include/node_file-inl.h rename to server/deps/nodejs/include/node_file-inl.h diff --git a/deps/nodejs/include/node_file.h b/server/deps/nodejs/include/node_file.h similarity index 100% rename from deps/nodejs/include/node_file.h rename to server/deps/nodejs/include/node_file.h diff --git a/deps/nodejs/include/node_http2.h b/server/deps/nodejs/include/node_http2.h similarity index 100% rename from deps/nodejs/include/node_http2.h rename to server/deps/nodejs/include/node_http2.h diff --git a/deps/nodejs/include/node_http2_state.h b/server/deps/nodejs/include/node_http2_state.h similarity index 100% rename from deps/nodejs/include/node_http2_state.h rename to server/deps/nodejs/include/node_http2_state.h diff --git a/deps/nodejs/include/node_http_common-inl.h b/server/deps/nodejs/include/node_http_common-inl.h similarity index 100% rename from deps/nodejs/include/node_http_common-inl.h rename to server/deps/nodejs/include/node_http_common-inl.h diff --git a/deps/nodejs/include/node_http_common.h b/server/deps/nodejs/include/node_http_common.h similarity index 100% rename from deps/nodejs/include/node_http_common.h rename to server/deps/nodejs/include/node_http_common.h diff --git a/deps/nodejs/include/node_i18n.h b/server/deps/nodejs/include/node_i18n.h similarity index 100% rename from deps/nodejs/include/node_i18n.h rename to server/deps/nodejs/include/node_i18n.h diff --git a/deps/nodejs/include/node_internals.h b/server/deps/nodejs/include/node_internals.h similarity index 100% rename from deps/nodejs/include/node_internals.h rename to server/deps/nodejs/include/node_internals.h diff --git a/deps/nodejs/include/node_main_instance.h b/server/deps/nodejs/include/node_main_instance.h similarity index 100% rename from deps/nodejs/include/node_main_instance.h rename to server/deps/nodejs/include/node_main_instance.h diff --git a/deps/nodejs/include/node_mem-inl.h b/server/deps/nodejs/include/node_mem-inl.h similarity index 100% rename from deps/nodejs/include/node_mem-inl.h rename to server/deps/nodejs/include/node_mem-inl.h diff --git a/deps/nodejs/include/node_mem.h b/server/deps/nodejs/include/node_mem.h similarity index 100% rename from deps/nodejs/include/node_mem.h rename to server/deps/nodejs/include/node_mem.h diff --git a/deps/nodejs/include/node_messaging.h b/server/deps/nodejs/include/node_messaging.h similarity index 100% rename from deps/nodejs/include/node_messaging.h rename to server/deps/nodejs/include/node_messaging.h diff --git a/deps/nodejs/include/node_metadata.h b/server/deps/nodejs/include/node_metadata.h similarity index 100% rename from deps/nodejs/include/node_metadata.h rename to server/deps/nodejs/include/node_metadata.h diff --git a/deps/nodejs/include/node_mutex.h b/server/deps/nodejs/include/node_mutex.h similarity index 100% rename from deps/nodejs/include/node_mutex.h rename to server/deps/nodejs/include/node_mutex.h diff --git a/deps/nodejs/include/node_native_module.h b/server/deps/nodejs/include/node_native_module.h similarity index 100% rename from deps/nodejs/include/node_native_module.h rename to server/deps/nodejs/include/node_native_module.h diff --git a/deps/nodejs/include/node_native_module_env.h b/server/deps/nodejs/include/node_native_module_env.h similarity index 100% rename from deps/nodejs/include/node_native_module_env.h rename to server/deps/nodejs/include/node_native_module_env.h diff --git a/deps/nodejs/include/node_object_wrap.h b/server/deps/nodejs/include/node_object_wrap.h similarity index 100% rename from deps/nodejs/include/node_object_wrap.h rename to server/deps/nodejs/include/node_object_wrap.h diff --git a/deps/nodejs/include/node_options-inl.h b/server/deps/nodejs/include/node_options-inl.h similarity index 100% rename from deps/nodejs/include/node_options-inl.h rename to server/deps/nodejs/include/node_options-inl.h diff --git a/deps/nodejs/include/node_options.h b/server/deps/nodejs/include/node_options.h similarity index 100% rename from deps/nodejs/include/node_options.h rename to server/deps/nodejs/include/node_options.h diff --git a/deps/nodejs/include/node_perf.h b/server/deps/nodejs/include/node_perf.h similarity index 100% rename from deps/nodejs/include/node_perf.h rename to server/deps/nodejs/include/node_perf.h diff --git a/deps/nodejs/include/node_perf_common.h b/server/deps/nodejs/include/node_perf_common.h similarity index 100% rename from deps/nodejs/include/node_perf_common.h rename to server/deps/nodejs/include/node_perf_common.h diff --git a/deps/nodejs/include/node_platform.h b/server/deps/nodejs/include/node_platform.h similarity index 100% rename from deps/nodejs/include/node_platform.h rename to server/deps/nodejs/include/node_platform.h diff --git a/deps/nodejs/include/node_process.h b/server/deps/nodejs/include/node_process.h similarity index 100% rename from deps/nodejs/include/node_process.h rename to server/deps/nodejs/include/node_process.h diff --git a/deps/nodejs/include/node_report.h b/server/deps/nodejs/include/node_report.h similarity index 100% rename from deps/nodejs/include/node_report.h rename to server/deps/nodejs/include/node_report.h diff --git a/deps/nodejs/include/node_revert.h b/server/deps/nodejs/include/node_revert.h similarity index 100% rename from deps/nodejs/include/node_revert.h rename to server/deps/nodejs/include/node_revert.h diff --git a/deps/nodejs/include/node_root_certs.h b/server/deps/nodejs/include/node_root_certs.h similarity index 100% rename from deps/nodejs/include/node_root_certs.h rename to server/deps/nodejs/include/node_root_certs.h diff --git a/deps/nodejs/include/node_sockaddr-inl.h b/server/deps/nodejs/include/node_sockaddr-inl.h similarity index 100% rename from deps/nodejs/include/node_sockaddr-inl.h rename to server/deps/nodejs/include/node_sockaddr-inl.h diff --git a/deps/nodejs/include/node_sockaddr.h b/server/deps/nodejs/include/node_sockaddr.h similarity index 100% rename from deps/nodejs/include/node_sockaddr.h rename to server/deps/nodejs/include/node_sockaddr.h diff --git a/deps/nodejs/include/node_stat_watcher.h b/server/deps/nodejs/include/node_stat_watcher.h similarity index 100% rename from deps/nodejs/include/node_stat_watcher.h rename to server/deps/nodejs/include/node_stat_watcher.h diff --git a/deps/nodejs/include/node_union_bytes.h b/server/deps/nodejs/include/node_union_bytes.h similarity index 100% rename from deps/nodejs/include/node_union_bytes.h rename to server/deps/nodejs/include/node_union_bytes.h diff --git a/deps/nodejs/include/node_url.h b/server/deps/nodejs/include/node_url.h similarity index 100% rename from deps/nodejs/include/node_url.h rename to server/deps/nodejs/include/node_url.h diff --git a/deps/nodejs/include/node_v8_platform-inl.h b/server/deps/nodejs/include/node_v8_platform-inl.h similarity index 100% rename from deps/nodejs/include/node_v8_platform-inl.h rename to server/deps/nodejs/include/node_v8_platform-inl.h diff --git a/deps/nodejs/include/node_version.h b/server/deps/nodejs/include/node_version.h similarity index 100% rename from deps/nodejs/include/node_version.h rename to server/deps/nodejs/include/node_version.h diff --git a/deps/nodejs/include/node_wasi.h b/server/deps/nodejs/include/node_wasi.h similarity index 100% rename from deps/nodejs/include/node_wasi.h rename to server/deps/nodejs/include/node_wasi.h diff --git a/deps/nodejs/include/node_watchdog.h b/server/deps/nodejs/include/node_watchdog.h similarity index 100% rename from deps/nodejs/include/node_watchdog.h rename to server/deps/nodejs/include/node_watchdog.h diff --git a/deps/nodejs/include/node_win32_etw_provider-inl.h b/server/deps/nodejs/include/node_win32_etw_provider-inl.h similarity index 100% rename from deps/nodejs/include/node_win32_etw_provider-inl.h rename to server/deps/nodejs/include/node_win32_etw_provider-inl.h diff --git a/deps/nodejs/include/node_win32_etw_provider.h b/server/deps/nodejs/include/node_win32_etw_provider.h similarity index 100% rename from deps/nodejs/include/node_win32_etw_provider.h rename to server/deps/nodejs/include/node_win32_etw_provider.h diff --git a/deps/nodejs/include/node_worker.h b/server/deps/nodejs/include/node_worker.h similarity index 100% rename from deps/nodejs/include/node_worker.h rename to server/deps/nodejs/include/node_worker.h diff --git a/deps/nodejs/include/pipe_wrap.h b/server/deps/nodejs/include/pipe_wrap.h similarity index 100% rename from deps/nodejs/include/pipe_wrap.h rename to server/deps/nodejs/include/pipe_wrap.h diff --git a/deps/nodejs/include/req_wrap-inl.h b/server/deps/nodejs/include/req_wrap-inl.h similarity index 100% rename from deps/nodejs/include/req_wrap-inl.h rename to server/deps/nodejs/include/req_wrap-inl.h diff --git a/deps/nodejs/include/req_wrap.h b/server/deps/nodejs/include/req_wrap.h similarity index 100% rename from deps/nodejs/include/req_wrap.h rename to server/deps/nodejs/include/req_wrap.h diff --git a/deps/nodejs/include/spawn_sync.h b/server/deps/nodejs/include/spawn_sync.h similarity index 100% rename from deps/nodejs/include/spawn_sync.h rename to server/deps/nodejs/include/spawn_sync.h diff --git a/deps/nodejs/include/stream_base-inl.h b/server/deps/nodejs/include/stream_base-inl.h similarity index 100% rename from deps/nodejs/include/stream_base-inl.h rename to server/deps/nodejs/include/stream_base-inl.h diff --git a/deps/nodejs/include/stream_base.h b/server/deps/nodejs/include/stream_base.h similarity index 100% rename from deps/nodejs/include/stream_base.h rename to server/deps/nodejs/include/stream_base.h diff --git a/deps/nodejs/include/stream_pipe.h b/server/deps/nodejs/include/stream_pipe.h similarity index 100% rename from deps/nodejs/include/stream_pipe.h rename to server/deps/nodejs/include/stream_pipe.h diff --git a/deps/nodejs/include/stream_wrap.h b/server/deps/nodejs/include/stream_wrap.h similarity index 100% rename from deps/nodejs/include/stream_wrap.h rename to server/deps/nodejs/include/stream_wrap.h diff --git a/deps/nodejs/include/string_bytes.h b/server/deps/nodejs/include/string_bytes.h similarity index 100% rename from deps/nodejs/include/string_bytes.h rename to server/deps/nodejs/include/string_bytes.h diff --git a/deps/nodejs/include/string_decoder-inl.h b/server/deps/nodejs/include/string_decoder-inl.h similarity index 100% rename from deps/nodejs/include/string_decoder-inl.h rename to server/deps/nodejs/include/string_decoder-inl.h diff --git a/deps/nodejs/include/string_decoder.h b/server/deps/nodejs/include/string_decoder.h similarity index 100% rename from deps/nodejs/include/string_decoder.h rename to server/deps/nodejs/include/string_decoder.h diff --git a/deps/nodejs/include/string_search.h b/server/deps/nodejs/include/string_search.h similarity index 100% rename from deps/nodejs/include/string_search.h rename to server/deps/nodejs/include/string_search.h diff --git a/deps/nodejs/include/tcp_wrap.h b/server/deps/nodejs/include/tcp_wrap.h similarity index 100% rename from deps/nodejs/include/tcp_wrap.h rename to server/deps/nodejs/include/tcp_wrap.h diff --git a/deps/nodejs/include/threadpoolwork-inl.h b/server/deps/nodejs/include/threadpoolwork-inl.h similarity index 100% rename from deps/nodejs/include/threadpoolwork-inl.h rename to server/deps/nodejs/include/threadpoolwork-inl.h diff --git a/deps/nodejs/include/tls_wrap.h b/server/deps/nodejs/include/tls_wrap.h similarity index 100% rename from deps/nodejs/include/tls_wrap.h rename to server/deps/nodejs/include/tls_wrap.h diff --git a/deps/nodejs/include/tracing/agent.h b/server/deps/nodejs/include/tracing/agent.h similarity index 100% rename from deps/nodejs/include/tracing/agent.h rename to server/deps/nodejs/include/tracing/agent.h diff --git a/deps/nodejs/include/tracing/node_trace_buffer.h b/server/deps/nodejs/include/tracing/node_trace_buffer.h similarity index 100% rename from deps/nodejs/include/tracing/node_trace_buffer.h rename to server/deps/nodejs/include/tracing/node_trace_buffer.h diff --git a/deps/nodejs/include/tracing/node_trace_writer.h b/server/deps/nodejs/include/tracing/node_trace_writer.h similarity index 100% rename from deps/nodejs/include/tracing/node_trace_writer.h rename to server/deps/nodejs/include/tracing/node_trace_writer.h diff --git a/deps/nodejs/include/tracing/trace_event.h b/server/deps/nodejs/include/tracing/trace_event.h similarity index 100% rename from deps/nodejs/include/tracing/trace_event.h rename to server/deps/nodejs/include/tracing/trace_event.h diff --git a/deps/nodejs/include/tracing/trace_event_common.h b/server/deps/nodejs/include/tracing/trace_event_common.h similarity index 100% rename from deps/nodejs/include/tracing/trace_event_common.h rename to server/deps/nodejs/include/tracing/trace_event_common.h diff --git a/deps/nodejs/include/tracing/traced_value.h b/server/deps/nodejs/include/tracing/traced_value.h similarity index 100% rename from deps/nodejs/include/tracing/traced_value.h rename to server/deps/nodejs/include/tracing/traced_value.h diff --git a/deps/nodejs/include/tty_wrap.h b/server/deps/nodejs/include/tty_wrap.h similarity index 100% rename from deps/nodejs/include/tty_wrap.h rename to server/deps/nodejs/include/tty_wrap.h diff --git a/deps/nodejs/include/udp_wrap.h b/server/deps/nodejs/include/udp_wrap.h similarity index 100% rename from deps/nodejs/include/udp_wrap.h rename to server/deps/nodejs/include/udp_wrap.h diff --git a/deps/nodejs/include/util-inl.h b/server/deps/nodejs/include/util-inl.h similarity index 100% rename from deps/nodejs/include/util-inl.h rename to server/deps/nodejs/include/util-inl.h diff --git a/deps/nodejs/include/util.h b/server/deps/nodejs/include/util.h similarity index 100% rename from deps/nodejs/include/util.h rename to server/deps/nodejs/include/util.h diff --git a/deps/nodejs/include/v8abbr.h b/server/deps/nodejs/include/v8abbr.h similarity index 100% rename from deps/nodejs/include/v8abbr.h rename to server/deps/nodejs/include/v8abbr.h diff --git a/generate.bat b/server/generate.bat similarity index 100% rename from generate.bat rename to server/generate.bat diff --git a/src/CNodeResourceImpl.cpp b/server/src/CNodeResourceImpl.cpp similarity index 100% rename from src/CNodeResourceImpl.cpp rename to server/src/CNodeResourceImpl.cpp diff --git a/src/CNodeResourceImpl.h b/server/src/CNodeResourceImpl.h similarity index 91% rename from src/CNodeResourceImpl.h rename to server/src/CNodeResourceImpl.h index 70dedf84..d8336340 100644 --- a/src/CNodeResourceImpl.h +++ b/server/src/CNodeResourceImpl.h @@ -4,9 +4,9 @@ #include "cpp-sdk/IPackage.h" #include "cpp-sdk/IResource.h" -#include "helpers/V8ResourceImpl.h" -#include "helpers/V8Entity.h" -#include "helpers/V8Helpers.h" +#include "V8ResourceImpl.h" +#include "V8Entity.h" +#include "V8Helpers.h" #include "node.h" #include "uv.h" diff --git a/src/CNodeScriptRuntime.cpp b/server/src/CNodeScriptRuntime.cpp similarity index 95% rename from src/CNodeScriptRuntime.cpp rename to server/src/CNodeScriptRuntime.cpp index 620d6917..28b97f1c 100644 --- a/src/CNodeScriptRuntime.cpp +++ b/server/src/CNodeScriptRuntime.cpp @@ -45,7 +45,9 @@ CNodeScriptRuntime::~CNodeScriptRuntime() alt::IResource::Impl* CNodeScriptRuntime::CreateImpl(alt::IResource* resource) { - return new CNodeResourceImpl{ this, isolate, resource }; + auto res = new CNodeResourceImpl{ this, isolate, resource }; + resources.insert(res); + return res; } void CNodeScriptRuntime::OnTick() diff --git a/src/CNodeScriptRuntime.h b/server/src/CNodeScriptRuntime.h similarity index 78% rename from src/CNodeScriptRuntime.h rename to server/src/CNodeScriptRuntime.h index de5c2221..f7357564 100644 --- a/src/CNodeScriptRuntime.h +++ b/server/src/CNodeScriptRuntime.h @@ -3,13 +3,14 @@ #include "cpp-sdk/IScriptRuntime.h" #include "cpp-sdk/events/CRemoveEntityEvent.h" -#include "helpers/V8Helpers.h" +#include "V8Helpers.h" #include "CNodeResourceImpl.h" class CNodeScriptRuntime : public alt::IScriptRuntime { v8::Isolate* isolate; std::unique_ptr platform; + std::unordered_set resources; public: CNodeScriptRuntime(); @@ -21,6 +22,7 @@ class CNodeScriptRuntime : public alt::IScriptRuntime void DestroyImpl(alt::IResource::Impl* impl) override { + resources.insert(static_cast(impl)); delete static_cast(impl); } @@ -29,6 +31,8 @@ class CNodeScriptRuntime : public alt::IScriptRuntime node::MultiIsolatePlatform* GetPlatform() const { return platform.get(); } + std::unordered_set GetResources() { return resources; } + static CNodeScriptRuntime& Instance() { static CNodeScriptRuntime _Instance; diff --git a/src/bindings/Blip.cpp b/server/src/bindings/Blip.cpp similarity index 93% rename from src/bindings/Blip.cpp rename to server/src/bindings/Blip.cpp index 91ddd65a..e70d5d6b 100644 --- a/src/bindings/Blip.cpp +++ b/server/src/bindings/Blip.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" -#include "helpers/V8Helpers.h" -#include "helpers/V8ResourceImpl.h" +#include "V8Helpers.h" +#include "V8ResourceImpl.h" using namespace alt; diff --git a/server/src/bindings/Checkpoint.cpp b/server/src/bindings/Checkpoint.cpp new file mode 100644 index 00000000..aebb9168 --- /dev/null +++ b/server/src/bindings/Checkpoint.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" + +#include "V8Helpers.h" +#include "V8ResourceImpl.h" + +using namespace alt; + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN2(5, 10); + + alt::Position pos; + alt::RGBA color; + float radius, height; + + V8_ARG_TO_INT(1, type); + if(info.Length() == 5) + { + V8_ARG_TO_VECTOR3(2, posVal); + V8_ARG_TO_NUMBER(3, radiusVal); + V8_ARG_TO_NUMBER(4, heightVal); + V8_ARG_TO_RGBA(5, colorVal); + + pos = posVal; + color = colorVal; + radius = radiusVal; + height = heightVal; + } + else + { + V8_ARG_TO_NUMBER(2, x); + V8_ARG_TO_NUMBER(3, y); + V8_ARG_TO_NUMBER(4, z); + V8_ARG_TO_NUMBER(5, radiusVal); + V8_ARG_TO_NUMBER(6, heightVal); + V8_ARG_TO_INT(7, r); + V8_ARG_TO_INT(8, g); + V8_ARG_TO_INT(9, b); + V8_ARG_TO_INT(10, a); + + pos = { x, y, z }; + color = { (uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a }; + radius = radiusVal; + height = heightVal; + } + + Ref cp = ICore::Instance().CreateCheckpoint(type, pos, radius, height, color); + + if (cp) + resource->BindEntity(info.This(), cp.Get()); + else + V8Helpers::Throw(isolate, "Failed to create Checkpoint"); +} + +extern V8Class v8Colshape; +extern V8Class v8Checkpoint("Checkpoint", v8Colshape, Constructor, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); +}); diff --git a/src/bindings/ColShape.cpp b/server/src/bindings/ColShape.cpp similarity index 97% rename from src/bindings/ColShape.cpp rename to server/src/bindings/ColShape.cpp index 37e149d4..a89c63de 100755 --- a/src/bindings/ColShape.cpp +++ b/server/src/bindings/ColShape.cpp @@ -1,8 +1,8 @@ #include "stdafx.h" -#include "helpers/V8Helpers.h" -#include "helpers/V8BindHelpers.h" -#include "helpers/V8ResourceImpl.h" +#include "V8Helpers.h" +#include "V8BindHelpers.h" +#include "V8ResourceImpl.h" using namespace alt; diff --git a/src/bindings/Main.cpp b/server/src/bindings/Main.cpp similarity index 95% rename from src/bindings/Main.cpp rename to server/src/bindings/Main.cpp index 5495b18b..c80916eb 100644 --- a/src/bindings/Main.cpp +++ b/server/src/bindings/Main.cpp @@ -1,6 +1,6 @@ #include "stdafx.h" -#include "helpers/V8Module.h" +#include "V8Module.h" #include "CNodeResourceImpl.h" using namespace alt; @@ -94,7 +94,9 @@ static void EmitClient(const v8::FunctionCallbackInfo& info) for (int i = 0; i < arr->Length(); ++i) { Ref player; - V8Entity* v8Player = V8Entity::Get(arr->Get(ctx, i).ToLocalChecked()); + v8::Local ply; + V8_CHECK(arr->Get(ctx, i).ToLocal(&ply), "Invalid player in emitClient players array"); + V8Entity* v8Player = V8Entity::Get(ply); V8_CHECK(v8Player && v8Player->GetHandle()->GetType() == alt::IBaseObject::Type::PLAYER, "player inside array expected"); targets.Push(v8Player->GetHandle().As()); @@ -239,13 +241,7 @@ static void GetResourceExports(const v8::FunctionCallbackInfo& info) } } -extern V8Class v8Vector3, - v8RGBA, - v8File, - v8BaseObject, - v8WorldObject, - v8Entity, - v8Player, +extern V8Class v8Player, v8Vehicle, v8Blip, v8PointBlip, @@ -258,14 +254,10 @@ extern V8Class v8Vector3, v8ColshapeCuboid, v8ColshapeRectangle; -extern V8Module v8Alt("alt", +extern V8Module sharedModule; + +extern V8Module v8Alt("alt", &sharedModule, { - v8Vector3, - v8RGBA, - v8File, - v8BaseObject, - v8WorldObject, - v8Entity, v8Player, v8Vehicle, v8Blip, @@ -282,8 +274,6 @@ extern V8Module v8Alt("alt", [](v8::Local ctx, v8::Local exports) { v8::Isolate* isolate = ctx->GetIsolate(); - V8::RegisterSharedMain(ctx, exports); - V8Helpers::RegisterFunc(exports, "getResourceMain", &GetResourceMain); V8Helpers::RegisterFunc(exports, "getResourcePath", &GetResourcePath); V8Helpers::RegisterFunc(exports, "getResourceExports", &GetResourceExports); diff --git a/src/bindings/Player.cpp b/server/src/bindings/Player.cpp similarity index 89% rename from src/bindings/Player.cpp rename to server/src/bindings/Player.cpp index ed1f3e44..dc3ebef8 100644 --- a/src/bindings/Player.cpp +++ b/server/src/bindings/Player.cpp @@ -1,8 +1,8 @@ #include "stdafx.h" -#include "helpers/V8Helpers.h" -#include "helpers/V8ResourceImpl.h" -#include "helpers/V8BindHelpers.h" +#include "V8Helpers.h" +#include "V8ResourceImpl.h" +#include "V8BindHelpers.h" using namespace alt; @@ -207,6 +207,8 @@ static void SetClothes(const v8::FunctionCallbackInfo& info) V8_ARG_TO_INT(2, drawable); V8_ARG_TO_INT(3, texture); + V8_CHECK(component >= 0 && drawable >= 0 && texture >= 0, "setClothes args have to be positive, -1 won't reset"); + if(info.Length() == 3) { player->SetClothes(component, drawable, texture, 2); @@ -229,6 +231,8 @@ static void SetDlcClothes(const v8::FunctionCallbackInfo& info) V8_ARG_TO_INT(3, drawable); V8_ARG_TO_INT(4, texture); + V8_CHECK(component >= 0 && drawable >= 0 && texture >= 0, "setDlcClothes args have to be positive, -1 won't reset"); + if(info.Length() == 4) { player->SetDlcClothes(component, drawable, texture, 2, dlc); @@ -287,6 +291,8 @@ static void SetProps(const v8::FunctionCallbackInfo& info) V8_ARG_TO_INT(2, drawable); V8_ARG_TO_INT(3, texture); + V8_CHECK(component >= 0 && drawable >= 0 && texture >= 0, "setProp args have to be positive, -1 won't reset use clearProp"); + player->SetProps(component, drawable, texture); } @@ -301,6 +307,8 @@ static void SetDlcProps(const v8::FunctionCallbackInfo& info) V8_ARG_TO_INT(3, drawable); V8_ARG_TO_INT(4, texture); + V8_CHECK(component >= 0 && drawable >= 0 && texture >= 0, "setDlcProp args have to be positive, -1 won't reset use clearProp"); + player->SetDlcProps(component, drawable, texture, dlc); } @@ -361,6 +369,18 @@ static void IsEntityInStreamRange(const v8::FunctionCallbackInfo& inf V8_RETURN_BOOLEAN(player->IsEntityInStreamingRange(entity)); } +static void SetIntoVehicle(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(2); + V8_GET_THIS_BASE_OBJECT(player, IPlayer); + + V8_ARG_TO_BASE_OBJECT(1, vehicle, alt::IVehicle, "Vehicle"); + V8_ARG_TO_UINT32(2, seat); + + player->SetIntoVehicle(vehicle, seat); +} + static void AllGetter(v8::Local name, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); @@ -409,6 +429,10 @@ extern V8Class v8Player("Player", v8Entity, nullptr, [](v8::Local(isolate, tpl, "armour"); V8::SetAccessor(isolate, tpl, "maxArmour"); V8::SetAccessor(isolate, tpl, "isDead"); + V8::SetAccessor(isolate, tpl, "isInRagdoll"); + V8::SetAccessor(isolate, tpl, "isAiming"); + V8::SetAccessor(isolate, tpl, "aimPos"); + V8::SetAccessor(isolate, tpl, "headRot"); V8::SetAccessor, &IPlayer::GetEntityAimingAt>(isolate, tpl, "entityAimingAt"); V8::SetAccessor(isolate, tpl, "entityAimOffset"); @@ -426,6 +450,10 @@ extern V8Class v8Player("Player", v8Entity, nullptr, [](v8::Local(isolate, tpl, "flashlightActive"); + V8::SetAccessor(isolate, tpl, "moveSpeed"); + + V8::SetAccessor(isolate, tpl, "invincible"); + V8::SetMethod(isolate, tpl, "spawn", &Spawn); V8::SetMethod(isolate, tpl, "setDateTime", &SetDateTime); V8::SetMethod(isolate, tpl, "setWeather", &SetWeather); @@ -454,4 +482,6 @@ extern V8Class v8Player("Player", v8Entity, nullptr, [](v8::LocalGetBaseObjectOrNull(ev->GetTarget())); args.push_back(resource->GetBaseObjectOrNull(ev->GetAttacker())); - args.push_back(v8::Integer::New(isolate, ev->GetDamage())); + args.push_back(v8::Integer::New(isolate, ev->GetHealthDamage())); + args.push_back(v8::Integer::New(isolate, ev->GetArmourDamage())); args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetWeapon())); } ); diff --git a/src/events/Vehicle.cpp b/server/src/events/Vehicle.cpp similarity index 65% rename from src/events/Vehicle.cpp rename to server/src/events/Vehicle.cpp index 75878ea0..b064c9ad 100644 --- a/src/events/Vehicle.cpp +++ b/server/src/events/Vehicle.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" -#include "helpers/V8ResourceImpl.h" -#include "helpers/V8Helpers.h" +#include "V8ResourceImpl.h" +#include "V8Helpers.h" #include "cpp-sdk/events/CVehicleAttachEvent.h" #include "cpp-sdk/events/CVehicleDetachEvent.h" @@ -51,4 +51,21 @@ V8::LocalEventHandler netOwnerChange( args.push_back(resource->GetBaseObjectOrNull(ev->GetNewOwner())); args.push_back(resource->GetBaseObjectOrNull(ev->GetOldOwner())); } -); \ No newline at end of file +); + +V8::LocalEventHandler vehicleDamage( + EventType::VEHICLE_DAMAGE, + "vehicleDamage", + [](V8ResourceImpl* resource, const CEvent* e, std::vector>& args) { + auto ev = static_cast(e); + auto isolate = resource->GetIsolate(); + + args.push_back(resource->GetBaseObjectOrNull(ev->GetTarget())); + args.push_back(resource->GetBaseObjectOrNull(ev->GetDamager())); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetBodyHealthDamage())); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetBodyAdditionalHealthDamage())); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetEngineHealthDamage())); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetPetrolTankHealthDamage())); + args.push_back(v8::Integer::NewFromUnsigned(isolate, ev->GetDamagedWith())); + } +); diff --git a/src/node-module.cpp b/server/src/node-module.cpp similarity index 64% rename from src/node-module.cpp rename to server/src/node-module.cpp index 1a8eed3e..62d4b7c0 100644 --- a/src/node-module.cpp +++ b/server/src/node-module.cpp @@ -1,6 +1,6 @@ #include "stdafx.h" -#include "helpers/V8Module.h" +#include "V8Module.h" #include "CNodeScriptRuntime.h" /*static void NodeStop() @@ -25,14 +25,30 @@ }*/ extern V8Module v8Alt; +namespace main +{ static void Initialize(v8::Local exports) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::HandleScope handle_scope(isolate); - v8Alt.Register(isolate, isolate->GetEnteredContext(), exports); + v8Alt.Register(isolate, isolate->GetEnteredOrMicrotaskContext(), exports); } NODE_MODULE_LINKED(alt, Initialize) +} + +extern V8Module sharedModule; +namespace shared +{ +static void InitializeShared(v8::Local exports) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + + sharedModule.Register(isolate, isolate->GetEnteredOrMicrotaskContext(), exports); +} +NODE_MODULE_LINKED(altShared, InitializeShared) +} static void CommandHandler(alt::Array args, void* userData) { @@ -41,7 +57,7 @@ static void CommandHandler(alt::Array args, void* userData) Log::Colored << "~ly~js-module: " << JS_MODULE_VERSION << Log::Endl; Log::Colored << "~ly~" JS_MODULE_COPYRIGHT << Log::Endl; - Log::Colored << "~ly~nodejs: " NODEJS_VERSION << Log::Endl; + Log::Colored << "~ly~nodejs: " << NODE_MAJOR_VERSION << "." << NODE_MINOR_VERSION << "." << NODE_PATCH_VERSION << Log::Endl; Log::Colored << "~ly~" NODEJS_COPYRIGHT << Log::Endl; } else if (args.GetSize() > 0 && args[0] == "--help") @@ -58,6 +74,17 @@ static void CommandHandler(alt::Array args, void* userData) } } +static void TimersCommand(alt::Array, void* runtime) +{ + auto resources = static_cast(runtime)->GetResources(); + Log::Info << "================ Timer info =================" << Log::Endl; + for(auto resource : resources) + { + resource->TimerBenchmark(); + } + Log::Info << "======================================================" << Log::Endl; +} + EXPORT uint32_t GetSDKVersion() { return alt::ICore::SDK_VERSION; @@ -72,6 +99,7 @@ EXPORT bool altMain(alt::ICore* _core) apiCore.RegisterScriptRuntime("js", &runtime); apiCore.SubscribeCommand("js-module", &CommandHandler); + apiCore.SubscribeCommand("timers", &TimersCommand); return true; } \ No newline at end of file diff --git a/src/stdafx.h b/server/src/stdafx.h similarity index 65% rename from src/stdafx.h rename to server/src/stdafx.h index ed2828fb..0161a32e 100644 --- a/src/stdafx.h +++ b/server/src/stdafx.h @@ -1,9 +1,8 @@ #pragma once -#define JS_MODULE_COPYRIGHT u8"Copyright © 2020 altMP team." +#define JS_MODULE_COPYRIGHT u8"Copyright � 2020 altMP team." -#define NODEJS_VERSION u8"v12.4.0" -#define NODEJS_COPYRIGHT u8"Copyright © 2020 Node.js Foundation." +#define NODEJS_COPYRIGHT u8"Copyright � 2020 Node.js Foundation." #define NODE_WANT_INTERNALS 1 @@ -18,7 +17,7 @@ #include #include "cpp-sdk/SDK.h" -#include "helpers/Log.h" +#include "Log.h" #include "node.h" #include "node_platform.h" #include "node_internals.h" diff --git a/tools/altv-v8-tool.exe.REMOVED.git-id b/server/tools/altv-v8-tool.exe.REMOVED.git-id similarity index 100% rename from tools/altv-v8-tool.exe.REMOVED.git-id rename to server/tools/altv-v8-tool.exe.REMOVED.git-id diff --git a/tools/nodejsdefs.js b/server/tools/nodejsdefs.js similarity index 100% rename from tools/nodejsdefs.js rename to server/tools/nodejsdefs.js diff --git a/shared/Log.h b/shared/Log.h new file mode 100644 index 00000000..8b347221 --- /dev/null +++ b/shared/Log.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include "cpp-sdk/ICore.h" + +class Log +{ + std::stringstream buf; + + typedef Log &(*LogFn)(Log &); + static Log *_Instance; + + enum Type + { + INFO, + DEBUG, + WARNING, + ERROR, + COLORED + } type = INFO; + + Log() = default; + +public: + Log(const Log &) = delete; + Log(Log &&) = delete; + Log &operator=(const Log &) = delete; + + template + Log &Put(const T &val) + { + buf << val; + return *this; + } + Log &Put(LogFn val) { return val(*this); } + Log &SetType(Type _type) + { + type = _type; + return *this; + } + template + Log &operator<<(const T &val) { return Put(val); } + + static constexpr struct Log_Info + { + template + Log &operator<<(const T &val) const { return Instance().SetType(INFO).Put(val); } + } Info{}; + + static constexpr struct Log_Debug + { + template + Log &operator<<(const T &val) const { return Instance().SetType(DEBUG).Put(val); } + } Debug{}; + + static constexpr struct Log_Warning + { + template + Log &operator<<(const T &val) const { return Instance().SetType(WARNING).Put(val); } + } Warning{}; + + static constexpr struct Log_Error + { + template + Log &operator<<(const T &val) const { return Instance().SetType(ERROR).Put(val); } + } Error{}; + + static constexpr struct Log_Colored + { + template + Log &operator<<(const T &val) const { return Instance().SetType(COLORED).Put(val); } + } Colored{}; + + static Log &Endl(Log &log) + { + switch (log.type) + { + case INFO: + alt::ICore::Instance().LogInfo(log.buf.str()); + break; + case DEBUG: + alt::ICore::Instance().LogDebug(log.buf.str().c_str()); + break; + case WARNING: + alt::ICore::Instance().LogWarning(log.buf.str().c_str()); + break; + case ERROR: + alt::ICore::Instance().LogError(log.buf.str().c_str()); + break; + case COLORED: + alt::ICore::Instance().LogColored(log.buf.str().c_str()); + break; + } + + log.buf.str(""); + return log; + } + + static Log &Instance() + { + static Log _Instance; + return _Instance; + } +}; \ No newline at end of file diff --git a/shared/PromiseRejections.cpp b/shared/PromiseRejections.cpp new file mode 100644 index 00000000..9ea8a2c9 --- /dev/null +++ b/shared/PromiseRejections.cpp @@ -0,0 +1,62 @@ + +#include "V8ResourceImpl.h" +#include "V8Helpers.h" + +#include "PromiseRejections.h" + +void V8::PromiseRejections::RejectedWithNoHandler(V8ResourceImpl *resource, v8::PromiseRejectMessage &data) +{ + v8::Isolate *isolate = resource->GetIsolate(); + + queue.push_back(std::make_unique(isolate, data.GetPromise(), + data.GetValue(), V8::SourceLocation::GetCurrent(isolate))); +} + +void V8::PromiseRejections::HandlerAdded(V8ResourceImpl *resource, v8::PromiseRejectMessage &data) +{ + v8::Isolate *isolate = resource->GetIsolate(); + + auto newEnd = std::remove_if( + queue.begin(), queue.end(), + [&](std::unique_ptr &rejection) { + return rejection->promise.Get(isolate) == data.GetPromise(); + }); + + queue.erase(newEnd, queue.end()); +} + +void V8::PromiseRejections::ProcessQueue(V8ResourceImpl *resource) +{ + v8::Isolate *isolate = resource->GetIsolate(); + v8::Local ctx = isolate->GetEnteredOrMicrotaskContext(); + + for (auto &rejection : queue) + { + auto rejectionMsg = *v8::String::Utf8Value(isolate, rejection->value.Get(isolate)->ToString(ctx).ToLocalChecked()); + auto fileName = rejection->location.GetFileName(); + if (rejection->location.GetLineNumber() != 0) + { + Log::Error << "[V8] Unhandled promise rejection at " + << resource->GetResource()->GetName() << ":" << fileName << ":" << rejection->location.GetLineNumber() + << " (" << rejectionMsg << ")" << Log::Endl; + } + else + { + Log::Error << "[V8] Unhandled promise rejection at " + << resource->GetResource()->GetName() << ":" << fileName + << " (" << rejectionMsg << ")" << Log::Endl; + } + + resource->DispatchErrorEvent( + rejectionMsg, + "", + fileName, + rejection->location.GetLineNumber()); + } + + queue.clear(); +} + +V8::PromiseRejection::PromiseRejection(v8::Isolate *isolate, v8::Local _promise, v8::Local _value, V8::SourceLocation &&_location) : promise(isolate, _promise), value(isolate, _value), location(std::move(_location)) +{ +} diff --git a/shared/PromiseRejections.h b/shared/PromiseRejections.h new file mode 100644 index 00000000..14034a2d --- /dev/null +++ b/shared/PromiseRejections.h @@ -0,0 +1,32 @@ +#pragma once + +// Inspired by chromium and nodejs + +#include "v8.h" +#include "V8Helpers.h" + +class V8ResourceImpl; + +namespace V8 +{ + struct PromiseRejection + { + v8::Persistent promise; + v8::Persistent value; + V8::SourceLocation location; + + PromiseRejection(v8::Isolate* isolate, v8::Local promise, + v8::Local value, V8::SourceLocation&& location); + }; + + class PromiseRejections + { + public: + void RejectedWithNoHandler(V8ResourceImpl* resource, v8::PromiseRejectMessage& data); + void HandlerAdded(V8ResourceImpl* resource, v8::PromiseRejectMessage& data); + void ProcessQueue(V8ResourceImpl* resource); + + private: + std::vector> queue; + }; +} \ No newline at end of file diff --git a/shared/V8BindHelpers.h b/shared/V8BindHelpers.h new file mode 100644 index 00000000..8dab667e --- /dev/null +++ b/shared/V8BindHelpers.h @@ -0,0 +1,109 @@ +#pragma once + +#include "V8Helpers.h" +#include "V8ResourceImpl.h" + +#define V8_CALL_GETTER(type, req, retn) \ + template \ + static inline void CallGetter(const v8::PropertyCallbackInfo& info, T* _this, type(T::* getter)() const) \ + { \ + req(); \ + retn((_this->*getter)()); \ + } + +#define V8_CALL_SETTER(type, req, convert) \ + template \ + static inline void CallSetter(const v8::PropertyCallbackInfo& info, v8::Local value, T* _this, void(T::* setter)(type)) \ + { \ + req(); \ + convert(value, _val); \ + (_this->*setter)(type(_val)); \ + } + +namespace V8 +{ + namespace detail + { + V8_CALL_GETTER(bool, V8_GET_ISOLATE, V8_RETURN_BOOLEAN); + V8_CALL_GETTER(uint8_t, V8_GET_ISOLATE, V8_RETURN_UINT); + V8_CALL_GETTER(uint16_t, V8_GET_ISOLATE, V8_RETURN_UINT); + V8_CALL_GETTER(uint32_t, V8_GET_ISOLATE, V8_RETURN_UINT); + V8_CALL_GETTER(uint64_t, V8_GET_ISOLATE, V8_RETURN_UINT64); + V8_CALL_GETTER(int8_t, V8_GET_ISOLATE, V8_RETURN_INT); + V8_CALL_GETTER(int16_t, V8_GET_ISOLATE, V8_RETURN_INT); + V8_CALL_GETTER(int32_t, V8_GET_ISOLATE, V8_RETURN_INT); + V8_CALL_GETTER(int64_t, V8_GET_ISOLATE, V8_RETURN_INT64); + V8_CALL_GETTER(float, V8_GET_ISOLATE, V8_RETURN_NUMBER); + V8_CALL_GETTER(double, V8_GET_ISOLATE, V8_RETURN_NUMBER); + V8_CALL_GETTER(alt::StringView, V8_GET_ISOLATE, V8_RETURN_ALT_STRING); + V8_CALL_GETTER(alt::Position, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_VECTOR3); + V8_CALL_GETTER(alt::Rotation, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_VECTOR3); + V8_CALL_GETTER(alt::Vector3f, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_VECTOR3); + V8_CALL_GETTER(alt::Vector2f, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_VECTOR2); + V8_CALL_GETTER(alt::RGBA, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_RGBA); + V8_CALL_GETTER(alt::IColShape::ColShapeType, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_ENUM); + V8_CALL_GETTER(alt::IBaseObject::Type, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_ENUM); + V8_CALL_GETTER(alt::Ref, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_BASE_OBJECT); + V8_CALL_GETTER(alt::Ref, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_BASE_OBJECT); + V8_CALL_GETTER(alt::Ref, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_BASE_OBJECT); + V8_CALL_GETTER(alt::Ref, V8_GET_ISOLATE_CONTEXT_RESOURCE, V8_RETURN_BASE_OBJECT); + + V8_CALL_SETTER(bool, V8_GET_ISOLATE, V8_TO_BOOLEAN); + V8_CALL_SETTER(uint8_t, V8_GET_ISOLATE_CONTEXT, V8_TO_INTEGER); + V8_CALL_SETTER(uint16_t, V8_GET_ISOLATE_CONTEXT, V8_TO_INTEGER); + V8_CALL_SETTER(uint32_t, V8_GET_ISOLATE_CONTEXT, V8_TO_INTEGER); + V8_CALL_SETTER(int8_t, V8_GET_ISOLATE_CONTEXT, V8_TO_INTEGER); + V8_CALL_SETTER(int16_t, V8_GET_ISOLATE_CONTEXT, V8_TO_INTEGER); + V8_CALL_SETTER(int32_t, V8_GET_ISOLATE_CONTEXT, V8_TO_INTEGER); + V8_CALL_SETTER(float, V8_GET_ISOLATE_CONTEXT, V8_TO_NUMBER); + V8_CALL_SETTER(double, V8_GET_ISOLATE_CONTEXT, V8_TO_NUMBER); + V8_CALL_SETTER(alt::StringView, V8_GET_ISOLATE_CONTEXT, V8_TO_STRING); + V8_CALL_SETTER(alt::Position, V8_GET_ISOLATE_CONTEXT, V8_TO_VECTOR3); + V8_CALL_SETTER(alt::Rotation, V8_GET_ISOLATE_CONTEXT, V8_TO_VECTOR3); + V8_CALL_SETTER(alt::Vector3f, V8_GET_ISOLATE_CONTEXT, V8_TO_VECTOR3); + V8_CALL_SETTER(alt::Vector2f, V8_GET_ISOLATE_CONTEXT, V8_TO_VECTOR2); + V8_CALL_SETTER(alt::RGBA, V8_GET_ISOLATE_CONTEXT, V8_TO_RGBA); + + template + static void WrapGetter(v8::Local, const v8::PropertyCallbackInfo& info) + { + V8_GET_ISOLATE(); + V8_GET_THIS_BASE_OBJECT(_this, T); + CallGetter(info, _this.Get(), Getter); + } + + template + static void WrapSetter(v8::Local, v8::Local value, const v8::PropertyCallbackInfo& info) + { + V8_GET_ISOLATE(); + V8_GET_THIS_BASE_OBJECT(_this, T); + CallSetter(info, value, _this.Get(), Setter); + } + + template + static void WrapMethod(const v8::FunctionCallbackInfo &info) + { + V8_GET_ISOLATE(); + V8_GET_THIS_BASE_OBJECT(_this, T); + (_this.Get()->*Method)(); + } + } + + template + inline void SetAccessor(v8::Isolate* isolate, v8::Local tpl, const char* name) + { + V8::SetAccessor(isolate, tpl, name, &V8::detail::WrapGetter, nullptr); + } + + template + inline void SetAccessor(v8::Isolate* isolate, v8::Local tpl, const char* name) + { + V8::SetAccessor(isolate, tpl, name, V8::detail::WrapGetter, V8::detail::WrapSetter); + } + + template + inline void SetMethod(v8::Isolate* isolate, v8::Local tpl, const char* name) + { + V8::SetMethod(isolate, tpl, name, V8::detail::WrapMethod); + } +} diff --git a/shared/V8Class.cpp b/shared/V8Class.cpp new file mode 100644 index 00000000..f8500583 --- /dev/null +++ b/shared/V8Class.cpp @@ -0,0 +1,53 @@ + +#include "V8Helpers.h" +#include "V8Class.h" + +v8::Local V8Class::New(v8::Local ctx, std::vector> &args) +{ + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + v8::Local _tpl = tpl.Get(isolate); + v8::Local obj; + + V8Helpers::TryCatch([&] { + v8::Local func; + if(!_tpl->GetFunction(ctx).ToLocal(&func)) + { + Log::Error << "V8Class::New Invalid constructor called" << Log::Endl; + return false; + } + + v8::MaybeLocal maybeObj = func->CallAsConstructor(ctx, args.size(), args.data()); + + if (!maybeObj.IsEmpty()) + { + obj = maybeObj.ToLocalChecked(); + return true; + } + else + { + obj = v8::Null(isolate); + return false; + } + }); + + return obj; +} + +v8::Local V8Class::CreateInstance(v8::Isolate *isolate, v8::Local ctx, std::vector> args) +{ + v8::Local constructor = JSValue(isolate, ctx); + v8::Local obj; + + V8Helpers::TryCatch([&] { + v8::MaybeLocal maybeObj = constructor->CallAsConstructor(ctx, args.size(), args.data()); + + if (maybeObj.IsEmpty()) + return false; + + obj = maybeObj.ToLocalChecked(); + return true; + }); + + return obj; +} diff --git a/shared/V8Class.h b/shared/V8Class.h new file mode 100644 index 00000000..6e3331e4 --- /dev/null +++ b/shared/V8Class.h @@ -0,0 +1,134 @@ +#pragma once + +#include +#include + +#include "Log.h" + +class V8Class +{ + using InitCallback = std::function)>; + + V8Class *parent = nullptr; + std::string name; + v8::FunctionCallback constructor; + InitCallback initCb; + v8::Persistent tpl; + bool loaded = false; + +public: + static auto &All() + { + static std::unordered_map _All; + return _All; + } + + V8Class( + const std::string &className, + V8Class &parent, + v8::FunctionCallback constructor, + InitCallback &&init = {}) : parent(&parent), + name(className), + constructor(constructor), + initCb(std::move(init)) + { + All()[name] = this; + } + + V8Class( + const std::string &className, + V8Class &parent, + InitCallback &&init = {}) : parent(&parent), + name(className), + initCb(std::move(init)) + { + All()[name] = this; + } + + V8Class( + const std::string &className, + v8::FunctionCallback constructor, + InitCallback &&init = {}) : name(className), + constructor(constructor), + initCb(std::move(init)) + { + All()[name] = this; + } + + V8Class( + const std::string &className, + InitCallback &&init = {}) : name(className), + initCb(std::move(init)) + { + All()[name] = this; + } + + const std::string &GetName() + { + return name; + } + + v8::Local CreateInstance(v8::Local ctx) + { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + v8::Local _tpl = tpl.Get(isolate); + v8::Local obj = _tpl->InstanceTemplate()->NewInstance(ctx).ToLocalChecked(); + + return obj; + } + + v8::Local CreateInstance(v8::Isolate *isolate, v8::Local ctx, std::vector> args); + + v8::Local JSValue(v8::Isolate *isolate, v8::Local ctx) + { + return tpl.Get(isolate)->GetFunction(ctx).ToLocalChecked(); + } + + v8::Local New(v8::Local ctx, std::vector> &args); + + static void LoadAll(v8::Isolate *isolate) + { + for (auto &p : All()) + p.second->Load(isolate); + } + + void Load(v8::Isolate *isolate) + { + if (loaded) + return; + + loaded = true; + + v8::Local _tpl = v8::FunctionTemplate::New(isolate, constructor); + _tpl->SetClassName(v8::String::NewFromUtf8(isolate, name.c_str(), v8::NewStringType::kNormal).ToLocalChecked()); + + if (initCb) + initCb(_tpl); + + if (parent) + { + parent->Load(isolate); + auto parenttpl = parent->tpl.Get(isolate); + _tpl->Inherit(parenttpl); + + // if parent has more internal fields, + // set the current internal field count to the parent's count + auto parentInternalFieldCount = parenttpl->InstanceTemplate()->InternalFieldCount(); + if(parentInternalFieldCount > _tpl->InstanceTemplate()->InternalFieldCount()) + _tpl->InstanceTemplate()->SetInternalFieldCount(parentInternalFieldCount); + } + + if (!tpl.IsEmpty()) + { + Log::Error << "Already loaded " << name << Log::Endl; + } + + tpl.Reset(isolate, _tpl); + } + + void Register(v8::Isolate *isolate, v8::Local context, v8::Local exports) + { + exports->Set(context, v8::String::NewFromUtf8(isolate, name.c_str(), v8::NewStringType::kNormal).ToLocalChecked(), tpl.Get(isolate)->GetFunction(context).ToLocalChecked()); + } +}; \ No newline at end of file diff --git a/shared/V8Entity.h b/shared/V8Entity.h new file mode 100644 index 00000000..4776b28f --- /dev/null +++ b/shared/V8Entity.h @@ -0,0 +1,83 @@ +#pragma once + +#include + +#include "cpp-sdk/objects/IEntity.h" + +#include "V8Class.h" + +class V8Entity +{ + V8Class *_class; + alt::Ref handle; + v8::Persistent jsVal; + +public: + V8Entity(v8::Local ctx, V8Class *__class, v8::Local obj, alt::Ref _handle) : _class(__class), + handle(_handle) + { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + obj->SetInternalField(0, v8::External::New(isolate, this)); + jsVal.Reset(isolate, obj); + } + + V8Class *GetClass() { return _class; } + + alt::Ref GetHandle() { return handle; } + + v8::Local GetJSVal(v8::Isolate *isolate) { return jsVal.Get(isolate); } + + static V8Entity *Get(v8::Local val) + { + if (!val->IsObject()) + return nullptr; + + v8::Local obj = val.As(); + if (obj->InternalFieldCount() != 1) + return nullptr; + + v8::Local i = obj->GetInternalField(0); + if (!i->IsExternal()) + return nullptr; + + return static_cast(i.As()->Value()); + } + + static V8Class* GetClass(alt::Ref handle) + { + extern V8Class v8Player, v8Vehicle, v8Blip; +#ifdef ALT_SERVER_API + extern V8Class v8VoiceChannel, v8Colshape, v8Checkpoint; +#else + extern V8Class v8WebView, v8LocalPlayer; +#endif + + if (!handle) + return nullptr; + + switch (handle->GetType()) + { + case alt::IBaseObject::Type::PLAYER: + return &v8Player; + case alt::IBaseObject::Type::VEHICLE: + return &v8Vehicle; + case alt::IBaseObject::Type::BLIP: + return &v8Blip; +#ifdef ALT_SERVER_API + case alt::IBaseObject::Type::COLSHAPE: + return &v8Colshape; + case alt::IBaseObject::Type::CHECKPOINT: + return &v8Checkpoint; + case alt::IBaseObject::Type::VOICE_CHANNEL: + return &v8VoiceChannel; +#else + case alt::IBaseObject::Type::WEBVIEW: + return &v8WebView; + case alt::IBaseObject::Type::LOCAL_PLAYER: + return &v8LocalPlayer; +#endif + } + + return nullptr; + } +}; \ No newline at end of file diff --git a/shared/V8Helpers.cpp b/shared/V8Helpers.cpp new file mode 100644 index 00000000..3ff4df97 --- /dev/null +++ b/shared/V8Helpers.cpp @@ -0,0 +1,812 @@ + +#include "cpp-sdk/ICore.h" +#include "V8ResourceImpl.h" +#include "V8Helpers.h" +#include + +bool V8Helpers::TryCatch(const std::function& fn) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Local context = isolate->GetEnteredOrMicrotaskContext(); + v8::TryCatch tryCatch(isolate); + + V8ResourceImpl* v8resource = V8ResourceImpl::Get(context); + alt::IResource* resource = v8resource->GetResource(); + + if (!fn()) + { + v8::Local exception = tryCatch.Exception(); + v8::Local message = tryCatch.Message(); + + if (!message.IsEmpty() && !context.IsEmpty()) + { + v8::MaybeLocal maybeSourceLine = message->GetSourceLine(context); + v8::Maybe line = message->GetLineNumber(context); + v8::ScriptOrigin origin = message->GetScriptOrigin(); + + if (!origin.ResourceName()->IsUndefined()) + { + Log::Error << "[V8] Exception at " << resource->GetName() << ":" + << *v8::String::Utf8Value(isolate, origin.ResourceName()) << ":" << line.ToChecked() << Log::Endl; + + if (!maybeSourceLine.IsEmpty()) + { + v8::Local sourceLine = maybeSourceLine.ToLocalChecked(); + + if (sourceLine->Length() <= 80) + { + Log::Error << " " << *v8::String::Utf8Value(isolate, sourceLine) << Log::Endl; + } + else + { + Log::Error << " " << std::string{ *v8::String::Utf8Value(isolate, sourceLine), 80 } << "..." << Log::Endl; + } + } + + auto stackTrace = tryCatch.StackTrace(context); + v8resource->DispatchErrorEvent( + exception.IsEmpty() ? "unknown" : *v8::String::Utf8Value(isolate, exception), + (!stackTrace.IsEmpty() && stackTrace.ToLocalChecked()->IsString()) ? *v8::String::Utf8Value(isolate, stackTrace.ToLocalChecked()) : "", + *v8::String::Utf8Value(isolate, origin.ResourceName()), + line.IsNothing() ? -1 : line.ToChecked()); + } + else + { + Log::Error << "[V8] Exception at " << resource->GetName() << Log::Endl; + } + + v8::MaybeLocal stackTrace = tryCatch.StackTrace(context); + if (!stackTrace.IsEmpty() && stackTrace.ToLocalChecked()->IsString()) + { + v8::String::Utf8Value stackTraceStr(isolate, stackTrace.ToLocalChecked().As()); + Log::Error << " " << *stackTraceStr << Log::Endl; + } + } + else if (!exception.IsEmpty()) + { + Log::Error << "[V8] Exception: " + << *v8::String::Utf8Value(isolate, exception) << Log::Endl; + } + else + { + Log::Error << "[V8] Exception occured" << Log::Endl; + } + + return false; + } + + return true; +} + +void V8Helpers::RegisterFunc(v8::Local exports, const std::string& _name, v8::FunctionCallback cb, void* data) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Local ctx = isolate->GetEnteredOrMicrotaskContext(); + + v8::Local name = v8::String::NewFromUtf8(isolate, _name.data(), v8::NewStringType::kNormal, _name.size()).ToLocalChecked(); + + v8::Local fn = v8::Function::New(ctx, cb, v8::External::New(isolate, data)).ToLocalChecked(); + fn->SetName(name); + + exports->Set(ctx, name, fn); +} + +void V8Helpers::FunctionCallback(const v8::FunctionCallbackInfo& info) +{ + auto fn = static_cast(info.Data().As()->Value()); + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Local ctx = isolate->GetEnteredOrMicrotaskContext(); + V8ResourceImpl* resource = V8ResourceImpl::Get(ctx); + + Log::Debug << "FunctionCallback " << resource->GetResource()->GetName() << " " << V8ResourceImpl::Get(isolate->GetEnteredOrMicrotaskContext())->GetResource()->GetName() << Log::Endl; + + alt::MValueArgs args; + + for (uint64_t i = 0; i < info.Length(); ++i) + args.Push(V8Helpers::V8ToMValue(info[i])); + + alt::MValue res = (*fn)->Call(args); + + info.GetReturnValue().Set(V8Helpers::MValueToV8(res)); +} + +alt::MValue V8Helpers::V8ToMValue(v8::Local val) +{ + auto& core = alt::ICore::Instance(); + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Local ctx = isolate->GetEnteredOrMicrotaskContext(); + + if (val.IsEmpty()) + return core.CreateMValueNone(); + + if (val->IsUndefined()) + return core.CreateMValueNone(); + + if (val->IsNull()) + return core.CreateMValueNil(); + + if (val->IsBoolean()) + return core.CreateMValueBool(val->BooleanValue(isolate)); + + if (val->IsInt32()) + return core.CreateMValueInt(val->Int32Value(ctx).ToChecked()); + + if (val->IsUint32()) + return core.CreateMValueUInt(val->Uint32Value(ctx).ToChecked()); + + if (val->IsBigInt()) + return core.CreateMValueInt(val.As()->Int64Value()); + + if (val->IsNumber()) + return core.CreateMValueDouble(val->NumberValue(ctx).ToChecked()); + + if (val->IsString()) + return core.CreateMValueString(*v8::String::Utf8Value(isolate, val)); + + if (val->IsObject()) + { + if (val->IsArray()) + { + v8::Local v8Arr = val.As(); + alt::MValueList list = core.CreateMValueList(v8Arr->Length()); + + for (uint32_t i = 0; i < v8Arr->Length(); ++i) + { + v8::Local value; + if(!v8Arr->Get(ctx, i).ToLocal(&value)) continue; + list->Set(i, V8ToMValue(value)); + } + + return list; + } + else if (val->IsFunction()) + { + v8::Local v8Func = val.As(); + return V8ResourceImpl::Get(ctx)->GetFunction(v8Func); + } + else if (val->IsArrayBuffer()) + { + auto v8Buffer = val.As()->GetBackingStore(); + return core.CreateMValueByteArray((uint8_t*)v8Buffer->Data(), v8Buffer->ByteLength()); + } + else + { + V8ResourceImpl* resource = V8ResourceImpl::Get(ctx); + v8::Local v8Obj = val.As(); + + //if (v8Obj->InstanceOf(ctx, v8Vector3->JSValue(isolate, ctx)).ToChecked()) + if (resource->IsVector3(v8Obj)) + { + v8::Local x, y, z; + V8_CHECK_RETN(v8Obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocal(&x), "Failed to convert Vector3 to MValue", core.CreateMValueNil()); + V8_CHECK_RETN(v8Obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocal(&y), "Failed to convert Vector3 to MValue", core.CreateMValueNil()); + V8_CHECK_RETN(v8Obj->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocal(&z), "Failed to convert Vector3 to MValue", core.CreateMValueNil()); + + return core.CreateMValueVector3( + alt::Vector3f{ + x.As()->Value(), + y.As()->Value(), + z.As()->Value() }); + } + else if (resource->IsVector2(v8Obj)) + { + v8::Local x, y; + V8_CHECK_RETN(v8Obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocal(&x), "Failed to convert Vector2 to MValue", core.CreateMValueNil()); + V8_CHECK_RETN(v8Obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocal(&y), "Failed to convert Vector2 to MValue", core.CreateMValueNil()); + + return core.CreateMValueVector2( + alt::Vector2f{ + x.As()->Value(), + y.As()->Value() }); + } + else if (resource->IsRGBA(v8Obj)) + { + v8::Local r, g, b, a; + V8_CHECK_RETN(v8Obj->Get(ctx, V8::RGBA_RKey(isolate)).ToLocal(&r), "Failed to convert RGBA to MValue", core.CreateMValueNil()); + V8_CHECK_RETN(v8Obj->Get(ctx, V8::RGBA_GKey(isolate)).ToLocal(&g), "Failed to convert RGBA to MValue", core.CreateMValueNil()); + V8_CHECK_RETN(v8Obj->Get(ctx, V8::RGBA_BKey(isolate)).ToLocal(&b), "Failed to convert RGBA to MValue", core.CreateMValueNil()); + V8_CHECK_RETN(v8Obj->Get(ctx, V8::RGBA_AKey(isolate)).ToLocal(&a), "Failed to convert RGBA to MValue", core.CreateMValueNil()); + + return core.CreateMValueRGBA( + alt::RGBA{ + (uint8_t)r.As()->Value(), + (uint8_t)g.As()->Value(), + (uint8_t)b.As()->Value(), + (uint8_t)a.As()->Value() }); + } + else if (resource->IsBaseObject(v8Obj)) + { + V8Entity* ent = V8Entity::Get(v8Obj); + Log::Debug << "Instanceof BaseObject" << Log::Endl; + + V8_CHECK_RETN(ent, "Unable to convert base object to MValue because it was destroyed and is now invalid", core.CreateMValueNil()); + return core.CreateMValueBaseObject(ent->GetHandle()); + } + else + { + alt::MValueDict dict = core.CreateMValueDict(); + v8::Local keys; + + V8_CHECK_RETN(v8Obj->GetOwnPropertyNames(ctx).ToLocal(&keys), "Failed to convert object to MValue", core.CreateMValueNil()); + for (uint32_t i = 0; i < keys->Length(); ++i) + { + v8::Local v8Key; + V8_CHECK_RETN(keys->Get(ctx, i).ToLocal(&v8Key), "Failed to convert object to MValue", core.CreateMValueNil()); + v8::Local value; + V8_CHECK_RETN(v8Obj->Get(ctx, v8Key).ToLocal(&value), "Failed to convert object to MValue", core.CreateMValueNil()); + + if(value->IsUndefined()) continue; + std::string key = *v8::String::Utf8Value(isolate, v8Key); + dict->Set(key, V8ToMValue(value)); + } + + return dict; + } + } + } + + return core.CreateMValueNone(); +} + +v8::Local V8Helpers::MValueToV8(alt::MValueConst val) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Local ctx = isolate->GetEnteredOrMicrotaskContext(); + + switch (val->GetType()) + { + case alt::IMValue::Type::NONE: + return v8::Undefined(isolate); + case alt::IMValue::Type::NIL: + return v8::Null(isolate); + case alt::IMValue::Type::BOOL: + return v8::Boolean::New(isolate, val.As()->Value()); + case alt::IMValue::Type::INT: + { + int64_t _val = val.As()->Value(); + + if (_val >= INT_MIN && _val <= INT_MAX) + return v8::Integer::New(isolate, _val); + + return v8::BigInt::New(isolate, _val); + } + case alt::IMValue::Type::UINT: + { + uint64_t _val = val.As()->Value(); + + if (_val <= UINT_MAX) + return v8::Integer::NewFromUnsigned(isolate, _val); + + return v8::BigInt::NewFromUnsigned(isolate, _val); + } + case alt::IMValue::Type::DOUBLE: + return v8::Number::New(isolate, val.As()->Value()); + case alt::IMValue::Type::STRING: + return v8::String::NewFromUtf8(isolate, val.As()->Value().CStr(), v8::NewStringType::kNormal).ToLocalChecked(); + case alt::IMValue::Type::LIST: + { + alt::MValueListConst list = val.As(); + v8::Local v8Arr = v8::Array::New(isolate, list->GetSize()); + + for (uint32_t i = 0; i < list->GetSize(); ++i) + v8Arr->Set(ctx, i, MValueToV8(list->Get(i))); + + return v8Arr; + } + case alt::IMValue::Type::DICT: + { + alt::MValueDictConst dict = val.As(); + v8::Local v8Obj = v8::Object::New(isolate); + + for (auto it = dict->Begin(); it; it = dict->Next()) + { + v8Obj->Set(ctx, v8::String::NewFromUtf8(isolate, it->GetKey().CStr(), v8::NewStringType::kNormal).ToLocalChecked(), MValueToV8(it->GetValue())); + } + + return v8Obj; + } + case alt::IMValue::Type::BASE_OBJECT: + { + alt::Ref ref = val.As()->Value(); + return V8ResourceImpl::Get(ctx)->GetBaseObjectOrNull(ref); + } + case alt::IMValue::Type::FUNCTION: + { + alt::MValueFunctionConst fn = val.As(); + v8::Local extFn = v8::External::New(isolate, new alt::MValueFunctionConst(fn)); + + v8::Local func; + V8_CHECK_RETN(v8::Function::New(ctx, V8Helpers::FunctionCallback, extFn).ToLocal(&func), "Failed to convert MValue to function", v8::Undefined(isolate)); + return func; + } + case alt::IMValue::Type::VECTOR3: + return V8ResourceImpl::Get(ctx)->CreateVector3(val.As()->Value()); + case alt::IMValue::Type::VECTOR2: + return V8ResourceImpl::Get(ctx)->CreateVector2(val.As()->Value()); + case alt::IMValue::Type::RGBA: + return V8ResourceImpl::Get(ctx)->CreateRGBA(val.As()->Value()); + case alt::IMValue::Type::BYTE_ARRAY: + { + alt::MValueByteArrayConst buffer = val.As(); + v8::Local v8Buffer = v8::ArrayBuffer::New(isolate, buffer->GetSize()); + std::memcpy(v8Buffer->GetBackingStore()->Data(), buffer->GetData(), buffer->GetSize()); + return v8Buffer; + } + default: + Log::Warning << "V8::MValueToV8 Unknown MValue type " << (int)val->GetType() << Log::Endl; + } + + return v8::Undefined(isolate); +} + +void V8Helpers::MValueArgsToV8(alt::MValueArgs args, std::vector>& v8Args) +{ + for (uint64_t i = 0; i < args.GetSize(); ++i) + v8Args.push_back(MValueToV8(args[i])); +} + +void V8Helpers::SetAccessor(v8::Local tpl, v8::Isolate* isolate, const char* name, v8::AccessorGetterCallback getter, v8::AccessorSetterCallback setter) +{ + tpl->SetNativeDataProperty(v8::String::NewFromUtf8(isolate, name, + v8::NewStringType::kInternalized) + .ToLocalChecked(), + getter, setter); +} + +void V8::DefineOwnProperty(v8::Isolate* isolate, v8::Local ctx, v8::Local val, const char* name, v8::Local value, v8::PropertyAttribute attributes) +{ + val->DefineOwnProperty(ctx, v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked(), value, attributes); +} + +void V8::DefineOwnProperty(v8::Isolate* isolate, v8::Local ctx, v8::Local val, v8::Local name, v8::Local value, v8::PropertyAttribute attributes) +{ + val->DefineOwnProperty(ctx, name, value, attributes); +} + +void V8::SetAccessor(v8::Isolate* isolate, v8::Local tpl, const char* name, v8::AccessorGetterCallback getter, v8::AccessorSetterCallback setter) +{ + tpl->PrototypeTemplate()->SetAccessor(v8::String::NewFromUtf8(isolate, name, + v8::NewStringType::kInternalized) + .ToLocalChecked(), + getter, setter, + v8::Local(), v8::AccessControl::DEFAULT, + setter != nullptr ? v8::PropertyAttribute::None : v8::PropertyAttribute::ReadOnly); +} + +void V8::SetMethod(v8::Isolate* isolate, v8::Local tpl, const char* name, v8::FunctionCallback callback) +{ + tpl->PrototypeTemplate()->Set(isolate, name, v8::FunctionTemplate::New(isolate, callback)); +} + +void V8::SetStaticAccessor(v8::Isolate* isolate, v8::Local tpl, const char* name, v8::AccessorGetterCallback getter, v8::AccessorSetterCallback setter) +{ + tpl->SetNativeDataProperty(v8::String::NewFromUtf8(isolate, name, + v8::NewStringType::kInternalized) + .ToLocalChecked(), + getter, setter); +} + +void V8::SetStaticMethod(v8::Isolate* isolate, v8::Local tpl, const char* name, v8::FunctionCallback callback) +{ + tpl->Set(isolate, name, v8::FunctionTemplate::New(isolate, callback)); +} + +v8::Local V8::Get(v8::Local ctx, v8::Local obj, const char* name) +{ + return obj->Get(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), name, + v8::NewStringType::kInternalized) + .ToLocalChecked()) + .ToLocalChecked(); +} + +v8::Local V8::Get(v8::Local ctx, v8::Local obj, v8::Local name) +{ + return obj->Get(ctx, name).ToLocalChecked(); +} + +void V8::SetFunction(v8::Isolate* isolate, v8::Local ctx, v8::Local target, const char* name, v8::FunctionCallback cb, void* userData) +{ + v8::Local _name = v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked(); + + v8::Local fn = v8::Function::New(ctx, cb, v8::External::New(isolate, userData)).ToLocalChecked(); + fn->SetName(_name); + + target->Set(ctx, _name, fn); +} + +v8::Local V8::New(v8::Isolate* isolate, v8::Local ctx, v8::Local constructor, std::vector>& args) +{ + v8::Local obj; + + V8Helpers::TryCatch([&] { + v8::MaybeLocal maybeObj = constructor->CallAsConstructor(ctx, args.size(), args.data()); + + if (maybeObj.IsEmpty()) + return false; + + obj = maybeObj.ToLocalChecked(); + return true; + }); + + return obj; +} + +V8::SourceLocation V8::SourceLocation::GetCurrent(v8::Isolate* isolate) +{ + v8::Local stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 1); + if (stackTrace->GetFrameCount() > 0) + { + v8::Local frame = stackTrace->GetFrame(isolate, 0); + + v8::Local name = frame->GetScriptName(); + if (!name.IsEmpty()) + { + std::string fileName = *v8::String::Utf8Value(isolate, frame->GetScriptName()); + int line = frame->GetLineNumber(); + + return SourceLocation{ std::move(fileName), line }; + } + else if (frame->IsEval()) + { + return SourceLocation{ "[eval]", 0 }; + } + } + + return SourceLocation{ "[unknown]", 0 }; +} + +V8::SourceLocation::SourceLocation(std::string&& _fileName, int _line) : fileName(_fileName), line(_line) +{ +} + +v8::Local V8::Vector3_XKey(v8::Isolate* isolate) +{ + static v8::Persistent xKey{ isolate, v8::String::NewFromUtf8(isolate, "x", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return xKey.Get(isolate); +} + +v8::Local V8::Vector3_YKey(v8::Isolate* isolate) +{ + static v8::Persistent yKey{ isolate, v8::String::NewFromUtf8(isolate, "y", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return yKey.Get(isolate); +} + +v8::Local V8::Vector3_ZKey(v8::Isolate* isolate) +{ + static v8::Persistent zKey{ isolate, v8::String::NewFromUtf8(isolate, "z", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return zKey.Get(isolate); +} + +v8::Local V8::RGBA_RKey(v8::Isolate* isolate) +{ + static v8::Persistent rKey{ isolate, v8::String::NewFromUtf8(isolate, "r", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return rKey.Get(isolate); +} + +v8::Local V8::RGBA_GKey(v8::Isolate* isolate) +{ + static v8::Persistent gKey{ isolate, v8::String::NewFromUtf8(isolate, "g", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return gKey.Get(isolate); +} + +v8::Local V8::RGBA_BKey(v8::Isolate* isolate) +{ + static v8::Persistent bKey{ isolate, v8::String::NewFromUtf8(isolate, "b", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return bKey.Get(isolate); +} + +v8::Local V8::RGBA_AKey(v8::Isolate* isolate) +{ + static v8::Persistent aKey{ isolate, v8::String::NewFromUtf8(isolate, "a", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return aKey.Get(isolate); +} + +v8::Local V8::Fire_PosKey(v8::Isolate* isolate) +{ + static v8::Persistent aKey{ isolate, v8::String::NewFromUtf8(isolate, "pos", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return aKey.Get(isolate); +} + +v8::Local V8::Fire_WeaponKey(v8::Isolate* isolate) +{ + static v8::Persistent aKey{ isolate, v8::String::NewFromUtf8(isolate, "weapon", + v8::NewStringType::kInternalized) + .ToLocalChecked() }; + + return aKey.Get(isolate); +} + +bool V8::SafeToBoolean(v8::Local val, v8::Isolate* isolate, bool& out) +{ + out = val->ToBoolean(isolate)->Value(); + return true; +} + +bool V8::SafeToInteger(v8::Local val, v8::Local ctx, int64_t& out) +{ + v8::MaybeLocal maybeVal = val->ToInteger(ctx); + if(maybeVal.IsEmpty()) return false; + out = maybeVal.ToLocalChecked()->Value(); + return true; +} + +bool V8::SafeToUInt64(v8::Local val, v8::Local ctx, uint64_t& out) +{ + v8::MaybeLocal maybeVal = val->ToBigInt(ctx); + if(maybeVal.IsEmpty()) return false; + out = maybeVal.ToLocalChecked()->Uint64Value(); + return true; +} + +bool V8::SafeToInt64(v8::Local val, v8::Local ctx, int64_t& out) +{ + v8::MaybeLocal maybeVal = val->ToBigInt(ctx); + if(maybeVal.IsEmpty()) return false; + out = maybeVal.ToLocalChecked()->Int64Value(); + return true; +} + +bool V8::SafeToUInt32(v8::Local val, v8::Local ctx, uint32_t& out) +{ + v8::MaybeLocal maybeVal = val->ToUint32(ctx); + if(maybeVal.IsEmpty()) return false; + out = maybeVal.ToLocalChecked()->Value(); + return true; +} + +bool V8::SafeToInt32(v8::Local val, v8::Local ctx, int32_t& out) +{ + v8::MaybeLocal maybeVal = val->ToInt32(ctx); + if(maybeVal.IsEmpty()) return false; + out = maybeVal.ToLocalChecked()->Value(); + return true; +} + +bool V8::SafeToNumber(v8::Local val, v8::Local ctx, double& out) +{ + v8::MaybeLocal maybeVal = val->ToNumber(ctx); + if(maybeVal.IsEmpty()) return false; + out = maybeVal.ToLocalChecked()->Value(); + return true; +} + +bool V8::SafeToString(v8::Local val, v8::Isolate* isolate, v8::Local ctx, alt::String& out) +{ + v8::MaybeLocal maybeVal = val->ToString(ctx); + if(maybeVal.IsEmpty()) return false; + out = *v8::String::Utf8Value(isolate, maybeVal.ToLocalChecked()); + return true; +} + +bool V8::SafeToFunction(v8::Local val, v8::Local ctx, v8::Local& out) +{ + if (val->IsFunction()) + { + out = val.As(); + return true; + } + + return false; +} + +bool V8::SafeToObject(v8::Local val, v8::Local ctx, v8::Local& out) +{ + v8::MaybeLocal maybeVal = val->ToObject(ctx); + if (maybeVal.IsEmpty()) return false; + out = maybeVal.ToLocalChecked(); + return true; +} + +bool V8::SafeToRGBA(v8::Local val, v8::Local ctx, alt::RGBA& out) +{ + v8::MaybeLocal maybeVal = val->ToObject(ctx); + if (!maybeVal.IsEmpty()) + { + v8::Local val = maybeVal.ToLocalChecked(); + + uint32_t r, g, b, a; + if (SafeToUInt32(V8::Get(ctx, val, "r"), ctx, r) + && SafeToUInt32(V8::Get(ctx, val, "g"), ctx, g) + && SafeToUInt32(V8::Get(ctx, val, "b"), ctx, b) + && SafeToUInt32(V8::Get(ctx, val, "a"), ctx, a) + ) { + out = alt::RGBA{ uint8_t(r), uint8_t(g), uint8_t(b), uint8_t(a) }; + return true; + } + } + + return false; +} + +bool V8::SafeToVector3(v8::Local val, v8::Local ctx, alt::Vector3f& out) +{ + v8::MaybeLocal maybeVal = val->ToObject(ctx); + if (!maybeVal.IsEmpty()) + { + v8::Local val = maybeVal.ToLocalChecked(); + + double x, y, z; + if (SafeToNumber(V8::Get(ctx, val, "x"), ctx, x) + && SafeToNumber(V8::Get(ctx, val, "y"), ctx, y) + && SafeToNumber(V8::Get(ctx, val, "z"), ctx, z) + ) { + out = alt::Vector3f{ float(x), float(y), float(z) }; + return true; + } + } + + return false; +} + +bool V8::SafeToVector2(v8::Local val, v8::Local ctx, alt::Vector2f& out) +{ + v8::MaybeLocal maybeVal = val->ToObject(ctx); + if (!maybeVal.IsEmpty()) + { + v8::Local val = maybeVal.ToLocalChecked(); + + double x, y; + if (SafeToNumber(V8::Get(ctx, val, "x"), ctx, x) + && SafeToNumber(V8::Get(ctx, val, "y"), ctx, y) + ) { + out = alt::Vector2f{ float(x), float(y) }; + return true; + } + } + + return false; +} + +bool V8::SafeToArrayBuffer(v8::Local val, v8::Local ctx, v8::Local& out) +{ + if (val->IsArrayBuffer()) + { + out = val.As(); + return true; + } + + return false; +} + +bool V8::SafeToArrayBufferView(v8::Local val, v8::Local ctx, v8::Local& out) +{ + if (val->IsArrayBufferView()) + { + out = val.As(); + return true; + } + + return false; +} + +bool V8::SafeToArray(v8::Local val, v8::Local ctx, v8::Local& out) +{ + if (val->IsArray()) + { + out = val.As(); + return true; + } + + return false; +} + +std::vector V8::EventHandler::GetCallbacks(V8ResourceImpl *impl, const alt::CEvent *e) +{ + return callbacksGetter(impl, e); +} + +std::vector> V8::EventHandler::GetArgs(V8ResourceImpl* impl, const alt::CEvent* e) +{ + std::vector> args; + argsGetter(impl, e, args); + return args; +} + +V8::EventHandler* V8::EventHandler::Get(const alt::CEvent* e) +{ + auto& _all = all(); + auto it = _all.find(e->GetType()); + + return (it != _all.end()) ? it->second : nullptr; +} + +void V8::EventHandler::Register(alt::CEvent::Type type, EventHandler* handler) +{ + auto& _all = all(); + if (_all.count(type) == 0) + { + _all.insert({ type, handler }); + } + else + { + Log::Error << "Handler for " << (int)type << " already defined" << Log::Endl; + } +} + +V8::EventHandler::CallbacksGetter V8::LocalEventHandler::GetCallbacksGetter(const std::string& name) +{ + return [name](V8ResourceImpl* resource, const alt::CEvent*) -> std::vector { + return resource->GetLocalHandlers(name); + }; +} + +V8::EventHandler::EventHandler(alt::CEvent::Type type, CallbacksGetter &&_handlersGetter, ArgsGetter &&_argsGetter) + : callbacksGetter(std::move(_handlersGetter)), argsGetter(std::move(_argsGetter)), type(type) +{ + Register(type, this); +} + +// Temp issue fix for https://stackoverflow.com/questions/9459980/c-global-variable-not-initialized-when-linked-through-static-libraries-but-ok +void V8::EventHandler::Reference() { + Log::Info << "[V8] Registered handler for " << std::to_string((int)type) << Log::Endl; +} + +alt::String V8::Stringify(v8::Local val, v8::Local ctx) +{ + v8::Local str; + if(!val->ToString(ctx).ToLocal(&str)) return nullptr; + if (val->IsObject() && strcmp(*v8::String::Utf8Value(ctx->GetIsolate(), str), "[object Object]") == 0) { + v8::MaybeLocal maybe = v8::JSON::Stringify(ctx, val); + v8::Local stringified; + if (maybe.ToLocal(&stringified)) str = stringified; + } + return *v8::String::Utf8Value(ctx->GetIsolate(), str); +} + +alt::String V8::GetJSValueTypeName(v8::Local val) +{ + if(val->IsUndefined()) return "undefined"; + if(val->IsNull()) return "null"; + if(val->IsNumber()) return "number"; + if(val->IsString()) return "string"; + if(val->IsArray()) return "array"; + if(val->IsBoolean()) return "bool"; + if(val->IsBigInt()) return "bigint"; + if(val->IsArrayBuffer()) return "arraybuffer"; + if(val->IsArrayBufferView()) return "arraybufferview"; + if(val->IsDate()) return "date"; + if(val->IsArgumentsObject()) return "arguments"; + if(val->IsAsyncFunction()) return "asyncfunction"; + if(val->IsExternal()) return "external"; + if(val->IsDataView()) return "dataview"; + if(val->IsSymbol()) return "symbol"; + if(val->IsFunction()) return "function"; + if(val->IsRegExp()) return "regexp"; + if(val->IsGeneratorFunction()) return "generatorfunction"; + if(val->IsPromise()) return "promise"; + if(val->IsProxy()) return "proxy"; + if(val->IsMap()) return "map"; + if(val->IsSet()) return "set"; + if(val->IsWeakMap()) return "weakmap"; + if(val->IsWeakSet()) return "weakset"; + if(val->IsTypedArray()) return "typedarray"; + if(val->IsProxy()) return "proxy"; + if(val->IsObject()) return "object"; + else return "unknown"; +} diff --git a/shared/V8Helpers.h b/shared/V8Helpers.h new file mode 100644 index 00000000..43b4a5da --- /dev/null +++ b/shared/V8Helpers.h @@ -0,0 +1,497 @@ +#pragma once + +#include +#include + +#include +#include + +#include "cpp-sdk/objects/IEntity.h" +#include "cpp-sdk/types/MValue.h" +#include "V8Entity.h" + +namespace V8Helpers +{ + inline void Throw(v8::Isolate *isolate, const std::string &msg) + { + isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(isolate, msg.data(), v8::NewStringType::kNormal, msg.size()).ToLocalChecked())); + } + + bool TryCatch(const std::function &fn); + + struct HashFunc + { + size_t operator()(v8::Local fn) const + { + return fn->GetIdentityHash(); + } + }; + + struct EqFunc + { + size_t operator()(v8::Local lhs, v8::Local rhs) const + { + return lhs->StrictEquals(rhs); + } + }; + + class Binding + { + public: + using Callback = std::function ctx, v8::Local exports)>; + + Binding(Callback &&fn) + { + All().push_back(std::move(fn)); + } + + static std::vector &All() + { + static std::vector _All; + return _All; + } + + static void RegisterAll(v8::Local ctx, v8::Local exports) + { + for (auto &binding : All()) + binding(ctx, exports); + } + }; + + void RegisterFunc(v8::Local exports, const std::string& _name, v8::FunctionCallback cb, void* data = nullptr); + + void FunctionCallback(const v8::FunctionCallbackInfo &info); + + alt::MValue V8ToMValue(v8::Local val); + + v8::Local MValueToV8(alt::MValueConst val); + + void MValueArgsToV8(alt::MValueArgs args, std::vector>& v8Args); + + + void SetAccessor(v8::Local tpl, v8::Isolate* isolate, const char* name, v8::AccessorGetterCallback getter, + v8::AccessorSetterCallback setter = nullptr); + +}; // namespace V8Helpers + +class V8ResourceImpl; + +namespace V8 +{ + template + using CPersistent = v8::Persistent>; + + class SourceLocation + { + public: + SourceLocation(std::string &&fileName, int line); + SourceLocation(){}; + + const std::string &GetFileName() const { return fileName; } + int GetLineNumber() const { return line; } + + static SourceLocation GetCurrent(v8::Isolate *isolate); + + private: + std::string fileName; + int line = 0; + }; + + struct EventCallback + { + v8::UniquePersistent fn; + SourceLocation location; + bool removed = false; + bool once; + + EventCallback(v8::Isolate *isolate, v8::Local _fn, SourceLocation &&location, bool once = false) : fn(isolate, _fn), location(std::move(location)), once(once) {} + }; + + class EventHandler + { + public: + using CallbacksGetter = std::function(V8ResourceImpl *resource, const alt::CEvent *)>; + using ArgsGetter = std::function> &args)>; + + EventHandler(alt::CEvent::Type type, CallbacksGetter &&_handlersGetter, ArgsGetter &&_argsGetter); + + // Temp issue fix for https://stackoverflow.com/questions/9459980/c-global-variable-not-initialized-when-linked-through-static-libraries-but-ok + void Reference(); + + std::vector GetCallbacks(V8ResourceImpl *impl, const alt::CEvent *e); + std::vector> GetArgs(V8ResourceImpl *impl, const alt::CEvent *e); + + static EventHandler *Get(const alt::CEvent *e); + + private: + alt::CEvent::Type type; + CallbacksGetter callbacksGetter; + ArgsGetter argsGetter; + + static std::unordered_map &all() + { + static std::unordered_map _all; + return _all; + } + + static void Register(alt::CEvent::Type type, EventHandler *handler); + }; + + class LocalEventHandler : public EventHandler + { + public: + LocalEventHandler(alt::CEvent::Type type, const std::string &name, ArgsGetter &&argsGetter) : EventHandler(type, std::move(GetCallbacksGetter(name)), std::move(argsGetter)) {} + + private: + static CallbacksGetter GetCallbacksGetter(const std::string &name); + }; + + void DefineOwnProperty(v8::Isolate *isolate, v8::Local ctx, v8::Local val, + const char *name, v8::Local value, v8::PropertyAttribute attributes = v8::PropertyAttribute::None); + + void DefineOwnProperty(v8::Isolate *isolate, v8::Local ctx, v8::Local val, + v8::Local name, v8::Local value, v8::PropertyAttribute attributes = v8::PropertyAttribute::None); + + void SetAccessor(v8::Isolate* isolate, v8::Local tpl, const char* name, + v8::AccessorGetterCallback getter, v8::AccessorSetterCallback setter = nullptr); + + void SetMethod(v8::Isolate *isolate, v8::Local tpl, const char *name, + v8::FunctionCallback callback); + + void SetStaticAccessor(v8::Isolate *isolate, v8::Local tpl, const char *name, + v8::AccessorGetterCallback getter, v8::AccessorSetterCallback setter = nullptr); + + void SetStaticMethod(v8::Isolate *isolate, v8::Local tpl, const char *name, + v8::FunctionCallback callback); + + void SetFunction(v8::Isolate *isolate, v8::Local ctx, v8::Local target, + const char *name, v8::FunctionCallback cb, void *userData = nullptr); + + v8::Local Get(v8::Local ctx, v8::Local obj, const char *name); + v8::Local Get(v8::Local ctx, v8::Local obj, v8::Local name); + + v8::Local New(v8::Isolate *isolate, v8::Local ctx, v8::Local constructor, std::vector> &args); + + // TODO: create c++ classes for v8 classes and move there + v8::Local Vector3_XKey(v8::Isolate *isolate); + v8::Local Vector3_YKey(v8::Isolate *isolate); + v8::Local Vector3_ZKey(v8::Isolate *isolate); + + v8::Local RGBA_RKey(v8::Isolate *isolate); + v8::Local RGBA_GKey(v8::Isolate *isolate); + v8::Local RGBA_BKey(v8::Isolate *isolate); + v8::Local RGBA_AKey(v8::Isolate *isolate); + + v8::Local Fire_PosKey(v8::Isolate *isolate); + v8::Local Fire_WeaponKey(v8::Isolate *isolate); + + bool SafeToBoolean(v8::Local val, v8::Isolate *isolate, bool &out); + bool SafeToInteger(v8::Local val, v8::Local ctx, int64_t &out); + bool SafeToNumber(v8::Local val, v8::Local ctx, double &out); + bool SafeToString(v8::Local val, v8::Isolate *isolate, v8::Local ctx, alt::String &out); + bool SafeToFunction(v8::Local val, v8::Local ctx, v8::Local& out); + bool SafeToObject(v8::Local val, v8::Local ctx, v8::Local& out); + bool SafeToRGBA(v8::Local val, v8::Local ctx, alt::RGBA& out); + bool SafeToVector3(v8::Local val, v8::Local ctx, alt::Vector3f& out); + bool SafeToVector2(v8::Local val, v8::Local ctx, alt::Vector2f& out); + bool SafeToArrayBuffer(v8::Local val, v8::Local ctx, v8::Local& out); + bool SafeToArrayBufferView(v8::Local val, v8::Local ctx, v8::Local& out); + bool SafeToArray(v8::Local val, v8::Local ctx, v8::Local& out); + + bool SafeToUInt64(v8::Local val, v8::Local ctx, uint64_t& out); + bool SafeToInt64(v8::Local val, v8::Local ctx, int64_t& out); + bool SafeToUInt32(v8::Local val, v8::Local ctx, uint32_t& out); + bool SafeToInt32(v8::Local val, v8::Local ctx, int32_t& out); + + template + bool SafeToBaseObject(v8::Local val, v8::Isolate *isolate, alt::Ref &out) + { + V8Entity *v8BaseObject = V8Entity::Get(val); + if (!v8BaseObject) + return false; + + out = v8BaseObject->GetHandle().As(); + if (out.IsEmpty()) + return false; + + return true; + } + + alt::String Stringify(v8::Local val, v8::Local ctx); + alt::String GetJSValueTypeName(v8::Local val); +} // namespace V8 + +#define V8_GET_ISOLATE() v8::Isolate *isolate = info.GetIsolate() +#define V8_GET_CONTEXT() v8::Local ctx = isolate->GetEnteredOrMicrotaskContext() +#define V8_GET_ISOLATE_CONTEXT() \ + V8_GET_ISOLATE(); \ + V8_GET_CONTEXT() + +#define V8_GET_RESOURCE() \ + V8ResourceImpl *resource = V8ResourceImpl::Get(isolate->GetEnteredOrMicrotaskContext()); \ + V8_CHECK(resource, "invalid resource"); + +#define V8_GET_IRESOURCE() \ + alt::IResource *resource = V8ResourceImpl::GetResource(isolate->GetEnteredOrMicrotaskContext()); \ + V8_CHECK(resource, "invalid resource"); + +#define V8_GET_ISOLATE_CONTEXT_RESOURCE() \ + V8_GET_ISOLATE_CONTEXT(); \ + V8_GET_RESOURCE() + +#define V8_GET_ISOLATE_CONTEXT_IRESOURCE() \ + V8_GET_ISOLATE_CONTEXT(); \ + V8_GET_IRESOURCE() + +#define V8_CHECK_RETN(a, b, c) \ + if (!(a)) \ + { \ + V8Helpers::Throw(isolate, (b)); \ + return c; \ + } +#define V8_CHECK(a, b) V8_CHECK_RETN(a, b, ) + +#define V8_GET_THIS_BASE_OBJECT(val, type) \ + ::alt::Ref val; \ + { \ + V8Entity *__val = V8Entity::Get(info.This()); \ + V8_CHECK(__val, "baseobject is invalid"); \ + val = __val->GetHandle().As(); \ + V8_CHECK(val, "baseobject is not of type " #type); \ + } + +// idx starts with 1 +#define V8_GET_THIS_INTERNAL_FIELD_OBJECT(idx, val) \ + auto val = info.This()->GetInternalField((idx)-1)->ToObject(isolate->GetEnteredOrMicrotaskContext()).ToLocalChecked(); + +// idx starts with 1 +#define V8_GET_THIS_INTERNAL_FIELD_V8ENTITY(idx, val) \ + auto val = V8Entity::Get(info.This()->GetInternalField((idx)-1)->ToObject(isolate->GetEnteredOrMicrotaskContext()).ToLocalChecked()); + +// idx starts with 1 +#define V8_GET_THIS_INTERNAL_FIELD_ENTITY(idx, val, type) \ + auto val = V8Entity::Get(info.This()->GetInternalField((idx)-1)->ToObject(isolate->GetEnteredOrMicrotaskContext()).ToLocalChecked())->GetHandle().As(); + +// idx starts with 1 +#define V8_GET_THIS_INTERNAL_FIELD_INTEGER(idx, val) \ + auto val = info.This()->GetInternalField((idx)-1)->IntegerValue(ctx).ToChecked(); + +// idx starts with 1 +#define V8_GET_THIS_INTERNAL_FIELD_UINT32(idx, val) \ + auto val = info.This()->GetInternalField((idx)-1)->Uint32Value(ctx).ToChecked(); + +// idx starts with 1 +#define V8_GET_THIS_INTERNAL_FIELD_EXTERNAL(idx, val, type) \ + auto val = static_cast(info.This()->GetInternalField((idx)-1).As()->Value()); + +// idx starts with 1 +#define V8_GET_THIS_INTERNAL_FIELD_PTR(idx, val, type) \ + auto val = static_cast(info.This()->GetAlignedPointerFromInternalField((idx)-1)); + +#define V8_CHECK_CONSTRUCTOR() V8_CHECK(info.IsConstructCall(), "function can't be called without new") + +#define V8_CHECK_ARGS_LEN(count) V8_CHECK(info.Length() == (count), #count " arguments expected") +#define V8_CHECK_ARGS_LEN2(count1, count2) V8_CHECK(info.Length() == (count1) || info.Length() == (count2), #count1 " or " #count2 " arguments expected") +#define V8_CHECK_ARGS_LEN_MIN_MAX(count1, count2) V8_CHECK(info.Length() >= (count1) && info.Length() <= (count2), "Minimum " #count1 ", maximum " #count2 " arguments expected") +#define V8_CHECK_ARGS_LEN_MIN(count) V8_CHECK(info.Length() >= (count), "Minimum " #count " arguments expected") + +#define V8_TO_BOOLEAN(v8Val, val) \ + bool val; \ + V8_CHECK(V8::SafeToBoolean((v8Val), isolate, val), "Failed to convert value to boolean") + +#define V8_TO_NUMBER(v8Val, val) \ + double val; \ + V8_CHECK(V8::SafeToNumber((v8Val), ctx, val), "Failed to convert value to number") + +#define V8_TO_INTEGER(v8Val, val) \ + int64_t val; \ + V8_CHECK(V8::SafeToInteger((v8Val), ctx, val), "Failed to convert value to integer") + +#define V8_TO_INT32(v8Val, val) \ + int32_t val; \ + V8_CHECK(V8::SafeToInt32((v8Val), ctx, val), "Failed to convert value to integer") + +#define V8_TO_STRING(v8Val, val) \ + alt::String val; \ + V8_CHECK(V8::SafeToString((v8Val), isolate, ctx, val), "Failed to convert value to string") + +#define V8_TO_OBJECT(v8Val, val) \ + v8::Local val; \ + V8_CHECK(V8::SafeToObject((v8Val), ctx, val), "Failed to convert value to object") + +#define V8_TO_VECTOR3(v8Val, val) \ + alt::Vector3f val; \ + V8_CHECK(V8::SafeToVector3((v8Val), ctx, val), "Failed to convert value to Vector3") + +#define V8_TO_VECTOR2(v8Val, val) \ + alt::Vector2f val; \ + V8_CHECK(V8::SafeToVector2((v8Val), ctx, val), "Failed to convert value to Vector2") + +#define V8_TO_RGBA(v8Val, val) \ + alt::RGBA val; \ + V8_CHECK(V8::SafeToRGBA((v8Val), ctx, val), "Failed to convert value to RGBA") + +#define V8_OBJECT_GET_NUMBER(v8Val, prop, val) \ + V8_TO_NUMBER((v8Val)->Get(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked()).ToLocalChecked(), val) + +#define V8_OBJECT_SET_NUMBER(v8Val, prop, val) \ + (v8Val)->Set(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked(), v8::Number::New(isolate, val)); + +#define V8_OBJECT_GET_INT(v8Val, prop, val) \ + V8_TO_INTEGER((v8Val)->Get(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked()).ToLocalChecked(), val) + +#define V8_OBJECT_SET_INT(v8Val, prop, val) \ + (v8Val)->Set(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked(), v8::Integer::New(isolate, val)); + +#define V8_OBJECT_SET_UINT(v8Val, prop, val) \ + (v8Val)->Set(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked(), v8::Integer::NewFromUnsigned(isolate, val)); + +#define V8_OBJECT_GET_BOOLEAN(v8Val, prop, val) \ + V8_TO_BOOLEAN((v8Val)->Get(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked()).ToLocalChecked(), val) + +#define V8_OBJECT_SET_BOOLEAN(v8Val, prop, val) \ + (v8Val)->Set(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked(), v8::Boolean::New(isolate, val)); + +#define V8_OBJECT_GET_STRING(v8Val, prop, val) \ + V8_TO_STRING((v8Val)->Get(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked()).ToLocalChecked(), val) + +#define V8_OBJECT_SET_STRING(v8Val, prop, val) \ + if(!val.IsEmpty()) (v8Val)->Set(ctx, v8::String::NewFromUtf8(isolate, prop).ToLocalChecked(), v8::String::NewFromUtf8(isolate, val.CStr()).ToLocalChecked()); + +#define V8_NEW_STRING(val) v8::String::NewFromUtf8(isolate, val).ToLocalChecked() + +#define V8_NEW_OBJECT(val) \ + v8::Local val = v8::Object::New(isolate); + +#define V8_NEW_ARGS(val) \ + std::vector> val; + +#define V8_ADD_ARG(args, val) \ + (args).push_back(val); + +// idx starts with 1 +#define V8_ARG_CHECK_NUMBER(idx) V8_CHECK(info[(idx)-1]->IsNumber(), "Argument " #idx " must be a number") + +// idx starts with 1 +#define V8_ARG_TO_BOOLEAN(idx, val) \ + bool val; \ + V8_CHECK(V8::SafeToBoolean(info[(idx)-1], isolate, val), "Failed to convert argument " #idx " to boolean") + +// idx starts with 1 +#define V8_ARG_TO_BOOLEAN_OPT(idx, val, defaultVal) \ + bool val; \ + if (info.Length() >= (idx)) \ + { \ + V8_CHECK(V8::SafeToBoolean(info[(idx)-1], isolate, val), "Failed to convert argument " #idx " to boolean"); \ + } \ + else \ + { \ + val = defaultVal; \ + } + +// idx starts with 1 +#define V8_ARG_TO_MVALUE(idx, val) \ + alt::MValue val = V8Helpers::V8ToMValue(info[(idx)-1]); + +// idx starts with 1 +#define V8_ARG_TO_INT(idx, val) \ + int64_t val; \ + V8_CHECK(V8::SafeToInteger(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to integer") + +// idx starts with 1 +#define V8_ARG_TO_NUMBER(idx, val) \ + double val; \ + V8_CHECK(V8::SafeToNumber(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to number") + +// idx starts with 1 +#define V8_ARG_TO_STRING(idx, val) \ + alt::String val; \ + V8_CHECK(V8::SafeToString(info[(idx)-1], isolate, ctx, val), "Failed to convert argument " #idx " to string") + +// idx starts with 1 +#define V8_ARG_TO_FUNCTION(idx, val) \ + v8::Local val; \ + V8_CHECK(V8::SafeToFunction(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to function") + +// idx starts with 1 +#define V8_ARG_TO_OBJECT(idx, val) \ + v8::Local val; \ + V8_CHECK(V8::SafeToObject(info[(idx) - 1], ctx, val), "Failed to convert argument " #idx " to object") + +// idx starts with 1 +#define V8_ARG_TO_BASE_OBJECT(idx, val, type, jsClassName) \ + alt::Ref val; \ + V8_CHECK(V8::SafeToBaseObject(info[(idx)-1], isolate, val), "Argument " #idx " must be a " jsClassName) + +// idx starts with 1 +#define V8_ARG_TO_ARRAY_BUFFER(idx, val) \ + v8::Local val; \ + V8_CHECK(V8::SafeToArrayBuffer(info[(idx) - 1], ctx, val), "Failed to convert argument " #idx " to ArrayBuffer") + +// idx starts with 1 +#define V8_ARG_TO_ARRAY_BUFFER_VIEW(idx, val) \ + v8::Local val; \ + V8_CHECK(V8::SafeToArrayBufferView(info[(idx) - 1], ctx, val), "Failed to convert argument " #idx " to ArrayBufferView") + +#define V8_ARG_TO_ARRAY(idx, val) \ + v8::Local val; \ + V8_CHECK(V8::SafeToArray(info[(idx) - 1], ctx, val), "Failed to convert argument " #idx " to Array") + +// idx starts with 1 +#define V8_ARG_TO_UINT64(idx, val) \ + uint64_t val; \ + V8_CHECK(V8::SafeToUInt64(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to integer") + +// idx starts with 1 +#define V8_ARG_TO_INT64(idx, val) \ + int64_t val; \ + V8_CHECK(V8::SafeToInt64(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to integer") + +// idx starts with 1 +#define V8_ARG_TO_UINT32(idx, val) \ + uint32_t val; \ + V8_CHECK(V8::SafeToUInt32(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to integer") + +// idx starts with 1 +#define V8_ARG_TO_INT32(idx, val) \ + int32_t val; \ + V8_CHECK(V8::SafeToInt32(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to integer") + +// idx starts with 1 +#define V8_ARG_TO_VECTOR3(idx, val) \ + alt::Vector3f val; \ + V8_CHECK(V8::SafeToVector3(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to vector3") + +// idx starts with 1 +#define V8_ARG_TO_RGBA(idx, val) \ + alt::RGBA val; \ + V8_CHECK(V8::SafeToRGBA(info[(idx)-1], ctx, val), "Failed to convert argument " #idx " to rgba") + +#define V8_RETURN(val) info.GetReturnValue().Set(val) +#define V8_RETURN_NULL() info.GetReturnValue().SetNull() +#define V8_RETURN_BOOLEAN(val) V8_RETURN(val) +#define V8_RETURN_INT(val) V8_RETURN(static_cast(val)) +#define V8_RETURN_UINT(val) V8_RETURN(static_cast(val)) +#define V8_RETURN_NUMBER(val) V8_RETURN(static_cast(val)) +#define V8_RETURN_STRING(val) V8_RETURN(v8::String::NewFromUtf8(isolate, (val), v8::NewStringType::kNormal).ToLocalChecked()) +#define V8_RETURN_ALT_STRING(val) V8_RETURN(v8::String::NewFromUtf8(isolate, (val).CStr(), v8::NewStringType::kNormal).ToLocalChecked()) +#define V8_RETURN_MVALUE(val) V8_RETURN(V8Helpers::MValueToV8(val)) +#define V8_RETURN_UINT64(val) V8_RETURN(v8::BigInt::NewFromUnsigned(isolate, static_cast(val))) +#define V8_RETURN_INT64(val) V8_RETURN(v8::BigInt::New(isolate, static_cast(val))) +#define V8_RETURN_VECTOR3(val) V8_RETURN(resource->CreateVector3(val)) +#define V8_RETURN_VECTOR2(val) V8_RETURN(resource->CreateVector2(val)) +#define V8_RETURN_RGBA(val) V8_RETURN(resource->CreateRGBA(val)) +#define V8_RETURN_ENUM(val) V8_RETURN(uint32_t(val)) + +#define V8_RETURN_BASE_OBJECT(baseObjectRef) V8_RETURN(resource->GetBaseObjectOrNull(baseObjectRef)) + +#define V8_BIND_BASE_OBJECT(baseObjectRef, reason) \ + { \ + V8_CHECK(!baseObjectRef.IsEmpty(), reason); \ + resource->BindEntity(info.This(), baseObjectRef); \ + } + +#define V8_EVENT_HANDLER extern V8::EventHandler +#define V8_LOCAL_EVENT_HANDLER extern V8::LocalEventHandler +#define V8_REFERENCE_EVENT_HANDLER(name) V8_EVENT_HANDLER name; name.Reference(); +#define V8_REFERENCE_LOCAL_EVENT_HANDLER(name) V8_LOCAL_EVENT_HANDLER name; name.Reference(); diff --git a/shared/V8Module.h b/shared/V8Module.h new file mode 100644 index 00000000..9a78a007 --- /dev/null +++ b/shared/V8Module.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +#include +#include +#include "V8Class.h" +#include + +class V8Module +{ + using Callback = std::function ctx, v8::Local exports)>; + +public: + static std::map &All() + { + static std::map all; + return all; + } + + static void Add(V8Module &module) + { + All()[module.moduleName] = &module; + } + + static void Add(std::initializer_list> modules) + { + for (auto &m : modules) + All()[m.get().moduleName] = &m.get(); + } + + static bool Exists(const std::string &name) + { + if (All().find(name) == All().end()) + return false; + else + return true; + } + + std::string moduleName; + std::unordered_set classes; + Callback creator; + V8Module* parentModule; + + template + V8Module( + std::string moduleName, + V8Module* parent, + std::initializer_list> _classes, + Callback fn) : moduleName(moduleName), creator(fn), parentModule(parent) + { + for (auto &c : _classes) + classes.insert(&c.get()); + + // All()[moduleName] = this; + } + + void Register(v8::Isolate *isolate, v8::Local context, v8::Local exports) + { + if(parentModule) parentModule->Register(isolate, context, exports); + // Load all classes + for (auto c : classes) + { + c->Register(isolate, context, exports); + } + + creator(context, exports); + } + + v8::Local GetExports(v8::Isolate *isolate, v8::Local context) + { + v8::Local _exports = v8::Object::New(isolate); + Register(isolate, context, _exports); + return _exports; + } +}; diff --git a/shared/V8ResourceImpl.cpp b/shared/V8ResourceImpl.cpp new file mode 100644 index 00000000..9016c28e --- /dev/null +++ b/shared/V8ResourceImpl.cpp @@ -0,0 +1,378 @@ + +#include "cpp-sdk/objects/IPlayer.h" +#include "cpp-sdk/objects/IVehicle.h" + +#include "V8ResourceImpl.h" + +#ifdef ALT_SERVER_API +#include "CNodeResourceImpl.h" +#include "node.h" +#endif + +using namespace alt; + +V8ResourceImpl::~V8ResourceImpl() { + for(auto& [obj, ent] : entities) { + delete ent; + } + + entities.clear(); +} + +extern V8Class v8Vector3, v8Vector2, v8RGBA, v8BaseObject; +bool V8ResourceImpl::Start() +{ + vector3Class.Reset(isolate, v8Vector3.JSValue(isolate, GetContext())); + vector2Class.Reset(isolate, v8Vector2.JSValue(isolate, GetContext())); + rgbaClass.Reset(isolate, v8RGBA.JSValue(isolate, GetContext())); + baseObjectClass.Reset(isolate, v8BaseObject.JSValue(isolate, GetContext())); + + return true; +} + +void V8ResourceImpl::OnTick() +{ + for (auto &id : oldTimers) + timers.erase(id); + + oldTimers.clear(); + + for (auto &p : timers) + { + int64_t time = GetTime(); + + if (!p.second->Update(time)) + RemoveTimer(p.first); + + if (GetTime() - time > 10) + { + auto &location = p.second->GetLocation(); + + if (location.GetLineNumber() != 0) + { + Log::Warning << "Timer at " + << resource->GetName() << ":" << location.GetFileName() << ":" << location.GetLineNumber() + << " was too long " << GetTime() - time << "ms" << Log::Endl; + } + else + { + Log::Warning << "Timer at " + << resource->GetName() << ":" << location.GetFileName() + << " was too long " << GetTime() - time << "ms" << Log::Endl; + } + } + } + + for (auto it = localHandlers.begin(); it != localHandlers.end();) + { + if (it->second.removed) + it = localHandlers.erase(it); + else + ++it; + } + + for (auto it = remoteHandlers.begin(); it != remoteHandlers.end();) + { + if (it->second.removed) + it = remoteHandlers.erase(it); + else + ++it; + } + + for (auto it = localGenericHandlers.begin(); it != localGenericHandlers.end(); it++) + { + if (it->removed) localGenericHandlers.erase(it); + } + + for (auto it = remoteGenericHandlers.begin(); it != remoteGenericHandlers.end(); it++) + { + if (it->removed) remoteGenericHandlers.erase(it); + } + + promiseRejections.ProcessQueue(this); +} + +void V8ResourceImpl::BindEntity(v8::Local val, alt::Ref handle) +{ + V8Entity *ent = new V8Entity(GetContext(), V8Entity::GetClass(handle), val, handle); + entities.insert({handle.Get(), ent}); +} + +v8::Local V8ResourceImpl::GetBaseObjectOrNull(alt::IBaseObject *handle) +{ + if (handle == nullptr) + { + return v8::Null(isolate); + } + else + { + return GetOrCreateEntity(handle)->GetJSVal(isolate); + } +} + +v8::Local V8ResourceImpl::CreateVector3(alt::Vector3f vec) +{ + std::vector> args{ + v8::Number::New(isolate, vec[0]), + v8::Number::New(isolate, vec[1]), + v8::Number::New(isolate, vec[2])}; + + return v8Vector3.CreateInstance(isolate, GetContext(), args); +} + +v8::Local V8ResourceImpl::CreateVector2(alt::Vector2f vec) +{ + std::vector> args{ + v8::Number::New(isolate, vec[0]), + v8::Number::New(isolate, vec[1])}; + + return v8Vector2.CreateInstance(isolate, GetContext(), args); +} + +v8::Local V8ResourceImpl::CreateRGBA(alt::RGBA rgba) +{ + std::vector> args{ + v8::Number::New(isolate, rgba.r), + v8::Number::New(isolate, rgba.g), + v8::Number::New(isolate, rgba.b), + v8::Number::New(isolate, rgba.a)}; + + return V8::New(isolate, GetContext(), rgbaClass.Get(isolate), args); +} + +bool V8ResourceImpl::IsVector3(v8::Local val) +{ + bool result = false; + val->InstanceOf(GetContext(), vector3Class.Get(isolate)).To(&result); + return result; +} + +bool V8ResourceImpl::IsVector2(v8::Local val) +{ + bool result = false; + val->InstanceOf(GetContext(), vector2Class.Get(isolate)).To(&result); + return result; +} + +bool V8ResourceImpl::IsRGBA(v8::Local val) +{ + bool result = false; + val->InstanceOf(GetContext(), rgbaClass.Get(isolate)).To(&result); + return result; +} + +bool V8ResourceImpl::IsBaseObject(v8::Local val) +{ + bool result = false; + val->InstanceOf(GetContext(), baseObjectClass.Get(isolate)).To(&result); + return result; +} + +void V8ResourceImpl::OnCreateBaseObject(alt::Ref handle) +{ + Log::Debug << "OnCreateBaseObject " << handle.Get() << " " << (entities.find(handle.Get()) != entities.end()) << Log::Endl; + + /*if (entities.find(handle.Get()) == entities.end()) + { + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + v8::Context::Scope scope(GetContext()); + CreateEntity(handle.Get()); + }*/ + + NotifyPoolUpdate(handle.Get()); +} + +void V8ResourceImpl::OnRemoveBaseObject(alt::Ref handle) +{ + NotifyPoolUpdate(handle.Get()); + + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope scope(GetContext()); + + V8Entity *ent = GetEntity(handle.Get()); + + if (!ent) + return; + + entities.erase(handle.Get()); + + // TODO: ent->SetWeak(); + ent->GetJSVal(isolate)->SetInternalField(0, v8::External::New(isolate, nullptr)); + delete ent; +} + +void V8ResourceImpl::NotifyPoolUpdate(alt::IBaseObject *ent) +{ + switch (ent->GetType()) + { + case alt::IBaseObject::Type::PLAYER: + playerPoolDirty = true; + break; + case alt::IBaseObject::Type::VEHICLE: + vehiclePoolDirty = true; + break; + } +} + +v8::Local V8ResourceImpl::GetAllPlayers() +{ + if (playerPoolDirty) + { + playerPoolDirty = false; + + Array> all = ICore::Instance().GetPlayers(); + v8::Local jsAll = v8::Array::New(isolate, all.GetSize()); + + for (uint32_t i = 0; i < all.GetSize(); ++i) + jsAll->Set(GetContext(), i, GetBaseObjectOrNull(all[i])); + + players.Reset(isolate, jsAll); + return jsAll; + } + + return players.Get(isolate); +} + +v8::Local V8ResourceImpl::GetAllVehicles() +{ + if (vehiclePoolDirty) + { + vehiclePoolDirty = false; + + Array> all = ICore::Instance().GetVehicles(); + v8::Local jsAll = v8::Array::New(isolate, all.GetSize()); + + for (uint32_t i = 0; i < all.GetSize(); ++i) + jsAll->Set(GetContext(), i, GetBaseObjectOrNull(all[i])); + + vehicles.Reset(isolate, jsAll); + return jsAll; + } + + return vehicles.Get(isolate); +} + +std::vector V8ResourceImpl::GetLocalHandlers(const std::string &name) +{ + std::vector handlers; + auto range = localHandlers.equal_range(name); + + for (auto it = range.first; it != range.second; ++it) + handlers.push_back(&it->second); + + return handlers; +} + +std::vector V8ResourceImpl::GetRemoteHandlers(const std::string &name) +{ + std::vector handlers; + auto range = remoteHandlers.equal_range(name); + + for (auto it = range.first; it != range.second; ++it) + handlers.push_back(&it->second); + + return handlers; +} + +std::vector V8ResourceImpl::GetGenericHandlers(bool local) +{ + std::vector handlers; + if(local) + { + for(auto& it : localGenericHandlers) + { + handlers.push_back(&it); + } + } + else + { + for(auto& it : remoteGenericHandlers) + { + handlers.push_back(&it); + } + } + return handlers; +} + +void V8ResourceImpl::InvokeEventHandlers(const alt::CEvent *ev, const std::vector &handlers, std::vector> &args) +{ + for (auto handler : handlers) + { + int64_t time = GetTime(); + + if (handler->removed) + continue; + + V8Helpers::TryCatch([&] { + v8::MaybeLocal retn = handler->fn.Get(isolate)->Call(GetContext(), v8::Undefined(isolate), args.size(), args.data()); + + if (retn.IsEmpty()) + return false; + + if (ev && retn.ToLocalChecked()->IsFalse()) + ev->Cancel(); + + return true; + }); + + if (GetTime() - time > 5) + { + if (handler->location.GetLineNumber() != 0) + { + Log::Warning << "Event handler at " + << resource->GetName() << ":" << handler->location.GetFileName() << ":" << handler->location.GetLineNumber() + << " was too long " << (GetTime() - time) << "ms" << Log::Endl; + } + else + { + Log::Warning << "Event handler at " + << resource->GetName() << ":" << handler->location.GetFileName() + << " was too long " << (GetTime() - time) << "ms" << Log::Endl; + } + } + + if (handler->once) + handler->removed = true; + } +} + +alt::MValue V8ResourceImpl::FunctionImpl::Call(alt::MValueArgs args) const +{ + v8::Isolate *isolate = resource->GetIsolate(); + + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + + v8::Local ctx = resource->GetContext(); + v8::Context::Scope scope(ctx); + +#ifdef ALT_SERVER_API + CNodeResourceImpl *nodeRes = static_cast(resource); + node::CallbackScope callbackScope(isolate, nodeRes->GetAsyncResource(), nodeRes->GetAsyncContext()); +#endif // ALT_SERVER_API + + std::vector> v8Args; + V8Helpers::MValueArgsToV8(args, v8Args); + + alt::MValue res; + V8Helpers::TryCatch([&] { + v8::MaybeLocal _res = function.Get(isolate)->CallAsFunction(resource->GetContext(), v8::Undefined(isolate), v8Args.size(), v8Args.data()); + + if (_res.IsEmpty()) + return false; + + res = V8Helpers::V8ToMValue(_res.ToLocalChecked()); + return true; + }); + + if (res.IsEmpty()) + res = alt::ICore::Instance().CreateMValueNone(); + + return res; +} diff --git a/shared/V8ResourceImpl.h b/shared/V8ResourceImpl.h new file mode 100644 index 00000000..bed9d0c2 --- /dev/null +++ b/shared/V8ResourceImpl.h @@ -0,0 +1,286 @@ +#pragma once + +#include +#include + +#include "cpp-sdk/types/MValue.h" +#include "cpp-sdk/IResource.h" +#include "cpp-sdk/objects/IBaseObject.h" + +#include "V8Entity.h" +#include "V8Timer.h" +#include "PromiseRejections.h" + +class V8ResourceImpl : public alt::IResource::Impl +{ +public: + class FunctionImpl : public alt::IMValueFunction::Impl + { + public: + FunctionImpl(V8ResourceImpl *_resource, v8::Local fn) : resource(_resource), + function(_resource->GetIsolate(), fn) + { + } + + alt::MValue Call(alt::MValueArgs args) const override; + + private: + V8ResourceImpl *resource; + v8::UniquePersistent function; + }; + + V8ResourceImpl(v8::Isolate *_isolate, alt::IResource *_resource) : isolate(_isolate), + resource(_resource) + { + } + + ~V8ResourceImpl(); + + struct PathInfo + { + alt::IPackage *pkg = nullptr; + std::string fileName; + std::string prefix; + }; + + bool Start() override; + + void OnTick() override; + + inline alt::IResource *GetResource() { return resource; } + inline v8::Isolate *GetIsolate() { return isolate; } + inline v8::Local GetContext() { return context.Get(isolate); } + + void SubscribeLocal(const std::string &ev, v8::Local cb, V8::SourceLocation &&location, bool once = false) + { + localHandlers.insert({ev, V8::EventCallback{isolate, cb, std::move(location), once}}); + } + + void SubscribeRemote(const std::string &ev, v8::Local cb, V8::SourceLocation &&location, bool once = false) + { + remoteHandlers.insert({ev, V8::EventCallback{isolate, cb, std::move(location), once}}); + } + + void SubscribeGenericLocal(v8::Local cb, V8::SourceLocation &&location, bool once = false) + { + localGenericHandlers.push_back(V8::EventCallback{isolate, cb, std::move(location), once}); + } + + void SubscribeGenericRemote(v8::Local cb, V8::SourceLocation &&location, bool once = false) + { + remoteGenericHandlers.push_back(V8::EventCallback{isolate, cb, std::move(location), once}); + } + + void UnsubscribeLocal(const std::string &ev, v8::Local cb) + { + auto range = localHandlers.equal_range(ev); + + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.fn.Get(isolate)->StrictEquals(cb)) + it->second.removed = true; + } + } + + void UnsubscribeRemote(const std::string &ev, v8::Local cb) + { + auto range = remoteHandlers.equal_range(ev); + + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.fn.Get(isolate)->StrictEquals(cb)) + it->second.removed = true; + } + } + + void UnsubscribeGenericLocal(v8::Local cb) + { + for (auto& it : localGenericHandlers) + { + if (it.fn.Get(isolate)->StrictEquals(cb)) + it.removed = true; + } + } + + void UnsubscribeGenericRemote(v8::Local cb) + { + for (auto& it : localGenericHandlers) + { + if (it.fn.Get(isolate)->StrictEquals(cb)) + it.removed = true; + } + } + + void DispatchStartEvent(bool error) + { + std::vector> args; + args.push_back(v8::Boolean::New(isolate, error)); + + InvokeEventHandlers(nullptr, GetLocalHandlers("resourceStart"), args); + } + + void DispatchStopEvent() + { + std::vector> args; + InvokeEventHandlers(nullptr, GetLocalHandlers("resourceStop"), args); + } + + void DispatchErrorEvent(const std::string& error, const std::string& stackTrace, const std::string& file, int32_t line) + { + std::vector> args = { V8_NEW_STRING(error.c_str()), V8_NEW_STRING(stackTrace.c_str()), V8_NEW_STRING(file.c_str()), v8::Integer::New(isolate, line) }; + InvokeEventHandlers(nullptr, GetLocalHandlers("resourceError"), args); + } + + V8Entity *GetEntity(alt::IBaseObject *handle) + { + auto it = entities.find(handle); + + if (it == entities.end()) + return nullptr; + + return it->second; + } + + V8Entity *CreateEntity(alt::IBaseObject *handle) + { + V8Class *_class = V8Entity::GetClass(handle); + + V8Entity *ent = new V8Entity(GetContext(), _class, _class->CreateInstance(GetContext()), handle); + entities.insert({handle, ent}); + return ent; + } + + void BindEntity(v8::Local val, alt::Ref handle); + + V8Entity *GetOrCreateEntity(alt::IBaseObject *handle, const char *className = "") + { + if (!handle) + Log::Error << __FUNCTION__ << " received invalid handle please contact developers if you see this" << Log::Endl; + + V8Entity *ent = GetEntity(handle); + + if (!ent) + ent = CreateEntity(handle); + + return ent; + } + + v8::Local GetBaseObjectOrNull(alt::IBaseObject *handle); + + template + v8::Local GetBaseObjectOrNull(const alt::Ref &handle) + { + return GetBaseObjectOrNull(handle.Get()); + } + + v8::Local CreateVector3(alt::Vector3f vec); + v8::Local CreateVector2(alt::Vector2f vec); + v8::Local CreateRGBA(alt::RGBA rgba); + + bool IsVector3(v8::Local val); + bool IsVector2(v8::Local val); + bool IsRGBA(v8::Local val); + bool IsBaseObject(v8::Local val); + + void OnCreateBaseObject(alt::Ref handle) override; + void OnRemoveBaseObject(alt::Ref handle) override; + + alt::MValue GetFunction(v8::Local val) + { + FunctionImpl *impl = new FunctionImpl{this, val.As()}; + return alt::ICore::Instance().CreateMValueFunction(impl); + } + + uint32_t CreateTimer(v8::Local context, v8::Local callback, uint32_t interval, bool once, V8::SourceLocation &&location) + { + uint32_t id = nextTimerId++; + //Log::Debug << "Create timer " << id << Log::Endl; + timers[id] = new V8Timer{isolate, context, GetTime(), callback, interval, once, std::move(location)}; + + return id; + } + + void RemoveTimer(uint32_t id) + { + oldTimers.push_back(id); + } + + void TimerBenchmark() + { + size_t totalCount = 0, everyTickCount = 0, intervalCount = 0, timeoutCount = 0; + totalCount = timers.size(); + for(auto [id, timer] : timers) + { + if(timer->GetInterval() == 0 && !timer->IsOnce()) everyTickCount += 1; + else if(timer->IsOnce()) timeoutCount += 1; + else intervalCount += 1; + } + + Log::Info << GetResource()->GetName() << ": " << totalCount << " running timers (" + << everyTickCount << " EveryTick, " << intervalCount << " Interval, " << timeoutCount << " Timeout" + << ")" << Log::Endl; + } + + void NotifyPoolUpdate(alt::IBaseObject *ent); + + v8::Local GetAllPlayers(); + v8::Local GetAllVehicles(); + + std::vector GetLocalHandlers(const std::string &name); + std::vector GetRemoteHandlers(const std::string &name); + std::vector GetGenericHandlers(bool local); + + static V8ResourceImpl *Get(v8::Local ctx) + { + alt::IResource *resource = GetResource(ctx); + return resource + ? static_cast(resource->GetImpl()) + : nullptr; + } + + static alt::IResource *GetResource(v8::Local ctx) + { + return static_cast(ctx->GetAlignedPointerFromEmbedderData(1)); + } + +protected: + v8::Isolate *isolate; + alt::IResource *resource; + + v8::Persistent context; + + std::unordered_map entities; + std::unordered_map timers; + + std::unordered_multimap localHandlers; + std::unordered_multimap remoteHandlers; + std::vector localGenericHandlers; + std::vector remoteGenericHandlers; + + + uint32_t nextTimerId = 0; + std::vector oldTimers; + + bool playerPoolDirty = true; + v8::UniquePersistent players; + + bool vehiclePoolDirty = true; + v8::UniquePersistent vehicles; + + V8::PromiseRejections promiseRejections; + + v8::Persistent vector3Class; + v8::Persistent vector2Class; + v8::Persistent rgbaClass; + v8::Persistent baseObjectClass; + + // TEMP + static int64_t GetTime() + { + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + } + + void InvokeEventHandlers(const alt::CEvent *ev, const std::vector &handlers, std::vector> &args); +}; diff --git a/shared/V8Timer.h b/shared/V8Timer.h new file mode 100644 index 00000000..a4ea3138 --- /dev/null +++ b/shared/V8Timer.h @@ -0,0 +1,49 @@ +#pragma once + +#include "V8Helpers.h" +#include "V8ResourceImpl.h" + +class V8Timer +{ +public: + V8Timer(v8::Isolate *_isolate, v8::Local _context, int64_t curTime, v8::Local _callback, uint32_t _interval, bool _once, V8::SourceLocation &&_location) + : isolate(_isolate), + context(_isolate, _context), + lastRun(curTime), + callback(_isolate, _callback), + interval(_interval), once(_once), + location(std::move(_location)) + { + //Log::Debug << "Create timer: " << curTime << " " << interval << Log::Endl; + } + + bool Update(int64_t curTime) + { + if (curTime - lastRun >= interval) + { + V8Helpers::TryCatch([&] { + v8::MaybeLocal result = callback.Get(isolate)->CallAsFunction(context.Get(isolate), v8::Undefined(isolate), 0, nullptr); + return !result.IsEmpty(); + }); + + lastRun = curTime; + + return !once; + } + + return true; + } + + const V8::SourceLocation &GetLocation() const { return location; } + int64_t GetInterval() { return interval; } + bool IsOnce() { return once; } + +private: + v8::Isolate *isolate; + v8::Persistent context; + v8::Persistent callback; + int64_t interval; + int64_t lastRun = 0; + bool once; + V8::SourceLocation location; +}; diff --git a/shared/bindings/BaseObject.cpp b/shared/bindings/BaseObject.cpp new file mode 100644 index 00000000..2e2de57e --- /dev/null +++ b/shared/bindings/BaseObject.cpp @@ -0,0 +1,99 @@ + +#include "../V8Helpers.h" +#include "../V8BindHelpers.h" +#include "../V8ResourceImpl.h" +#include "../V8Class.h" +#include "../V8Entity.h" + +using namespace alt; + +static void TypeGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE(); + + V8_GET_THIS_BASE_OBJECT(obj, alt::IBaseObject); + + V8_RETURN_INT((uint32_t)obj->GetType()); +} + +static void ValidGetter(v8::Local, const v8::PropertyCallbackInfo &info) +{ + V8_GET_ISOLATE(); + + V8Entity* ent = V8Entity::Get(info.This()); + + V8_RETURN_BOOLEAN(ent ? true : false); +} + +static void HasMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(obj, alt::IBaseObject); + + V8_RETURN_BOOLEAN(obj->HasMetaData(key)); +} + +static void GetMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(obj, alt::IBaseObject); + + V8_RETURN_MVALUE(obj->GetMetaData(key)); +} + +static void SetMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, key); + V8_ARG_TO_MVALUE(2, value); + + V8_GET_THIS_BASE_OBJECT(obj, alt::IBaseObject); + + obj->SetMetaData(key, value); +} + +static void DeleteMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(obj, alt::IBaseObject); + + obj->DeleteMetaData(key); +} + +static void Destroy(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_GET_THIS_BASE_OBJECT(obj, alt::IBaseObject); + + alt::ICore::Instance().DestroyBaseObject(obj); +} + +extern V8Class v8BaseObject("BaseObject", [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + V8::SetAccessor(isolate, tpl, "type"); + V8::SetAccessor(isolate, tpl, "valid", &ValidGetter); + + V8::SetMethod(isolate, tpl, "hasMeta", HasMeta); + V8::SetMethod(isolate, tpl, "getMeta", GetMeta); + V8::SetMethod(isolate, tpl, "setMeta", SetMeta); + V8::SetMethod(isolate, tpl, "deleteMeta", DeleteMeta); + V8::SetMethod(isolate, tpl, "destroy", Destroy); +}); diff --git a/shared/bindings/Entity.cpp b/shared/bindings/Entity.cpp new file mode 100644 index 00000000..1c9677b2 --- /dev/null +++ b/shared/bindings/Entity.cpp @@ -0,0 +1,273 @@ + +#include "../V8Helpers.h" +#include "../V8BindHelpers.h" +#include "../V8ResourceImpl.h" +#include "../V8Class.h" +#include "../V8Entity.h" +#include "cpp-sdk/objects/IPlayer.h" + +using namespace alt; + +static void HasSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + + V8_RETURN_BOOLEAN(ent->HasSyncedMetaData(key)); +} + +static void GetSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + + V8_RETURN_MVALUE(ent->GetSyncedMetaData(key)); +} + +static void HasStreamSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + + V8_RETURN_BOOLEAN(ent->HasStreamSyncedMetaData(key)); +} + +static void GetStreamSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + + V8_RETURN_MVALUE(ent->GetStreamSyncedMetaData(key)); +} + +#ifdef ALT_SERVER_API + +static void ModelGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + V8_RETURN_UINT(ent->GetModel()); +} + +static void ModelSetter(v8::Local, v8::Local val, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_GET_THIS_BASE_OBJECT(player, alt::IPlayer); + + if (val->IsNumber()) + { + V8_TO_INTEGER(val, model); + player->SetModel(model); + } + else + { + V8_TO_STRING(val, model); + player->SetModel(alt::ICore::Instance().Hash(model)); + } +} + +static void SetSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, key); + V8_ARG_TO_MVALUE(2, value); + + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + + ent->SetSyncedMetaData(key, value); +} + +static void DeleteSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + + ent->DeleteSyncedMetaData(key); +} + +static void SetStreamSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(2); + V8_ARG_TO_STRING(1, key); + V8_ARG_TO_MVALUE(2, value); + + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + + ent->SetStreamSyncedMetaData(key, value); +} + +static void DeleteStreamSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, key); + + V8_GET_THIS_BASE_OBJECT(ent, alt::IEntity); + + ent->DeleteStreamSyncedMetaData(key); +} + +static void SetNetOwner(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN2(1, 2); + V8_GET_THIS_BASE_OBJECT(_this, IEntity); + V8_ARG_TO_BASE_OBJECT(1, player, IPlayer, "Player"); + V8_ARG_TO_BOOLEAN_OPT(2, disableMigration, false); + + _this->SetNetworkOwner(player, disableMigration); +} + +static void ResetNetOwner(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN2(0, 1); + V8_GET_THIS_BASE_OBJECT(_this, IEntity); + V8_ARG_TO_BOOLEAN_OPT(1, disableMigration, false); + + _this->SetNetworkOwner(nullptr, disableMigration); +} + +static void AttachTo(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(7); + V8_GET_THIS_BASE_OBJECT(_this, IEntity); + + V8_ARG_TO_BASE_OBJECT(1, entity, IEntity, "Entity"); + V8_ARG_TO_INT32(2, otherBone); + V8_ARG_TO_INT32(3, ownBone); + V8_ARG_TO_VECTOR3(4, pos); + V8_ARG_TO_VECTOR3(5, rot); + V8_ARG_TO_BOOLEAN(6, collision); + V8_ARG_TO_BOOLEAN(7, noFixedRot); + + _this->AttachToEntity(entity, otherBone, ownBone, pos, rot, collision, noFixedRot); +} + +static void Detach(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE(); + V8_GET_THIS_BASE_OBJECT(_this, IEntity); + + _this->Detach(); +} + +#endif // ALT_SERVER_API + +#ifdef ALT_CLIENT_API + +static void IsSpawnedGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_THIS_BASE_OBJECT(entity, alt::IEntity); + + V8_RETURN_BOOLEAN(entity->GetScriptGuid() != 0); +} + +static void StaticGetByScriptID(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, scriptGuid); + V8_RETURN_BASE_OBJECT(alt::ICore::Instance().GetEntityByScriptGuid(scriptGuid)); +} + +#endif // ALT_CLIENT_API + +static void StaticGetByID(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_INT(1, id); + V8_RETURN_BASE_OBJECT(alt::ICore::Instance().GetEntityByID(id)); +} + +static void StaticAllGetter(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local arr = v8::Array::New(isolate); + + uint16_t i = 0; + for (auto entity : alt::ICore::Instance().GetEntities()) + { + if (entity) + arr->Set(ctx, i++, resource->GetOrCreateEntity(entity.Get(), "Entity")->GetJSVal(isolate)); + }; + + V8_RETURN(arr); +} + +extern V8Class v8WorldObject; +extern V8Class v8Entity("Entity", v8WorldObject, [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetStaticMethod(isolate, tpl, "getByID", StaticGetByID); + V8::SetStaticAccessor(isolate, tpl, "all", StaticAllGetter); + + V8::SetAccessor(isolate, tpl, "id"); + V8::SetAccessor, &IEntity::GetNetworkOwner>(isolate, tpl, "netOwner"); + + V8::SetMethod(isolate, tpl, "hasSyncedMeta", HasSyncedMeta); + V8::SetMethod(isolate, tpl, "getSyncedMeta", GetSyncedMeta); + + V8::SetMethod(isolate, tpl, "hasStreamSyncedMeta", HasStreamSyncedMeta); + V8::SetMethod(isolate, tpl, "getStreamSyncedMeta", GetStreamSyncedMeta); + +#ifdef ALT_SERVER_API + V8::SetAccessor(isolate, tpl, "rot"); + V8::SetAccessor(isolate, tpl, "model", &ModelGetter, &ModelSetter); + V8::SetAccessor(isolate, tpl, "visible"); + V8::SetAccessor(isolate, tpl, "streamed"); + + V8::SetMethod(isolate, tpl, "setSyncedMeta", SetSyncedMeta); + V8::SetMethod(isolate, tpl, "deleteSyncedMeta", DeleteSyncedMeta); + + V8::SetMethod(isolate, tpl, "setStreamSyncedMeta", SetStreamSyncedMeta); + V8::SetMethod(isolate, tpl, "deleteStreamSyncedMeta", DeleteStreamSyncedMeta); + + V8::SetMethod(isolate, tpl, "setNetOwner", SetNetOwner); + V8::SetMethod(isolate, tpl, "resetNetOwner", ResetNetOwner); + + V8::SetMethod(isolate, tpl, "attachTo", AttachTo); + V8::SetMethod(isolate, tpl, "detach", Detach); +#endif // ALT_SERVER_API + +#ifdef ALT_CLIENT_API + V8::SetStaticMethod(isolate, tpl, "getByScriptID", StaticGetByScriptID); + + V8::SetAccessor(isolate, tpl, "pos"); + V8::SetAccessor(isolate, tpl, "rot"); + V8::SetAccessor(isolate, tpl, "model"); + V8::SetAccessor(isolate, tpl, "scriptID"); + V8::SetAccessor(isolate, tpl, "visible"); + + V8::SetAccessor(isolate, tpl, "isSpawned", &IsSpawnedGetter); +#endif // ALT_CLIENT_API +}); diff --git a/shared/bindings/File.cpp b/shared/bindings/File.cpp new file mode 100644 index 00000000..742082df --- /dev/null +++ b/shared/bindings/File.cpp @@ -0,0 +1,77 @@ + +#include "../V8Helpers.h" +#include "../V8ResourceImpl.h" +#include "../V8Class.h" + +static void StaticExists(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, _path); + +#ifdef ALT_CLIENT + auto path = alt::ICore::Instance().Resolve(resource, _path, ""); + V8_CHECK(path.pkg, "invalid asset pack"); + bool exists = path.pkg->FileExists(path.fileName); +#else + bool exists = alt::ICore::Instance().FileExists(_path); +#endif + + V8_RETURN_BOOLEAN(exists); +} + +static void StaticRead(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_IRESOURCE(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, name); + + alt::String encoding = "utf-8"; + + if (info.Length() >= 2) + { + V8_ARG_TO_STRING(2, _encoding); + encoding = _encoding; + } + +#ifdef ALT_CLIENT + auto path = alt::ICore::Instance().Resolve(resource, name, ""); + V8_CHECK(path.pkg, "invalid asset pack"); + + alt::IPackage::File *file = path.pkg->OpenFile(path.fileName); + V8_CHECK(file, "file does not exist"); + + alt::String data(path.pkg->GetFileSize(file)); + path.pkg->ReadFile(file, data.GetData(), data.GetSize()); + path.pkg->CloseFile(file); +#else + alt::String data = alt::ICore::Instance().FileRead(name); +#endif // ALT_CLIENT + + if (encoding == "utf-8") + { + V8_RETURN(v8::String::NewFromUtf8(isolate, data.GetData(), v8::NewStringType::kNormal, data.GetSize()).ToLocalChecked()); + } + else if (encoding == "utf-16") + { + V8_RETURN(v8::String::NewFromTwoByte(isolate, (uint16_t *)data.GetData(), v8::NewStringType::kNormal, data.GetSize() / 2).ToLocalChecked()); + } + else if (encoding == "binary") + { + v8::Local buffer = v8::ArrayBuffer::New(isolate, data.GetSize()); + auto contents = buffer->GetBackingStore(); + + std::memcpy(contents->Data(), data.GetData(), data.GetSize()); + + V8_RETURN(buffer); + } +} + +extern V8Class v8File("File", [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetStaticMethod(isolate, tpl, "exists", StaticExists); + V8::SetStaticMethod(isolate, tpl, "read", StaticRead); +}); diff --git a/shared/bindings/Main.cpp b/shared/bindings/Main.cpp new file mode 100644 index 00000000..45e6d8a0 --- /dev/null +++ b/shared/bindings/Main.cpp @@ -0,0 +1,409 @@ + +#include "../V8Helpers.h" +#include "../V8ResourceImpl.h" +#include "../V8Module.h" + +static void HashCb(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(1); + V8_ARG_TO_STRING(1, str); + + V8_RETURN_UINT(alt::ICore::Instance().Hash(str)); +} + +static void On(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 2); + if(info.Length() == 1) + { + V8_ARG_TO_FUNCTION(1, callback); + + resource->SubscribeGenericLocal(callback, V8::SourceLocation::GetCurrent(isolate)); + } + else if(info.Length() == 2) + { + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, callback); + + resource->SubscribeLocal(evName.ToString(), callback, V8::SourceLocation::GetCurrent(isolate)); + } +} + +static void Once(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 2); + if(info.Length() == 1) + { + V8_ARG_TO_FUNCTION(1, callback); + + resource->SubscribeGenericLocal(callback, V8::SourceLocation::GetCurrent(isolate), true); + } + else if(info.Length() == 2) + { + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, callback); + + resource->SubscribeLocal(evName.ToString(), callback, V8::SourceLocation::GetCurrent(isolate), true); + } +} + +static void Off(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 2); + if(info.Length() == 1) + { + V8_ARG_TO_FUNCTION(1, callback); + + resource->UnsubscribeGenericLocal(callback); + } + else if(info.Length() == 2) + { + V8_ARG_TO_STRING(1, evName); + V8_ARG_TO_FUNCTION(2, callback); + + resource->UnsubscribeLocal(evName.ToString(), callback); + } +} + +static void Emit(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, name); + + alt::MValueArgs args; + + for (int i = 1; i < info.Length(); ++i) + args.Push(V8Helpers::V8ToMValue(info[i])); + + alt::ICore::Instance().TriggerLocalEvent(name, args); +} + +static void HasMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, key); + + V8_RETURN(alt::ICore::Instance().HasMetaData(key)); +} + +static void GetMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, key); + + V8_RETURN_MVALUE(alt::ICore::Instance().GetMetaData(key)); +} + +static void SetMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN_MIN(2); + V8_ARG_TO_STRING(1, key); + V8_ARG_TO_MVALUE(2, value); + + alt::ICore::Instance().SetMetaData(key, value); +} + +static void DeleteMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, key); + + alt::ICore::Instance().DeleteMetaData(key); +} + +static void HasSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, key); + + V8_RETURN(alt::ICore::Instance().HasSyncedMetaData(key)); +} + +static void GetSyncedMeta(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN_MIN(1); + V8_ARG_TO_STRING(1, key); + + V8_RETURN_MVALUE(alt::ICore::Instance().GetSyncedMetaData(key)); +} + +static void Log(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN_MIN(1); + + std::stringstream ss; + for (int i = 0; i < info.Length(); ++i) + { + v8::Local val = info[i]; + + if (i > 0) + ss << " "; + + auto str = V8::Stringify(val, ctx); + if(str.IsEmpty()) continue; + + ss << str.CStr(); + } + + alt::ICore::Instance().LogColored(ss.str()); +} + +static void LogWarning(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN_MIN(1); + + std::stringstream ss; + for (int i = 0; i < info.Length(); ++i) + { + v8::Local val = info[i]; + + if (i > 0) + ss << " "; + + auto str = V8::Stringify(val, ctx); + if(str.IsEmpty()) continue; + + ss << str.CStr(); + } + + alt::ICore::Instance().LogWarning(ss.str()); +} + +static void LogError(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN_MIN(1); + + std::stringstream ss; + for (int i = 0; i < info.Length(); ++i) + { + v8::Local val = info[i]; + + if (i > 0) + ss << " "; + + auto str = V8::Stringify(val, ctx); + if(str.IsEmpty()) continue; + + ss << str.CStr(); + } + + alt::ICore::Instance().LogError(ss.str()); +} + +static void SetTimeout(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_FUNCTION(1, callback); + V8_ARG_TO_INT(2, time); + + V8_RETURN_INT(resource->CreateTimer(ctx, callback, time, true, V8::SourceLocation::GetCurrent(isolate))); +} + +static void SetInterval(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + + V8_ARG_TO_FUNCTION(1, callback); + V8_ARG_TO_INT(2, time); + + V8_RETURN_INT(resource->CreateTimer(ctx, callback, time, false, V8::SourceLocation::GetCurrent(isolate))); +} + +static void NextTick(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_FUNCTION(1, callback); + + V8_RETURN_INT(resource->CreateTimer(ctx, callback, 0, true, V8::SourceLocation::GetCurrent(isolate))); +} + +static void EveryTick(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_FUNCTION(1, callback); + + V8_RETURN_INT(resource->CreateTimer(ctx, callback, 0, false, V8::SourceLocation::GetCurrent(isolate))); +} + +static void ClearTimer(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_INT(1, timer); + + resource->RemoveTimer(timer); +} + +static void HasResource(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + V8_CHECK_ARGS_LEN(1); + + V8_ARG_TO_STRING(1, name); + + alt::IResource* resource = alt::ICore::Instance().GetResource(name); + + V8_RETURN_BOOLEAN(resource && resource->IsStarted()); +} + +static void GetEventListeners(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + + std::vector handlers; + + if(info[0]->IsNull()) + { + handlers = std::move(resource->GetGenericHandlers(true)); + } + else + { + V8_ARG_TO_STRING(1, eventName); + handlers = std::move(resource->GetLocalHandlers(eventName.ToString())); + } + + auto array = v8::Array::New(isolate, handlers.size()); + for(int i = 0; i < handlers.size(); i++) + { + array->Set(ctx, i, handlers[i]->fn.Get(isolate)); + } + + V8_RETURN(array); +} + +static void GetRemoteEventListeners(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + + std::vector handlers; + + if(info[0]->IsNull()) + { + handlers = std::move(resource->GetGenericHandlers(false)); + } + else + { + V8_ARG_TO_STRING(1, eventName); + handlers = std::move(resource->GetRemoteHandlers(eventName.ToString())); + } + + auto array = v8::Array::New(isolate, handlers.size()); + for(int i = 0; i < handlers.size(); i++) + { + array->Set(ctx, i, handlers[i]->fn.Get(isolate)); + } + + V8_RETURN(array); +} + +extern V8Class v8BaseObject, + v8WorldObject, + v8Entity, + v8File, + v8RGBA, + v8Vector2, + v8Vector3; + +extern V8Module sharedModule("alt-shared", nullptr, +{ + v8BaseObject, + v8WorldObject, + v8Entity, + v8File, + v8RGBA, + v8Vector2, + v8Vector3 +}, +[](v8::Local ctx, v8::Local exports) { + v8::Isolate *isolate = ctx->GetIsolate(); + + V8Helpers::RegisterFunc(exports, "hash", &HashCb); + + V8Helpers::RegisterFunc(exports, "log", &Log); + V8Helpers::RegisterFunc(exports, "logWarning", &LogWarning); + V8Helpers::RegisterFunc(exports, "logError", &LogError); + + V8Helpers::RegisterFunc(exports, "on", &On); + V8Helpers::RegisterFunc(exports, "once", &Once); + V8Helpers::RegisterFunc(exports, "off", &Off); + V8Helpers::RegisterFunc(exports, "emit", &Emit); + + V8Helpers::RegisterFunc(exports, "getEventListeners", &GetEventListeners); + V8Helpers::RegisterFunc(exports, "getRemoteEventListeners", &GetRemoteEventListeners); + + V8Helpers::RegisterFunc(exports, "hasMeta", &HasMeta); + V8Helpers::RegisterFunc(exports, "getMeta", &GetMeta); + V8Helpers::RegisterFunc(exports, "setMeta", &SetMeta); + V8Helpers::RegisterFunc(exports, "deleteMeta", &DeleteMeta); + + V8Helpers::RegisterFunc(exports, "hasSyncedMeta", &HasSyncedMeta); + V8Helpers::RegisterFunc(exports, "getSyncedMeta", &GetSyncedMeta); + + V8Helpers::RegisterFunc(exports, "nextTick", &NextTick); + V8Helpers::RegisterFunc(exports, "everyTick", &EveryTick); + V8Helpers::RegisterFunc(exports, "setTimeout", &SetTimeout); + V8Helpers::RegisterFunc(exports, "setInterval", &SetInterval); + V8Helpers::RegisterFunc(exports, "clearTimer", &ClearTimer); + V8Helpers::RegisterFunc(exports, "clearNextTick", &ClearTimer); + V8Helpers::RegisterFunc(exports, "clearEveryTick", &ClearTimer); + V8Helpers::RegisterFunc(exports, "clearTimeout", &ClearTimer); + V8Helpers::RegisterFunc(exports, "clearInterval", &ClearTimer); + + V8Helpers::RegisterFunc(exports, "hasResource", &HasResource); + + V8_OBJECT_SET_STRING(exports, "version", alt::ICore::Instance().GetVersion()); + V8_OBJECT_SET_STRING(exports, "branch", alt::ICore::Instance().GetBranch()); + V8_OBJECT_SET_INT(exports, "sdkVersion", alt::ICore::Instance().SDK_VERSION); + V8_OBJECT_SET_BOOLEAN(exports, "debug", alt::ICore::Instance().IsDebug()); + + V8_OBJECT_SET_STRING(exports, "resourceName", V8ResourceImpl::GetResource(ctx)->GetName()); + +#ifdef ALT_CLIENT_API + V8_OBJECT_SET_BOOLEAN(exports, "isClient", true); + V8_OBJECT_SET_BOOLEAN(exports, "isServer", false); +#endif +#ifdef ALT_SERVER_API + V8_OBJECT_SET_BOOLEAN(exports, "isClient", false); + V8_OBJECT_SET_BOOLEAN(exports, "isServer", true); +#endif +}); diff --git a/shared/bindings/RGBA.cpp b/shared/bindings/RGBA.cpp new file mode 100644 index 00000000..08bb9808 --- /dev/null +++ b/shared/bindings/RGBA.cpp @@ -0,0 +1,46 @@ + +#include "../V8Class.h" +#include "../V8Helpers.h" +#include "../V8ResourceImpl.h" + +static void ToString(const v8::FunctionCallbackInfo &info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_TO_NUMBER(info.This()->Get(ctx, V8::RGBA_RKey(isolate)).ToLocalChecked(), r); + V8_TO_NUMBER(info.This()->Get(ctx, V8::RGBA_GKey(isolate)).ToLocalChecked(), g); + V8_TO_NUMBER(info.This()->Get(ctx, V8::RGBA_BKey(isolate)).ToLocalChecked(), b); + V8_TO_NUMBER(info.This()->Get(ctx, V8::RGBA_AKey(isolate)).ToLocalChecked(), a); + + std::ostringstream ss; + ss << "RGBA{ r: " << r << ", g: " << g << ", b: " << b << ", a: " << a << " }"; + + V8_RETURN_STRING(ss.str().c_str()); +} + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_ARGS_LEN(4); + + V8_ARG_TO_INT(1, r); + V8_CHECK(r >= 0 && r < 256, "Invalid RGBA R value. Allowed is 0 - 255"); + V8_ARG_TO_INT(2, g); + V8_CHECK(g >= 0 && g < 256, "Invalid RGBA G value. Allowed is 0 - 255"); + V8_ARG_TO_INT(3, b); + V8_CHECK(b >= 0 && b < 256, "Invalid RGBA B value. Allowed is 0 - 255"); + V8_ARG_TO_INT(4, a); + V8_CHECK(a >= 0 && a < 256, "Invalid RGBA A value. Allowed is 0 - 255"); + + V8::DefineOwnProperty(isolate, ctx, info.This(), V8::RGBA_RKey(isolate), v8::Integer::New(isolate, r), v8::PropertyAttribute::ReadOnly); + V8::DefineOwnProperty(isolate, ctx, info.This(), V8::RGBA_GKey(isolate), v8::Integer::New(isolate, g), v8::PropertyAttribute::ReadOnly); + V8::DefineOwnProperty(isolate, ctx, info.This(), V8::RGBA_BKey(isolate), v8::Integer::New(isolate, b), v8::PropertyAttribute::ReadOnly); + V8::DefineOwnProperty(isolate, ctx, info.This(), V8::RGBA_AKey(isolate), v8::Integer::New(isolate, a), v8::PropertyAttribute::ReadOnly); +} + +extern V8Class v8RGBA("RGBA", &Constructor, [](v8::Local tpl) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + V8::SetMethod(isolate, tpl, "toString", ToString); +}); diff --git a/shared/bindings/Vector2.cpp b/shared/bindings/Vector2.cpp new file mode 100644 index 00000000..9e95f4f8 --- /dev/null +++ b/shared/bindings/Vector2.cpp @@ -0,0 +1,646 @@ +#include + +#include "../V8Class.h" +#include "../V8Helpers.h" +#include "../V8ResourceImpl.h" + +constexpr double PI = 3.141592653589793238463; + +static void ToString(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + std::ostringstream ss; + ss << std::fixed << std::setprecision(4) + << "Vector2{ x: " << x << ", y: " << y << " }"; + + V8_RETURN_STRING(ss.str().c_str()); +} + +static void ToArray(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + v8::Local arr = v8::Array::New(isolate, 2); + arr->Set(ctx, 0, v8::Number::New(isolate, x)); + arr->Set(ctx, 1, v8::Number::New(isolate, y)); + + V8_RETURN(arr); +} + +static void Length(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + double length = sqrt(x * x + y * y); + + V8_RETURN_NUMBER(length); +} + + +static void Add(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 2); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + if (info.Length() == 2) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + + V8_RETURN(resource->CreateVector2({ x + x2, y + y2 })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + V8_RETURN(resource->CreateVector2({ x + value, y + value })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 2, "Argument must be an array of 2 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_RETURN(resource->CreateVector2({ x + x2, y + y2 })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + + V8_RETURN(resource->CreateVector2({ x + x2, y + y2 })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 2 numbers or IVector2"); + } + } +} + +static void Sub(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 2); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + if (info.Length() == 2) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + + V8_RETURN(resource->CreateVector2({ x - x2, y - y2 })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + V8_RETURN(resource->CreateVector2({ x - value, y - value })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 2, "Argument must be an array of 2 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_RETURN(resource->CreateVector2({ x - x2, y - y2 })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + + V8_RETURN(resource->CreateVector2({ x - x2, y - y2 })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 2 numbers or IVector2"); + } + } +} + +static void Divide(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 2); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + if (info.Length() == 2) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + + V8_CHECK(x2 != 0 && y2 != 0, "Division by zero"); + V8_RETURN(resource->CreateVector2({ x / x2, y / y2 })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + V8_CHECK(value != 0, "Division by zero"); + V8_RETURN(resource->CreateVector2({ x / value, y / value })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 2, "Argument must be an array of 2 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_CHECK(x2 != 0 && y2 != 0, "Division by zero"); + V8_RETURN(resource->CreateVector2({ x / x2, y / y2 })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_CHECK(x2 != 0 && y2 != 0, "Division by zero"); + V8_RETURN(resource->CreateVector2({ x / x2, y / y2 })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 2 numbers or IVector2"); + } + } +} + +static void Multiply(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 2); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + if (info.Length() == 2) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + + V8_RETURN(resource->CreateVector2({ x * x2, y * y2 })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + V8_RETURN(resource->CreateVector2({ x * value, y * value })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 2, "Argument must be an array of 2 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_RETURN(resource->CreateVector2({ x * x2, y * y2 })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + + V8_RETURN(resource->CreateVector2({ x * x2, y * y2 })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 2 numbers or IVector2"); + } + } +} + + +static void Dot(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 2); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + if (info.Length() == 2) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + + V8_RETURN_NUMBER(x * x2 + y * y2); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + + V8_RETURN_NUMBER(x * value + y * value); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 2, "Argument must be an array of 2 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + + V8_RETURN_NUMBER(x * x2 + y * y2); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + + V8_RETURN_NUMBER(x * x2 + y * y2); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 2 numbers or IVector2"); + } + } +} + +static void Negative(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + V8_RETURN(resource->CreateVector2({ -x, -y })); +} + +static void Normalize(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + double length = sqrt(x * x + y * y); + + V8_RETURN(resource->CreateVector2({ x / length, y / length })); +} + + +static void DistanceTo(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + V8_ARG_TO_OBJECT(1, vec); + + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + + double xFinal = x - x2; + double yFinal = y - y2; + double dist = sqrt((xFinal * xFinal) + (yFinal * yFinal)); + + V8_RETURN_NUMBER(dist); +} + +static void AngleTo(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + V8_ARG_TO_OBJECT(1, vec); + + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + + double xy = x * x2 + y * y2; + double posALength = sqrt(std::pow(x, 2) + std::pow(y, 2)); + double posBLength = sqrt(std::pow(x2, 2) + std::pow(y2, 2)); + + if (posALength == 0 || posBLength == 0) + { + V8Helpers::Throw(isolate, "Division by zero!"); + return; + } + + double cos = xy / (posALength * posBLength); + double radians = std::acos(cos); + // double angle = radians * (180 / PI); + + V8_RETURN_NUMBER(radians); +} + +static void AngleToDegrees(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + V8_ARG_TO_OBJECT(1, vec); + + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + + double xy = x * x2 + y * y2; + double posALength = sqrt(std::pow(x, 2) + std::pow(y, 2)); + double posBLength = sqrt(std::pow(x2, 2) + std::pow(y2, 2)); + + if (posALength == 0 || posBLength == 0) + { + V8Helpers::Throw(isolate, "Division by zero!"); + return; + } + + double cos = xy / (posALength * posBLength); + double radians = std::acos(cos); + double angle = radians * (180 / PI); + + V8_RETURN_NUMBER(angle); +} + +static void ToDegrees(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + double x2 = (x * 180) / PI; + double y2 = (y * 180) / PI; + + V8_RETURN(resource->CreateVector2({ x2, y2 })); +} + +static void ToRadians(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + double x2 = (x * PI) / 180; + double y2 = (y * PI) / 180; + + V8_RETURN(resource->CreateVector2({ x2, y2 })); +} + +static void IsInRange(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + + V8_ARG_TO_OBJECT(1, vec); + V8_ARG_TO_NUMBER(2, range); + + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + + double dx = abs(x - x2); + double dy = abs(y - y2); + + bool isInRange = + dx <= range && // perform fast check first + dy <= range && + dx * dx + dy * dy <= range * range; // perform exact check + + V8_RETURN_BOOLEAN(isInRange); +} + +static void StaticZero(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto zero = v8::Eternal(isolate, resource->CreateVector2({0, 0}).As()); + + V8_RETURN(zero.Get(isolate)); +} + +static void StaticOne(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto one = v8::Eternal(isolate, resource->CreateVector2({1, 1}).As()); + + V8_RETURN(one.Get(isolate)); +} + +static void StaticUp(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto up = v8::Eternal(isolate, resource->CreateVector2({0, 1}).As()); + + V8_RETURN(up.Get(isolate)); +} + +static void StaticDown(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto down = v8::Eternal(isolate, resource->CreateVector2({0, -1}).As()); + + V8_RETURN(down.Get(isolate)); +} + +static void StaticLeft(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto left = v8::Eternal(isolate, resource->CreateVector2({-1, 0}).As()); + + V8_RETURN(left.Get(isolate)); +} + +static void StaticRight(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto right = v8::Eternal(isolate, resource->CreateVector2({1, 0}).As()); + + V8_RETURN(right.Get(isolate)); +} + +static void StaticNegativeInfinity(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + + float infinity = -std::numeric_limits::infinity(); + static auto negativeInfinity = v8::Eternal(isolate, resource->CreateVector2({infinity, infinity}).As()); + + V8_RETURN(negativeInfinity.Get(isolate)); +} + +static void StaticPositiveInfinity(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + + float infinity = std::numeric_limits::infinity(); + static auto positiveInfinity = v8::Eternal(isolate, resource->CreateVector2({infinity, infinity}).As()); + + V8_RETURN(positiveInfinity.Get(isolate)); +} + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN2(1, 2); + + v8::Local _this = info.This(); + + v8::Local x, y; + + if (info.Length() == 2) + { + V8_ARG_CHECK_NUMBER(1); + V8_ARG_CHECK_NUMBER(2); + + x = info[0]; + y = info[1]; + } + else + { + v8::Local val = info[0]; + + if (val->IsArray()) + { + v8::Local arr = val.As(); + V8_CHECK(arr->Length() == 2, "Argument must be an array of 2 numbers"); + + x = arr->Get(ctx, 0).ToLocalChecked(); + y = arr->Get(ctx, 1).ToLocalChecked(); + + V8_CHECK(x->IsNumber(), "Argument must be an array of 2 numbers"); + V8_CHECK(y->IsNumber(), "Argument must be an array of 2 numbers"); + } + else if (val->IsObject()) + { + v8::Local obj = val.As(); + + x = obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(); + y = obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(); + + V8_CHECK(x->IsNumber(), "x must be a number"); + V8_CHECK(y->IsNumber(), "y must be a number"); + } + else + { + V8Helpers::Throw(isolate, "Argument must be an array of 2 numbers or IVector2"); + return; + } + } + + V8::DefineOwnProperty(isolate, ctx, _this, V8::Vector3_XKey(isolate), x, v8::PropertyAttribute::ReadOnly); + V8::DefineOwnProperty(isolate, ctx, _this, V8::Vector3_YKey(isolate), y, v8::PropertyAttribute::ReadOnly); +} + +extern V8Class v8Vector2("Vector2", Constructor, [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetStaticAccessor(isolate, tpl, "zero", StaticZero); + V8::SetStaticAccessor(isolate, tpl, "one", StaticOne); + V8::SetStaticAccessor(isolate, tpl, "up", StaticUp); + V8::SetStaticAccessor(isolate, tpl, "down", StaticDown); + V8::SetStaticAccessor(isolate, tpl, "left", StaticLeft); + V8::SetStaticAccessor(isolate, tpl, "right", StaticRight); + V8::SetStaticAccessor(isolate, tpl, "negativeInfinity", StaticNegativeInfinity); + V8::SetStaticAccessor(isolate, tpl, "positiveInfinity", StaticPositiveInfinity); + + V8::SetAccessor(isolate, tpl, "length", Length); + V8::SetMethod(isolate, tpl, "toString", ToString); + V8::SetMethod(isolate, tpl, "toArray", ToArray); + V8::SetMethod(isolate, tpl, "add", Add); + V8::SetMethod(isolate, tpl, "sub", Sub); + V8::SetMethod(isolate, tpl, "div", Divide); + V8::SetMethod(isolate, tpl, "mul", Multiply); + V8::SetMethod(isolate, tpl, "negative", Negative); + V8::SetMethod(isolate, tpl, "normalize", Normalize); + V8::SetMethod(isolate, tpl, "distanceTo", DistanceTo); + V8::SetMethod(isolate, tpl, "angleTo", AngleTo); + V8::SetMethod(isolate, tpl, "angleToDegrees", AngleToDegrees); + V8::SetMethod(isolate, tpl, "toRadians", ToRadians); + V8::SetMethod(isolate, tpl, "toDegrees", ToDegrees); + V8::SetMethod(isolate, tpl, "isInRange", IsInRange); +}); diff --git a/shared/bindings/Vector3.cpp b/shared/bindings/Vector3.cpp new file mode 100644 index 00000000..00f3053d --- /dev/null +++ b/shared/bindings/Vector3.cpp @@ -0,0 +1,774 @@ + +#include + +#include "../V8Class.h" +#include "../V8Helpers.h" +#include "../V8ResourceImpl.h" + +constexpr double PI = 3.141592653589793238463; + +static void ToString(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + std::ostringstream ss; + ss << std::fixed << std::setprecision(4) + << "Vector3{ x: " << x << ", y: " << y << ", z: " << z << " }"; + + V8_RETURN_STRING(ss.str().c_str()); +} + +static void ToArray(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + v8::Local arr = v8::Array::New(isolate, 3); + arr->Set(ctx, 0, v8::Number::New(isolate, x)); + arr->Set(ctx, 1, v8::Number::New(isolate, y)); + arr->Set(ctx, 2, v8::Number::New(isolate, z)); + + V8_RETURN(arr); +} + +static void Length(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + double length = sqrt(x * x + y * y + z * z); + + V8_RETURN_NUMBER(length); +} + + +static void Add(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 3); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + if (info.Length() == 3) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + V8_ARG_TO_NUMBER(3, z2); + + V8_RETURN(resource->CreateVector3({ x + x2, y + y2, z + z2 })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + V8_RETURN(resource->CreateVector3({ x + value, y + value, z + value })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 3, "Argument must be an array of 3 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_TO_NUMBER(arr->Get(ctx, 2).ToLocalChecked(), z2); + V8_RETURN(resource->CreateVector3({ x + x2, y + y2, z + z2 })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + V8_RETURN(resource->CreateVector3({ x + x2, y + y2, z + z2 })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 3 numbers or IVector3"); + } + } +} + +static void Sub(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 3); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + if (info.Length() == 3) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + V8_ARG_TO_NUMBER(3, z2); + + V8_RETURN(resource->CreateVector3({ x - x2, y - y2, z - z2 })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + V8_RETURN(resource->CreateVector3({ x - value, y - value, z - value })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 3, "Argument must be an array of 3 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_TO_NUMBER(arr->Get(ctx, 2).ToLocalChecked(), z2); + V8_RETURN(resource->CreateVector3({ x - x2, y - y2, z - z2 })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + V8_RETURN(resource->CreateVector3({ x - x2, y - y2, z - z2 })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 3 numbers or IVector3"); + } + } +} + +static void Divide(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 3); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + if (info.Length() == 3) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + V8_ARG_TO_NUMBER(3, z2); + + V8_CHECK(x2 != 0 && y2 != 0 && z2 != 0, "Division by zero"); + V8_RETURN(resource->CreateVector3({ x / x2, y / y2, z / z2 })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + V8_CHECK(value != 0, "Division by zero"); + V8_RETURN(resource->CreateVector3({ x / value, y / value, z / value })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 3, "Argument must be an array of 3 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_TO_NUMBER(arr->Get(ctx, 2).ToLocalChecked(), z2); + V8_CHECK(x2 != 0 && y2 != 0 && z2 != 0, "Division by zero"); + V8_RETURN(resource->CreateVector3({ x / x2, y / y2, z / z2 })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + V8_CHECK(x2 != 0 && y2 != 0 && z2 != 0, "Division by zero"); + V8_RETURN(resource->CreateVector3({ x / x2, y / y2, z / z2 })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 3 numbers or IVector3"); + } + } +} + +static void Multiply(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 3); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + if (info.Length() == 3) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + V8_ARG_TO_NUMBER(3, z2); + + V8_RETURN(resource->CreateVector3({ x * x2, y * y2, z * z2 })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + V8_RETURN(resource->CreateVector3({ x * value, y * value, z * value })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 3, "Argument must be an array of 3 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_TO_NUMBER(arr->Get(ctx, 2).ToLocalChecked(), z2); + V8_RETURN(resource->CreateVector3({ x * x2, y * y2, z * z2 })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + V8_RETURN(resource->CreateVector3({ x * x2, y * y2, z * z2 })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 3 numbers or IVector3"); + } + } +} + +static void Dot(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 3); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + if (info.Length() == 3) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + V8_ARG_TO_NUMBER(3, z2); + + V8_RETURN_NUMBER(x * x2 + y * y2 + z * z2); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + + V8_RETURN_NUMBER(x * value + y * value + z * value); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 3, "Argument must be an array of 3 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_TO_NUMBER(arr->Get(ctx, 2).ToLocalChecked(), z2); + + V8_RETURN_NUMBER(x * x2 + y * y2 + z * z2); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + V8_RETURN_NUMBER(x * x2 + y * y2 + z * z2); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 3 numbers or IVector3"); + } + } +} + +static void Cross(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN2(1, 3); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + if (info.Length() == 3) + { + V8_ARG_TO_NUMBER(1, x2); + V8_ARG_TO_NUMBER(2, y2); + V8_ARG_TO_NUMBER(3, z2); + + V8_RETURN(resource->CreateVector3({ (y * z2) - (z * y2), (z * x2) - (x * z2), (x * y2) - (y * x2) })); + } + else if (info.Length() == 1) + { + auto arg = info[0]; + if (arg->IsNumber()) + { + V8_ARG_TO_NUMBER(1, value); + + V8_RETURN(resource->CreateVector3({ (y * value) - (z * value), (z * value) - (x * value), (x * value) - (y * value) })); + } + else if (arg->IsArray()) + { + v8::Local arr = arg.As(); + V8_CHECK(arr->Length() == 3, "Argument must be an array of 3 numbers"); + + V8_TO_NUMBER(arr->Get(ctx, 0).ToLocalChecked(), x2); + V8_TO_NUMBER(arr->Get(ctx, 1).ToLocalChecked(), y2); + V8_TO_NUMBER(arr->Get(ctx, 2).ToLocalChecked(), z2); + + V8_RETURN(resource->CreateVector3({ (y * z2) - (z * y2), (z * x2) - (x * z2), (x * y2) - (y * x2) })); + } + else if (arg->IsObject()) + { + v8::Local obj = arg.As(); + + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(obj->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + V8_RETURN(resource->CreateVector3({ (y * z2) - (z * y2), (z * x2) - (x * z2), (x * y2) - (y * x2) })); + } + else + { + V8Helpers::Throw(isolate, "Argument must be a number, an array of 3 numbers or IVector3"); + } + } +} + +static void Negative(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + V8_RETURN(resource->CreateVector3({ -x, -y, -z })); +} + +static void Normalize(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + double length = sqrt(x * x + y * y + z * z); + + V8_RETURN(resource->CreateVector3({ x / length, y / length, z / length })); +} + + +static void DistanceTo(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + V8_ARG_TO_OBJECT(1, vec); + + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + double xFinal = x - x2; + double yFinal = y - y2; + double zFinal = z - z2; + double dist = sqrt((xFinal * xFinal) + (yFinal * yFinal) + (zFinal * zFinal)); + + V8_RETURN_NUMBER(dist); +} + +static void AngleTo(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + V8_ARG_TO_OBJECT(1, vec); + + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + double xy = x * x2 + y * y2; + double posALength = sqrt(std::pow(x, 2) + std::pow(y, 2)); + double posBLength = sqrt(std::pow(x2, 2) + std::pow(y2, 2)); + + if (posALength == 0 || posBLength == 0) + { + V8Helpers::Throw(isolate, "Division by zero!"); + return; + } + + double cos = xy / (posALength * posBLength); + double radians = std::acos(cos); + // double angle = radians * (180 / PI); + + V8_RETURN_NUMBER(radians); +} + +static void AngleToDegrees(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + V8_CHECK_ARGS_LEN(1); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + V8_ARG_TO_OBJECT(1, vec); + + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + double xy = x * x2 + y * y2; + double posALength = sqrt(std::pow(x, 2) + std::pow(y, 2)); + double posBLength = sqrt(std::pow(x2, 2) + std::pow(y2, 2)); + + if (posALength == 0 || posBLength == 0) + { + V8Helpers::Throw(isolate, "Division by zero!"); + return; + } + + double cos = xy / (posALength * posBLength); + double radians = std::acos(cos); + double angle = radians * (180 / PI); + + V8_RETURN_NUMBER(angle); +} + +static void ToDegrees(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + double x2 = (x * 180) / PI; + double y2 = (y * 180) / PI; + double z2 = (z * 180) / PI; + + V8_RETURN(resource->CreateVector3({ x2, y2, z2 })); +} + +static void ToRadians(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + double x2 = (x * PI) / 180; + double y2 = (y * PI) / 180; + double z2 = (z * PI) / 180; + + V8_RETURN(resource->CreateVector3({ x2, y2, z2 })); +} + +static void IsInRange(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT_RESOURCE(); + + V8_CHECK_ARGS_LEN(2); + + v8::Local _this = info.This(); + + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_XKey(isolate)), x); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_YKey(isolate)), y); + V8_TO_NUMBER(V8::Get(ctx, _this, V8::Vector3_ZKey(isolate)), z); + + V8_ARG_TO_OBJECT(1, vec); + V8_ARG_TO_NUMBER(2, range); + + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(), x2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(), y2); + V8_TO_NUMBER(vec->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(), z2); + + double dx = abs(x - x2); + double dy = abs(y - y2); + double dz = abs(z - z2); + + bool isInRange = + dx <= range && // perform fast check first + dy <= range && + dz <= range && + dx * dx + dy * dy + dz * dz <= range * range; // perform exact check + + V8_RETURN_BOOLEAN(isInRange); +} + +static void StaticZero(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto zero = v8::Eternal(isolate, resource->CreateVector3({0, 0, 0}).As()); + + V8_RETURN(zero.Get(isolate)); +} + +static void StaticOne(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto one = v8::Eternal(isolate, resource->CreateVector3({1, 1, 1}).As()); + + V8_RETURN(one.Get(isolate)); +} + +static void StaticBack(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto back = v8::Eternal(isolate, resource->CreateVector3({0, 0, -1}).As()); + + V8_RETURN(back.Get(isolate)); +} + +static void StaticDown(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto down = v8::Eternal(isolate, resource->CreateVector3({0, -1, 0}).As()); + + V8_RETURN(down.Get(isolate)); +} + +static void StaticForward(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto forward = v8::Eternal(isolate, resource->CreateVector3({0, 0, 1}).As()); + + V8_RETURN(forward.Get(isolate)); +} + +static void StaticLeft(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto left = v8::Eternal(isolate, resource->CreateVector3({-1, 0, 0}).As()); + + V8_RETURN(left.Get(isolate)); +} + +static void StaticRight(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto right = v8::Eternal(isolate, resource->CreateVector3({1, 0, 0}).As()); + + V8_RETURN(right.Get(isolate)); +} + +static void StaticUp(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + static auto up = v8::Eternal(isolate, resource->CreateVector3({0, 1, 0}).As()); + + V8_RETURN(up.Get(isolate)); +} + +static void StaticNegativeInfinity(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + + float infinity = -std::numeric_limits::infinity(); + static auto negativeInfinity = v8::Eternal(isolate, resource->CreateVector3({infinity, infinity, infinity}).As()); + + V8_RETURN(negativeInfinity.Get(isolate)); +} + +static void StaticPositiveInfinity(v8::Local, const v8::PropertyCallbackInfo& info) +{ + V8_GET_ISOLATE(); + V8_GET_RESOURCE(); + + float infinity = std::numeric_limits::infinity(); + static auto positiveInfinity = v8::Eternal(isolate, resource->CreateVector3({infinity, infinity, infinity}).As()); + + V8_RETURN(positiveInfinity.Get(isolate)); +} + + +static void Constructor(const v8::FunctionCallbackInfo& info) +{ + V8_GET_ISOLATE_CONTEXT(); + + V8_CHECK_CONSTRUCTOR(); + V8_CHECK_ARGS_LEN2(1, 3); + + v8::Local _this = info.This(); + + v8::Local x, y, z; + + if (info.Length() == 3) + { + V8_ARG_CHECK_NUMBER(1); + V8_ARG_CHECK_NUMBER(2); + V8_ARG_CHECK_NUMBER(3); + + x = info[0]; + y = info[1]; + z = info[2]; + } + else + { + v8::Local val = info[0]; + + if (val->IsArray()) + { + v8::Local arr = val.As(); + V8_CHECK(arr->Length() == 3, "Argument must be an array of 3 numbers"); + + x = arr->Get(ctx, 0).ToLocalChecked(); + y = arr->Get(ctx, 1).ToLocalChecked(); + z = arr->Get(ctx, 2).ToLocalChecked(); + + V8_CHECK(x->IsNumber(), "Argument must be an array of 3 numbers"); + V8_CHECK(y->IsNumber(), "Argument must be an array of 3 numbers"); + V8_CHECK(z->IsNumber(), "Argument must be an array of 3 numbers"); + } + else if (val->IsObject()) + { + v8::Local obj = val.As(); + + x = obj->Get(ctx, V8::Vector3_XKey(isolate)).ToLocalChecked(); + y = obj->Get(ctx, V8::Vector3_YKey(isolate)).ToLocalChecked(); + z = obj->Get(ctx, V8::Vector3_ZKey(isolate)).ToLocalChecked(); + + V8_CHECK(x->IsNumber(), "x must be a number"); + V8_CHECK(y->IsNumber(), "y must be a number"); + V8_CHECK(z->IsNumber(), "z must be a number"); + } + else + { + V8Helpers::Throw(isolate, "Argument must be an array of 3 numbers or IVector3"); + return; + } + } + + V8::DefineOwnProperty(isolate, ctx, _this, V8::Vector3_XKey(isolate), x, v8::PropertyAttribute::ReadOnly); + V8::DefineOwnProperty(isolate, ctx, _this, V8::Vector3_YKey(isolate), y, v8::PropertyAttribute::ReadOnly); + V8::DefineOwnProperty(isolate, ctx, _this, V8::Vector3_ZKey(isolate), z, v8::PropertyAttribute::ReadOnly); +} + +extern V8Class v8Vector3("Vector3", Constructor, [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetStaticAccessor(isolate, tpl, "zero", StaticZero); + V8::SetStaticAccessor(isolate, tpl, "one", StaticOne); + V8::SetStaticAccessor(isolate, tpl, "back", StaticBack); + V8::SetStaticAccessor(isolate, tpl, "down", StaticDown); + V8::SetStaticAccessor(isolate, tpl, "forward", StaticForward); + V8::SetStaticAccessor(isolate, tpl, "left", StaticLeft); + V8::SetStaticAccessor(isolate, tpl, "right", StaticRight); + V8::SetStaticAccessor(isolate, tpl, "up", StaticUp); + V8::SetStaticAccessor(isolate, tpl, "negativeInfinity", StaticNegativeInfinity); + V8::SetStaticAccessor(isolate, tpl, "positiveInfinity", StaticPositiveInfinity); + + V8::SetAccessor(isolate, tpl, "length", Length); + V8::SetMethod(isolate, tpl, "toString", ToString); + V8::SetMethod(isolate, tpl, "toArray", ToArray); + V8::SetMethod(isolate, tpl, "add", Add); + V8::SetMethod(isolate, tpl, "sub", Sub); + V8::SetMethod(isolate, tpl, "div", Divide); + V8::SetMethod(isolate, tpl, "mul", Multiply); + V8::SetMethod(isolate, tpl, "dot", Dot); + V8::SetMethod(isolate, tpl, "cross", Cross); + V8::SetMethod(isolate, tpl, "negative", Negative); + V8::SetMethod(isolate, tpl, "normalize", Normalize); + V8::SetMethod(isolate, tpl, "distanceTo", DistanceTo); + V8::SetMethod(isolate, tpl, "angleTo", AngleTo); + V8::SetMethod(isolate, tpl, "angleToDegrees", AngleToDegrees); + V8::SetMethod(isolate, tpl, "toRadians", ToRadians); + V8::SetMethod(isolate, tpl, "toDegrees", ToDegrees); + V8::SetMethod(isolate, tpl, "isInRange", IsInRange); +}); diff --git a/shared/bindings/WorldObject.cpp b/shared/bindings/WorldObject.cpp new file mode 100644 index 00000000..ed661e9a --- /dev/null +++ b/shared/bindings/WorldObject.cpp @@ -0,0 +1,19 @@ + +#include "../V8Helpers.h" +#include "../V8BindHelpers.h" +#include "../V8ResourceImpl.h" +#include "../V8Class.h" +#include "../V8Entity.h" + +using namespace alt; + +extern V8Class v8BaseObject; +extern V8Class v8WorldObject("WorldObject", v8BaseObject, [](v8::Local tpl) { + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + + V8::SetAccessor(isolate, tpl, "pos"); + +#ifdef ALT_SERVER_API + V8::SetAccessor(isolate, tpl, "dimension"); +#endif // ALT_SERVER_API +}); \ No newline at end of file diff --git a/shared/deps/cpp-sdk b/shared/deps/cpp-sdk new file mode 160000 index 00000000..eee1380f --- /dev/null +++ b/shared/deps/cpp-sdk @@ -0,0 +1 @@ +Subproject commit eee1380f901c0650595d3298d15ef9e734650bd8 diff --git a/shared/events/Main.cpp b/shared/events/Main.cpp new file mode 100644 index 00000000..8c364669 --- /dev/null +++ b/shared/events/Main.cpp @@ -0,0 +1,19 @@ + +#include "../V8ResourceImpl.h" +#include "../V8Helpers.h" + +#include "cpp-sdk/events/CConsoleCommandEvent.h" + +using EventType = alt::CEvent::Type; + +V8_LOCAL_EVENT_HANDLER consoleCommand( + EventType::CONSOLE_COMMAND_EVENT, + "consoleCommand", + [](V8ResourceImpl *resource, const alt::CEvent *e, std::vector> &args) { + auto ev = static_cast(e); + v8::Isolate *isolate = resource->GetIsolate(); + + args.push_back(v8::String::NewFromUtf8(isolate, ev->GetName().GetData(), v8::NewStringType::kNormal, ev->GetName().GetSize()).ToLocalChecked()); + for (auto &arg : ev->GetArgs()) + args.push_back(v8::String::NewFromUtf8(isolate, arg.GetData(), v8::NewStringType::kNormal, arg.GetSize()).ToLocalChecked()); + }); diff --git a/src/Log.h b/src/Log.h deleted file mode 100644 index f27babf4..00000000 --- a/src/Log.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include -#include "cpp-sdk/ICore.h" - -class Log -{ - std::stringstream buf; - - typedef Log&(*LogFn)(Log&); - static Log* _Instance; - - enum Type - { - INFO, - DEBUG, - WARNING, - ERROR, - COLORED - } type = INFO; - - Log() = default; - -public: - Log(const Log&) = delete; - Log(Log&&) = delete; - Log& operator=(const Log&) = delete; - - template Log& Put(const T& val) { buf << val; return *this; } - Log& Put(LogFn val) { return val(*this); } - Log& SetType(Type _type) { type = _type; return *this; } - template Log& operator<<(const T& val) { return Put(val); } - - static constexpr struct Log_Info - { - template Log& operator<<(const T& val) const { return Instance().SetType(INFO).Put(val); } - } Info{}; - - static constexpr struct Log_Debug - { - template Log& operator<<(const T& val) const { return Instance().SetType(DEBUG).Put(val); } - } Debug{}; - - static constexpr struct Log_Warning - { - template Log& operator<<(const T& val) const { return Instance().SetType(WARNING).Put(val); } - } Warning{}; - - static constexpr struct Log_Error - { - template Log& operator<<(const T& val) const { return Instance().SetType(ERROR).Put(val); } - } Error{}; - - static constexpr struct Log_Colored - { - template Log& operator<<(const T& val) const { return Instance().SetType(COLORED).Put(val); } - } Colored{}; - - static Log& Endl(Log& log) - { - switch (log.type) - { - case INFO: - alt::ICore::Instance().LogInfo(log.buf.str()); - break; - case DEBUG: - alt::ICore::Instance().LogDebug(log.buf.str().c_str()); - break; - case WARNING: - alt::ICore::Instance().LogWarning(log.buf.str().c_str()); - break; - case ERROR: - alt::ICore::Instance().LogError(log.buf.str().c_str()); - break; - case COLORED: - alt::ICore::Instance().LogColored(log.buf.str().c_str()); - break; - } - - log.buf.str(""); - return log; - } - - static Log& Instance() - { - static Log _Instance; - return _Instance; - } -}; \ No newline at end of file diff --git a/src/bindings/Checkpoint.cpp b/src/bindings/Checkpoint.cpp deleted file mode 100644 index ab50b4bd..00000000 --- a/src/bindings/Checkpoint.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "stdafx.h" - -#include "helpers/V8Helpers.h" -#include "helpers/V8ResourceImpl.h" - -using namespace alt; - -static void Constructor(const v8::FunctionCallbackInfo& info) -{ - V8_GET_ISOLATE_CONTEXT_RESOURCE(); - - V8_CHECK(info.IsConstructCall(), "Checkpoint constructor is not a function"); - V8_CHECK_ARGS_LEN(10); - - V8_ARG_TO_INT(1, type); - V8_ARG_TO_NUMBER(2, x); - V8_ARG_TO_NUMBER(3, y); - V8_ARG_TO_NUMBER(4, z); - V8_ARG_TO_NUMBER(5, radius); - V8_ARG_TO_NUMBER(6, height); - V8_ARG_TO_INT(7, r); - V8_ARG_TO_INT(8, g); - V8_ARG_TO_INT(9, b); - V8_ARG_TO_INT(10, a); - - alt::Position pos(x, y, z); - alt::RGBA color(r, g, b, a); - - Ref cp = ICore::Instance().CreateCheckpoint(type, pos, radius, height, color); - - if (cp) - resource->BindEntity(info.This(), cp.Get()); - else - V8Helpers::Throw(isolate, "Failed to create Checkpoint"); -} - -extern V8Class v8Colshape; -extern V8Class v8Checkpoint("Checkpoint", v8Colshape, Constructor, [](v8::Local tpl) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); -}); diff --git a/src/cpp-sdk b/src/cpp-sdk deleted file mode 160000 index 2752a9ca..00000000 --- a/src/cpp-sdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2752a9ca8ce2bb3e8677d274f41d216d2736451e diff --git a/src/helpers b/src/helpers deleted file mode 160000 index e69bee17..00000000 --- a/src/helpers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e69bee17bbe8eda1d899998c21a675486ba73020