Files
LuaVox/Src/Client/ServerSession.cpp

1153 lines
43 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";
}
}
const char* toClientPacketName(ToClient type) {
switch(type) {
case ToClient::Init: return "Init";
case ToClient::Disconnect: return "Disconnect";
case ToClient::AssetsBindDK: return "AssetsBindDK";
case ToClient::AssetsBindHH: return "AssetsBindHH";
case ToClient::AssetsInitSend: return "AssetsInitSend";
case ToClient::AssetsNextSend: return "AssetsNextSend";
case ToClient::DefinitionsUpdate: return "DefinitionsUpdate";
case ToClient::ChunkVoxels: return "ChunkVoxels";
case ToClient::ChunkNodes: return "ChunkNodes";
case ToClient::ChunkLightPrism: return "ChunkLightPrism";
case ToClient::RemoveRegion: return "RemoveRegion";
case ToClient::Tick: return "Tick";
case ToClient::TestLinkCameraToEntity: return "TestLinkCameraToEntity";
case ToClient::TestUnlinkCamera: return "TestUnlinkCamera";
default: return "Unknown";
}
}
}
ServerSession::ServerSession(asio::io_context &ioc, std::unique_ptr<Net::AsyncSocket>&& socket)
: IAsyncDestructible(ioc), Socket(std::move(socket)), AM(ioc, "Cache")
{
assert(Socket.get());
Profiles.DefNode[0] = {0};
Profiles.DefNode[1] = {1};
Profiles.DefNode[2] = {2};
Profiles.DefNode[3] = {3};
Profiles.DefNode[4] = {4};
try {
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) {
// Если AssetsManager запрашивает ресурсы с сервера
{
std::vector<Hash_t> needRequest = AM.pullNeededResources();
if(!needRequest.empty()) {
Net::Packet pack;
std::vector<Net::Packet> packets;
auto check = [&]() {
if(pack.size() > 64000)
packets.emplace_back(std::move(pack));
};
pack << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::ResourceRequest;
pack << (uint16_t) needRequest.size();
for(const Hash_t& hash : needRequest) {
pack.write((const std::byte*) hash.data(), 32);
check();
}
if(pack.size())
packets.emplace_back(std::move(pack));
Socket->pushPackets(&packets);
}
}
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) {
// Привязки Id -> Domain+Key
for(const auto& binds : data.BindsDK) {
AM.pushAssetsBindDK(binds.Domains, binds.Keys);
}
// Привязки Id -> Hash+Header
for(auto& binds : data.BindsHH) {
AM.pushAssetsBindHH(std::move(binds.HashAndHeaders));
}
// Полученные ресурсы с сервера
if(!data.ReceivedAssets.empty())
AM.pushNewResources(std::move(data.ReceivedAssets));
{
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());
}
}
{
AssetsManager::ResourceUpdates updates = AM.pullResourceUpdates();
result.AssetsNodestates = std::move(updates.Nodestates);
result.AssetsModels = std::move(updates.Models);
result.AssetsTextures = std::move(updates.Textures);
}
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(auto& [wId, regions] : regions_Lost_Result)
result.Chunks_Lost[wId] = std::vector<Pos::GlobalRegion>(regions.begin(), regions.end());
{
for(TickData& data : ticks) {
}
}
if(RS)
RS->pushStageTickSync();
// Применяем изменения по ресурсам, профилям и контенту
// Определения
{
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);
}
AM.tick();
// Здесь нужно обработать управляющие пакеты
// Оповещение модуля рендера об изменениях ресурсов
// 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() {
AsyncContext.AssetsLoading.clear();
AsyncContext.ThisTickEntry = {};
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>();
if(DebugLogPackets) {
ToClient type = static_cast<ToClient>(first);
LOG.debug() << "Recv packet=" << toClientPacketName(type) << " id=" << int(first);
}
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) {
UpdateAssetsBindsDK update;
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>();
update.Domains.reserve(domainsCount);
for(uint16_t i = 0; i < domainsCount; ++i)
update.Domains.push_back(lr.read<std::string>());
for(size_t type = 0; type < static_cast<size_t>(EnumAssets::MAX_ENUM); ++type) {
update.Keys[type].resize(update.Domains.size());
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 >= update.Domains.size())
continue;
update.Keys.at(type).at(domainId).push_back(key);
}
}
AsyncContext.ThisTickEntry.BindsDK.emplace_back(std::move(update));
}
coro<> ServerSession::rP_AssetsBindHH(Net::AsyncSocket &sock) {
UpdateAssetsBindsHH update;
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;
for(size_t iter = 0; iter < count; ++iter) {
uint32_t id = co_await sock.read<uint32_t>();
ResourceFile::Hash_t hash;
co_await sock.read((std::byte*) hash.data(), hash.size());
std::string headerStr = co_await sock.read<std::string>();
update.HashAndHeaders[typeIndex].emplace_back(id, hash, std::u8string((const char8_t*) headerStr.data(), headerStr.size()));
}
}
AsyncContext.ThisTickEntry.BindsHH.emplace_back(std::move(update));
}
coro<> ServerSession::rP_AssetsInitSend(Net::AsyncSocket &sock) {
uint32_t size = co_await sock.read<uint32_t>();
Hash_t hash;
co_await sock.read((std::byte*) hash.data(), hash.size());
AsyncContext.AssetsLoading[hash] = AssetLoading{
.Data = std::u8string(size, '\0'),
.Offset = 0
};
}
coro<> ServerSession::rP_AssetsNextSend(Net::AsyncSocket &sock) {
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;
AsyncContext.ThisTickEntry.ReceivedAssets.emplace_back(hash, std::move(al.Data));
AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash));
}
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;
}
}