Changeset - r25817:3f5c3838fe0a
[Not reviewed]
master
0 10 0
Patric Stout - 3 years ago 2021-07-11 19:57:05
truebrain@openttd.org
Add: allow setting your server visibility to "invite-only" (#9434)

In this mode you do register to the Game Coordinator, but your
server will not show up in the public server listing. You can give
your friends the invite code of the server with which they can
join.
10 files changed with 86 insertions and 31 deletions:
0 comments (0 inline, 0 general)
docs/multiplayer.md
Show inline comments
 
@@ -45,12 +45,16 @@ Last updated:    2011-02-16
 
    - click on 'join game'
 
 - If you want to play and you have the ip or hostname of the game server you
 
   want connect to.
 
    - click add server
 
    - type in the ip address or hostname
 
    - if you want to add a port use :<port>
 
 - If you want to play and you have the invite code of the game server you
 
   want connect to.
 
    - click add server
 
    - type in the invite code
 
 - Now you can select a company and press: "Join company", to help that company
 
    - Or you can press "Spectate game", to spectate the game
 
    - Or you can press "New company", and start your own company (if there are
 
   slots free)
 
 - You see a progressbar how far you are with joining the server.
 
 - Happy playing
 
@@ -107,15 +111,16 @@ Last updated:    2011-02-16
 

	
 
 - You can also do this manually via the console: 'reset_company'.
 

	
 
 - You can let your server automatically restart a map when, let's say, year 2030
 
   is reached. See 'set restart_game_date' for detail.
 

	
 
 - If you want to be on the server-list, enable Advertising. To do this, select
 
   'Internet (advertise)' in the Start Server menu, or type in console:
 
   'set server_advertise 1'.
 
 - If you want to be on the server-list, make your server public. You can do
 
   this either from the Start Server GUI, via the in-game Online Players GUI,
 
   or by typing in the console:
 
   'set server_game_type 1'.
 

	
 
 - You can protect your server with a password via the console: 'set server_pw',
 
   or via the Start Server menu.
 

	
 
 - When you have many clients connected to your server via Internet, watch your
 
   bandwidth (if you have any limit on it, set by your ISP). One client uses
src/console_cmds.cpp
Show inline comments
 
@@ -2417,13 +2417,12 @@ void IConsoleStdLibRegister()
 
	IConsole::AliasRegister("server_password",       "setting server_password %+");
 
	IConsole::AliasRegister("rcon_pw",               "setting rcon_password %+");
 
	IConsole::AliasRegister("rcon_password",         "setting rcon_password %+");
 
	IConsole::AliasRegister("name",                  "setting client_name %+");
 
	IConsole::AliasRegister("server_name",           "setting server_name %+");
 
	IConsole::AliasRegister("server_port",           "setting server_port %+");
 
	IConsole::AliasRegister("server_advertise",      "setting server_advertise %+");
 
	IConsole::AliasRegister("max_clients",           "setting max_clients %+");
 
	IConsole::AliasRegister("max_companies",         "setting max_companies %+");
 
	IConsole::AliasRegister("max_spectators",        "setting max_spectators %+");
 
	IConsole::AliasRegister("max_join_time",         "setting max_join_time %+");
 
	IConsole::AliasRegister("pause_on_join",         "setting pause_on_join %+");
 
	IConsole::AliasRegister("autoclean_companies",   "setting autoclean_companies %+");
src/lang/english.txt
Show inline comments
 
@@ -1992,14 +1992,17 @@ STR_FACE_JACKET_TOOLTIP                 
 
STR_FACE_COLLAR                                                 :Collar:
 
STR_FACE_COLLAR_TOOLTIP                                         :{BLACK}Change collar
 
STR_FACE_TIE                                                    :Tie:
 
STR_FACE_EARRING                                                :Earring:
 
STR_FACE_TIE_EARRING_TOOLTIP                                    :{BLACK}Change tie or earring
 

	
 
############ Next lines match ServerGameType
 
STR_NETWORK_SERVER_VISIBILITY_LOCAL                             :Local
 
STR_NETWORK_SERVER_VISIBILITY_PUBLIC                            :Public
 
STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY                       :Invite only
 
############ End of leave-in-this-order
 

	
 
# Network server list
 
STR_NETWORK_SERVER_LIST_CAPTION                                 :{WHITE}Multiplayer
 
STR_NETWORK_SERVER_LIST_PLAYER_NAME                             :{BLACK}Player name:
 
STR_NETWORK_SERVER_LIST_ENTER_NAME_TOOLTIP                      :{BLACK}This is the name other players will identify you by
 

	
src/network/network.cpp
Show inline comments
 
@@ -931,13 +931,13 @@ bool NetworkServerStart()
 

	
 
	_network_clients_connected = 0;
 
	_network_company_passworded = 0;
 

	
 
	NetworkInitGameInfo();
 

	
 
	if (_settings_client.network.server_advertise) {
 
	if (_settings_client.network.server_game_type != SERVER_GAME_TYPE_LOCAL) {
 
		_network_coordinator_client.Register();
 
	}
 

	
 
	/* execute server initialization script */
 
	IConsoleCmdExec("exec scripts/on_server.scr 0");
 
	/* if the server is dedicated ... add some other script */
 
@@ -997,12 +997,35 @@ void NetworkDisconnect(bool blocking, bo
 

	
 
	/* Reinitialize the UDP stack, i.e. close all existing connections. */
 
	NetworkUDPInitialize();
 
}
 

	
 
/**
 
 * The setting server_game_type was updated; possibly we need to take some
 
 * action.
 
 */
 
void NetworkUpdateServerGameType()
 
{
 
	if (!_networking) return;
 

	
 
	switch (_settings_client.network.server_game_type) {
 
		case SERVER_GAME_TYPE_LOCAL:
 
			_network_coordinator_client.CloseConnection();
 
			break;
 

	
 
		case SERVER_GAME_TYPE_INVITE_ONLY:
 
		case SERVER_GAME_TYPE_PUBLIC:
 
			_network_coordinator_client.Register();
 
			break;
 

	
 
		default:
 
			NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Receives something from the network.
 
 * @return true if everything went fine, false when the connection got closed.
 
 */
 
static bool NetworkReceive()
 
{
 
	if (_network_server) {
src/network/network_coordinator.cpp
Show inline comments
 
@@ -91,14 +91,14 @@ bool ClientNetworkCoordinatorSocketHandl
 
			return false;
 

	
 
		case NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED:
 
			SetDParamStr(0, detail);
 
			ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED, STR_JUST_RAW_STRING, WL_ERROR);
 

	
 
			/* To prevent that we constantly try to reconnect, switch to private game. */
 
			_settings_client.network.server_advertise = false;
 
			/* To prevent that we constantly try to reconnect, switch to local game. */
 
			_settings_client.network.server_game_type = SERVER_GAME_TYPE_LOCAL;
 

	
 
			this->CloseConnection();
 
			return false;
 

	
 
		case NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE: {
 
			/* Find the connecter based on the invite code. */
 
@@ -150,15 +150,24 @@ bool ClientNetworkCoordinatorSocketHandl
 
			case CONNECTION_TYPE_DIRECT:   connection_type = "Public"; break;
 

	
 
			case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator.
 
			default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does.
 
		}
 

	
 
		std::string game_type;
 
		switch (_settings_client.network.server_game_type) {
 
			case SERVER_GAME_TYPE_INVITE_ONLY: game_type = "Invite only"; break;
 
			case SERVER_GAME_TYPE_PUBLIC: game_type = "Public"; break;
 

	
 
			case SERVER_GAME_TYPE_LOCAL: // Impossible to register local servers.
 
			default: game_type = "Unknown"; break; // Should never happen, but don't fail if it does.
 
		}
 

	
 
		Debug(net, 3, "----------------------------------------");
 
		Debug(net, 3, "Your server is now registered with the Game Coordinator:");
 
		Debug(net, 3, "  Game type:       Public");
 
		Debug(net, 3, "  Game type:       {}", game_type);
 
		Debug(net, 3, "  Connection type: {}", connection_type);
 
		Debug(net, 3, "  Invite code:     {}", _network_server_invite_code);
 
		Debug(net, 3, "----------------------------------------");
 
	} else {
 
		Debug(net, 3, "Game Coordinator registered our server with invite code '{}'", _network_server_invite_code);
 
	}
 
@@ -295,13 +304,13 @@ void ClientNetworkCoordinatorSocketHandl
 
	SetWindowDirty(WC_CLIENT_LIST, 0);
 

	
 
	this->Connect();
 

	
 
	Packet *p = new Packet(PACKET_COORDINATOR_SERVER_REGISTER);
 
	p->Send_uint8(NETWORK_COORDINATOR_VERSION);
 
	p->Send_uint8(SERVER_GAME_TYPE_PUBLIC);
 
	p->Send_uint8(_settings_client.network.server_game_type);
 
	p->Send_uint16(_settings_client.network.server_port);
 
	if (_settings_client.network.server_invite_code.empty() || _settings_client.network.server_invite_code_secret.empty()) {
 
		p->Send_string("");
 
		p->Send_string("");
 
	} else {
 
		p->Send_string(_settings_client.network.server_invite_code);
 
@@ -464,13 +473,13 @@ void ClientNetworkCoordinatorSocketHandl
 
 * Check whether we received/can send some data from/to the Game Coordinator server and
 
 * when that's the case handle it appropriately.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::SendReceive()
 
{
 
	/* Private games are not listed via the Game Coordinator. */
 
	if (_network_server && !_settings_client.network.server_advertise) {
 
	if (_network_server && _settings_client.network.server_game_type == SERVER_GAME_TYPE_LOCAL) {
 
		if (this->sock != INVALID_SOCKET) {
 
			this->CloseConnection();
 
		}
 
		return;
 
	}
 

	
src/network/network_func.h
Show inline comments
 
@@ -36,12 +36,13 @@ extern StringList _network_ban_list;
 
byte NetworkSpectatorCount();
 
bool NetworkIsValidClientName(const std::string_view client_name);
 
bool NetworkValidateOurClientName();
 
bool NetworkValidateClientName(std::string &client_name);
 
bool NetworkValidateServerName(std::string &server_name);
 
void NetworkUpdateClientName(const std::string &client_name);
 
void NetworkUpdateServerGameType();
 
bool NetworkCompanyHasClients(CompanyID company);
 
std::string NetworkChangeCompanyPassword(CompanyID company_id, std::string password);
 
void NetworkReboot();
 
void NetworkDisconnect(bool blocking = false, bool close_admins = true);
 
void NetworkGameLoop();
 
void NetworkBackgroundLoop();
src/network/network_gui.cpp
Show inline comments
 
@@ -58,30 +58,31 @@ static void ShowNetworkLobbyWindow(Netwo
 
static const int NETWORK_LIST_REFRESH_DELAY = 30; ///< Time, in seconds, between updates of the network list.
 

	
 
static ClientID _admin_client_id = INVALID_CLIENT_ID; ///< For what client a confirmation window is open.
 
static CompanyID _admin_company_id = INVALID_COMPANY; ///< For what company a confirmation window is open.
 

	
 
/**
 
 * Visibility of the server. Public servers advertise, where private servers
 
 * do not.
 
 */
 
static const StringID _server_visibility_dropdown[] = {
 
	STR_NETWORK_SERVER_VISIBILITY_LOCAL,
 
	STR_NETWORK_SERVER_VISIBILITY_PUBLIC,
 
	INVALID_STRING_ID
 
};
 

	
 
/**
 
 * Update the network new window because a new server is
 
 * found on the network.
 
 */
 
void UpdateNetworkGameWindow()
 
{
 
	InvalidateWindowData(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME, 0);
 
}
 

	
 
static DropDownList BuildVisibilityDropDownList()
 
{
 
	DropDownList list;
 

	
 
	list.emplace_back(new DropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_LOCAL, SERVER_GAME_TYPE_LOCAL, false));
 
	list.emplace_back(new DropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY, SERVER_GAME_TYPE_INVITE_ONLY, false));
 
	list.emplace_back(new DropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_PUBLIC, SERVER_GAME_TYPE_PUBLIC, false));
 

	
 
	return list;
 
}
 

	
 
typedef GUIList<NetworkGameList*, StringFilter&> GUIGameServerList;
 
typedef int ServerListPosition;
 
static const ServerListPosition SLP_INVALID = -1;
 

	
 
/** Full blown container to make it behave exactly as we want :) */
 
class NWidgetServerListHeader : public NWidgetContainer {
 
@@ -1012,13 +1013,13 @@ struct NetworkStartServerWindow : public
 
	}
 

	
 
	void SetStringParameters(int widget) const override
 
	{
 
		switch (widget) {
 
			case WID_NSS_CONNTYPE_BTN:
 
				SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]);
 
				SetDParam(0, STR_NETWORK_SERVER_VISIBILITY_LOCAL + _settings_client.network.server_game_type);
 
				break;
 

	
 
			case WID_NSS_CLIENTS_TXT:
 
				SetDParam(0, _settings_client.network.max_clients);
 
				break;
 

	
 
@@ -1033,13 +1034,13 @@ struct NetworkStartServerWindow : public
 
	}
 

	
 
	void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
 
	{
 
		switch (widget) {
 
			case WID_NSS_CONNTYPE_BTN:
 
				*size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1]));
 
				*size = maxdim(maxdim(GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_LOCAL), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_PUBLIC)), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY));
 
				size->width += padding.width;
 
				size->height += padding.height;
 
				break;
 
		}
 
	}
 

	
 
