mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-09 09:38:19 +00:00
Add support for Rust code in DDNet
The glue is done using the [cxx crate](https://cxx.rs/) on the Rust side. As a proof-of-concept, only a small console command (`rust_version`) printing the currently used Rust version was added. You can generate and open the Rust documentation using `DDNET_TEST_NO_LINK=1 cargo doc --open`. You can run the Rust tests using `cmake --build <build dir> --target run_rust_tests`, they're automatically included in the `run_tests` target as well. Rust tests don't work on Windows in debug mode on Windows because Rust cannot currently link with the debug version of the C stdlib on Windows: https://github.com/rust-lang/rust/issues/39016. --- The stuff in `src/rust-bridge` is generated using ``` cxxbridge src/engine/shared/rust_version.rs --output src/rust-bridge/engine/shared/rust_version.cpp --output src/rust-bridge/engine/shared/rust_version.h cxxbridge src/engine/console.rs --output src/rust-bridge/cpp/console.cpp --output src/rust-bridge/cpp/console.h ```
This commit is contained in:
parent
1b114b2565
commit
dcd76fd3e1
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[target.'cfg(target_env = "msvc")']
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
21
.github/workflows/build.yaml
vendored
21
.github/workflows/build.yaml
vendored
|
@ -25,10 +25,9 @@ jobs:
|
|||
cmake-path: /usr/bin/
|
||||
cmake-args: -G Ninja -DTEST_MYSQL=ON
|
||||
cmake-init-env: CXXFLAGS=-Werror
|
||||
gtest-env: GTEST_FILTER=-*SQLite*
|
||||
package-file: "*-linux_x86_64.tar.xz"
|
||||
fancy: false
|
||||
env:
|
||||
GTEST_FILTER: -*SQLite*
|
||||
- os: macOS-latest
|
||||
cmake-args: -G Ninja
|
||||
cmake-init-env: CXXFLAGS=-Werror
|
||||
|
@ -55,6 +54,7 @@ jobs:
|
|||
- name: Prepare Linux (non-fancy)
|
||||
if: ${{ contains(matrix.os, 'ubuntu') && !matrix.fancy }}
|
||||
run: |
|
||||
rustup default 1.48.0
|
||||
sudo rm -rf /var/lib/mysql/ /var/run/mysqld
|
||||
sudo mkdir /var/lib/mysql/ /var/run/mysqld/
|
||||
sudo chown mysql:mysql /var/lib/mysql/ /var/run/mysqld/
|
||||
|
@ -93,7 +93,6 @@ jobs:
|
|||
sudo rm -rf /Library/Developer/CommandLineTools
|
||||
|
||||
- name: Build in debug mode
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
mkdir debug
|
||||
cd debug
|
||||
|
@ -101,19 +100,16 @@ jobs:
|
|||
${{ matrix.cmake-path }}cmake --build . --config Debug --target everything ${{ matrix.build-args }}
|
||||
|
||||
- name: Test debug
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
cd debug
|
||||
${{ matrix.cmake-path }}cmake --build . --config Debug --target run_tests ${{ matrix.build-args }}
|
||||
${{ matrix.cmake-path }}cmake -E env ${{ matrix.gtest-env }} ${{ matrix.cmake-path }}cmake --build . --config Debug --target run_tests ${{ matrix.build-args }}
|
||||
|
||||
- name: Run debug server
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
cd debug
|
||||
./DDNet-Server shutdown
|
||||
|
||||
- name: Build in release mode
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
mkdir release
|
||||
cd release
|
||||
|
@ -121,20 +117,17 @@ jobs:
|
|||
${{ matrix.cmake-path }}cmake --build . --config Release --target everything ${{ matrix.build-args }}
|
||||
|
||||
- name: Test release
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
cd release
|
||||
${{ matrix.cmake-path }}cmake --build . --config Release --target run_tests ${{ matrix.build-args }}
|
||||
${{ matrix.cmake-path }}cmake -E env ${{ matrix.gtest-env }} ${{ matrix.cmake-path }}cmake --build . --config Release --target run_tests ${{ matrix.build-args }}
|
||||
|
||||
- name: Run release server
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
cd release
|
||||
./DDNet-Server shutdown
|
||||
|
||||
- name: Build headless client
|
||||
if: contains(matrix.os, 'ubuntu-latest')
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
mkdir headless
|
||||
cd headless
|
||||
|
@ -178,7 +171,6 @@ jobs:
|
|||
|
||||
- name: Build in release mode with debug info and all features on
|
||||
if: matrix.fancy
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
mkdir fancy
|
||||
cd fancy
|
||||
|
@ -187,14 +179,13 @@ jobs:
|
|||
|
||||
- name: Test fancy
|
||||
if: matrix.fancy
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
find /usr/lib/ -name '*libwebsockets*'
|
||||
cd fancy
|
||||
${{ matrix.cmake-path }}cmake --build . --config RelWithDebInfo --target run_tests ${{ matrix.build-args }}
|
||||
${{ matrix.cmake-path }}cmake -E env ${{ matrix.gtest-env }} ${{ matrix.cmake-path }}cmake --build . --config RelWithDebInfo --target run_tests ${{ matrix.build-args }}
|
||||
|
||||
- name: Run fancy server
|
||||
if: matrix.fancy
|
||||
env: ${{ matrix.env }}
|
||||
run: |
|
||||
cd fancy
|
||||
./DDNet-Server shutdown
|
||||
|
|
42
.github/workflows/rust.yml
vendored
Normal file
42
.github/workflows/rust.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
name: Check Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- staging.tmp
|
||||
- trying.tmp
|
||||
- staging-squash-merge.tmp
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
rustdoc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run Rustdoc
|
||||
run: |
|
||||
RUSTDOCFLAGS=-Dwarnings DDNET_TEST_NO_LINK=1 cargo doc
|
||||
|
||||
rustfmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run Rustfmt
|
||||
run:
|
||||
cargo fmt -- --check
|
||||
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
checks:
|
||||
- advisories
|
||||
- bans licenses sources
|
||||
|
||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
command: check ${{ matrix.checks }}
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -11,6 +11,7 @@ bundle/
|
|||
.DS_Store
|
||||
.ninja_deps
|
||||
.ninja_log
|
||||
CACHEDIR.TAG
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeSettings*
|
||||
|
@ -20,10 +21,12 @@ CTestTestfile.cmake
|
|||
Debug
|
||||
Makefile
|
||||
Release
|
||||
SAN.*
|
||||
_CPack_Packages/
|
||||
build.ninja
|
||||
checksummed_*
|
||||
cmake_install.cmake
|
||||
debug
|
||||
gmock.pc
|
||||
gmock_main.pc
|
||||
googletest-build/
|
||||
|
@ -34,8 +37,8 @@ gtest_main.pc
|
|||
install_manifest*.txt
|
||||
ninja_package
|
||||
pack_*/
|
||||
release
|
||||
rules.ninja
|
||||
SAN.*
|
||||
testrunner\[1\]_include.cmake
|
||||
vulkan_shaders_sha256.txt
|
||||
|
||||
|
|
224
CMakeLists.txt
224
CMakeLists.txt
|
@ -547,6 +547,7 @@ find_package(Opus)
|
|||
find_package(Opusfile)
|
||||
find_package(PNG)
|
||||
find_package(PythonInterp 3)
|
||||
find_package(Rust)
|
||||
find_package(SDL2)
|
||||
find_package(SQLite3)
|
||||
if(DISCORD)
|
||||
|
@ -590,6 +591,7 @@ if(TARGET_OS STREQUAL "mac")
|
|||
endif()
|
||||
message(STATUS ${TARGET})
|
||||
message(STATUS "Compiler: ${CMAKE_CXX_COMPILER}")
|
||||
message(STATUS "Rust version: ${RUST_VERSION_STRING}")
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
message(STATUS "Dependencies:")
|
||||
|
@ -654,6 +656,9 @@ endif()
|
|||
if(NOT(PYTHONINTERP_FOUND))
|
||||
message(SEND_ERROR "You must install Python to compile ${CMAKE_PROJECT_NAME}")
|
||||
endif()
|
||||
if(NOT(RUST_FOUND))
|
||||
message(SEND_ERROR "You must install Rust and Cargo to compile ${CMAKE_PROJECT_NAME}")
|
||||
endif()
|
||||
if(NOT(SQLite3_FOUND))
|
||||
message(SEND_ERROR "You must install SQLite3 to compile ${CMAKE_PROJECT_NAME}")
|
||||
endif()
|
||||
|
@ -721,7 +726,12 @@ endif()
|
|||
if(TARGET_OS STREQUAL "windows")
|
||||
set(PLATFORM_CLIENT)
|
||||
set(PLATFORM_CLIENT_LIBS opengl32 winmm)
|
||||
set(PLATFORM_LIBS shlwapi version ws2_32) # Windows sockets
|
||||
set(PLATFORM_LIBS)
|
||||
list(APPEND PLATFORM_LIBS shlwapi) # PathIsRelativeW
|
||||
list(APPEND PLATFORM_LIBS version ws2_32) # Windows sockets
|
||||
list(APPEND PLATFORM_LIBS bcrypt userenv) # for Rust (https://github.com/rust-lang/rust/issues/91974)
|
||||
list(APPEND PLATFORM_LIBS ole32) # CoInitialize(Ex)
|
||||
list(APPEND PLATFORM_LIBS shell32)
|
||||
elseif(TARGET_OS STREQUAL "mac")
|
||||
find_library(CARBON Carbon)
|
||||
find_library(COCOA Cocoa)
|
||||
|
@ -753,7 +763,9 @@ else()
|
|||
set(PLATFORM_CLIENT_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR} ${NOTIFY_INCLUDE_DIRS})
|
||||
set(PLATFORM_CLIENT)
|
||||
if(TARGET_OS STREQUAL "linux")
|
||||
set(PLATFORM_LIBS rt) # clock_gettime for glibc < 2.17
|
||||
set(PLATFORM_LIBS)
|
||||
list(APPEND PLATFORM_LIBS rt) # clock_gettime for glibc < 2.17
|
||||
list(APPEND PLATFORM_LIBS dl) # for Rust
|
||||
else()
|
||||
set(PLATFORM_LIBS)
|
||||
endif()
|
||||
|
@ -834,6 +846,161 @@ if(NOT CRYPTO_FOUND)
|
|||
set(DEP_MD5 $<TARGET_OBJECTS:md5>)
|
||||
endif()
|
||||
|
||||
########################################################################
|
||||
# RUST
|
||||
########################################################################
|
||||
|
||||
set_glob(RUST_BASE GLOB_RECURSE "rs;toml" src/base
|
||||
Cargo.toml
|
||||
color.rs
|
||||
lib.rs
|
||||
rust.rs
|
||||
)
|
||||
|
||||
set_glob(RUST_ENGINE_INTERFACE GLOB "rs;toml" src/engine
|
||||
Cargo.toml
|
||||
console.rs
|
||||
lib.rs
|
||||
)
|
||||
|
||||
set_glob(RUST_ENGINE_SHARED GLOB_RECURSE "rs;toml" src/engine/shared
|
||||
Cargo.toml
|
||||
build.rs
|
||||
config.rs
|
||||
lib.rs
|
||||
rust_version.rs
|
||||
)
|
||||
|
||||
set_src(RUST_BRIDGE_SHARED GLOB_RECURSE src/rust-bridge
|
||||
cpp/console.cpp
|
||||
cpp/console.h
|
||||
engine/shared/rust_version.cpp
|
||||
engine/shared/rust_version.h
|
||||
)
|
||||
|
||||
set_glob(RUST_MASTERSRV GLOB "rs;toml" src/mastersrv/src
|
||||
addr.rs
|
||||
locations.rs
|
||||
main.rs
|
||||
)
|
||||
|
||||
add_library(rust-bridge-shared EXCLUDE_FROM_ALL OBJECT ${RUST_BRIDGE_SHARED})
|
||||
list(APPEND TARGETS_OWN rust-bridge-shared)
|
||||
|
||||
set(CARGO_BUILD_DIR "")
|
||||
set(CARGO_BUILD ${CMAKE_COMMAND} -E env CARGO_TARGET_DIR=${PROJECT_BINARY_DIR} DDNET_TEST_NO_LINK=1 ${RUST_CARGO} build)
|
||||
set(CARGO_TEST ${CMAKE_COMMAND} -E env CARGO_TARGET_DIR=${PROJECT_BINARY_DIR} ${RUST_CARGO} test)
|
||||
if(MSVC)
|
||||
list(INSERT CARGO_BUILD 0 ${CMAKE_COMMAND} -E env $<$<CONFIG:Debug>:CFLAGS=/MTd> $<$<CONFIG:Debug>:CXXFLAGS=/MTd>)
|
||||
list(INSERT CARGO_TEST 0 ${CMAKE_COMMAND} -E env RUSTFLAGS=-Ctarget-feature=+crt-static)
|
||||
endif()
|
||||
if(RUST_NIGHTLY)
|
||||
list(APPEND CARGO_BUILD -Z build-std=std,panic_abort)
|
||||
endif()
|
||||
if(NOT CMAKE_OSX_ARCHITECTURES AND (DEFINED CMAKE_RUST_COMPILER_TARGET OR RUST_NIGHTLY))
|
||||
if(DEFINED CMAKE_RUST_COMPILER_TARGET)
|
||||
list(APPEND CARGO_TEST --target ${CMAKE_RUST_COMPILER_TARGET})
|
||||
set(RUST_TARGET ${CMAKE_RUST_COMPILER_TARGET})
|
||||
else()
|
||||
set(RUST_TARGET ${RUST_TARGET_HOST})
|
||||
endif()
|
||||
list(APPEND CARGO_BUILD --target ${RUST_TARGET})
|
||||
set(CARGO_BUILD_DIR "${RUST_TARGET}/")
|
||||
endif()
|
||||
set(CARGO_BUILD_DIR_DEBUG "${CARGO_BUILD_DIR}debug")
|
||||
set(CARGO_BUILD_DIR_RELEASE "${CARGO_BUILD_DIR}release")
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.20)
|
||||
message(SEND_ERROR "Multi-config generators only supported from CMake 3.20 and up")
|
||||
else()
|
||||
set(CARGO_BUILD_DIR "${CARGO_BUILD_DIR}$<$<CONFIG:Debug>:debug>$<$<NOT:$<CONFIG:Debug>>:release>")
|
||||
endif()
|
||||
else()
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set(CARGO_BUILD_DIR "${CARGO_BUILD_DIR_DEBUG}")
|
||||
else()
|
||||
set(CARGO_BUILD_DIR "${CARGO_BUILD_DIR_RELEASE}")
|
||||
endif()
|
||||
endif()
|
||||
list(APPEND CARGO_BUILD $<$<NOT:$<CONFIG:Debug>>:--release>)
|
||||
|
||||
if(CMAKE_OSX_ARCHITECTURES)
|
||||
set(RUST_OSX_ARCHITECTURES)
|
||||
foreach(arch ${CMAKE_OSX_ARCHITECTURES})
|
||||
if(${arch} STREQUAL arm64)
|
||||
list(APPEND RUST_OSX_ARCHITECTURES aarch64-apple-darwin)
|
||||
elseif(${arch} STREQUAL x86_64)
|
||||
list(APPEND RUST_OSX_ARCHITECTURES x86_64-apple-darwin)
|
||||
else()
|
||||
message(SEND_ERROR "CMAKE_OSX_ARCHITECTURES' architecture ${arch} unknown, can't build Rust code (known: arm64, x86_64)")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set(RUST_SRC
|
||||
${RUST_BASE}
|
||||
${RUST_ENGINE_INTERFACE}
|
||||
${RUST_ENGINE_SHARED}
|
||||
Cargo.toml
|
||||
Cargo.lock
|
||||
)
|
||||
set(RUST_TARGETS engine_shared)
|
||||
if(NOT CMAKE_OSX_ARCHITECTURES)
|
||||
set(RUST_OUTPUTS)
|
||||
foreach(rust_target ${RUST_TARGETS})
|
||||
set(LIBRARY_NAME "${CMAKE_STATIC_LIBRARY_PREFIX}ddnet_${rust_target}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
add_library(rust_${rust_target} STATIC IMPORTED GLOBAL)
|
||||
add_custom_target(rust_${rust_target}_target DEPENDS "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR}/${LIBRARY_NAME}")
|
||||
add_dependencies(rust_${rust_target} rust_${rust_target}_target)
|
||||
set_target_properties(rust_${rust_target} PROPERTIES
|
||||
IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR_RELEASE}/${LIBRARY_NAME}"
|
||||
IMPORTED_LOCATION_DEBUG "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR_DEBUG}/${LIBRARY_NAME}"
|
||||
)
|
||||
list(APPEND RUST_OUTPUTS "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR}/${LIBRARY_NAME}")
|
||||
endforeach()
|
||||
add_custom_command(
|
||||
OUTPUT ${RUST_OUTPUTS}
|
||||
COMMAND ${CARGO_BUILD}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
USES_TERMINAL
|
||||
DEPENDS ${RUST_SRC}
|
||||
)
|
||||
else()
|
||||
foreach(rust_target ${RUST_TARGETS})
|
||||
set(LIBRARY_NAME "${CMAKE_STATIC_LIBRARY_PREFIX}ddnet_${rust_target}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
add_library(rust_${rust_target} STATIC IMPORTED GLOBAL)
|
||||
set_target_properties(rust_${rust_target} PROPERTIES
|
||||
IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR_RELEASE}/${LIBRARY_NAME}"
|
||||
IMPORTED_LOCATION_DEBUG "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR_DEBUG}/${LIBRARY_NAME}"
|
||||
)
|
||||
add_custom_target(rust_${rust_target}_target DEPENDS "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR}/${LIBRARY_NAME}")
|
||||
add_dependencies(rust_${rust_target} rust_${rust_target}_target)
|
||||
set(ARCH_LIBRARIES)
|
||||
foreach(arch ${RUST_OSX_ARCHITECTURES})
|
||||
list(APPEND ARCH_LIBRARIES "${PROJECT_BINARY_DIR}/${arch}/${CARGO_BUILD_DIR}/${LIBRARY_NAME}")
|
||||
endforeach()
|
||||
add_custom_command(
|
||||
OUTPUT "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR}/${LIBRARY_NAME}"
|
||||
COMMAND lipo ${ARCH_LIBRARIES} -create -output "${PROJECT_BINARY_DIR}/${CARGO_BUILD_DIR}/${LIBRARY_NAME}"
|
||||
DEPENDS ${ARCH_LIBRARIES}
|
||||
)
|
||||
endforeach()
|
||||
foreach(arch ${RUST_OSX_ARCHITECTURES})
|
||||
set(RUST_OUTPUTS)
|
||||
foreach(rust_target ${RUST_TARGETS})
|
||||
set(LIBRARY_NAME "${CMAKE_STATIC_LIBRARY_PREFIX}ddnet_${rust_target}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
list(APPEND RUST_OUTPUTS "${PROJECT_BINARY_DIR}/${arch}/${CARGO_BUILD_DIR}/${LIBRARY_NAME}")
|
||||
endforeach()
|
||||
add_custom_command(
|
||||
OUTPUT ${RUST_OUTPUTS}
|
||||
COMMAND ${CARGO_BUILD} --target=${arch}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
USES_TERMINAL
|
||||
DEPENDS ${RUST_SRC}
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
########################################################################
|
||||
# DATA
|
||||
########################################################################
|
||||
|
@ -1703,6 +1870,7 @@ set_src(BASE GLOB_RECURSE src/base
|
|||
log.h
|
||||
logger.h
|
||||
math.h
|
||||
rust.h
|
||||
system.cpp
|
||||
system.h
|
||||
tl/threading.h
|
||||
|
@ -1733,6 +1901,7 @@ set_src(ENGINE_INTERFACE GLOB src/engine
|
|||
keys.h
|
||||
map.h
|
||||
message.h
|
||||
rust.h
|
||||
server.h
|
||||
serverbrowser.h
|
||||
sound.h
|
||||
|
@ -2131,7 +2300,6 @@ if(CLIENT)
|
|||
|
||||
# Libraries
|
||||
set(LIBS_CLIENT
|
||||
${LIBS}
|
||||
${FREETYPE_LIBRARIES}
|
||||
${GLEW_LIBRARIES}
|
||||
${PNG_LIBRARIES}
|
||||
|
@ -2147,12 +2315,10 @@ if(CLIENT)
|
|||
${VULKAN_LIBRARIES}
|
||||
|
||||
${TARGET_STEAMAPI}
|
||||
rust_engine_shared
|
||||
|
||||
${PLATFORM_CLIENT_LIBS}
|
||||
|
||||
# Add pthreads (on non-Windows) at the end, so that other libraries can depend
|
||||
# on it.
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${LIBS}
|
||||
)
|
||||
|
||||
if(DISCORD)
|
||||
|
@ -2188,6 +2354,7 @@ if(CLIENT)
|
|||
$<TARGET_OBJECTS:engine-gfx>
|
||||
$<TARGET_OBJECTS:engine-shared>
|
||||
$<TARGET_OBJECTS:game-shared>
|
||||
$<TARGET_OBJECTS:rust-bridge-shared>
|
||||
)
|
||||
else()
|
||||
add_executable(${TARGET_CLIENT} WIN32
|
||||
|
@ -2198,6 +2365,7 @@ if(CLIENT)
|
|||
$<TARGET_OBJECTS:engine-gfx>
|
||||
$<TARGET_OBJECTS:engine-shared>
|
||||
$<TARGET_OBJECTS:game-shared>
|
||||
$<TARGET_OBJECTS:rust-bridge-shared>
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(${TARGET_CLIENT} ${LIBS_CLIENT})
|
||||
|
@ -2370,13 +2538,12 @@ if(SERVER)
|
|||
|
||||
# Libraries
|
||||
set(LIBS_SERVER
|
||||
${LIBS}
|
||||
${MINIUPNPC_LIBRARIES}
|
||||
${MYSQL_LIBRARIES}
|
||||
${TARGET_ANTIBOT}
|
||||
${MINIUPNPC_LIBRARIES}
|
||||
# Add pthreads (on non-Windows) at the end, so that other libraries can depend
|
||||
# on it.
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
rust_engine_shared
|
||||
|
||||
${LIBS}
|
||||
)
|
||||
|
||||
# Target
|
||||
|
@ -2387,6 +2554,7 @@ if(SERVER)
|
|||
${SERVER_ICON}
|
||||
$<TARGET_OBJECTS:engine-shared>
|
||||
$<TARGET_OBJECTS:game-shared>
|
||||
$<TARGET_OBJECTS:rust-bridge-shared>
|
||||
)
|
||||
target_link_libraries(${TARGET_SERVER} ${LIBS_SERVER})
|
||||
target_include_directories(${TARGET_SERVER} PRIVATE ${PNG_INCLUDE_DIRS})
|
||||
|
@ -2571,7 +2739,7 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST)
|
|||
$<TARGET_OBJECTS:game-shared>
|
||||
${DEPS}
|
||||
)
|
||||
target_link_libraries(${TARGET_TESTRUNNER} ${LIBS} ${MYSQL_LIBRARIES} ${PNG_LIBRARIES} ${GTEST_LIBRARIES})
|
||||
target_link_libraries(${TARGET_TESTRUNNER} ${MYSQL_LIBRARIES} ${PNG_LIBRARIES} ${GTEST_LIBRARIES} ${LIBS})
|
||||
target_include_directories(${TARGET_TESTRUNNER} SYSTEM PRIVATE ${GTEST_INCLUDE_DIRS})
|
||||
|
||||
list(APPEND TARGETS_OWN ${TARGET_TESTRUNNER})
|
||||
|
@ -2583,7 +2751,36 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST)
|
|||
DEPENDS ${TARGET_TESTRUNNER}
|
||||
USES_TERMINAL
|
||||
)
|
||||
if(NOT MSVC OR CMAKE_BUILD_TYPE STREQUAL Release)
|
||||
# On MSVC, Rust tests only work in the release mode because we link our C++
|
||||
# code with the debug C standard library (/MTd) but Rust only supports
|
||||
# linking to the release C standard library (/MT).
|
||||
#
|
||||
# See also https://github.com/rust-lang/rust/issues/39016.
|
||||
add_dependencies(run_tests run_rust_tests)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(rust_test STATIC EXCLUDE_FROM_ALL
|
||||
$<TARGET_OBJECTS:engine-gfx>
|
||||
$<TARGET_OBJECTS:engine-shared>
|
||||
$<TARGET_OBJECTS:game-shared>
|
||||
$<TARGET_OBJECTS:rust-bridge-shared>
|
||||
${DEPS}
|
||||
)
|
||||
|
||||
list(APPEND TARGETS_OWN rust_test)
|
||||
list(APPEND TARGETS_LINK rust_test)
|
||||
|
||||
set(RUST_TEST_LIBS ${LIBS} $<TARGET_FILE:rust_test>)
|
||||
list(REMOVE_ITEM RUST_TEST_LIBS "-pthread")
|
||||
add_custom_target(run_rust_tests
|
||||
COMMAND ${CMAKE_COMMAND} -E env "DDNET_TEST_LIBRARIES=${RUST_TEST_LIBS}" ${CARGO_TEST}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
USES_TERMINAL
|
||||
DEPENDS rust_test
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(run_integration_tests
|
||||
COMMAND ${PROJECT_BINARY_DIR}/integration_test.sh ${INTEGRATIONTESTRUNNER_ARGS}
|
||||
|
@ -3060,6 +3257,7 @@ foreach(target ${TARGETS_OWN})
|
|||
endif()
|
||||
target_include_directories(${target} PRIVATE ${PROJECT_BINARY_DIR}/src)
|
||||
target_include_directories(${target} PRIVATE src)
|
||||
target_include_directories(${target} PRIVATE src/rust-bridge)
|
||||
target_compile_definitions(${target} PRIVATE $<$<CONFIG:Debug>:CONF_DEBUG>)
|
||||
target_include_directories(${target} SYSTEM PRIVATE ${CURL_INCLUDE_DIRS} ${SQLite3_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS})
|
||||
target_compile_definitions(${target} PRIVATE GLEW_STATIC)
|
||||
|
|
114
Cargo.lock
generated
Normal file
114
Cargo.lock
generated
Normal file
|
@ -0,0 +1,114 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5469a6f42296f4fd40789b397383718f9a0bd75d2f9b7cedbb249996811fba27"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fef2b4ffdc935c973bc7817d541fc936fdc8a85194cfdd9c761aca8387edd48"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d3a240a54f5526967ffae81fdcda1fc80564964220d90816960b2eae2eab7f4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ddnet-base"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"ddnet-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ddnet-engine"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"ddnet-base",
|
||||
"ddnet-engine-shared",
|
||||
"ddnet-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ddnet-engine-shared"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"ddnet-base",
|
||||
"ddnet-engine",
|
||||
"ddnet-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ddnet-test"
|
||||
version = "0.0.1"
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cae2cd7ba2f3f63938b9c724475dfb7b9861b545a90324476324ed21dbc8c8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"src/base",
|
||||
"src/engine",
|
||||
"src/engine/shared",
|
||||
"src/rust-bridge/test",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
panic = "abort"
|
||||
|
|
@ -185,7 +185,7 @@ Expect a large slow down.
|
|||
Building on Windows with Visual Studio
|
||||
--------------------------------------
|
||||
|
||||
Download and install some version of [Microsoft Visual Studio](https://www.visualstudio.com/) (as of writing, MSVS Community 2017) with **C++ support**, install [Python 3](https://www.python.org/downloads/) **for all users** and install [CMake](https://cmake.org/download/#latest).
|
||||
Download and install some version of [Microsoft Visual Studio](https://www.visualstudio.com/) (as of writing, MSVS Community 2017) with **C++ support**, install [Python 3](https://www.python.org/downloads/) **for all users** and install [CMake](https://cmake.org/download/#latest). You also need to install [Rust](https://rustup.rs/).
|
||||
|
||||
Start CMake and select the source code folder (where DDNet resides, the directory with `CMakeLists.txt`). Additionally select a build folder, e.g. create a build subdirectory in the source code directory. Click "Configure" and select the Visual Studio generator (it should be pre-selected, so pressing "Finish" will suffice). After configuration finishes and the "Generate" reactivates, click it. When that finishes, click "Open Project". Visual Studio should open. You can compile the DDNet client by right-clicking the DDNet project (not the solution) and select "Select as StartUp project". Now you should be able to compile DDNet by clicking the green, triangular "Run" button.
|
||||
|
||||
|
|
29
cmake/FindRust.cmake
Normal file
29
cmake/FindRust.cmake
Normal file
|
@ -0,0 +1,29 @@
|
|||
find_program(RUST_RUSTC rustc)
|
||||
find_program(RUST_CARGO cargo)
|
||||
|
||||
if(RUST_RUSTC)
|
||||
execute_process(COMMAND ${RUST_RUSTC} --version --verbose OUTPUT_VARIABLE RUSTC_VERSION_OUTPUT)
|
||||
string(REPLACE "\n" ";" RUSTC_VERSION_OUTPUT "${RUSTC_VERSION_OUTPUT}")
|
||||
set(RUST_NIGHTLY OFF)
|
||||
foreach(line ${RUSTC_VERSION_OUTPUT})
|
||||
if(NOT DEFINED RUST_VERSION_STRING)
|
||||
set(RUST_VERSION_STRING ${line})
|
||||
endif()
|
||||
if(line MATCHES "^([^:]+): (.*)$")
|
||||
set(KEY ${CMAKE_MATCH_1})
|
||||
set(VALUE ${CMAKE_MATCH_2})
|
||||
if(KEY STREQUAL "release")
|
||||
set(RUST_VERSION ${VALUE})
|
||||
if(VALUE MATCHES "nightly")
|
||||
set(RUST_NIGHTLY ON)
|
||||
endif()
|
||||
elseif(KEY STREQUAL "host")
|
||||
set(RUST_TARGET_HOST ${VALUE})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Rust DEFAULT_MSG RUST_RUSTC RUST_CARGO)
|
||||
mark_as_advanced(RUST_RUSTC RUST_CARGO)
|
|
@ -35,3 +35,4 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -D_REENTRANT -g -O3 ${WASM_CXX_
|
|||
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -pthread -D_REENTRANT -g -O3 ${WASM_CXX_ENGINE_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread ${WASM_ENGINE_FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${WASM_ENGINE_FLAGS}")
|
||||
set(CMAKE_RUST_COMPILER_TARGET wasm32-unknown-emscripten)
|
||||
|
|
|
@ -11,6 +11,7 @@ set(CMAKE_C_COMPILER oa64-clang)
|
|||
set(CMAKE_CXX_COMPILER oa64-clang++)
|
||||
set(CMAKE_INSTALL_NAME_TOOL aarch64-apple-$ENV{OSXCROSS_TARGET}-install_name_tool)
|
||||
set(CMAKE_OTOOL aarch64-apple-$ENV{OSXCROSS_TARGET}-otool)
|
||||
set(CMAKE_RUST_COMPILER_TARGET aarch64-apple-darwin)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
|
|
|
@ -11,6 +11,7 @@ set(CMAKE_C_COMPILER o64-clang)
|
|||
set(CMAKE_CXX_COMPILER o64-clang++)
|
||||
set(CMAKE_INSTALL_NAME_TOOL x86_64-apple-$ENV{OSXCROSS_TARGET}-install_name_tool)
|
||||
set(CMAKE_OTOOL x86_64-apple-$ENV{OSXCROSS_TARGET}-otool)
|
||||
set(CMAKE_RUST_COMPILER_TARGET x86_64-apple-darwin)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
|
|
|
@ -3,6 +3,7 @@ set(CMAKE_SYSTEM_NAME Windows)
|
|||
set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
|
||||
set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
|
||||
set(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
|
||||
set(CMAKE_RUST_COMPILER_TARGET i686-pc-windows-gnu)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
|
|
|
@ -3,6 +3,7 @@ set(CMAKE_SYSTEM_NAME Windows)
|
|||
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
|
||||
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
|
||||
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
|
||||
set(CMAKE_RUST_COMPILER_TARGET x86_64-pc-windows-gnu)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
|
|
|
@ -40,7 +40,7 @@ def check_dir(directory):
|
|||
for file in file_list:
|
||||
path = directory + file
|
||||
if os.path.isdir(path):
|
||||
if file not in ("external", "generated"):
|
||||
if file not in ("external", "generated", "rust-bridge"):
|
||||
errors += check_dir(path + "/")
|
||||
elif file.endswith(".h") and file != "keynames.h":
|
||||
errors += check_file(path)
|
||||
|
|
|
@ -20,7 +20,8 @@ IGNORE_FILES = [
|
|||
def filter_ignored(filenames):
|
||||
return [filename for filename in filenames
|
||||
if filename not in IGNORE_FILES
|
||||
and not filename.startswith("src/game/generated/")]
|
||||
and not filename.startswith("src/game/generated/")
|
||||
and not filename.startswith("src/rust-bridge")]
|
||||
|
||||
def filter_cpp(filenames):
|
||||
return [filename for filename in filenames
|
||||
|
|
15
src/base/Cargo.toml
Normal file
15
src/base/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "ddnet-base"
|
||||
version = "0.0.1"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
license = "Zlib"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
cxx = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ddnet-test = { path = "../rust-bridge/test", features = ["link-test-libraries"] }
|
77
src/base/color.rs
Normal file
77
src/base/color.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
/// Color, in RGBA format. Corresponds to the C++ type `ColorRGBA`.
|
||||
///
|
||||
/// The color is represented by red, green, blue and alpha values between `0.0`
|
||||
/// and `1.0`.
|
||||
///
|
||||
/// See also <https://en.wikipedia.org/wiki/RGBA_color_model>.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ddnet_base::ColorRGBA;
|
||||
///
|
||||
/// let white = ColorRGBA { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
|
||||
/// let black = ColorRGBA { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
|
||||
/// let red = ColorRGBA { r: 1.0, g: 0.0, b: 0.0, a: 1.0 };
|
||||
/// let transparent = ColorRGBA { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
|
||||
///
|
||||
/// // #ffa500
|
||||
/// let ddnet_logo_color = ColorRGBA { r: 1.0, g: 0.6470588235294118, b: 0.0, a: 1.0 };
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct ColorRGBA {
|
||||
/// Red
|
||||
pub r: f32,
|
||||
/// Green
|
||||
pub g: f32,
|
||||
/// Blue
|
||||
pub b: f32,
|
||||
/// Alpha (i.e. opacity. `0.0` means fully transparent, `1.0`
|
||||
/// nontransparent).
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
unsafe impl cxx::ExternType for ColorRGBA {
|
||||
type Id = cxx::type_id!("ColorRGBA");
|
||||
type Kind = cxx::kind::Trivial;
|
||||
}
|
||||
|
||||
/// Color, in HSLA format. Corresponds to the C++ type `ColorHSLA`.
|
||||
///
|
||||
/// The color is represented by hue, saturation, lightness and alpha values
|
||||
/// between `0.0` and `1.0`.
|
||||
///
|
||||
/// See also <https://en.wikipedia.org/wiki/HSL_and_HSV>.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ddnet_base::ColorHSLA;
|
||||
///
|
||||
/// let white = ColorHSLA { h: 0.0, s: 0.0, l: 1.0, a: 1.0 };
|
||||
/// let black = ColorHSLA { h: 0.0, s: 0.0, l: 0.0, a: 1.0 };
|
||||
/// let red = ColorHSLA { h: 0.0, s: 1.0, l: 0.5, a: 1.0 };
|
||||
/// let transparent = ColorHSLA { h: 0.0, s: 0.0, l: 0.0, a: 0.0 };
|
||||
///
|
||||
/// // #ffa500
|
||||
/// let ddnet_logo_color = ColorHSLA { h: 0.10784314, s: 1.0, l: 0.5, a: 1.0 };
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct ColorHSLA {
|
||||
/// Hue
|
||||
pub h: f32,
|
||||
/// Saturation
|
||||
pub s: f32,
|
||||
/// Lightness
|
||||
pub l: f32,
|
||||
/// Alpha (i.e. opacity. `0.0` means fully transparent, `1.0`
|
||||
/// nontransparent).
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
unsafe impl cxx::ExternType for ColorHSLA {
|
||||
type Id = cxx::type_id!("ColorHSLA");
|
||||
type Kind = cxx::kind::Trivial;
|
||||
}
|
19
src/base/lib.rs
Normal file
19
src/base/lib.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
//! DDNet's base library, Rust part.
|
||||
//!
|
||||
//! DDNet's code base is separated into three major parts, `base`, `engine` and
|
||||
//! `game`.
|
||||
//!
|
||||
//! The base library consists of operating system abstractions, and
|
||||
//! game-independent data structures such as color handling and math vectors.
|
||||
//! Additionally, it contains some types to support the C++-Rust-translation.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate ddnet_test;
|
||||
|
||||
mod color;
|
||||
mod rust;
|
||||
|
||||
pub use color::*;
|
||||
pub use rust::*;
|
5
src/base/rust.h
Normal file
5
src/base/rust.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#ifndef BASE_RUST_H
|
||||
#define BASE_RUST_H
|
||||
typedef const char *StrRef;
|
||||
typedef void *UserPtr;
|
||||
#endif // BASE_RUST_H
|
268
src/base/rust.rs
Normal file
268
src/base/rust.rs
Normal file
|
@ -0,0 +1,268 @@
|
|||
use std::cmp;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
|
||||
/// User pointer, as used in callbacks. Corresponds to the C++ type `void *`.
|
||||
///
|
||||
/// Callbacks in C are usually represented by a function pointer and some
|
||||
/// "userdata" pointer that is also passed to the function pointer. This allows
|
||||
/// to hand data to the callback. This type represents such a userdata poiner.
|
||||
///
|
||||
/// It is `unsafe` to convert the `UserPtr` back to its original pointer using
|
||||
/// [`UserPtr::cast`] because its lifetime and type information was lost.
|
||||
///
|
||||
/// When dealing with Rust code exclusively, closures are preferred.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ddnet_base::UserPtr;
|
||||
///
|
||||
/// struct CallbackData {
|
||||
/// favorite_color: &'static str,
|
||||
/// }
|
||||
///
|
||||
/// let data = CallbackData {
|
||||
/// favorite_color: "green",
|
||||
/// };
|
||||
///
|
||||
/// callback(UserPtr::from(&data));
|
||||
///
|
||||
/// fn callback(pointer: UserPtr) {
|
||||
/// let data: &CallbackData = unsafe { pointer.cast() };
|
||||
/// println!("favorite color: {}", data.favorite_color);
|
||||
/// }
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct UserPtr(*mut ());
|
||||
|
||||
unsafe impl cxx::ExternType for UserPtr {
|
||||
type Id = cxx::type_id!("UserPtr");
|
||||
type Kind = cxx::kind::Trivial;
|
||||
}
|
||||
|
||||
impl UserPtr {
|
||||
/// Create a null `UserPtr`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ddnet_base::UserPtr;
|
||||
///
|
||||
/// // Can't do anything useful with this.
|
||||
/// let _user = UserPtr::null();
|
||||
/// ```
|
||||
pub fn null() -> UserPtr {
|
||||
UserPtr(ptr::null_mut())
|
||||
}
|
||||
|
||||
/// Cast `UserPtr` back to a reference to its real type.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller is responsible for checking type and lifetime correctness.
|
||||
/// Also, they must make sure that there are only immutable references or at
|
||||
/// most one mutable reference live at the same time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ddnet_base::UserPtr;
|
||||
///
|
||||
/// let the_answer = 42;
|
||||
/// let user = UserPtr::from(&the_answer);
|
||||
///
|
||||
/// assert_eq!(unsafe { *user.cast::<i32>() }, 42);
|
||||
/// ```
|
||||
pub unsafe fn cast<T>(&self) -> &T {
|
||||
&*(self.0 as *const _)
|
||||
}
|
||||
|
||||
/// Cast `UserPtr` back to a mutable reference to its real type.
|
||||
///
|
||||
/// See [`UserPtr`] documentation for details and an example.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller is responsible for checking type and lifetime correctness.
|
||||
/// Also, they must make sure that there are only immutable references or at
|
||||
/// most one mutable reference live at the same time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ddnet_base::UserPtr;
|
||||
///
|
||||
/// let mut seen_it = false;
|
||||
/// let mut user = UserPtr::from(&mut seen_it);
|
||||
///
|
||||
/// unsafe {
|
||||
/// *user.cast_mut() = true;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(seen_it, true);
|
||||
/// ```
|
||||
pub unsafe fn cast_mut<T>(&mut self) -> &mut T {
|
||||
&mut *(self.0 as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<&'a T> for UserPtr {
|
||||
fn from(t: &'a T) -> UserPtr {
|
||||
UserPtr(t as *const _ as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<&'a mut T> for UserPtr {
|
||||
fn from(t: &'a mut T) -> UserPtr {
|
||||
UserPtr(t as *mut _ as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// C-style string pointer to UTF-8 data. Corresponds to the C++ type `const
|
||||
/// char *`.
|
||||
///
|
||||
/// The lifetime is the lifetime of the underlying string.
|
||||
///
|
||||
/// This is a separate type from [`std::ffi::CStr`] because that type is not
|
||||
/// FFI-safe and does not guarantee UTF-8.
|
||||
///
|
||||
/// In Rust code, [`String`] is preferred. For constructing C strings,
|
||||
/// [`std::ffi::CString`] or this crate's [`s!`](`crate::s!`) macro can be used.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn some_c_function(_: StrRef<'_>) {}
|
||||
/// use ddnet_base::StrRef;
|
||||
/// use ddnet_base::s;
|
||||
/// use std::ffi::CStr;
|
||||
/// use std::ffi::CString;
|
||||
/// use std::process;
|
||||
///
|
||||
/// some_c_function(CStr::from_bytes_with_nul(b"Hello!\0").unwrap().into());
|
||||
///
|
||||
/// let string = CString::new(format!("Current PID is {}.", process::id())).unwrap();
|
||||
/// some_c_function(string.as_ref().into());
|
||||
///
|
||||
/// fn c_function_wrapper(s: &CStr) {
|
||||
/// some_c_function(s.into());
|
||||
/// }
|
||||
///
|
||||
/// some_c_function(s!("こんにちはC言語"));
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
#[derive(Eq)]
|
||||
pub struct StrRef<'a>(*const c_char, PhantomData<&'a ()>);
|
||||
|
||||
unsafe impl<'a> cxx::ExternType for StrRef<'a> {
|
||||
type Id = cxx::type_id!("StrRef");
|
||||
type Kind = cxx::kind::Trivial;
|
||||
}
|
||||
|
||||
impl<'a> StrRef<'a> {
|
||||
/// Get the wrapped string reference.
|
||||
///
|
||||
/// This does the same as the `Deref` implementation, differing only in the
|
||||
/// returned lifetime. `Deref`'s return type is bound by `self`'s lifetime,
|
||||
/// this returns the more correct and longer lifetime.
|
||||
///
|
||||
/// This is an O(n) operation as it needs to calculate the length of a C
|
||||
/// string by finding the first NUL byte.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ddnet_base::s;
|
||||
///
|
||||
/// let str1: &'static str = s!("static string").to_str();
|
||||
/// ```
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// use ddnet_base::s;
|
||||
///
|
||||
/// // Wrong lifetime.
|
||||
/// let str2: &'static str = &*s!("another static string");
|
||||
/// ```
|
||||
///
|
||||
pub fn to_str(&self) -> &'a str {
|
||||
unsafe { str::from_utf8_unchecked(CStr::from_ptr(self.0).to_bytes()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a CStr> for StrRef<'a> {
|
||||
fn from(s: &'a CStr) -> StrRef<'a> {
|
||||
let bytes = s.to_bytes_with_nul();
|
||||
str::from_utf8(bytes).expect("valid UTF-8");
|
||||
StrRef(bytes.as_ptr() as *const _, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for StrRef<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.to_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for StrRef<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.to_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> cmp::PartialEq for StrRef<'a> {
|
||||
fn eq(&self, other: &StrRef<'a>) -> bool {
|
||||
self.to_str().eq(other.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> cmp::PartialEq<&'a str> for StrRef<'a> {
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
self.to_str().eq(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> cmp::PartialOrd for StrRef<'a> {
|
||||
fn partial_cmp(&self, other: &StrRef<'a>) -> Option<cmp::Ordering> {
|
||||
self.to_str().partial_cmp(other.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> cmp::Ord for StrRef<'a> {
|
||||
fn cmp(&self, other: &StrRef<'a>) -> cmp::Ordering {
|
||||
self.to_str().cmp(other.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ops::Deref for StrRef<'a> {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &str {
|
||||
self.to_str()
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a [`StrRef`] statically.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ddnet_base::StrRef;
|
||||
/// use ddnet_base::s;
|
||||
///
|
||||
/// let greeting: StrRef<'static> = s!("Hallöchen, C!");
|
||||
/// let status: StrRef<'static> = s!(concat!("Current file: ", file!()));
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! s {
|
||||
($str:expr) => {
|
||||
::ddnet_base::StrRef::from(
|
||||
::std::ffi::CStr::from_bytes_with_nul(::std::concat!($str, "\0").as_bytes()).unwrap(),
|
||||
)
|
||||
};
|
||||
}
|
18
src/engine/Cargo.toml
Normal file
18
src/engine/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "ddnet-engine"
|
||||
version = "0.0.1"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
license = "Zlib"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
ddnet-base = { path = "../base" }
|
||||
|
||||
cxx = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ddnet-engine-shared = { path = "shared" }
|
||||
ddnet-test = { path = "../rust-bridge/test", features = ["link-test-libraries"] }
|
|
@ -46,6 +46,7 @@
|
|||
#include <engine/shared/packer.h>
|
||||
#include <engine/shared/protocol.h>
|
||||
#include <engine/shared/protocol_ex.h>
|
||||
#include <engine/shared/rust_version.h>
|
||||
#include <engine/shared/snapshot.h>
|
||||
#include <engine/shared/uuid_manager.h>
|
||||
|
||||
|
@ -4448,9 +4449,11 @@ void CClient::RegisterCommands()
|
|||
m_pConsole->Register("demo_play", "", CFGFLAG_CLIENT, Con_DemoPlay, this, "Play demo");
|
||||
m_pConsole->Register("demo_speed", "i[speed]", CFGFLAG_CLIENT, Con_DemoSpeed, this, "Set demo speed");
|
||||
|
||||
m_pConsole->Register("save_replay", "?i[length] ?s[filename]", CFGFLAG_CLIENT, Con_SaveReplay, this, "Save a replay of the last defined amount of seconds");
|
||||
m_pConsole->Register("save_replay", "?i[length] s[filename]", CFGFLAG_CLIENT, Con_SaveReplay, this, "Save a replay of the last defined amount of seconds");
|
||||
m_pConsole->Register("benchmark_quit", "i[seconds] r[file]", CFGFLAG_CLIENT | CFGFLAG_STORE, Con_BenchmarkQuit, this, "Benchmark frame times for number of seconds to file, then quit");
|
||||
|
||||
RustVersionRegister(*m_pConsole);
|
||||
|
||||
m_pConsole->Chain("cl_timeout_seed", ConchainTimeoutSeed, this);
|
||||
m_pConsole->Chain("cl_replays", ConchainReplays, this);
|
||||
|
||||
|
@ -4607,7 +4610,7 @@ int main(int argc, const char **argv)
|
|||
|
||||
// create the components
|
||||
IEngine *pEngine = CreateEngine(GAME_NAME, pFutureConsoleLogger, 2);
|
||||
IConsole *pConsole = CreateConsole(CFGFLAG_CLIENT);
|
||||
IConsole *pConsole = CreateConsole(CFGFLAG_CLIENT).release();
|
||||
IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_CLIENT, argc, (const char **)argv);
|
||||
IConfigManager *pConfigManager = CreateConfigManager();
|
||||
IEngineSound *pEngineSound = CreateEngineSound();
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <base/color.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
static const ColorRGBA gs_ConsoleDefaultColor(1, 1, 1, 1);
|
||||
|
||||
enum LEVEL : char;
|
||||
|
@ -48,10 +50,10 @@ public:
|
|||
IResult() { m_NumArgs = 0; }
|
||||
virtual ~IResult() {}
|
||||
|
||||
virtual int GetInteger(unsigned Index) = 0;
|
||||
virtual float GetFloat(unsigned Index) = 0;
|
||||
virtual const char *GetString(unsigned Index) = 0;
|
||||
virtual ColorHSLA GetColor(unsigned Index, bool Light) = 0;
|
||||
virtual int GetInteger(unsigned Index) const = 0;
|
||||
virtual float GetFloat(unsigned Index) const = 0;
|
||||
virtual const char *GetString(unsigned Index) const = 0;
|
||||
virtual ColorHSLA GetColor(unsigned Index, bool Light) const = 0;
|
||||
|
||||
virtual void RemoveArgument(unsigned Index) = 0;
|
||||
|
||||
|
@ -60,7 +62,7 @@ public:
|
|||
|
||||
// DDRace
|
||||
|
||||
virtual int GetVictim() = 0;
|
||||
virtual int GetVictim() const = 0;
|
||||
};
|
||||
|
||||
class CCommandInfo
|
||||
|
@ -110,7 +112,7 @@ public:
|
|||
virtual void ExecuteFile(const char *pFilename, int ClientID = -1, bool LogFailure = false, int StorageType = IStorage::TYPE_ALL) = 0;
|
||||
|
||||
virtual char *Format(char *pBuf, int Size, const char *pFrom, const char *pStr) = 0;
|
||||
virtual void Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor = gs_ConsoleDefaultColor) = 0;
|
||||
virtual void Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor = gs_ConsoleDefaultColor) const = 0;
|
||||
virtual void SetTeeHistorianCommandCallback(FTeeHistorianCommandCallback pfnCallback, void *pUser) = 0;
|
||||
virtual void SetUnknownCommandCallback(FUnknownCommandCallback pfnCallback, void *pUser) = 0;
|
||||
virtual void InitChecksum(CChecksumData *pData) const = 0;
|
||||
|
@ -127,6 +129,6 @@ public:
|
|||
virtual void SetFlagMask(int FlagMask) = 0;
|
||||
};
|
||||
|
||||
extern IConsole *CreateConsole(int FlagMask);
|
||||
std::unique_ptr<IConsole> CreateConsole(int FlagMask);
|
||||
|
||||
#endif // FILE_ENGINE_CONSOLE_H
|
||||
|
|
544
src/engine/console.rs
Normal file
544
src/engine/console.rs
Normal file
|
@ -0,0 +1,544 @@
|
|||
use ddnet_base::ColorRGBA;
|
||||
use ddnet_base::UserPtr;
|
||||
|
||||
pub use self::ffi::CreateConsole;
|
||||
pub use self::ffi::IConsole;
|
||||
pub use self::ffi::IConsole_IResult;
|
||||
|
||||
/// Command callback for `IConsole`.
|
||||
///
|
||||
/// See [`IConsole::Register`] for an example.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(transparent)]
|
||||
pub struct IConsole_FCommandCallback(pub extern "C" fn(result: &IConsole_IResult, user: UserPtr));
|
||||
|
||||
unsafe impl cxx::ExternType for IConsole_FCommandCallback {
|
||||
type Id = cxx::type_id!("IConsole_FCommandCallback");
|
||||
type Kind = cxx::kind::Trivial;
|
||||
}
|
||||
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("base/rust.h");
|
||||
include!("engine/console.h");
|
||||
include!("engine/rust.h");
|
||||
|
||||
type ColorRGBA = ddnet_base::ColorRGBA;
|
||||
type ColorHSLA = ddnet_base::ColorHSLA;
|
||||
type StrRef<'a> = ddnet_base::StrRef<'a>;
|
||||
type UserPtr = ddnet_base::UserPtr;
|
||||
type IConsole_FCommandCallback = super::IConsole_FCommandCallback;
|
||||
|
||||
/// Represents the arguments to a console command for [`IConsole`].
|
||||
///
|
||||
/// You can only obtain this type in the command callback
|
||||
/// [`IConsole_FCommandCallback`] specified in [`IConsole::Register`].
|
||||
type IConsole_IResult;
|
||||
|
||||
/// Get the n-th parameter of the command as an integer.
|
||||
///
|
||||
/// If the index is out of range, this returns 0. If the parameter
|
||||
/// cannot be parsed as an integer, this also returns 0.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ddnet_test;
|
||||
/// # use ddnet_base::UserPtr;
|
||||
/// # use ddnet_base::s;
|
||||
/// # use ddnet_engine::CreateConsole;
|
||||
/// # use ddnet_engine::IConsole;
|
||||
/// # use ddnet_engine::IConsole_FCommandCallback;
|
||||
/// # use ddnet_engine::IConsole_IResult;
|
||||
/// # use ddnet_engine_shared::CFGFLAG_SERVER;
|
||||
/// #
|
||||
/// # let mut console = CreateConsole(CFGFLAG_SERVER);
|
||||
/// # let mut executed = false;
|
||||
/// # console.pin_mut().Register(s!("command"), s!("sss"), CFGFLAG_SERVER, IConsole_FCommandCallback(callback), UserPtr::from(&mut executed), s!(""));
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command "1337" abc -7331def"#), -1, true);
|
||||
/// # extern "C" fn callback(result_param: &IConsole_IResult, mut user: UserPtr) {
|
||||
/// # unsafe { *user.cast_mut::<bool>() = true; }
|
||||
/// let result: &IConsole_IResult /* = `command "1337" abc -7331def` */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetInteger(0), 1337);
|
||||
/// assert_eq!(result.GetInteger(1), 0); // unparsable
|
||||
/// assert_eq!(result.GetInteger(2), -7331); // parsable start
|
||||
/// assert_eq!(result.GetInteger(3), 0); // out of range
|
||||
/// # }
|
||||
/// # assert!(executed);
|
||||
/// ```
|
||||
pub fn GetInteger(self: &IConsole_IResult, Index: u32) -> i32;
|
||||
|
||||
/// Get the n-th parameter of the command as a floating point number.
|
||||
///
|
||||
/// If the index is out of range, this returns 0.0. If the parameter
|
||||
/// cannot be parsed as a floating point number, this also returns 0.0.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ddnet_test;
|
||||
/// # use ddnet_base::UserPtr;
|
||||
/// # use ddnet_base::s;
|
||||
/// # use ddnet_engine::CreateConsole;
|
||||
/// # use ddnet_engine::IConsole;
|
||||
/// # use ddnet_engine::IConsole_FCommandCallback;
|
||||
/// # use ddnet_engine::IConsole_IResult;
|
||||
/// # use ddnet_engine_shared::CFGFLAG_SERVER;
|
||||
/// #
|
||||
/// # let mut console = CreateConsole(CFGFLAG_SERVER);
|
||||
/// # let mut executed = false;
|
||||
/// # console.pin_mut().Register(s!("command"), s!("sss"), CFGFLAG_SERVER, IConsole_FCommandCallback(callback), UserPtr::from(&mut executed), s!(""));
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command "13.37" abc -73.31def"#), -1, true);
|
||||
/// # extern "C" fn callback(result_param: &IConsole_IResult, mut user: UserPtr) {
|
||||
/// # unsafe { *user.cast_mut::<bool>() = true; }
|
||||
/// let result: &IConsole_IResult /* = `command "13.37" abc -73.31def` */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetFloat(0), 13.37);
|
||||
/// assert_eq!(result.GetFloat(1), 0.0); // unparsable
|
||||
/// assert_eq!(result.GetFloat(2), -73.31); // parsable start
|
||||
/// assert_eq!(result.GetFloat(3), 0.0); // out of range
|
||||
/// # }
|
||||
/// # assert!(executed);
|
||||
/// ```
|
||||
pub fn GetFloat(self: &IConsole_IResult, Index: u32) -> f32;
|
||||
|
||||
/// Get the n-th parameter of the command as a string.
|
||||
///
|
||||
/// If the index is out of range, this returns the empty string `""`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ddnet_test;
|
||||
/// # use ddnet_base::UserPtr;
|
||||
/// # use ddnet_base::s;
|
||||
/// # use ddnet_engine::CreateConsole;
|
||||
/// # use ddnet_engine::IConsole;
|
||||
/// # use ddnet_engine::IConsole_FCommandCallback;
|
||||
/// # use ddnet_engine::IConsole_IResult;
|
||||
/// # use ddnet_engine_shared::CFGFLAG_SERVER;
|
||||
/// #
|
||||
/// # let mut console = CreateConsole(CFGFLAG_SERVER);
|
||||
/// # let mut executed = false;
|
||||
/// # console.pin_mut().Register(s!("command"), s!("sss"), CFGFLAG_SERVER, IConsole_FCommandCallback(callback), UserPtr::from(&mut executed), s!(""));
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command "I'm in space" '' "\"\\Escapes\?\"\n""#), -1, true);
|
||||
/// # extern "C" fn callback(result_param: &IConsole_IResult, mut user: UserPtr) {
|
||||
/// # unsafe { *user.cast_mut::<bool>() = true; }
|
||||
/// let result: &IConsole_IResult /* = `command "I'm in space" '' "\"\\Escapes\?\"\n"` */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetString(0), "I'm in space");
|
||||
/// assert_eq!(result.GetString(1), "''");
|
||||
/// assert_eq!(result.GetString(2), r#""\Escapes\?"\n"#); // only \\ and \" escapes
|
||||
/// assert_eq!(result.GetString(3), ""); // out of range
|
||||
/// # }
|
||||
/// # assert!(executed);
|
||||
/// ```
|
||||
pub fn GetString(self: &IConsole_IResult, Index: u32) -> StrRef<'_>;
|
||||
|
||||
/// Get the n-th parameter of the command as a color.
|
||||
///
|
||||
/// If the index is out of range, this returns black. If the parameter
|
||||
/// cannot be parsed as a color, this also returns black.
|
||||
///
|
||||
/// It supports the following formats:
|
||||
/// - `$XXX` (RGB, e.g. `$f00` for red)
|
||||
/// - `$XXXXXX` (RGB, e.g. `$ffa500` for DDNet's logo color)
|
||||
/// - base 10 integers (24/32 bit HSL in base 10, e.g. `0` for black)
|
||||
/// - the following color names: `red`, `yellow`, `green`, `cyan`,
|
||||
/// `blue`, `magenta`, `white`, `gray`, `black`.
|
||||
///
|
||||
/// The `Light` parameter influences the interpretation of base 10
|
||||
/// integers, if it is set, the lightness channel is divided by 2 and
|
||||
/// 0.5 is added, making 0.5 the darkest and 1.0 the lightest possible
|
||||
/// value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ddnet_test;
|
||||
/// # use ddnet_base::ColorHSLA;
|
||||
/// # use ddnet_base::UserPtr;
|
||||
/// # use ddnet_base::s;
|
||||
/// # use ddnet_engine::CreateConsole;
|
||||
/// # use ddnet_engine::IConsole;
|
||||
/// # use ddnet_engine::IConsole_FCommandCallback;
|
||||
/// # use ddnet_engine::IConsole_IResult;
|
||||
/// # use ddnet_engine_shared::CFGFLAG_SERVER;
|
||||
/// #
|
||||
/// # let mut console = CreateConsole(CFGFLAG_SERVER);
|
||||
/// # let mut executed = false;
|
||||
/// # console.pin_mut().Register(s!("command"), s!("ssssss"), CFGFLAG_SERVER, IConsole_FCommandCallback(callback), UserPtr::from(&mut executed), s!(""));
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command "$f00" $ffa500 $1234 shiny cyan -16777216"#), -1, true);
|
||||
/// # extern "C" fn callback(result_param: &IConsole_IResult, mut user: UserPtr) {
|
||||
/// # unsafe { *user.cast_mut::<bool>() = true; }
|
||||
/// let result: &IConsole_IResult /* = `command "$f00" $ffa500 $1234 shiny cyan -16777216` */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetColor(0, false), ColorHSLA { h: 0.0, s: 1.0, l: 0.5, a: 1.0 }); // red
|
||||
/// assert_eq!(result.GetColor(1, false), ColorHSLA { h: 0.10784314, s: 1.0, l: 0.5, a: 1.0 }); // DDNet logo color
|
||||
/// assert_eq!(result.GetColor(2, false), ColorHSLA { h: 0.0, s: 0.0, l: 0.0, a: 1.0 }); // cannot be parsed => black
|
||||
/// assert_eq!(result.GetColor(3, false), ColorHSLA { h: 0.0, s: 0.0, l: 0.0, a: 1.0 }); // unknown color name => black
|
||||
/// assert_eq!(result.GetColor(4, false), ColorHSLA { h: 0.5, s: 1.0, l: 0.5, a: 1.0 }); // cyan
|
||||
/// assert_eq!(result.GetColor(5, false), ColorHSLA { h: 0.0, s: 0.0, l: 0.0, a: 1.0 }); // black
|
||||
/// assert_eq!(result.GetColor(6, false), ColorHSLA { h: 0.0, s: 0.0, l: 0.0, a: 1.0 }); // out of range => black
|
||||
///
|
||||
/// assert_eq!(result.GetColor(0, true), result.GetColor(0, false));
|
||||
/// assert_eq!(result.GetColor(1, true), result.GetColor(1, false));
|
||||
/// assert_eq!(result.GetColor(2, true), result.GetColor(2, false));
|
||||
/// assert_eq!(result.GetColor(3, true), result.GetColor(3, false));
|
||||
/// assert_eq!(result.GetColor(4, true), result.GetColor(4, false));
|
||||
/// assert_eq!(result.GetColor(5, true), ColorHSLA { h: 0.0, s: 0.0, l: 0.5, a: 1.0 }); // black, but has the `Light` parameter set
|
||||
/// assert_eq!(result.GetColor(6, true), result.GetColor(6, false));
|
||||
/// # }
|
||||
/// # assert!(executed);
|
||||
/// ```
|
||||
pub fn GetColor(self: &IConsole_IResult, Index: u32, Light: bool) -> ColorHSLA;
|
||||
|
||||
/// Get the number of parameters passed.
|
||||
///
|
||||
/// This is mostly important for commands that have optional parameters
|
||||
/// (`?`) and thus support variable numbers of arguments.
|
||||
///
|
||||
/// See [`IConsole::Register`] for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ddnet_test;
|
||||
/// # use ddnet_base::ColorHSLA;
|
||||
/// # use ddnet_base::UserPtr;
|
||||
/// # use ddnet_base::s;
|
||||
/// # use ddnet_engine::CreateConsole;
|
||||
/// # use ddnet_engine::IConsole;
|
||||
/// # use ddnet_engine::IConsole_FCommandCallback;
|
||||
/// # use ddnet_engine::IConsole_IResult;
|
||||
/// # use ddnet_engine_shared::CFGFLAG_SERVER;
|
||||
/// #
|
||||
/// # let mut console = CreateConsole(CFGFLAG_SERVER);
|
||||
/// # let mut executed: u32 = 0;
|
||||
/// # console.pin_mut().Register(s!("command"), s!("s?ss"), CFGFLAG_SERVER, IConsole_FCommandCallback(callback), UserPtr::from(&mut executed), s!(""));
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command one two three"#), -1, true);
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command "one two" "three four""#), -1, true);
|
||||
/// # extern "C" fn callback(result_param: &IConsole_IResult, mut user: UserPtr) {
|
||||
/// # let executed;
|
||||
/// # unsafe { executed = *user.cast_mut::<u32>(); *user.cast_mut::<u32>() += 1; }
|
||||
/// # if executed == 0 {
|
||||
/// let result: &IConsole_IResult /* = `command one two three` */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.NumArguments(), 3);
|
||||
///
|
||||
/// # } else if executed == 1 {
|
||||
/// let result: &IConsole_IResult /* = `command "one two" "three four"` */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.NumArguments(), 2);
|
||||
/// # }
|
||||
/// # }
|
||||
/// # assert!(executed == 2);
|
||||
/// ```
|
||||
pub fn NumArguments(self: &IConsole_IResult) -> i32;
|
||||
|
||||
/// Get the value of the sole victim (`v`) parameter.
|
||||
///
|
||||
/// This is mostly important for commands that have optional parameters
|
||||
/// and thus support variable numbers of arguments.
|
||||
///
|
||||
/// See [`IConsole::Register`] for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ddnet_test;
|
||||
/// # use ddnet_base::UserPtr;
|
||||
/// # use ddnet_base::s;
|
||||
/// # use ddnet_engine::CreateConsole;
|
||||
/// # use ddnet_engine::IConsole;
|
||||
/// # use ddnet_engine::IConsole_FCommandCallback;
|
||||
/// # use ddnet_engine::IConsole_IResult;
|
||||
/// # use ddnet_engine_shared::CFGFLAG_SERVER;
|
||||
/// #
|
||||
/// # let mut console = CreateConsole(CFGFLAG_SERVER);
|
||||
/// # let mut executed: u32 = 0;
|
||||
/// # console.pin_mut().Register(s!("command"), s!("v"), CFGFLAG_SERVER, IConsole_FCommandCallback(callback), UserPtr::from(&mut executed), s!(""));
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command me"#), 33, true);
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command string"#), 33, true);
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command 42"#), 33, true);
|
||||
/// # console.pin_mut().ExecuteLine(s!(r#"command all"#), 33, true);
|
||||
/// # extern "C" fn callback(result_param: &IConsole_IResult, mut user: UserPtr) {
|
||||
/// # let executed;
|
||||
/// # unsafe { executed = *user.cast_mut::<u32>(); *user.cast_mut::<u32>() += 1; }
|
||||
/// # match executed {
|
||||
/// # 0 => {
|
||||
/// let result: &IConsole_IResult /* = `command me` */;
|
||||
/// # result = result_param;
|
||||
/// // Assume the executing client ID is 33.
|
||||
/// assert_eq!(result.GetVictim(), 33);
|
||||
///
|
||||
/// # }
|
||||
/// # 1 => {
|
||||
/// let result: &IConsole_IResult /* = `command string` */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetVictim(), 0);
|
||||
///
|
||||
/// # }
|
||||
/// # 2 => {
|
||||
/// let result: &IConsole_IResult /* = `command 42` */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetVictim(), 42);
|
||||
///
|
||||
/// # }
|
||||
/// # 3 => {
|
||||
/// let result: &IConsole_IResult /* = `command all` first invocation */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetVictim(), 0);
|
||||
/// # }
|
||||
/// # 4 => {
|
||||
/// let result: &IConsole_IResult /* = `command all` second invocation */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetVictim(), 1);
|
||||
/// # }
|
||||
/// // …
|
||||
/// # 66 => {
|
||||
/// let result: &IConsole_IResult /* = `command all` last invocation */;
|
||||
/// # result = result_param;
|
||||
/// assert_eq!(result.GetVictim(), 63);
|
||||
/// # }
|
||||
/// # _ => {}
|
||||
/// # }
|
||||
/// # }
|
||||
/// # assert!(executed == 67);
|
||||
/// ```
|
||||
pub fn GetVictim(self: &IConsole_IResult) -> i32;
|
||||
|
||||
/// Console interface, consists of logging output and command execution.
|
||||
///
|
||||
/// This is used for the local console in the client and the remote
|
||||
/// console of the server. It handles commands, their inputs and
|
||||
/// outputs, command completion and also command and log output.
|
||||
///
|
||||
/// Call [`CreateConsole`] to obtain an instance.
|
||||
type IConsole;
|
||||
|
||||
/// Execute a command in the console.
|
||||
///
|
||||
/// Commands can be separated by semicolons (`;`), everything after a
|
||||
/// hash sign (`#`) is treated as a comment and discarded. Parameters
|
||||
/// are separated by spaces (` `). By quoting arguments with double
|
||||
/// quotes (`"`), the special meaning of the other characters can be
|
||||
/// disabled. Double quotes can be escaped as `\"` and backslashes can
|
||||
/// be escaped as `\\` inside double quotes.
|
||||
///
|
||||
/// When `InterpretSemicolons` is `false`, semicolons are not
|
||||
/// interpreted unless the command starts with `mc;`.
|
||||
///
|
||||
/// The `ClientID` parameter defaults to -1, `InterpretSemicolons` to
|
||||
/// `true` in C++.
|
||||
pub fn ExecuteLine(
|
||||
self: Pin<&mut IConsole>,
|
||||
pStr: StrRef<'_>,
|
||||
ClientID: i32,
|
||||
InterpretSemicolons: bool,
|
||||
);
|
||||
|
||||
/// Log a message.
|
||||
///
|
||||
/// `Level` is one of
|
||||
/// - [`IConsole_OUTPUT_LEVEL_STANDARD`](constant.IConsole_OUTPUT_LEVEL_STANDARD.html)
|
||||
/// - [`IConsole_OUTPUT_LEVEL_ADDINFO`](constant.IConsole_OUTPUT_LEVEL_ADDINFO.html)
|
||||
/// - [`IConsole_OUTPUT_LEVEL_DEBUG`](constant.IConsole_OUTPUT_LEVEL_DEBUG.html)
|
||||
///
|
||||
/// `pFrom` is some sort of module name, e.g. for code in the console,
|
||||
/// it is usually "console". Other examples include "client",
|
||||
/// "client/network", …
|
||||
///
|
||||
/// `pStr` is the actual log message.
|
||||
///
|
||||
/// `PrintColor` is the intended log color.
|
||||
///
|
||||
/// `PrintColor` defaults to [`gs_ConsoleDefaultColor`] in C++.
|
||||
pub fn Print(
|
||||
self: &IConsole,
|
||||
Level: i32,
|
||||
pFrom: StrRef<'_>,
|
||||
pStr: StrRef<'_>,
|
||||
PrintColor: ColorRGBA,
|
||||
);
|
||||
|
||||
/// Register a command.
|
||||
///
|
||||
/// This function needs a command name, a parameter shortcode, some
|
||||
/// flags that specify metadata about the command, a callback function
|
||||
/// with associated `UserPtr` and a help string.
|
||||
///
|
||||
/// `Flags` must be a combination of
|
||||
/// - [`CFGFLAG_SAVE`](../ddnet_engine_shared/constant.CFGFLAG_SAVE.html)
|
||||
/// - [`CFGFLAG_CLIENT`](../ddnet_engine_shared/constant.CFGFLAG_CLIENT.html)
|
||||
/// - [`CFGFLAG_SERVER`](../ddnet_engine_shared/constant.CFGFLAG_SERVER.html)
|
||||
/// - [`CFGFLAG_STORE`](../ddnet_engine_shared/constant.CFGFLAG_STORE.html)
|
||||
/// - [`CFGFLAG_MASTER`](../ddnet_engine_shared/constant.CFGFLAG_MASTER.html)
|
||||
/// - [`CFGFLAG_ECON`](../ddnet_engine_shared/constant.CFGFLAG_ECON.html)
|
||||
/// - [`CMDFLAG_TEST`](../ddnet_engine_shared/constant.CMDFLAG_TEST.html)
|
||||
/// - [`CFGFLAG_CHAT`](../ddnet_engine_shared/constant.CFGFLAG_CHAT.html)
|
||||
/// - [`CFGFLAG_GAME`](../ddnet_engine_shared/constant.CFGFLAG_GAME.html)
|
||||
/// - [`CFGFLAG_NONTEEHISTORIC`](../ddnet_engine_shared/constant.CFGFLAG_NONTEEHISTORIC.html)
|
||||
/// - [`CFGFLAG_COLLIGHT`](../ddnet_engine_shared/constant.CFGFLAG_COLLIGHT.html)
|
||||
/// - [`CFGFLAG_INSENSITIVE`](../ddnet_engine_shared/constant.CFGFLAG_INSENSITIVE.html)
|
||||
///
|
||||
/// # Parameter shortcode
|
||||
///
|
||||
/// The following parameter types are supported:
|
||||
///
|
||||
/// - `i`, `f`, `s`: **I**nteger, **f**loat, and **s**tring parameter,
|
||||
/// respectively. Since they're not type-checked, they're all the
|
||||
/// same, one word delimited by whitespace or any quoted string.
|
||||
/// Examples: `12`, `example`, `"Hello, World!"`.
|
||||
/// - `r`: **R**est of the command line, possibly multiple word, until
|
||||
/// the next command or the end of line. Alternatively one quoted
|
||||
/// parameter. Examples: `multiple words even without quotes`,
|
||||
/// `"quotes are fine, too"`.
|
||||
/// - `v`: **V**ictim client ID for this command. Supports the special
|
||||
/// parameters `me` for the executing client ID, and `all` to target
|
||||
/// all players. Examples: `0`, `63`, `words` (gets parsed as `0`),
|
||||
/// `me`, `all`.
|
||||
///
|
||||
/// The parameter shortcode is now a string consisting of any number of
|
||||
/// these parameter type characters, a question mark `?` to mark the
|
||||
/// beginning of optional parameters and `[<string>]` help strings after
|
||||
/// each parameter that can have a short explanation.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// - `echo` has `r[text]`: Takes the whole command line as the first
|
||||
/// parameter. This means we get the following arguments:
|
||||
/// ```text
|
||||
/// ## "1"
|
||||
/// echo 1
|
||||
///
|
||||
/// ## "multiple words"
|
||||
/// echo multiple words
|
||||
///
|
||||
/// ## error: missing argument
|
||||
/// echo
|
||||
///
|
||||
/// ## "multiple"
|
||||
/// echo "multiple" "quoted" "arguments"
|
||||
///
|
||||
/// ## "comments"
|
||||
/// echo comments # work too
|
||||
///
|
||||
/// ## "multiple"; "commands"
|
||||
/// echo multiple; echo commands
|
||||
/// ```
|
||||
/// - `muteid` has `v[id] i[seconds] ?r[reason]`. Assume the local
|
||||
/// player has client ID 33, then we get the following
|
||||
/// arguments:
|
||||
/// ```text
|
||||
/// ## "33" "60"
|
||||
/// muteid me 60
|
||||
///
|
||||
/// ## "12" "120" "You're a wonderful person"
|
||||
/// muteid 12 120 You're a wonderful person
|
||||
///
|
||||
/// ## "0" "180"; "1" "180"; …, "63" "180"
|
||||
/// muteid all 180
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ddnet_test;
|
||||
/// use ddnet_base::UserPtr;
|
||||
/// use ddnet_base::s;
|
||||
/// use ddnet_engine::CreateConsole;
|
||||
/// use ddnet_engine::IConsole;
|
||||
/// use ddnet_engine::IConsole_FCommandCallback;
|
||||
/// use ddnet_engine::IConsole_IResult;
|
||||
/// use ddnet_engine::IConsole_OUTPUT_LEVEL_STANDARD;
|
||||
/// use ddnet_engine::gs_ConsoleDefaultColor;
|
||||
/// use ddnet_engine_shared::CFGFLAG_SERVER;
|
||||
///
|
||||
/// extern "C" fn print_callback(_: &IConsole_IResult, user: UserPtr) {
|
||||
/// let console: &IConsole = unsafe { user.cast() };
|
||||
/// console.Print(IConsole_OUTPUT_LEVEL_STANDARD, s!("example"), s!("print callback"), gs_ConsoleDefaultColor);
|
||||
/// }
|
||||
///
|
||||
/// let mut console = CreateConsole(CFGFLAG_SERVER);
|
||||
/// let user = (&*console).into();
|
||||
/// console.pin_mut().Register(s!("example"), s!(""), CFGFLAG_SERVER, IConsole_FCommandCallback(print_callback), user, s!("Example command outputting a single line into the console"));
|
||||
/// console.pin_mut().ExecuteLine(s!("example"), -1, true);
|
||||
/// ```
|
||||
pub fn Register(
|
||||
self: Pin<&mut IConsole>,
|
||||
pName: StrRef<'_>,
|
||||
pParams: StrRef<'_>,
|
||||
Flags: i32,
|
||||
pfnFunc: IConsole_FCommandCallback,
|
||||
pUser: UserPtr,
|
||||
pHelp: StrRef<'_>,
|
||||
);
|
||||
|
||||
/// Create a console instance.
|
||||
///
|
||||
/// It comes with a few registered commands by default. Only the
|
||||
/// commands sharing a bit with the `FlagMask` parameter are considered
|
||||
/// when executing commands.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ddnet_test;
|
||||
/// use ddnet_base::s;
|
||||
/// use ddnet_engine::CreateConsole;
|
||||
/// use ddnet_engine_shared::CFGFLAG_SERVER;
|
||||
///
|
||||
/// let mut console = CreateConsole(CFGFLAG_SERVER);
|
||||
/// console.pin_mut().ExecuteLine(s!("cmdlist; echo hi!"), -1, true);
|
||||
/// ```
|
||||
pub fn CreateConsole(FlagMask: i32) -> UniquePtr<IConsole>;
|
||||
}
|
||||
}
|
||||
|
||||
/// Default console output color. White.
|
||||
///
|
||||
/// Corresponds to the `gs_ConsoleDefaultColor` constant of the same name in
|
||||
/// C++.
|
||||
///
|
||||
/// Used as a last parameter in [`IConsole::Print`].
|
||||
///
|
||||
/// It is treated as "no color" in the console code, meaning that the default
|
||||
/// color of the output medium isn't overriden.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const gs_ConsoleDefaultColor: ColorRGBA = ColorRGBA {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
/// Default console output level.
|
||||
///
|
||||
/// Corresponds to the `IConsole::OUTPUT_LEVEL_STANDARD` enum value in C++.
|
||||
///
|
||||
/// Used as a first parameter in [`IConsole::Print`].
|
||||
///
|
||||
/// This is the only level that is shown by default.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const IConsole_OUTPUT_LEVEL_STANDARD: i32 = 0;
|
||||
/// First more verbose console output level.
|
||||
///
|
||||
/// Corresponds to the `IConsole::OUTPUT_LEVEL_ADDINFO` enum value in C++.
|
||||
///
|
||||
/// Used as a first parameter in [`IConsole::Print`].
|
||||
///
|
||||
/// This output level is not shown by default.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const IConsole_OUTPUT_LEVEL_ADDINFO: i32 = 1;
|
||||
/// Most verbose console output level.
|
||||
///
|
||||
/// Corresponds to the `IConsole::OUTPUT_LEVEL_DEBUG` enum value in C++.
|
||||
///
|
||||
/// Used as a first parameter in [`IConsole::Print`].
|
||||
///
|
||||
/// This output level is not shown by default.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const IConsole_OUTPUT_LEVEL_DEBUG: i32 = 2;
|
19
src/engine/lib.rs
Normal file
19
src/engine/lib.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
//! DDNet's engine interfaces, Rust part.
|
||||
//!
|
||||
//! DDNet's code base is separated into three major parts, `base`, `engine` and
|
||||
//! `game`.
|
||||
//!
|
||||
//! The engine consists of game-independent code such as display setup,
|
||||
//! low-level network protocol, low-level map format, etc.
|
||||
//!
|
||||
//! This crate in particular corresponds to the `src/engine` directory that only
|
||||
//! contains interfaces used by `engine` and `game` code.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate ddnet_test;
|
||||
|
||||
mod console;
|
||||
|
||||
pub use console::*;
|
7
src/engine/rust.h
Normal file
7
src/engine/rust.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#ifndef ENGINE_RUST_H
|
||||
#define ENGINE_RUST_H
|
||||
#include "console.h"
|
||||
|
||||
typedef IConsole::IResult IConsole_IResult;
|
||||
typedef IConsole::FCommandCallback IConsole_FCommandCallback;
|
||||
#endif // ENGINE_RUST_H
|
|
@ -108,7 +108,7 @@ int main(int argc, const char **argv)
|
|||
IEngine *pEngine = CreateEngine(GAME_NAME, pFutureConsoleLogger, 2);
|
||||
IEngineMap *pEngineMap = CreateEngineMap();
|
||||
IGameServer *pGameServer = CreateGameServer();
|
||||
IConsole *pConsole = CreateConsole(CFGFLAG_SERVER | CFGFLAG_ECON);
|
||||
IConsole *pConsole = CreateConsole(CFGFLAG_SERVER | CFGFLAG_ECON).release();
|
||||
IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_SERVER, argc, argv);
|
||||
IConfigManager *pConfigManager = CreateConfigManager();
|
||||
IEngineAntibot *pEngineAntibot = CreateEngineAntibot();
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <engine/shared/packer.h>
|
||||
#include <engine/shared/protocol.h>
|
||||
#include <engine/shared/protocol_ex.h>
|
||||
#include <engine/shared/rust_version.h>
|
||||
#include <engine/shared/snapshot.h>
|
||||
|
||||
#include <game/version.h>
|
||||
|
@ -3666,6 +3667,8 @@ void CServer::RegisterCommands()
|
|||
Console()->Register("name_unban", "s[name]", CFGFLAG_SERVER, ConNameUnban, this, "Unban a certain nickname");
|
||||
Console()->Register("name_bans", "", CFGFLAG_SERVER, ConNameBans, this, "List all name bans");
|
||||
|
||||
RustVersionRegister(*Console());
|
||||
|
||||
Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
|
||||
Console()->Chain("loglevel", ConchainLoglevel, this);
|
||||
Console()->Chain("password", ConchainSpecialInfoupdate, this);
|
||||
|
|
19
src/engine/shared/Cargo.toml
Normal file
19
src/engine/shared/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "ddnet-engine-shared"
|
||||
version = "0.0.1"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
license = "Zlib"
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib", "staticlib"]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
ddnet-base = { path = "../../base" }
|
||||
ddnet-engine = { path = ".." }
|
||||
|
||||
cxx = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ddnet-test = { path = "../../rust-bridge/test", features = ["link-test-libraries"] }
|
18
src/engine/shared/build.rs
Normal file
18
src/engine/shared/build.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let mut out = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR"));
|
||||
out.push("rustc-version");
|
||||
let rustc = env::var_os("RUSTC").expect("RUSTC");
|
||||
let rustc_output = Command::new(rustc)
|
||||
.arg("--version")
|
||||
.output()
|
||||
.expect("rustc --version");
|
||||
if !rustc_output.status.success() {
|
||||
panic!("rustc --version: exit status {}", rustc_output.status);
|
||||
}
|
||||
fs::write(&out, rustc_output.stdout).expect("file write");
|
||||
}
|
57
src/engine/shared/config.rs
Normal file
57
src/engine/shared/config.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
/// Config variable that is saved when the client is closed.
|
||||
///
|
||||
/// Has no effect on other commands.
|
||||
pub const CFGFLAG_SAVE: i32 = 1 << 0;
|
||||
|
||||
/// Command that is available in the client.
|
||||
pub const CFGFLAG_CLIENT: i32 = 1 << 1;
|
||||
|
||||
/// Command that is available on the server.
|
||||
pub const CFGFLAG_SERVER: i32 = 1 << 2;
|
||||
|
||||
/// Command that is delayed in the execution until
|
||||
/// `IConsole::StoreCommands(false)` is called.
|
||||
pub const CFGFLAG_STORE: i32 = 1 << 3;
|
||||
|
||||
/// Command that is available in the master server.
|
||||
pub const CFGFLAG_MASTER: i32 = 1 << 4;
|
||||
|
||||
/// Command that has something to do with the external console (econ).
|
||||
pub const CFGFLAG_ECON: i32 = 1 << 5;
|
||||
|
||||
/// Command that can be used for testing or cheating maps. Only available if
|
||||
/// `sv_test_cmds 1` is set.
|
||||
pub const CMDFLAG_TEST: i32 = 1 << 6;
|
||||
|
||||
/// Command that can be used from the chat on the server.
|
||||
pub const CFGFLAG_CHAT: i32 = 1 << 7;
|
||||
|
||||
/// Command that can be used from a map config.
|
||||
///
|
||||
/// Only commands that are not security sensitive should have this flag.
|
||||
pub const CFGFLAG_GAME: i32 = 1 << 8;
|
||||
|
||||
/// Command that is not recorded into teehistorian.
|
||||
///
|
||||
/// This should only be set for security sensitive commands like passwords etc.
|
||||
/// that should not be recorded.
|
||||
pub const CFGFLAG_NONTEEHISTORIC: i32 = 1 << 9;
|
||||
|
||||
/// Color config variable that can only have lightness 0.5 to 1.0.
|
||||
///
|
||||
/// This is achieved by dividing the lightness channel by and adding 0.5, i.e.
|
||||
/// remapping all the colors.
|
||||
///
|
||||
/// Has no effect on other commands or config variables.
|
||||
pub const CFGFLAG_COLLIGHT: i32 = 1 << 10;
|
||||
|
||||
/// Color config variable that includes an alpha (opacity) value.
|
||||
///
|
||||
/// Has no effect on other commands or config variables.
|
||||
pub const CFGFLAG_COLALPHA: i32 = 1 << 11;
|
||||
|
||||
/// Config variable with insensitive data that can be included in client
|
||||
/// integrity checks.
|
||||
///
|
||||
/// This should only be set on config variables the server could observe anyway.
|
||||
pub const CFGFLAG_INSENSITIVE: i32 = 1 << 12;
|
|
@ -19,28 +19,28 @@
|
|||
|
||||
// todo: rework this
|
||||
|
||||
const char *CConsole::CResult::GetString(unsigned Index)
|
||||
const char *CConsole::CResult::GetString(unsigned Index) const
|
||||
{
|
||||
if(Index >= m_NumArgs)
|
||||
return "";
|
||||
return m_apArgs[Index];
|
||||
}
|
||||
|
||||
int CConsole::CResult::GetInteger(unsigned Index)
|
||||
int CConsole::CResult::GetInteger(unsigned Index) const
|
||||
{
|
||||
if(Index >= m_NumArgs)
|
||||
return 0;
|
||||
return str_toint(m_apArgs[Index]);
|
||||
}
|
||||
|
||||
float CConsole::CResult::GetFloat(unsigned Index)
|
||||
float CConsole::CResult::GetFloat(unsigned Index) const
|
||||
{
|
||||
if(Index >= m_NumArgs)
|
||||
return 0.0f;
|
||||
return str_tofloat(m_apArgs[Index]);
|
||||
}
|
||||
|
||||
ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light)
|
||||
ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light) const
|
||||
{
|
||||
ColorHSLA Hsla = ColorHSLA(0, 0, 0);
|
||||
if(Index >= m_NumArgs)
|
||||
|
@ -322,7 +322,7 @@ LOG_COLOR ColorToLogColor(ColorRGBA Color)
|
|||
(uint8_t)(Color.b * 255.0)};
|
||||
}
|
||||
|
||||
void CConsole::Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor)
|
||||
void CConsole::Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor) const
|
||||
{
|
||||
LEVEL LogLevel = IConsole::ToLogLevel(Level);
|
||||
// if the color is pure white, use default terminal color
|
||||
|
@ -1268,7 +1268,7 @@ const IConsole::CCommandInfo *CConsole::GetCommandInfo(const char *pName, int Fl
|
|||
return 0;
|
||||
}
|
||||
|
||||
extern IConsole *CreateConsole(int FlagMask) { return new CConsole(FlagMask); }
|
||||
std::unique_ptr<IConsole> CreateConsole(int FlagMask) { return std::make_unique<CConsole>(FlagMask); }
|
||||
|
||||
void CConsole::ResetServerGameSettings()
|
||||
{
|
||||
|
@ -1307,7 +1307,7 @@ void CConsole::ResetServerGameSettings()
|
|||
#undef MACRO_CONFIG_STR
|
||||
}
|
||||
|
||||
int CConsole::CResult::GetVictim()
|
||||
int CConsole::CResult::GetVictim() const
|
||||
{
|
||||
return m_Victim;
|
||||
}
|
||||
|
|
|
@ -114,10 +114,10 @@ class CConsole : public IConsole
|
|||
m_apArgs[m_NumArgs++] = pArg;
|
||||
}
|
||||
|
||||
const char *GetString(unsigned Index) override;
|
||||
int GetInteger(unsigned Index) override;
|
||||
float GetFloat(unsigned Index) override;
|
||||
ColorHSLA GetColor(unsigned Index, bool Light) override;
|
||||
const char *GetString(unsigned Index) const override;
|
||||
int GetInteger(unsigned Index) const override;
|
||||
float GetFloat(unsigned Index) const override;
|
||||
ColorHSLA GetColor(unsigned Index, bool Light) const override;
|
||||
|
||||
void RemoveArgument(unsigned Index) override
|
||||
{
|
||||
|
@ -142,7 +142,7 @@ class CConsole : public IConsole
|
|||
bool HasVictim();
|
||||
void SetVictim(int Victim);
|
||||
void SetVictim(const char *pVictim);
|
||||
int GetVictim() override;
|
||||
int GetVictim() const override;
|
||||
};
|
||||
|
||||
int ParseStart(CResult *pResult, const char *pString, int Length);
|
||||
|
@ -215,7 +215,7 @@ public:
|
|||
void ExecuteFile(const char *pFilename, int ClientID = -1, bool LogFailure = false, int StorageType = IStorage::TYPE_ALL) override;
|
||||
|
||||
char *Format(char *pBuf, int Size, const char *pFrom, const char *pStr) override;
|
||||
void Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor = gs_ConsoleDefaultColor) override;
|
||||
void Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor = gs_ConsoleDefaultColor) const override;
|
||||
void SetTeeHistorianCommandCallback(FTeeHistorianCommandCallback pfnCallback, void *pUser) override;
|
||||
void SetUnknownCommandCallback(FUnknownCommandCallback pfnCallback, void *pUser) override;
|
||||
void InitChecksum(CChecksumData *pData) const override;
|
||||
|
|
21
src/engine/shared/lib.rs
Normal file
21
src/engine/shared/lib.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
//! DDNet's engine interfaces, Rust part.
|
||||
//!
|
||||
//! DDNet's code base is separated into three major parts, `base`, `engine` and
|
||||
//! `game`.
|
||||
//!
|
||||
//! The engine consists of game-independent code such as display setup,
|
||||
//! low-level network protocol, low-level map format, etc.
|
||||
//!
|
||||
//! This crate in particular corresponds to the `src/engine/shared` directory
|
||||
//! that contains code shared between client, server and other components.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate ddnet_test;
|
||||
|
||||
mod config;
|
||||
mod rust_version;
|
||||
|
||||
pub use config::*;
|
||||
pub use rust_version::*;
|
59
src/engine/shared/rust_version.rs
Normal file
59
src/engine/shared/rust_version.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use super::CFGFLAG_CLIENT;
|
||||
use super::CFGFLAG_SERVER;
|
||||
use ddnet_base::s;
|
||||
use ddnet_base::UserPtr;
|
||||
use ddnet_engine::gs_ConsoleDefaultColor;
|
||||
use ddnet_engine::IConsole;
|
||||
use ddnet_engine::IConsole_FCommandCallback;
|
||||
use ddnet_engine::IConsole_IResult;
|
||||
use ddnet_engine::IConsole_OUTPUT_LEVEL_STANDARD;
|
||||
use std::pin::Pin;
|
||||
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
extern "C++" {
|
||||
include!("base/rust.h");
|
||||
include!("engine/console.h");
|
||||
|
||||
type IConsole = ddnet_engine::IConsole;
|
||||
}
|
||||
extern "Rust" {
|
||||
fn RustVersionPrint(console: &IConsole);
|
||||
fn RustVersionRegister(console: Pin<&mut IConsole>);
|
||||
}
|
||||
}
|
||||
|
||||
/// Print the Rust version used for compiling this crate.
|
||||
///
|
||||
/// Uses [`IConsole::Print`] for printing.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn RustVersionPrint(console: &IConsole) {
|
||||
console.Print(
|
||||
IConsole_OUTPUT_LEVEL_STANDARD,
|
||||
s!("rust_version"),
|
||||
s!(include_str!(concat!(env!("OUT_DIR"), "/rustc-version"))),
|
||||
gs_ConsoleDefaultColor,
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
extern "C" fn PrintRustVersionCallback(_: &IConsole_IResult, user: UserPtr) {
|
||||
RustVersionPrint(unsafe { user.cast() })
|
||||
}
|
||||
|
||||
/// Register the `rust_version` command to the given console instance.
|
||||
///
|
||||
/// This command calls the [`RustVersionPrint`] function to print the Rust
|
||||
/// version used for compiling this crate.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn RustVersionRegister(console: Pin<&mut IConsole>) {
|
||||
let user = console.as_ref().get_ref().into();
|
||||
console.Register(
|
||||
s!("rust_version"),
|
||||
s!(""),
|
||||
CFGFLAG_CLIENT | CFGFLAG_SERVER,
|
||||
IConsole_FCommandCallback(PrintRustVersionCallback),
|
||||
user,
|
||||
s!("Prints the Rust version used to compile DDNet"),
|
||||
);
|
||||
}
|
|
@ -43,8 +43,8 @@ CONSOLE_COMMAND("move_raw", "i[x] i[y]", CFGFLAG_SERVER | CMDFLAG_TEST, ConMoveR
|
|||
CONSOLE_COMMAND("force_pause", "v[id] i[seconds]", CFGFLAG_SERVER, ConForcePause, this, "Force i to pause for i seconds")
|
||||
CONSOLE_COMMAND("force_unpause", "v[id]", CFGFLAG_SERVER, ConForcePause, this, "Set force-pause timer of i to 0.")
|
||||
|
||||
CONSOLE_COMMAND("set_team_ddr", "v[id] ?i[team]", CFGFLAG_SERVER, ConSetDDRTeam, this, "Set ddrace team of a player")
|
||||
CONSOLE_COMMAND("uninvite", "v[id] ?i[team]", CFGFLAG_SERVER, ConUninvite, this, "Uninvite player from team")
|
||||
CONSOLE_COMMAND("set_team_ddr", "v[id] i[team]", CFGFLAG_SERVER, ConSetDDRTeam, this, "Set ddrace team of a player")
|
||||
CONSOLE_COMMAND("uninvite", "v[id] i[team]", CFGFLAG_SERVER, ConUninvite, this, "Uninvite player from team")
|
||||
|
||||
CONSOLE_COMMAND("vote_mute", "v[id] i[seconds]", CFGFLAG_SERVER, ConVoteMute, this, "Remove v's right to vote for i seconds")
|
||||
CONSOLE_COMMAND("vote_unmute", "v[id]", CFGFLAG_SERVER, ConVoteUnmute, this, "Give back v's right to vote.")
|
||||
|
|
|
@ -3,6 +3,10 @@ name = "mastersrv"
|
|||
version = "0.0.1"
|
||||
authors = ["heinrich5991 <heinrich5991@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
license = "Zlib"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
arrayvec = { version = "0.5.2", features = ["serde"] }
|
||||
|
|
3
src/rust-bridge/.clang-tidy
Normal file
3
src/rust-bridge/.clang-tidy
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Need at least one check, otherwise clang-tidy fails, use one that can't
|
||||
# happen in our code since we don't use OpenMP API.
|
||||
Checks: '-*,openmp-exception-escape'
|
164
src/rust-bridge/cpp/console.cpp
Normal file
164
src/rust-bridge/cpp/console.cpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include "base/rust.h"
|
||||
#include "engine/console.h"
|
||||
#include "engine/rust.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace rust {
|
||||
inline namespace cxxbridge1 {
|
||||
// #include "rust/cxx.h"
|
||||
|
||||
#ifndef CXXBRIDGE1_IS_COMPLETE
|
||||
#define CXXBRIDGE1_IS_COMPLETE
|
||||
namespace detail {
|
||||
namespace {
|
||||
template <typename T, typename = std::size_t>
|
||||
struct is_complete : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_complete<T, decltype(sizeof(T))> : std::true_type {};
|
||||
} // namespace
|
||||
} // namespace detail
|
||||
#endif // CXXBRIDGE1_IS_COMPLETE
|
||||
|
||||
#ifndef CXXBRIDGE1_RELOCATABLE
|
||||
#define CXXBRIDGE1_RELOCATABLE
|
||||
namespace detail {
|
||||
template <typename... Ts>
|
||||
struct make_void {
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template <typename Void, template <typename...> class, typename...>
|
||||
struct detect : std::false_type {};
|
||||
template <template <typename...> class T, typename... A>
|
||||
struct detect<void_t<T<A...>>, T, A...> : std::true_type {};
|
||||
|
||||
template <template <typename...> class T, typename... A>
|
||||
using is_detected = detect<void, T, A...>;
|
||||
|
||||
template <typename T>
|
||||
using detect_IsRelocatable = typename T::IsRelocatable;
|
||||
|
||||
template <typename T>
|
||||
struct get_IsRelocatable
|
||||
: std::is_same<typename T::IsRelocatable, std::true_type> {};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
struct IsRelocatable
|
||||
: std::conditional<
|
||||
detail::is_detected<detail::detect_IsRelocatable, T>::value,
|
||||
detail::get_IsRelocatable<T>,
|
||||
std::integral_constant<
|
||||
bool, std::is_trivially_move_constructible<T>::value &&
|
||||
std::is_trivially_destructible<T>::value>>::type {};
|
||||
#endif // CXXBRIDGE1_RELOCATABLE
|
||||
|
||||
namespace {
|
||||
template <bool> struct deleter_if {
|
||||
template <typename T> void operator()(T *) {}
|
||||
};
|
||||
|
||||
template <> struct deleter_if<true> {
|
||||
template <typename T> void operator()(T *ptr) { ptr->~T(); }
|
||||
};
|
||||
} // namespace
|
||||
} // namespace cxxbridge1
|
||||
} // namespace rust
|
||||
|
||||
using IConsole_IResult = ::IConsole_IResult;
|
||||
using IConsole = ::IConsole;
|
||||
|
||||
static_assert(
|
||||
::rust::IsRelocatable<::ColorRGBA>::value,
|
||||
"type ColorRGBA should be trivially move constructible and trivially destructible in C++ to be used as an argument of `Print` in Rust");
|
||||
static_assert(
|
||||
::rust::IsRelocatable<::ColorHSLA>::value,
|
||||
"type ColorHSLA should be trivially move constructible and trivially destructible in C++ to be used as a return value of `GetColor` in Rust");
|
||||
static_assert(
|
||||
::rust::IsRelocatable<::StrRef>::value,
|
||||
"type StrRef should be trivially move constructible and trivially destructible in C++ to be used as an argument of `ExecuteLine`, `Print`, `Register` or return value of `GetString` in Rust");
|
||||
static_assert(
|
||||
::rust::IsRelocatable<::UserPtr>::value,
|
||||
"type UserPtr should be trivially move constructible and trivially destructible in C++ to be used as an argument of `Register` in Rust");
|
||||
static_assert(
|
||||
::rust::IsRelocatable<::IConsole_FCommandCallback>::value,
|
||||
"type IConsole_FCommandCallback should be trivially move constructible and trivially destructible in C++ to be used as an argument of `Register` in Rust");
|
||||
|
||||
extern "C" {
|
||||
::std::int32_t cxxbridge1$IConsole_IResult$GetInteger(const ::IConsole_IResult &self, ::std::uint32_t Index) noexcept {
|
||||
::std::int32_t (::IConsole_IResult::*GetInteger$)(::std::uint32_t) const = &::IConsole_IResult::GetInteger;
|
||||
return (self.*GetInteger$)(Index);
|
||||
}
|
||||
|
||||
float cxxbridge1$IConsole_IResult$GetFloat(const ::IConsole_IResult &self, ::std::uint32_t Index) noexcept {
|
||||
float (::IConsole_IResult::*GetFloat$)(::std::uint32_t) const = &::IConsole_IResult::GetFloat;
|
||||
return (self.*GetFloat$)(Index);
|
||||
}
|
||||
|
||||
void cxxbridge1$IConsole_IResult$GetString(const ::IConsole_IResult &self, ::std::uint32_t Index, ::StrRef *return$) noexcept {
|
||||
::StrRef (::IConsole_IResult::*GetString$)(::std::uint32_t) const = &::IConsole_IResult::GetString;
|
||||
new (return$) ::StrRef((self.*GetString$)(Index));
|
||||
}
|
||||
|
||||
void cxxbridge1$IConsole_IResult$GetColor(const ::IConsole_IResult &self, ::std::uint32_t Index, bool Light, ::ColorHSLA *return$) noexcept {
|
||||
::ColorHSLA (::IConsole_IResult::*GetColor$)(::std::uint32_t, bool) const = &::IConsole_IResult::GetColor;
|
||||
new (return$) ::ColorHSLA((self.*GetColor$)(Index, Light));
|
||||
}
|
||||
|
||||
::std::int32_t cxxbridge1$IConsole_IResult$NumArguments(const ::IConsole_IResult &self) noexcept {
|
||||
::std::int32_t (::IConsole_IResult::*NumArguments$)() const = &::IConsole_IResult::NumArguments;
|
||||
return (self.*NumArguments$)();
|
||||
}
|
||||
|
||||
::std::int32_t cxxbridge1$IConsole_IResult$GetVictim(const ::IConsole_IResult &self) noexcept {
|
||||
::std::int32_t (::IConsole_IResult::*GetVictim$)() const = &::IConsole_IResult::GetVictim;
|
||||
return (self.*GetVictim$)();
|
||||
}
|
||||
|
||||
void cxxbridge1$IConsole$ExecuteLine(::IConsole &self, ::StrRef *pStr, ::std::int32_t ClientID, bool InterpretSemicolons) noexcept {
|
||||
void (::IConsole::*ExecuteLine$)(::StrRef, ::std::int32_t, bool) = &::IConsole::ExecuteLine;
|
||||
(self.*ExecuteLine$)(::std::move(*pStr), ClientID, InterpretSemicolons);
|
||||
}
|
||||
|
||||
void cxxbridge1$IConsole$Print(const ::IConsole &self, ::std::int32_t Level, ::StrRef *pFrom, ::StrRef *pStr, ::ColorRGBA *PrintColor) noexcept {
|
||||
void (::IConsole::*Print$)(::std::int32_t, ::StrRef, ::StrRef, ::ColorRGBA) const = &::IConsole::Print;
|
||||
(self.*Print$)(Level, ::std::move(*pFrom), ::std::move(*pStr), ::std::move(*PrintColor));
|
||||
}
|
||||
|
||||
void cxxbridge1$IConsole$Register(::IConsole &self, ::StrRef *pName, ::StrRef *pParams, ::std::int32_t Flags, ::IConsole_FCommandCallback *pfnFunc, ::UserPtr *pUser, ::StrRef *pHelp) noexcept {
|
||||
void (::IConsole::*Register$)(::StrRef, ::StrRef, ::std::int32_t, ::IConsole_FCommandCallback, ::UserPtr, ::StrRef) = &::IConsole::Register;
|
||||
(self.*Register$)(::std::move(*pName), ::std::move(*pParams), Flags, ::std::move(*pfnFunc), ::std::move(*pUser), ::std::move(*pHelp));
|
||||
}
|
||||
|
||||
::IConsole *cxxbridge1$CreateConsole(::std::int32_t FlagMask) noexcept {
|
||||
::std::unique_ptr<::IConsole> (*CreateConsole$)(::std::int32_t) = ::CreateConsole;
|
||||
return CreateConsole$(FlagMask).release();
|
||||
}
|
||||
|
||||
static_assert(::rust::detail::is_complete<::IConsole>::value, "definition of IConsole is required");
|
||||
static_assert(sizeof(::std::unique_ptr<::IConsole>) == sizeof(void *), "");
|
||||
static_assert(alignof(::std::unique_ptr<::IConsole>) == alignof(void *), "");
|
||||
void cxxbridge1$unique_ptr$IConsole$null(::std::unique_ptr<::IConsole> *ptr) noexcept {
|
||||
::new (ptr) ::std::unique_ptr<::IConsole>();
|
||||
}
|
||||
void cxxbridge1$unique_ptr$IConsole$raw(::std::unique_ptr<::IConsole> *ptr, ::IConsole *raw) noexcept {
|
||||
::new (ptr) ::std::unique_ptr<::IConsole>(raw);
|
||||
}
|
||||
const ::IConsole *cxxbridge1$unique_ptr$IConsole$get(const ::std::unique_ptr<::IConsole>& ptr) noexcept {
|
||||
return ptr.get();
|
||||
}
|
||||
::IConsole *cxxbridge1$unique_ptr$IConsole$release(::std::unique_ptr<::IConsole>& ptr) noexcept {
|
||||
return ptr.release();
|
||||
}
|
||||
void cxxbridge1$unique_ptr$IConsole$drop(::std::unique_ptr<::IConsole> *ptr) noexcept {
|
||||
::rust::deleter_if<::rust::detail::is_complete<::IConsole>::value>{}(ptr);
|
||||
}
|
||||
} // extern "C"
|
9
src/rust-bridge/cpp/console.h
Normal file
9
src/rust-bridge/cpp/console.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
#include "base/rust.h"
|
||||
#include "engine/console.h"
|
||||
#include "engine/rust.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
using IConsole_IResult = ::IConsole_IResult;
|
||||
using IConsole = ::IConsole;
|
16
src/rust-bridge/engine/shared/rust_version.cpp
Normal file
16
src/rust-bridge/engine/shared/rust_version.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "base/rust.h"
|
||||
#include "engine/console.h"
|
||||
|
||||
extern "C" {
|
||||
void cxxbridge1$RustVersionPrint(const ::IConsole &console) noexcept;
|
||||
|
||||
void cxxbridge1$RustVersionRegister(::IConsole &console) noexcept;
|
||||
} // extern "C"
|
||||
|
||||
void RustVersionPrint(const ::IConsole &console) noexcept {
|
||||
cxxbridge1$RustVersionPrint(console);
|
||||
}
|
||||
|
||||
void RustVersionRegister(::IConsole &console) noexcept {
|
||||
cxxbridge1$RustVersionRegister(console);
|
||||
}
|
7
src/rust-bridge/engine/shared/rust_version.h
Normal file
7
src/rust-bridge/engine/shared/rust_version.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
#include "base/rust.h"
|
||||
#include "engine/console.h"
|
||||
|
||||
void RustVersionPrint(const ::IConsole &console) noexcept;
|
||||
|
||||
void RustVersionRegister(::IConsole &console) noexcept;
|
12
src/rust-bridge/test/Cargo.toml
Normal file
12
src/rust-bridge/test/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "ddnet-test"
|
||||
version = "0.0.1"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
license = "Zlib"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[features]
|
||||
link-test-libraries = []
|
68
src/rust-bridge/test/build.rs
Normal file
68
src/rust-bridge/test/build.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let rustc = env::var_os("RUSTC").expect("RUSTC");
|
||||
let rustc_output = Command::new(rustc)
|
||||
.arg("--version")
|
||||
.output()
|
||||
.expect("rustc --version");
|
||||
if !rustc_output.status.success() {
|
||||
panic!("rustc --version: exit status {}", rustc_output.status);
|
||||
}
|
||||
let rustc_version = &rustc_output.stdout[..];
|
||||
let supports_whole_archive =
|
||||
!rustc_version.starts_with(b"rustc ") || rustc_version >= &b"rustc 1.61.0"[..];
|
||||
|
||||
println!("cargo:rerun-if-env-changed=DDNET_TEST_LIBRARIES");
|
||||
println!("cargo:rerun-if-env-changed=DDNET_TEST_NO_LINK");
|
||||
if env::var_os("DDNET_TEST_NO_LINK").is_some() {
|
||||
return;
|
||||
}
|
||||
if env::var_os("CARGO_FEATURE_LINK_TEST_LIBRARIES").is_some() {
|
||||
let libraries = env::var("DDNET_TEST_LIBRARIES")
|
||||
.expect("environment variable DDNET_TEST_LIBRARIES required but not found");
|
||||
let mut seen_library_dirs = HashSet::new();
|
||||
for library in libraries.split(';') {
|
||||
let library = Path::new(library);
|
||||
let extension = library.extension().and_then(OsStr::to_str);
|
||||
let kind = match extension {
|
||||
Some("framework") => "framework=",
|
||||
Some("so") => "dylib=",
|
||||
Some("a") => {
|
||||
if supports_whole_archive {
|
||||
"static:-whole-archive="
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
_ => "",
|
||||
};
|
||||
let dir_kind = match extension {
|
||||
Some("framework") => "framework=",
|
||||
_ => "",
|
||||
};
|
||||
if let Some(parent) = library.parent() {
|
||||
if parent != Path::new("") {
|
||||
let parent = parent.to_str().expect("should have errored earlier");
|
||||
if !seen_library_dirs.contains(&(dir_kind, parent)) {
|
||||
println!("cargo:rustc-link-search={}{}", dir_kind, parent);
|
||||
seen_library_dirs.insert((dir_kind, parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut name = library
|
||||
.file_stem()
|
||||
.expect("library name")
|
||||
.to_str()
|
||||
.expect("should have errored earlier");
|
||||
if name.starts_with("lib") {
|
||||
name = &name[3..];
|
||||
}
|
||||
println!("cargo:rustc-link-lib={}{}", kind, name);
|
||||
}
|
||||
}
|
||||
}
|
10
src/rust-bridge/test/lib.rs
Normal file
10
src/rust-bridge/test/lib.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
//! Helper module to link the C++ code of DDNet for Rust unit tests.
|
||||
//!
|
||||
//! Use the global `run_rust_tests` target from CMakeLists.txt to actually run
|
||||
//! the Rust test; this sets compiles the C++ and sets `DDNET_TEST_LIBRARIES`
|
||||
//! appropriately, e.g. using `cmake --build build --target run_rust_tests`.
|
||||
//!
|
||||
//! To call `cargo doc`, set `DDNET_TEST_NO_LINK=1` so that this crate becomes a
|
||||
//! stub.
|
||||
|
||||
#![warn(missing_docs)]
|
|
@ -13,7 +13,7 @@ TEST(ServerBrowser, PingCache)
|
|||
CTestInfo Info;
|
||||
Info.m_DeleteTestStorageFilesOnSuccess = true;
|
||||
|
||||
auto pConsole = std::unique_ptr<IConsole>(CreateConsole(CFGFLAG_CLIENT));
|
||||
auto pConsole = CreateConsole(CFGFLAG_CLIENT);
|
||||
auto pStorage = std::unique_ptr<IStorage>(Info.CreateTestStorage());
|
||||
auto pPingCache = std::unique_ptr<IServerBrowserPingCache>(CreateServerBrowserPingCache(pConsole.get(), pStorage.get()));
|
||||
|
||||
|
|
Loading…
Reference in a new issue