Skip to content

Commit dddd882

Browse files
committed
Source code
1 parent 2e8d2ba commit dddd882

16 files changed

+866
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ install_manifest.txt
99
compile_commands.json
1010
CTestTestfile.cmake
1111
_deps
12+
/build*
13+
.vscode
14+
CMakeUserPresets.json

.gitmodules

Whitespace-only changes.

CMakeLists.txt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
3+
# Enable IDE folders
4+
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
5+
6+
# Force x86_64 architecture on MacOSX
7+
if(APPLE)
8+
set(CMAKE_OSX_ARCHITECTURES "x86_64")
9+
10+
# Disable deprecation warnings
11+
add_compile_options(
12+
-Wno-deprecated-declarations
13+
)
14+
endif()
15+
16+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
17+
18+
# Get the latest abbreviated commit hash of the working branch
19+
execute_process(
20+
COMMAND git log -1 --format=%h
21+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
22+
OUTPUT_VARIABLE GIT_HASH
23+
OUTPUT_STRIP_TRAILING_WHITESPACE
24+
)
25+
26+
# Get the current working branch
27+
if(NOT DEFINED GIT_BRANCH)
28+
execute_process(
29+
COMMAND git rev-parse --abbrev-ref HEAD
30+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
31+
OUTPUT_VARIABLE GIT_BRANCH
32+
OUTPUT_STRIP_TRAILING_WHITESPACE
33+
)
34+
endif()
35+
36+
project(gm_pgsqloo
37+
VERSION 1.0.0
38+
LANGUAGES C CXX
39+
HOMEPAGE_URL "https://github.com/Pika-Software/gm_pgsqloo"
40+
)
41+
42+
# Enable -fPIC flag
43+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
44+
45+
# Require C++ 20
46+
set(CMAKE_CXX_STANDARD 20)
47+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
48+
set(CMAKE_CXX_EXTENSIONS ON)
49+
50+
# Enable multithreaded compilation on Windows
51+
if(MSVC)
52+
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" "/MP")
53+
endif()
54+
55+
find_package(PostgreSQL REQUIRED)
56+
find_package(GarrysmodCommon REQUIRED)
57+
58+
add_subdirectory(source)

CMakePresets.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"version": 2,
3+
"configurePresets": [
4+
{
5+
"name": "vcpkg",
6+
"generator": "Ninja",
7+
"binaryDir": "${sourceDir}/build",
8+
"cacheVariables": {
9+
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
10+
"BUILD_SHARED_LIBS": "OFF"
11+
}
12+
}
13+
]
14+
}

cmake/FindGarrysmodCommon.cmake

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
message(STATUS "Looking for garrysmod_common...")
2+
set(GARRYSMOD_COMMON_PATH "GARRYSMOD_COMMON_NOT_FOUND" CACHE PATH "Path to garrysmod_common (https://github.com/dankmolot/garrysmod_common/tree/master-cmake)")
3+
cmake_path(ABSOLUTE_PATH GARRYSMOD_COMMON_PATH NORMALIZE)
4+
5+
if(NOT IS_DIRECTORY ${GARRYSMOD_COMMON_PATH} OR NOT EXISTS ${GARRYSMOD_COMMON_PATH}/CMakeLists.txt OR ${GARRYSMOD_COMMON_PATH} STREQUAL ${CMAKE_CURRENT_LIST_DIR})
6+
message(FATAL_ERROR "Invalid path to garrysmod_common. Please set valid GARRYSMOD_COMMON_PATH")
7+
endif()
8+
9+
add_subdirectory(${GARRYSMOD_COMMON_PATH} ${CMAKE_BINARY_DIR}/garrysmod_common)
10+
set(GarrysmodCommon_FOUND TRUE)

source/CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
configure_file(config.hpp.in ${CMAKE_BINARY_DIR}/include/config.hpp)
2+
3+
# Include every source file in source directory
4+
file(GLOB_RECURSE SOURCES RELATIVE ${CMAKE_CURRENT_LIST_DIR} *.cpp *.hpp)
5+
source_group(TREE ${CMAKE_CURRENT_LIST_DIR} PREFIX "Sources" FILES ${SOURCES})
6+
7+
add_library(pgsqloo SHARED ${SOURCES})
8+
9+
target_link_libraries(pgsqloo PRIVATE
10+
gmod::common
11+
gmod::helpers
12+
PostgreSQL::PostgreSQL
13+
)
14+
15+
target_include_directories(pgsqloo PRIVATE
16+
${CMAKE_CURRENT_LIST_DIR}
17+
${CMAKE_BINARY_DIR}/include
18+
)
19+
20+
set_gmod_suffix_prefix(pgsqloo)
21+
22+
# Autoinstall
23+
set(AUTOINSTALL "" CACHE PATH "Autoinstall path")
24+
if(IS_DIRECTORY ${AUTOINSTALL})
25+
autoinstall(pgsqloo ${AUTOINSTALL})
26+
endif()

