#pragma once #include #include #include #include namespace LV::Server { using ResourceId_t = uint32_t; // Двоичные данные using BinTextureId_t = ResourceId_t; using BinSoundId_t = ResourceId_t; using BinModelId_t = ResourceId_t; // Игровые определения using DefWorldId_t = ResourceId_t; using DefVoxelId_t = ResourceId_t; using DefNodeId_t = ResourceId_t; using DefPortalId_t = ResourceId_t; using DefEntityId_t = ResourceId_t; // Контент, основанный на игровых определениях using WorldId_t = ResourceId_t; // В одном регионе может быть максимум 2^16 сущностей. Клиенту адресуются сущности в формате <мир>+<позиция региона>+ // И если сущность перешла из одного региона в другой, идентификатор сущности на стороне клиента сохраняется using LocalEntityId_t = uint16_t; using GlobalEntityId_t = std::tuple; using PortalId_t = uint16_t; using MediaStreamId_t = uint16_t; using ContentBridgeId_t = uint16_t; using PlayerId_t = uint32_t; /* Сервер загружает информацию о локальных текстурах Синхронизация часто используемых текстур? Пересмотр списка текстур? Динамичные текстуры? */ struct ResourceFile { using Hash_t = boost::uuids::detail::sha1::digest_type; Hash_t Hash; std::vector Data; void calcHash() { boost::uuids::detail::sha1 hash; hash.process_bytes(Data.data(), Data.size()); hash.get_digest(Hash); } }; struct ServerTime { uint32_t Seconds : 24, Sub : 8; }; struct VoxelCube { Pos::Local256_u Left, Right; DefVoxelId_t VoxelId; auto operator<=>(const VoxelCube&) const = default; }; struct VoxelCube_Region { Pos::Local4096_u Left, Right; DefVoxelId_t VoxelId; auto operator<=>(const VoxelCube_Region&) const = default; }; struct Node { DefNodeId_t NodeId; uint8_t Rotate : 6; }; struct AABB { Pos::Object VecMin, VecMax; void sortMinMax() { Pos::Object::value_type left, right; for(int iter = 0; iter < 3; iter++) { left = std::min(VecMin[iter], VecMax[iter]); right = std::max(VecMin[iter], VecMax[iter]); VecMin[iter] = left; VecMax[iter] = right; } } bool isCollideWith(const AABB &other, bool axis[3] = nullptr) { return calcBoxToBoxCollide(VecMin, VecMax, other.VecMin, other.VecMax, axis); } bool collideWithDelta(const AABB &other, const Pos::Object &my_speed, int32_t &delta, bool axis[3] = nullptr) { return calcBoxToBoxCollideWithDelta(VecMin, VecMax, other.VecMin, other.VecMax, my_speed, &delta, Pos::Object_t::BS, axis); } }; struct LocalAABB { uint64_t x : 20, y : 20, z : 20; AABB atPos(const Pos::Object &pos) const { return {pos-Pos::Object(x/2, y/2, z/2), pos+Pos::Object(x/2, y/2, z/2)}; } }; struct CollisionAABB : public AABB { enum struct EnumType { Voxel, Node, Entity, Barrier, Portal, Another } Type; union { struct { LocalEntityId_t Index; } Entity; struct { Pos::Local16_u Pos; } Node; struct { Pos::Local16_u Chunk; uint32_t Index; DefVoxelId_t Id; } Voxel; struct { } Barrier; struct { } Portal; struct { } Another; }; bool Skip = false; }; class Entity { DefEntityId_t DefId; public: LocalAABB ABBOX; // PosQuat DefWorldId_t WorldId; Pos::Object Pos, Speed, Acceleration; glm::quat Quat; static constexpr uint16_t HP_BS = 4096, HP_BS_Bit = 12; uint32_t HP = 0; Pos::GlobalRegion InRegionPos; // State std::unordered_map Tags; // m_attached_particle_spawners // states bool // Сущность будет удалена в слудующем такте NeedRemove = false, // Сущность была удалена или не действительна IsRemoved = false; public: Entity(DefEntityId_t defId); AABB aabbAtPos() { return {Pos-Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2), Pos+Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2)}; } DefEntityId_t getDefId() const { return DefId; } }; template struct VoxelCuboidsFuncs { // Кубы должны быть отсортированы static bool canMerge(const Vec& a, const Vec& b) { if (a.VoxelId != b.VoxelId) return false; // Проверяем, что кубы смежны по одной из осей bool xAdjacent = (a.Right.X == b.Left.X) && (a.Left.Y == b.Left.Y) && (a.Right.Y == b.Right.Y) && (a.Left.Z == b.Left.Z) && (a.Right.Z == b.Right.Z); bool yAdjacent = (a.Right.Y == b.Left.Y) && (a.Left.X == b.Left.X) && (a.Right.X == b.Right.X) && (a.Left.Z == b.Left.Z) && (a.Right.Z == b.Right.Z); bool zAdjacent = (a.Right.Z == b.Left.Z) && (a.Left.X == b.Left.X) && (a.Right.X == b.Right.X) && (a.Left.Y == b.Left.Y) && (a.Right.Y == b.Right.Y); return xAdjacent || yAdjacent || zAdjacent; } static Vec mergeCubes(const Vec& a, const Vec& b) { Vec merged; merged.VoxelId = a.VoxelId; // Объединяем кубы по минимальным и максимальным координатам merged.Left.X = std::min(a.Left.X, b.Left.X); merged.Left.Y = std::min(a.Left.Y, b.Left.Y); merged.Left.Z = std::min(a.Left.Z, b.Left.Z); merged.Right.X = std::max(a.Right.X, b.Right.X); merged.Right.Y = std::max(a.Right.Y, b.Right.Y); merged.Right.Z = std::max(a.Right.Z, b.Right.Z); return merged; } static std::vector optimizeVoxelRegions(std::vector regions) { bool changed; do { changed = false; for (size_t i = 0; i < regions.size(); ++i) { for (size_t j = i + 1; j < regions.size(); ++j) { if (canMerge(regions[i], regions[j])) { regions[i] = mergeCubes(regions[i], regions[j]); regions.erase(regions.begin() + j); changed = true; --j; } } } } while (changed); return regions; } static bool isCubesIntersect(const Vec& a, const Vec& b) { return !(a.Right.X < b.Left.X || a.Left.X > b.Right.X || a.Right.Y < b.Left.Y || a.Left.Y > b.Right.Y || a.Right.Z < b.Left.Z || a.Left.Z > b.Right.Z); } static std::vector subtractCube(const Vec& a, const Vec& b) { std::vector result; if (!isCubesIntersect(a, b)) { result.push_back(a); return result; } decltype(a.Left) intersectLeft = { std::max(a.Left.X, b.Left.X), std::max(a.Left.Y, b.Left.Y), std::max(a.Left.Z, b.Left.Z) }; decltype(a.Left) intersectRight = { std::min(a.Right.X, b.Right.X), std::min(a.Right.Y, b.Right.Y), std::min(a.Right.Z, b.Right.Z) }; // Разделяем куб a на меньшие кубы, исключая пересечение if (a.Left.X < intersectLeft.X) { result.push_back({a.Left, decltype(a.Left)(intersectLeft.X - 1, a.Right.Y, a.Right.Z), a.Material}); } if (a.Right.X > intersectRight.X) { result.push_back({decltype(a.Left)(intersectRight.X + 1, a.Left.Y, a.Left.Z), a.Right, a.Material}); } if (a.Left.Y < intersectLeft.Y) { result.push_back({ {intersectLeft.X, a.Left.Y, a.Left.Z}, decltype(a.Left)(intersectRight.X, intersectLeft.Y - 1, a.Right.Z), a.Material }); } if (a.Right.Y > intersectRight.Y) { result.push_back({ decltype(a.Left)(intersectLeft.X, intersectRight.Y + 1, a.Left.Z), {intersectRight.X, a.Right.Y, a.Right.Z}, a.Material }); } if (a.Left.Z < intersectLeft.Z) { result.push_back({ {intersectLeft.X, intersectLeft.Y, a.Left.Z}, decltype(a.Left)(intersectRight.X, intersectRight.Y, intersectLeft.Z - 1), a.Material }); } if (a.Right.Z > intersectRight.Z) { result.push_back({ decltype(a.Left)(intersectLeft.X, intersectLeft.Y, intersectRight.Z + 1), {intersectRight.X, intersectRight.Y, a.Right.Z}, a.Material }); } return result; } }; inline void convertRegionVoxelsToChunks(const std::vector& regions, std::vector *chunks) { for (const auto& region : regions) { int minX = region.Left.X >> 8; int minY = region.Left.Y >> 8; int minZ = region.Left.Z >> 8; int maxX = region.Right.X >> 8; int maxY = region.Right.Y >> 8; int maxZ = region.Right.Z >> 8; for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { for (int z = minZ; z <= maxZ; ++z) { Pos::Local256_u left { static_cast(std::max((x << 8), region.Left.X) - (x << 8)), static_cast(std::max((y << 8), region.Left.Y) - (y << 8)), static_cast(std::max((z << 8), region.Left.Z) - (z << 8)) }; Pos::Local256_u right { static_cast(std::min(((x+1) << 8)-1, region.Right.X) - (x << 8)), static_cast(std::min(((y+1) << 8)-1, region.Right.Y) - (y << 8)), static_cast(std::min(((z+1) << 8)-1, region.Right.Z) - (z << 8)) }; int chunkIndex = z * 16 * 16 + y * 16 + x; chunks[chunkIndex].emplace_back(left, right, region.VoxelId); } } } } } inline void convertChunkVoxelsToRegion(const std::vector *chunks, std::vector ®ions) { for (int x = 0; x < 16; ++x) { for (int y = 0; y < 16; ++y) { for (int z = 0; z < 16; ++z) { int chunkIndex = z * 16 * 16 + y * 16 + x; Pos::Local4096_u left(x << 8, y << 8, z << 8); for (const auto& cube : chunks[chunkIndex]) { regions.emplace_back( Pos::Local4096_u(left.X+cube.Left.X, left.Y+cube.Left.Y, left.Z+cube.Left.Z), Pos::Local4096_u(left.X+cube.Right.X, left.Y+cube.Right.Y, left.Z+cube.Right.Z), cube.VoxelId ); } } } } std::sort(regions.begin(), regions.end()); regions = VoxelCuboidsFuncs::optimizeVoxelRegions(regions); } }