Первый коммит

This commit is contained in:
2025-02-03 15:16:12 +06:00
commit d40c3bad86
287 changed files with 124575 additions and 0 deletions

136
Src/Common/Abstract.hpp Normal file
View File

@@ -0,0 +1,136 @@
#pragma once
#include <cstdint>
#include <glm/ext.hpp>
namespace AL {
namespace Pos {
struct Local4_u {
uint8_t X : 2, Y : 2, Z : 2;
using Key = uint8_t;
operator Key() const {
return Key(X) | (Key(Y) << 2) | (Key(Z) << 4);
};
};
struct Local16_u {
uint8_t X : 4, Y : 4, Z : 4;
using Key = uint16_t;
operator Key() const {
return Key(X) | (Key(Y) << 4) | (Key(Z) << 8);
};
Local4_u left() const { return Local4_u{uint8_t(uint16_t(X) >> 2), uint8_t(uint16_t(Y) >> 2), uint8_t(uint16_t(Z) >> 2)}; }
Local4_u right() const { return Local4_u{uint8_t(uint16_t(X) & 0b11), uint8_t(uint16_t(Y) & 0b11), uint8_t(uint16_t(Z) & 0b11)}; }
};
struct Local16 {
int8_t X : 4, Y : 4, Z : 4;
using Key = uint16_t;
operator Key() const {
return Key(X) | (Key(Y) << 4) | (Key(Z) << 8);
};
Local4_u left() const { return Local4_u{uint8_t(uint16_t(X) >> 2), uint8_t(uint16_t(Y) >> 2), uint8_t(uint16_t(Z) >> 2)}; }
Local4_u right() const { return Local4_u{uint8_t(uint16_t(X) & 0b11), uint8_t(uint16_t(Y) & 0b11), uint8_t(uint16_t(Z) & 0b11)}; }
};
struct Local256 {
int8_t X : 8, Y : 8, Z : 8;
auto operator<=>(const Local256&) const = default;
};
struct Local256_u {
uint8_t X : 8, Y : 8, Z : 8;
auto operator<=>(const Local256_u&) const = default;
};
struct Local4096 {
int16_t X : 12, Y : 12, Z : 12;
auto operator<=>(const Local4096&) const = default;
};
struct Local4096_u {
uint16_t X : 12, Y : 12, Z : 12;
auto operator<=>(const Local4096_u&) const = default;
};
struct GlobalVoxel {
int32_t X : 24, Y : 24, Z : 24;
auto operator<=>(const GlobalVoxel&) const = default;
};
struct GlobalNode {
int32_t X : 20, Y : 20, Z : 20;
using Key = uint64_t;
operator Key() const {
return Key(X) | (Key(Y) << 20) | (Key(Z) << 40);
};
auto operator<=>(const GlobalNode&) const = default;
};
struct GlobalChunk {
int16_t X : 16, Y : 16, Z : 16;
using Key = uint64_t;
operator Key() const {
return Key(X) | (Key(Y) << 16) | (Key(Z) << 32);
};
auto operator<=>(const GlobalChunk&) const = default;
};
struct GlobalRegion {
int16_t X : 12, Y : 12, Z : 12;
using Key = uint64_t;
operator Key() const {
return Key(X) | (Key(Y) << 12) | (Key(Z) << 24);
};
auto operator<=>(const GlobalRegion&) const = default;
};
using Object = glm::i32vec3;
struct Object_t {
// Позиции объектов целочисленные, BS единиц это один метр
static constexpr int32_t BS = 4096, BS_Bit = 12;
static glm::vec3 asFloatVec(Object &obj) { return glm::vec3(float(obj.x)/float(BS), float(obj.y)/float(BS), float(obj.z)/float(BS)); }
static GlobalNode asNodePos(Object &obj) { return GlobalNode(obj.x >> BS_Bit, obj.y >> BS_Bit, obj.z >> BS_Bit); }
};
}
struct LightPrism {
uint8_t R : 2, G : 2, B : 2;
};
}
#include <functional>
namespace std {
#define hash_for_pos(type) template <> struct hash<AL::Pos::type> { std::size_t operator()(const AL::Pos::type& obj) const { return std::hash<AL::Pos::type::Key>()((AL::Pos::type::Key) obj); } };
hash_for_pos(Local4_u)
hash_for_pos(Local16_u)
hash_for_pos(Local16)
hash_for_pos(GlobalChunk)
hash_for_pos(GlobalRegion)
#undef hash_for_pos
}

