1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-05-13 22:06:58 +02:00

Initial version of global lobby server available in client

This commit is contained in:
Ivan Savenko 2023-11-11 16:43:58 +02:00
parent c473e65b0f
commit c2c43602ea
23 changed files with 1062 additions and 0 deletions

View File

@ -89,6 +89,10 @@ if(NOT APPLE_IOS AND NOT ANDROID)
option(ENABLE_MONOLITHIC_INSTALL "Install everything in single directory on Linux and Mac" OFF)
endif()
if(NOT APPLE_IOS AND NOT ANDROID)
option(ENABLE_LOBBY "Enable compilation of lobby server" ON)
endif()
option(ENABLE_CCACHE "Speed up recompilation by caching previous compilations" OFF)
if(ENABLE_CCACHE)
find_program(CCACHE ccache REQUIRED)
@ -475,6 +479,10 @@ if(TARGET SDL2_ttf::SDL2_ttf)
add_library(SDL2::TTF ALIAS SDL2_ttf::SDL2_ttf)
endif()
if(ENABLE_LOBBY)
find_package(SQLite3 REQUIRED)
endif()
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
# Widgets finds its own dependencies (QtGui and QtCore).
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
@ -622,6 +630,9 @@ endif()
if(ENABLE_EDITOR)
add_subdirectory(mapeditor)
endif()
if(ENABLE_LOBBY)
add_subdirectory(lobby)
endif()
add_subdirectory(client)
add_subdirectory(server)
if(ENABLE_TEST)

View File

