Files
Volodymyr FialkoandBIAGINI Nathan 15d823dab6 Enable build flags and fix compilation warnings (gcc 14.1.0) (#66)
* Fix rpc build with other examples

CPP_COMPILE is an option, a cache variable, thus regular unset() is not
working for it since it tries to unset the scope variable.

This causing cmake to skip compilation of rpc.
Unset CPP_COMPILE from CACHE to make it work as intended.

* Fix compiler flags with build targets

Currently tests, soak and examples are build without extra flags,
because add_compile_options is specified after defining build targets.

From add_compile_options() docs:
Adds options to the compiler command line for targets in the current
directory and below that are added *after* this command is invoked.

* Pass debug callbacks as struct

Fixes:
warning: ISO C forbids conversion of function pointer to object pointer
type

* Move global variables under NBNET_IMPL

Nb net could be included multiple times, to prevent creation of global
variables in each file move them under NBNET_IMPL.

Also use designated initializer to initiate a driver structure to
remove warnings about partial initialization. Designated initializer
would initialize other members to zero.

* Resolve unused variables warnings.

Cast unused function arguments to (void) to suspend warnings.

* Resolve signed vs unsigned warnings

Remove unnecessary lt 0 checks and sync types to remove signed vs
unsigned warnings.

* Resolve string related warnings

Fixes:
nbnet/tests/string_tests.c:18:9: error: ‘strncpy’
output truncated before terminating nul copying 5 bytes from a string of
the same length
[-Werror=stringop-truncation]
   18 |         strncpy(res + pos, b, len_b);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

nbnet/tests/string_tests.c:24:9: error: ‘strncpy’
specified bound depends on the length of the source argument
[-Werror=stringop-truncation]

* Resolve function defined but not used warnings

Mark GameServer_GetClientCount() as inline.

* Attempt to fix warning in ASSERT_VALUE_IN_RANGE

In case when value in unsigned type and min is 0, this commit just
obfuscates types, maybe there's better solution.

Fixes:
warning: comparison of unsigned expression in ‘>= 0’ is always true
[-Wtype-limits]

* Ensure that read len is less than word size

In release mode(-DCMAKE_BUILD_TYPE=Release) tests returning following
error:
nbnet/tests/../nbnet.h:2272:9: error: ‘memcpy’ forming offset [4, 7] is
out of the bound s [0, 4] of object ‘word’ with type ‘Word’ {aka
‘unsigned int’} [-Werror=array-bounds=]
 2272 |         memcpy(bytes, &word, length); |
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

Add check to verify that length is less than Word size.

* add -Wno-unknown-pragmas flag

* fix examples compilation on windows

* fix examples compilation on windows

* fix examples compilation on windows

* fix unit tests compilation on windows

* fix soak test compilation on windows

* fix unit tests compilation on windows

* fix unit tests compilation on windows

---------

Co-authored-by: BIAGINI Nathan <nath.biagini@gmail.com>
2024-08-10 12:02:42 +02:00

349 lines
9.0 KiB
C

/*
Copyright (C) 2024 BIAGINI Nathan
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include <stdlib.h>
#include <stdbool.h>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#elif !defined(_WIN32) && !defined(_WIN64)
// we are on unix or osx
#include <time.h>
#endif
#include "soak.h"
#include "cargs.h"
static bool running = true;
static SoakOptions soak_options = {0};
static unsigned int created_outgoing_soak_message_count = 0;
static unsigned int created_incoming_soak_message_count = 0;
static unsigned int destroyed_outgoing_soak_message_count = 0;
static unsigned int destroyed_incoming_soak_message_count = 0;
static void Usage(void)
{
#ifdef SOAK_CLIENT
#ifdef WEBRTC_NATIVE
printf("Usage: client --message_count=<value> --channel_count=<value> [--packet_loss=<value>] \
[--packet_duplication=<value>] [--ping=<value>] [--jitter=<value>] [--webrtc]\n");
#else
printf("Usage: client --message_count=<value> --channel_count=<value> [--packet_loss=<value>] \
[--packet_duplication=<value>] [--ping=<value>] [--jitter=<value>]\n");
#endif // WEBRTC_NATIVE
#endif // SOAK_CLIENT
#ifdef SOAK_SERVER
printf("Usage: server --channel_count=<value> [--packet_loss=<value>] \
[--packet_duplication=<value>] [--ping=<value>] [--jitter=<value>]\n");
#endif
}
int Soak_Init(int argc, char *argv[])
{
(void) argc;
(void) argv;
srand(SOAK_SEED);
SoakOptions options = Soak_GetOptions();
Soak_LogInfo("Soak test initialized (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)",
options.packet_loss, options.packet_duplication, options.ping, options.jitter);
#ifdef SOAK_CLIENT
NBN_GameClient_RegisterMessage(SOAK_MESSAGE,
(NBN_MessageBuilder)SoakMessage_CreateIncoming,
(NBN_MessageDestructor)SoakMessage_Destroy,
(NBN_MessageSerializer)SoakMessage_Serialize);
#endif
#ifdef SOAK_SERVER
NBN_GameServer_RegisterMessage(SOAK_MESSAGE,
(NBN_MessageBuilder)SoakMessage_CreateIncoming,
(NBN_MessageDestructor)SoakMessage_Destroy,
(NBN_MessageSerializer)SoakMessage_Serialize);
#endif
/* Packet simulator configuration */
#ifdef SOAK_CLIENT
NBN_GameClient_SetPing(soak_options.ping);
NBN_GameClient_SetJitter(soak_options.jitter);
NBN_GameClient_SetPacketLoss(soak_options.packet_loss);
NBN_GameClient_SetPacketDuplication(soak_options.packet_duplication);
#endif
#ifdef SOAK_SERVER
NBN_GameServer_SetPing(soak_options.ping);
NBN_GameServer_SetJitter(soak_options.jitter);
NBN_GameServer_SetPacketLoss(soak_options.packet_loss);
NBN_GameServer_SetPacketDuplication(soak_options.packet_duplication);
#endif
return 0;
}
void Soak_Deinit(void)
{
Soak_LogInfo("Done.");
Soak_LogInfo("Memory report:\n");
// TODO
}
int Soak_ReadCommandLine(int argc, char *argv[])
{
struct cag_option options[] = {
#ifdef SOAK_CLIENT
{'m', NULL, "message_count", "VALUE", "Number of messages to send"},
#ifdef WEBRTC_NATIVE
{'w', NULL, "webrtc", NULL, "Use the native WebRTC driver instead of the UDP driver"},
#endif // WEBRTC_NATIVE
#endif // SOAK_CLIENT
{'c', NULL, "channel_count", "VALUE", "Number of channels (1-NBN_MAX_CHANNELS)"},
{'l', NULL, "packet_loss", "VALUE", "Packet loss frenquency (0-1)"},
{'d', NULL, "packet_duplication", "VALUE", "Packet duplication frequency (0-1)"},
{'p', NULL, "ping", "VALUE", "Ping in seconds"},
{'j', NULL, "jitter", "VALUE", "Jitter in seconds"}
};
cag_option_context context;
cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), argc, argv);
while (cag_option_fetch(&context))
{
char option = cag_option_get(&context);
#ifdef SOAK_CLIENT
if (option == 'm')
{
const char *val = cag_option_get_value(&context);
if (val)
{
soak_options.message_count = atoi(val);
}
}
else if (option == 'w')
{
soak_options.webrtc = true;
}
#else
if (false) {}
#endif
else if (option == 'c')
{
const char *val = cag_option_get_value(&context);
if (val)
{
soak_options.channel_count = atoi(val);
}
}
else if (option == 'l')
{
soak_options.packet_loss = atof(cag_option_get_value(&context));
}
else if (option == 'd')
{
soak_options.packet_duplication = atof(cag_option_get_value(&context));
}
else if (option == 'p')
{
soak_options.ping = atof(cag_option_get_value(&context));
}
else if (option == 'j')
{
soak_options.jitter = atof(cag_option_get_value(&context));
}
}
if (soak_options.channel_count <= 0)
{
Usage();
return -1;
}
if (soak_options.channel_count > NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS)
{
Soak_LogError("Channel count cannot exceed %d", NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS);
return -1;
}
#ifdef SOAK_CLIENT
if (soak_options.message_count <= 0)
{
Usage();
return -1;
}
#endif
if (soak_options.channel_count > SOAK_MAX_CHANNELS)
{
Soak_LogError("Too many channels (max: %d)", SOAK_MAX_CHANNELS);
return -1;
}
return 0;
}
int Soak_MainLoop(int (*Tick)(void *), void *data)
{
while (running)
{
int ret = Tick(data);
if (ret < 0) // Error
return 1;
if (ret == SOAK_DONE) // All soak messages have been received
return 0;
#ifdef __EMSCRIPTEN__
emscripten_sleep(SOAK_TICK_DT * 1000);
#elif defined(_WIN32) || defined(_WIN64)
Sleep(SOAK_TICK_DT * 1000);
#else
long nanos = SOAK_TICK_DT * 1e9;
struct timespec t = { .tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999 };
nanosleep(&t, &t);
#endif
}
return 0;
}
void Soak_Stop(void)
{
running = false;
Soak_LogInfo("Soak test stopped");
}
SoakOptions Soak_GetOptions(void)
{
return soak_options;
}
void Soak_Debug_PrintAddedToRecvQueue(NBN_Connection *conn, NBN_Message *msg)
{
(void) conn;
(void) msg;
// FIXME
/*if (msg->header.type == NBN_MESSAGE_CHUNK_TYPE)
{
NBN_MessageChunk *chunk = (NBN_MessageChunk *)msg->data;
Soak_LogDebug("Soak message chunk added to recv queue (chunk id: %d, chunk total: %d)",
chunk->id, chunk->total);
}
else
{
SoakMessage *soak_message = (SoakMessage *)msg->data;
Soak_LogDebug("Soak message added to recv queue (conn id: %d, msg id: %d, soak msg id: %d)",
conn->id, msg->header.id, soak_message->id);
}*/
}
unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void)
{
return created_outgoing_soak_message_count;
}
unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void)
{
return destroyed_outgoing_soak_message_count;
}
unsigned int Soak_GetCreatedIncomingSoakMessageCount(void)
{
return created_incoming_soak_message_count;
}
unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void)
{
return destroyed_incoming_soak_message_count;
}
SoakMessage *SoakMessage_CreateIncoming(void)
{
SoakMessage *msg = (SoakMessage *)malloc(sizeof(SoakMessage));
msg->outgoing = false;
created_incoming_soak_message_count++;
return msg;
}
SoakMessage *SoakMessage_CreateOutgoing(void)
{
SoakMessage *msg = (SoakMessage *)malloc(sizeof(SoakMessage));
msg->outgoing = true;
created_outgoing_soak_message_count++;
return msg;
}
void SoakMessage_Destroy(SoakMessage *msg)
{
if (msg->outgoing)
{
destroyed_outgoing_soak_message_count++;
Soak_LogDebug("Destroying outgoing soak message (destroyed count: %d, created count: %d)",
destroyed_outgoing_soak_message_count, created_outgoing_soak_message_count);
}
else
{
destroyed_incoming_soak_message_count++;
}
free(msg);
}
int SoakMessage_Serialize(SoakMessage *msg, NBN_Stream *stream)
{
NBN_SerializeUInt(stream, msg->id, 0, UINT32_MAX);
NBN_SerializeUInt(stream, msg->data_length, 1, SOAK_MESSAGE_MAX_DATA_LENGTH);
NBN_SerializeBytes(stream, msg->data, msg->data_length);
return 0;
}