171
Src/Common/Async.hpp Normal file
View File

@@ -0,0 +1,171 @@
#pragma once
#include <boost/asio.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/this_coro.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/thread.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
namespace AL {
using namespace boost::asio::experimental::awaitable_operators;
namespace asio = boost::asio;
using tcp = asio::ip::tcp;
template<typename T = void>
using coro = asio::awaitable<T>;
class AsyncObject {
protected:
asio::io_context &IOC;
asio::deadline_timer WorkDeadline;
public:
AsyncObject(asio::io_context &ioc)
: IOC(ioc), WorkDeadline(ioc, boost::posix_time::pos_infin)
{
}
inline asio::io_context& EXEC()
{
return IOC;
}
protected:
template<typename Coroutine>
void co_spawn(Coroutine &&coroutine) {
asio::co_spawn(IOC, WorkDeadline.async_wait(asio::use_awaitable) || std::move(coroutine), asio::detached);
}
};
template<typename ValueType>
class AsyncAtomic : public AsyncObject {
protected:
asio::deadline_timer Deadline;
ValueType Value;
boost::mutex Mtx;
public:
AsyncAtomic(asio::io_context &ioc, ValueType &&value)
: AsyncObject(ioc), Deadline(ioc), Value(std::move(value))
{
}
AsyncAtomic& operator=(ValueType &&value) {
boost::unique_lock lock(Mtx);
Value = std::move(value);
Deadline.expires_from_now(boost::posix_time::pos_infin);
return *this;
}
operator ValueType() const {
return Value;
}
ValueType operator*() const {
return Value;
}
AsyncAtomic& operator++() {
boost::unique_lock lock(Mtx);
Value--;
Deadline.expires_from_now(boost::posix_time::pos_infin);
return *this;
}
AsyncAtomic& operator--() {
boost::unique_lock lock(Mtx);
Value--;
Deadline.expires_from_now(boost::posix_time::pos_infin);
return *this;
}
AsyncAtomic& operator+=(ValueType value) {
boost::unique_lock lock(Mtx);
Value += value;
Deadline.expires_from_now(boost::posix_time::pos_infin);
return *this;
}
AsyncAtomic& operator-=(ValueType value) {
boost::unique_lock lock(Mtx);
Value -= value;
Deadline.expires_from_now(boost::posix_time::pos_infin);
return *this;
}
void wait(ValueType oldValue) {
while(true) {
if(oldValue != Value)
return;
boost::unique_lock lock(Mtx);
if(oldValue != Value)
return;
std::atomic_bool flag = false;
Deadline.async_wait([&](boost::system::error_code errc) { flag.store(true); });
lock.unlock();
flag.wait(false);
}
}
void await(ValueType needValue) {
while(true) {
if(needValue == Value)
return;
boost::unique_lock lock(Mtx);
if(needValue == Value)
return;
std::atomic_bool flag = false;
Deadline.async_wait([&](boost::system::error_code errc) { flag.store(true); });
lock.unlock();
flag.wait(false);
}
}
coro<> async_wait(ValueType oldValue) {
while(true) {
if(oldValue != Value)
co_return;
boost::unique_lock lock(Mtx);
if(oldValue != Value)
co_return;
auto coroutine = Deadline.async_wait();
lock.unlock();
try { co_await std::move(coroutine); } catch(...) {}
}
}
coro<> async_await(ValueType needValue) {
while(true) {
if(needValue == Value)
co_return;
boost::unique_lock lock(Mtx);
if(needValue == Value)
co_return;
auto coroutine = Deadline.async_wait();
lock.unlock();
try { co_await std::move(coroutine); } catch(...) {}
}
}
};
}

60
Src/Common/Collide.hpp Normal file
View File

