Files
LuaVox/Src/Client/ServerSession.cpp
2025-09-10 21:09:37 +06:00

1249 lines
48 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 <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 <unordered_map>
#include <unordered_set>
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());
Profiles.DefNode[0] = {0};
Profiles.DefNode[1] = {1};
Profiles.DefNode[2] = {2};
Profiles.DefNode[3] = {3};
Profiles.DefNode[4] = {4};
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::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) {
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
{
std::vector<std::pair<AssetsManager::ResourceKey, std::optional<Resource>>> resources = AM->pullReads();
std::vector<Hash_t> needRequest;
for(auto& [key, res] : resources) {
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 {
AssetEntry entry {
.Type = key.Type,
.Id = key.Id,
.Domain = key.Domain,
.Key = key.Key,
.Res = *res
};
AsyncContext.LoadedResources.emplace_back(std::move(entry));
}
}
if(!needRequest.empty()) {
assert(needRequest.size() < (1 << 16));
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 = false;
}
}
// Проверить если такой запрос уже был отправлен в 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.emplace_back(bind.Hash, bind.Type, bind.Domain, bind.Key, bind.Id);
}
}
// Отправляем запрос на получение ресурсов
if(!needToLoad.empty())
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, void*> profile_Entity_AddOrChange;
std::vector<DefEntityId> profile_Entity_Lost;
std::unordered_map<DefItemId, void*> profile_Item_AddOrChange;
std::vector<DefItemId> profile_Item_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, _] : 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) {
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) {
unCompressNodes(val, caocvr[pos].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) {
MyAssets.ExistBinds[(int) entry.Type].insert(entry.Id);
result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id);
// Если ресурс был в кеше, то достаётся от туда
auto iter = MyAssets.NotInUse[(int) entry.Type].find(entry.Domain+':'+entry.Key);
if(iter != MyAssets.NotInUse[(int) entry.Type].end()) {
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& [wId, lost] : regions_Lost_Result) {
auto iterWorld = Content.Worlds.find(wId);
if(iterWorld == Content.Worlds.end())
continue;
for(const Pos::GlobalRegion& rPos : lost) {
auto iterRegion = iterWorld->second.Regions.find(rPos);
if(iterRegion != iterWorld->second.Regions.end())
iterWorld->second.Regions.erase(iterRegion);
}
}
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);
}
}
}
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;
}
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;
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::L1) first) {
case ToClient::L1::System: co_await rP_System(sock); co_return;
case ToClient::L1::Resource: co_await rP_Resource(sock); co_return;
case ToClient::L1::Definition: co_await rP_Definition(sock); co_return;
case ToClient::L1::Content: co_await rP_Content(sock); co_return;
default:
protocolError();
}
}
coro<> ServerSession::rP_System(Net::AsyncSocket &sock) {
uint8_t second = co_await sock.read<uint8_t>();
switch((ToClient::L2System) second) {
case ToClient::L2System::Init:
co_return;
case ToClient::L2System::Disconnect:
{
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;
}
case ToClient::L2System::LinkCameraToEntity:
co_return;
case ToClient::L2System::UnlinkCamera:
co_return;
case ToClient::L2System::SyncTick:
AsyncContext.TickSequence.lock()->push_back(std::move(AsyncContext.ThisTickEntry));
co_return;
default:
protocolError();
}
}
coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
uint8_t second = co_await sock.read<uint8_t>();
switch((ToClient::L2Resource) second) {
case ToClient::L2Resource::Bind:
{
uint32_t count = co_await sock.read<uint32_t>();
std::vector<AssetBindEntry> binds;
binds.reserve(count);
for(size_t iter = 0; iter < count; iter++) {
uint8_t type = co_await sock.read<uint8_t>();
if(type >= (int) EnumAssets::MAX_ENUM)
protocolError();
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;
co_await sock.read((std::byte*) hash.data(), hash.size());
binds.emplace_back(
(EnumAssets) type, (ResourceId) id, std::move(domain),
std::move(key), hash
);
}
AsyncContext.AssetsBinds.lock()->push_back(AssetsBindsChange(binds, {}));
co_return;
}
case ToClient::L2Resource::Lost:
{
uint32_t count = co_await sock.read<uint32_t>();
AssetsBindsChange abc;
for(size_t iter = 0; iter < count; iter++) {
uint8_t type = co_await sock.read<uint8_t>();
uint32_t id = co_await sock.read<uint32_t>();
if(type >= (int) EnumAssets::MAX_ENUM)
protocolError();
abc.Lost[(int) type].push_back(id);
}
AsyncContext.AssetsBinds.lock()->emplace_back(std::move(abc));
co_return;
}
case ToClient::L2Resource::InitResSend:
{
uint32_t size = co_await sock.read<uint32_t>();
Hash_t hash;
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>();
if(type >= EnumAssets::MAX_ENUM)
protocolError();
std::string domain = co_await sock.read<std::string>();
std::string key = co_await sock.read<std::string>();
AsyncContext.AssetsLoading[hash] = AssetLoading{
type, id, std::move(domain), std::move(key),
std::u8string(size, '\0'), 0
};
co_return;
}
case ToClient::L2Resource::ChunkSend:
{
Hash_t hash;
co_await sock.read((std::byte*) hash.data(), hash.size());
try {
uint32_t size = co_await sock.read<uint32_t>();
assert(AsyncContext.AssetsLoading.contains(hash));
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));
}
} catch(const std::exception& exc) {
std::string err = exc.what();
int g = 0;
}
co_return;
}
default:
protocolError();
}
}
coro<> ServerSession::rP_Definition(Net::AsyncSocket &sock) {
uint8_t second = co_await sock.read<uint8_t>();
switch((ToClient::L2Definition) second) {
case ToClient::L2Definition::World: {
DefWorldId cdId = co_await sock.read<DefWorldId>();
co_return;
}
case ToClient::L2Definition::FreeWorld: {
DefWorldId cdId = co_await sock.read<DefWorldId>();
co_return;
}
case ToClient::L2Definition::Voxel: {
DefVoxelId cdId = co_await sock.read<DefVoxelId>();
co_return;
}
case ToClient::L2Definition::FreeVoxel: {
DefVoxelId cdId = co_await sock.read<DefVoxelId>();
co_return;
}
case ToClient::L2Definition::Node:
{
DefNode_t def;
DefNodeId id = co_await sock.read<DefNodeId>();
def.NodestateId = co_await sock.read<uint32_t>();
def.TexId = id;
AsyncContext.ThisTickEntry.Profile_Node_AddOrChange.emplace_back(id, def);
co_return;
}
case ToClient::L2Definition::FreeNode:
{
DefNodeId id = co_await sock.read<DefNodeId>();
AsyncContext.ThisTickEntry.Profile_Node_Lost.push_back(id);
co_return;
}
case ToClient::L2Definition::Portal:
co_return;
case ToClient::L2Definition::FreePortal:
co_return;
case ToClient::L2Definition::Entity:
co_return;
case ToClient::L2Definition::FreeEntity:
co_return;
default:
protocolError();
}
}
coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) {
uint8_t second = co_await sock.read<uint8_t>();
switch((ToClient::L2Content) second) {
case ToClient::L2Content::World: {
WorldId_t wId = co_await sock.read<uint32_t>();
AsyncContext.ThisTickEntry.Worlds_AddOrChange.emplace_back(wId, nullptr);
co_return;
}
case ToClient::L2Content::RemoveWorld: {
WorldId_t wId = co_await sock.read<uint32_t>();
AsyncContext.ThisTickEntry.Worlds_Lost.push_back(wId);
co_return;
}
case ToClient::L2Content::Portal:
co_return;
case ToClient::L2Content::RemovePortal:
co_return;
case ToClient::L2Content::Entity:
co_return;
case ToClient::L2Content::RemoveEntity:
co_return;
case ToClient::L2Content::ChunkVoxels:
{
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;
}
case ToClient::L2Content::ChunkNodes:
{
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;
}
case ToClient::L2Content::ChunkLightPrism:
co_return;
case ToClient::L2Content::RemoveRegion: {
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;
}
default:
protocolError();
}
}
}