codex-5.2: кое как доведено до почти рабочего состояния

This commit is contained in:
2026-01-05 00:35:52 +06:00
parent 5904fe6853
commit 8ce820569a
8 changed files with 438 additions and 256 deletions

View File

@@ -73,6 +73,68 @@ static std::u8string readOptionalMeta(const fs::path& path) {
return readFileBytes(metaPath);
}
static std::vector<uint32_t> collectTexturePipelineIds(const std::vector<uint8_t>& code);
struct ParsedModelHeader {
std::vector<AssetsManager::AssetId> ModelDeps;
std::vector<std::vector<uint8_t>> TexturePipelines;
std::vector<AssetsManager::AssetId> TextureDeps;
};
std::optional<std::vector<AssetsManager::AssetId>> parseNodestateHeaderBytes(const std::vector<uint8_t>& header) {
if(header.empty() || header.size() % sizeof(AssetsManager::AssetId) != 0)
return std::nullopt;
const size_t count = header.size() / sizeof(AssetsManager::AssetId);
std::vector<AssetsManager::AssetId> deps;
deps.resize(count);
for(size_t i = 0; i < count; ++i) {
AssetsManager::AssetId raw = 0;
std::memcpy(&raw, header.data() + i * sizeof(AssetsManager::AssetId), sizeof(AssetsManager::AssetId));
deps[i] = raw;
}
return deps;
}
std::optional<ParsedModelHeader> parseModelHeaderBytes(const std::vector<uint8_t>& header) {
if(header.empty())
return std::nullopt;
ParsedModelHeader result;
try {
TOS::ByteBuffer buffer(header.size(), header.data());
auto reader = buffer.reader();
uint16_t modelCount = reader.readUInt16();
result.ModelDeps.reserve(modelCount);
for(uint16_t i = 0; i < modelCount; ++i)
result.ModelDeps.push_back(reader.readUInt32());
uint16_t texCount = reader.readUInt16();
result.TexturePipelines.reserve(texCount);
for(uint16_t i = 0; i < texCount; ++i) {
uint32_t size32 = reader.readUInt32();
TOS::ByteBuffer pipe;
reader.readBuffer(pipe);
if(pipe.size() != size32)
return std::nullopt;
result.TexturePipelines.emplace_back(pipe.begin(), pipe.end());
}
std::unordered_set<AssetsManager::AssetId> seen;
for(const auto& pipe : result.TexturePipelines) {
for(uint32_t id : collectTexturePipelineIds(pipe)) {
if(seen.insert(id).second)
result.TextureDeps.push_back(id);
}
}
} catch(const std::exception&) {
return std::nullopt;
}
return result;
}
struct PipelineRemapResult {
bool Ok = true;
std::string Error;
@@ -508,9 +570,23 @@ AssetsManager::PackReloadResult AssetsManager::reloadPacks(const PackRegister& r
auto [mDomain, mKey] = parseDomainKey(model, entry.Domain);
return getOrCreateLocalId(AssetType::Model, mDomain, mKey);
};
auto normalizeTexturePipelineSrc = [](std::string_view src) -> std::string {
std::string out(src);
auto isSpace = [](unsigned char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; };
size_t start = 0;
while(start < out.size() && isSpace(static_cast<unsigned char>(out[start])))
++start;
if(out.compare(start, 3, "tex") != 0) {
std::string pref = "tex ";
pref += out.substr(start);
return pref;
}
return out;
};
auto textureResolver = [&](std::string_view textureSrc) -> std::vector<uint8_t> {
TexturePipelineProgram tpp;
if(!tpp.compile(std::string(textureSrc)))
if(!tpp.compile(normalizeTexturePipelineSrc(textureSrc)))
return {};
auto textureIdResolver = [&](std::string_view name) -> std::optional<uint32_t> {
auto [tDomain, tKey] = parseDomainKey(name, entry.Domain);
@@ -759,52 +835,21 @@ std::optional<AssetsManager::ParsedHeader> AssetsManager::parseHeader(AssetType
result.Type = type;
if(type == AssetType::Nodestate) {
if(header.size() % sizeof(AssetId) != 0)
auto deps = parseNodestateHeaderBytes(header);
if(!deps)
return std::nullopt;
const size_t count = header.size() / sizeof(AssetId);
result.ModelDeps.resize(count);
for(size_t i = 0; i < count; ++i) {
AssetId raw = 0;
std::memcpy(&raw, header.data() + i * sizeof(AssetId), sizeof(AssetId));
result.ModelDeps[i] = raw;
}
result.ModelDeps = std::move(*deps);
return result;
}
if(type == AssetType::Model) {
try {
TOS::ByteBuffer buffer(header.size(), header.data());
auto reader = buffer.reader();
uint16_t modelCount = reader.readUInt16();
result.ModelDeps.reserve(modelCount);
for(uint16_t i = 0; i < modelCount; ++i)
result.ModelDeps.push_back(reader.readUInt32());
uint16_t texCount = reader.readUInt16();
result.TexturePipelines.reserve(texCount);
for(uint16_t i = 0; i < texCount; ++i) {
uint32_t size32 = reader.readUInt32();
TOS::ByteBuffer pipe;
reader.readBuffer(pipe);
if(pipe.size() != size32) {
return std::nullopt;
}
result.TexturePipelines.emplace_back(pipe.begin(), pipe.end());
}
std::unordered_set<AssetId> seen;
for(const auto& pipe : result.TexturePipelines) {
for(uint32_t id : collectTexturePipelineIds(pipe)) {
if(seen.insert(id).second)
result.TextureDeps.push_back(id);
}
}
return result;
} catch(const std::exception&) {
auto parsed = parseModelHeaderBytes(header);
if(!parsed)
return std::nullopt;
}
result.ModelDeps = std::move(parsed->ModelDeps);
result.TexturePipelines = std::move(parsed->TexturePipelines);
result.TextureDeps = std::move(parsed->TextureDeps);
return result;
}
return std::nullopt;

View File

@@ -1424,68 +1424,73 @@ coro<> ServerSession::rP_AssetsInitSend(Net::AsyncSocket &sock) {
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;
std::vector<AssetLoadingEntry> matches;
for(int typeIndex = 0; typeIndex < (int) EnumAssets::MAX_ENUM && !found; ++typeIndex) {
for(int typeIndex = 0; typeIndex < (int) EnumAssets::MAX_ENUM; ++typeIndex) {
auto& waitingByDomain = AsyncContext.ResourceWait[typeIndex];
for(auto iterDomain = waitingByDomain.begin(); iterDomain != waitingByDomain.end() && !found; ) {
for(auto iterDomain = waitingByDomain.begin(); iterDomain != waitingByDomain.end(); ) {
auto& entries = iterDomain->second;
for(size_t i = 0; i < entries.size(); ++i) {
for(size_t i = 0; i < entries.size(); ) {
if(entries[i].second == hash) {
type = static_cast<EnumAssets>(typeIndex);
domain = iterDomain->first;
key = entries[i].first;
EnumAssets type = static_cast<EnumAssets>(typeIndex);
const std::string& domain = iterDomain->first;
const std::string& key = entries[i].first;
ResourceId localId = AM->getOrCreateLocalId(type, domain, key);
matches.push_back(AssetLoadingEntry{
.Type = type,
.Id = localId,
.Domain = domain,
.Key = key
});
entries.erase(entries.begin() + i);
if(entries.empty())
iterDomain = waitingByDomain.erase(iterDomain);
else
++iterDomain;
found = true;
break;
} else {
++i;
}
}
if(!found)
if(entries.empty())
iterDomain = waitingByDomain.erase(iterDomain);
else
++iterDomain;
}
}
if(!found) {
if(matches.empty()) {
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
.Entries = {},
.Data = std::u8string(size, '\0'),
.Offset = 0
};
co_return;
}
ResourceId localId = AM->getOrCreateLocalId(type, domain, key);
AssetLoadingEntry first = matches.front();
if(domain == "test"
&& (type == EnumAssets::Nodestate
|| type == EnumAssets::Model
|| type == EnumAssets::Texture))
if(first.Domain == "test"
&& (first.Type == EnumAssets::Nodestate
|| first.Type == EnumAssets::Model
|| first.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;
LOG.debug() << "AssetsInitSend type=" << assetTypeName(first.Type)
<< " id=" << first.Id
<< " key=" << first.Domain << ':' << first.Key
<< " size=" << size
<< " matches=" << matches.size();
}
}
AsyncContext.AssetsLoading[hash] = AssetLoading{
type, localId, std::move(domain), std::move(key),
std::u8string(size, '\0'), 0
.Entries = std::move(matches),
.Data = std::u8string(size, '\0'),
.Offset = 0
};
LOG.debug() << "Server started sending type=" << assetTypeName(type)
<< " id=" << localId
<< " key=" << AsyncContext.AssetsLoading[hash].Domain << ':'
<< AsyncContext.AssetsLoading[hash].Key
LOG.debug() << "Server started sending type=" << assetTypeName(first.Type)
<< " id=" << first.Id
<< " key=" << first.Domain << ':'
<< first.Key
<< " hash=" << int(hash[0]) << '.'
<< int(hash[1]) << '.'
<< int(hash[2]) << '.'
@@ -1517,43 +1522,47 @@ coro<> ServerSession::rP_AssetsNextSend(Net::AsyncSocket &sock) {
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;
if(!al.Entries.empty()) {
const size_t resSize = al.Data.size();
Resource res(std::move(al.Data));
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;
for(AssetLoadingEntry& entry : al.Entries) {
if(entry.Domain == "test"
&& (entry.Type == EnumAssets::Nodestate
|| entry.Type == EnumAssets::Model
|| entry.Type == EnumAssets::Texture))
{
uint32_t idx = debugResourceLogCount.fetch_add(1);
if(idx < 128) {
LOG.debug() << "Resource loaded type=" << assetTypeName(entry.Type)
<< " id=" << entry.Id
<< " key=" << entry.Domain << ':' << entry.Key
<< " size=" << resSize;
}
}
const EnumAssets type = entry.Type;
const ResourceId id = entry.Id;
const std::string domain = entry.Domain;
const std::string key = entry.Key;
AsyncContext.LoadedAssets.lock()->emplace_back(AssetEntry{
.Type = type,
.Id = id,
.Domain = std::move(entry.Domain),
.Key = std::move(entry.Key),
.Res = res,
.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));

View File

@@ -79,12 +79,17 @@ private:
std::unordered_map<std::string, std::pair<AssetEntry, uint64_t>> NotInUse[(int) EnumAssets::MAX_ENUM];
} MyAssets;
struct AssetLoading {
struct AssetLoadingEntry {
EnumAssets Type;
ResourceId Id;
std::string Domain, Key;
std::string Domain;
std::string Key;
};
struct AssetLoading {
std::vector<AssetLoadingEntry> Entries;
std::u8string Data;
size_t Offset;
size_t Offset = 0;
};
struct AssetBindEntry {

View File

@@ -140,6 +140,14 @@ void ChunkMeshGenerator::run(uint8_t id) {
}
};
std::vector<NodeStateInfo> metaStatesInfo;
{
NodeStateInfo info;
info.Name = "meta";
info.Variations = 256;
metaStatesInfo.push_back(std::move(info));
}
// Воксели пока не рендерим
if(auto iterWorld = SS->Content.Worlds.find(wId); iterWorld != SS->Content.Worlds.end()) {
Pos::GlobalRegion rPos = pos >> 2;
@@ -188,6 +196,38 @@ void ChunkMeshGenerator::run(uint8_t id) {
return (nodeFullCuboidCache[node.Data] = true);
}
if(NSP && profile->NodestateId != 0 && NSP->hasNodestate(profile->NodestateId)) {
std::unordered_map<std::string, int32_t> states;
int32_t meta = node.Meta;
states.emplace("meta", meta);
const auto routes = NSP->getModelsForNode(profile->NodestateId, metaStatesInfo, states);
bool isFull = !routes.empty();
if(isFull) {
for(const auto& variants : routes) {
for(const auto& [weight, faces] : variants) {
(void)weight;
auto hasFace = [&](EnumFace face) -> bool {
auto iterFace = faces.find(face);
return iterFace != faces.end() && !iterFace->second.empty();
};
if(!hasFace(EnumFace::Up)
|| !hasFace(EnumFace::Down)
|| !hasFace(EnumFace::East)
|| !hasFace(EnumFace::West)
|| !hasFace(EnumFace::South)
|| !hasFace(EnumFace::North))
{
isFull = false;
break;
}
}
if(!isFull)
break;
}
}
return (nodeFullCuboidCache[node.Data] = isFull);
}
return (nodeFullCuboidCache[node.Data] = false);
} else {
return iterCache->second;
@@ -325,14 +365,6 @@ void ChunkMeshGenerator::run(uint8_t id) {
std::unordered_map<uint32_t, ModelCacheEntry> modelCache;
std::unordered_map<AssetsTexture, uint32_t> baseTextureCache;
std::vector<NodeStateInfo> metaStatesInfo;
{
NodeStateInfo info;
info.Name = "meta";
info.Variations = 256;
metaStatesInfo.push_back(std::move(info));
}
auto isFaceCovered = [&](EnumFace face, int covered) -> bool {
switch(face) {
case EnumFace::Up: return covered & (1 << 2);

View File

@@ -5,6 +5,8 @@
#include "Common/Abstract.hpp"
#include <Client/Vulkan/Vulkan.hpp>
#include <algorithm>
#include <array>
#include <atomic>
#include <bitset>
#include <condition_variable>
#include <cstring>
@@ -134,14 +136,6 @@ public:
Models.erase(iterModel);
}
std::unordered_map<std::string, ResourceId> modelKeyToId;
if(modelAssets) {
modelKeyToId.reserve(modelAssets->size());
for(const auto& [id, entry] : *modelAssets) {
modelKeyToId.emplace(entry.Domain + ':' + entry.Key, id);
}
}
for(const auto& [key, resource, deps] : newOrChanged) {
result.push_back(key);
@@ -173,19 +167,39 @@ public:
return pipe;
};
size_t dataSize = 0;
std::array<uint8_t, 4> prefix = {};
try {
std::u8string_view data((const char8_t*) resource.data(), resource.size());
dataSize = data.size();
if(!data.empty()) {
const size_t prefixLen = std::min<size_t>(prefix.size(), data.size());
for(size_t i = 0; i < prefixLen; ++i)
prefix[i] = static_cast<uint8_t>(data[i]);
}
if(data.starts_with((const char8_t*) "bm")) {
type = "InternalBinary";
// Компилированная модель внутреннего формата
LV::PreparedModel pm((std::u8string) data);
HeadlessModel hm;
hm.load(data);
model.TextureMap.clear();
model.TextureMap.reserve(pm.CompiledTextures.size());
for(auto& [tkey, pipe] : pm.CompiledTextures)
model.TextureMap.emplace(tkey, remapPipeline(std::move(pipe)));
model.TextureMap.reserve(hm.Textures.size());
for(const auto& [tkey, id] : hm.Textures) {
TexturePipeline pipe;
if(header && id < header->TexturePipelines.size()) {
pipe.Pipeline = header->TexturePipelines[id];
} else {
LOG.warn() << "Model texture pipeline id out of range: model=" << key
<< " local=" << id
<< " pipelines=" << (header ? header->TexturePipelines.size() : 0);
pipe.BinTextures.push_back(id);
pipe = remapPipeline(std::move(pipe));
}
model.TextureMap.emplace(tkey, std::move(pipe));
}
model.TextureKeys = {};
for(const PreparedModel::Cuboid& cb : pm.Cuboids) {
for(const HeadlessModel::Cuboid& cb : hm.Cuboids) {
glm::vec3 min = glm::min(cb.From, cb.To), max = glm::max(cb.From, cb.To);
for(const auto& [face, params] : cb.Faces) {
@@ -265,13 +279,16 @@ public:
}
}
if(!pm.SubModels.empty() && modelAssets) {
model.Depends.reserve(pm.SubModels.size());
for(const auto& sub : pm.SubModels) {
auto iter = modelKeyToId.find(sub.Domain + ':' + sub.Key);
if(iter == modelKeyToId.end())
if(!hm.SubModels.empty()) {
model.Depends.reserve(hm.SubModels.size());
for(const auto& sub : hm.SubModels) {
if(!header || sub.Id >= header->ModelDeps.size()) {
LOG.warn() << "Model sub-model id out of range: model=" << key
<< " local=" << sub.Id
<< " deps=" << (header ? header->ModelDeps.size() : 0);
continue;
model.Depends.emplace_back(iter->second, Transformations{});
}
model.Depends.emplace_back(header->ModelDeps[sub.Id], Transformations{});
}
}
@@ -297,6 +314,35 @@ public:
continue;
}
{
static std::atomic<uint32_t> debugModelLogCount = 0;
uint32_t idx = debugModelLogCount.fetch_add(1);
if(idx < 128) {
size_t vertexCount = 0;
for(const auto& [_, verts] : model.Vertecies)
vertexCount += verts.size();
size_t texDepsCount = textureDeps ? textureDeps->size() : 0;
LOG.debug() << "Model loaded id=" << key
<< " verts=" << vertexCount
<< " texKeys=" << model.TextureKeys.size()
<< " texDeps=" << texDepsCount;
}
}
if(model.Vertecies.empty()) {
static std::atomic<uint32_t> debugEmptyModelLogCount = 0;
uint32_t idx = debugEmptyModelLogCount.fetch_add(1);
if(idx < 128) {
LOG.warn() << "Model has empty geometry id=" << key
<< " type=" << type
<< " size=" << dataSize
<< " prefix=" << int(prefix[0]) << '.'
<< int(prefix[1]) << '.'
<< int(prefix[2]) << '.'
<< int(prefix[3]);
}
}
Models.insert_or_assign(key, std::move(model));
}
@@ -625,13 +671,24 @@ public:
std::move(pixels)
));
bool animated = false;
if(auto anim = getDefaultAnimation(update.Key, width, height)) {
AnimatedSources[key] = *anim;
animated = true;
} else {
AnimatedSources.erase(key);
}
NeedsUpload = true;
static std::atomic<uint32_t> debugTextureLogCount = 0;
uint32_t idx = debugTextureLogCount.fetch_add(1);
if(idx < 128) {
LOG.debug() << "Texture loaded id=" << key
<< " key=" << update.Domain << ':' << update.Key
<< " size=" << width << 'x' << height
<< " animated=" << (animated ? 1 : 0);
}
}
for(AssetsTexture key : lost) {
@@ -897,7 +954,7 @@ public:
}
if(deps && !deps->empty()) {
auto header = AssetsManager::parseHeader(EnumAssets::Model, *deps);
auto header = AssetsManager::parseHeader(EnumAssets::Nodestate, *deps);
if(header && header->Type == EnumAssets::Nodestate) {
nodestate.LocalToModel.assign(header->ModelDeps.begin(), header->ModelDeps.end());
}
@@ -968,6 +1025,11 @@ public:
auto appendModel = [&](AssetsModel modelId, const std::vector<Transformation>& transforms, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>& out) {
ModelProvider::Model model = MP.getModel(modelId);
if(model.Vertecies.empty()) {
if(MissingModelGeometryLogged.insert(modelId).second) {
LOG.warn() << "Model has no geometry id=" << modelId;
}
}
Transformations trf{transforms};
for(auto& [l, r] : model.Vertecies) {
@@ -1015,13 +1077,28 @@ public:
if(const PreparedNodeState::Model* ptr = std::get_if<PreparedNodeState::Model>(&m)) {
AssetsModel modelId;
if(resolveModelId(ptr->Id, modelId))
if(resolveModelId(ptr->Id, modelId)) {
appendModel(modelId, ptr->Transforms, out);
} else {
uint64_t missKey = (uint64_t(id) << 32) | ptr->Id;
if(MissingLocalModelMapLogged.insert(missKey).second) {
LOG.warn() << "Missing model mapping nodestate=" << id
<< " local=" << ptr->Id
<< " locals=" << nodestate.LocalToModel.size();
}
}
} else if(const PreparedNodeState::VectorModel* ptr = std::get_if<PreparedNodeState::VectorModel>(&m)) {
for(const auto& sub : ptr->Models) {
AssetsModel modelId;
if(!resolveModelId(sub.Id, modelId))
if(!resolveModelId(sub.Id, modelId)) {
uint64_t missKey = (uint64_t(id) << 32) | sub.Id;
if(MissingLocalModelMapLogged.insert(missKey).second) {
LOG.warn() << "Missing model mapping nodestate=" << id
<< " local=" << sub.Id
<< " locals=" << nodestate.LocalToModel.size();
}
continue;
}
std::vector<Transformation> transforms = sub.Transforms;
transforms.insert(transforms.end(), ptr->Transforms.begin(), ptr->Transforms.end());
@@ -1058,6 +1135,8 @@ private:
TextureProvider& TP;
std::unordered_map<AssetsNodestate, PreparedNodeState> Nodestates;
std::unordered_set<AssetsNodestate> MissingNodestateLogged;
std::unordered_set<uint64_t> MissingLocalModelMapLogged;
std::unordered_set<AssetsModel> MissingModelGeometryLogged;
std::unordered_set<uint64_t> EmptyRouteLogged;
};