Передача ресурсов клиенту
This commit is contained in:
@@ -136,23 +136,34 @@ struct DefNode_t {};
|
|||||||
|
|
||||||
/* Интерфейс обработчика сессии с сервером */
|
/* Интерфейс обработчика сессии с сервером */
|
||||||
class IServerSession {
|
class IServerSession {
|
||||||
struct ArrayHasher {
|
// struct ArrayHasher {
|
||||||
std::size_t operator()(const Hash_t& a) const {
|
// std::size_t operator()(const Hash_t& a) const {
|
||||||
std::size_t h = 0;
|
// std::size_t h = 0;
|
||||||
for (auto e : a)
|
// for (auto e : a)
|
||||||
h ^= std::hash<int>{}(e) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
// h ^= std::hash<int>{}(e) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||||
|
|
||||||
return h;
|
// return h;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct AssetEntry {
|
||||||
|
EnumAssets Type;
|
||||||
|
ResourceId Id;
|
||||||
|
std::string Domain, Key;
|
||||||
|
Resource Res;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint64_t TIME_BEFORE_UNLOAD_RESOURCE = 180;
|
||||||
struct {
|
struct {
|
||||||
std::unordered_map<Hash_t, BinaryResource, ArrayHasher> Resources;
|
// Оперируемые ресурсы
|
||||||
|
std::unordered_map<Hash_t, AssetEntry> Assets;
|
||||||
|
// Недавно использованные ресурсы, пока хранятся здесь в течении TIME_BEFORE_UNLOAD_RESOURCE секунд
|
||||||
|
std::unordered_map<Hash_t, std::pair<AssetEntry, uint64_t>> NotInUseAssets;
|
||||||
} Binary;
|
} Binary;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::unordered_map<DefVoxelId, DefVoxel_t> DefVoxel;
|
std::unordered_map<DefVoxelId, DefVoxel_t> DefVoxel;
|
||||||
std::unordered_map<DefNodeId, DefNode_t> DefNode;
|
std::unordered_map<DefNodeId, DefNode_t> DefNode;
|
||||||
std::unordered_map<DefWorldId, DefWorldInfo> DefWorld;
|
std::unordered_map<DefWorldId, DefWorldInfo> DefWorld;
|
||||||
std::unordered_map<DefPortalId, DefPortalInfo> DefPortal;
|
std::unordered_map<DefPortalId, DefPortalInfo> DefPortal;
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#include "AssetsManager.hpp"
|
#include "AssetsManager.hpp"
|
||||||
#include "Common/Abstract.hpp"
|
#include "Common/Abstract.hpp"
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
|
#include <chrono>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
@@ -114,7 +116,7 @@ AssetsManager::AssetsManager(boost::asio::io_context &ioc, const fs::path &cache
|
|||||||
}
|
}
|
||||||
|
|
||||||
sql = R"(
|
sql = R"(
|
||||||
SELECT data inline_cache where sha256=?;
|
SELECT data FROM inline_cache where sha256=?;
|
||||||
)";
|
)";
|
||||||
|
|
||||||
if(sqlite3_prepare_v2(DB, sql, -1, &STMT_INLINE_GET, nullptr) != SQLITE_OK) {
|
if(sqlite3_prepare_v2(DB, sql, -1, &STMT_INLINE_GET, nullptr) != SQLITE_OK) {
|
||||||
@@ -157,16 +159,21 @@ AssetsManager::~AssetsManager() {
|
|||||||
STMT_DISK_INSERT, STMT_DISK_UPDATE_TIME, STMT_DISK_REMOVE, STMT_DISK_CONTAINS,
|
STMT_DISK_INSERT, STMT_DISK_UPDATE_TIME, STMT_DISK_REMOVE, STMT_DISK_CONTAINS,
|
||||||
STMT_DISK_SUM, STMT_DISK_COUNT, STMT_INLINE_INSERT, STMT_INLINE_GET,
|
STMT_DISK_SUM, STMT_DISK_COUNT, STMT_INLINE_INSERT, STMT_INLINE_GET,
|
||||||
STMT_INLINE_UPDATE_TIME, STMT_INLINE_SUM, STMT_INLINE_COUNT
|
STMT_INLINE_UPDATE_TIME, STMT_INLINE_SUM, STMT_INLINE_COUNT
|
||||||
})
|
}) {
|
||||||
if(stmt)
|
if(stmt)
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
if(DB)
|
if(DB)
|
||||||
sqlite3_close(DB);
|
sqlite3_close(DB);
|
||||||
|
|
||||||
|
OffThread.join();
|
||||||
|
|
||||||
|
LOG.info() << "Хранилище кеша закрыто";
|
||||||
}
|
}
|
||||||
|
|
||||||
coro<> AssetsManager::asyncDestructor() {
|
coro<> AssetsManager::asyncDestructor() {
|
||||||
assert(NeedShutdown); // Должен быть вызван нормальный shutdown
|
NeedShutdown = true;
|
||||||
co_await IAsyncDestructible::asyncDestructor();
|
co_await IAsyncDestructible::asyncDestructor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +182,7 @@ void AssetsManager::readWriteThread(AsyncUseControl::Lock lock) {
|
|||||||
std::vector<fs::path> assets;
|
std::vector<fs::path> assets;
|
||||||
size_t maxCacheDatabaseSize, maxLifeTime;
|
size_t maxCacheDatabaseSize, maxLifeTime;
|
||||||
|
|
||||||
while(!NeedShutdown && !WriteQueue.get_read().empty()) {
|
while(!NeedShutdown || !WriteQueue.get_read().empty()) {
|
||||||
// Получить новые данные
|
// Получить новые данные
|
||||||
if(Changes.get_read().AssetsChange) {
|
if(Changes.get_read().AssetsChange) {
|
||||||
auto lock = Changes.lock();
|
auto lock = Changes.lock();
|
||||||
@@ -377,9 +384,11 @@ void AssetsManager::readWriteThread(AsyncUseControl::Lock lock) {
|
|||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
} catch(const std::exception& exc) {
|
} catch(const std::exception& exc) {
|
||||||
LOG.warn() << "Ошибка в работе потока: " << exc.what();
|
LOG.warn() << "Ошибка в работе потока:\n" << exc.what();
|
||||||
IssuedAnError = true;
|
IssuedAnError = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,17 +146,6 @@ public:
|
|||||||
lock->FullRecheck = true;
|
lock->FullRecheck = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Уведомление о завершении работы
|
|
||||||
void prepareShutdown() {
|
|
||||||
NeedShutdown = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// После этого вызова уже нельзя будет обращатся ко внешним ресурсам
|
|
||||||
void shutdown() {
|
|
||||||
assert(NeedShutdown);
|
|
||||||
OffThread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasError() {
|
bool hasError() {
|
||||||
return IssuedAnError;
|
return IssuedAnError;
|
||||||
}
|
}
|
||||||
@@ -208,7 +197,7 @@ private:
|
|||||||
bool NeedShutdown = false, IssuedAnError = false;
|
bool NeedShutdown = false, IssuedAnError = false;
|
||||||
std::thread OffThread;
|
std::thread OffThread;
|
||||||
|
|
||||||
|
|
||||||
virtual coro<> asyncDestructor();
|
virtual coro<> asyncDestructor();
|
||||||
AssetsManager(boost::asio::io_context &ioc, const fs::path &cachePath,
|
AssetsManager(boost::asio::io_context &ioc, const fs::path &cachePath,
|
||||||
size_t maxCacheDatabaseSize, size_t maxLifeTime);
|
size_t maxCacheDatabaseSize, size_t maxLifeTime);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "Client/Abstract.hpp"
|
#include "Client/Abstract.hpp"
|
||||||
#include "Common/Abstract.hpp"
|
#include "Common/Abstract.hpp"
|
||||||
#include "Common/Net.hpp"
|
#include "Common/Net.hpp"
|
||||||
|
#include "TOSAsync.hpp"
|
||||||
#include "TOSLib.hpp"
|
#include "TOSLib.hpp"
|
||||||
#include "glm/ext/quaternion_geometric.hpp"
|
#include "glm/ext/quaternion_geometric.hpp"
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
@@ -18,6 +19,28 @@
|
|||||||
|
|
||||||
namespace LV::Client {
|
namespace LV::Client {
|
||||||
|
|
||||||
|
ServerSession::ServerSession(asio::io_context &ioc, std::unique_ptr<Net::AsyncSocket>&& socket)
|
||||||
|
: IAsyncDestructible(ioc), Socket(std::move(socket)), NetInputPackets(1024)
|
||||||
|
{
|
||||||
|
assert(Socket.get());
|
||||||
|
|
||||||
|
try {
|
||||||
|
AM = AssetsManager::Create(ioc, "Cache");
|
||||||
|
asio::co_spawn(ioc, run(AUC.use()), asio::detached);
|
||||||
|
// TODO: добавить оптимизацию для подключения клиента к внутреннему серверу
|
||||||
|
} catch(const std::exception &exc) {
|
||||||
|
MAKE_ERROR("Ошибка инициализации обработчика объекта подключения к серверу:\n" << exc.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coro<> ServerSession::asyncDestructor() {
|
||||||
|
co_await IAsyncDestructible::asyncDestructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ParsedPacket::~ParsedPacket() = default;
|
ParsedPacket::~ParsedPacket() = default;
|
||||||
|
|
||||||
struct PP_Content_ChunkVoxels : public ParsedPacket {
|
struct PP_Content_ChunkVoxels : public ParsedPacket {
|
||||||
@@ -88,20 +111,9 @@ struct PP_Definition_FreeNode : public ParsedPacket {
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PP_Resource_InitResSend : public ParsedPacket {
|
|
||||||
Hash_t Hash;
|
|
||||||
BinaryResource Resource;
|
|
||||||
|
|
||||||
PP_Resource_InitResSend(Hash_t hash, BinaryResource res)
|
|
||||||
: ParsedPacket(ToClient::L1::Resource, (uint8_t) ToClient::L2Resource::InitResSend), Hash(hash), Resource(res)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
using namespace TOS;
|
using namespace TOS;
|
||||||
|
|
||||||
ServerSession::~ServerSession() {
|
ServerSession::~ServerSession() {
|
||||||
WorkDeadline.cancel();
|
|
||||||
UseLock.wait_no_use();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coro<> ServerSession::asyncAuthorizeWithServer(tcp::socket &socket, const std::string username, const std::string token, int a_ar_r, std::function<void(const std::string&)> onProgress) {
|
coro<> ServerSession::asyncAuthorizeWithServer(tcp::socket &socket, const std::string username, const std::string token, int a_ar_r, std::function<void(const std::string&)> onProgress) {
|
||||||
@@ -338,13 +350,6 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) {
|
|||||||
ParsedPacket *pack;
|
ParsedPacket *pack;
|
||||||
while(NetInputPackets.pop(pack)) {
|
while(NetInputPackets.pop(pack)) {
|
||||||
if(pack->Level1 == ToClient::L1::Definition) {
|
if(pack->Level1 == ToClient::L1::Definition) {
|
||||||
ToClient::L2Resource l2 = ToClient::L2Resource(pack->Level2);
|
|
||||||
if(l2 == ToClient::L2Resource::InitResSend) {
|
|
||||||
PP_Resource_InitResSend &p = *dynamic_cast<PP_Resource_InitResSend*>(pack);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(pack->Level1 == ToClient::L1::Definition) {
|
|
||||||
ToClient::L2Definition l2 = ToClient::L2Definition(pack->Level2);
|
ToClient::L2Definition l2 = ToClient::L2Definition(pack->Level2);
|
||||||
|
|
||||||
if(l2 == ToClient::L2Definition::Voxel) {
|
if(l2 == ToClient::L2Definition::Voxel) {
|
||||||
@@ -471,21 +476,17 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coro<> ServerSession::run() {
|
void ServerSession::setRenderSession(IRenderSession* session) {
|
||||||
auto useLock = UseLock.lock();
|
RS = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
coro<> ServerSession::run(AsyncUseControl::Lock) {
|
||||||
try {
|
try {
|
||||||
while(!IsGoingShutdown && IsConnected) {
|
while(!IsGoingShutdown && IsConnected) {
|
||||||
co_await readPacket(*Socket);
|
co_await readPacket(*Socket);
|
||||||
}
|
}
|
||||||
} catch(const std::exception &exc) {
|
} catch(const std::exception &exc) {
|
||||||
// if(const auto *errc = dynamic_cast<const boost::system::system_error*>(&exc);
|
LOG.error() << "Ошибка обработки сокета:\n" << exc.what();
|
||||||
// errc && errc->code() == boost::asio::error::operation_aborted)
|
|
||||||
// {
|
|
||||||
// co_return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
TOS::Logger("ServerSession").warn() << exc.what();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IsConnected = false;
|
IsConnected = false;
|
||||||
@@ -551,19 +552,33 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
|
|||||||
case ToClient::L2Resource::Bind:
|
case ToClient::L2Resource::Bind:
|
||||||
{
|
{
|
||||||
uint32_t count = co_await sock.read<uint32_t>();
|
uint32_t count = co_await sock.read<uint32_t>();
|
||||||
|
AsyncContext.ThisTickEntry.AssetsBinds.reserve(AsyncContext.ThisTickEntry.AssetsBinds.size()+count);
|
||||||
|
|
||||||
for(size_t iter = 0; iter < count; iter++) {
|
for(size_t iter = 0; iter < count; iter++) {
|
||||||
uint8_t type = co_await sock.read<uint8_t>();
|
uint8_t type = co_await sock.read<uint8_t>();
|
||||||
uint32_t id = co_await sock.read<uint32_t>();
|
uint32_t id = co_await sock.read<uint32_t>();
|
||||||
|
std::string domain, key;
|
||||||
|
domain = co_await sock.read<std::string>();
|
||||||
|
key = co_await sock.read<std::string>();
|
||||||
Hash_t hash;
|
Hash_t hash;
|
||||||
co_await sock.read((std::byte*) hash.data(), hash.size());
|
co_await sock.read((std::byte*) hash.data(), hash.size());
|
||||||
|
|
||||||
|
AsyncContext.ThisTickEntry.AssetsBinds.emplace_back(
|
||||||
|
(EnumAssets) type, (ResourceId) id, std::move(domain),
|
||||||
|
std::move(key), hash
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case ToClient::L2Resource::Lost:
|
case ToClient::L2Resource::Lost:
|
||||||
{
|
{
|
||||||
uint32_t count = co_await sock.read<uint32_t>();
|
uint32_t count = co_await sock.read<uint32_t>();
|
||||||
|
AsyncContext.ThisTickEntry.AssetsLost.reserve(AsyncContext.ThisTickEntry.AssetsLost.size()+count);
|
||||||
|
|
||||||
for(size_t iter = 0; iter < count; iter++) {
|
for(size_t iter = 0; iter < count; iter++) {
|
||||||
uint8_t type = co_await sock.read<uint8_t>();
|
// uint8_t type = co_await sock.read<uint8_t>();
|
||||||
uint32_t id = co_await sock.read<uint32_t>();
|
uint32_t id = co_await sock.read<uint32_t>();
|
||||||
|
|
||||||
|
AsyncContext.ThisTickEntry.AssetsLost.push_back(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case ToClient::L2Resource::InitResSend:
|
case ToClient::L2Resource::InitResSend:
|
||||||
@@ -571,26 +586,41 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
|
|||||||
uint32_t size = co_await sock.read<uint32_t>();
|
uint32_t size = co_await sock.read<uint32_t>();
|
||||||
Hash_t hash;
|
Hash_t hash;
|
||||||
co_await sock.read((std::byte*) hash.data(), hash.size());
|
co_await sock.read((std::byte*) hash.data(), hash.size());
|
||||||
|
ResourceId id = co_await sock.read<uint32_t>();
|
||||||
|
EnumAssets type = (EnumAssets) co_await sock.read<uint8_t>();
|
||||||
|
std::string domain = co_await sock.read<std::string>();
|
||||||
|
std::string key = co_await sock.read<std::string>();
|
||||||
|
|
||||||
uint32_t chunkSize = co_await sock.read<uint32_t>();
|
AsyncContext.AssetsLoading[hash] = AssetLoading{
|
||||||
assert(chunkSize < std::pow(2, 26));
|
type, id, std::move(domain), std::move(key),
|
||||||
|
std::u8string(size, '\0'), 0
|
||||||
std::u8string data(size, '\0');
|
};
|
||||||
|
|
||||||
co_await sock.read((std::byte*) data.data(), data.size());
|
|
||||||
|
|
||||||
PP_Resource_InitResSend *packet = new PP_Resource_InitResSend(
|
|
||||||
hash,
|
|
||||||
std::make_shared<std::u8string>(std::move(data))
|
|
||||||
);
|
|
||||||
|
|
||||||
while(!NetInputPackets.push(packet));
|
|
||||||
|
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
case ToClient::L2Resource::ChunkSend:
|
case ToClient::L2Resource::ChunkSend:
|
||||||
|
{
|
||||||
|
Hash_t hash;
|
||||||
|
co_await sock.read((std::byte*) hash.data(), hash.size());
|
||||||
|
uint32_t size = co_await sock.read<uint32_t>();
|
||||||
|
AssetLoading& al = AsyncContext.AssetsLoading.at(hash);
|
||||||
|
if(al.Data.size()-al.Offset < size)
|
||||||
|
MAKE_ERROR("Несоответствие ожидаемого размера ресурса");
|
||||||
|
|
||||||
|
co_await sock.read((std::byte*) al.Data.data() + al.Offset, size);
|
||||||
|
al.Offset += size;
|
||||||
|
|
||||||
|
if(al.Offset == al.Data.size()) {
|
||||||
|
// Ресурс полностью загружен
|
||||||
|
AsyncContext.LoadedAssets.lock()->emplace_back(
|
||||||
|
al.Type, al.Id, std::move(al.Domain), std::move(al.Key), std::move(al.Data)
|
||||||
|
);
|
||||||
|
|
||||||
|
AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash));
|
||||||
|
}
|
||||||
|
|
||||||
co_return;
|
co_return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
protocolError();
|
protocolError();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,15 @@
|
|||||||
#include "Common/Lockable.hpp"
|
#include "Common/Lockable.hpp"
|
||||||
#include "Common/Net.hpp"
|
#include "Common/Net.hpp"
|
||||||
#include "Common/Packets.hpp"
|
#include "Common/Packets.hpp"
|
||||||
|
#include "TOSAsync.hpp"
|
||||||
#include <TOSLib.hpp>
|
#include <TOSLib.hpp>
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/lockfree/spsc_queue.hpp>
|
#include <boost/lockfree/spsc_queue.hpp>
|
||||||
#include <Client/ResourceCache.hpp>
|
#include <Client/AssetsManager.hpp>
|
||||||
|
#include <queue>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
namespace LV::Client {
|
namespace LV::Client {
|
||||||
@@ -26,18 +29,100 @@ struct ParsedPacket {
|
|||||||
virtual ~ParsedPacket();
|
virtual ~ParsedPacket();
|
||||||
};
|
};
|
||||||
|
|
||||||
class ServerSession : public AsyncObject, public IServerSession, public ISurfaceEventListener {
|
class ServerSession : public IAsyncDestructible, public IServerSession, public ISurfaceEventListener {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<ServerSession>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Ptr Create(asio::io_context &ioc, std::unique_ptr<Net::AsyncSocket> &&socket) {
|
||||||
|
return createShared(ioc, new ServerSession(ioc, std::move(socket)));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ServerSession();
|
||||||
|
|
||||||
|
// Авторизоваться или (зарегистрироваться и авторизоваться) или зарегистрироваться
|
||||||
|
static coro<> asyncAuthorizeWithServer(tcp::socket &socket, const std::string username, const std::string token, int a_ar_r, std::function<void(const std::string&)> onProgress = nullptr);
|
||||||
|
// Начать игровой протокол в авторизированном сокете
|
||||||
|
static coro<std::unique_ptr<Net::AsyncSocket>> asyncInitGameProtocol(asio::io_context &ioc, tcp::socket &&socket, std::function<void(const std::string&)> onProgress = nullptr);
|
||||||
|
|
||||||
|
void shutdown(EnumDisconnect type);
|
||||||
|
|
||||||
|
bool isConnected() {
|
||||||
|
return Socket->isAlive() && IsConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISurfaceEventListener
|
||||||
|
|
||||||
|
virtual void onResize(uint32_t width, uint32_t height) override;
|
||||||
|
virtual void onChangeFocusState(bool isFocused) override;
|
||||||
|
virtual void onCursorPosChange(int32_t width, int32_t height) override;
|
||||||
|
virtual void onCursorMove(float xMove, float yMove) override;
|
||||||
|
|
||||||
|
virtual void onCursorBtn(EnumCursorBtn btn, bool state) override;
|
||||||
|
virtual void onKeyboardBtn(int btn, int state) override;
|
||||||
|
virtual void onJoystick() override;
|
||||||
|
|
||||||
|
// IServerSession
|
||||||
|
|
||||||
|
virtual void atFreeDrawTime(GlobalTime gTime, float dTime) override;
|
||||||
|
void setRenderSession(IRenderSession* session);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TOS::Logger LOG = "ServerSession";
|
||||||
|
|
||||||
std::unique_ptr<Net::AsyncSocket> Socket;
|
std::unique_ptr<Net::AsyncSocket> Socket;
|
||||||
IRenderSession *RS = nullptr;
|
IRenderSession *RS = nullptr;
|
||||||
|
|
||||||
// Обработчик кеша ресурсов сервера
|
// Обработчик кеша ресурсов сервера
|
||||||
CacheHandler::Ptr CHDB;
|
AssetsManager::Ptr AM;
|
||||||
|
|
||||||
|
struct AssetLoading {
|
||||||
|
EnumAssets Type;
|
||||||
|
ResourceId Id;
|
||||||
|
std::string Domain, Key;
|
||||||
|
std::u8string Data;
|
||||||
|
size_t Offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssetBindEntry {
|
||||||
|
EnumAssets Type;
|
||||||
|
ResourceId Id;
|
||||||
|
std::string Domain, Key;
|
||||||
|
Hash_t Hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TickData {
|
||||||
|
std::vector<WorldId_t> LostWorld;
|
||||||
|
// std::vector<std::pair<WorldId_t, DefWorld>>
|
||||||
|
|
||||||
|
// Потерянные из видимости ресурсы
|
||||||
|
std::vector<ResourceId> AssetsLost;
|
||||||
|
// Новые привязки ресурсов
|
||||||
|
std::vector<AssetBindEntry> AssetsBinds;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
// Сюда обращается ветка, обрабатывающая сокет; run()
|
||||||
|
// Получение ресурсов с сервера
|
||||||
|
std::unordered_map<Hash_t, AssetLoading> AssetsLoading;
|
||||||
|
// Получение привязок
|
||||||
|
|
||||||
|
// Накопление данных за такт сервера
|
||||||
|
TickData ThisTickEntry;
|
||||||
|
|
||||||
|
// Обменный пункт
|
||||||
|
// Привязки ресурсов
|
||||||
|
TOS::SpinlockObject<std::vector<AssetEntry>> AssetsBindings;
|
||||||
|
// Полученные ресурсы с сервера
|
||||||
|
TOS::SpinlockObject<std::vector<AssetEntry>> LoadedAssets;
|
||||||
|
// Пакеты обновлений игрового мира
|
||||||
|
TOS::SpinlockObject<std::queue<TickData>> TickSequence;
|
||||||
|
} AsyncContext;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DestroyLock UseLock;
|
|
||||||
bool IsConnected = true, IsGoingShutdown = false;
|
bool IsConnected = true, IsGoingShutdown = false;
|
||||||
|
|
||||||
TOS::Logger LOG = "ServerSession";
|
|
||||||
|
|
||||||
boost::lockfree::spsc_queue<ParsedPacket*> NetInputPackets;
|
boost::lockfree::spsc_queue<ParsedPacket*> NetInputPackets;
|
||||||
|
|
||||||
// PYR - поворот камеры по осям xyz в радианах, PYR_Offset для сглаживание поворота
|
// PYR - поворот камеры по осям xyz в радианах, PYR_Offset для сглаживание поворота
|
||||||
@@ -59,70 +144,20 @@ class ServerSession : public AsyncObject, public IServerSession, public ISurface
|
|||||||
|
|
||||||
GlobalTime LastSendPYR_POS;
|
GlobalTime LastSendPYR_POS;
|
||||||
|
|
||||||
public:
|
// Приём данных с сокета
|
||||||
// Нужен сокет, на котором только что был согласован игровой протокол (asyncInitGameProtocol)
|
coro<> run(AsyncUseControl::Lock);
|
||||||
ServerSession(asio::io_context &ioc, std::unique_ptr<Net::AsyncSocket> &&socket, IRenderSession *rs = nullptr)
|
|
||||||
: AsyncObject(ioc), Socket(std::move(socket)), RS(rs), NetInputPackets(1024)
|
|
||||||
{
|
|
||||||
assert(Socket.get());
|
|
||||||
|
|
||||||
try {
|
|
||||||
fs::create_directories("Cache");
|
|
||||||
CHDB = CacheHandlerBasic::Create(ioc, "Cache");
|
|
||||||
|
|
||||||
// Отправка информации о загруженном кеше
|
|
||||||
// TODO: добавить оптимизацию для подключения клиента к внутреннему серверу
|
|
||||||
auto [data, count] = CHDB->getAll();
|
|
||||||
Net::Packet packet;
|
|
||||||
packet << uint32_t(count);
|
|
||||||
packet.write((const std::byte*) data.data(), data.size());
|
|
||||||
Socket->pushPacket(std::move(packet));
|
|
||||||
} catch(const std::exception &exc) {
|
|
||||||
MAKE_ERROR("Ошибка инициализации обработчика кеша ресурсов сервера:\n" << exc.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
co_spawn(run());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ServerSession();
|
|
||||||
|
|
||||||
// Авторизоваться или (зарегистрироваться и авторизоваться) или зарегистрироваться
|
|
||||||
static coro<> asyncAuthorizeWithServer(tcp::socket &socket, const std::string username, const std::string token, int a_ar_r, std::function<void(const std::string&)> onProgress = nullptr);
|
|
||||||
// Начать игровой протокол в авторизированном сокете
|
|
||||||
static coro<std::unique_ptr<Net::AsyncSocket>> asyncInitGameProtocol(asio::io_context &ioc, tcp::socket &&socket, std::function<void(const std::string&)> onProgress = nullptr);
|
|
||||||
|
|
||||||
void shutdown(EnumDisconnect type);
|
|
||||||
|
|
||||||
bool isConnected() {
|
|
||||||
return Socket->isAlive() && IsConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waitShutdown() {
|
|
||||||
UseLock.wait_no_use();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ISurfaceEventListener
|
|
||||||
|
|
||||||
virtual void onResize(uint32_t width, uint32_t height) override;
|
|
||||||
virtual void onChangeFocusState(bool isFocused) override;
|
|
||||||
virtual void onCursorPosChange(int32_t width, int32_t height) override;
|
|
||||||
virtual void onCursorMove(float xMove, float yMove) override;
|
|
||||||
|
|
||||||
virtual void onCursorBtn(EnumCursorBtn btn, bool state) override;
|
|
||||||
virtual void onKeyboardBtn(int btn, int state) override;
|
|
||||||
virtual void onJoystick() override;
|
|
||||||
|
|
||||||
virtual void atFreeDrawTime(GlobalTime gTime, float dTime) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
coro<> run();
|
|
||||||
void protocolError();
|
void protocolError();
|
||||||
coro<> readPacket(Net::AsyncSocket &sock);
|
coro<> readPacket(Net::AsyncSocket &sock);
|
||||||
coro<> rP_System(Net::AsyncSocket &sock);
|
coro<> rP_System(Net::AsyncSocket &sock);
|
||||||
coro<> rP_Resource(Net::AsyncSocket &sock);
|
coro<> rP_Resource(Net::AsyncSocket &sock);
|
||||||
coro<> rP_Definition(Net::AsyncSocket &sock);
|
coro<> rP_Definition(Net::AsyncSocket &sock);
|
||||||
coro<> rP_Content(Net::AsyncSocket &sock);
|
coro<> rP_Content(Net::AsyncSocket &sock);
|
||||||
|
|
||||||
|
|
||||||
|
// Нужен сокет, на котором только что был согласован игровой протокол (asyncInitGameProtocol)
|
||||||
|
ServerSession(asio::io_context &ioc, std::unique_ptr<Net::AsyncSocket> &&socket);
|
||||||
|
|
||||||
|
virtual coro<> asyncDestructor() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2215,7 +2215,8 @@ void Vulkan::gui_MainMenu() {
|
|||||||
std::unique_ptr<Net::AsyncSocket> sock = std::move(ConnectionProgress.Socket);
|
std::unique_ptr<Net::AsyncSocket> sock = std::move(ConnectionProgress.Socket);
|
||||||
Game.RSession = std::make_unique<VulkanRenderSession>();
|
Game.RSession = std::make_unique<VulkanRenderSession>();
|
||||||
*this << Game.RSession;
|
*this << Game.RSession;
|
||||||
Game.Session = std::make_unique<ServerSession>(IOC, std::move(sock), Game.RSession.get());
|
Game.Session = ServerSession::Create(IOC, std::move(sock));
|
||||||
|
Game.Session->setRenderSession(Game.RSession.get());
|
||||||
Game.RSession->setServerSession(Game.Session.get());
|
Game.RSession->setServerSession(Game.Session.get());
|
||||||
Game.ImGuiInterfaces.push_back(&Vulkan::gui_ConnectedToServer);
|
Game.ImGuiInterfaces.push_back(&Vulkan::gui_ConnectedToServer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ public:
|
|||||||
DestroyLock UseLock;
|
DestroyLock UseLock;
|
||||||
std::thread MainThread;
|
std::thread MainThread;
|
||||||
std::shared_ptr<VulkanRenderSession> RSession;
|
std::shared_ptr<VulkanRenderSession> RSession;
|
||||||
std::unique_ptr<ServerSession> Session;
|
ServerSession::Ptr Session;
|
||||||
|
|
||||||
std::list<void (Vulkan::*)()> ImGuiInterfaces;
|
std::list<void (Vulkan::*)()> ImGuiInterfaces;
|
||||||
std::unique_ptr<ServerObj> Server;
|
std::unique_ptr<ServerObj> Server;
|
||||||
|
|||||||
@@ -411,8 +411,6 @@ using AssetsTexture = ResourceId;
|
|||||||
using AssetsSound = ResourceId;
|
using AssetsSound = ResourceId;
|
||||||
using AssetsFont = ResourceId;
|
using AssetsFont = ResourceId;
|
||||||
|
|
||||||
using BinaryResource = std::shared_ptr<const std::u8string>;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Определения контента, доставляются клиентам сразу
|
Определения контента, доставляются клиентам сразу
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -487,6 +487,10 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
|
|||||||
nextRequest = std::move(lock->NextRequest);
|
nextRequest = std::move(lock->NextRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(AssetsInWork.AssetsPacket.size()) {
|
||||||
|
toSend.push_back(std::move(AssetsInWork.AssetsPacket));
|
||||||
|
}
|
||||||
|
|
||||||
Socket.pushPackets(&toSend);
|
Socket.pushPackets(&toSend);
|
||||||
toSend.clear();
|
toSend.clear();
|
||||||
|
|
||||||
@@ -759,7 +763,7 @@ void RemoteClient::NetworkAndResource_t::decrementAssets(ResUses_t::RefAssets_t&
|
|||||||
<< uint32_t(lost.size());
|
<< uint32_t(lost.size());
|
||||||
|
|
||||||
for(auto& [type, id] : lost)
|
for(auto& [type, id] : lost)
|
||||||
NextPacket << uint8_t(type) << uint32_t(id);
|
NextPacket /* << uint8_t(type)*/ << uint32_t(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,6 +799,48 @@ void RemoteClient::onUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LastPos = cameraPos;
|
LastPos = cameraPos;
|
||||||
|
|
||||||
|
// Отправка ресурсов
|
||||||
|
if(!AssetsInWork.ToSend.empty()) {
|
||||||
|
auto& toSend = AssetsInWork.ToSend;
|
||||||
|
size_t chunkSize = std::max<size_t>(1'024'000 / toSend.size(), 4096);
|
||||||
|
|
||||||
|
Net::Packet& p = AssetsInWork.AssetsPacket;
|
||||||
|
|
||||||
|
bool hasFullSended = false;
|
||||||
|
|
||||||
|
for(auto& [type, domain, key, id, res, sended] : toSend) {
|
||||||
|
if(sended == 0) {
|
||||||
|
// Оповещаем о начале отправки ресурса
|
||||||
|
p << (uint8_t) ToClient::L1::Resource
|
||||||
|
<< (uint8_t) ToClient::L2Resource::InitResSend
|
||||||
|
<< uint32_t(res.size());
|
||||||
|
p.write((const std::byte*) res.hash().data(), 32);
|
||||||
|
p << uint32_t(id) << uint8_t(type) << domain << key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отправляем чанк
|
||||||
|
size_t willSend = std::min(chunkSize, res.size()-sended);
|
||||||
|
p << (uint8_t) ToClient::L1::Resource
|
||||||
|
<< (uint8_t) ToClient::L2Resource::ChunkSend;
|
||||||
|
p.write((const std::byte*) res.hash().data(), 32);
|
||||||
|
p << uint32_t(willSend);
|
||||||
|
p.write(res.data() + sended, willSend);
|
||||||
|
sended += willSend;
|
||||||
|
|
||||||
|
if(sended == willSend) {
|
||||||
|
hasFullSended = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hasFullSended) {
|
||||||
|
for(ssize_t iter = toSend.size()-1; iter > 0; iter--) {
|
||||||
|
if(std::get<4>(toSend[iter]).size() == std::get<5>(toSend[iter])) {
|
||||||
|
toSend.erase(toSend.begin()+iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::tuple<WorldId_t, Pos::Object, uint8_t>> RemoteClient::getViewPoints() {
|
std::vector<std::tuple<WorldId_t, Pos::Object, uint8_t>> RemoteClient::getViewPoints() {
|
||||||
|
|||||||
@@ -313,6 +313,8 @@ class RemoteClient {
|
|||||||
// Отправляемые на клиент ресурсы
|
// Отправляемые на клиент ресурсы
|
||||||
// Тип, домен, ключ, идентификатор, ресурс, количество отправленных байт
|
// Тип, домен, ключ, идентификатор, ресурс, количество отправленных байт
|
||||||
std::vector<std::tuple<EnumAssets, std::string, std::string, ResourceId, Resource, size_t>> ToSend;
|
std::vector<std::tuple<EnumAssets, std::string, std::string, ResourceId, Resource, size_t>> ToSend;
|
||||||
|
// Пакет с ресурсами
|
||||||
|
Net::Packet AssetsPacket;
|
||||||
} AssetsInWork;
|
} AssetsInWork;
|
||||||
|
|
||||||
TOS::SpinlockObject<NetworkAndResource_t> NetworkAndResource;
|
TOS::SpinlockObject<NetworkAndResource_t> NetworkAndResource;
|
||||||
|
|||||||
@@ -227,12 +227,13 @@ 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(asio::io_context &ioc, T *ptr)
|
static std::shared_ptr<T> createShared(asio::io_context &ioc, T *ptr)
|
||||||
{
|
{
|
||||||
return std::shared_ptr<T>(ptr, [&ioc = ioc](T *ptr) {
|
return std::shared_ptr<T>(ptr, [&ioc](T *ptr) {
|
||||||
boost::asio::co_spawn(ioc, [&ioc = ioc](IAsyncDestructible *ptr) -> coro<> {
|
boost::asio::co_spawn(ioc,
|
||||||
try { co_await ptr->asyncDestructor(); } catch(...) { }
|
[ptr, &ioc]() mutable -> coro<> {
|
||||||
delete ptr;
|
try { co_await dynamic_cast<IAsyncDestructible*>(ptr)->asyncDestructor(); } catch(...) { }
|
||||||
co_return;
|
asio::post(ioc, [ptr](){ delete ptr; });
|
||||||
} (ptr), boost::asio::detached);
|
},
|
||||||
|
boost::asio::detached);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,23 +241,25 @@ protected:
|
|||||||
static coro<std::shared_ptr<T>> createShared(T *ptr)
|
static coro<std::shared_ptr<T>> createShared(T *ptr)
|
||||||
{
|
{
|
||||||
co_return std::shared_ptr<T>(ptr, [ioc = asio::get_associated_executor(co_await asio::this_coro::executor)](T *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,
|
||||||
try { co_await ptr->asyncDestructor(); } catch(...) { }
|
[ptr, &ioc]() mutable -> coro<> {
|
||||||
delete ptr;
|
try { co_await dynamic_cast<IAsyncDestructible*>(ptr)->asyncDestructor(); } catch(...) { }
|
||||||
co_return;
|
asio::post(ioc, [ptr](){ delete ptr; });
|
||||||
} (ptr), boost::asio::detached);
|
},
|
||||||
|
boost::asio::detached);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename = typename std::is_same<IAsyncDestructible, T>>
|
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)
|
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) {
|
return std::unique_ptr<T, std::function<void(T*)>>(ptr, [&ioc](T *ptr) {
|
||||||
boost::asio::co_spawn(ioc, [](IAsyncDestructible *ptr) -> coro<> {
|
boost::asio::co_spawn(ioc,
|
||||||
try { co_await ptr->asyncDestructor(); } catch(...) { }
|
[ptr, &ioc]() mutable -> coro<> {
|
||||||
delete ptr;
|
try { co_await dynamic_cast<IAsyncDestructible*>(ptr)->asyncDestructor(); } catch(...) { }
|
||||||
co_return;
|
asio::post(ioc, [ptr](){ delete ptr; });
|
||||||
} (ptr), boost::asio::detached);
|
},
|
||||||
|
boost::asio::detached);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,11 +267,12 @@ protected:
|
|||||||
static coro<std::unique_ptr<T, std::function<void(T*)>>> createUnique(T *ptr)
|
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) {
|
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<> {
|
boost::asio::co_spawn(ioc,
|
||||||
try { co_await ptr->asyncDestructor(); } catch(...) { }
|
[ptr, &ioc]() mutable -> coro<> {
|
||||||
delete ptr;
|
try { co_await dynamic_cast<IAsyncDestructible*>(ptr)->asyncDestructor(); } catch(...) { }
|
||||||
co_return;
|
asio::post(ioc, [ptr](){ delete ptr; });
|
||||||
} (ptr), boost::asio::detached);
|
},
|
||||||
|
boost::asio::detached);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,17 +11,17 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
namespace LV {
|
namespace LV {
|
||||||
|
|
||||||
Resource::Resource() = default;
|
iResource::iResource() = default;
|
||||||
Resource::~Resource() = default;
|
iResource::~iResource() = default;
|
||||||
|
|
||||||
static std::mutex ResourceCacheMtx;
|
static std::mutex iResourceCacheMtx;
|
||||||
static std::unordered_map<std::string, std::weak_ptr<Resource>> ResourceCache;
|
static std::unordered_map<std::string, std::weak_ptr<iResource>> iResourceCache;
|
||||||
|
|
||||||
class FS_Resource : public Resource {
|
class FS_iResource : public iResource {
|
||||||
boost::scoped_array<uint8_t> Array;
|
boost::scoped_array<uint8_t> Array;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FS_Resource(const std::filesystem::path &path)
|
FS_iResource(const std::filesystem::path &path)
|
||||||
{
|
{
|
||||||
std::ifstream fd(path);
|
std::ifstream fd(path);
|
||||||
|
|
||||||
@@ -36,18 +36,18 @@ public:
|
|||||||
Data = Array.get();
|
Data = Array.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~FS_Resource() = default;
|
virtual ~FS_iResource() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<Resource> getResource(const std::string &path) {
|
std::shared_ptr<iResource> getResource(const std::string &path) {
|
||||||
std::unique_lock<std::mutex> lock(ResourceCacheMtx);
|
std::unique_lock<std::mutex> lock(iResourceCacheMtx);
|
||||||
|
|
||||||
if(auto iter = ResourceCache.find(path); iter != ResourceCache.end()) {
|
if(auto iter = iResourceCache.find(path); iter != iResourceCache.end()) {
|
||||||
std::shared_ptr<Resource> resource = iter->second.lock();
|
std::shared_ptr<iResource> iResource = iter->second.lock();
|
||||||
if(!resource) {
|
if(!iResource) {
|
||||||
ResourceCache.erase(iter);
|
iResourceCache.erase(iter);
|
||||||
} else {
|
} else {
|
||||||
return resource;
|
return iResource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,15 +55,15 @@ std::shared_ptr<Resource> getResource(const std::string &path) {
|
|||||||
fs_path /= path;
|
fs_path /= path;
|
||||||
|
|
||||||
if(fs::exists(fs_path)) {
|
if(fs::exists(fs_path)) {
|
||||||
std::shared_ptr<Resource> resource = std::make_shared<FS_Resource>(fs_path);
|
std::shared_ptr<iResource> iResource = std::make_shared<FS_iResource>(fs_path);
|
||||||
ResourceCache.emplace(path, resource);
|
iResourceCache.emplace(path, iResource);
|
||||||
TOS::Logger("Resources").debug() << "Ресурс " << fs_path << " найден в фс";
|
TOS::Logger("iResources").debug() << "Ресурс " << fs_path << " найден в фс";
|
||||||
return resource;
|
return iResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(auto iter = _binary_assets_symbols.find(path); iter != _binary_assets_symbols.end()) {
|
if(auto iter = _binary_assets_symbols.find(path); iter != _binary_assets_symbols.end()) {
|
||||||
TOS::Logger("Resources").debug() << "Ресурс " << fs_path << " is inlined";
|
TOS::Logger("iResources").debug() << "Ресурс " << fs_path << " is inlined";
|
||||||
return std::make_shared<Resource>((const uint8_t*) std::get<0>(iter->second), std::get<1>(iter->second)-std::get<0>(iter->second));
|
return std::make_shared<iResource>((const uint8_t*) std::get<0>(iter->second), std::get<1>(iter->second)-std::get<0>(iter->second));
|
||||||
}
|
}
|
||||||
|
|
||||||
MAKE_ERROR("Ресурс " << path << " не найден");
|
MAKE_ERROR("Ресурс " << path << " не найден");
|
||||||
|
|||||||
@@ -22,24 +22,24 @@ struct iBinaryStream : detail::membuf {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Resource {
|
class iResource {
|
||||||
protected:
|
protected:
|
||||||
const uint8_t* Data;
|
const uint8_t* Data;
|
||||||
size_t Size;
|
size_t Size;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Resource();
|
iResource();
|
||||||
Resource(const uint8_t* data, size_t size)
|
iResource(const uint8_t* data, size_t size)
|
||||||
: Data(data), Size(size)
|
: Data(data), Size(size)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual ~Resource();
|
virtual ~iResource();
|
||||||
|
|
||||||
Resource(const Resource&) = delete;
|
iResource(const iResource&) = delete;
|
||||||
Resource(Resource&&) = delete;
|
iResource(iResource&&) = delete;
|
||||||
Resource& operator=(const Resource&) = delete;
|
iResource& operator=(const iResource&) = delete;
|
||||||
Resource& operator=(Resource&&) = delete;
|
iResource& operator=(iResource&&) = delete;
|
||||||
|
|
||||||
const uint8_t* getData() const { return Data; }
|
const uint8_t* getData() const { return Data; }
|
||||||
size_t getSize() const { return Size; }
|
size_t getSize() const { return Size; }
|
||||||
@@ -49,6 +49,6 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<Resource> getResource(const std::string &path);
|
std::shared_ptr<iResource> getResource(const std::string &path);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,6 @@
|
|||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <Client/Vulkan/Vulkan.hpp>
|
#include <Client/Vulkan/Vulkan.hpp>
|
||||||
|
|
||||||
#include <Client/ResourceCache.hpp>
|
|
||||||
|
|
||||||
namespace LV {
|
namespace LV {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user