source/config.hpp.in

Whitespace-only changes.

source/connection.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include "pgsqloo.hpp"
2+
3+
using namespace pgsqloo;
4+
5+
std::vector<Connection*> pgsqloo::connections = {};
6+
7+
struct ConnectionEvent {
8+
PGconnPtr conn;
9+
GLua::AutoReference callback;
10+
PostgresPollingStatusType status = PGRES_POLLING_WRITING;
11+
bool is_reset = false;
12+
};
13+
14+
std::vector<ConnectionEvent> pending_connections = {};
15+
16+
inline bool socket_is_ready(PGconn* conn, PostgresPollingStatusType status) {
17+
if (status == PGRES_POLLING_READING || status == PGRES_POLLING_WRITING) {
18+
auto socket = check_socket_status(conn);
19+
return socket.failed ||
20+
((status == PGRES_POLLING_READING && socket.read_ready) ||
21+
(status == PGRES_POLLING_WRITING && socket.write_ready));
22+
}
23+
return true;
24+
}
25+
26+
void pgsqloo::connect(GLua::ILuaInterface* lua, std::string_view url,
27+
GLua::AutoReference&& callback) {
28+
auto conn = PGconnPtr(PQconnectStart(url.data()), &PQfinish);
29+
auto conn_ptr = conn.get();
30+
31+
if (!conn) {
32+
// funnily enough, this probably will instead throw a std::bad_alloc
33+
throw std::runtime_error("failed to allocate connection");
34+
}
35+
36+
if (PQstatus(conn_ptr) == CONNECTION_BAD ||
37+
PQsetnonblocking(conn_ptr, 1) != 0) {
38+
throw std::runtime_error(PQerrorMessage(conn_ptr));
39+
}
40+
41+
auto event = ConnectionEvent{std::move(conn), std::move(callback)};
42+
pending_connections.push_back(std::move(event));
43+
}
44+
45+
// returns true if we finished polling
46+
// returns false if we need to poll again
47+
inline bool poll_pending_connection(GLua::ILuaInterface* lua,
48+
ConnectionEvent& event) {
49+
if (!socket_is_ready(event.conn.get(), event.status)) {
50+
lua->Msg("socket is not ready (%s)\n",
51+
event.status == PGRES_POLLING_READING ? "reading" : "writing");
52+
return false;
53+
}
54+
55+
// TODO: handle reset
56+
57+
event.status = PQconnectPoll(event.conn.get());
58+
lua->Msg("status: %d (%d)\n", event.status, PQstatus(event.conn.get()));
59+
if (event.status == PGRES_POLLING_OK) {
60+
auto state = new Connection{std::move(event.conn)};
61+
62+
lua->CreateTable();
63+
state->lua_table = GLua::AutoReference(lua);
64+
65+
connections.push_back(state);
66+
67+
event.callback.Push();
68+
lua->PushBool(true);
69+
lua->PushUserType(state, connection_meta);
70+
lua->PushMetaTable(connection_meta);
71+
lua->SetMetaTable(-2);
72+
pcall(lua, 2, 0);
73+
74+
return true;
75+
} else if (event.status == PGRES_POLLING_FAILED) {
76+
event.callback.Push();
77+
lua->PushBool(false);
78+
lua->PushString(PQerrorMessage(event.conn.get()));
79+
pcall(lua, 2, 0);
80+
81+
return true;
82+
}
83+
84+
return false;
85+
}
86+
87+
void pgsqloo::process_pending_connections(GLua::ILuaInterface* lua) {
88+
for (auto it = pending_connections.begin();
89+
it != pending_connections.end();) {
90+
auto& event = *it;
91+
92+
if (poll_pending_connection(lua, event)) {
93+
it = pending_connections.erase(it);
94+
} else {
95+
++it;
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)