Merge branch 'master' into jgrpp-beta

# Conflicts:
#	src/lang/spanish.txt
#	src/network/core/tcp_content_type.h
#	src/network/network_content.cpp
#	src/saveload/cheat_sl.cpp
#	src/saveload/saveload.cpp
#	src/saveload/saveload.h
#	src/saveload/station_sl.cpp
#	src/saveload/vehicle_sl.cpp
#	src/settings.cpp
#	src/settings_internal.h
#	src/table/settings.h.preamble
#	src/table/settings/company_settings.ini
#	src/table/settings/currency_settings.ini
#	src/table/settings/gameopt_settings.ini
#	src/table/settings/misc_settings.ini
#	src/table/settings/settings.ini
#	src/table/settings/win32_settings.ini
#	src/table/settings/window_settings.ini
This commit is contained in:
Jonathan G Rennison
2021-10-28 01:07:51 +01:00
33 changed files with 789 additions and 939 deletions

View File

@@ -67,6 +67,12 @@ static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maxim
static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0'
static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = COMPAT_MTU-3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than COMPAT_MTU including header (3 bytes)
static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0'
static const uint NETWORK_CONTENT_FILENAME_LENGTH = 48; ///< The maximum length of a content's filename, in bytes including '\0'.
static const uint NETWORK_CONTENT_NAME_LENGTH = 64; ///< The maximum length of a content's name, in bytes including '\0'.
static const uint NETWORK_CONTENT_VERSION_LENGTH = 16; ///< The maximum length of a content's version, in bytes including '\0'.
static const uint NETWORK_CONTENT_URL_LENGTH = 96; ///< The maximum length of a content's url, in bytes including '\0'.
static const uint NETWORK_CONTENT_DESC_LENGTH = 512; ///< The maximum length of a content's description, in bytes including '\0'.
static const uint NETWORK_CONTENT_TAG_LENGTH = 32; ///< The maximum length of a content's tag, in bytes including '\0'.
static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF

View File

@@ -373,37 +373,6 @@ uint64 Packet::Recv_uint64()
return n;
}
/**
* Reads a string till it finds a '\0' in the stream.
* @param buffer The buffer to put the data into.
* @param size The size of the buffer.
* @param settings The string validation settings.
*/
void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings settings)
{
char *bufp = buffer;
const char *last = buffer + size - 1;
/* Don't allow reading from a closed socket */
if (cs->HasClientQuit()) return;
size_t pos = this->pos;
while (--size > 0 && pos < this->Size() && (*buffer++ = this->buffer[pos++]) != '\0') {}
if (size == 0 || pos == this->Size()) {
*buffer = '\0';
/* If size was sooner to zero then the string in the stream
* skip till the \0, so than packet can be read out correctly for the rest */
while (pos < this->Size() && this->buffer[pos] != '\0') pos++;
pos++;
}
assert(pos <= std::numeric_limits<PacketSize>::max());
this->pos = static_cast<PacketSize>(pos);
StrMakeValidInPlace(bufp, last, settings);
}
/**
* Reads characters (bytes) from the packet until it finds a '\0', or reaches a
* maximum of \c length characters.

View File

@@ -87,7 +87,6 @@ public:
uint16 Recv_uint16();
uint32 Recv_uint32();
uint64 Recv_uint64();
void Recv_string(char *buffer, size_t size, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
std::string Recv_string(size_t length, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
void Recv_string(std::string &buffer, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
void Recv_binary(char *buffer, size_t size);

View File

@@ -20,50 +20,6 @@
#include "../../safeguards.h"
/** Clear everything in the struct */
ContentInfo::ContentInfo()
{
memset(this, 0, sizeof(*this));
}
/** Free everything allocated */
ContentInfo::~ContentInfo()
{
free(this->dependencies);
free(this->tags);
}
/**
* Copy data from other #ContentInfo and take ownership of allocated stuff.
* @param other Source to copy from. #dependencies and #tags will be NULLed.
*/
void ContentInfo::TransferFrom(ContentInfo *other)
{
if (other != this) {
free(this->dependencies);
free(this->tags);
memcpy(this, other, sizeof(ContentInfo));
other->dependencies = nullptr;
other->tags = nullptr;
}
}
/**
* Get the size of the data as send over the network.
* @return the size.
*/
size_t ContentInfo::Size() const
{
size_t len = 0;
for (uint i = 0; i < this->tag_count; i++) len += strlen(this->tags[i]) + 1;
/* The size is never larger than the content info size plus the size of the
* tags and dependencies */
return sizeof(*this) +
sizeof(this->dependency_count) +
sizeof(*this->dependencies) * this->dependency_count;
}
/**
* Is the state either selected or autoselected?
* @return true iff that's the case

View File

@@ -26,6 +26,7 @@ enum ContentType {
CONTENT_TYPE_GAME = 9, ///< The content consists of a game script
CONTENT_TYPE_GAME_LIBRARY = 10, ///< The content consists of a GS library
CONTENT_TYPE_END, ///< Helper to mark the end of the types
INVALID_CONTENT_TYPE = 0xFF, ///< Invalid/uninitialized content
};
/** Enum with all types of TCP content packets. The order MUST not be changed **/
@@ -57,29 +58,21 @@ struct ContentInfo {
INVALID, ///< The content's invalid
};
ContentType type; ///< Type of content
ContentID id; ///< Unique (server side) ID for the content
uint32 filesize; ///< Size of the file
char filename[48]; ///< Filename (for the .tar.gz; only valid on download)
char name[64]; ///< Name of the content
char version[16]; ///< Version of the content
char url[96]; ///< URL related to the content
char description[512]; ///< Description of the content
uint32 unique_id; ///< Unique ID; either GRF ID or shortname
byte md5sum[16]; ///< The MD5 checksum
uint8 dependency_count; ///< Number of dependencies
ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids)
uint8 tag_count; ///< Number of tags
char (*tags)[32]; ///< Malloced array of tags (strings)
State state; ///< Whether the content info is selected (for download)
bool upgrade; ///< This item is an upgrade
ContentType type = INVALID_CONTENT_TYPE; ///< Type of content
ContentID id = INVALID_CONTENT_ID; ///< Unique (server side) ID for the content
uint32 filesize = 0; ///< Size of the file
std::string filename; ///< Filename (for the .tar.gz; only valid on download)
std::string name; ///< Name of the content
std::string version; ///< Version of the content
std::string url; ///< URL related to the content
std::string description; ///< Description of the content
uint32 unique_id = 0; ///< Unique ID; either GRF ID or shortname
byte md5sum[16] = {0}; ///< The MD5 checksum
std::vector<ContentID> dependencies; ///< The dependencies (unique server side ids)
StringList tags; ///< Tags associated with the content
State state = State::UNSELECTED; ///< Whether the content info is selected (for download)
bool upgrade = false; ///< This item is an upgrade
ContentInfo();
~ContentInfo();
void TransferFrom(ContentInfo *other);
size_t Size() const;
bool IsSelected() const;
bool IsValid() const;
const char *GetTextfile(TextfileType type) const;

