Skip to content

Commit

Permalink
initial working codebase commit
Browse files Browse the repository at this point in the history
  • Loading branch information
s95rob committed Oct 20, 2023
1 parent 7081998 commit 9035fca
Show file tree
Hide file tree
Showing 17 changed files with 555 additions and 29 deletions.
14 changes: 14 additions & 0 deletions CMake/Package.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
set(SOURCE2PY_PACKAGE_DIR ${BUILD_DIR}/Package/Source2Py)
set(SOURCE2PY_PACKAGE_RES_DIR ${SCRIPTS_DIR}/Resources)

add_custom_command(TARGET Source2Py POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${SOURCE2PY_PACKAGE_DIR}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${SOURCE2PY_PACKAGE_DIR}/bin"
COMMAND ${CMAKE_COMMAND} -E make_directory "${SOURCE2PY_PACKAGE_DIR}/plugins"

COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:Source2Py> "${SOURCE2PY_PACKAGE_DIR}/bin"
COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE2PY_PACKAGE_RES_DIR}/pyplugins.ini" "${SOURCE2PY_PACKAGE_DIR}"

# Copy sample plugins to package directory
COMMAND ${CMAKE_COMMAND} -E copy "${SAMPLE_PLUGINS_DIR}/SamplePlugin.py" "${SOURCE2PY_PACKAGE_DIR}/plugins"
)
23 changes: 23 additions & 0 deletions CMake/Python.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Check if Python vars have been set in CLI

message(STATUS "Checking if Python paths have been set...")

if (NOT DEFINED PYTHON_INCLUDE_DIR)
message(FATAL_ERROR "Please provide Python include directory as CMake argument! Usage: -DPYTHON_INCLUDE_DIR=/path/to/python/include")
else()
message(STATUS "PYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIR}")
endif()

if (NOT DEFINED PYTHON_LIB_DIR)
message(FATAL_ERROR "Please provide Python lib directory as CMake argument! Usage: -DPYTHON_LIB_DIR=/path/to/python/lib")
else()
message(STATUS "PYTHON_LIB_DIR=${PYTHON_LIB_DIR}")
endif()

if (NOT DEFINED PYTHON_LIB)
message(FATAL_ERROR "Please provide Python library as CMake argument! Usage -DPYTHON_LIB=python3.11")
else()
message(STATUS "PYTHON_LIB=${PYTHON_LIB}")
endif()

message(STATUS "Python OK!")
3 changes: 3 additions & 0 deletions CMake/Resources/pyplugins.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# List Python plugins to include here

plugins/SamplePlugin.py
46 changes: 31 additions & 15 deletions CMake/Source2SDK.cmake
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
# Linux only for now

set(SOURCE2SDK_DIR ${EXTERN_DIR}/hl2sdk-cs2)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -Wno-error")
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -Wno-error")

add_compile_definitions(
POSIX
COMPILER_GCC
PLATFORM_64BITS
)
add_compile_definitions(
POSIX
COMPILER_GCC
PLATFORM_64BITS
)

set(SOURCE2SDK_LIB_DIRS
${SOURCE2SDK_DIR}/lib/linux64
)
set(SOURCE2SDK_LIB_DIRS
${SOURCE2SDK_DIR}/lib/linux64
)

set(SOURCE2SDK_LIBS
-l:interfaces.a
-l:tier1.a
)
set(SOURCE2SDK_LIBS
-l:interfaces.a
-l:tier1.a
)
elseif (WIN32)
add_compile_definitions(
COMPILER_MSVC64
COMPILER_MSVC
PLATFORM_64BITS
)

set(SOURCE2SDK_LIB_DIRS
${SOURCE2SDK_DIR}/lib/public/win64
)

set(SOURCE2SDK_LIBS
tier0
tier1
interfaces
)
endif()

set(SOURCE2SDK_INCLUDE_DIRS
${SOURCE2SDK_DIR}
Expand Down
18 changes: 16 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
cmake_minimum_required(VERSION 3.18)

set(ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(EXTERN_DIR ${ROOT_DIR}/Extern)
set(SCRIPTS_DIR ${ROOT_DIR}/CMake)
set(SAMPLE_PLUGINS_DIR ${ROOT_DIR}/Samples)
include("${SCRIPTS_DIR}/Python.cmake")

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED true)
Expand All @@ -12,18 +15,29 @@ project(Source2Py LANGUAGES CXX)
include("${SCRIPTS_DIR}/Source2SDK.cmake")
include("${SCRIPTS_DIR}/Metamod.cmake")

file(GLOB SOURCE2PY_SRCS "Source2Py/src/*.cpp")
file(GLOB SOURCE2PY_SRCS
"Source2Py/src/*.cpp"
"Source2Py/src/*.h"
)

add_library(Source2Py SHARED ${SOURCE2PY_SRCS})
target_include_directories(Source2Py PRIVATE
${SOURCE2SDK_INCLUDE_DIRS}
${METAMOD_INCLUDE_DIRS}
${EXTERN_DIR}/pybind11/include
${PYTHON_INCLUDE_DIR}
)
target_link_directories(Source2Py PRIVATE
${PYTHON_LIB_DIR}
)
target_link_libraries(Source2Py PRIVATE
Source2SDK
Metamod
${PYTHON_LIB}
)
set_target_properties(Source2Py PROPERTIES
OUTPUT_NAME "Source2Py"
PREFIX ""
)
)

include("${SCRIPTS_DIR}/Package.cmake")
8 changes: 8 additions & 0 deletions Samples/SamplePlugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Source2Py

class SamplePlugin:
def Load(self):
Source2Py.Print("Hello world!")

