Files
LuaVox/Src/Client/ServerSession.cpp

1683 lines
66 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ServerSession.hpp"
#include "Client/Abstract.hpp"
#include "Client/AssetsManager.hpp"
#include "Common/Abstract.hpp"
#include "Common/Net.hpp"
#include "TOSAsync.hpp"
#include "TOSLib.hpp"
#include "glm/ext/quaternion_geometric.hpp"
#include <GLFW/glfw3.h>
#include <algorithm>
#include <atomic>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/this_coro.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <functional>
#include <memory>
#include <Common/Packets.hpp>
#include <glm/ext.hpp>
#include <optional>
#include <unordered_map>
#include <unordered_set>
namespace LV::Client {
namespace {
const char* assetTypeName(EnumAssets type) {
switch(type) {
case EnumAssets::Nodestate: return "nodestate";
case EnumAssets::Model: return "model";
case EnumAssets::Texture: return "texture";
case EnumAssets::Particle: return "particle";
case EnumAssets::Animation: return "animation";
case EnumAssets::Sound: return "sound";
case EnumAssets::Font: return "font";
default: return "unknown";
}
}
}
ServerSession::ServerSession(asio::io_context &ioc, std::unique_ptr<Net::AsyncSocket>&& socket)
: IAsyncDestructible(ioc), Socket(std::move(socket)) //, NetInputPackets(1024)
{
assert(Socket.get());
Profiles.DefNode[0] = {0};
Profiles.DefNode[1] = {1};
Profiles.DefNode[2] = {2};
Profiles.DefNode[3] = {3};
Profiles.DefNode[4] = {4};
std::fill(NextServerId.begin(), NextServerId.end(), 1);
for(auto& vec : ServerIdToDK)
vec.emplace_back();
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();
}
using namespace TOS;
ServerSession::~ServerSession() {
}
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) {
assert(a_ar_r >= 0 && a_ar_r <= 2);
std::string progress;
auto addLog = [&](const std::string &msg) {
progress += '\n';
progress += msg;
if(onProgress)
onProgress('\n'+msg);
};
if(username.size() > 255) {
addLog("Имя пользователя слишком велико (>255)");
MAKE_ERROR(progress);
}
if(token.size() > 255) {
addLog("Пароль слишком велик (>255)");
MAKE_ERROR(progress);
}
Net::Packet packet;
packet.write((const std::byte*) "AlterLuanti", 11);
packet << uint8_t(0) << uint8_t(a_ar_r) << username << token;
addLog("Отправляем первый пакет, авторизация или регистрация");
co_await packet.sendAndFastClear(socket);
addLog("Ожидаем код ответа");
uint8_t code = co_await Net::AsyncSocket::read<uint8_t>(socket);
if(code == 0) {
addLog("Код = Авторизированы");
} else if(code == 1) {
addLog("Код = Зарегистрированы и авторизированы");
} else if(code == 2 || code == 3) {
if(code == 2)
addLog("Код = Не удалось зарегистрироваться");
else
addLog("Код = Не удалось авторизоваться");
std::string reason = co_await Net::AsyncSocket::read<std::string>(socket);
addLog(reason);
if(code == 2)
MAKE_ERROR("Не удалось зарегистрироваться, причина: " << reason);
else
MAKE_ERROR("Не удалось авторизоваться, причина: " << reason);
} else {
addLog("Получен неизвестный код ответа (может это не игровой сервер?), прерываем");
MAKE_ERROR(progress);
}
}
coro<std::unique_ptr<Net::AsyncSocket>> ServerSession::asyncInitGameProtocol(asio::io_context &ioc, tcp::socket &&socket, 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);
};
addLog("Инициализируем игровой протокол");
uint8_t code = 0;
co_await Net::AsyncSocket::write<>(socket, code);
asio::deadline_timer timer(socket.get_executor());
while(true) {
code = co_await Net::AsyncSocket::read<uint8_t>(socket);
if(code == 0) {
addLog("Код = Успешно");
break;
} else if(code == 1) {
addLog("Код = Ошибка с причиной");
addLog(co_await Net::AsyncSocket::read<std::string>(socket));
MAKE_ERROR(progress);
} else if(code == 2) {
addLog("Код = Подождать 4 секунды");
timer.expires_from_now(boost::posix_time::seconds(4));
co_await timer.async_wait();
addLog("Ожидаем новый код");
} else {
addLog("Получен неизвестный код ответа (может это не игровой сервер?), прерываем");
MAKE_ERROR(progress);
}
}
co_return std::make_unique<Net::AsyncSocket>(ioc, std::move(socket));
}
void ServerSession::shutdown(EnumDisconnect type) {
IsGoingShutdown = true;
Socket->closeRead();
Net::Packet packet;
packet << (uint8_t) ToServer::L1::System
<< (uint8_t) ToServer::L2System::Disconnect
<< (uint8_t) type;
Socket->pushPacket(std::move(packet));
std::string reason;
if(type == EnumDisconnect::ByInterface)
reason = "по запросу интерфейса";
else if(type == EnumDisconnect::CriticalError)
reason = "на сервере произошла критическая ошибка";
else if(type == EnumDisconnect::ProtocolError)
reason = "ошибка протокола (клиент)";
LOG.info() << "Отключение от сервера: " << reason;
}
void ServerSession::requestModsReload() {
if(!Socket || !isConnected())
return;
Net::Packet packet;
packet << (uint8_t) ToServer::L1::System
<< (uint8_t) ToServer::L2System::ReloadMods;
Socket->pushPacket(std::move(packet));
LOG.info() << "Запрос на перезагрузку модов отправлен";
}
void ServerSession::onResize(uint32_t width, uint32_t height) {
}
void ServerSession::onChangeFocusState(bool isFocused) {
if(!isFocused)
CursorMode = EnumCursorMoveMode::Default;
}
void ServerSession::onCursorPosChange(int32_t width, int32_t height) {
}
void ServerSession::onCursorMove(float xMove, float yMove) {
xMove /= 10.f;
yMove /= 10.f;
glm::vec3 deltaPYR;
static constexpr float PI = glm::pi<float>(), PI2 = PI*2, PI_HALF = PI/2, PI_DEG = PI/180;
deltaPYR.x = std::clamp(PYR.x - yMove*PI_DEG, -PI_HALF+PI_DEG, PI_HALF-PI_DEG)-PYR.x;
deltaPYR.y = std::fmod(PYR.y - xMove*PI_DEG, PI2)-PYR.y;
deltaPYR.z = 0;
double gTime = GTime;
float deltaTime = 1-std::min<float>(gTime-PYR_At, 1/PYR_TIME_DELTA)*PYR_TIME_DELTA;
PYR_At = GTime;
PYR += deltaPYR;
PYR_Offset = deltaPYR+deltaTime*PYR_Offset;
}
void ServerSession::onCursorBtn(ISurfaceEventListener::EnumCursorBtn btn, bool state) {
if(!state)
return;
if(btn == EnumCursorBtn::Left) {
Net::Packet packet;
packet << (uint8_t) ToServer::L1::System
<< (uint8_t) ToServer::L2System::BlockChange
<< uint8_t(0);
Socket->pushPacket(std::move(packet));
} else if(btn == EnumCursorBtn::Right) {
Net::Packet packet;
packet << (uint8_t) ToServer::L1::System
<< (uint8_t) ToServer::L2System::BlockChange
<< uint8_t(1);
Socket->pushPacket(std::move(packet));
}
}
void ServerSession::onKeyboardBtn(int btn, int state) {
if(btn == GLFW_KEY_TAB && !state) {
CursorMode = CursorMode == EnumCursorMoveMode::Default ? EnumCursorMoveMode::MoveAndHidden : EnumCursorMoveMode::Default;
Keys.clear();
}
if(CursorMode == EnumCursorMoveMode::MoveAndHidden)
{
if(btn == GLFW_KEY_W)
Keys.W = state;
else if(btn == GLFW_KEY_A)
Keys.A = state;
else if(btn == GLFW_KEY_S)
Keys.S = state;
else if(btn == GLFW_KEY_D)
Keys.D = state;
else if(btn == GLFW_KEY_LEFT_SHIFT)
Keys.SHIFT = state;
else if(btn == GLFW_KEY_SPACE)
Keys.SPACE = state;
else if(btn == GLFW_KEY_LEFT_CONTROL)
Keys.CTRL = state;
}
}
void ServerSession::onJoystick() {
}
void ServerSession::update(GlobalTime gTime, float dTime) {
// Если были получены ресурсы, отправим их на запись в кеш
if(!AsyncContext.LoadedAssets.get_read().empty()) {
std::vector<AssetEntry> assets = std::move(*AsyncContext.LoadedAssets.lock());
std::vector<Resource> resources;
resources.reserve(assets.size());
for(AssetEntry& entry : assets) {
entry.Hash = entry.Res.hash();
if(const AssetsManager::BindInfo* bind = AM->getBind(entry.Type, entry.Id))
entry.Dependencies = AM->rebindHeader(entry.Type, bind->Header);
else
entry.Dependencies.clear();
resources.push_back(entry.Res);
AsyncContext.LoadedResources.emplace_back(std::move(entry));
// // Проверяем используется ли сейчас ресурс
// auto iter = MyAssets.ExistBinds[(int) entry.Type].find(entry.Id);
// if(iter == MyAssets.ExistBinds[(int) entry.Type].end()) {
// // Не используется
// MyAssets.NotInUse[(int) entry.Type][entry.Domain + ':' + entry.Key] = {entry, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)};
// } else {
// // Используется
// Assets.InUse[(int) entry.Type][entry.Id] = entry;
// changedResources[entry.Type].insert({entry.Id, entry});
// }
}
AM->pushResources(std::move(resources));
}
// Получить ресурсы с AssetsManager
{
static std::atomic<uint32_t> debugAssetReadLogCount = 0;
std::vector<std::pair<AssetsManager::ResourceKey, std::optional<Resource>>> resources = AM->pullReads();
std::vector<Hash_t> needRequest;
for(auto& [key, res] : resources) {
bool cacheHit = false;
Hash_t actualHash = {};
if(res) {
actualHash = res->hash();
cacheHit = actualHash == key.Hash;
}
if(cacheHit) {
auto& waitingByDomain = AsyncContext.ResourceWait[(int) key.Type];
auto iterDomain = waitingByDomain.find(key.Domain);
if(iterDomain != waitingByDomain.end()) {
auto& entries = iterDomain->second;
entries.erase(std::remove_if(entries.begin(), entries.end(),
[&](const std::pair<std::string, Hash_t>& entry) {
return entry.first == key.Key && entry.second == key.Hash;
}),
entries.end());
if(entries.empty())
waitingByDomain.erase(iterDomain);
}
}
if(key.Domain == "test"
&& (key.Type == EnumAssets::Nodestate
|| key.Type == EnumAssets::Model
|| key.Type == EnumAssets::Texture))
{
uint32_t idx = debugAssetReadLogCount.fetch_add(1);
if(idx < 128) {
if(res) {
LOG.debug() << "Cache hit type=" << assetTypeName(key.Type)
<< " id=" << key.Id
<< " key=" << key.Domain << ':' << key.Key
<< " size=" << res->size();
} else {
LOG.debug() << "Cache miss type=" << assetTypeName(key.Type)
<< " id=" << key.Id
<< " key=" << key.Domain << ':' << key.Key
<< " hash=" << int(key.Hash[0]) << '.'
<< int(key.Hash[1]) << '.'
<< int(key.Hash[2]) << '.'
<< int(key.Hash[3]);
}
}
}
if(!res) {
// Проверить не был ли уже отправлен запрос на получение этого хеша
auto iter = std::lower_bound(AsyncContext.AlreadyLoading.begin(), AsyncContext.AlreadyLoading.end(), key.Hash);
if(iter == AsyncContext.AlreadyLoading.end() || *iter != key.Hash) {
AsyncContext.AlreadyLoading.insert(iter, key.Hash);
needRequest.push_back(key.Hash);
}
} else {
if(actualHash != key.Hash) {
auto iter = std::lower_bound(AsyncContext.AlreadyLoading.begin(), AsyncContext.AlreadyLoading.end(), key.Hash);
if(iter == AsyncContext.AlreadyLoading.end() || *iter != key.Hash) {
AsyncContext.AlreadyLoading.insert(iter, key.Hash);
needRequest.push_back(key.Hash);
}
}
std::vector<uint8_t> deps;
if(const AssetsManager::BindInfo* bind = AM->getBind(key.Type, key.Id))
deps = AM->rebindHeader(key.Type, bind->Header);
AssetEntry entry {
.Type = key.Type,
.Id = key.Id,
.Domain = key.Domain,
.Key = key.Key,
.Res = std::move(*res),
.Hash = actualHash,
.Dependencies = std::move(deps)
};
AsyncContext.LoadedResources.emplace_back(std::move(entry));
}
}
if(!needRequest.empty()) {
assert(needRequest.size() < (1 << 16));
uint32_t idx = debugAssetReadLogCount.fetch_add(1);
if(idx < 128) {
LOG.debug() << "Send ResourceRequest count=" << needRequest.size();
}
for(const auto& hash : needRequest) {
LOG.debug() << "Client request hash="
<< int(hash[0]) << '.'
<< int(hash[1]) << '.'
<< int(hash[2]) << '.'
<< int(hash[3]);
}
Net::Packet p;
p << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::ResourceRequest;
p << (uint16_t) needRequest.size();
for(const Hash_t& hash : needRequest)
p.write((const std::byte*) hash.data(), 32);
Socket->pushPacket(std::move(p));
}
}
// Разбираемся с полученными меж тактами привязками ресурсов
if(!AsyncContext.AssetsBinds.get_read().empty()) {
AssetsBindsChange abc;
// Нужно объеденить изменения в один AssetsBindsChange (abc)
{
std::vector<AssetsBindsChange> list = std::move(*AsyncContext.AssetsBinds.lock());
for(AssetsBindsChange entry : list) {
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++)
std::sort(entry.Lost[type].begin(), entry.Lost[type].end());
// Если до этого была объявлена привязка, а теперь она потеряна, то просто сокращаем значения.
// Иначе дописываем в lost
for(ssize_t iter = abc.Binds.size()-1; iter >= 0; iter--) {
const AssetBindEntry& abe = abc.Binds[iter];
auto& lost = entry.Lost[(int) abe.Type];
auto iterator = std::lower_bound(lost.begin(), lost.end(), abe.Id);
if(iterator != lost.end() && *iterator == abe.Id) {
// Привязка будет удалена
lost.erase(iterator);
abc.Binds.erase(abc.Binds.begin()+iter);
}
}
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
abc.Lost[type].append_range(entry.Lost[type]);
entry.Lost[type].clear();
std::sort(abc.Lost[type].begin(), abc.Lost[type].end());
}
for(AssetBindEntry& abe : entry.Binds) {
auto iterator = std::lower_bound(entry.Lost[(int) abe.Type].begin(), entry.Lost[(int) abe.Type].end(), abe.Id);
if(iterator != entry.Lost[(int) abe.Type].end() && *iterator == abe.Id) {
// Получили новую привязку, которая была удалена в предыдущем такте
entry.Lost[(int) abe.Type].erase(iterator);
} else {
// Данная привязка не удалялась, может она была изменена?
bool hasChanged = false;
for(AssetBindEntry& abe2 : abc.Binds) {
if(abe2.Type == abe.Type && abe2.Id == abe.Id) {
// Привязка была изменена
abe2 = std::move(abe);
hasChanged = true;
break;
}
}
if(!hasChanged)
// Изменения не было, это просто новая привязка
abc.Binds.emplace_back(std::move(abe));
}
}
entry.Binds.clear();
}
}
// Запрос к дисковому кешу новых ресурсов
std::vector<AssetsManager::ResourceKey> needToLoad;
for(const AssetBindEntry& bind : abc.Binds) {
bool needQuery = true;
// Проверить in memory кеш по домену+ключу
{
std::string dk = bind.Domain + ':' + bind.Key;
auto &niubdk = MyAssets.NotInUse[(int) bind.Type];
auto iter = niubdk.find(dk);
if(iter != niubdk.end()) {
// Есть ресурс
needQuery = iter->second.first.Hash != bind.Hash;
}
}
// Проверить если такой запрос уже был отправлен в AssetsManager и ожидает ответа
if(needQuery) {
auto& list = AsyncContext.ResourceWait[(int) bind.Type];
auto iterDomain = list.find(bind.Domain);
if(iterDomain != list.end()) {
for(const auto& [key, hash] : iterDomain->second) {
if(key == bind.Key && hash == bind.Hash) {
needQuery = false;
break;
}
}
}
}
// Под рукой нет ресурса, отправим на проверку в AssetsManager
if(needQuery) {
AsyncContext.ResourceWait[(int) bind.Type][bind.Domain].emplace_back(bind.Key, bind.Hash);
needToLoad.push_back(AssetsManager::ResourceKey{
.Hash = bind.Hash,
.Type = bind.Type,
.Domain = bind.Domain,
.Key = bind.Key,
.Id = bind.Id
});
}
}
// Отправляем запрос на получение ресурсов
if(!needToLoad.empty()) {
static std::atomic<uint32_t> debugReadRequestLogCount = 0;
AssetsManager::ResourceKey firstDebug;
bool hasDebug = false;
for(const auto& entry : needToLoad) {
if(entry.Domain == "test"
&& (entry.Type == EnumAssets::Nodestate
|| entry.Type == EnumAssets::Model
|| entry.Type == EnumAssets::Texture))
{
firstDebug = entry;
hasDebug = true;
break;
}
}
if(hasDebug && debugReadRequestLogCount.fetch_add(1) < 64) {
LOG.debug() << "Queue asset read count=" << needToLoad.size()
<< " type=" << assetTypeName(firstDebug.Type)
<< " id=" << firstDebug.Id
<< " key=" << firstDebug.Domain << ':' << firstDebug.Key
<< " hash=" << int(firstDebug.Hash[0]) << '.'
<< int(firstDebug.Hash[1]) << '.'
<< int(firstDebug.Hash[2]) << '.'
<< int(firstDebug.Hash[3]);
}
for(const auto& entry : needToLoad) {
LOG.debug() << "Client wants type=" << assetTypeName(entry.Type)
<< " id=" << entry.Id
<< " key=" << entry.Domain << ':' << entry.Key
<< " hash=" << int(entry.Hash[0]) << '.'
<< int(entry.Hash[1]) << '.'
<< int(entry.Hash[2]) << '.'
<< int(entry.Hash[3]);
}
AM->pushReads(std::move(needToLoad));
}
AsyncContext.Binds.push_back(std::move(abc));
}
if(!AsyncContext.TickSequence.get_read().empty()) {
// Есть такты с сервера
// Оповещаем о подготовке к обработке тактов
if(RS)
RS->prepareTickSync();
std::vector<TickData> ticks = std::move(*AsyncContext.TickSequence.lock());
IRenderSession::TickSyncData result;
// Перевариваем данные по тактам
// Профили
std::unordered_map<DefVoxelId, void*> profile_Voxel_AddOrChange;
std::vector<DefVoxelId> profile_Voxel_Lost;
std::unordered_map<DefNodeId, DefNode_t> profile_Node_AddOrChange;
std::vector<DefNodeId> profile_Node_Lost;
std::unordered_map<DefWorldId, void*> profile_World_AddOrChange;
std::vector<DefWorldId> profile_World_Lost;
std::unordered_map<DefPortalId, void*> profile_Portal_AddOrChange;
std::vector<DefPortalId> profile_Portal_Lost;
std::unordered_map<DefEntityId, DefEntityInfo> profile_Entity_AddOrChange;
std::vector<DefEntityId> profile_Entity_Lost;
std::unordered_map<DefItemId, void*> profile_Item_AddOrChange;
std::vector<DefItemId> profile_Item_Lost;
std::unordered_map<EntityId_t, EntityInfo> entity_AddOrChange;
std::vector<EntityId_t> entity_Lost;
{
for(TickData& data : ticks) {
{
for(auto& [id, profile] : data.Profile_Voxel_AddOrChange) {
auto iter = std::lower_bound(profile_Voxel_Lost.begin(), profile_Voxel_Lost.end(), id);
if(iter != profile_Voxel_Lost.end() && *iter == id)
profile_Voxel_Lost.erase(iter);
profile_Voxel_AddOrChange[id] = profile;
}
for(DefVoxelId id : data.Profile_Voxel_Lost) {
profile_Voxel_AddOrChange.erase(id);
}
profile_Voxel_Lost.insert(profile_Voxel_Lost.end(), data.Profile_Voxel_Lost.begin(), data.Profile_Voxel_Lost.end());
std::sort(profile_Voxel_Lost.begin(), profile_Voxel_Lost.end());
auto eraseIter = std::unique(profile_Voxel_Lost.begin(), profile_Voxel_Lost.end());
profile_Voxel_Lost.erase(eraseIter, profile_Voxel_Lost.end());
}
{
for(auto& [id, profile] : data.Profile_Node_AddOrChange) {
auto iter = std::lower_bound(profile_Node_Lost.begin(), profile_Node_Lost.end(), id);
if(iter != profile_Node_Lost.end() && *iter == id)
profile_Node_Lost.erase(iter);
profile_Node_AddOrChange[id] = profile;
}
for(DefNodeId id : data.Profile_Node_Lost) {
profile_Node_AddOrChange.erase(id);
}
profile_Node_Lost.insert(profile_Node_Lost.end(), data.Profile_Node_Lost.begin(), data.Profile_Node_Lost.end());
std::sort(profile_Node_Lost.begin(), profile_Node_Lost.end());
auto eraseIter = std::unique(profile_Node_Lost.begin(), profile_Node_Lost.end());
profile_Node_Lost.erase(eraseIter, profile_Node_Lost.end());
}
{
for(auto& [id, profile] : data.Profile_World_AddOrChange) {
auto iter = std::lower_bound(profile_World_Lost.begin(), profile_World_Lost.end(), id);
if(iter != profile_World_Lost.end() && *iter == id)
profile_World_Lost.erase(iter);
profile_World_AddOrChange[id] = profile;
}
for(DefWorldId id : data.Profile_World_Lost) {
profile_World_AddOrChange.erase(id);
}
profile_World_Lost.insert(profile_World_Lost.end(), data.Profile_World_Lost.begin(), data.Profile_World_Lost.end());
std::sort(profile_World_Lost.begin(), profile_World_Lost.end());
auto eraseIter = std::unique(profile_World_Lost.begin(), profile_World_Lost.end());
profile_World_Lost.erase(eraseIter, profile_World_Lost.end());
}
{
for(auto& [id, profile] : data.Profile_Portal_AddOrChange) {
auto iter = std::lower_bound(profile_Portal_Lost.begin(), profile_Portal_Lost.end(), id);
if(iter != profile_Portal_Lost.end() && *iter == id)
profile_Portal_Lost.erase(iter);
profile_Portal_AddOrChange[id] = profile;
}
for(DefPortalId id : data.Profile_Portal_Lost) {
profile_Portal_AddOrChange.erase(id);
}
profile_Portal_Lost.insert(profile_Portal_Lost.end(), data.Profile_Portal_Lost.begin(), data.Profile_Portal_Lost.end());
std::sort(profile_Portal_Lost.begin(), profile_Portal_Lost.end());
auto eraseIter = std::unique(profile_Portal_Lost.begin(), profile_Portal_Lost.end());
profile_Portal_Lost.erase(eraseIter, profile_Portal_Lost.end());
}
{
for(auto& [id, profile] : data.Profile_Entity_AddOrChange) {
auto iter = std::lower_bound(profile_Entity_Lost.begin(), profile_Entity_Lost.end(), id);
if(iter != profile_Entity_Lost.end() && *iter == id)
profile_Entity_Lost.erase(iter);
profile_Entity_AddOrChange[id] = profile;
}
for(DefEntityId id : data.Profile_Entity_Lost) {
profile_Entity_AddOrChange.erase(id);
}
profile_Entity_Lost.insert(profile_Entity_Lost.end(), data.Profile_Entity_Lost.begin(), data.Profile_Entity_Lost.end());
std::sort(profile_Entity_Lost.begin(), profile_Entity_Lost.end());
auto eraseIter = std::unique(profile_Entity_Lost.begin(), profile_Entity_Lost.end());
profile_Entity_Lost.erase(eraseIter, profile_Entity_Lost.end());
}
{
for(auto& [id, profile] : data.Profile_Item_AddOrChange) {
auto iter = std::lower_bound(profile_Item_Lost.begin(), profile_Item_Lost.end(), id);
if(iter != profile_Item_Lost.end() && *iter == id)
profile_Item_Lost.erase(iter);
profile_Item_AddOrChange[id] = profile;
}
for(DefItemId id : data.Profile_Item_Lost) {
profile_Item_AddOrChange.erase(id);
}
profile_Item_Lost.insert(profile_Item_Lost.end(), data.Profile_Item_Lost.begin(), data.Profile_Item_Lost.end());
std::sort(profile_Item_Lost.begin(), profile_Item_Lost.end());
auto eraseIter = std::unique(profile_Item_Lost.begin(), profile_Item_Lost.end());
profile_Item_Lost.erase(eraseIter, profile_Item_Lost.end());
}
{
for(auto& [id, info] : data.Entity_AddOrChange) {
auto iter = std::lower_bound(entity_Lost.begin(), entity_Lost.end(), id);
if(iter != entity_Lost.end() && *iter == id)
entity_Lost.erase(iter);
entity_AddOrChange[id] = info;
}
for(EntityId_t id : data.Entity_Lost) {
entity_AddOrChange.erase(id);
}
entity_Lost.insert(entity_Lost.end(), data.Entity_Lost.begin(), data.Entity_Lost.end());
std::sort(entity_Lost.begin(), entity_Lost.end());
auto eraseIter = std::unique(entity_Lost.begin(), entity_Lost.end());
entity_Lost.erase(eraseIter, entity_Lost.end());
}
}
for(auto& [id, _] : profile_Voxel_AddOrChange)
result.Profiles_ChangeOrAdd[EnumDefContent::Voxel].push_back(id);
result.Profiles_Lost[EnumDefContent::Voxel] = profile_Voxel_Lost;
for(auto& [id, _] : profile_Node_AddOrChange)
result.Profiles_ChangeOrAdd[EnumDefContent::Node].push_back(id);
result.Profiles_Lost[EnumDefContent::Node] = profile_Node_Lost;
for(auto& [id, _] : profile_World_AddOrChange)
result.Profiles_ChangeOrAdd[EnumDefContent::World].push_back(id);
result.Profiles_Lost[EnumDefContent::World] = profile_World_Lost;
for(auto& [id, _] : profile_Portal_AddOrChange)
result.Profiles_ChangeOrAdd[EnumDefContent::Portal].push_back(id);
result.Profiles_Lost[EnumDefContent::Portal] = profile_Portal_Lost;
for(auto& [id, _] : profile_Entity_AddOrChange)
result.Profiles_ChangeOrAdd[EnumDefContent::Entity].push_back(id);
result.Profiles_Lost[EnumDefContent::Entity] = profile_Entity_Lost;
for(auto& [id, _] : profile_Item_AddOrChange)
result.Profiles_ChangeOrAdd[EnumDefContent::Item].push_back(id);
result.Profiles_Lost[EnumDefContent::Item] = profile_Item_Lost;
}
// Чанки
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalChunk, std::vector<VoxelCube>>> chunks_AddOrChange_Voxel_Result;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalChunk, std::array<Node, 16*16*16>>> chunks_AddOrChange_Node_Result;
std::unordered_map<WorldId_t, std::vector<Pos::GlobalChunk>> chunks_Changed;
std::unordered_map<WorldId_t, std::unordered_set<Pos::GlobalRegion>> regions_Lost_Result;
{
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalChunk, std::u8string>> chunks_AddOrChange_Voxel;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalChunk, std::u8string>> chunks_AddOrChange_Node;
std::unordered_map<WorldId_t, std::unordered_set<Pos::GlobalRegion>> regions_Lost;
for(TickData& data : ticks) {
for(auto& [wId, chunks] : data.Chunks_AddOrChange_Voxel) {
if(auto iter = regions_Lost.find(wId); iter != regions_Lost.end()) {
for(const auto& [pos, value] : chunks) {
iter->second.erase(Pos::GlobalRegion(pos >> 2));
}
}
chunks_AddOrChange_Voxel[wId].merge(chunks);
}
data.Chunks_AddOrChange_Voxel.clear();
for(auto& [wId, chunks] : data.Chunks_AddOrChange_Node) {
if(auto iter = regions_Lost.find(wId); iter != regions_Lost.end()) {
for(const auto& [pos, value] : chunks) {
iter->second.erase(Pos::GlobalRegion(pos >> 2));
}
}
chunks_AddOrChange_Node[wId].merge(chunks);
}
data.Chunks_AddOrChange_Node.clear();
for(auto& [wId, regions] : data.Regions_Lost) {
std::sort(regions.begin(), regions.end());
if(auto iter = chunks_AddOrChange_Voxel.find(wId); iter != chunks_AddOrChange_Voxel.end())
{
std::vector<Pos::GlobalChunk> toDelete;
for(auto& [pos, value] : iter->second) {
if(std::binary_search(regions.begin(), regions.end(), Pos::GlobalRegion(pos >> 2))) {
toDelete.push_back(pos);
}
}
for(Pos::GlobalChunk pos : toDelete)
iter->second.erase(iter->second.find(pos));
}
if(auto iter = chunks_AddOrChange_Node.find(wId); iter != chunks_AddOrChange_Node.end())
{
std::vector<Pos::GlobalChunk> toDelete;
for(auto& [pos, value] : iter->second) {
if(std::binary_search(regions.begin(), regions.end(), Pos::GlobalRegion(pos >> 2))) {
toDelete.push_back(pos);
}
}
for(Pos::GlobalChunk pos : toDelete)
iter->second.erase(iter->second.find(pos));
}
regions_Lost[wId].insert_range(regions);
}
data.Regions_Lost.clear();
}
for(auto& [wId, list] : chunks_AddOrChange_Voxel) {
auto& caocvr = chunks_AddOrChange_Voxel_Result[wId];
auto& c = chunks_Changed[wId];
for(auto& [pos, val] : list) {
auto& sizes = VisibleChunkCompressed[wId][pos];
VisibleChunkCompressedBytes -= sizes.Voxels;
sizes.Voxels = val.size();
VisibleChunkCompressedBytes += sizes.Voxels;
caocvr[pos] = unCompressVoxels(val);
c.push_back(pos);
}
}
for(auto& [wId, list] : chunks_AddOrChange_Node) {
auto& caocvr = chunks_AddOrChange_Node_Result[wId];
auto& c = chunks_Changed[wId];
for(auto& [pos, val] : list) {
auto& sizes = VisibleChunkCompressed[wId][pos];
VisibleChunkCompressedBytes -= sizes.Nodes;
sizes.Nodes = val.size();
VisibleChunkCompressedBytes += sizes.Nodes;
auto& chunkNodes = caocvr[pos];
unCompressNodes(val, chunkNodes.data());
c.push_back(pos);
}
}
regions_Lost_Result = std::move(regions_Lost);
for(auto& [wId, list] : chunks_Changed) {
std::sort(list.begin(), list.end());
auto eraseIter = std::unique(list.begin(), list.end());
list.erase(eraseIter, list.end());
}
}
result.Chunks_ChangeOrAdd = std::move(chunks_Changed);
{
for(TickData& data : ticks) {
}
}
if(RS)
RS->pushStageTickSync();
// Применяем изменения по ресурсам, профилям и контенту
// Разбираемся с изменениями в привязках ресурсов
{
AssetsBindsChange abc;
for(AssetsBindsChange entry : AsyncContext.Binds) {
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++)
std::sort(entry.Lost[type].begin(), entry.Lost[type].end());
for(ssize_t iter = abc.Binds.size()-1; iter >= 0; iter--) {
const AssetBindEntry& abe = abc.Binds[iter];
auto& lost = entry.Lost[(int) abe.Type];
auto iterator = std::lower_bound(lost.begin(), lost.end(), abe.Id);
if(iterator != lost.end() && *iterator == abe.Id) {
lost.erase(iterator);
abc.Binds.erase(abc.Binds.begin()+iter);
}
}
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
abc.Lost[type].append_range(entry.Lost[type]);
entry.Lost[type].clear();
std::sort(abc.Lost[type].begin(), abc.Lost[type].end());
}
for(AssetBindEntry& abe : entry.Binds) {
auto iterator = std::lower_bound(entry.Lost[(int) abe.Type].begin(), entry.Lost[(int) abe.Type].end(), abe.Id);
if(iterator != entry.Lost[(int) abe.Type].end() && *iterator == abe.Id) {
// Получили новую привязку, которая была удалена в предыдущем такте
entry.Lost[(int) abe.Type].erase(iterator);
} else {
// Данная привязка не удалялась, может она была изменена?
bool hasChanged = false;
for(AssetBindEntry& abe2 : abc.Binds) {
if(abe2.Type == abe.Type && abe2.Id == abe.Id) {
// Привязка была изменена
abe2 = std::move(abe);
hasChanged = true;
break;
}
}
if(!hasChanged)
// Изменения не было, это просто новая привязка
abc.Binds.emplace_back(std::move(abe));
}
}
entry.Binds.clear();
}
AsyncContext.Binds.clear();
for(AssetBindEntry& entry : abc.Binds) {
std::vector<uint8_t> deps;
if(!entry.Header.empty())
deps = AM->rebindHeader(entry.Type, entry.Header);
MyAssets.ExistBinds[(int) entry.Type].insert(entry.Id);
result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id);
auto iterLoaded = IServerSession::Assets[entry.Type].find(entry.Id);
if(iterLoaded != IServerSession::Assets[entry.Type].end())
iterLoaded->second.Dependencies = deps;
// Если ресурс был в кеше, то достаётся от туда
auto iter = MyAssets.NotInUse[(int) entry.Type].find(entry.Domain+':'+entry.Key);
if(iter != MyAssets.NotInUse[(int) entry.Type].end()) {
iter->second.first.Dependencies = deps;
IServerSession::Assets[entry.Type][entry.Id] = std::get<0>(iter->second);
result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id);
MyAssets.NotInUse[(int) entry.Type].erase(iter);
}
}
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
for(ResourceId id : abc.Lost[type]) {
MyAssets.ExistBinds[type].erase(id);
// Потерянные ресурсы уходят в кеш
auto iter = IServerSession::Assets[(EnumAssets) type].find(id);
if(iter != IServerSession::Assets[(EnumAssets) type].end()) {
MyAssets.NotInUse[(int) iter->second.Type][iter->second.Domain+':'+iter->second.Key] = {iter->second, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)};
IServerSession::Assets[(EnumAssets) type].erase(iter);
result.Assets_Lost[iter->second.Type].push_back(iter->second.Id);
}
}
result.Assets_Lost[(EnumAssets) type] = std::move(abc.Lost[type]);
}
}
// Получаем ресурсы
{
for(AssetEntry& entry : AsyncContext.LoadedResources) {
if(MyAssets.ExistBinds[(int) entry.Type].contains(entry.Id)) {
// Ресурс ещё нужен
IServerSession::Assets[entry.Type][entry.Id] = entry;
result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id);
} else {
// Ресурс уже не нужен, отправляем в кеш
MyAssets.NotInUse[(int) entry.Type][entry.Domain+':'+entry.Key] = {entry, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)};
}
}
AsyncContext.LoadedResources.clear();
}
// Чистим кеш ресурсов
{
uint64_t now = time(nullptr);
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
std::vector<std::string> toDelete;
for(auto& [key, value] : MyAssets.NotInUse[type]) {
if(std::get<1>(value) < now)
toDelete.push_back(key);
}
for(std::string& key : toDelete)
MyAssets.NotInUse[type].erase(MyAssets.NotInUse[type].find(key));
}
}
// Определения
{
for(auto& [resId, def] : profile_Node_AddOrChange) {
Profiles.DefNode[resId] = def;
}
for(auto& [resId, def] : profile_Entity_AddOrChange) {
Profiles.DefEntity[resId] = def;
}
}
// Чанки
{
for(auto& [wId, lost] : regions_Lost_Result) {
auto iterWorld = Content.Worlds.find(wId);
auto iterSizesWorld = VisibleChunkCompressed.find(wId);
if(iterWorld != Content.Worlds.end()) {
for(const Pos::GlobalRegion& rPos : lost) {
auto iterRegion = iterWorld->second.Regions.find(rPos);
if(iterRegion != iterWorld->second.Regions.end())
iterWorld->second.Regions.erase(iterRegion);
}
}
if(iterSizesWorld == VisibleChunkCompressed.end())
continue;
for(const Pos::GlobalRegion& rPos : lost) {
for(auto iter = iterSizesWorld->second.begin(); iter != iterSizesWorld->second.end(); ) {
if(Pos::GlobalRegion(iter->first >> 2) == rPos) {
VisibleChunkCompressedBytes -= iter->second.Voxels;
VisibleChunkCompressedBytes -= iter->second.Nodes;
iter = iterSizesWorld->second.erase(iter);
} else {
++iter;
}
}
}
if(iterSizesWorld->second.empty())
VisibleChunkCompressed.erase(iterSizesWorld);
}
for(auto& [wId, voxels] : chunks_AddOrChange_Voxel_Result) {
auto& regions = Content.Worlds[wId].Regions;
for(auto& [pos, data] : voxels) {
regions[pos >> 2].Chunks[Pos::bvec4u(pos & 0x3).pack()].Voxels = std::move(data);
}
}
for(auto& [wId, nodes] : chunks_AddOrChange_Node_Result) {
auto& regions = Content.Worlds[wId].Regions;
for(auto& [pos, data] : nodes) {
regions[pos >> 2].Chunks[Pos::bvec4u(pos & 0x3).pack()].Nodes = std::move(data);
}
}
}
// Сущности
{
for(auto& [entityId, info] : entity_AddOrChange) {
auto iter = Content.Entityes.find(entityId);
if(iter != Content.Entityes.end() && iter->second.WorldId != info.WorldId) {
auto iterWorld = Content.Worlds.find(iter->second.WorldId);
if(iterWorld != Content.Worlds.end()) {
auto &list = iterWorld->second.Entitys;
list.erase(std::remove(list.begin(), list.end(), entityId), list.end());
}
}
Content.Entityes[entityId] = info;
auto &list = Content.Worlds[info.WorldId].Entitys;
if(std::find(list.begin(), list.end(), entityId) == list.end())
list.push_back(entityId);
}
for(EntityId_t entityId : entity_Lost) {
auto iter = Content.Entityes.find(entityId);
if(iter != Content.Entityes.end()) {
auto iterWorld = Content.Worlds.find(iter->second.WorldId);
if(iterWorld != Content.Worlds.end()) {
auto &list = iterWorld->second.Entitys;
list.erase(std::remove(list.begin(), list.end(), entityId), list.end());
}
Content.Entityes.erase(iter);
} else {
for(auto& [wId, worldInfo] : Content.Worlds) {
auto &list = worldInfo.Entitys;
list.erase(std::remove(list.begin(), list.end(), entityId), list.end());
}
}
}
}
if(RS)
RS->tickSync(result);
}
// Здесь нужно обработать управляющие пакеты
// Оповещение модуля рендера об изменениях ресурсов
std::unordered_map<EnumAssets, std::unordered_map<ResourceId, AssetEntry>> changedResources;
std::unordered_map<EnumAssets, std::unordered_set<ResourceId>> lostResources;
// Обработка полученных ресурсов
// Получаем ресурсы, загруженные с дискового кеша
GTime = gTime;
Pos += glm::vec3(Speed) * dTime;
Speed -= glm::dvec3(Speed) * double(dTime);
glm::mat4 rot(1);
float deltaTime = 1-std::min<float>(gTime-PYR_At, 1/PYR_TIME_DELTA)*PYR_TIME_DELTA;
rot = glm::rotate(rot, PYR.y-deltaTime*PYR_Offset.y, {0, 1, 0});
float mltpl = 16*dTime*Pos::Object_t::BS;
if(Keys.CTRL)
mltpl *= 16;
Speed += glm::vec3(rot*glm::vec4(0, 0, -1, 1)*float(Keys.W))*mltpl;
Speed += glm::vec3(rot*glm::vec4(-1, 0, 0, 1)*float(Keys.A))*mltpl;
Speed += glm::vec3(rot*glm::vec4(0, 0, 1, 1)*float(Keys.S))*mltpl;
Speed += glm::vec3(rot*glm::vec4(1, 0, 0, 1)*float(Keys.D))*mltpl;
Speed += glm::vec3(0, -1, 0)*float(Keys.SHIFT)*mltpl;
Speed += glm::vec3(0, 1, 0)*float(Keys.SPACE)*mltpl;
// {
// // Пакеты
// ParsedPacket *pack;
// while(NetInputPackets.pop(pack)) {
// delete pack;
// }
// }
// Расчёт камеры
{
float deltaTime = 1-std::min<float>(gTime-PYR_At, 1/PYR_TIME_DELTA)*PYR_TIME_DELTA;
glm::quat quat =
glm::angleAxis(PYR.x-deltaTime*PYR_Offset.x, glm::vec3(1.f, 0.f, 0.f))
*
glm::angleAxis(PYR.y-deltaTime*PYR_Offset.y, glm::vec3(0.f, -1.f, 0.f));
quat = glm::normalize(quat);
if(RS)
RS->setCameraPos(0, Pos, quat);
// Отправка текущей позиции камеры
if(gTime-LastSendPYR_POS > 1/20.f)
{
LastSendPYR_POS = gTime;
Net::Packet packet;
ToServer::PacketQuat q;
q.fromQuat(glm::inverse(quat));
packet << (uint8_t) ToServer::L1::System
<< (uint8_t) ToServer::L2System::Test_CAM_PYR_POS
<< Pos.x << Pos.y << Pos.z;
for(int iter = 0; iter < 5; iter++)
packet << q.Data[iter];
Socket->pushPacket(std::move(packet));
}
}
}
void ServerSession::setRenderSession(IRenderSession* session) {
RS = session;
}
void ServerSession::resetResourceSyncState() {
AM->clearServerBindings();
AsyncContext.AssetsLoading.clear();
AsyncContext.AlreadyLoading.clear();
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++)
AsyncContext.ResourceWait[type].clear();
for(auto& vec : ServerIdToDK)
vec.clear();
std::fill(NextServerId.begin(), NextServerId.end(), 1);
for(auto& vec : ServerIdToDK)
vec.emplace_back();
AsyncContext.Binds.clear();
AsyncContext.LoadedResources.clear();
AsyncContext.ThisTickEntry = {};
AsyncContext.LoadedAssets.lock()->clear();
AsyncContext.AssetsBinds.lock()->clear();
AsyncContext.TickSequence.lock()->clear();
}
coro<> ServerSession::run(AsyncUseControl::Lock) {
try {
while(!IsGoingShutdown && IsConnected) {
co_await readPacket(*Socket);
}
} catch(const std::exception &exc) {
LOG.error() << "Ошибка обработки сокета:\n" << exc.what();
}
IsConnected = false;
resetResourceSyncState();
co_return;
}
void ServerSession::protocolError() {
shutdown(EnumDisconnect::ProtocolError);
}
coro<> ServerSession::readPacket(Net::AsyncSocket &sock) {
uint8_t first = co_await sock.read<uint8_t>();
switch((ToClient) first) {
case ToClient::Init:
co_return;
case ToClient::Disconnect:
co_await rP_Disconnect(sock);
co_return;
case ToClient::AssetsBindDK:
co_await rP_AssetsBindDK(sock);
co_return;
case ToClient::AssetsBindHH:
co_await rP_AssetsBindHH(sock);
co_return;
case ToClient::AssetsInitSend:
co_await rP_AssetsInitSend(sock);
co_return;
case ToClient::AssetsNextSend:
co_await rP_AssetsNextSend(sock);
co_return;
case ToClient::DefinitionsUpdate:
co_await rP_DefinitionsUpdate(sock);
co_return;
case ToClient::ChunkVoxels:
co_await rP_ChunkVoxels(sock);
co_return;
case ToClient::ChunkNodes:
co_await rP_ChunkNodes(sock);
co_return;
case ToClient::ChunkLightPrism:
co_await rP_ChunkLightPrism(sock);
co_return;
case ToClient::RemoveRegion:
co_await rP_RemoveRegion(sock);
co_return;
case ToClient::Tick:
co_await rP_Tick(sock);
co_return;
case ToClient::TestLinkCameraToEntity:
co_await rP_TestLinkCameraToEntity(sock);
co_return;
case ToClient::TestUnlinkCamera:
co_await rP_TestUnlinkCamera(sock);
co_return;
default:
protocolError();
}
}
coro<> ServerSession::rP_Disconnect(Net::AsyncSocket &sock) {
EnumDisconnect type = (EnumDisconnect) co_await sock.read<uint8_t>();
std::string reason = co_await sock.read<std::string>();
if(type == EnumDisconnect::ByInterface)
reason = "по запросу интерфейса " + reason;
else if(type == EnumDisconnect::CriticalError)
reason = "на сервере произошла критическая ошибка " + reason;
else if(type == EnumDisconnect::ProtocolError)
reason = "ошибка протокола (сервер) " + reason;
LOG.info() << "Отключение от сервера: " << reason;
co_return;
}
coro<> ServerSession::rP_AssetsBindDK(Net::AsyncSocket &sock) {
std::string compressed = co_await sock.read<std::string>();
std::u8string in((const char8_t*) compressed.data(), compressed.size());
std::u8string data = unCompressLinear(in);
Net::LinearReader lr(data);
uint16_t domainsCount = lr.read<uint16_t>();
std::vector<std::string> domains;
domains.reserve(domainsCount);
for(uint16_t i = 0; i < domainsCount; ++i)
domains.push_back(lr.read<std::string>());
for(size_t type = 0; type < static_cast<size_t>(EnumAssets::MAX_ENUM); ++type) {
uint32_t count = lr.read<uint32_t>();
for(uint32_t i = 0; i < count; ++i) {
uint16_t domainId = lr.read<uint16_t>();
std::string key = lr.read<std::string>();
if(domainId >= domains.size())
continue;
ResourceId serverId = NextServerId[type]++;
auto& table = ServerIdToDK[type];
if(table.size() <= serverId)
table.resize(serverId + 1);
table[serverId] = {domains[domainId], std::move(key)};
}
}
co_return;
}
coro<> ServerSession::rP_AssetsBindHH(Net::AsyncSocket &sock) {
static std::atomic<uint32_t> debugResourceLogCount = 0;
AssetsBindsChange abc;
for(size_t typeIndex = 0; typeIndex < static_cast<size_t>(EnumAssets::MAX_ENUM); ++typeIndex) {
uint32_t count = co_await sock.read<uint32_t>();
if(count == 0)
continue;
std::vector<AssetBindEntry> binds;
binds.reserve(count);
for(size_t iter = 0; iter < count; ++iter) {
uint32_t id = co_await sock.read<uint32_t>();
Hash_t hash;
co_await sock.read((std::byte*) hash.data(), hash.size());
std::string headerStr = co_await sock.read<std::string>();
std::vector<uint8_t> header(headerStr.begin(), headerStr.end());
auto& table = ServerIdToDK[typeIndex];
assert(id <= table.size());
if(id >= table.size()) {
LOG.warn() << "AssetsBindHH without domain/key for id=" << id;
continue;
}
const auto& [domain, key] = table[id];
assert(!domain.empty() && !key.empty());
if(domain.empty() && key.empty()) {
LOG.warn() << "AssetsBindHH missing domain/key for id=" << id;
continue;
}
EnumAssets type = static_cast<EnumAssets>(typeIndex);
AssetsManager::BindResult bindResult = AM->bindServerResource(
type, (ResourceId) id, domain, key, hash, header);
if(bindResult.ReboundFrom)
abc.Lost[typeIndex].push_back(*bindResult.ReboundFrom);
if(!bindResult.Changed && !bindResult.ReboundFrom)
continue;
binds.emplace_back(AssetBindEntry{
.Type = type,
.Id = bindResult.LocalId,
.Domain = domain,
.Key = key,
.Hash = hash,
.Header = std::move(header)
});
if(binds.back().Domain == "test"
&& (binds.back().Type == EnumAssets::Nodestate
|| binds.back().Type == EnumAssets::Model
|| binds.back().Type == EnumAssets::Texture))
{
uint32_t idx = debugResourceLogCount.fetch_add(1);
if(idx < 128) {
LOG.debug() << "Bind asset type=" << assetTypeName(binds.back().Type)
<< " id=" << binds.back().Id
<< " key=" << binds.back().Domain << ':' << binds.back().Key
<< " hash=" << int(binds.back().Hash[0]) << '.'
<< int(binds.back().Hash[1]) << '.'
<< int(binds.back().Hash[2]) << '.'
<< int(binds.back().Hash[3]);
}
}
}
if(!binds.empty()) {
abc.Binds.append_range(binds);
binds.clear();
}
}
bool hasLost = false;
for(const auto& list : abc.Lost) {
if(!list.empty()) {
hasLost = true;
break;
}
}
if(!abc.Binds.empty() || hasLost)
AsyncContext.AssetsBinds.lock()->push_back(std::move(abc));
else
co_return;
co_return;
}
coro<> ServerSession::rP_AssetsInitSend(Net::AsyncSocket &sock) {
static std::atomic<uint32_t> debugResourceLogCount = 0;
uint32_t size = co_await sock.read<uint32_t>();
Hash_t hash;
co_await sock.read((std::byte*) hash.data(), hash.size());
bool found = false;
EnumAssets type = EnumAssets::Texture;
std::string domain;
std::string key;
for(int typeIndex = 0; typeIndex < (int) EnumAssets::MAX_ENUM && !found; ++typeIndex) {
auto& waitingByDomain = AsyncContext.ResourceWait[typeIndex];
for(auto iterDomain = waitingByDomain.begin(); iterDomain != waitingByDomain.end() && !found; ) {
auto& entries = iterDomain->second;
for(size_t i = 0; i < entries.size(); ++i) {
if(entries[i].second == hash) {
type = static_cast<EnumAssets>(typeIndex);
domain = iterDomain->first;
key = entries[i].first;
entries.erase(entries.begin() + i);
if(entries.empty())
iterDomain = waitingByDomain.erase(iterDomain);
else
++iterDomain;
found = true;
break;
}
}
if(!found)
++iterDomain;
}
}
if(!found) {
LOG.warn() << "AssetsInitSend for unknown hash " << int(hash[0]) << '.'
<< int(hash[1]) << '.' << int(hash[2]) << '.' << int(hash[3]);
AsyncContext.AssetsLoading[hash] = AssetLoading{
EnumAssets::Texture, 0, {}, {},
std::u8string(size, '\0'), 0
};
co_return;
}
ResourceId localId = AM->getOrCreateLocalId(type, domain, key);
if(domain == "test"
&& (type == EnumAssets::Nodestate
|| type == EnumAssets::Model
|| type == EnumAssets::Texture))
{
uint32_t idx = debugResourceLogCount.fetch_add(1);
if(idx < 128) {
LOG.debug() << "AssetsInitSend type=" << assetTypeName(type)
<< " id=" << localId
<< " key=" << domain << ':' << key
<< " size=" << size;
}
}
AsyncContext.AssetsLoading[hash] = AssetLoading{
type, localId, std::move(domain), std::move(key),
std::u8string(size, '\0'), 0
};
LOG.debug() << "Server started sending type=" << assetTypeName(type)
<< " id=" << localId
<< " key=" << AsyncContext.AssetsLoading[hash].Domain << ':'
<< AsyncContext.AssetsLoading[hash].Key
<< " hash=" << int(hash[0]) << '.'
<< int(hash[1]) << '.'
<< int(hash[2]) << '.'
<< int(hash[3])
<< " size=" << size;
co_return;
}
coro<> ServerSession::rP_AssetsNextSend(Net::AsyncSocket &sock) {
static std::atomic<uint32_t> debugResourceLogCount = 0;
Hash_t hash;
co_await sock.read((std::byte*) hash.data(), hash.size());
uint32_t size = co_await sock.read<uint32_t>();
if(!AsyncContext.AssetsLoading.contains(hash)) {
std::vector<std::byte> discard(size);
co_await sock.read(discard.data(), size);
co_return;
}
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())
co_return;
if(!al.Domain.empty() || !al.Key.empty()) {
if(al.Domain == "test"
&& (al.Type == EnumAssets::Nodestate
|| al.Type == EnumAssets::Model
|| al.Type == EnumAssets::Texture))
{
uint32_t idx = debugResourceLogCount.fetch_add(1);
if(idx < 128) {
LOG.debug() << "Resource loaded type=" << assetTypeName(al.Type)
<< " id=" << al.Id
<< " key=" << al.Domain << ':' << al.Key
<< " size=" << al.Data.size();
}
}
const EnumAssets type = al.Type;
const ResourceId id = al.Id;
const std::string domain = al.Domain;
const std::string key = al.Key;
const size_t resSize = al.Data.size();
AsyncContext.LoadedAssets.lock()->emplace_back(AssetEntry{
.Type = type,
.Id = id,
.Domain = std::move(al.Domain),
.Key = std::move(al.Key),
.Res = std::move(al.Data),
.Hash = hash
});
LOG.debug() << "Client received type=" << assetTypeName(type)
<< " id=" << id
<< " key=" << domain << ':' << key
<< " hash=" << int(hash[0]) << '.'
<< int(hash[1]) << '.'
<< int(hash[2]) << '.'
<< int(hash[3])
<< " size=" << resSize;
}
AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash));
auto iter = std::lower_bound(AsyncContext.AlreadyLoading.begin(), AsyncContext.AlreadyLoading.end(), hash);
if(iter != AsyncContext.AlreadyLoading.end() && *iter == hash)
AsyncContext.AlreadyLoading.erase(iter);
co_return;
}
coro<> ServerSession::rP_DefinitionsUpdate(Net::AsyncSocket &sock) {
static std::atomic<uint32_t> debugDefLogCount = 0;
uint32_t typeCount = co_await sock.read<uint32_t>();
typeCount = std::min<uint32_t>(typeCount, static_cast<uint32_t>(EnumDefContent::MAX_ENUM));
for(uint32_t type = 0; type < typeCount; ++type) {
uint32_t count = co_await sock.read<uint32_t>();
for(uint32_t i = 0; i < count; ++i) {
ResourceId id = co_await sock.read<ResourceId>();
std::string dataStr = co_await sock.read<std::string>();
(void)dataStr;
if(type == static_cast<uint32_t>(EnumDefContent::Node)) {
DefNode_t def;
def.NodestateId = 0;
def.TexId = id;
AsyncContext.ThisTickEntry.Profile_Node_AddOrChange.emplace_back(id, def);
if(id < 32) {
uint32_t idx = debugDefLogCount.fetch_add(1);
if(idx < 64) {
LOG.debug() << "DefNode id=" << id
<< " nodestate=" << def.NodestateId
<< " tex=" << def.TexId;
}
}
}
}
}
uint32_t lostCount = co_await sock.read<uint32_t>();
lostCount = std::min<uint32_t>(lostCount, static_cast<uint32_t>(EnumDefContent::MAX_ENUM));
for(uint32_t type = 0; type < lostCount; ++type) {
uint32_t count = co_await sock.read<uint32_t>();
for(uint32_t i = 0; i < count; ++i) {
ResourceId id = co_await sock.read<ResourceId>();
if(type == static_cast<uint32_t>(EnumDefContent::Node))
AsyncContext.ThisTickEntry.Profile_Node_Lost.push_back(id);
}
}
uint32_t dkCount = co_await sock.read<uint32_t>();
dkCount = std::min<uint32_t>(dkCount, static_cast<uint32_t>(EnumDefContent::MAX_ENUM));
for(uint32_t type = 0; type < dkCount; ++type) {
uint32_t count = co_await sock.read<uint32_t>();
for(uint32_t i = 0; i < count; ++i) {
std::string key = co_await sock.read<std::string>();
std::string domain = co_await sock.read<std::string>();
(void)key;
(void)domain;
}
}
co_return;
}
coro<> ServerSession::rP_ChunkVoxels(Net::AsyncSocket &sock) {
WorldId_t wcId = co_await sock.read<WorldId_t>();
Pos::GlobalChunk pos;
pos.unpack(co_await sock.read<Pos::GlobalChunk::Pack>());
uint32_t compressedSize = co_await sock.read<uint32_t>();
assert(compressedSize <= std::pow(2, 24));
std::u8string compressed(compressedSize, '\0');
co_await sock.read((std::byte*) compressed.data(), compressedSize);
AsyncContext.ThisTickEntry.Chunks_AddOrChange_Voxel[wcId].insert({pos, std::move(compressed)});
co_return;
}
coro<> ServerSession::rP_ChunkNodes(Net::AsyncSocket &sock) {
WorldId_t wcId = co_await sock.read<WorldId_t>();
Pos::GlobalChunk pos;
pos.unpack(co_await sock.read<Pos::GlobalChunk::Pack>());
uint32_t compressedSize = co_await sock.read<uint32_t>();
assert(compressedSize <= std::pow(2, 24));
std::u8string compressed(compressedSize, '\0');
co_await sock.read((std::byte*) compressed.data(), compressedSize);
AsyncContext.ThisTickEntry.Chunks_AddOrChange_Node[wcId].insert({pos, std::move(compressed)});
co_return;
}
coro<> ServerSession::rP_ChunkLightPrism(Net::AsyncSocket &sock) {
(void)sock;
co_return;
}
coro<> ServerSession::rP_RemoveRegion(Net::AsyncSocket &sock) {
WorldId_t wcId = co_await sock.read<WorldId_t>();
Pos::GlobalRegion pos;
pos.unpack(co_await sock.read<Pos::GlobalRegion::Pack>());
AsyncContext.ThisTickEntry.Regions_Lost[wcId].push_back(pos);
co_return;
}
coro<> ServerSession::rP_Tick(Net::AsyncSocket &sock) {
(void)sock;
AsyncContext.TickSequence.lock()->push_back(std::move(AsyncContext.ThisTickEntry));
AsyncContext.ThisTickEntry = {};
co_return;
}
coro<> ServerSession::rP_TestLinkCameraToEntity(Net::AsyncSocket &sock) {
(void) sock;
co_return;
}
coro<> ServerSession::rP_TestUnlinkCamera(Net::AsyncSocket &sock) {
(void) sock;
co_return;
}
}