// Copyright Epic Games, Inc. All Rights Reserved. #include #include #include #include #include #include #include #include #include #include #include namespace zen { ////////////////////////////////////////////////////////////////////////// struct FsBeacon::Impl { Impl(std::filesystem::path ShareRoot); ~Impl(); void EnsureValid(); void AddGroup(std::string_view GroupId, CbObject Metadata); void ScanGroup(std::string_view GroupId, std::vector& OutSessions); void ReadMetadata(std::string_view GroupId, const std::vector& InSessions, std::vector& OutMetadata); private: std::filesystem::path m_ShareRoot; zen::Oid m_SessionId; struct GroupData { CbObject Metadata; BasicFile LockFile; }; std::map m_Registration; std::filesystem::path GetSessionMarkerPath(std::string_view GroupId, const Oid& SessionId) { Oid::String_t SessionIdString; SessionId.ToString(SessionIdString); return m_ShareRoot / GroupId / SessionIdString; } }; FsBeacon::Impl::Impl(std::filesystem::path ShareRoot) : m_ShareRoot(ShareRoot), m_SessionId(GetSessionId()) { } FsBeacon::Impl::~Impl() { } void FsBeacon::Impl::EnsureValid() { } void FsBeacon::Impl::AddGroup(std::string_view GroupId, CbObject Metadata) { zen::CreateDirectories(m_ShareRoot / GroupId); std::filesystem::path MarkerFile = GetSessionMarkerPath(GroupId, m_SessionId); GroupData& Group = m_Registration[std::string(GroupId)]; Group.Metadata = Metadata; std::error_code Ec; Group.LockFile.Open(MarkerFile, BasicFile::Mode::kTruncate | BasicFile::Mode::kPreventDelete | BasicFile::Mode::kPreventWrite /* | BasicFile::Mode::kDeleteOnClose */, Ec); if (Ec) { throw std::system_error(Ec, fmt::format("failed to open beacon marker file '{}' for write", MarkerFile)); } Group.LockFile.WriteAll(Metadata.GetBuffer().AsIoBuffer(), Ec); if (Ec) { throw std::system_error(Ec, fmt::format("failed to write to beacon marker file '{}'", MarkerFile)); } Group.LockFile.Flush(); } void FsBeacon::Impl::ScanGroup(std::string_view GroupId, std::vector& OutSessions) { DirectoryContent Dc; zen::GetDirectoryContent(m_ShareRoot / GroupId, zen::DirectoryContentFlags::IncludeFiles, /* out */ Dc); for (const std::filesystem::path& FilePath : Dc.Files) { std::filesystem::path File = FilePath.filename(); std::error_code Ec; if (std::filesystem::remove(FilePath, Ec) == false) { auto FileString = File.generic_string(); if (FileString.length() != Oid::StringLength) continue; if (const Oid SessionId = Oid::FromHexString(FileString)) { if (std::filesystem::file_size(File, Ec) > 0) { OutSessions.push_back(SessionId); } } } } } void FsBeacon::Impl::ReadMetadata(std::string_view GroupId, const std::vector& InSessions, std::vector& OutMetadata) { for (const Oid& SessionId : InSessions) { const std::filesystem::path MarkerFile = GetSessionMarkerPath(GroupId, SessionId); if (CbObject Metadata = LoadCompactBinaryObject(MarkerFile).Object) { OutMetadata.push_back(std::move(Metadata)); } } } ////////////////////////////////////////////////////////////////////////// FsBeacon::FsBeacon(std::filesystem::path ShareRoot) : m_Impl(std::make_unique(ShareRoot)) { } FsBeacon::~FsBeacon() { } void FsBeacon::AddGroup(std::string_view GroupId, CbObject Metadata) { m_Impl->AddGroup(GroupId, Metadata); } void FsBeacon::ScanGroup(std::string_view GroupId, std::vector& OutSessions) { m_Impl->ScanGroup(GroupId, OutSessions); } void FsBeacon::ReadMetadata(std::string_view GroupId, const std::vector& InSessions, std::vector& OutMetadata) { m_Impl->ReadMetadata(GroupId, InSessions, OutMetadata); } ////////////////////////////////////////////////////////////////////////// } // namespace zen