Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ jobs:
test:
runs-on: ubuntu-latest

services:
db:
image: mariadb

env:
MARIADB_USER: forgottenserver
MARIADB_PASSWORD: forgottenserver
MARIADB_DATABASE: forgottenserver
MARIADB_INITDB_SKIP_TZINFO: "yes"
MARIADB_RANDOM_ROOT_PASSWORD: "yes"

options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3

ports:
- 3306:3306

steps:
- uses: actions/checkout@v4

Expand All @@ -16,6 +32,12 @@ jobs:
- name: Run vcpkg
uses: lukka/run-vcpkg@v11

- name: Setup database
run: |
sudo apt update -qq
sudo apt install -yqq mariadb-client
mysql -h 0.0.0.0 -u forgottenserver -pforgottenserver forgottenserver < schema.sql

- name: Build with CMake
uses: lukka/run-cmake@v10
with:
Expand All @@ -24,4 +46,4 @@ jobs:
configurePreset: vcpkg
configurePresetAdditionalArgs: "['-DBUILD_TESTING=ON']"
testPreset: vcpkg
testPresetAdditionalArgs: "['--build-config', 'Debug']"
testPresetAdditionalArgs: "['--build-config', 'Debug', '--output-on-failure']"
29 changes: 17 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ if (DEFINED ENV{VCPKG_DEFAULT_TRIPLET} AND NOT DEFINED VCPKG_TARGET_TRIPLET)
set(VCPKG_TARGET_TRIPLET "$ENV{VCPKG_DEFAULT_TRIPLET}" CACHE STRING "The vcpkg triplet")
endif()

option(HTTP "Enable HTTP support" ON)
if (HTTP)
list(APPEND VCPKG_MANIFEST_FEATURES "http")
endif()

option(BUILD_TESTING "Build unit tests" OFF)
if (BUILD_TESTING)
list(APPEND VCPKG_MANIFEST_FEATURES "unit-tests")
Expand Down Expand Up @@ -71,6 +76,9 @@ if (APPLE)
endif()

