mirror of
https://github.com/odamex/odamex
synced 2026-08-19 02:23:09 -04:00
354 lines
7.6 KiB
C++
354 lines
7.6 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
// Copyright (C) 2006-2026 by The Odamex Team.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Source code and protocol versioning
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "odamex.h"
|
|
|
|
#ifndef ODAMEX_NO_GITVER
|
|
#include "git_describe.h"
|
|
#endif
|
|
|
|
#include <unordered_map>
|
|
#include <sstream>
|
|
#include <memory>
|
|
#include <sstream>
|
|
|
|
#include "c_dispatch.h"
|
|
#include "cmdlib.h"
|
|
|
|
/**
|
|
* @brief Compare two "packed" versions of Odamex to see if they are expected
|
|
* to be protocol-compatible.
|
|
*
|
|
* @param server Packed version of the server.
|
|
* @param client Packed version of the client.
|
|
* @return 0 if they are compatible, -1 if the server is on the older verison
|
|
* 1 if the client is on the older version.
|
|
*/
|
|
int VersionCompat(const int server, const int client)
|
|
{
|
|
// Early-out if versions are identical.
|
|
if (server == client)
|
|
return 0;
|
|
|
|
int sv_maj, sv_min, sv_pat;
|
|
BREAKVER(server, sv_maj, sv_min, sv_pat);
|
|
int cl_maj, cl_min, cl_pat;
|
|
BREAKVER(client, cl_maj, cl_min, cl_pat);
|
|
|
|
// Major version must be identical, client is allowed to have a newer
|
|
// minor version, patch doesn't matter. We don't need to account for
|
|
// 0.x's version selection because it's all incompatible anyway.
|
|
if (sv_maj == cl_maj && sv_min <= cl_min)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Compare all members of the patch number, even if it's redundant,
|
|
// because eventually the condition above might change.
|
|
else if (sv_maj < cl_maj)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (sv_maj > cl_maj)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (sv_min < cl_min)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (sv_min > cl_min)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (sv_pat < cl_pat)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (sv_pat > cl_pat)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Generate a version mismatch message.
|
|
*
|
|
* @param server Packed version of the server.
|
|
* @param client Packed version of the client.
|
|
* @param email E-mail address of server host.
|
|
* @return String message, or blank string if compatible.
|
|
*/
|
|
std::string VersionMessage(const int server, const int client, const char* email)
|
|
{
|
|
std::string rvo;
|
|
|
|
int cmp = VersionCompat(server, client);
|
|
if (!cmp)
|
|
return rvo;
|
|
|
|
int sv_maj, sv_min, sv_pat;
|
|
BREAKVER(server, sv_maj, sv_min, sv_pat);
|
|
int cl_maj, cl_min, cl_pat;
|
|
BREAKVER(client, cl_maj, cl_min, cl_pat);
|
|
|
|
rvo += fmt::sprintf(
|
|
"Your version of Odamex %d.%d.%d does not match the server version %d.%d.%d.\n",
|
|
cl_maj, cl_min, cl_pat, sv_maj, sv_min, sv_pat);
|
|
|
|
if (cmp > 0)
|
|
{
|
|
rvo += fmt::sprintf(
|
|
"Please visit https://odamex.net/ to obtain Odamex %d.%d.%d or "
|
|
"newer.\nIf you do not see this version available for download, "
|
|
"you are likely attempting to connect to a server running a "
|
|
"development version of Odamex.\n",
|
|
sv_maj, sv_min, sv_pat);
|
|
}
|
|
else
|
|
{
|
|
rvo += fmt::sprintf("Please allow the server admin some time to upgrade.");
|
|
|
|
if (email != NULL)
|
|
{
|
|
rvo += fmt::sprintf(" If the problem persists, you can contact them at %s.\n",
|
|
email);
|
|
}
|
|
else
|
|
{
|
|
rvo += "\n";
|
|
}
|
|
}
|
|
|
|
return rvo;
|
|
}
|
|
|
|
using source_files_t = std::unordered_map<std::string, std::string>;
|
|
|
|
source_files_t& get_source_files()
|
|
{
|
|
static auto source_files = std::make_unique<source_files_t>();
|
|
return *source_files.get();
|
|
}
|
|
|
|
file_version::file_version(const char *uid, const char *id, const char *pp, int l, const char *t, const char *d)
|
|
{
|
|
std::stringstream rs(id), ss;
|
|
std::string p = pp;
|
|
|
|
size_t e = p.find_last_of("/\\");
|
|
std::string file = p.substr(e == std::string::npos ? 0 : e + 1);
|
|
|
|
ss << id << " " << l << " " << t << " " << d << " "
|
|
<< p.substr(e == std::string::npos ? 0 : e + 1);
|
|
|
|
get_source_files()[file] = ss.str();
|
|
}
|
|
|
|
/**
|
|
* @brief Return true we have the bare minimum git information
|
|
*/
|
|
static bool NoGitVersion()
|
|
{
|
|
#if defined(ODAMEX_NO_GITVER) || !defined(GIT_SHORT_HASH) || !defined(GIT_REV_COUNT)
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Return the current commit hash.
|
|
*/
|
|
const char* GitHash()
|
|
{
|
|
#ifdef GIT_HASH
|
|
return GIT_HASH;
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Return the current branch name.
|
|
*/
|
|
const char* GitBranch()
|
|
{
|
|
#ifdef GIT_BRANCH
|
|
return GIT_BRANCH;
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Return the number of commits since the first commit.
|
|
*
|
|
* @detail Two branches that are the same distance from the first commit
|
|
* can have the same number.
|
|
*/
|
|
const char* GitRevCount()
|
|
{
|
|
#ifdef GIT_REV_COUNT
|
|
return GIT_REV_COUNT;
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Return a truncated unambiguous hash.
|
|
*/
|
|
const char* GitShortHash()
|
|
{
|
|
#ifdef GIT_SHORT_HASH
|
|
return GIT_SHORT_HASH;
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Return version details in a format that's good-enough to display
|
|
* in most end-user contexts.
|
|
*/
|
|
const char* NiceVersionDetails()
|
|
{
|
|
static std::string version;
|
|
static bool tried = false;
|
|
|
|
if (tried)
|
|
{
|
|
return version.c_str();
|
|
}
|
|
tried = true;
|
|
|
|
// Debug builds get a special callout.
|
|
#if !defined(ODAMEX_DEBUG)
|
|
const char* debug = "";
|
|
#else
|
|
const char* debug = ", Debug Build";
|
|
#endif
|
|
|
|
static constexpr std::string_view RELEASE_PREFIX = "release";
|
|
|
|
|
|
if (NoGitVersion())
|
|
{
|
|
// Without a git version, the only useful info we know is if
|
|
// this is a debug build.
|
|
if (debug[0] != '\0')
|
|
{
|
|
version = "Debug Build";
|
|
}
|
|
}
|
|
else if (!strncmp(GitBranch(), RELEASE_PREFIX.data(), RELEASE_PREFIX.length()))
|
|
{
|
|
// "Release" branch shows total revisions as a build number
|
|
version = fmt::sprintf("-prerelease.%s%s", GitRevCount(), debug);
|
|
}
|
|
else
|
|
{
|
|
// Other branches are written in.
|
|
version = fmt::sprintf("%s, g%s-%s%s", GitBranch(), GitShortHash(), GitRevCount(),
|
|
debug);
|
|
}
|
|
|
|
return version.c_str();
|
|
}
|
|
|
|
/**
|
|
* @brief Return a "full" version string, starting with the version number
|
|
* and putting appropriate details in parenthesis.
|
|
*/
|
|
const char* NiceVersion()
|
|
{
|
|
static std::string version;
|
|
static bool tried = false;
|
|
static constexpr std::string_view RELEASE_PREFIX = "release";
|
|
|
|
if (tried)
|
|
{
|
|
return version.c_str();
|
|
}
|
|
tried = true;
|
|
|
|
// Get the version details.
|
|
const char* details = NiceVersionDetails();
|
|
if (details[0] == '\0')
|
|
{
|
|
// No version details, no parenthesis.
|
|
version = DOTVERSIONSTR;
|
|
}
|
|
else
|
|
{
|
|
// Release candidates show everything together
|
|
if (!strncmp(GitBranch(), RELEASE_PREFIX.data(), RELEASE_PREFIX.length()))
|
|
{
|
|
version = fmt::sprintf("%s%s", DOTVERSIONSTR, details);
|
|
}
|
|
else
|
|
{
|
|
// Put details in parens.
|
|
version = fmt::sprintf("%s (%s)", DOTVERSIONSTR, details);
|
|
}
|
|
}
|
|
|
|
return version.c_str();
|
|
}
|
|
|
|
BEGIN_COMMAND(version)
|
|
{
|
|
if (argc == 1)
|
|
{
|
|
// distribution
|
|
PrintFmt("Odamex v{} - {}\n", NiceVersion(), COPYRIGHTSTR);
|
|
}
|
|
else
|
|
{
|
|
// specific file version
|
|
const auto it = get_source_files().find(argv[1]);
|
|
if (it == get_source_files().end())
|
|
{
|
|
PrintFmt(PRINT_WARNING, "no such file: {}", argv[1]);
|
|
}
|
|
else
|
|
{
|
|
PrintFmt("{}", it->second);
|
|
}
|
|
}
|
|
}
|
|
END_COMMAND(version)
|
|
|
|
BEGIN_COMMAND(listsourcefiles)
|
|
{
|
|
for (const auto& file : get_source_files())
|
|
{
|
|
PrintFmt(PRINT_HIGH, "{} {}\n", file.first, file.second);
|
|
}
|
|
}
|
|
END_COMMAND(listsourcefiles)
|
|
|
|
VERSION_CONTROL(version_cpp, "$Id$")
|