@@ -0,0 +1,60 @@
#include "Common/Abstract.hpp"
#include <iostream>
namespace AL {
template<typename VecType>
bool calcBoxToBoxCollide(const VecType vec1_min, const VecType vec1_max,
const VecType vec2_min, const VecType vec2_max, bool axis[VecType::length()] = nullptr
) {
using ValType = VecType::value_type;
ValType max_delta = 0;
ValType result = 0;
for(int iter = 0; iter < VecType::length(); iter++) {
ValType left = vec2_min[iter]-vec1_max[iter];
ValType right = vec1_min[iter]-vec2_max[iter];
result = std::max(result, std::max(left, right));
if(axis)
axis[iter] = std::min(left, right) < ValType(0);
}
return result <= ValType(0);
}
template<typename VecType>
bool calcBoxToBoxCollideWithDelta(const VecType vec1_min, const VecType vec1_max,
const VecType vec2_min, const VecType vec2_max, VecType vec1_speed,
typename VecType::value_type *delta, typename VecType::value_type deltaBias, bool axis[VecType::length()]
) {
using ValType = VecType::value_type;
ValType max_delta = 0;
for(int iter = 0; iter < VecType::length(); iter++) {
ValType left = vec2_min[iter]-vec1_max[iter];
ValType right = vec1_min[iter]-vec2_max[iter];
ValType new_detla = (right > left ? -right : left)*deltaBias/vec1_speed[iter];
max_delta = std::max(max_delta, new_detla);
}
ValType result = 0;
for(int iter = 0; iter < VecType::length(); iter++) {
ValType left = vec2_min[iter]-(vec1_max[iter]+vec1_speed[iter]*max_delta/deltaBias);
ValType right = (vec1_min[iter]+vec1_speed[iter]*max_delta/deltaBias)-vec2_max[iter];
if(axis)
axis[iter] = std::min(left, right) < ValType(0);
result = std::min(std::max(left, right), result);
}
*delta = max_delta;
return result < ValType(0);
}
}

136
Src/Common/Lockable.hpp Normal file
View File

@@ -0,0 +1,136 @@
#pragma once
#include <atomic>
#include <boost/thread/lock_types.hpp>
#include <boost/thread/pthread/mutex.hpp>
#include <mutex>
#include <boost/thread.hpp>
namespace AL {
template<typename T>
class Lockable {
public:
template<typename ...Args>
Lockable(Args&& ...args)
: Obj(std::forward<Args>(args)...)
{}
class ReadLockGuard {
public:
template<typename ...Args>
ReadLockGuard(T& obj, Args&& ...args)
: Lock(std::forward<Args>(args)...), Ref(obj) {}
const T& operator*() const { assert(Lock.owns_lock()); return Ref; }
const T* operator->() const { assert(Lock.owns_lock()); return &Ref; }
bool owns_lock() {
return Lock.owns_lock();
}
operator bool() {
return Lock.owns_lock();
}
void unlock() {
Lock.unlock();
}
private:
boost::shared_lock<boost::shared_mutex> Lock;
T& Ref;
};
class WriteLockGuard {
public:
template<typename ...Args>
WriteLockGuard(T& obj, Args&& ...args)
: Lock(std::forward<Args>(args)...), Ref(obj) {}
T& operator*() const { assert(Lock.owns_lock()); return Ref; }
T* operator->() const { assert(Lock.owns_lock()); return &Ref; }
bool owns_lock() {
return Lock.owns_lock();
}
operator bool() {
return Lock.owns_lock();
}
void unlock() {
Lock.unlock();
}
private:
std::unique_lock<boost::shared_mutex> Lock;
T& Ref;
};
ReadLockGuard lock_read() {
return ReadLockGuard(Obj, Mtx);
}
ReadLockGuard try_lock_read() {
return ReadLockGuard(Obj, Mtx, boost::try_to_lock);
}
template<typename Clock, typename Duration>
ReadLockGuard try_lock_read(const boost::chrono::time_point<Clock, Duration>& atime) {
return ReadLockGuard(Obj, Mtx, atime);
}
WriteLockGuard lock_write() {
return WriteLockGuard(Obj, Mtx);
}
WriteLockGuard try_lock_write() {
return WriteLockGuard(Obj, Mtx, boost::try_to_lock);
}
template<typename Clock, typename Duration>
WriteLockGuard try_lock_write(const boost::chrono::time_point<Clock, Duration>& atime) {
return WriteLockGuard(Obj, Mtx, atime);
}
const T& no_lock_readable() { return Obj; }
T& no_lock_writeable() { return Obj; }
private:
T Obj;
boost::shared_mutex Mtx;
};
class DestroyLock {
public:
DestroyLock() = default;
struct Guard {
Guard(DestroyLock &lock)
: Lock(lock)
{
lock.UseCount++;
}
~Guard() {
Lock.UseCount--;
}
private:
DestroyLock &Lock;
};
void wait_no_use() {
while(int val = UseCount)
UseCount.wait(val);
}
Guard lock() {
return Guard(*this);
}
private:
std::atomic<int> UseCount;
};
}