@@ -1063,13 +1064,13 @@ struct NetworkStartServerWindow : public
 
				this->widget_id = WID_NSS_SETPWD;
 
				SetDParamStr(0, _settings_client.network.server_password);
 
				ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_START_SERVER_SET_PASSWORD, 20, this, CS_ALPHANUMERAL, QSF_NONE);
 
				break;
 

	
 
			case WID_NSS_CONNTYPE_BTN: // Connection type
 
				ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_NSS_CONNTYPE_BTN, 0, 0); // do it for widget WID_NSS_CONNTYPE_BTN
 
				ShowDropDownList(this, BuildVisibilityDropDownList(), _settings_client.network.server_game_type, WID_NSS_CONNTYPE_BTN);
 
				break;
 

	
 
			case WID_NSS_CLIENTS_BTND:    case WID_NSS_CLIENTS_BTNU:    // Click on up/down button for number of clients
 
			case WID_NSS_COMPANIES_BTND:  case WID_NSS_COMPANIES_BTNU:  // Click on up/down button for number of companies
 
			case WID_NSS_SPECTATORS_BTND: case WID_NSS_SPECTATORS_BTNU: // Click on up/down button for number of spectators
 
				/* Don't allow too fast scrolling. */
 
@@ -1141,13 +1142,13 @@ struct NetworkStartServerWindow : public
 
	}
 

	
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		switch (widget) {
 
			case WID_NSS_CONNTYPE_BTN:
 
				_settings_client.network.server_advertise = (index != 0);
 
				_settings_client.network.server_game_type = (ServerGameType)index;
 
				break;
 
			default:
 
				NOT_REACHED();
 
		}
 

	
 
		this->SetDirty();
 