set(BOOST_REQUIRED_COMPONENTS system iostreams locale)
if (HTTP)
list(APPEND BOOST_REQUIRED_COMPONENTS json)
endif ()
if (BUILD_TESTING)
list(APPEND BOOST_REQUIRED_COMPONENTS unit_test_framework)
endif ()
Expand All @@ -81,23 +89,26 @@ include_directories(${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${LUA_INCLUDE_D
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(src)
add_executable(tfs ${tfs_MAIN})
target_link_libraries(tfs tfslib)

if (BUILD_TESTING)
message(STATUS "Building unit tests")
enable_testing()
add_subdirectory(src/tests)
endif()

# Option to disable unity builds
option(ENABLE_UNITY_BUILD "Enable unity build" ON)

add_subdirectory(src)
add_executable(tfs ${tfs_MAIN})
target_link_libraries(tfs tfslib)

### INTERPROCEDURAL_OPTIMIZATION ###
cmake_policy(SET CMP0069 NEW)
include(CheckIPOSupported)
check_ipo_supported(RESULT result OUTPUT error)
if (result)
message(STATUS "IPO / LTO enabled")
set(ENABLE_IPO ON)
set_target_properties(tfs PROPERTIES INTERPROCEDURAL_OPTIMIZATION True)
message(STATUS "IPO / LTO enabled")
else ()
message(STATUS "IPO / LTO not supported: <${error}>")
endif ()
Expand All @@ -118,10 +129,4 @@ if(NOT SKIP_GIT)
endif()
### END Git Version ###

# Option to disable unity builds
option(ENABLE_UNITY_BUILD "Enable unity build" ON)
if(ENABLE_UNITY_BUILD)
set_target_properties(tfslib PROPERTIES UNITY_BUILD ON)
endif()

target_precompile_headers(tfs PUBLIC src/otpch.h)
2 changes: 2 additions & 0 deletions config.lua.dist
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ bindOnlyGlobalAddress = false
loginProtocolPort = 7171
gameProtocolPort = 7172
statusProtocolPort = 7171
httpPort = 8080
httpWorkers = 1
maxPlayers = 0
onePlayerOnlinePerAccount = true
allowClones = false
Expand Down
17 changes: 16 additions & 1 deletion data/migrations/36.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
function onUpdateDatabase()
return false
print("> Updating database to version 37 (session tokens)")

db.query([[
CREATE TABLE IF NOT EXISTS `sessions` (
`id` int NOT NULL AUTO_INCREMENT,
`token` binary(16) NOT NULL,
`account_id` int NOT NULL,
`ip` varbinary(16) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expired_at` timestamp,
PRIMARY KEY (`id`),
UNIQUE KEY `token` (`token`),
FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
]])
return true
end
3 changes: 3 additions & 0 deletions data/migrations/37.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function onUpdateDatabase()
return false
end
12 changes: 12 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,18 @@ CREATE TABLE IF NOT EXISTS `server_config` (
PRIMARY KEY `config` (`config`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;

CREATE TABLE IF NOT EXISTS `sessions` (
`id` int NOT NULL AUTO_INCREMENT,
`token` binary(16) NOT NULL,
`account_id` int NOT NULL,
`ip` varbinary(16) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expired_at` timestamp,
PRIMARY KEY (`id`),
UNIQUE KEY `token` (`token`),
FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;

CREATE TABLE IF NOT EXISTS `tile_store` (
`house_id` int NOT NULL,
`data` longblob NOT NULL,
Expand Down
17 changes: 17 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(tfs_SRC
${CMAKE_CURRENT_LIST_DIR}/otpch.cpp
${CMAKE_CURRENT_LIST_DIR}/actions.cpp
${CMAKE_CURRENT_LIST_DIR}/ban.cpp
${CMAKE_CURRENT_LIST_DIR}/base64.cpp
${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp
${CMAKE_CURRENT_LIST_DIR}/bed.cpp
${CMAKE_CURRENT_LIST_DIR}/chat.cpp
Expand Down Expand Up @@ -82,6 +83,7 @@ set(tfs_HDR
${CMAKE_CURRENT_LIST_DIR}/account.h
${CMAKE_CURRENT_LIST_DIR}/actions.h
${CMAKE_CURRENT_LIST_DIR}/ban.h
${CMAKE_CURRENT_LIST_DIR}/base64.h
${CMAKE_CURRENT_LIST_DIR}/baseevents.h
${CMAKE_CURRENT_LIST_DIR}/bed.h
${CMAKE_CURRENT_LIST_DIR}/chat.h
Expand Down Expand Up @@ -181,8 +183,23 @@ target_link_libraries(tfslib PRIVATE
${LUA_LIBRARIES}
${MYSQL_CLIENT_LIBS}
)
set_target_properties(tfslib PROPERTIES UNITY_BUILD ${ENABLE_UNITY_BUILD})

if (APPLE)
target_link_libraries(tfslib PRIVATE Iconv::Iconv)
endif()

if (ENABLE_IPO)
set_target_properties(tfslib PROPERTIES INTERPROCEDURAL_OPTIMIZATION True)
endif()

if (HTTP)
add_subdirectory(http)
target_link_libraries(tfslib PRIVATE http)
endif ()

add_custom_target(format COMMAND /usr/bin/clang-format -style=file -i ${tfs_HDR} ${tfs_SRC} ${tfs_MAIN})

if (BUILD_TESTING)
add_subdirectory(tests)
endif()
35 changes: 35 additions & 0 deletions src/base64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "otpch.h"

#include "base64.h"

#include <openssl/evp.h>

std::string tfs::base64::encode(std::string_view input)
{
std::unique_ptr<BIO, decltype(&BIO_free_all)> b64(BIO_new(BIO_f_base64()), BIO_free_all);
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);

BIO* sink = BIO_new(BIO_s_mem());
BIO_push(b64.get(), sink);
BIO_write(b64.get(), input.data(), input.size());
BIO_flush(b64.get());

const char* encoded;
auto len = BIO_get_mem_data(sink, &encoded);
return {encoded, static_cast<size_t>(len)};
}

std::string tfs::base64::decode(std::string_view input)
{
std::unique_ptr<BIO, decltype(&BIO_free_all)> b64(BIO_new(BIO_f_base64()), BIO_free_all);
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);

BIO* source = BIO_new_mem_buf(input.data(), input.size()); // read-only source
BIO_push(b64.get(), source);

size_t decodedLength = 3 * input.size() / 4;
auto decoded = std::string(decodedLength, '\0');

const int len = BIO_read(b64.get(), decoded.data(), decodedLength);
return decoded.substr(0, len);
}
11 changes: 11 additions & 0 deletions src/base64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef TFS_BASE64_H
#define TFS_BASE64_H

namespace tfs::base64 {

std::string encode(std::string_view input);
std::string decode(std::string_view input);

} // namespace tfs::base64

#endif
2 changes: 2 additions & 0 deletions src/configmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ bool ConfigManager::load()
}

integer[STATUS_PORT] = getGlobalNumber(L, "statusProtocolPort", 7171);
integer[HTTP_PORT] = getGlobalNumber(L, "httpPort", 8080);
integer[HTTP_WORKERS] = getGlobalNumber(L, "httpWorkers", 1);

integer[MARKET_OFFER_DURATION] = getGlobalNumber(L, "marketOfferDuration", 30 * 24 * 60 * 60);
}
Expand Down
2 changes: 2 additions & 0 deletions src/configmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ enum integer_config_t
GAME_PORT,
LOGIN_PORT,
STATUS_PORT,
HTTP_PORT,
HTTP_WORKERS,
STAIRHOP_DELAY,
MARKET_OFFER_DURATION,
CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES,
Expand Down
8 changes: 8 additions & 0 deletions src/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ void ConnectionManager::closeAll()

// Connection

Connection::Connection(boost::asio::io_context& io_context, ConstServicePort_ptr service_port) :
readTimer(io_context),
writeTimer(io_context),
service_port(std::move(service_port)),
socket(io_context),
timeConnected(time(nullptr))
{}

void Connection::close(bool force)
{
// any thread
Expand Down
8 changes: 1 addition & 7 deletions src/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,7 @@ class Connection : public std::enable_shared_from_this<Connection>
FORCE_CLOSE = true
};

Connection(boost::asio::io_context& io_context, ConstServicePort_ptr service_port) :
readTimer(io_context),
writeTimer(io_context),
service_port(std::move(service_port)),
socket(io_context),
timeConnected(time(nullptr))
{}
Connection(boost::asio::io_context& io_context, ConstServicePort_ptr service_port);
~Connection();

friend class ConnectionManager;
Expand Down
2 changes: 2 additions & 0 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "events.h"
#include "globalevent.h"
#include "housetile.h"
#include "http/http.h"
#include "inbox.h"
#include "iologindata.h"
#include "iomarket.h"
Expand Down Expand Up @@ -117,6 +118,7 @@ void Game::setGameState(GameState_t newState)
g_scheduler.stop();
g_databaseTasks.stop();
g_dispatcher.stop();
tfs::http::stop();
break;
}

Expand Down
31 changes: 31 additions & 0 deletions src/http/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
set(http_SRC
${CMAKE_CURRENT_LIST_DIR}/cacheinfo.cpp
${CMAKE_CURRENT_LIST_DIR}/error.cpp
${CMAKE_CURRENT_LIST_DIR}/http.cpp
${CMAKE_CURRENT_LIST_DIR}/listener.cpp
${CMAKE_CURRENT_LIST_DIR}/login.cpp
${CMAKE_CURRENT_LIST_DIR}/router.cpp
${CMAKE_CURRENT_LIST_DIR}/session.cpp
)

set(http_HDR
${CMAKE_CURRENT_LIST_DIR}/cacheinfo.h
${CMAKE_CURRENT_LIST_DIR}/error.h
${CMAKE_CURRENT_LIST_DIR}/http.h
${CMAKE_CURRENT_LIST_DIR}/listener.h
${CMAKE_CURRENT_LIST_DIR}/login.h
${CMAKE_CURRENT_LIST_DIR}/router.h
${CMAKE_CURRENT_LIST_DIR}/session.h
)

add_library(http OBJECT ${http_SRC})
target_link_libraries(http PRIVATE Boost::json)
set_target_properties(http PROPERTIES UNITY_BUILD ${ENABLE_UNITY_BUILD})

if (ENABLE_IPO)
set_target_properties(http PROPERTIES INTERPROCEDURAL_OPTIMIZATION True)
endif()

if (BUILD_TESTING)
add_subdirectory(tests)
endif()
21 changes: 21 additions & 0 deletions src/http/cacheinfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "../otpch.h"

#include "cacheinfo.h"

#include "../database.h"
#include "error.h"

namespace beast = boost::beast;
namespace json = boost::json;
using boost::beast::http::status;

std::pair<status, json::value> tfs::http::handle_cacheinfo(const json::object&, std::string_view)
{
thread_local auto& db = Database::getInstance();
auto result = db.storeQuery("SELECT COUNT(*) AS `count` FROM `players_online`");
if (!result) {
return make_error_response();
}

return {status::ok, {{"playersonline", result->getNumber<uint32_t>("count")}}};
}
11 changes: 11 additions & 0 deletions src/http/cacheinfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#include <boost/beast/http/status.hpp>
#include <boost/json/value.hpp>

namespace tfs::http {

std::pair<boost::beast::http::status, boost::json::value> handle_cacheinfo(const boost::json::object& body,
std::string_view ip);

}
14 changes: 14 additions & 0 deletions src/http/error.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "error.h"

namespace beast = boost::beast;
namespace json = boost::json;
using boost::beast::http::status;

std::pair<status, json::value> tfs::http::make_error_response(detail::ErrorResponseParams params /*= {}*/)
{
json::object body;
body["errorCode"] = params.code;
body["errorMessage"] = params.message;

return std::make_pair(status::ok, body);
}
Loading