2009-08-21 22:21:05 +02:00
/*
* This file is part of OpenTTD .
* OpenTTD 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 , version 2.
* OpenTTD 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 . You should have received a copy of the GNU General Public License along with OpenTTD . If not , see < http : //www.gnu.org/licenses/>.
*/
2009-01-08 15:38:21 +01:00
/** @file network_command.cpp Command handling over network connections. */
2008-05-06 17:11:33 +02:00
2007-01-02 18:34:03 +01:00
# include "../stdafx.h"
2010-12-30 19:14:37 +01:00
# include "network_admin.h"
2007-01-02 18:34:03 +01:00
# include "network_client.h"
2010-10-15 21:58:56 +02:00
# include "network_server.h"
2007-12-21 22:50:46 +01:00
# include "../command_func.h"
2008-09-30 22:51:04 +02:00
# include "../company_func.h"
2010-08-19 10:59:36 +02:00
# include "../settings_type.h"
2021-10-10 02:35:06 +02:00
# include "../airport_cmd.h"
2021-10-28 23:48:26 +02:00
# include "../aircraft_cmd.h"
# include "../autoreplace_cmd.h"
# include "../company_cmd.h"
2021-10-10 02:35:06 +02:00
# include "../depot_cmd.h"
# include "../dock_cmd.h"
2021-10-28 23:48:26 +02:00
# include "../economy_cmd.h"
# include "../engine_cmd.h"
2023-04-19 22:47:36 +02:00
# include "../error_func.h"
2021-10-28 23:48:26 +02:00
# include "../goal_cmd.h"
2021-10-10 02:35:06 +02:00
# include "../group_cmd.h"
# include "../industry_cmd.h"
2021-10-28 23:48:26 +02:00
# include "../landscape_cmd.h"
2022-11-26 18:03:03 +01:00
# include "../league_cmd.h"
2021-10-28 23:48:26 +02:00
# include "../misc_cmd.h"
# include "../news_cmd.h"
# include "../object_cmd.h"
# include "../order_cmd.h"
2021-10-10 02:35:06 +02:00
# include "../rail_cmd.h"
# include "../road_cmd.h"
2021-10-28 23:48:26 +02:00
# include "../roadveh_cmd.h"
# include "../settings_cmd.h"
# include "../signs_cmd.h"
# include "../station_cmd.h"
# include "../story_cmd.h"
# include "../subsidy_cmd.h"
2021-10-10 02:35:06 +02:00
# include "../terraform_cmd.h"
2021-10-28 23:48:26 +02:00
# include "../timetable_cmd.h"
2021-10-10 02:35:06 +02:00
# include "../town_cmd.h"
# include "../train_cmd.h"
2021-10-28 23:48:26 +02:00
# include "../tree_cmd.h"
2021-10-10 02:35:06 +02:00
# include "../tunnelbridge_cmd.h"
# include "../vehicle_cmd.h"
2021-10-28 23:48:26 +02:00
# include "../viewport_cmd.h"
# include "../water_cmd.h"
# include "../waypoint_cmd.h"
2021-10-10 02:35:06 +02:00
# include "../script/script_cmd.h"
2007-01-02 18:34:03 +01:00
2014-04-23 22:13:33 +02:00
# include "../safeguards.h"
2021-11-28 17:37:04 +01:00
/** Typed list of all possible callbacks. */
static constexpr auto _callback_tuple = std : : make_tuple (
( CommandCallback * ) nullptr , // Make sure this is actually a pointer-to-function.
& CcBuildPrimaryVehicle ,
& CcBuildAirport ,
& CcBuildBridge ,
& CcPlaySound_CONSTRUCTION_WATER ,
& CcBuildDocks ,
& CcFoundTown ,
& CcBuildRoadTunnel ,
& CcBuildRailTunnel ,
& CcBuildWagon ,
& CcRoadDepot ,
& CcRailDepot ,
& CcPlaceSign ,
& CcPlaySound_EXPLOSION ,
& CcPlaySound_CONSTRUCTION_OTHER ,
& CcPlaySound_CONSTRUCTION_RAIL ,
& CcStation ,
& CcTerraform ,
& CcAI ,
& CcCloneVehicle ,
& CcCreateGroup ,
& CcFoundRandomTown ,
& CcRoadStop ,
& CcBuildIndustry ,
& CcStartStopVehicle ,
& CcGame ,
& CcAddVehicleNewGroup
) ;
# ifdef SILENCE_GCC_FUNCTION_POINTER_CAST
/*
* We cast specialized function pointers to a generic one , but don ' t use the
* converted value to call the function , which is safe , except that GCC
* helpfully thinks it is not .
*
* " Any pointer to function can be converted to a pointer to a different function type.
* Calling the function through a pointer to a different function type is undefined ,
* but converting such pointer back to pointer to the original function type yields
* the pointer to the original function . " */
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
/* Helpers to generate the callback table from the callback list. */
inline constexpr size_t _callback_tuple_size = std : : tuple_size_v < decltype ( _callback_tuple ) > ;
template < size_t . . . i >
inline auto MakeCallbackTable ( std : : index_sequence < i . . . > ) noexcept
{
return std : : array < CommandCallback * , sizeof . . . ( i ) > { { reinterpret_cast < CommandCallback * > ( reinterpret_cast < void ( * ) ( ) > ( std : : get < i > ( _callback_tuple ) ) ) . . . } } ; // MingW64 fails linking when casting a pointer to its own type. To work around, cast it to some other type first.
}
/** Type-erased table of callbacks. */
static auto _callback_table = MakeCallbackTable ( std : : make_index_sequence < _callback_tuple_size > { } ) ;
2021-12-10 22:41:36 +01:00
template < typename T > struct CallbackArgsHelper ;
template < typename . . . Targs >
struct CallbackArgsHelper < void ( * const ) ( Commands , const CommandCost & , Targs . . . ) > {
using Args = std : : tuple < std : : decay_t < Targs > . . . > ;
} ;
2010-01-11 21:00:14 +01:00
2021-10-28 23:48:26 +02:00
/* Helpers to generate the command dispatch table from the command traits. */
template < Commands Tcmd > static CommandDataBuffer SanitizeCmdStrings ( const CommandDataBuffer & data ) ;
2024-02-04 17:20:25 +01:00
template < Commands Tcmd , size_t cb > static void UnpackNetworkCommand ( const CommandPacket & cp ) ;
2021-11-02 21:34:39 +01:00
template < Commands Tcmd > static void NetworkReplaceCommandClientId ( CommandPacket & cp , ClientID client_id ) ;
2024-02-04 17:20:25 +01:00
using UnpackNetworkCommandProc = void ( * ) ( const CommandPacket & ) ;
2021-11-28 17:37:04 +01:00
using UnpackDispatchT = std : : array < UnpackNetworkCommandProc , _callback_tuple_size > ;
2021-10-28 23:48:26 +02:00
struct CommandDispatch {
CommandDataBuffer ( * Sanitize ) ( const CommandDataBuffer & ) ;
2021-11-02 21:34:39 +01:00
void ( * ReplaceClientId ) ( CommandPacket & , ClientID ) ;
2021-11-28 17:37:04 +01:00
UnpackDispatchT Unpack ;
2021-10-28 23:48:26 +02:00
} ;
2021-12-10 22:41:36 +01:00
template < Commands Tcmd , size_t Tcb >
constexpr UnpackNetworkCommandProc MakeUnpackNetworkCommandCallback ( ) noexcept
{
/* Check if the callback matches with the command arguments. If not, don't generate an Unpack proc. */
using Tcallback = std : : tuple_element_t < Tcb , decltype ( _callback_tuple ) > ;
2021-12-20 16:59:07 +01:00
if constexpr ( std : : is_same_v < Tcallback , CommandCallback * const > | | // Callback type is CommandCallback.
std : : is_same_v < Tcallback , CommandCallbackData * const > | | // Callback type is CommandCallbackData.
std : : is_same_v < typename CommandTraits < Tcmd > : : CbArgs , typename CallbackArgsHelper < Tcallback > : : Args > | | // Callback proc takes all command return values and parameters.
( ! std : : is_void_v < typename CommandTraits < Tcmd > : : RetTypes > & & std : : is_same_v < typename CallbackArgsHelper < typename CommandTraits < Tcmd > : : RetCallbackProc const > : : Args , typename CallbackArgsHelper < Tcallback > : : Args > ) ) { // Callback return is more than CommandCost and the proc takes all return values.
2021-12-10 22:41:36 +01:00
return & UnpackNetworkCommand < Tcmd , Tcb > ;
} else {
return nullptr ;
}
}
2021-11-28 17:37:04 +01:00
template < Commands Tcmd , size_t . . . i >
constexpr UnpackDispatchT MakeUnpackNetworkCommand ( std : : index_sequence < i . . . > ) noexcept
2021-10-28 23:48:26 +02:00
{
2022-11-02 00:53:44 +01:00
return UnpackDispatchT { { MakeUnpackNetworkCommandCallback < Tcmd , i > ( ) . . . } } ;
2021-10-28 23:48:26 +02:00
}
2021-11-28 17:37:04 +01:00
template < typename T , T . . . i , size_t . . . j >
inline constexpr auto MakeDispatchTable ( std : : integer_sequence < T , i . . . > , std : : index_sequence < j . . . > ) noexcept
{
return std : : array < CommandDispatch , sizeof . . . ( i ) > { { { & SanitizeCmdStrings < static_cast < Commands > ( i ) > , & NetworkReplaceCommandClientId < static_cast < Commands > ( i ) > , MakeUnpackNetworkCommand < static_cast < Commands > ( i ) > ( std : : make_index_sequence < _callback_tuple_size > { } ) } . . . } } ;
}
/** Command dispatch table. */
static constexpr auto _cmd_dispatch = MakeDispatchTable ( std : : make_integer_sequence < std : : underlying_type_t < Commands > , CMD_END > { } , std : : make_index_sequence < _callback_tuple_size > { } ) ;
# ifdef SILENCE_GCC_FUNCTION_POINTER_CAST
# pragma GCC diagnostic pop
# endif
2021-10-28 23:48:26 +02:00
2010-08-19 00:40:17 +02:00
/** Local queue of packets waiting for handling. */
static CommandQueue _local_wait_queue ;
2010-08-18 23:30:38 +02:00
/** Local queue of packets waiting for execution. */
static CommandQueue _local_execution_queue ;
2009-01-08 14:57:50 +01:00
2021-10-28 23:48:26 +02:00
2021-11-28 17:37:04 +01:00
/**
* Find the callback index of a callback pointer .
* @ param callback Address of callback to search for .
* @ return Callback index or std : : numeric_limits < size_t > : : max ( ) if the function wasn ' t found in the callback list .
*/
static size_t FindCallbackIndex ( CommandCallback * callback )
{
if ( auto it = std : : find ( std : : cbegin ( _callback_table ) , std : : cend ( _callback_table ) , callback ) ; it ! = std : : cend ( _callback_table ) ) {
return static_cast < size_t > ( std : : distance ( std : : cbegin ( _callback_table ) , it ) ) ;
}
return std : : numeric_limits < size_t > : : max ( ) ;
}
2021-10-28 23:48:26 +02:00
/**
* Prepare a DoCommand to be send over the network
* @ param cmd The command to execute ( a CMD_ * value )
* @ param err_message Message prefix to show on error
* @ param callback A callback function to call after the command is finished
* @ param company The company that wants to send the command
* @ param cmd_data The command proc arguments .
*/
2023-02-24 22:50:11 +01:00
void NetworkSendCommand ( Commands cmd , StringID err_message , CommandCallback * callback , CompanyID company , const CommandDataBuffer & cmd_data )
2007-01-02 18:34:03 +01:00
{
2007-06-10 21:59:08 +02:00
CommandPacket c ;
2010-01-11 21:32:32 +01:00
c . company = company ;
2009-01-08 15:40:18 +01:00
c . cmd = cmd ;
2021-10-03 21:13:32 +02:00
c . err_msg = err_message ;
2009-01-08 15:40:18 +01:00
c . callback = callback ;
2021-10-28 23:48:26 +02:00
c . data = cmd_data ;
2007-01-02 18:34:03 +01:00
if ( _network_server ) {
2007-06-10 21:59:08 +02:00
/* If we are the server, we queue the command in our 'special' queue.
* In theory , we could execute the command right away , but then the
* client on the server can do everything 1 tick faster than others .
* So to keep the game fair , we delay the command with 1 tick
* which gives about the same speed as most clients .
*/
c . frame = _frame_counter_max + 1 ;
2009-01-08 15:40:18 +01:00
c . my_cmd = true ;
2007-06-10 21:59:08 +02:00
2024-02-04 17:02:08 +01:00
_local_wait_queue . push_back ( c ) ;
2007-01-02 18:34:03 +01:00
return ;
}
2007-06-10 21:59:08 +02:00
c . frame = 0 ; // The client can't tell which frame, so just make it 0
/* Clients send their command to the server and forget all about the packet */
2024-02-04 17:20:25 +01:00
MyClient : : SendCommand ( c ) ;
2007-01-02 18:34:03 +01:00
}
2010-04-13 20:55:31 +02:00
/**
* Sync our local command queue to the command queue of the given
* socket . This is needed for the case where we receive a command
* before saving the game for a joining client , but without the
* execution of those commands . Not syncing those commands means
* that the client will never get them and as such will be in a
* desynced state from the time it started with joining .
* @ param cs The client to sync the queue to .
*/
void NetworkSyncCommandQueue ( NetworkClientSocket * cs )
{
2024-02-04 17:02:08 +01:00
for ( auto & p : _local_execution_queue ) {
CommandPacket & c = cs - > outgoing_queue . emplace_back ( p ) ;
2021-06-16 21:12:08 +02:00
c . callback = nullptr ;
2010-04-13 20:55:31 +02:00
}
}
2009-01-08 14:57:50 +01:00
/**
* Execute all commands on the local command queue that ought to be executed this frame .
*/
void NetworkExecuteLocalCommandQueue ( )
{
2010-07-31 23:43:07 +02:00
assert ( IsLocalCompany ( ) ) ;
2010-06-05 15:32:42 +02:00
2010-10-15 21:58:56 +02:00
CommandQueue & queue = ( _network_server ? _local_execution_queue : ClientNetworkGameSocketHandler : : my_client - > incoming_queue ) ;
2010-08-19 00:40:17 +02:00
2024-02-04 17:02:08 +01:00
auto cp = queue . begin ( ) ;
for ( ; cp ! = queue . end ( ) ; cp + + ) {
2009-01-08 14:57:50 +01:00
/* The queue is always in order, which means
* that the first element will be executed first . */
2010-08-16 01:44:45 +02:00
if ( _frame_counter < cp - > frame ) break ;
2009-01-08 14:57:50 +01:00
2010-08-16 01:44:45 +02:00
if ( _frame_counter > cp - > frame ) {
2009-01-08 14:57:50 +01:00
/* If we reach here, it means for whatever reason, we've already executed
* past the command we need to execute . */
2023-04-19 22:47:36 +02:00
FatalError ( " [net] Trying to execute a packet in the past! " ) ;
2009-01-08 14:57:50 +01:00
}
/* We can execute this command */
2009-01-08 15:55:28 +01:00
_current_company = cp - > company ;
2021-11-28 17:37:04 +01:00
size_t cb_index = FindCallbackIndex ( cp - > callback ) ;
assert ( cb_index < _callback_tuple_size ) ;
2021-12-10 22:41:36 +01:00
assert ( _cmd_dispatch [ cp - > cmd ] . Unpack [ cb_index ] ! = nullptr ) ;
2024-02-04 17:20:25 +01:00
_cmd_dispatch [ cp - > cmd ] . Unpack [ cb_index ] ( * cp ) ;
2009-01-08 14:57:50 +01:00
}
2024-02-04 17:02:08 +01:00
queue . erase ( queue . begin ( ) , cp ) ;
2010-06-05 15:32:42 +02:00
/* Local company may have changed, so we should not restore the old value */
_current_company = _local_company ;
2009-01-08 14:57:50 +01:00
}
/**
2011-02-20 21:55:21 +01:00
* Free the local command queues .
2009-01-08 14:57:50 +01:00
*/
void NetworkFreeLocalCommandQueue ( )
{
2024-02-04 17:02:08 +01:00
_local_wait_queue . clear ( ) ;
_local_execution_queue . clear ( ) ;
2009-01-08 14:57:50 +01:00
}
2010-08-19 00:40:17 +02:00
/**
* " Send " a particular CommandPacket to all clients .
* @ param cp The command that has to be distributed .
* @ param owner The client that owns the command ,
*/
2013-09-14 13:34:09 +02:00
static void DistributeCommandPacket ( CommandPacket & cp , const NetworkClientSocket * owner )
2010-08-19 00:40:17 +02:00
{
CommandCallback * callback = cp . callback ;
cp . frame = _frame_counter_max + 1 ;
2019-12-16 20:56:10 +01:00
for ( NetworkClientSocket * cs : NetworkClientSocket : : Iterate ( ) ) {
2010-10-24 22:07:32 +02:00
if ( cs - > status > = NetworkClientSocket : : STATUS_MAP ) {
2010-08-19 00:40:17 +02:00
/* Callbacks are only send back to the client who sent them in the
* first place . This filters that out . */
2019-04-10 23:07:06 +02:00
cp . callback = ( cs ! = owner ) ? nullptr : callback ;
2010-08-19 00:40:17 +02:00
cp . my_cmd = ( cs = = owner ) ;
2024-02-04 16:23:44 +01:00
cs - > outgoing_queue . push_back ( cp ) ;
2010-08-19 00:40:17 +02:00
}
}
2019-12-16 20:56:10 +01:00
cp . callback = ( nullptr ! = owner ) ? nullptr : callback ;
cp . my_cmd = ( nullptr = = owner ) ;
2024-02-04 17:02:08 +01:00
_local_execution_queue . push_back ( cp ) ;
2010-08-19 00:40:17 +02:00
}
/**
* " Send " a particular CommandQueue to all clients .
* @ param queue The queue of commands that has to be distributed .
* @ param owner The client that owns the commands ,
*/
2024-02-04 17:02:08 +01:00
static void DistributeQueue ( CommandQueue & queue , const NetworkClientSocket * owner )
2010-08-19 00:40:17 +02:00
{
2012-01-17 22:12:19 +01:00
# ifdef DEBUG_DUMP_COMMANDS
/* When replaying we do not want this limitation. */
2012-01-22 22:27:15 +01:00
int to_go = UINT16_MAX ;
2012-01-17 22:12:19 +01:00
# else
2010-08-19 10:59:36 +02:00
int to_go = _settings_client . network . commands_per_frame ;
2023-06-27 13:32:45 +02:00
if ( owner = = nullptr ) {
/* This is the server, use the commands_per_frame_server setting if higher */
to_go = std : : max < int > ( to_go , _settings_client . network . commands_per_frame_server ) ;
}
2012-01-17 22:12:19 +01:00
# endif
2010-08-19 10:59:36 +02:00
2024-02-04 17:02:08 +01:00
/* Not technically the most performant way, but consider clients rarely click more than once per tick. */
for ( auto cp = queue . begin ( ) ; cp ! = queue . end ( ) ; /* removing some items */ ) {
/* Do not distribute commands when paused and the command is not allowed while paused. */
if ( _pause_mode ! = PM_UNPAUSED & & ! IsCommandAllowedWhilePaused ( cp - > cmd ) ) {
+ + cp ;
continue ;
}
2024-02-20 21:51:35 +01:00
/* Limit the number of commands per client per tick. */
if ( - - to_go < 0 ) break ;
2010-08-19 00:40:17 +02:00
DistributeCommandPacket ( * cp , owner ) ;
2024-02-04 17:20:25 +01:00
NetworkAdminCmdLogging ( owner , * cp ) ;
2024-02-04 17:02:08 +01:00
cp = queue . erase ( cp ) ;
2010-08-19 00:40:17 +02:00
}
}
2011-05-02 18:14:23 +02:00
/** Distribute the commands of ourself and the clients. */
2010-08-19 00:40:17 +02:00
void NetworkDistributeCommands ( )
{
/* First send the server's commands. */
2024-02-04 17:02:08 +01:00
DistributeQueue ( _local_wait_queue , nullptr ) ;
2010-08-19 00:40:17 +02:00
/* Then send the queues of the others. */
2019-12-16 20:56:10 +01:00
for ( NetworkClientSocket * cs : NetworkClientSocket : : Iterate ( ) ) {
2024-02-04 17:02:08 +01:00
DistributeQueue ( cs - > incoming_queue , cs ) ;
2010-08-19 00:40:17 +02:00
}
}
2009-01-08 15:40:18 +01:00
/**
* Receives a command from the network .
* @ param p the packet to read from .
* @ param cp the struct to write the data to .
2019-04-10 23:07:06 +02:00
* @ return an error message . When nullptr there has been no error .
2009-01-08 15:40:18 +01:00
*/
2024-02-04 17:20:25 +01:00
const char * NetworkGameSocketHandler : : ReceiveCommand ( Packet & p , CommandPacket & cp )
2009-01-08 15:40:18 +01:00
{
2024-02-04 17:20:25 +01:00
cp . company = ( CompanyID ) p . Recv_uint8 ( ) ;
cp . cmd = static_cast < Commands > ( p . Recv_uint16 ( ) ) ;
if ( ! IsValidCommand ( cp . cmd ) ) return " invalid command " ;
if ( GetCommandFlags ( cp . cmd ) & CMD_OFFLINE ) return " single-player only command " ;
cp . err_msg = p . Recv_uint16 ( ) ;
cp . data = _cmd_dispatch [ cp . cmd ] . Sanitize ( p . Recv_buffer ( ) ) ;
2009-01-08 15:40:18 +01:00
2024-03-16 23:59:32 +01:00
uint8_t callback = p . Recv_uint8 ( ) ;
2024-02-04 17:20:25 +01:00
if ( callback > = _callback_table . size ( ) | | _cmd_dispatch [ cp . cmd ] . Unpack [ callback ] = = nullptr ) return " invalid callback " ;
2009-01-08 15:40:18 +01:00
2024-02-04 17:20:25 +01:00
cp . callback = _callback_table [ callback ] ;
2019-04-10 23:07:06 +02:00
return nullptr ;
2009-01-08 15:40:18 +01:00
}
/**
* Sends a command over the network .
* @ param p the packet to send it in .
* @ param cp the packet to actually send .
*/
2024-02-04 17:20:25 +01:00
void NetworkGameSocketHandler : : SendCommand ( Packet & p , const CommandPacket & cp )
2009-01-08 15:40:18 +01:00
{
2024-02-04 17:20:25 +01:00
p . Send_uint8 ( cp . company ) ;
p . Send_uint16 ( cp . cmd ) ;
p . Send_uint16 ( cp . err_msg ) ;
p . Send_buffer ( cp . data ) ;
size_t callback = FindCallbackIndex ( cp . callback ) ;
if ( callback > UINT8_MAX | | _cmd_dispatch [ cp . cmd ] . Unpack [ callback ] = = nullptr ) {
Debug ( net , 0 , " Unknown callback for command; no callback sent (command: {}) " , cp . cmd ) ;
2019-04-10 23:07:06 +02:00
callback = 0 ; // _callback_table[0] == nullptr
2009-01-08 15:40:18 +01:00
}
2024-02-03 20:28:52 +01:00
p . Send_uint8 ( ( uint8_t ) callback ) ;
2009-01-08 15:40:18 +01:00
}
2021-10-28 23:48:26 +02:00
2021-11-02 21:34:39 +01:00
/** Helper to process a single ClientID argument. */
template < class T >
static inline void SetClientIdHelper ( T & data , [[maybe_unused]] ClientID client_id )
{
if constexpr ( std : : is_same_v < ClientID , T > ) {
data = client_id ;
}
}
/** Set all invalid ClientID's to the proper value. */
template < class Ttuple , size_t . . . Tindices >
static inline void SetClientIds ( Ttuple & values , ClientID client_id , std : : index_sequence < Tindices . . . > )
{
( ( SetClientIdHelper ( std : : get < Tindices > ( values ) , client_id ) ) , . . . ) ;
}
template < Commands Tcmd >
static void NetworkReplaceCommandClientId ( CommandPacket & cp , ClientID client_id )
2021-10-28 23:48:26 +02:00
{
/* Unpack command parameters. */
2021-11-02 21:34:39 +01:00
auto params = EndianBufferReader : : ToValue < typename CommandTraits < Tcmd > : : Args > ( cp . data ) ;
2021-10-28 23:48:26 +02:00
/* Insert client id. */
2021-11-02 21:34:39 +01:00
SetClientIds ( params , client_id , std : : make_index_sequence < std : : tuple_size_v < decltype ( params ) > > { } ) ;
2021-10-28 23:48:26 +02:00
/* Repack command parameters. */
cp . data = EndianBufferWriter < CommandDataBuffer > : : FromValue ( params ) ;
}
2021-11-02 21:34:39 +01:00
/**
* Insert a client ID into the command data in a command packet .
* @ param cp Command packet to modify .
* @ param client_id Client id to insert .
*/
void NetworkReplaceCommandClientId ( CommandPacket & cp , ClientID client_id )
{
_cmd_dispatch [ cp . cmd ] . ReplaceClientId ( cp , client_id ) ;
}
2021-10-28 23:48:26 +02:00
/** Validate a single string argument coming from network. */
template < class T >
static inline void SanitizeSingleStringHelper ( [[maybe_unused]] CommandFlags cmd_flags , T & data )
{
if constexpr ( std : : is_same_v < std : : string , T > ) {
data = StrMakeValid ( data . substr ( 0 , NETWORK_COMPANY_NAME_LENGTH ) , ( ! _network_server & & cmd_flags & CMD_STR_CTRL ) ! = 0 ? SVS_ALLOW_CONTROL_CODE | SVS_REPLACE_WITH_QUESTION_MARK : SVS_REPLACE_WITH_QUESTION_MARK ) ;
}
}
/** Helper function to perform validation on command data strings. */
template < class Ttuple , size_t . . . Tindices >
static inline void SanitizeStringsHelper ( CommandFlags cmd_flags , Ttuple & values , std : : index_sequence < Tindices . . . > )
{
( ( SanitizeSingleStringHelper ( cmd_flags , std : : get < Tindices > ( values ) ) ) , . . . ) ;
}
/**
* Validate and sanitize strings in command data .
* @ tparam Tcmd Command this data belongs to .
* @ param data Command data .
* @ return Sanitized command data .
*/
template < Commands Tcmd >
CommandDataBuffer SanitizeCmdStrings ( const CommandDataBuffer & data )
{
auto args = EndianBufferReader : : ToValue < typename CommandTraits < Tcmd > : : Args > ( data ) ;
SanitizeStringsHelper ( CommandTraits < Tcmd > : : flags , args , std : : make_index_sequence < std : : tuple_size_v < typename CommandTraits < Tcmd > : : Args > > { } ) ;
return EndianBufferWriter < CommandDataBuffer > : : FromValue ( args ) ;
}
2021-11-28 17:37:04 +01:00
/**
* Unpack a generic command packet into its actual typed components .
* @ tparam Tcmd Command type to be unpacked .
* @ tparam Tcb Index into the callback list .
* @ param cp Command packet to unpack .
*/
template < Commands Tcmd , size_t Tcb >
2024-02-04 17:20:25 +01:00
void UnpackNetworkCommand ( const CommandPacket & cp )
2021-10-28 23:48:26 +02:00
{
2024-02-04 17:20:25 +01:00
auto args = EndianBufferReader : : ToValue < typename CommandTraits < Tcmd > : : Args > ( cp . data ) ;
Command < Tcmd > : : PostFromNet ( cp . err_msg , std : : get < Tcb > ( _callback_tuple ) , cp . my_cmd , args ) ;
2021-10-28 23:48:26 +02:00
}