131
Src/Common/MemoryPool.hpp Normal file
View File

@@ -0,0 +1,131 @@
#pragma once
#include <boost/pool/pool_alloc.hpp>
namespace AL {
template<unsigned PageSize_PowOf2, unsigned CountPageInChunk_PowOf2, unsigned MaxSize = 0, typename Mutex = boost::details::pool::default_mutex, typename UserAllocator = boost::default_user_allocator_new_delete>
struct BoostPool {
static constexpr unsigned
PageSize = 1 << PageSize_PowOf2,
PagesInChunk = 1 << CountPageInChunk_PowOf2,
ChunkSize = PageSize << CountPageInChunk_PowOf2;
using Page = std::array<std::byte, PageSize>;
using Allocator = boost::fast_pool_allocator<Page, UserAllocator, Mutex, PagesInChunk, MaxSize>;
using SingletonPool = boost::singleton_pool<boost::fast_pool_allocator_tag, PageSize, UserAllocator, Mutex, PagesInChunk, MaxSize>;
using vector = std::vector<Page, Allocator>;
template<uint16_t PagesNum>
class Array {
void *Ptr = nullptr;
public:
Array() {
Ptr = SingletonPool::ordered_malloc(PagesNum);
}
~Array() {
if(Ptr)
SingletonPool::ordered_free(Ptr, PagesNum);
}
Array(const Array &array) {
Ptr = SingletonPool::ordered_malloc(PagesNum);
std::copy((const std::byte*) array.Ptr, (const std::byte*) array.Ptr + PageSize*PagesNum, (std::byte*) Ptr);
}
Array(Array &&pages) {
Ptr = pages.Ptr;
pages.Ptr = nullptr;
}
Array& operator=(const Array &pages) {
if(this == &pages)
return *this;
std::copy((const std::byte*) pages.Ptr, (const std::byte*) pages.Ptr + PageSize*PagesNum, (std::byte*) Ptr);
return *this;
}
Array& operator=(Array &&pages) {
if(this == &pages)
return *this;
std::swap(Ptr, pages.Ptr);
return *this;
}
std::byte* data() { return (std::byte*) Ptr; }
const std::byte* data() const { return (std::byte*) Ptr; }
constexpr size_t size() const { return PageSize*PagesNum; }
std::byte& front() { return *(std::byte*) Ptr; }
const std::byte& front() const { return *(const std::byte*) Ptr; }
std::byte& back() { return *((std::byte*) Ptr + size()); }
const std::byte& back() const { return *((const std::byte*) Ptr + size()); }
std::byte& operator[](size_t index) { return ((std::byte*) Ptr)[index]; }
const std::byte& operator[](size_t index) const { return ((const std::byte*) Ptr)[index]; }
};
class PagePtr {
void *Ptr = nullptr;
public:
PagePtr() {
Ptr = SingletonPool::malloc();
}
~PagePtr() {
if(Ptr)
SingletonPool::free(Ptr);
}
PagePtr(const PagePtr &array) {
Ptr = SingletonPool::malloc();
std::copy((const std::byte*) array.Ptr, (const std::byte*) array.Ptr + PageSize, (std::byte*) Ptr);
}
PagePtr(PagePtr &&pages) {
Ptr = pages.Ptr;
pages.Ptr = nullptr;
}
PagePtr& operator=(const PagePtr &pages) {
if(this == &pages)
return *this;
std::copy((const std::byte*) pages.Ptr, (const std::byte*) pages.Ptr + PageSize, (std::byte*) Ptr);
return *this;
}
PagePtr& operator=(PagePtr &&pages) {
if(this == &pages)
return *this;
std::swap(Ptr, pages.Ptr);
return *this;
}
std::byte* data() { return (std::byte*) Ptr; }
const std::byte* data() const { return (std::byte*) Ptr; }
constexpr size_t size() const { return PageSize; }
std::byte& front() { return *(std::byte*) Ptr; }
const std::byte& front() const { return *(const std::byte*) Ptr; }
std::byte& back() { return *((std::byte*) Ptr + size()); }
const std::byte& back() const { return *((const std::byte*) Ptr + size()); }
std::byte& operator[](size_t index) { return ((std::byte*) Ptr)[index]; }
const std::byte& operator[](size_t index) const { return ((const std::byte*) Ptr)[index]; }
};
};
}

295
Src/Common/Net.cpp Normal file
View File