@ -95,6 +95,8 @@ set(client_SRCS
renderSDL/ScreenHandler.cpp
renderSDL/SDL_Extensions.cpp
serverLobby/LobbyWindow.cpp
widgets/Buttons.cpp
widgets/CArtifactHolder.cpp
widgets/CComponent.cpp
@ -270,6 +272,8 @@ set(client_HEADERS
renderSDL/SDL_Extensions.h
renderSDL/SDL_PixelAccess.h
serverLobby/LobbyWindow.h
widgets/Buttons.h
widgets/CArtifactHolder.h
widgets/CComponent.h

View File

@ -129,6 +129,9 @@ void InterfaceObjectConfigurable::build(const JsonNode &config)
for(const auto & item : items->Vector())
addWidget(item["name"].String(), buildWidget(item));
pos.w = config["width"].Integer();
pos.h = config["height"].Integer();
}
void InterfaceObjectConfigurable::addConditional(const std::string & name, bool active)

View File

@ -24,6 +24,7 @@
#include "../gui/Shortcut.h"
#include "../gui/WindowHandler.h"
#include "../render/Canvas.h"
#include "../serverLobby/LobbyWindow.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"
#include "../widgets/MiscWidgets.h"
@ -459,9 +460,17 @@ CMultiMode::CMultiMode(ESelectionScreen ScreenType)
buttonHotseat = std::make_shared<CButton>(Point(373, 78), AnimationPath::builtin("MUBHOT.DEF"), CGI->generaltexth->zelp[266], std::bind(&CMultiMode::hostTCP, this));
buttonHost = std::make_shared<CButton>(Point(373, 78 + 57 * 1), AnimationPath::builtin("MUBHOST.DEF"), CButton::tooltip(CGI->generaltexth->translate("vcmi.mainMenu.hostTCP"), ""), std::bind(&CMultiMode::hostTCP, this));
buttonJoin = std::make_shared<CButton>(Point(373, 78 + 57 * 2), AnimationPath::builtin("MUBJOIN.DEF"), CButton::tooltip(CGI->generaltexth->translate("vcmi.mainMenu.joinTCP"), ""), std::bind(&CMultiMode::joinTCP, this));
buttonLobby = std::make_shared<CButton>(Point(373, 78 + 57 * 4), AnimationPath::builtin("MUBONL.DEF"), CGI->generaltexth->zelp[265], std::bind(&CMultiMode::openLobby, this));
buttonCancel = std::make_shared<CButton>(Point(373, 424), AnimationPath::builtin("MUBCANC.DEF"), CGI->generaltexth->zelp[288], [=](){ close();}, EShortcut::GLOBAL_CANCEL);
}
void CMultiMode::openLobby()
{
close();
GH.windows().createAndPushWindow<LobbyWindow>();
}
void CMultiMode::hostTCP()
{
auto savedScreenType = screenType;

View File

@ -86,12 +86,14 @@ public:
std::shared_ptr<CPicture> picture;
std::shared_ptr<CTextInput> playerName;
std::shared_ptr<CButton> buttonHotseat;
std::shared_ptr<CButton> buttonLobby;
std::shared_ptr<CButton> buttonHost;
std::shared_ptr<CButton> buttonJoin;
std::shared_ptr<CButton> buttonCancel;
std::shared_ptr<CGStatusBar> statusBar;
CMultiMode(ESelectionScreen ScreenType);
void openLobby();
void hostTCP();
void joinTCP();
std::string getPlayerName();

View File

@ -0,0 +1,40 @@
/*
* LobbyWindow.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "LobbyWindow.h"
#include "../gui/CGuiHandler.h"
#include "../gui/WindowHandler.h"
void LobbyClient::onPacketReceived(const std::vector<uint8_t> & message)
{
}
LobbyWidget::LobbyWidget()
{
addCallback("closeWindow", [](int) { GH.windows().popWindows(1); });
const JsonNode config(JsonPath::builtin("config/widgets/lobbyWindow.json"));
build(config);
}
LobbyWindow::LobbyWindow():
CWindowObject(BORDERED)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
widget = std::make_shared<LobbyWidget>();
pos = widget->pos;
center();
connection = std::make_shared<LobbyClient>();
connection->start("127.0.0.1", 30303);
}

View File

@ -0,0 +1,37 @@
/*
* LobbyWindow.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../gui/InterfaceObjectConfigurable.h"
#include "../windows/CWindowObject.h"
#include "../../lib/network/NetworkClient.h"
class LobbyWidget : public InterfaceObjectConfigurable
{
public:
LobbyWidget();
};
class LobbyClient : public NetworkClient
{
void onPacketReceived(const std::vector<uint8_t> & message) override;
public:
LobbyClient() = default;
};
class LobbyWindow : public CWindowObject
{
std::shared_ptr<LobbyWidget> widget;
std::shared_ptr<LobbyClient> connection;
public:
LobbyWindow();
};

View File

@ -124,6 +124,10 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/modding/IdentifierStorage.cpp
${MAIN_LIB_DIR}/modding/ModUtility.cpp
${MAIN_LIB_DIR}/network/NetworkClient.cpp
${MAIN_LIB_DIR}/network/NetworkConnection.cpp
${MAIN_LIB_DIR}/network/NetworkServer.cpp
${MAIN_LIB_DIR}/networkPacks/NetPacksLib.cpp
${MAIN_LIB_DIR}/pathfinder/CGPathNode.cpp
@ -471,6 +475,11 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/modding/ModUtility.h
${MAIN_LIB_DIR}/modding/ModVerificationInfo.h
${MAIN_LIB_DIR}/network/NetworkClient.h
${MAIN_LIB_DIR}/network/NetworkConnection.h
${MAIN_LIB_DIR}/network/NetworkDefines.h
${MAIN_LIB_DIR}/network/NetworkServer.h
${MAIN_LIB_DIR}/networkPacks/ArtifactLocation.h
${MAIN_LIB_DIR}/networkPacks/BattleChanges.h
${MAIN_LIB_DIR}/networkPacks/Component.h

View File

@ -0,0 +1,186 @@
{
"customTypes" : {
"labelTitleMain" : {
"type": "label",
"font": "big",
"alignment": "left",
"color": "yellow"
},
"labelTitle" : {
"type": "label",
"font": "small",
"alignment": "left",
"color": "yellow"
},
"backgroundTexture" : {
"type": "texture",
"font": "tiny",
"color" : "blue",
"image": "DIBOXBCK"
},
"areaFilled":{
"type": "transparentFilledRectangle",
"color": [0, 0, 0, 75],
"colorLine": [64, 80, 128, 255]
}
},
"width": 1024,
"height": 600,
"items":
[
{
"type": "backgroundTexture",
"rect": {"w": 1024, "h": 600}
},
{
"type": "areaFilled",
"rect": {"x": 5, "y": 5, "w": 250, "h": 40}
},
{
"type": "labelTitleMain",
"position": {"x": 15, "y": 10},
"text" : "Player Name"
},
{
"type": "areaFilled",
"rect": {"x": 5, "y": 50, "w": 250, "h": 150}
},
{
"type": "labelTitle",
"position": {"x": 15, "y": 53},
"text" : "Match Filter"
},
{
"type": "areaFilled",
"rect": {"x": 5, "y": 210, "w": 250, "h": 310}
},
{
"type": "labelTitle",
"position": {"x": 15, "y": 213},
"text" : "Match List"
},
{
"type": "areaFilled",
"rect": {"x": 270, "y": 50, "w": 150, "h": 540}
},
{
"type": "labelTitle",
"position": {"x": 280, "y": 53},
"text" : "Channel List"
},
{
"type": "areaFilled",
"rect": {"x": 430, "y": 50, "w": 430, "h": 515}
},
{
"type": "labelTitle",
"position": {"x": 440, "y": 53},
"text" : "Game Chat"
},
{
"type": "areaFilled",
"rect": {"x": 430, "y": 565, "w": 395, "h": 25}
},
{
"type": "labelTitle",
"position": {"x": 440, "y": 568},
"text" : "Enter Message"
},
{
"type": "areaFilled",
"rect": {"x": 870, "y": 50, "w": 150, "h": 540}
},
{
"type": "labelTitle",
"position": {"x": 880, "y": 53},
"text" : "Player List"
},
{
"type": "button",
"position": {"x": 940, "y": 10},
"image": "settingsWindow/button80",
"help": "core.help.288",
"callback": "closeWindow",
"hotkey": "globalReturn",
"items":
[
{
"type": "label",
"font": "medium",
"alignment": "center",
"color": "yellow",
"text": "Exit"
}
]
},
{
"type": "button",
"position": {"x": 828, "y": 565},
"image": "settingsWindow/button32",
"help": "core.help.288",
"callback": "closeWindow",
"hotkey": "globalReturn",
"items":
[
{
"type": "label",
"font": "medium",
"alignment": "center",
"color": "yellow",
"text": ">"
}
]
},
{
"type": "button",
"position": {"x": 10, "y": 520},
"image": "settingsWindow/button190",
"help": "core.help.288",
"callback": "closeWindow",
"hotkey": "globalReturn",
"items":
[
{
"type": "label",
"font": "medium",
"alignment": "center",
"color": "yellow",
"text": "Start Public Game"
}
]
},
{
"type": "button",
"position": {"x": 10, "y": 555},
"image": "settingsWindow/button190",
"help": "core.help.288",
"callback": "closeWindow",
"hotkey": "globalReturn",
"items":
[
{
"type": "label",
"font": "medium",
"alignment": "center",
"color": "yellow",
"text": "Start Private Game"
}
]
},
]
}

View File

@ -0,0 +1,47 @@
/*
* NetworkClient.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "NetworkClient.h"
#include "NetworkConnection.h"
VCMI_LIB_NAMESPACE_BEGIN
NetworkClient::NetworkClient()
: io(new NetworkService)
, socket(new NetworkSocket(*io))
// , timer(new NetworkTimer(*io))
{
}
void NetworkClient::start(const std::string & host, uint16_t port)
{
boost::asio::ip::tcp::resolver resolver(*io);
auto endpoints = resolver.resolve(host, std::to_string(port));
boost::asio::async_connect(*socket, endpoints, std::bind(&NetworkClient::onConnected, this, _1));
}
void NetworkClient::onConnected(const boost::system::error_code & ec)
{
connection = std::make_shared<NetworkConnection>(socket);
connection->start();
}
void NetworkClient::run()
{
io->run();
}
void NetworkClient::sendPacket(const std::vector<uint8_t> & message)
{
connection->sendPacket(message);
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,37 @@
/*
* NetworkClient.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "NetworkDefines.h"
VCMI_LIB_NAMESPACE_BEGIN
class NetworkConnection;
class DLL_LINKAGE NetworkClient : boost::noncopyable
{
std::shared_ptr<NetworkService> io;
std::shared_ptr<NetworkSocket> socket;
std::shared_ptr<NetworkConnection> connection;
std::shared_ptr<NetworkTimer> timer;
void onConnected(const boost::system::error_code & ec);
protected:
virtual void onPacketReceived(const std::vector<uint8_t> & message) = 0;
public:
NetworkClient();
virtual ~NetworkClient() = default;
void start(const std::string & host, uint16_t port);
void sendPacket(const std::vector<uint8_t> & message);
void run();
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,97 @@
/*
* NetworkConnection.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "NetworkConnection.h"
VCMI_LIB_NAMESPACE_BEGIN
NetworkConnection::NetworkConnection(const std::shared_ptr<NetworkSocket> & socket)
: socket(socket)
{
}
void NetworkConnection::start()
{
boost::asio::async_read(*socket,
readBuffer,
boost::asio::transfer_exactly(messageHeaderSize),
std::bind(&NetworkConnection::onHeaderReceived,this, _1));
}
void NetworkConnection::onHeaderReceived(const boost::system::error_code & ec)
{
uint32_t messageSize = readPacketSize(ec);
boost::asio::async_read(*socket,
readBuffer,
boost::asio::transfer_exactly(messageSize),
std::bind(&NetworkConnection::onPacketReceived,this, _1, messageSize));
}
uint32_t NetworkConnection::readPacketSize(const boost::system::error_code & ec)
{
if (ec)
{
throw std::runtime_error("Connection aborted!");
}
if (readBuffer.size() < messageHeaderSize)
{
throw std::runtime_error("Failed to read header!");
}
std::istream istream(&readBuffer);
uint32_t messageSize;
istream.read(reinterpret_cast<char *>(&messageSize), messageHeaderSize);
if (messageSize > messageMaxSize)
{
throw std::runtime_error("Invalid packet size!");
}
return messageSize;
}
void NetworkConnection::onPacketReceived(const boost::system::error_code & ec, uint32_t expectedPacketSize)
{
if (ec)
{
throw std::runtime_error("Connection aborted!");
}
if (readBuffer.size() < expectedPacketSize)
{
throw std::runtime_error("Failed to read header!");
}
std::vector<uint8_t> message;
message.resize(expectedPacketSize);
std::istream istream(&readBuffer);
istream.read(reinterpret_cast<char *>(message.data()), messageHeaderSize);
start();
}
void NetworkConnection::sendPacket(const std::vector<uint8_t> & message)
{
NetworkBuffer writeBuffer;
std::ostream ostream(&writeBuffer);
uint32_t messageSize = message.size();
ostream.write(reinterpret_cast<const char *>(&messageSize), messageHeaderSize);
ostream.write(reinterpret_cast<const char *>(message.data()), message.size());
boost::asio::write(*socket, writeBuffer );
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,36 @@
/*
* NetworkConnection.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "NetworkDefines.h"
VCMI_LIB_NAMESPACE_BEGIN
class DLL_LINKAGE NetworkConnection : boost::noncopyable
{
static const int messageHeaderSize = sizeof(uint32_t);
static const int messageMaxSize = 1024;
std::shared_ptr<NetworkSocket> socket;
NetworkBuffer readBuffer;
void onHeaderReceived(const boost::system::error_code & ec);
void onPacketReceived(const boost::system::error_code & ec, uint32_t expectedPacketSize);
uint32_t readPacketSize(const boost::system::error_code & ec);
public:
NetworkConnection(const std::shared_ptr<NetworkSocket> & socket);
void start();
void sendPacket(const std::vector<uint8_t> & message);
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,22 @@
/*
* NetworkDefines.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include <boost/asio.hpp>
VCMI_LIB_NAMESPACE_BEGIN
using NetworkService = boost::asio::io_service;
using NetworkSocket = boost::asio::basic_stream_socket<boost::asio::ip::tcp>;
using NetworkAcceptor = boost::asio::basic_socket_acceptor<boost::asio::ip::tcp>;
using NetworkBuffer = boost::asio::streambuf;
using NetworkTimer = boost::asio::steady_timer;
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,63 @@
/*
* NetworkServer.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "NetworkServer.h"
#include "NetworkConnection.h"
VCMI_LIB_NAMESPACE_BEGIN
void NetworkServer::start(uint16_t port)
{
io = std::make_shared<boost::asio::io_service>();
acceptor = std::make_shared<NetworkAcceptor>(*io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port));
startAsyncAccept();
}
void NetworkServer::startAsyncAccept()
{
std::shared_ptr<NetworkSocket> upcomingConnection = std::make_shared<NetworkSocket>(*io);
acceptor->async_accept(*upcomingConnection, std::bind(&NetworkServer::connectionAccepted, this, upcomingConnection, _1));
}
void NetworkServer::run()
{
io->run();
}
void NetworkServer::connectionAccepted(std::shared_ptr<NetworkSocket> upcomingConnection, const boost::system::error_code & ec)
{
if(ec)
{
logNetwork->info("Something wrong during accepting: %s", ec.message());
return;
}
try
{
logNetwork->info("We got a new connection! :)");
auto connection = std::make_shared<NetworkConnection>(upcomingConnection);
connections.insert(connection);
connection->start();
}
catch(std::exception & e)
{
logNetwork->error("Failure processing new connection! %s", e.what());
}
startAsyncAccept();
}
void NetworkServer::sendPacket(const std::shared_ptr<NetworkConnection> & connection, const std::vector<uint8_t> & message)
{
connection->sendPacket(message);
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,39 @@
/*
* NetworkServer.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "NetworkDefines.h"
VCMI_LIB_NAMESPACE_BEGIN
class NetworkConnection;
class DLL_LINKAGE NetworkServer : boost::noncopyable
{
std::shared_ptr<NetworkService> io;
std::shared_ptr<NetworkAcceptor> acceptor;
std::set<std::shared_ptr<NetworkConnection>> connections;
void connectionAccepted(std::shared_ptr<NetworkSocket>, const boost::system::error_code & ec);
void startAsyncAccept();
protected:
virtual void onNewConnection(std::shared_ptr<NetworkConnection>) = 0;
virtual void onPacketReceived(std::shared_ptr<NetworkConnection>, const std::vector<uint8_t> & message) = 0;
void sendPacket(const std::shared_ptr<NetworkConnection> &, const std::vector<uint8_t> & message);
public:
virtual ~NetworkServer() = default;
void start(uint16_t port);
void run();
};
VCMI_LIB_NAMESPACE_END

39
lobby/CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
set(lobby_SRCS
StdInc.cpp
LobbyServer.cpp
SQLiteConnection.cpp
)
set(lobby_HEADERS
StdInc.h
LobbyServer.h
SQLiteConnection.h
)
assign_source_group(${lobby_SRCS} ${lobby_HEADERS})
add_executable(vcmilobby ${lobby_SRCS} ${lobby_HEADERS})
set(lobby_LIBS vcmi)
if(CMAKE_SYSTEM_NAME MATCHES FreeBSD OR HAIKU)
set(lobby_LIBS execinfo ${lobby_LIBS})
endif()
target_link_libraries(vcmilobby PRIVATE ${lobby_LIBS} ${SQLite3_LIBRARIES})
target_include_directories(vcmilobby PRIVATE ${SQLite3_INCLUDE_DIRS})
target_include_directories(vcmilobby PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
if(WIN32)
set_target_properties(vcmilobby
PROPERTIES
OUTPUT_NAME "VCMI_lobby"
PROJECT_LABEL "VCMI_lobby"
)
endif()
vcmi_set_output_dir(vcmilobby "")
enable_pch(vcmilobby)
install(TARGETS vcmilobby DESTINATION ${BIN_DIR})

45
lobby/LobbyServer.cpp Normal file
View File

@ -0,0 +1,45 @@
/*
* LobbyServer.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "LobbyServer.h"
#include "SQLiteConnection.h"
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
static const std::string DATABASE_PATH = "~/vcmi.db";
static const int LISTENING_PORT = 30303;
//static const std::string SERVER_NAME = GameConstants::VCMI_VERSION + " (server)";
//static const std::string SERVER_UUID = boost::uuids::to_string(boost::uuids::random_generator()());
void LobbyServer::onNewConnection(std::shared_ptr<NetworkConnection>)
{
}
void LobbyServer::onPacketReceived(std::shared_ptr<NetworkConnection>, const std::vector<uint8_t> & message)
{
}
LobbyServer::LobbyServer()
{
database = SQLiteInstance::open(DATABASE_PATH, true);
}
int main(int argc, const char * argv[])
{
LobbyServer server;
server.start(LISTENING_PORT);
server.run();
}

24
lobby/LobbyServer.h Normal file
View File

@ -0,0 +1,24 @@
/*
* LobbyServer.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../lib/network/NetworkServer.h"
class SQLiteInstance;
class LobbyServer : public NetworkServer
{
std::unique_ptr<SQLiteInstance> database;
void onNewConnection(std::shared_ptr<NetworkConnection>) override;
void onPacketReceived(std::shared_ptr<NetworkConnection>, const std::vector<uint8_t> & message) override;
public:
LobbyServer();
};

197
lobby/SQLiteConnection.cpp Normal file
View File

@ -0,0 +1,197 @@
/*
* SQLiteConnection.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "SQLiteConnection.h"
#include <sqlite3.h>
static void on_sqlite_error( sqlite3 * connection, [[maybe_unused]] int result )
{
if ( result != SQLITE_OK )
{
printf( "sqlite error: %s\n", sqlite3_errmsg( connection ) );
}
assert( result == SQLITE_OK );
}
SQLiteStatement::SQLiteStatement( SQLiteInstance & instance, sqlite3_stmt * statement ):
m_instance( instance ),
m_statement( statement )
{ }
SQLiteStatement::~SQLiteStatement( )
{
int result = sqlite3_finalize( m_statement );
on_sqlite_error( m_instance.m_connection, result );
}
bool SQLiteStatement::execute( )
{
int result = sqlite3_step( m_statement );
switch ( result )
{
case SQLITE_DONE:
return false;
case SQLITE_ROW:
return true;
default:
on_sqlite_error( m_instance.m_connection, result );
return false;
}
}
void SQLiteStatement::reset( )
{
int result = sqlite3_reset( m_statement );
on_sqlite_error( m_instance.m_connection, result );
}
void SQLiteStatement::clear()
{
int result = sqlite3_clear_bindings(m_statement);
on_sqlite_error(m_instance.m_connection, result);
}
void SQLiteStatement::setBindSingle( size_t index, double const & value )
{
int result = sqlite3_bind_double( m_statement, static_cast<int>(index), value );
on_sqlite_error( m_instance.m_connection, result );
}
void SQLiteStatement::setBindSingle(size_t index, uint8_t const & value)
{
int result = sqlite3_bind_int(m_statement, static_cast<int>(index), value);
on_sqlite_error(m_instance.m_connection, result);
}
void SQLiteStatement::setBindSingle(size_t index, uint16_t const & value)
{
int result = sqlite3_bind_int(m_statement, static_cast<int>(index), value);
on_sqlite_error(m_instance.m_connection, result);
}
void SQLiteStatement::setBindSingle(size_t index, uint32_t const & value)
{
int result = sqlite3_bind_int(m_statement, static_cast<int>(index), value);
on_sqlite_error(m_instance.m_connection, result);
}
void SQLiteStatement::setBindSingle( size_t index, int32_t const & value )
{
int result = sqlite3_bind_int( m_statement, static_cast<int>( index ), value );
on_sqlite_error( m_instance.m_connection, result );
}
void SQLiteStatement::setBindSingle( size_t index, int64_t const & value )
{
int result = sqlite3_bind_int64( m_statement, static_cast<int>( index ), value );
on_sqlite_error( m_instance.m_connection, result );
}
void SQLiteStatement::setBindSingle( size_t index, std::string const & value )
{
int result = sqlite3_bind_text( m_statement, static_cast<int>( index ), value.data(), static_cast<int>( value.size() ), SQLITE_STATIC );
on_sqlite_error( m_instance.m_connection, result );
}
void SQLiteStatement::setBindSingle( size_t index, char const * value )
{
int result = sqlite3_bind_text( m_statement, static_cast<int>( index ), value, -1, SQLITE_STATIC );
on_sqlite_error( m_instance.m_connection, result );
}
void SQLiteStatement::getColumnSingle( size_t index, double & value )
{
value = sqlite3_column_double( m_statement, static_cast<int>( index ) );
}
void SQLiteStatement::getColumnSingle( size_t index, uint8_t & value )
{
value = static_cast<uint8_t>(sqlite3_column_int( m_statement, static_cast<int>( index ) ));
}
void SQLiteStatement::getColumnSingle(size_t index, uint16_t & value)
{
value = static_cast<uint16_t>(sqlite3_column_int(m_statement, static_cast<int>(index)));
}
void SQLiteStatement::getColumnSingle( size_t index, int32_t & value )
{
value = sqlite3_column_int( m_statement, static_cast<int>( index ) );
}
void SQLiteStatement::getColumnSingle(size_t index, uint32_t & value)
{
value = sqlite3_column_int(m_statement, static_cast<int>(index));
}
void SQLiteStatement::getColumnSingle( size_t index, int64_t & value )
{
value = sqlite3_column_int64( m_statement, static_cast<int>( index ) );
}
void SQLiteStatement::getColumnSingle( size_t index, std::string & value )
{
auto value_raw = sqlite3_column_text(m_statement, static_cast<int>(index));
value = reinterpret_cast<char const*>( value_raw );
}
void SQLiteStatement::getColumnBlob(size_t index, std::byte * value, [[maybe_unused]] size_t capacity)
{
auto * blob_data = sqlite3_column_blob(m_statement, static_cast<int>(index));
size_t blob_size = sqlite3_column_bytes(m_statement, static_cast<int>(index));
assert(blob_size < capacity);
std::copy_n(static_cast<std::byte const*>(blob_data), blob_size, value);
value[blob_size] = std::byte(0);
}
SQLiteInstancePtr SQLiteInstance::open( std::string const & db_path, bool allow_write)
{
int flags = allow_write ? (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) : SQLITE_OPEN_READONLY;
sqlite3 * connection;
int result = sqlite3_open_v2( db_path.c_str( ), &connection, flags, nullptr );
on_sqlite_error( connection, result );
assert(result == SQLITE_OK);
if ( result == SQLITE_OK )
return SQLiteInstancePtr( new SQLiteInstance( connection ) );
sqlite3_close( connection );
return SQLiteInstancePtr( );
}
SQLiteInstance::SQLiteInstance( sqlite3 * connection ):
m_connection( connection )
{ }
SQLiteInstance::~SQLiteInstance( )
{
int result = sqlite3_close( m_connection );
on_sqlite_error( m_connection, result );
}
SQLiteStatementPtr SQLiteInstance::prepare( std::string const & sql_text )
{
sqlite3_stmt * statement;
int result = sqlite3_prepare_v2( m_connection, sql_text.data(), static_cast<int>( sql_text.size()), &statement, nullptr );
on_sqlite_error( m_connection, result );
if ( result == SQLITE_OK )
return SQLiteStatementPtr( new SQLiteStatement( *this, statement ) );
sqlite3_finalize( statement );
return SQLiteStatementPtr( );
}

99
lobby/SQLiteConnection.h Normal file
View File

@ -0,0 +1,99 @@
/*
* SQLiteConnection.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
typedef struct sqlite3 sqlite3;
typedef struct sqlite3_stmt sqlite3_stmt;
class SQLiteInstance;
class SQLiteStatement;
using SQLiteInstancePtr = std::unique_ptr< SQLiteInstance >;
using SQLiteStatementPtr = std::unique_ptr< SQLiteStatement >;
class SQLiteStatement : boost::noncopyable
{
public:
friend class SQLiteInstance;
bool execute( );
void reset( );
void clear( );
~SQLiteStatement();
template<typename ... Args >
void setBinds( Args const & ... args )
{
setBindSingle( 1, args... ); // The leftmost SQL parameter has an index of 1
}
template<typename ... Args >
void getColumns( Args & ... args )
{
getColumnSingle( 0, args... ); // The leftmost column of the result set has the index 0
}
private:
void setBindSingle( size_t index, double const & value );
void setBindSingle( size_t index, uint8_t const & value );
void setBindSingle( size_t index, uint16_t const & value );
void setBindSingle( size_t index, uint32_t const & value );
void setBindSingle( size_t index, int32_t const & value );
void setBindSingle( size_t index, int64_t const & value );
void setBindSingle( size_t index, std::string const & value );
void setBindSingle( size_t index, char const * value );
void getColumnSingle( size_t index, double & value );
void getColumnSingle( size_t index, uint8_t & value );
void getColumnSingle( size_t index, uint16_t & value );
void getColumnSingle( size_t index, uint32_t & value );
void getColumnSingle( size_t index, int32_t & value );
void getColumnSingle( size_t index, int64_t & value );
void getColumnSingle( size_t index, std::string & value );
SQLiteStatement( SQLiteInstance & instance, sqlite3_stmt * statement );
template<typename T, typename ... Args >
void setBindSingle( size_t index, T const & arg, Args const & ... args )
{
setBindSingle( index, arg );
setBindSingle( index + 1, args... );
}
template<typename T, typename ... Args >
void getColumnSingle( size_t index, T & arg, Args & ... args )
{
getColumnSingle( index, arg );
getColumnSingle( index + 1, args... );
}
void getColumnBlob(size_t index, std::byte * value, size_t capacity);
SQLiteInstance & m_instance;
sqlite3_stmt * m_statement;
};
class SQLiteInstance : boost::noncopyable
{
public:
friend class SQLiteStatement;
static SQLiteInstancePtr open(std::string const & db_path, bool allow_write );
~SQLiteInstance( );
SQLiteStatementPtr prepare( std::string const & statement );
private:
SQLiteInstance( sqlite3 * connection );
sqlite3 * m_connection;
};

2
lobby/StdInc.cpp Normal file
View File

@ -0,0 +1,2 @@
// Creates the precompiled header
#include "StdInc.h"

14
lobby/StdInc.h Normal file
View File

@ -0,0 +1,14 @@
/*
* StdInc.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../Global.h"
VCMI_LIB_USING_NAMESPACE