View File

@@ -56,26 +56,27 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p)
ci->id = (ContentID)p->Recv_uint32();
ci->filesize = p->Recv_uint32();
p->Recv_string(ci->name, lengthof(ci->name));
p->Recv_string(ci->version, lengthof(ci->version));
p->Recv_string(ci->url, lengthof(ci->url));
p->Recv_string(ci->description, lengthof(ci->description), SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
ci->name = p->Recv_string(NETWORK_CONTENT_NAME_LENGTH);
ci->version = p->Recv_string(NETWORK_CONTENT_VERSION_LENGTH);
ci->url = p->Recv_string(NETWORK_CONTENT_URL_LENGTH);
ci->description = p->Recv_string(NETWORK_CONTENT_DESC_LENGTH, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
ci->unique_id = p->Recv_uint32();
for (uint j = 0; j < sizeof(ci->md5sum); j++) {
ci->md5sum[j] = p->Recv_uint8();
}
ci->dependency_count = p->Recv_uint8();
ci->dependencies = MallocT<ContentID>(ci->dependency_count);
for (uint i = 0; i < ci->dependency_count; i++) {
ci->dependencies[i] = (ContentID)p->Recv_uint32();
this->reverse_dependency_map.insert({ ci->dependencies[i], ci->id });
uint dependency_count = p->Recv_uint8();
ci->dependencies.reserve(dependency_count);
for (uint i = 0; i < dependency_count; i++) {
ContentID cid = (ContentID)p->Recv_uint32();
ci->dependencies.push_back(cid);
this->reverse_dependency_map.insert({ cid, ci->id });
}
ci->tag_count = p->Recv_uint8();
ci->tags = MallocT<char[32]>(ci->tag_count);
for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
uint tag_count = p->Recv_uint8();
ci->tags.reserve(tag_count);
for (uint i = 0; i < tag_count; i++) ci->tags.push_back(p->Recv_string(NETWORK_CONTENT_TAG_LENGTH));
if (!ci->IsValid()) {
delete ci;
@@ -146,16 +147,15 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p)
if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
/* Preserve the name if possible */
if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
if (ci->name.empty()) ci->name = ici->name;
if (ici->IsSelected()) ci->state = ici->state;
/*
* As ici might be selected by the content window we cannot delete that.
* However, we want to keep most of the values of ci, except the values
* we (just) already preserved.
* So transfer data and ownership of allocated memory from ci to ici.
*/
ici->TransferFrom(ci);
*ici = *ci;
delete ci;
this->OnReceiveContentInfo(ici);
@@ -509,7 +509,7 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p)
this->curInfo->type = (ContentType)p->Recv_uint8();
this->curInfo->id = (ContentID)p->Recv_uint32();
this->curInfo->filesize = p->Recv_uint32();
p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
this->curInfo->filename = p->Recv_string(NETWORK_CONTENT_FILENAME_LENGTH);
if (!this->BeforeDownload()) {
this->CloseConnection();
@@ -728,7 +728,7 @@ void ClientNetworkContentSocketHandler::OnReceiveData(const char *data, size_t l
}
/* Copy the string, without extension, to the filename. */
strecpy(this->curInfo->filename, tmp, lastof(this->curInfo->filename));
this->curInfo->filename = tmp;
/* Request the next file. */
if (!this->BeforeDownload()) {
@@ -988,10 +988,10 @@ void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
/* Selection is easy; just walk all children and set the
* autoselected state. That way we can see what we automatically
* selected and thus can unselect when a dependency is removed. */
for (uint i = 0; i < ci->dependency_count; i++) {
ContentInfo *c = this->GetContent(ci->dependencies[i]);
for (auto &dependency : ci->dependencies) {
ContentInfo *c = this->GetContent(dependency);
if (c == nullptr) {
this->DownloadContentInfo(ci->dependencies[i]);
this->DownloadContentInfo(dependency);
} else if (c->state == ContentInfo::UNSELECTED) {
c->state = ContentInfo::AUTOSELECTED;
this->CheckDependencyState(c);
@@ -1014,10 +1014,10 @@ void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
this->Unselect(c->id);
}
for (uint i = 0; i < ci->dependency_count; i++) {
const ContentInfo *c = this->GetContent(ci->dependencies[i]);
for (auto &dependency : ci->dependencies) {
const ContentInfo *c = this->GetContent(dependency);
if (c == nullptr) {
DownloadContentInfo(ci->dependencies[i]);
DownloadContentInfo(dependency);
continue;
}
if (c->state != ContentInfo::AUTOSELECTED) continue;

View File

@@ -146,7 +146,7 @@ void BaseNetworkContentDownloadStatusWindow::DrawWidget(const Rect &r, int widge
void BaseNetworkContentDownloadStatusWindow::OnDownloadProgress(const ContentInfo *ci, int bytes)
{
if (ci->id != this->cur_id) {
strecpy(this->name, ci->filename, lastof(this->name));
strecpy(this->name, ci->filename.c_str(), lastof(this->name));
this->cur_id = ci->id;
this->downloaded_files++;
}
@@ -406,7 +406,7 @@ class NetworkContentListWindow : public Window, ContentCallback {
/** Sort content by name. */
static bool NameSorter(const ContentInfo * const &a, const ContentInfo * const &b)
{
return strnatcmp(a->name, b->name, true) < 0; // Sort by name (natural sorting).
return strnatcmp(a->name.c_str(), b->name.c_str(), true) < 0; // Sort by name (natural sorting).
}
/** Sort content by type. */
@@ -441,10 +441,9 @@ class NetworkContentListWindow : public Window, ContentCallback {
static bool CDECL TagNameFilter(const ContentInfo * const *a, ContentListFilterData &filter)
{
filter.string_filter.ResetState();
for (int i = 0; i < (*a)->tag_count; i++) {
filter.string_filter.AddLine((*a)->tags[i]);
}
filter.string_filter.AddLine((*a)->name);
for (auto &tag : (*a)->tags) filter.string_filter.AddLine(tag.c_str());
filter.string_filter.AddLine((*a)->name.c_str());
return filter.string_filter.GetState();
}
@@ -702,17 +701,17 @@ public:
SetDParamStr(0, this->selected->name);
y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_NAME);
if (!StrEmpty(this->selected->version)) {
if (!this->selected->version.empty()) {
SetDParamStr(0, this->selected->version);
y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_VERSION);
}
if (!StrEmpty(this->selected->description)) {
if (!this->selected->description.empty()) {
SetDParamStr(0, this->selected->description);
y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_DESCRIPTION);
}
if (!StrEmpty(this->selected->url)) {
if (!this->selected->url.empty()) {
SetDParamStr(0, this->selected->url);
y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_URL);
}
@@ -724,20 +723,18 @@ public:
SetDParam(0, this->selected->filesize);
y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_FILESIZE);
if (this->selected->dependency_count != 0) {
if (!this->selected->dependencies.empty()) {
/* List dependencies */
char buf[DRAW_STRING_BUFFER] = "";
char *p = buf;
for (uint i = 0; i < this->selected->dependency_count; i++) {
ContentID cid = this->selected->dependencies[i];
for (auto &cid : this->selected->dependencies) {
/* Try to find the dependency */
ConstContentIterator iter = _network_content_client.Begin();
for (; iter != _network_content_client.End(); iter++) {
const ContentInfo *ci = *iter;
if (ci->id != cid) continue;
p += seprintf(p, lastof(buf), p == buf ? "%s" : ", %s", (*iter)->name);
p += seprintf(p, lastof(buf), p == buf ? "%s" : ", %s", (*iter)->name.c_str());
break;
}
}
@@ -745,12 +742,12 @@ public:
y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_DEPENDENCIES);
}
if (this->selected->tag_count != 0) {
if (!this->selected->tags.empty()) {
/* List all tags */
char buf[DRAW_STRING_BUFFER] = "";
char *p = buf;
for (uint i = 0; i < this->selected->tag_count; i++) {
p += seprintf(p, lastof(buf), i == 0 ? "%s" : ", %s", this->selected->tags[i]);
for (auto &tag : this->selected->tags) {
p += seprintf(p, lastof(buf), p == buf ? "%s" : ", %s", tag.c_str());
}
SetDParamStr(0, buf);
y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_TAGS);
@@ -766,7 +763,7 @@ public:
for (const ContentInfo *ci : tree) {
if (ci == this->selected || ci->state != ContentInfo::SELECTED) continue;
p += seprintf(p, lastof(buf), buf == p ? "%s" : ", %s", ci->name);
p += seprintf(p, lastof(buf), buf == p ? "%s" : ", %s", ci->name.c_str());
}
if (p != buf) {
SetDParamStr(0, buf);
@@ -843,7 +840,7 @@ public:
case WID_NCL_OPEN_URL:
if (this->selected != nullptr) {
extern void OpenBrowser(const char *url);
OpenBrowser(this->selected->url);
OpenBrowser(this->selected->url.c_str());
}
break;
@@ -984,7 +981,7 @@ public:
this->SetWidgetDisabledState(WID_NCL_UNSELECT, this->filesize_sum == 0);
this->SetWidgetDisabledState(WID_NCL_SELECT_ALL, !show_select_all);
this->SetWidgetDisabledState(WID_NCL_SELECT_UPDATE, !show_select_upgrade);
this->SetWidgetDisabledState(WID_NCL_OPEN_URL, this->selected == nullptr || StrEmpty(this->selected->url));
this->SetWidgetDisabledState(WID_NCL_OPEN_URL, this->selected == nullptr || this->selected->url.empty());
for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
this->SetWidgetDisabledState(WID_NCL_TEXTFILE + tft, this->selected == nullptr || this->selected->state != ContentInfo::ALREADY_HERE || this->selected->GetTextfile(tft) == nullptr);
}

View File

@@ -928,13 +928,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p)
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
char client_revision[NETWORK_REVISION_LENGTH];
p->Recv_string(client_revision, sizeof(client_revision));
std::string client_revision = p->Recv_string(NETWORK_REVISION_LENGTH);
uint32 newgrf_version = p->Recv_uint32();
/* Check if the client has revision control enabled */
if (!IsNetworkCompatibleVersion(client_revision) || _openttd_newgrf_version != newgrf_version) {
if (!IsNetworkCompatibleVersion(client_revision.c_str()) || _openttd_newgrf_version != newgrf_version) {
/* Different revisions!! */
return this->SendError(NETWORK_ERROR_WRONG_REVISION);
}
@@ -1048,11 +1046,10 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWO
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
char password[NETWORK_PASSWORD_LENGTH];
p->Recv_string(password, sizeof(password));
std::string password = p->Recv_string(NETWORK_PASSWORD_LENGTH);
/* Check settings password. Deny if no password is set */
if (StrEmpty(password)) {
if (password.empty()) {
if (this->settings_authed) DEBUG(net, 0, "[settings-ctrl] client-id %d deauthed", this->client_id);
this->settings_authed = false;
} else if (_settings_client.network.settings_password.empty() ||

View File

@@ -505,15 +505,14 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAdd
if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
for (i = 0; i < num_grfs; i++) {
char name[NETWORK_GRF_NAME_LENGTH];
GRFIdentifier c;
DeserializeGRFIdentifier(p, &c);
p->Recv_string(name, sizeof(name));
std::string name = p->Recv_string(NETWORK_GRF_NAME_LENGTH);
/* An empty name is not possible under normal circumstances
* and causes problems when showing the NewGRF list. */
if (StrEmpty(name)) continue;
if (name.empty()) continue;
/* Try to find the GRFTextWrapper for the name of this GRF ID and MD5sum tuple.
* If it exists and not resolved yet, then name of the fake GRF is