@@ -0,0 +1,295 @@
#include "Net.hpp"
#include <TOSLib.hpp>
#include <boost/asio/buffer.hpp>
namespace AL::Net {
using namespace TOS;
Server::~Server() {
stop();
wait();
}
bool Server::isStopped() {
return !IsAlive;
}
void Server::stop() {
NeedClose = true;
if(Acceptor.is_open())
Acceptor.close();
}
void Server::wait() {
if(!IsAlive)
return;
Lock.wait();
}
coro<void> Server::async_wait() {
co_await Lock.async_wait();
}
coro<void> Server::run() {
IsAlive.store(true);
try {
while(true) { // TODO: ловить ошибки на async_accept
co_spawn(OnConnect(co_await Acceptor.async_accept()));
}
} catch(const std::exception &exc) {
//if(!NeedClose)
// TODO: std::cout << exc.what() << std::endl;
}
IsAlive.store(false);
Lock.cancel();
}
AsyncSocket::~AsyncSocket() {
boost::lock_guard lock(SendPackets.Mtx);
if(SendPackets.Context)
SendPackets.Context->NeedShutdown = true;
SendPackets.SenderGuard.cancel();
}
void AsyncSocket::pushPackets(std::vector<Packet> *simplePackets, std::vector<SmartPacket> *smartPackets) {
boost::unique_lock lock(SendPackets.Mtx);
if(Socket.is_open()
&& (SendPackets.SimpleBuffer.size() + (simplePackets ? simplePackets->size() : 0) >= MAX_SIMPLE_PACKETS
|| SendPackets.SmartBuffer.size() + (smartPackets ? smartPackets->size() : 0) >= MAX_SMART_PACKETS
|| SendPackets.SizeInQueue >= MAX_PACKETS_SIZE_IN_WAIT))
{
Socket.close();
// TODO: std::cout << "Передоз пакетами, сокет закрыт" << std::endl;
}
if(!Socket.is_open()) {
if(simplePackets)
simplePackets->clear();
if(smartPackets)
smartPackets->clear();
return;
}
size_t addedSize = 0;
if(simplePackets) {
for(Packet &packet : *simplePackets) {
addedSize += packet.size();
SendPackets.SimpleBuffer.push_back(std::move(packet));
}
simplePackets->clear();
}
if(smartPackets) {
for(SmartPacket &packet : *smartPackets) {
addedSize += packet.size();
SendPackets.SmartBuffer.push_back(std::move(packet));
}
smartPackets->clear();
}
SendPackets.SizeInQueue += addedSize;
if(SendPackets.WaitForSemaphore) {
SendPackets.WaitForSemaphore = false;
SendPackets.Semaphore.cancel();
SendPackets.Semaphore.expires_at(boost::posix_time::pos_infin);
}
}
std::string AsyncSocket::getError() const {
return SendPackets.Context->Error;
}
bool AsyncSocket::isAlive() const {
return !SendPackets.Context->NeedShutdown
&& !SendPackets.Context->RunSendShutdowned;
}
coro<> AsyncSocket::read(std::byte *data, uint32_t size) {
while(size) {
if(RecvSize == 0) {
RecvSize = co_await Socket.async_receive(asio::buffer(RecvBuffer.data()+RecvPos, RecvBuffer.size()-RecvPos));
}
uint32_t needRecv = std::min<size_t>(size, RecvSize);
std::copy(RecvBuffer.data()+RecvPos, RecvBuffer.data()+RecvPos+needRecv, data);
data += needRecv;
RecvPos += needRecv;
RecvSize -= needRecv;
size -= needRecv;
if(RecvPos >= RecvBuffer.size())
RecvPos = 0;
}
}
coro<> AsyncSocket::waitForSend() {
asio::deadline_timer waiter(IOC);
while(!SendPackets.SimpleBuffer.empty()
|| !SendPackets.SmartBuffer.empty()
|| SendSize)
{
waiter.expires_from_now(boost::posix_time::milliseconds(1));
co_await waiter.async_wait();
}
}
coro<> AsyncSocket::runSender(std::shared_ptr<AsyncContext> context) {
int NextBuffer = 0;
try {
while(!context->NeedShutdown) {
{
boost::unique_lock lock(SendPackets.Mtx);
if(SendPackets.SimpleBuffer.empty() && SendPackets.SmartBuffer.empty()) {
SendPackets.WaitForSemaphore = true;
auto coroutine = SendPackets.Semaphore.async_wait();
lock.unlock();
try { co_await std::move(coroutine); } catch(...) {}
continue;
} else {
for(int cycle = 0; cycle < 2; cycle++, NextBuffer++) {
if(NextBuffer % 2) {
while(!SendPackets.SimpleBuffer.empty()) {
Packet &packet = SendPackets.SimpleBuffer.front();
if(SendSize+packet.size() >= SendBuffer.size())
break;
size_t packetSize = packet.size();
for(const auto &page : packet.getPages()) {
size_t needCopy = std::min<size_t>(packetSize, NetPool::PageSize);
std::copy(page.data(), page.data()+needCopy, SendBuffer.data()+SendSize);
SendSize += needCopy;
packetSize -= needCopy;
}
SendPackets.SimpleBuffer.pop_front();
}
} else {
while(!SendPackets.SmartBuffer.empty()) {
SmartPacket &packet = SendPackets.SmartBuffer.front();
if(SendSize+packet.size() >= SendBuffer.size())
break;
if(packet.IsStillRelevant && !packet.IsStillRelevant()) {
SendPackets.SmartBuffer.pop_front();
continue;
}
size_t packetSize = packet.size();
for(const auto &page : packet.getPages()) {
size_t needCopy = std::min<size_t>(packetSize, NetPool::PageSize);
std::copy(page.data(), page.data()+needCopy, SendBuffer.data()+SendSize);
SendSize += needCopy;
packetSize -= needCopy;
}
if(packet.OnSend) {
std::optional<SmartPacket> nextPacket = packet.OnSend();
if(nextPacket)
SendPackets.SmartBuffer.push_back(std::move(*nextPacket));
}
SendPackets.SmartBuffer.pop_front();
}
}
}
}
}
if(!SendSize)
continue;
try {
co_await asio::async_write(Socket, asio::buffer(SendBuffer.data(), SendSize));
SendSize = 0;
} catch(const std::exception &exc) {
context->Error = exc.what();
break;
}
}
} catch(...) {}
context->RunSendShutdowned = true;
}
coro<tcp::socket> asyncConnectTo(const std::string address, std::function<void(const std::string&)> onProgress) {
std::string progress;
auto addLog = [&](const std::string &msg) {
progress += '\n';
progress += msg;
if(onProgress)
onProgress('\n'+msg);
};
auto ioc = co_await asio::this_coro::executor;
addLog("Разбор адреса " + address);
auto re = Str::match(address, "((?:\\[[\\d\\w:]+\\])|(?:[\\d\\.]+))(?:\\:(\\d+))?");
std::vector<std::tuple<tcp::endpoint, std::string>> eps;
if(!re) {
re = Str::match(address, "([-_\\.\\w\\d]+)(?:\\:(\\d+))?");
if(!re) {
addLog("Не удалось разобрать адрес");
co_return nullptr;
}
tcp::resolver resv{ioc};
tcp::resolver::results_type result;
addLog("Разрешение имён...");
result = co_await resv.async_resolve(*re->at(1), re->at(2) ? *re->at(2) : "7890");
addLog("Получено " + std::to_string(result.size()) + " точек");
for(auto iter : result) {
std::string addr = iter.endpoint().address().to_string() + ':' + std::to_string(iter.endpoint().port());
std::string hostname = iter.host_name();
if(hostname == addr)
addLog("ep: " + addr);
else
addLog("ep: " + hostname + " (" + addr + ')');
eps.emplace_back(iter.endpoint(), iter.host_name());
}
} else {
eps.emplace_back(tcp::endpoint{asio::ip::make_address(*re->at(1)), (uint16_t) (re->at(2) ? Str::toVal<int>(*re->at(2)) : 7890)}, *re->at(1));
}
for(auto [ep, hostname] : eps) {
addLog("Подключение к " + hostname +" (" + ep.address().to_string() + ':' + std::to_string(ep.port()) + ")");
try {
tcp::socket sock{ioc};
co_await sock.async_connect(ep);
addLog("Подключились");
co_return sock;
} catch(const std::exception &exc) {
addLog(std::string("Сокет не смог установить соединение: ") + exc.what());
}
}
addLog("Не удалось подключится к серверу");
MAKE_ERROR(progress);
}
}

