├── .gitignore ├── src ├── ompy │ ├── network.pxd │ ├── CMakeLists.txt │ ├── __init__.pyx │ ├── types.pxd │ ├── core.pxd │ └── core.pyx ├── python_vars.h.in ├── CMakeLists.txt └── main.cpp ├── CMakeLists.txt ├── .gitmodules └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw* 2 | build/ 3 | -------------------------------------------------------------------------------- /src/ompy/network.pxd: -------------------------------------------------------------------------------- 1 | cdef extern from 'network.hpp': 2 | ctypedef struct BanEntry: 3 | # TODO 4 | pass 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | project(OMPy LANGUAGES C CXX VERSION 0.0.1) 3 | add_subdirectory(lib/open.mp-sdk) 4 | add_subdirectory(src) 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "open.mp-sdk"] 2 | path = lib/open.mp-sdk 3 | url = https://github.com/openmultiplayer/open.mp-sdk.git 4 | [submodule "scikit-build"] 5 | path = lib/scikit-build 6 | url = https://github.com/scikit-build/scikit-build/ 7 | -------------------------------------------------------------------------------- /src/python_vars.h.in: -------------------------------------------------------------------------------- 1 | #ifndef PYTHON_VARS_H 2 | #define PYTHON_VARS_H 3 | 4 | #ifndef WIN32 5 | #include 6 | #define PYTHON_LIBRARY "@Python3_LIBRARIES@" 7 | #endif 8 | 9 | #define PYTHON_HOME L"@PYTHON_HOME@" 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/ompy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE pyx_files "${CMAKE_CURRENT_SOURCE_DIR}/*.pyx") 2 | foreach(pyx_file ${pyx_files}) 3 | add_cython_target(${pyx_file} CXX OUTPUT_VAR generated_cxx_file) 4 | list(APPEND ompy_module ${generated_cxx_file}) 5 | endforeach() 6 | 7 | add_library(ompy_module STATIC ${ompy_module}) 8 | set_target_properties(ompy_module PROPERTIES 9 | PREFIX "" 10 | LIBRARY_OUTPUT_DIRECTORY ".." 11 | POSITION_INDEPENDENT_CODE ON 12 | ) 13 | target_include_directories(ompy_module PRIVATE 14 | ${Python3_INCLUDE_DIRS} 15 | ) 16 | target_link_libraries(ompy_module PRIVATE 17 | SDK 18 | ) 19 | -------------------------------------------------------------------------------- /src/ompy/__init__.pyx: -------------------------------------------------------------------------------- 1 | import sys 2 | from importlib.abc import MetaPathFinder 3 | from importlib.machinery import BuiltinImporter 4 | from importlib.util import spec_from_loader 5 | 6 | 7 | __path__ = 'ompy' 8 | 9 | 10 | class OMPyFinder(MetaPathFinder): 11 | valid_modules = set( 12 | name for name in sys.builtin_module_names 13 | if name.startswith('ompy.') 14 | ) 15 | 16 | def find_spec(self, fullname, path, target=None): 17 | if fullname not in self.valid_modules: 18 | return 19 | 20 | return spec_from_loader(fullname, BuiltinImporter()) 21 | 22 | 23 | sys.meta_path.insert(0, OMPyFinder()) 24 | -------------------------------------------------------------------------------- /src/ompy/types.pxd: -------------------------------------------------------------------------------- 1 | from libcpp.pair cimport pair 2 | from libcpp.string cimport string 3 | 4 | 5 | cdef extern from '' namespace 'std': 6 | cdef cppclass array[T, N]: 7 | T& operator[](size_t) 8 | T* data() 9 | 10 | 11 | cdef extern from '' namespace 'std': 12 | cdef cppclass span[T]: 13 | T& operator[](size_t) 14 | T* data() 15 | 16 | 17 | cdef extern from 'types.hpp': 18 | ctypedef array StaticArray 19 | ctypedef pair Pair 20 | ctypedef span Span 21 | ctypedef string StringView 22 | ctypedef char uint8_t 23 | ctypedef short uint16_t 24 | 25 | cdef cppclass SemanticVersion: 26 | SemanticVersion(const SemanticVersion&) 27 | uint8_t major 28 | uint8_t minor 29 | uint8_t patch 30 | uint16_t prerel 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OMPy 2 | 3 | Python support for open.mp. 4 | 5 | ## Tools 6 | 7 | * [CMake 3.19+](https://cmake.org/) 8 | * [Conan 1.33+](https://conan.io/) 9 | 10 | ## Tools on Windows 11 | 12 | * [Visual Studio 2019+](https://www.visualstudio.com/) 13 | 14 | Visual Studio needs the `Desktop development with C++` workload with the `MSVC v142`, `Windows 10 SDK` and `C++ Clang tools for Windows` components. 15 | 16 | ## Sources 17 | 18 | ```bash 19 | # With HTTPS: 20 | git clone --recursive https://github.com/Cheaterman/OMPy.git 21 | # With SSH: 22 | git clone --recursive git@github.com:Cheaterman/OMPy.git 23 | ``` 24 | 25 | Note the use of the `--recursive` argument, because this repository contains submodules. 26 | 27 | ## Building on Windows 28 | 29 | ```bash 30 | mkdir build 31 | cd build 32 | cmake .. -A Win32 -T ClangCL 33 | ``` 34 | 35 | Open Visual Studio and build the solution. 36 | 37 | ## Building on Linux 38 | 39 | ```bash 40 | ./build.sh 41 | ``` 42 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | list(APPEND CMAKE_MODULE_PATH 2 | ${PROJECT_SOURCE_DIR}/lib/scikit-build/skbuild/resources/cmake 3 | ) 4 | 5 | find_package( 6 | Python3 7 | COMPONENTS Development 8 | REQUIRED 9 | ) 10 | find_package(Cython REQUIRED) 11 | 12 | add_library(${PROJECT_NAME} SHARED 13 | main.cpp 14 | ) 15 | add_subdirectory(ompy) 16 | set_target_properties(${PROJECT_NAME} PROPERTIES 17 | PREFIX "" 18 | LIBRARY_OUTPUT_DIRECTORY ".." 19 | ) 20 | 21 | target_include_directories(${PROJECT_NAME} PRIVATE 22 | ${Python3_INCLUDE_DIRS} 23 | ${CMAKE_BINARY_DIR}/src 24 | ${CMAKE_BINARY_DIR}/generated 25 | ) 26 | target_link_libraries(${PROJECT_NAME} PRIVATE 27 | SDK 28 | ompy_module 29 | ${Python3_LIBRARIES} 30 | ) 31 | 32 | if(WIN32) 33 | get_filename_component(PYTHON_HOME "${Python3_INCLUDE_DIRS}/.." REALPATH) 34 | else() 35 | get_filename_component(PYTHON_HOME "${Python3_INCLUDE_DIRS}/../.." REALPATH) 36 | endif() 37 | 38 | configure_file(python_vars.h.in ${CMAKE_BINARY_DIR}/generated/python_vars.h) 39 | -------------------------------------------------------------------------------- /src/ompy/core.pxd: -------------------------------------------------------------------------------- 1 | from .network cimport BanEntry 2 | from .types cimport Pair, SemanticVersion, Span, StaticArray, StringView 3 | 4 | 5 | cdef extern from 'core.hpp': 6 | cdef cppclass shaOutputSize '64 + 1': 7 | pass 8 | 9 | cdef struct IConfig: 10 | const StringView getString(StringView key) 11 | int* getInt(StringView key) 12 | float* getFloat(StringView key) 13 | size_t getStrings(StringView key, Span[StringView] output) 14 | size_t getStringsCount(StringView key) 15 | size_t getBansCount() 16 | const BanEntry& getBan(size_t index) 17 | void addBan(const BanEntry& entry) 18 | void removeBan(size_t index) 19 | void writeBans() 20 | Pair[bint, StringView] getNameFromAlias(StringView alias) 21 | 22 | cdef struct ICore: 23 | SemanticVersion getVersion() 24 | IConfig& getConfig() 25 | void printLn(const char* fmt, ...) 26 | unsigned getTickCount() 27 | unsigned tickRate() 28 | bint sha256(StringView password, StringView salt, StaticArray[char, shaOutputSize]& output) 29 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #define PY_SSIZE_T_CLEAN 2 | #include 3 | #include 4 | 5 | #include "python_vars.h" 6 | #include "ompy/core.h" 7 | 8 | extern "C" { 9 | PyMODINIT_FUNC PyInit_ompy(void); 10 | PyMODINIT_FUNC PyInit_core(void); 11 | } 12 | 13 | struct OMPyComponent final : IComponent { 14 | PROVIDE_UID(0x33342353125330CC); 15 | 16 | StringView componentName() const override { 17 | return "OMPy"; 18 | } 19 | 20 | SemanticVersion componentVersion() const override { 21 | return SemanticVersion(0, 0, 1, 0); 22 | } 23 | 24 | void onLoad(ICore* _core) override { 25 | core = _core; 26 | } 27 | 28 | void onInit(IComponentList* components) override { 29 | #ifndef WIN32 30 | dlopen(PYTHON_LIBRARY, RTLD_GLOBAL | RTLD_LAZY); 31 | #endif 32 | Py_SetPythonHome(PYTHON_HOME); 33 | wchar_t *programName = (wchar_t *)L"OMPy"; 34 | Py_SetProgramName(programName); 35 | 36 | PyImport_AppendInittab("ompy", PyInit_ompy); 37 | PyImport_AppendInittab("ompy.core", PyInit_core); 38 | 39 | Py_InitializeEx(false); 40 | PySys_SetArgv(1, &programName); 41 | 42 | PyImport_ImportModule("ompy.core"); 43 | OMPy_setCore(core); 44 | 45 | Py_UNBLOCK_THREADS 46 | } 47 | 48 | void onReady() override { 49 | Py_BLOCK_THREADS 50 | 51 | if(PyImport_ImportModule("python") == nullptr) 52 | PyErr_Print(); 53 | 54 | Py_UNBLOCK_THREADS 55 | } 56 | 57 | void free() override { 58 | Py_BLOCK_THREADS 59 | OMPy_setCore(nullptr); 60 | Py_FinalizeEx(); 61 | delete this; 62 | } 63 | 64 | ICore* core = nullptr; 65 | PyThreadState *_save; 66 | }; 67 | 68 | 69 | COMPONENT_ENTRY_POINT() { 70 | return new OMPyComponent(); 71 | } 72 | -------------------------------------------------------------------------------- /src/ompy/core.pyx: -------------------------------------------------------------------------------- 1 | cdef ICore* _core = NULL 2 | 3 | 4 | cdef public void OMPy_setCore(ICore* core): 5 | global _core 6 | _core = core 7 | 8 | 9 | cdef class Config: 10 | cdef IConfig* config 11 | 12 | def __init__(self): 13 | if self.config is NULL: 14 | raise RuntimeError( 15 | 'Cannot instantiate Config directly: use Core().get_config().' 16 | ) 17 | 18 | @staticmethod 19 | cdef Config from_config(IConfig* config): 20 | cdef Config self = Config.__new__(Config) 21 | self.config = config 22 | return self 23 | 24 | def get_string(self, key): 25 | cdef const char* value = self.config.getString( 26 | key.encode('utf8') 27 | ).data() 28 | 29 | if value is NULL: 30 | raise KeyError(key) 31 | 32 | return bytes(value).decode('utf8') 33 | 34 | def get_int(self, key): 35 | value = self.config.getInt(key.encode('utf8')) 36 | 37 | if value is NULL: 38 | raise KeyError(key) 39 | 40 | return value[0] 41 | 42 | def get_float(self, key): 43 | value = self.config.getFloat(key.encode('utf8')) 44 | 45 | if value is NULL: 46 | raise KeyError(key) 47 | 48 | return value[0] 49 | 50 | 51 | cdef class Core: 52 | cdef ICore* core 53 | 54 | def __cinit__(self): 55 | if _core is NULL: 56 | raise RuntimeError( 57 | 'Cannot instantiate Core: OMPy is not initialized.' 58 | ) 59 | 60 | self.core = _core 61 | 62 | def get_version(self): 63 | cdef SemanticVersion* version 64 | try: 65 | version = new SemanticVersion(self.core.getVersion()) 66 | return ( 67 | version.major, 68 | version.minor, 69 | version.patch, 70 | version.prerel, 71 | ) 72 | finally: 73 | del version 74 | 75 | def get_config(self): 76 | cdef IConfig* core_config = &self.core.getConfig() 77 | return Config.from_config(core_config) 78 | 79 | def print_ln(self, text): 80 | cdef text_bytes = text.encode('utf8') 81 | cdef char* _text = text_bytes 82 | self.core.printLn('%s', _text) 83 | 84 | def get_tick_count(self): 85 | return self.core.getTickCount() 86 | 87 | def tick_rate(self): 88 | return self.core.tickRate() 89 | 90 | def sha256(self, value, salt): 91 | cdef StaticArray[char, shaOutputSize] result 92 | self.core.sha256(value.encode('utf8'), salt.encode('utf8'), result) 93 | return result.data().decode('utf8') 94 | --------------------------------------------------------------------------------