This commit is contained in:
2025-03-11 19:37:36 +06:00
parent e190c79d00
commit 1710eb974d
10 changed files with 178 additions and 64 deletions

View File

@@ -41,7 +41,6 @@ struct PP_Content_ChunkRemove : public ParsedPacket {
using namespace TOS; using namespace TOS;
ServerSession::~ServerSession() { ServerSession::~ServerSession() {
WorkDeadline.cancel();
UseLock.wait_no_use(); UseLock.wait_no_use();
} }

View File

@@ -120,10 +120,18 @@ coro<> AsyncSocket::read(std::byte *data, uint32_t size) {
void AsyncSocket::closeRead() { void AsyncSocket::closeRead() {
if(Socket.is_open() && !ReadShutdowned) { if(Socket.is_open() && !ReadShutdowned) {
ReadShutdowned = true; ReadShutdowned = true;
// TODO:
try { Socket.shutdown(boost::asio::socket_base::shutdown_receive); } catch(...) {} try { Socket.shutdown(boost::asio::socket_base::shutdown_receive); } catch(...) {}
} }
} }
void AsyncSocket::close() {
if(Socket.is_open()) {
Socket.close();
ReadShutdowned = true;
}
}
coro<> AsyncSocket::waitForSend() { coro<> AsyncSocket::waitForSend() {
asio::deadline_timer waiter(IOC); asio::deadline_timer waiter(IOC);

View File

@@ -239,6 +239,7 @@ protected:
coro<> read(std::byte *data, uint32_t size); coro<> read(std::byte *data, uint32_t size);
void closeRead(); void closeRead();
void close();
template<typename T, std::enable_if_t<std::is_integral_v<T> or std::is_same_v<T, std::string>, int> = 0> template<typename T, std::enable_if_t<std::is_integral_v<T> or std::is_same_v<T, std::string>, int> = 0>
coro<T> read() { coro<T> read() {

View File

@@ -7,7 +7,7 @@
namespace LV::Server { namespace LV::Server {
ContentEventController::ContentEventController(std::unique_ptr<RemoteClient> &&remote) ContentEventController::ContentEventController(RemoteClient_ptr &&remote)
: Remote(std::move(remote)) : Remote(std::move(remote))
{ {
} }

View File

@@ -14,6 +14,7 @@
namespace LV::Server { namespace LV::Server {
class RemoteClient; class RemoteClient;
using RemoteClient_ptr = std::unique_ptr<RemoteClient, std::function<void(RemoteClient*)>>;
class GameServer; class GameServer;
class World; class World;
@@ -162,7 +163,7 @@ private:
public: public:
// Управляется сервером // Управляется сервером
std::unique_ptr<RemoteClient> Remote; RemoteClient_ptr Remote;
// Регионы сюда заглядывают // Регионы сюда заглядывают
// Каждый такт значения изменений обновляются GameServer'ом // Каждый такт значения изменений обновляются GameServer'ом
// Объявленная в чанках территория точно отслеживается (активная зона) // Объявленная в чанках территория точно отслеживается (активная зона)
@@ -173,7 +174,7 @@ public:
// std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> SubscribedRegions; // std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> SubscribedRegions;
public: public:
ContentEventController(std::unique_ptr<RemoteClient> &&remote); ContentEventController(RemoteClient_ptr &&remote);
// Измеряется в чанках в радиусе (активная зона) // Измеряется в чанках в радиусе (активная зона)
uint16_t getViewRangeActive() const; uint16_t getViewRangeActive() const;

View File

@@ -247,7 +247,7 @@ coro<> GameServer::pushSocketGameProtocol(tcp::socket socket, const std::string
co_await Net::AsyncSocket::write<uint8_t>(socket, 0); co_await Net::AsyncSocket::write<uint8_t>(socket, 0);
External.NewConnectedPlayers.lock_write() External.NewConnectedPlayers.lock_write()
->push_back(std::make_unique<RemoteClient>(IOC, std::move(socket), username)); ->push_back(RemoteClient::Create(IOC, std::move(socket), username));
} }
} }
} }
@@ -333,7 +333,7 @@ void GameServer::run() {
// Отключить вновь подключившихся // Отключить вновь подключившихся
auto lock = External.NewConnectedPlayers.lock_write(); auto lock = External.NewConnectedPlayers.lock_write();
for(std::unique_ptr<RemoteClient> &client : *lock) { for(RemoteClient_ptr &client : *lock) {
client->shutdown(EnumDisconnect::ByInterface, ShutdownReason); client->shutdown(EnumDisconnect::ByInterface, ShutdownReason);
} }
@@ -449,8 +449,7 @@ void GameServer::stepPlayers() {
if(!External.NewConnectedPlayers.no_lock_readable().empty()) { if(!External.NewConnectedPlayers.no_lock_readable().empty()) {
auto lock = External.NewConnectedPlayers.lock_write(); auto lock = External.NewConnectedPlayers.lock_write();
for(std::unique_ptr<RemoteClient> &client : *lock) { for(RemoteClient_ptr &client : *lock) {
asio::co_spawn(IOC, client->run(), asio::detached);
Game.CECs.push_back(std::make_unique<ContentEventController>(std::move(client))); Game.CECs.push_back(std::make_unique<ContentEventController>(std::move(client)));
} }

View File

@@ -42,7 +42,7 @@ class GameServer {
struct { struct {
Lockable<std::set<std::string>> ConnectedPlayersSet; Lockable<std::set<std::string>> ConnectedPlayersSet;
Lockable<std::list<std::unique_ptr<RemoteClient>>> NewConnectedPlayers; Lockable<std::list<RemoteClient_ptr>> NewConnectedPlayers;
} External; } External;

View File

@@ -3,28 +3,33 @@
#include "Common/Net.hpp" #include "Common/Net.hpp"
#include "Server/Abstract.hpp" #include "Server/Abstract.hpp"
#include <boost/asio/error.hpp> #include <boost/asio/error.hpp>
#include <boost/chrono/duration.hpp>
#include <boost/system/system_error.hpp> #include <boost/system/system_error.hpp>
#include <exception> #include <exception>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include "World.hpp" #include "World.hpp"
#include "boost/asio/steady_timer.hpp"
#include <Common/Packets.hpp> #include <Common/Packets.hpp>
namespace LV::Server { namespace LV::Server {
RemoteClient::~RemoteClient() { coro<> RemoteClient::asyncDestructor() {
shutdown(EnumDisconnect::ByInterface, "~RemoteClient()"); shutdown(EnumDisconnect::ByInterface, "~RemoteClient()");
if(Socket.isAlive()) { asio::steady_timer deadline(IOC);
deadline.expires_after(std::chrono::seconds(1));
Socket.closeRead(); Socket.closeRead();
co_await (deadline.async_wait(asio::use_awaitable) || RunCoro.async_wait());
Socket.close();
co_await deadline.async_wait();
} }
UseLock.wait_no_use(); RemoteClient::~RemoteClient() = default;
}
coro<> RemoteClient::run() { coro<> RemoteClient::run() {
auto useLock = UseLock.lock();
try { try {
while(!IsGoingShutdown && IsConnected) { while(!IsGoingShutdown && IsConnected) {
co_await readPacket(Socket); co_await readPacket(Socket);

View File

@@ -3,12 +3,18 @@
#include <TOSLib.hpp> #include <TOSLib.hpp>
#include <Common/Lockable.hpp> #include <Common/Lockable.hpp>
#include <Common/Net.hpp> #include <Common/Net.hpp>
#include <Common/Async.hpp>
#include "Abstract.hpp" #include "Abstract.hpp"
#include "Common/Packets.hpp" #include "Common/Packets.hpp"
#include "Server/ContentEventController.hpp" #include "Server/ContentEventController.hpp"
#include "TOSAsync.hpp"
#include "boost/asio/detached.hpp"
#include "boost/asio/io_context.hpp"
#include "boost/asio/use_awaitable.hpp"
#include <Common/Abstract.hpp> #include <Common/Abstract.hpp>
#include <bitset> #include <bitset>
#include <initializer_list> #include <initializer_list>
#include <memory>
#include <set> #include <set>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
@@ -173,16 +179,16 @@ using EntityKey = std::tuple<WorldId_c, Pos::GlobalRegion>;
class RemoteClient;
using RemoteClient_ptr = std::unique_ptr<RemoteClient, std::function<void(RemoteClient*)>>;
/* /*
Обработчик сокета клиента. Обработчик сокета клиента.
Подписывает клиента на отслеживание необходимых ресурсов Подписывает клиента на отслеживание необходимых ресурсов
на основе передаваемых клиенту данных на основе передаваемых клиенту данных
*/ */
class RemoteClient { class RemoteClient : public TOS::IAsyncDestructible {
TOS::Logger LOG; TOS::Logger LOG;
DestroyLock UseLock;
Net::AsyncSocket Socket; Net::AsyncSocket Socket;
bool IsConnected = true, IsGoingShutdown = false; bool IsConnected = true, IsGoingShutdown = false;
@@ -264,20 +270,32 @@ class RemoteClient {
ResourceRequest NextRequest; ResourceRequest NextRequest;
std::vector<Net::Packet> SimplePackets; std::vector<Net::Packet> SimplePackets;
TOS::WaitableCoro RunCoro;
public: public:
const std::string Username; const std::string Username;
Pos::Object CameraPos = {0, 0, 0}; Pos::Object CameraPos = {0, 0, 0};
ToServer::PacketQuat CameraQuat = {0}; ToServer::PacketQuat CameraQuat = {0};
public: private:
RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username) RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username)
: LOG("RemoteClient " + username), Socket(ioc, std::move(socket)), Username(username) : IAsyncDestructible(ioc), LOG("RemoteClient " + username), Socket(ioc, std::move(socket)),
Username(username), RunCoro(ioc)
{ {
RunCoro.co_spawn(run());
} }
~RemoteClient(); virtual coro<> asyncDestructor() override;
coro<> run(); coro<> run();
public:
static RemoteClient_ptr Create(asio::io_context &ioc, tcp::socket socket, const std::string username) {
return createUnique<>(ioc, new RemoteClient(ioc, std::move(socket), username));
}
virtual ~RemoteClient();
void shutdown(EnumDisconnect type, const std::string reason); void shutdown(EnumDisconnect type, const std::string reason);
bool isConnected() { return IsConnected; } bool isConnected() { return IsConnected; }

View File

@@ -1,77 +1,136 @@
#pragma once #pragma once
#include "boost/asio/awaitable.hpp" #include "TOSLib.hpp"
#include "boost/asio/co_spawn.hpp" #include "boost/asio/associated_cancellation_slot.hpp"
#include "boost/asio/associated_executor.hpp"
#include "boost/asio/deadline_timer.hpp" #include "boost/asio/deadline_timer.hpp"
#include "boost/asio/detached.hpp"
#include "boost/asio/io_context.hpp" #include "boost/asio/io_context.hpp"
#include "boost/asio/use_awaitable.hpp" #include "boost/system/detail/error_code.hpp"
#include <boost/asio.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp> #include <boost/asio/experimental/awaitable_operators.hpp>
#include <exception>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
namespace TOS { namespace TOS {
using namespace boost::asio::experimental::awaitable_operators; using namespace boost::asio::experimental::awaitable_operators;
namespace asio = boost::asio;
template<typename T = void> template<typename T = void>
using coro = boost::asio::awaitable<T>; using coro = boost::asio::awaitable<T>;
class AsyncSemaphore // class AsyncSemaphore
{ // {
boost::asio::deadline_timer Deadline; // boost::asio::deadline_timer Deadline;
std::atomic<uint8_t> Lock = 0; // std::atomic<uint8_t> Lock = 0;
// public:
// AsyncSemaphore(boost::asio::io_context& ioc)
// : Deadline(ioc, boost::posix_time::ptime(boost::posix_time::pos_infin))
// {}
// coro<> async_wait() {
// try {
// co_await Deadline.async_wait(boost::asio::use_awaitable);
// } catch(boost::system::system_error code) {
// if(code.code() != boost::system::errc::operation_canceled)
// throw;
// }
// co_await asio::this_coro::throw_if_cancelled();
// }
// coro<> async_wait(std::function<bool()> predicate) {
// while(!predicate())
// co_await async_wait();
// }
// void notify_one() {
// Deadline.cancel_one();
// }
// void notify_all() {
// Deadline.cancel();
// }
// };
/*
Многие могут уведомлять одного
Ждёт события. После доставки уведомления ждёт повторно
*/
class MultipleToOne_AsyncSymaphore {
asio::deadline_timer Timer;
public: public:
AsyncSemaphore( MultipleToOne_AsyncSymaphore(asio::io_context &ioc)
boost::asio::io_context& ioc) : Timer(ioc, boost::posix_time::ptime(boost::posix_time::pos_infin))
: Deadline(ioc, boost::posix_time::ptime(boost::posix_time::pos_infin))
{} {}
boost::asio::awaitable<void> async_wait() { void notify() {
try { Timer.cancel();
co_await Deadline.async_wait(boost::asio::use_awaitable);
} catch(boost::system::system_error code) {
if(code.code() != boost::system::errc::operation_canceled)
throw;
} }
co_await boost::asio::this_coro::throw_if_cancelled(); void wait() {
Timer.wait();
Timer.expires_at(boost::posix_time::ptime(boost::posix_time::pos_infin));
} }
boost::asio::awaitable<void> async_wait(std::function<bool()> predicate) { coro<> async_wait() {
while(!predicate()) try { co_await Timer.async_wait(); } catch(...) {}
co_await async_wait();
} }
void notify_one() { };
Deadline.cancel_one();
class WaitableCoro {
asio::io_context &IOC;
std::shared_ptr<MultipleToOne_AsyncSymaphore> Symaphore;
std::exception_ptr LastException;
public:
WaitableCoro(asio::io_context &ioc)
: IOC(ioc)
{}
template<typename Token>
void co_spawn(Token token) {
Symaphore = std::make_shared<MultipleToOne_AsyncSymaphore>(IOC);
asio::co_spawn(IOC, [token = std::move(token), symaphore = Symaphore]() -> coro<> {
co_await std::move(token);
symaphore->notify();
}, asio::detached);
} }
void notify_all() { void wait() {
Deadline.cancel(); Symaphore->wait();
}
coro<> async_wait() {
return Symaphore->async_wait();
} }
}; };
/*
Используется, чтобы вместо уничтожения объекта в умной ссылке, вызвать корутину с co_await asyncDestructor()
*/
class IAsyncDestructible : public std::enable_shared_from_this<IAsyncDestructible> { class IAsyncDestructible : public std::enable_shared_from_this<IAsyncDestructible> {
protected: protected:
boost::asio::any_io_executor IOC; asio::io_context &IOC;
boost::asio::deadline_timer DestructLine;
virtual coro<> asyncDestructor() { DestructLine.cancel(); co_return; } virtual coro<> asyncDestructor() { co_return; }
public: public:
IAsyncDestructible(boost::asio::any_io_executor ioc) IAsyncDestructible(asio::io_context &ioc)
: IOC(ioc), DestructLine(ioc, boost::posix_time::ptime(boost::posix_time::pos_infin)) : IOC(ioc)
{} {}
virtual ~IAsyncDestructible() {} virtual ~IAsyncDestructible() {}
coro<std::variant<std::monostate, std::monostate>> cancelable(coro<> &&c) { return std::move(c) || DestructLine.async_wait(boost::asio::use_awaitable); } protected:
template<typename T, typename = typename std::is_same<IAsyncDestructible, T>> template<typename T, typename = typename std::is_same<IAsyncDestructible, T>>
static std::shared_ptr<T> createShared(boost::asio::any_io_executor ioc, T *ptr) static std::shared_ptr<T> createShared(asio::io_context &ioc, T *ptr)
{ {
return std::shared_ptr<T>(ptr, [ioc = std::move(ioc)](IAsyncDestructible *ptr) { return std::shared_ptr<T>(ptr, [&ioc = ioc](T *ptr) {
boost::asio::co_spawn(ioc, [](IAsyncDestructible *ptr) -> coro<> { boost::asio::co_spawn(ioc, [](IAsyncDestructible *ptr) -> coro<> {
try { co_await ptr->asyncDestructor(); } catch(...) { } try { co_await ptr->asyncDestructor(); } catch(...) { }
delete ptr; delete ptr;
@@ -80,16 +139,40 @@ public:
}); });
} }
template<typename T, typename ...Args, typename = typename std::is_same<IAsyncDestructible, T>> template<typename T, typename = typename std::is_same<IAsyncDestructible, T>>
static std::shared_ptr<T> makeShared(boost::asio::any_io_executor ioc, Args&& ... args) static coro<std::shared_ptr<T>> createShared(T *ptr)
{ {
std::shared_ptr<T>(new T(ioc, std::forward<Args>(args)..., [ioc = std::move(ioc)](IAsyncDestructible *ptr) { co_return std::shared_ptr<T>(ptr, [ioc = asio::get_associated_executor(co_await asio::this_coro::executor)](T *ptr) {
boost::asio::co_spawn(ioc, [](IAsyncDestructible *ptr) -> coro<> { boost::asio::co_spawn(ioc, [](IAsyncDestructible *ptr) -> coro<> {
try { co_await ptr->asyncDestructor(); } catch(...) { } try { co_await ptr->asyncDestructor(); } catch(...) { }
delete ptr; delete ptr;
co_return; co_return;
} (ptr), boost::asio::detached); } (ptr), boost::asio::detached);
})); });
}
template<typename T, typename = typename std::is_same<IAsyncDestructible, T>>
static std::unique_ptr<T, std::function<void(T*)>> createUnique(asio::io_context &ioc, T *ptr)
{
return std::unique_ptr<T, std::function<void(T*)>>(ptr, [&ioc = ioc](T *ptr) {
boost::asio::co_spawn(ioc, [](IAsyncDestructible *ptr) -> coro<> {
try { co_await ptr->asyncDestructor(); } catch(...) { }
delete ptr;
co_return;
} (ptr), boost::asio::detached);
});
}
template<typename T, typename = typename std::is_same<IAsyncDestructible, T>>
static coro<std::unique_ptr<T, std::function<void(T*)>>> createUnique(T *ptr)
{
co_return std::unique_ptr<T, std::function<void(T*)>>(ptr, [ioc = asio::get_associated_executor(co_await asio::this_coro::executor)](T *ptr) {
boost::asio::co_spawn(ioc, [](IAsyncDestructible *ptr) -> coro<> {
try { co_await ptr->asyncDestructor(); } catch(...) { }
delete ptr;
co_return;
} (ptr), boost::asio::detached);
});
} }
}; };