From b9b977e84b02acc1f12b19cda63f5fc7f320f36b Mon Sep 17 00:00:00 2001 From: bodnjenie14 <126781031+bodnjenie14@users.noreply.github.com> Date: Sun, 16 Jun 2024 23:14:39 +0100 Subject: [PATCH] Add Watermark with Build Tag * visual text drawing of build tag --------- Co-authored-by: project-bo4 <127137346+project-bo4@users.noreply.github.com> --- premake5.lua | 325 +++++++++++++++++++---- source/proxy-dll/component/watermark.cpp | 42 +++ 2 files changed, 311 insertions(+), 56 deletions(-) create mode 100644 source/proxy-dll/component/watermark.cpp diff --git a/premake5.lua b/premake5.lua index 03a68bb..6e2748c 100644 --- a/premake5.lua +++ b/premake5.lua @@ -1,3 +1,41 @@ +gitVersioningCommand = "git describe --tags --dirty --always" +gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD" + +-- Quote the given string input as a C string +function cstrquote(value) + if value == nil then + return "\"\"" + end + result = value:gsub("\\", "\\\\") + result = result:gsub("\"", "\\\"") + result = result:gsub("\n", "\\n") + result = result:gsub("\t", "\\t") + result = result:gsub("\r", "\\r") + result = result:gsub("\a", "\\a") + result = result:gsub("\b", "\\b") + result = "\"" .. result .. "\"" + return result +end + +-- Converts tags in "vX.X.X" format and given revision number Y to an array of numbers {X,X,X,Y}. +-- In the case where the format does not work fall back to padding with zeroes and just ending with the revision number. +-- partscount can be either 3 or 4. +function vertonumarr(value, vernumber, partscount) + vernum = {} + for num in string.gmatch(value or "", "%d+") do + if #vernum < 3 then + table.insert(vernum, tonumber(num)) + end + end + while #vernum < 3 do + table.insert(vernum, 0) + end + if #vernum < partscount then + table.insert(vernum, tonumber(vernumber)) + end + return vernum +end + dependencies = { basePath = "./deps" } @@ -44,92 +82,267 @@ newoption { description = "Enable CI builds of the client." } +newaction { + trigger = "version", + description = "Returns the version string for the current commit of the source code.", + onWorkspace = function(wks) + -- get current version via git + local proc = assert(io.popen(gitVersioningCommand, "r")) + local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "") + proc:close() + local version = gitDescribeOutput + + proc = assert(io.popen(gitCurrentBranchCommand, "r")) + local gitCurrentBranchOutput = assert(proc:read('*a')):gsub("%s+", "") + local gitCurrentBranchSuccess = proc:close() + if gitCurrentBranchSuccess then + -- We got a branch name, check if it is a feature branch + if gitCurrentBranchOutput ~= "develop" and gitCurrentBranchOutput ~= "master" then + version = version .. "-" .. gitCurrentBranchOutput + end + end + + print(version) + os.exit(0) + end +} + +newaction { + trigger = "generate-buildinfo", + description = "Sets up build information file like version.h.", + onWorkspace = function(wks) + -- get old version number from version.hpp if any + local oldVersion = "(none)" + local oldVersionHeader = io.open(wks.location .. "/s/version.h", "r") + if oldVersionHeader ~= nil then + local oldVersionHeaderContent = assert(oldVersionHeader:read('*l')) + while oldVersionHeaderContent do + m = string.match(oldVersionHeaderContent, "#define GIT_DESCRIBE (.+)%s*$") + if m ~= nil then + oldVersion = m + end + + oldVersionHeaderContent = oldVersionHeader:read('*l') + end + end + + -- get current version via git + local proc = assert(io.popen(gitVersioningCommand, "r")) + local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "") + proc:close() + + -- generate version.hpp with a revision number if not equal + gitDescribeOutputQuoted = cstrquote(gitDescribeOutput) + if oldVersion ~= gitDescribeOutputQuoted then + -- get current git hash and write to version.txt (used by the preliminary updater) + -- TODO - remove once proper updater and release versioning exists + local proc = assert(io.popen("git rev-parse HEAD", "r")) + local gitCommitHash = assert(proc:read('*a')):gsub("%s+", "") + proc:close() + + -- get whether this is a clean revision (no uncommitted changes) + proc = assert(io.popen("git status --porcelain", "r")) + local revDirty = (assert(proc:read('*a')) ~= "") + if revDirty then revDirty = 1 else revDirty = 0 end + proc:close() + + -- get current tag name + proc = assert(io.popen("git describe --tags --abbrev=0")) + local tagName = proc:read('*l') + + -- get current branch name + proc = assert(io.popen("git branch --show-current")) + local branchName = proc:read('*l') + + -- branch for ci + if branchName == nil or branchName == '' then + proc = assert(io.popen("git show -s --pretty=%d HEAD")) + local branchInfo = proc:read('*l') + m = string.match(branchInfo, ".+,.+, ([^)]+)") + if m ~= nil then + branchName = m + end + end + + if branchName == nil then + branchName = "develop" + end + + print("Detected branch: " .. branchName) + + -- get revision number via git + local proc = assert(io.popen("git rev-list --count HEAD", "r")) + local revNumber = assert(proc:read('*a')):gsub("%s+", "") + + print ("Update " .. oldVersion .. " -> " .. gitDescribeOutputQuoted) + + -- write to version.txt for preliminary updater + -- NOTE - remove this once we have a proper updater and proper release versioning + local versionFile = assert(io.open(wks.location .. "/version.txt", "w")) + versionFile:write(gitCommitHash) + versionFile:close() + + -- write version header + local versionHeader = assert(io.open(wks.location .. "/src/version.h", "w")) + versionHeader:write("/*\n") + versionHeader:write(" * Automatically generated by premake5.\n") + versionHeader:write(" * Do not touch!\n") + versionHeader:write(" */\n") + versionHeader:write("\n") + versionHeader:write("#define GIT_DESCRIBE " .. gitDescribeOutputQuoted .. "\n") + versionHeader:write("#define GIT_DIRTY " .. revDirty .. "\n") + versionHeader:write("#define GIT_HASH " .. cstrquote(gitCommitHash) .. "\n") + versionHeader:write("#define GIT_TAG " .. cstrquote(tagName) .. "\n") + versionHeader:write("#define GIT_BRANCH " .. cstrquote(branchName) .. "\n") + versionHeader:write("\n") + versionHeader:write("// Version transformed for RC files\n") + versionHeader:write("#define VERSION_PRODUCT_RC " .. table.concat(vertonumarr(tagName, revNumber, 3), ",") .. "\n") + versionHeader:write("#define VERSION_PRODUCT " .. cstrquote(table.concat(vertonumarr(tagName, revNumber, 3), ".")) .. "\n") + versionHeader:write("#define VERSION_FILE_RC " .. table.concat(vertonumarr(tagName, revNumber, 4), ",") .. "\n") + versionHeader:write("#define VERSION_FILE " .. cstrquote(table.concat(vertonumarr(tagName, revNumber, 4), ".")) .. "\n") + versionHeader:write("\n") + versionHeader:write("// Alias definitions\n") + versionHeader:write("#define VERSION GIT_DESCRIBE\n") + versionHeader:write("#define SHORTVERSION VERSION_PRODUCT\n") + versionHeader:close() + local versionHeader = assert(io.open(wks.location .. "/src/version.hpp", "w")) + versionHeader:write("/*\n") + versionHeader:write(" * Automatically generated by premake5.\n") + versionHeader:write(" * Do not touch!\n") + versionHeader:write(" *\n") + versionHeader:write(" * This file exists for reasons of complying with our coding standards.\n") + versionHeader:write(" *\n") + versionHeader:write(" * The Resource Compiler will ignore any content from C++ header files if they're not from STDInclude.hpp.\n") + versionHeader:write(" * That's the reason why we now place all version info in version.h instead.\n") + versionHeader:write(" */\n") + versionHeader:write("\n") + versionHeader:write("#include \".\\version.h\"\n") + versionHeader:close() + end + end +} + +--DEFUALT + +dependencies = { + basePath = "./deps" +} + +function dependencies.load() + dir = path.join(dependencies.basePath, "premake/*.lua") + deps = os.matchfiles(dir) + + for i, dep in pairs(deps) do + dep = dep:gsub(".lua", "") + require(dep) + end +end + +function dependencies.imports() + for i, proj in pairs(dependencies) do + if type(i) == 'number' then + proj.import() + end + end +end + +function dependencies.projects() + for i, proj in pairs(dependencies) do + if type(i) == 'number' then + proj.project() + end + end +end dependencies.load() + workspace "shield-development" - startproject "proxy-dll" - location "./build" - objdir "%{wks.location}/obj" - targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}" + startproject "proxy-dll" + location "./build" + objdir "%{wks.location}/obj" + targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}" - configurations {"Debug", "Release"} + configurations {"Debug", "Release"} - language "C++" - cppdialect "C++20" + language "C++" + cppdialect "C++20" - architecture "x86_64" - platforms "x64" + architecture "x86_64" + platforms "x64" - systemversion "latest" - symbols "On" - staticruntime "On" - editandcontinue "Off" - warnings "Extra" - characterset "ASCII" + systemversion "latest" + symbols "On" + staticruntime "On" + editandcontinue "Off" + warnings "Extra" + characterset "ASCII" - if _OPTIONS["dev-build"] then - defines {"DEV_BUILD"} - end + if _OPTIONS["dev-build"] then + defines {"DEV_BUILD"} + end - if _OPTIONS["ci-build"] then - defines {"CI"} - end + if _OPTIONS["ci-build"] then + defines {"CI"} + end - flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"} + flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"} - filter "platforms:x64" - defines {"_WINDOWS", "WIN32"} - filter {} + filter "platforms:x64" + defines {"_WINDOWS", "WIN32"} + filter {} - filter "configurations:Release" - optimize "Size" - buildoptions {"/GL"} - linkoptions { "/IGNORE:4702", "/LTCG" } - defines {"NDEBUG"} - flags {"FatalCompileWarnings"} - filter {} + filter "configurations:Release" + optimize "Size" + buildoptions {"/GL"} + linkoptions { "/IGNORE:4702", "/LTCG" } + defines {"NDEBUG"} + flags {"FatalCompileWarnings"} + warnings "off" + filter {} - filter "configurations:Debug" - optimize "Debug" - defines {"DEBUG", "_DEBUG"} - filter {} + filter "configurations:Debug" + optimize "Debug" + defines {"DEBUG", "_DEBUG"} + filter {} project "shared-code" - kind "StaticLib" - language "C++" + kind "StaticLib" + language "C++" - files {"./source/shared-code/**.hpp", "./source/shared-code/**.cpp"} + files {"./source/shared-code/**.hpp", "./source/shared-code/**.cpp"} - includedirs {"./source/shared-code", "%{prj.location}/src"} + includedirs {"./source/shared-code", "%{prj.location}/src"} - resincludedirs {"$(ProjectDir)src"} + resincludedirs {"$(ProjectDir)src"} - dependencies.imports() + dependencies.imports() project "proxy-dll" - kind "SharedLib" - language "C++" + kind "SharedLib" + language "C++" - targetname "d3d11" + targetname "d3d11" - pchheader "std_include.hpp" - pchsource "source/proxy-dll/std_include.cpp" + pchheader "std_include.hpp" + pchsource "source/proxy-dll/std_include.cpp" - files {"./source/proxy-dll/**.rc", "./source/proxy-dll/**.hpp", "./source/proxy-dll/**.cpp", "./source/proxy-dll/resources/**.*"} + files {"./source/proxy-dll/**.rc", "./source/proxy-dll/**.hpp", "./source/proxy-dll/**.cpp", "./source/proxy-dll/resources/**.*"} - includedirs {"./source/proxy-dll", "./source/shared-code", "%{prj.location}/src"} + includedirs {"./source/proxy-dll", "./source/shared-code", "%{prj.location}/src"} - resincludedirs {"$(ProjectDir)src"} + resincludedirs {"$(ProjectDir)src"} - links {"shared-code"} + links {"shared-code"} - if _OPTIONS["copy-to"] then - postbuildcommands {"copy /y \"$(TargetPath)\" \"" .. _OPTIONS["copy-to"] .. "\""} - end + if _OPTIONS["copy-to"] then + postbuildcommands {"copy /y \"$(TargetPath)\" \"" .. _OPTIONS["copy-to"] .. "\""} + end - dependencies.imports() + dependencies.imports() group "Dependencies" - dependencies.projects() + dependencies.projects() + +prebuildcommands {"pushd %{_MAIN_SCRIPT_DIR}", "tools\\premake5 generate-buildinfo", "popd"} diff --git a/source/proxy-dll/component/watermark.cpp b/source/proxy-dll/component/watermark.cpp new file mode 100644 index 0000000..93f7f50 --- /dev/null +++ b/source/proxy-dll/component/watermark.cpp @@ -0,0 +1,42 @@ +#include +#include "version.hpp" +#include "scheduler.hpp" +#include "definitions/game.hpp" +#include "loader/component_loader.hpp" + +namespace watermark +{ + namespace + { + void draw_version() + { + auto* font = reinterpret_cast(game::sharedUiInfo->assets.bigFont); + constexpr auto scale = 0.4f; + constexpr const char* text = "Project-BO4: " VERSION; + + if (!font /*|| *game::keyCatchers & 1*/) return; + + auto screenWidth = game::ScrPlace_GetView(0)->realViewportSize[0]; + auto textWidth = game::UI_TextWidth(0, text, 0x7FFFFFFF, font, scale); + + auto x = screenWidth - (textWidth + 14.0f); + auto y = game::UI_TextHeight(font, scale) + 12.0f; + + float color[4] = { 0.666f, 0.666f, 0.666f, 0.666f }; + + game::R_AddCmdDrawText(text, 0x7FFFFFFF, font, x, y, scale, scale, 0.0f, color, + game::ITEM_TEXTSTYLE_NORMAL); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + scheduler::loop(draw_version, scheduler::renderer); + } + }; +} + +REGISTER_COMPONENT(watermark::component)