def Unload(self):
Source2Py.Print("Goodbye world!")
20 changes: 20 additions & 0 deletions Source2Py/src/Log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <iostream>
#include <string_view>
#include <string>

namespace Source2Py {

class Log {
public:

static void Write(std::string_view message) {
std::cout << "[ Source2Py ] " << message << "\n";
}

static void Error(std::string_view message) {
std::cerr << "[ Source2Py ] ERROR: " << message << "\n";
}
};
}
13 changes: 13 additions & 0 deletions Source2Py/src/PyAPI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "PyAPI.h"

#include <ISmmPlugin.h>

namespace Source2Py {

PLUGIN_GLOBALVARS();

void PyAPI::ConPrint(std::string& message) {
message.append("\n");
META_CONPRINT(message.c_str());
}
}
14 changes: 14 additions & 0 deletions Source2Py/src/PyAPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <string>

namespace Source2Py {

class PyAPI {
public:

// Print message to console
static void ConPrint(std::string& message);

};
}
12 changes: 12 additions & 0 deletions Source2Py/src/PyModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "PyAPI.h"

#include <pybind11/embed.h>
namespace py = pybind11;

PYBIND11_EMBEDDED_MODULE(Source2Py, m) {
using namespace Source2Py;

m.def("Print", &PyAPI::ConPrint);
}
66 changes: 66 additions & 0 deletions Source2Py/src/PyPlugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "PyPlugin.h"

#include <filesystem>
namespace fs = std::filesystem;

namespace Source2Py {

bool PyPlugin::LoadFromFile(std::string filepath) {
std::string modulename = fs::path(filepath).stem().string();

// Format filepath into module path for Python
filepath.erase(filepath.find_last_of('.'), filepath.size());
for (char& c : filepath) {
if (c == '/' || c == '\\')
c = '.';
}

// Attempt to load Python plugin
try {
m_PluginObject = py::module_::import(filepath.c_str()).attr(modulename.c_str());
}
catch (py::error_already_set& e) {
Log::Error("Failed to load Python plugin: " + filepath + "\n" + std::string(e.what()));
m_Valid = false;
}

return m_Valid;
}

void PyPlugin::Load() {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "Load");
}

void PyPlugin::Unload() {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "Unload");
}

void PyPlugin::GameFrame(bool simulating, bool firstTick, bool lastTick) {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "GameFrame", simulating, firstTick, lastTick);
}

void PyPlugin::ClientActive(int playerSlot, bool loadGame, const char* name, uint64_t xuid) {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "ClientActive", playerSlot, loadGame, name, xuid);
}

void PyPlugin::ClientDisconnect(int playerSlot, int reason, const char* name, uint64_t xuid, const char* networkID) {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "ClientDisconnect", playerSlot, reason, name, xuid, networkID);
}

void PyPlugin::ClientPutInServer(int playerSlot, char const* name, int type, uint64_t xuid) {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "ClientPutInServer", playerSlot, name, type, xuid);
}

void PyPlugin::ClientSettingsChanged(int playerSlot) {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "ClientSettingsChanged", playerSlot);
}

void PyPlugin::OnClientConnected(int playerSlot, const char* name, uint64_t xuid, const char* networkID, const char* address, bool fakePlayer) {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "OnClientConnected", playerSlot, name, xuid, networkID, address, fakePlayer);
}

void PyPlugin::ClientConnect(int playerSlot, const char* name, uint64_t xuid, const char* networkID) {
PyRuntime::ExecuteObjectMethod(m_PluginObject, "ClientConnect", playerSlot, name, xuid, networkID);
}

}
36 changes: 36 additions & 0 deletions Source2Py/src/PyPlugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include "PyRuntime.h"

namespace Source2Py {

class PyPlugin {
public:
PyPlugin() = default;
PyPlugin(std::string filepath) { this->LoadFromFile(filepath); }

bool LoadFromFile(std::string filepath);

// Python plugin methods
void Load();
void Unload();

// Python plugin hooks
void GameFrame(bool simulating, bool firstTick, bool lastTick);
void ClientActive(int playerSlot, bool loadGame, const char* name, uint64_t xuid);
void ClientDisconnect(int playerSlot, int reason, const char* name, uint64_t xuid, const char* networkID);
void ClientPutInServer(int playerSlot, char const* name, int type, uint64_t xuid);
void ClientSettingsChanged(int playerSlot);
void OnClientConnected(int playerSlot, const char* name, uint64_t xuid, const char* networkID, const char* address, bool fakePlayer);
void ClientConnect(int playerSlot, const char* name, uint64_t xuid, const char* networkID);
//void ClientCommand(int playerSlot, const CCommand& _cmd); (todo: port CCommand)

bool IsValid() const { return m_Valid; }

operator bool() { return m_Valid; }

private:
py::object m_PluginObject;
bool m_Valid = true;
};
}
39 changes: 39 additions & 0 deletions Source2Py/src/PyRuntime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "PyRuntime.h"
#include "PyModule.h" // Include this once

namespace Source2Py {

bool PyRuntime::s_InterpreterRunning = false;

bool PyRuntime::Init() {
if (s_InterpreterRunning) {
Log::Error("PyRuntime is already running!");
return false;
}

try {
py::initialize_interpreter();
s_InterpreterRunning = true;
}
catch (std::exception& e) {
Log::Error(e.what());
return false;
}

Log::Write("PyRuntime started");

return true;
}

void PyRuntime::Close() {
if (!s_InterpreterRunning) {
Log::Error("PyRuntime is not running!");
return;
}

py::finalize_interpreter();
s_InterpreterRunning = false;

Log::Write("PyRuntime stopped");
}
}
Loading

0 comments on commit 9035fca

Please sign in to comment.