289
Src/Common/Net.hpp Normal file
View File

@@ -0,0 +1,289 @@
#pragma once
#include "MemoryPool.hpp"
#include "Async.hpp"
#include <atomic>
#include <boost/asio.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/write.hpp>
#include <boost/thread.hpp>
#include <boost/circular_buffer.hpp>
namespace AL::Net {
class Server : public AsyncObject {
protected:
std::atomic_bool IsAlive = false, NeedClose = false;
tcp::acceptor Acceptor;
asio::deadline_timer Lock;
std::function<coro<>(tcp::socket)> OnConnect;
public:
Server(asio::io_context &ioc, std::function<coro<>(tcp::socket)> &&onConnect, uint16_t port = 0)
: AsyncObject(ioc), Acceptor(ioc, tcp::endpoint(tcp::v4(), port)), Lock(ioc, boost::posix_time::pos_infin), OnConnect(std::move(onConnect))
{
assert(OnConnect);
co_spawn(run());
IsAlive.store(true);
}
~Server();
bool isStopped();
uint16_t getPort() {
return Acceptor.local_endpoint().port();
}
void stop();
void wait();
coro<void> async_wait();
protected:
coro<void> run();
};
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
static inline T swapEndian(const T &u) { return u; }
#else
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
static inline T swapEndian(const T &u) {
if constexpr (sizeof(T) == 1) {
return u;
} else if constexpr (sizeof(T) == 2) {
return __builtin_bswap16(u);
} else if constexpr (sizeof(T) == 4) {
return __builtin_bswap32(u);
} else if constexpr (sizeof(T) == 8) {
return __builtin_bswap64(u);
} else {
static_assert(sizeof(T) <= 8, "Неподдерживаемый размер для перестановки порядка байтов (Swap Endian)");
return u;
}
}
#endif
// Запись в сторону сокета производится пакетами
// Считывание потоком
using NetPool = BoostPool<12, 14>;
class Packet {
static constexpr size_t MAX_PACKET_SIZE = 1 << 16;
uint16_t Size = 0;
std::vector<NetPool::PagePtr> Pages;
public:
Packet() = default;
Packet(const Packet&) = default;
Packet(Packet&&) = default;
Packet& operator=(const Packet&) = default;
Packet& operator=(Packet&&) = default;
inline Packet& write(const std::byte *data, uint16_t size) {
assert(Size+size < MAX_PACKET_SIZE);
while(size) {
if(Pages.size()*NetPool::PageSize == Size)
Pages.emplace_back();
uint16_t needWrite = std::min<uint16_t>(Pages.size()*NetPool::PageSize-Size, size);
std::byte *ptr = &Pages.back().front() + (Size % NetPool::PageSize);
std::copy(data, data+needWrite, ptr);
Size += needWrite;
size -= needWrite;
}
return *this;
}
template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
inline Packet& write(T u) {
u = swapEndian(u);
write((const std::byte*) &u, sizeof(u));
return *this;
}
inline Packet& write(std::string_view str) {
assert(Size+str.size()+2 < MAX_PACKET_SIZE);
write((uint16_t) str.size());
write((const std::byte*) str.data(), str.size());
return *this;
}
inline Packet& write(const std::string &str) {
return write(std::string_view(str));
}
inline uint16_t size() const { return Size; }
inline const std::vector<NetPool::PagePtr>& getPages() const { return Pages; }
template<typename T, std::enable_if_t<std::is_integral_v<T> or std::is_convertible_v<T, std::string_view>, int> = 0>
inline Packet& operator<<(const T &value) {
if constexpr (std::is_convertible_v<T, std::string_view>)
return write((std::string_view) value);
else
return write(value);
}
void clear() {
clearFast();
Pages.clear();
}
void clearFast() {
Size = 0;
}
Packet& complite(std::vector<std::byte> &out) {
out.resize(Size);
for(size_t pos = 0; pos < Size; pos += NetPool::PageSize) {
const char *data = (const char*) Pages[pos / NetPool::PageSize].data();
std::copy(data, data+std::min<size_t>(Size-pos, NetPool::PageSize), (char*) &out[pos]);
}
return *this;
}
std::vector<std::byte> complite() {
std::vector<std::byte> out;
complite(out);
return out;
}
coro<> sendAndFastClear(tcp::socket &socket) {
for(size_t pos = 0; pos < Size; pos += NetPool::PageSize) {
const char *data = (const char*) Pages[pos / NetPool::PageSize].data();
size_t size = std::min<size_t>(Size-pos, NetPool::PageSize);
co_await asio::async_write(socket, asio::const_buffer(data, size));
}
clearFast();
}
};
class SmartPacket : public Packet {
public:
std::function<bool()> IsStillRelevant;
std::function<std::optional<SmartPacket>()> OnSend;
};
class AsyncSocket : public AsyncObject {
NetPool::Array<32> RecvBuffer, SendBuffer;
size_t RecvPos = 0, RecvSize = 0, SendSize = 0;
tcp::socket Socket;
static constexpr uint32_t
MAX_SIMPLE_PACKETS = 8192,
MAX_SMART_PACKETS = MAX_SIMPLE_PACKETS/4,
MAX_PACKETS_SIZE_IN_WAIT = 1 << 24;
struct AsyncContext {
volatile bool NeedShutdown = false, RunSendShutdowned = false;
std::string Error;
};
struct SendPacketsObj {
boost::mutex Mtx;
bool WaitForSemaphore = false;
asio::deadline_timer Semaphore, SenderGuard;
boost::circular_buffer_space_optimized<Packet> SimpleBuffer;
boost::circular_buffer_space_optimized<SmartPacket> SmartBuffer;
size_t SizeInQueue = 0;
std::shared_ptr<AsyncContext> Context;
SendPacketsObj(asio::io_context &ioc)
: Semaphore(ioc, boost::posix_time::pos_infin), SenderGuard(ioc, boost::posix_time::pos_infin)
{}
} SendPackets;
public:
AsyncSocket(asio::io_context &ioc, tcp::socket &&socket)
: AsyncObject(ioc), Socket(std::move(socket)), SendPackets(ioc)
{
SendPackets.SimpleBuffer.set_capacity(512);
SendPackets.SmartBuffer.set_capacity(SendPackets.SimpleBuffer.capacity()/4);
SendPackets.Context = std::make_shared<AsyncContext>();
boost::asio::socket_base::linger optionLinger(true, 4); // После закрытия сокета оставшиеся данные будут доставлены
Socket.set_option(optionLinger);
boost::asio::ip::tcp::no_delay optionNoDelay(true); // Отключает попытки объёденить данные в крупные пакеты
Socket.set_option(optionNoDelay);
asio::co_spawn(ioc, runSender(SendPackets.Context), asio::detached);
}
~AsyncSocket();
void pushPackets(std::vector<Packet> *simplePackets, std::vector<SmartPacket> *smartPackets = nullptr);
std::string getError() const;
bool isAlive() const;
coro<> read(std::byte *data, uint32_t size);
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() {
if constexpr(std::is_integral_v<T>) {
T value;
co_await read((std::byte*) &value, sizeof(value));
co_return swapEndian(value);
} else {
uint16_t size = co_await read<uint16_t>();
T value(size, ' ');
co_await read((std::byte*) value.data(), size);
co_return value;}
}
coro<> waitForSend();
static inline coro<> read(tcp::socket &socket, std::byte *data, uint32_t size) {
co_await asio::async_read(socket, asio::mutable_buffer(data, size));
}
template<typename T, std::enable_if_t<std::is_integral_v<T> or std::is_same_v<T, std::string>, int> = 0>
static inline coro<T> read(tcp::socket &socket) {
if constexpr(std::is_integral_v<T>) {
T value;
co_await read(socket, (std::byte*) &value, sizeof(value));
co_return swapEndian(value);
} else {
uint16_t size = co_await read<uint16_t>(socket);
T value(size, ' ');
co_await read(socket, (std::byte*) value.data(), size);
co_return value;}
}
static inline coro<> write(tcp::socket &socket, const std::byte *data, uint16_t size) {
co_await asio::async_write(socket, asio::const_buffer(data, size));
}
template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
static inline coro<> write(tcp::socket &socket, T u) {
u = swapEndian(u);
co_await write(socket, (const std::byte*) &u, sizeof(u));
}
static inline coro<> write(tcp::socket &socket, std::string_view str) {
co_await write(socket, (uint16_t) str.size());
co_await write(socket, (const std::byte*) str.data(), str.size());
}
static inline coro<> write(tcp::socket &socket, const std::string &str) {
return write(socket, std::string_view(str));
}
static inline coro<> write(tcp::socket &socket, const char *str) {
return write(socket, std::string_view(str));
}
private:
coro<> runSender(std::shared_ptr<AsyncContext> context);
};
coro<tcp::socket> asyncConnectTo(const std::string address, std::function<void(const std::string&)> onProgress = nullptr);
}