@@ -2038,13 +2039,13 @@ public:
 
	}
 

	
 
	void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
 
	{
 
		switch (widget) {
 
			case WID_CL_SERVER_VISIBILITY:
 
				*size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1]));
 
				*size = maxdim(maxdim(GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_LOCAL), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_PUBLIC)), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY));
 
				size->width += padding.width;
 
				size->height += padding.height;
 
				break;
 

	
 
			case WID_CL_MATRIX: {
 
				uint height = std::max({GetSpriteSize(SPR_COMPANY_ICON).height, GetSpriteSize(SPR_JOIN).height, GetSpriteSize(SPR_ADMIN).height, GetSpriteSize(SPR_CHAT).height});
 
@@ -2070,13 +2071,13 @@ public:
 
		switch (widget) {
 
			case WID_CL_SERVER_NAME:
 
				SetDParamStr(0, _settings_client.network.server_name);
 
				break;
 

	
 
			case WID_CL_SERVER_VISIBILITY:
 
				SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]);
 
				SetDParam(0, STR_NETWORK_SERVER_VISIBILITY_LOCAL + _settings_client.network.server_game_type);
 
				break;
 

	
 
			case WID_CL_SERVER_INVITE_CODE: {
 
				static std::string empty = {};
 
				SetDParamStr(0, _network_server_connection_type == CONNECTION_TYPE_UNKNOWN ? empty : _network_server_invite_code);
 
				break;
 
@@ -2114,13 +2115,13 @@ public:
 
				ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION, NETWORK_CLIENT_NAME_LENGTH, this, CS_ALPHANUMERAL, QSF_LEN_IN_CHARS);
 
				break;
 

	
 
			case WID_CL_SERVER_VISIBILITY:
 
				if (!_network_server) break;
 

	
 
				ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_CL_SERVER_VISIBILITY, 0, 0);
 
				ShowDropDownList(this, BuildVisibilityDropDownList(), _settings_client.network.server_game_type, WID_CL_SERVER_VISIBILITY);
 
				break;
 

	
 
			case WID_CL_MATRIX: {
 
				ButtonCommon *button = this->GetButtonAtPoint(pt);
 
				if (button == nullptr) break;
 

	
 
@@ -2180,13 +2181,14 @@ public:
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		switch (widget) {
 
			case WID_CL_SERVER_VISIBILITY:
 
				if (!_network_server) break;
 

	
 
				_settings_client.network.server_advertise = (index != 0);
 
				_settings_client.network.server_game_type = (ServerGameType)index;
 
				NetworkUpdateServerGameType();
 
				break;
 

	
 
			case WID_CL_MATRIX: {
 
				StringID text = STR_NULL;
 
				QueryCallbackProc *callback = nullptr;
 

	
src/network/network_type.h
Show inline comments
 
@@ -39,12 +39,13 @@ enum NetworkVehicleType {
 
 * Game type the server can be using.
 
 * Used on the network protocol to communicate with Game Coordinator.
 
 */
 
enum ServerGameType : uint8 {
 
	SERVER_GAME_TYPE_LOCAL = 0,
 
	SERVER_GAME_TYPE_PUBLIC,
 
	SERVER_GAME_TYPE_INVITE_ONLY,
 
};
 

	
 
/** 'Unique' identifier to be given to clients */
 
enum ClientID : uint32 {
 
	INVALID_CLIENT_ID = 0, ///< Client is not part of anything
 
	CLIENT_ID_SERVER  = 1, ///< Servers always have this ID
src/settings_type.h
Show inline comments
 
@@ -12,12 +12,13 @@
 

	
 
#include "date_type.h"
 
#include "economy_type.h"
 
#include "town_type.h"
 
#include "transport_type.h"
 
#include "network/core/config.h"
 
#include "network/network_type.h"
 
#include "company_type.h"
 
#include "cargotype.h"
 
#include "linkgraph/linkgraph_type.h"
 
#include "zoom_type.h"
 
#include "openttd.h"
 

	
 
@@ -263,12 +264,13 @@ struct NetworkSettings {
 
	uint16      max_password_time;                        ///< maximum amount of time, in game ticks, a client may take to enter the password
 
	uint16      max_lag_time;                             ///< maximum amount of time, in game ticks, a client may be lagging behind the server
 
	bool        pause_on_join;                            ///< pause the game when people join
 
	uint16      server_port;                              ///< port the server listens on
 
	uint16      server_admin_port;                        ///< port the server listens on for the admin network
 
	bool        server_admin_chat;                        ///< allow private chat for the server to be distributed to the admin network
 
	ServerGameType server_game_type;                      ///< Server type: local / public / invite-only.
 
	std::string server_invite_code;                       ///< Invite code to use when registering as server.
 
	std::string server_invite_code_secret;                ///< Secret to proof we got this invite code from the Game Coordinator.
 
	std::string server_name;                              ///< name of the server
 
	std::string server_password;                          ///< password for joining this server
 
	std::string rcon_password;                            ///< password for rconsole (server side)
 
	std::string admin_password;                           ///< password for the admin network
src/table/settings/network_settings.ini
Show inline comments
 
@@ -6,20 +6,24 @@
 

	
 
; Network settings as stored in the main configuration file ("openttd.cfg").
 

	
 
[pre-amble]
 
static void UpdateClientConfigValues();
 

	
 
static std::initializer_list<const char*> _server_game_type{"local", "public", "invite-only"};
 

	
 
static const SettingVariant _network_settings_table[] = {
 
[post-amble]
 
};
 
[templates]
 
SDTC_BOOL  =  SDTC_BOOL(              $var,        $flags, $def,                              $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to,        $cat, $extra, $startup),
 
SDTC_OMANY = SDTC_OMANY(              $var, $type, $flags, $def,             $max, $full,     $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to,        $cat, $extra, $startup),
 
SDTC_VAR   =   SDTC_VAR(              $var, $type, $flags, $def,       $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to,        $cat, $extra, $startup),
 

	
 
[validation]
 
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
 
SDTC_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
 

	
 
[defaults]
 
flags    = SF_NONE
 
interval = 0
 
str      = STR_NULL
 
@@ -156,16 +160,22 @@ cat      = SC_EXPERT
 
[SDTC_BOOL]
 
var      = network.server_admin_chat
 
flags    = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY
 
def      = true
 
cat      = SC_EXPERT
 

	
 
[SDTC_BOOL]
 
var      = network.server_advertise
 
[SDTC_OMANY]
 
var      = network.server_game_type
 
type     = SLE_UINT8
 
flags    = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY
 
def      = false
 
def      = SERVER_GAME_TYPE_LOCAL
 
min      = SERVER_GAME_TYPE_LOCAL
 
max      = SERVER_GAME_TYPE_INVITE_ONLY
 
full     = _server_game_type
 
post_cb  = [](auto) { NetworkUpdateServerGameType(); }
 
cat      = SC_BASIC
 

	
 
[SDTC_BOOL]
 
var      = network.autoclean_companies
 
flags    = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY
 
def      = false
 

	
0 comments (0 inline, 0 general)