#include #include #include #include #include #include #include #include #include #include #ifdef JULIET_ENABLE_IMGUI #include #endif void InitWorld(NonNullPtr world, NonNullPtr arena) { world->WorldArena = arena.Get(); } void ShutdownWorld(NonNullPtr world) { world->WorldArena = nullptr; } void AddToWorld(NonNullPtr /*world*/, NonNullPtr /*entity*/) { // RegisterEntity(*world->EntityManager, entity.Get()); } void RemoveWorldEntity(World& /*world*/, size_t /*index*/) {} void Serialize(Archive& ar, World& /*world*/, String filename) { Assert(IsValid(filename)); auto& entityManager = GetEntityManager(); if (ar.loading) { // Load ByteBuffer fileBuffer = LoadFile(ar.arena, filename); // TEST SERIALIZATION ParsedArchive archive = tokenize_archive(ar.arena, fileBuffer); // ArchivePropertyNode* property = find_property(&archive, "Position"_crc32); audit_unconsumed_properties(&archive, ConstString("World")); // if (fileBuffer.Size >= sizeof(WorldFileHeader)) // { // ar.base_ptr = fileBuffer.Data; // // WorldFileHeader header; // serialize_elem(ar, header); // Assert(header.Magic == kWorldMagic); // // for (typed_entity_array& type : entityManager.by_type) // { // serialize_elem(ar, type.count); // // if (type.count > 0) // { // // // Unserialize the base entity to get informations // Entity entity; // // serialize(ar, &entity); // } // } // } } else { // Save IOStream* stream = IOFromFile(ar.arena, filename, WrapString("wb")); index_t beginPos = ArenaPos(ar.arena); // Headers auto* header = ArenaPushStruct(ar.arena); header->Magic = kWorldMagic; ar.base_ptr = header; // TODO : Move to a one file per entity model for (typed_entity_array& type : entityManager.by_type) { serialize_elem(ar, type.count); auto* element = type.array; uint8* rawElement = reinterpret_cast(element); size_t stride = element->base->derived_kind->size_of; for (index_t idx = 0; idx < type.count; ++idx) { // Todo : utils // Get base entity from type Entity* entity = reinterpret_cast(rawElement + (idx * stride))->base; serialize(ar, entity); } } // Write index_t endPos = ArenaPos(ar.arena); ByteBuffer writeBuffer = { .Data = reinterpret_cast(header), .Size = endPos - beginPos }; size_t written = IOWrite(stream, writeBuffer); Assert(writeBuffer.Size == written); IOClose(stream); } // // uint32 entityCount = static_cast(world.Entities.Size()); // size_t totalBytes = sizeof(WorldFileHeader) + static_cast(entityCount) * sizeof(WorldEntityDiskRecord); // // ArenaParams tempParams = { .ReserveSize = Megabytes(1), .Name = "WorldSaveArena" }; // Arena* tempArena = ArenaAllocate(tempParams); // if (!tempArena) // { // LogError(LogCategory::Game, "SaveWorld: Failed to allocate memory for saving world."); // return false; // } // // auto deferRelease = Defer([&]() { ArenaRelease(tempArena); }); // // uint8* bufferData = ArenaPushArray(tempArena, totalBytes); // if (!bufferData) // { // LogError(LogCategory::Game, "SaveWorld: Failed to allocate buffer in arena."); // return false; // } // // auto* header = reinterpret_cast(bufferData); // header->Magic = kWorldMagic; // header->Version = kWorldVersion; // header->EntityCount = entityCount; // header->Reserved = 0; // // auto* records = reinterpret_cast(bufferData + sizeof(WorldFileHeader)); // for (size_t i = 0; i < entityCount; ++i) // { // const Entity& ent = world.Entities[i]; // records[i].X = ent.X; // records[i].Y = ent.Y; // records[i].Z = ent.Z; // } // // IOStream* stream = IOFromFile(tempArena, filename, WrapString("wb")); // if (!stream) // { // LogError(LogCategory::Game, "SaveWorld: Failed to open file for writing: %s", CStr(filename)); // return false; // } // // ByteBuffer writeBuffer = { .Data = reinterpret_cast(bufferData), .Size = totalBytes }; // // size_t written = IOWrite(stream, writeBuffer); // IOClose(stream); // // if (written != totalBytes) // { // LogError(LogCategory::Game, "SaveWorld: Failed to write complete world data to %s (wrote %zu / %zu bytes)", // CStr(filename), written, totalBytes); // return false; // } // // LogMessage(LogCategory::Game, "World successfully saved to %s (%u entities)", CStr(filename), entityCount); // return true; } [[nodiscard]] bool LoadWorld(World& /*world*/, String filename) { Assert(IsValid(filename)); // Assert(world.WorldArena != nullptr); // // TempArena loadArena = scratch_begin(nullptr, 0); // // ByteBuffer fileBuffer = LoadFile(loadArena.Arena, filename); // if (fileBuffer.Data && fileBuffer.Size < sizeof(WorldFileHeader)) // { // const WorldFileHeader* header = reinterpret_cast(fileBuffer.Data); // const bool invalidMagicNum = header->Magic != kWorldMagic; // const bool invalidVersion = header->Version != kWorldVersion; // if (invalidMagicNum) // { // LogError(LogCategory::Game, "LoadWorld: Invalid magic in world file: %s (expected 0x%08X, got 0x%08X)", // CStr(filename), kWorldMagic, header->Magic); // } // // if (invalidVersion) // { // LogError(LogCategory::Game, "LoadWorld: Unsupported world file version: %u in %s", header->Version, CStr(filename)); // } // // size_t expectedSize = sizeof(WorldFileHeader) + static_cast(header->EntityCount) * sizeof(WorldEntityDiskRecord); // const bool invalidSize = fileBuffer.Size < expectedSize; // if (invalidSize) // { // LogError(LogCategory::Game, "LoadWorld: Corrupted file %s: size %zu < expected %zu for %u entities", // CStr(filename), fileBuffer.Size, expectedSize, header->EntityCount); // } // // if (!invalidMagicNum && !invalidVersion && !invalidSize) // { // // // world.Entities.Clear(); // // // // const auto* records = reinterpret_cast( // // reinterpret_cast(fileBuffer.Data) + sizeof(WorldFileHeader)); // // // // for (uint32 i = 0; i < header->EntityCount; ++i) // // { // // Entity ent; // // (void)AddWorldEntity(world, records[i].X, records[i].Y, records[i].Z); // // } // // LogMessage(LogCategory::Game, "World successfully loaded from %s (%u entities)", CStr(filename), header->EntityCount); // } // } // else // { // LogError(LogCategory::Game, "LoadWorld: Failed to read world file or file is too small: %s", CStr(filename)); // } // // scratch_end(loadArena); return true; } #ifdef JULIET_EDITOR void RenderWorldEditorUI(World& world) { if (ImGui::Begin("World Editor")) { TempArena temp = scratch_begin(nullptr, 0); static char worldFilePath[256] = "../world.bin"; ImGui::InputText("World File", worldFilePath, sizeof(worldFilePath)); String path = GetAssetPath(temp.Arena, WrapString(worldFilePath)); if (ImGui::Button("Save World")) { Archive data = { .arena = temp.Arena, .loading = false }; Serialize(data, world, path); } ImGui::SameLine(); if (ImGui::Button("Load World")) { Archive data{ .arena = temp.Arena, .loading = true }; Serialize(data, world, path); } scratch_end(temp); // ImGui::SameLine(); // if (ImGui::Button("Clear All")) // { // ClearWorld(world); // } // // ImGui::Separator(); // // if (ImGui::Button("Add Entity")) // { // (void)AddWorldEntity(world, 0.0f, 0.0f, 0.0f); // } // // ImGui::Text("Entity Count: %zu", world.Entities.Size()); // ImGui::Separator(); // // static int selectedEntity = -1; // if (selectedEntity >= static_cast(world.Entities.Size())) // { // selectedEntity = -1; // } // // ImGui::BeginChild("EntityList", ImVec2(180, 200), true); // for (size_t i = 0; i < world.Entities.Size(); ++i) // { // char label[64]; // snprintf(label, sizeof(label), "Entity #%zu (ID: %llu)", i, world.Entities[i].ID); // if (ImGui::Selectable(label, selectedEntity == static_cast(i))) // { // selectedEntity = static_cast(i); // } // } // ImGui::EndChild(); // // ImGui::SameLine(); // // ImGui::BeginChild("EntityInspector", ImVec2(0, 200), true); // if (selectedEntity >= 0 && selectedEntity < static_cast(world.Entities.Size())) // { // Entity& ent = world.Entities[static_cast(selectedEntity)]; // ImGui::Text("Selected: Entity #%d", selectedEntity); // ImGui::Text("ID: %llu", ent.ID); // // float pos[3] = { ent.X, ent.Y, ent.Z }; // if (ImGui::DragFloat3("Position", pos, 0.1f)) // { // ent.X = pos[0]; // ent.Y = pos[1]; // ent.Z = pos[2]; // UpdateWorld(world); // } // // if (ImGui::Button("Delete Entity")) // { // RemoveWorldEntity(world, static_cast(selectedEntity)); // selectedEntity = -1; // UpdateWorld(world); // } // } // else // { // ImGui::Text("Select an entity to edit its properties."); // } // ImGui::EndChild(); } ImGui::End(); } #endif