Integrating SDL3_net Library with CMake on Windows & Ubuntu (Server) Интеграция C++-библиотеки SDL3_net с CMake под Windows и Ubuntu (Сервер)
A step-by-step guide to configuring and integrating the SDL3_net satellite library to build a server in your CMake projects on Windows and Ubuntu. Пошаговое руководство по настройке и интеграции вспомогательной библиотеки SDL3_net для создания сервера в проектах CMake под Windows и Ubuntu.
- Building SDL3 Static Library for Windows (MinGW & CMake) Сборка статической библиотеки SDL3 для Windows (MinGW и CMake)
- Building SDL3 Static Library for Ubuntu (GCC, CMake & Ninja) Сборка статической библиотеки SDL3 для Ubuntu (GCC, CMake и Ninja)
- Building SDL3_net Static Library for Windows (MinGW & CMake) Сборка статической библиотеки SDL3_net для Windows (MinGW и CMake)
- Building SDL3_net Static Library for Ubuntu (GCC, CMake & Ninja) Сборка статической библиотеки SDL3_net для Ubuntu (GCC, CMake и Ninja)
Step 1: Create Project Directory Шаг 1: Создание директории проекта
Create a workspace folder and a new project directory for your server project: Создайте рабочую папку и новую директорию проекта для вашего сервера:
Windows (Command Prompt / PowerShell)
mkdir C:\projects\sdl3-net-server-test
cd C:\projects\sdl3-net-server-test
Ubuntu (Terminal)
mkdir -p ~/projects/sdl3-net-server-test
cd ~/projects/sdl3-net-server-test
Step 2: Create Source Files (main.cpp & CMakeLists.txt) Шаг 2: Создание исходных файлов (main.cpp и CMakeLists.txt)
You can create these files using Notepad / Sublime Text on Windows, or nano in Ubuntu terminal:
Вы можете создать эти файлы через Блокнот / Sublime Text в Windows или с помощью nano в терминале Ubuntu:
Windows (Terminal / Notepad)
notepad main.cpp
notepad CMakeLists.txt
Ubuntu (Terminal / Nano)
nano main.cpp
nano CMakeLists.txt
1A. File Contents for main.cpp (Classic Loop) 1A. Содержимое файла main.cpp (Классический цикл)
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3_net/SDL_net.h>
#include <vector>
int main(int argc, char* argv[]) {
if (!SDL_Init(0)) {
SDL_Log("SDL Initialization failed: %s", SDL_GetError());
return -1;
}
if (!NET_Init()) {
SDL_Log("NET_Init failed: %s", SDL_GetError());
SDL_Quit();
return -1;
}
const Uint16 port = 12345;
NET_Server* server = NET_CreateServer(NULL, port, 0);
if (!server) {
SDL_Log("Failed to create server on port %u: %s", port, SDL_GetError());
NET_Quit();
SDL_Quit();
return -1;
}
SDL_Log("SDL3_net Server listening on port %u...", port);
std::vector<NET_StreamSocket*> clients;
bool running = true;
while (running) {
NET_StreamSocket* new_client = nullptr;
if (NET_AcceptClient(server, &new_client) && new_client) {
SDL_Log("New client connected!");
clients.push_back(new_client);
const char* message = "Hello from SDL3_net Server!\n";
NET_WriteToStreamSocket(new_client, message, (int)SDL_strlen(message));
}
char buffer[512];
for (auto it = clients.begin(); it != clients.end();) {
NET_StreamSocket* client = *it;
bool client_active = true;
while (true) {
int bytes_read = NET_ReadFromStreamSocket(client, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
SDL_Log("Received message: %s", buffer);
} else if (bytes_read == 0) {
break; // No more data available
} else {
SDL_Log("Client disconnected or socket error.");
NET_DestroyStreamSocket(client);
it = clients.erase(it);
client_active = false;
break;
}
}
if (client_active) {
++it;
}
}
SDL_Delay(10);
}
for (auto* client : clients) {
NET_DestroyStreamSocket(client);
}
clients.clear();
NET_DestroyServer(server);
NET_Quit();
SDL_Quit();
return 0;
}
1B. File Contents for main.cpp (SDL3 Callbacks API) 1B. Содержимое файла main.cpp (SDL3 Callbacks API)
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3_net/SDL_net.h>
#include <vector>
struct AppState {
NET_Server* server = nullptr;
std::vector<NET_StreamSocket*> clients;
};
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
if (!SDL_Init(0)) {
SDL_Log("SDL Initialization failed: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!NET_Init()) {
SDL_Log("NET_Init failed: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
AppState* state = new AppState();
*appstate = state;
const Uint16 port = 12345;
state->server = NET_CreateServer(NULL, port, 0);
if (!state->server) {
SDL_Log("Failed to create server on port %u: %s", port, SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_Log("SDL3_net Server listening on port %u (Callbacks API)...", port);
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS;
}
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void* appstate) {
auto* state = static_cast<AppState*>(appstate);
NET_StreamSocket* new_client = nullptr;
if (NET_AcceptClient(state->server, &new_client) && new_client) {
SDL_Log("New client connected!");
state->clients.push_back(new_client);
const char* message = "Hello from SDL3_net Server!\n";
NET_WriteToStreamSocket(new_client, message, (int)SDL_strlen(message));
}
char buffer[512];
for (auto it = state->clients.begin(); it != state->clients.end();) {
NET_StreamSocket* client = *it;
bool client_active = true;
while (true) {
int bytes_read = NET_ReadFromStreamSocket(client, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
SDL_Log("Received message: %s", buffer);
} else if (bytes_read == 0) {
break; // No more data available
} else {
SDL_Log("Client disconnected or socket error.");
NET_DestroyStreamSocket(client);
it = state->clients.erase(it);
client_active = false;
break;
}
}
if (client_active) {
++it;
}
}
SDL_Delay(10);
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
if (appstate) {
auto* state = static_cast<AppState*>(appstate);
for (auto* client : state->clients) {
NET_DestroyStreamSocket(client);
}
state->clients.clear();
if (state->server) {
NET_DestroyServer(state->server);
}
delete state;
}
NET_Quit();
SDL_Quit();
}
Option 1A uses the traditional main() loop. Option 1B uses the new SDL3 recommended Callbacks API (via SDL_MAIN_USE_CALLBACKS). Note that when using SDL_MAIN_USE_CALLBACKS, headers like <SDL3_net/SDL_net.h> should be included after setting up application definitions.
Вариант 1A использует традиционный цикл main(). Вариант 1B использует новый рекомендуемый в SDL3 Callbacks API (через SDL_MAIN_USE_CALLBACKS). Обратите внимание: при использовании SDL_MAIN_USE_CALLBACKS подключение заголовков, таких как <SDL3_net/SDL_net.h>, следует выполнять после установки мета-макросов.
2. File Contents for CMakeLists.txt 2. Содержимое файла CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
# Default to Debug if CMAKE_BUILD_TYPE is not set via command line
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()
project(sdl3_net_server_test CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# -----------------------------------------------------------------------------
# Locate SDL3 and SDL3_net Packages via Custom Paths
# -----------------------------------------------------------------------------
if(WIN32)
set(SDL3_DIR "C:/libs/sdl3-3.4.16-mingw/lib/cmake/SDL3")
set(SDL3_net_DIR "C:/libs/sdl3-net-3.2.0-mingw/lib/cmake/SDL3_net")
else()
set(SDL3_DIR "$ENV{HOME}/libs/sdl3-3.4.16-ubuntu/lib/cmake/SDL3")
set(SDL3_net_DIR "$ENV{HOME}/libs/sdl3-net-3.2.0-ubuntu/lib/cmake/SDL3_net")
endif()
find_package(SDL3 REQUIRED CONFIG)
find_package(SDL3_net REQUIRED CONFIG)
# -----------------------------------------------------------------------------
# Target Definition
# -----------------------------------------------------------------------------
set(PROJECT_SOURCES
main.cpp
)
add_executable(app ${PROJECT_SOURCES})
# Link SDL3 and SDL3_net statically (automatically brings in include dirs and
# socket system libraries like ws2_32 / iphlpapi on Windows)
target_link_libraries(app PRIVATE SDL3::SDL3-static SDL3_net::SDL3_net-static)
# -----------------------------------------------------------------------------
# Platform-Specific Compiler & Linker Flags
# -----------------------------------------------------------------------------
if(WIN32)
target_compile_options(app PRIVATE -Os)
target_link_options(app PRIVATE -static -s -static-libgcc -mconsole)
endif()
Step 3: Create Build & Execution Scripts Шаг 3: Создание скриптов сборки и запуска
Windows (MinGW / Batch Scripts)
config-exe.bat
cmake -G Ninja -S . -B dist/win
build-exe.bat
cmake --build dist/win
run-exe.bat
@echo off
.\dist\win\app.exe
Run the scripts sequentially in Command Prompt or PowerShell: Последовательно запустите скрипты в командной строке или PowerShell:
config-exe.bat
build-exe.bat
run-exe.bat
Ubuntu (Bash Scripts)
config-bin.sh
#!/usr/bin/env bash
set -e
cmake -G Ninja -S . -B dist/linux
build-bin.sh
#!/usr/bin/env bash
set -e
cmake --build dist/linux
run-bin.sh
#!/usr/bin/env bash
set -e
./dist/linux/app
Grant execute permissions on Ubuntu before running: Предоставьте права на выполнение в Ubuntu перед запуском:
chmod +x config-bin.sh build-bin.sh run-bin.sh
./config-bin.sh
./build-bin.sh
./run-bin.sh
Step 4: Expected Output Шаг 4: Ожидаемый результат
Upon running the server, the console will print a message indicating it is waiting for client connections: После запуска сервера в консоли появится сообщение о том, что он ожидает подключения клиентов:
INFO: SDL3_net Server listening on port 12345...
or (if using the Callbacks API version): или (если используется версия с Callbacks API):
SDL3_net Server listening on port 12345 (Callbacks API)...
Support My Work Поддержать проект
If these tutorials helped you, consider buying me a coffee! Если эти туториалы вам помогли, вы можете поддержать автора.
Sberbank (Russia only) Сбербанк (только для РФ)
Direct transfer via phone number (Russia only) Перевод по номеру телефона (только для РФ)
USDT TRC20
Support via Cryptocurrency Поддержка криптовалютой