Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_netserver.c

Nilorea Library gui networking api test.

Nilorea Library gui networking api test

Author
Castagnier Mickael
Version
1.0
Date
02/05/2020
#define WIDTH 640
#define HEIGHT 480
#include "nilorea/n_str.h"
#include "nilorea/n_list.h"
#include "nilorea/n_hash.h"
#include "nilorea/n_user.h"
#include <allegro5/allegro.h>
#include <allegro5/allegro_audio.h>
#include <allegro5/allegro_acodec.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_ttf.h>
void usage(void)
{
fprintf( stderr, " -v version\n"
" -V log level: LOG_INFO, LOG_NOTICE, LOG_ERR, LOG_DEBUG\n"
" -h help\n"
" -a serveur address name/ip to bind (server mode)\n"
" -p port\n"
" -i [v4,v6] ip version (default support both ipv4 and ipv6 )\n" );
} /* usage() */
void process_args( int argc, char **argv, char **bind_addr, char **port, int *ip_version )
{
int getoptret = 0,
log_level = LOG_ERR; /* default log level */
/* Arguments optionnels */
/* -v version
* -V log level
* -h help
* -a serveur address name/ip to bind (server mode, optionnal)
* -p port
* -i v4 ip version (default support both ipv4 and ipv6 )
* -i v6 ip version ( " " " " " " )
*/
if( argc == 1 )
{
fprintf( stderr, "No arguments given, help:\n" );
usage();
exit( 1 );
}
while( ( getoptret = getopt( argc, argv, "hva:p:i:V:" ) ) != EOF)
{
switch( getoptret )
{
case 'i' :
if( !strcmp( "v4", optarg ) )
{
(*ip_version) = NETWORK_IPV4 ;
n_log( LOG_NOTICE, "IPV4 selected" );
}
else if( !strcmp( "v6", optarg ) )
{
(*ip_version) = NETWORK_IPV6 ;
n_log( LOG_NOTICE, "IPV6 selected" );
}
else
{
n_log( LOG_NOTICE, "IPV4/6 selected" );
}
break;
case 'v' :
fprintf( stderr, "Date de compilation : %s a %s.\n", __DATE__, __TIME__ );
exit( 1 );
case 'V' :
if( !strncmp( "LOG_NULL", optarg, 8 ) )
{
}
else
{
if( !strncmp( "LOG_NOTICE", optarg, 10 ) )
{
}
else
{
if( !strncmp( "LOG_INFO", optarg, 8 ) )
{
}
else
{
if( !strncmp( "LOG_ERR", optarg, 7 ) )
{
}
else
{
if( !strncmp( "LOG_DEBUG", optarg, 9 ) )
{
}
else
{
fprintf( stderr, "%s n'est pas un niveau de log valide.\n", optarg );
exit( -1 );
}
}
}
}
}
break;
case 'a' :
(*bind_addr) = strdup( optarg );
break ;
case 'p' :
(*port) = strdup( optarg );
break ;
default :
case '?' :
if( optopt == 'V' )
{
fprintf( stderr, "\n Missing log level\n" );
}
else if( optopt == 'p' )
{
fprintf( stderr, "\n Missing port\n" );
}
else if( optopt != 's' )
{
fprintf( stderr, "\n Unknow missing option %c\n", optopt );
}
usage();
exit( 1 );
break ;
case 'h' :
usage();
exit( 1 );
break ;
}
}
} /* void process_args( ... ) */
int process_clients( NETWORK_POOL *netw_pool, N_USERLIST *userlist )
{
__n_assert( netw_pool, return FALSE );
LIST *netw_to_close = NULL ;
netw_to_close = new_generic_list( -1 );
/* write lock the pool */
read_lock( netw_pool -> rwlock );
HT_FOREACH( node, netw_pool -> pool,
{
N_STR *netw_exchange = NULL ;
int state = -1 ;
int thr_engine_status = -1 ;
netw_get_state( netw, &state, &thr_engine_status );
if( state != NETW_RUN || thr_engine_status != NETW_THR_ENGINE_STARTED )
list_push( netw_to_close, netw, NULL );
/* process all data sent by the client */
while( ( netw_exchange = netw_get_msg( netw ) ) )
{
int type = netw_msg_get_type( netw_exchange ) ;
switch( type )
{
case( NETMSG_POSITION ):
// add/update object with id at position
userlist_add_msg_to_all( userlist, netw_exchange );
break;
case( NETMSG_STRING ):
// directly add text to chat
netw_pool_broadcast( netw_pool, NULL, netw_exchange );
break;
{
// directly send back reply
int id_from = -1, id_to = -1, time = -1, type = -1 ;
netw_get_ping( netw_exchange, &id_from, &id_to, &time, &type );
if( id_to == -1 )
{
netw_send_ping( netw, NETMSG_PING_REPLY, id_to, id_from, time );
}
else
{
N_STR *pingmsg = netmsg_make_ping( NETMSG_PING_REQUEST, id_from, id_to, time );
userlist_add_msg_to( userlist, pingmsg, id_to );
}
}
//netw_get_ping( )
break ;
case( NETMSG_GET_BOX ):
// a world object at position X,Y,Z with associated datas, add/update local world cache
break;
{
N_STR *name = NULL, *passwd = NULL ;
int d_it = 0, ident = 0 ;
netw_get_ident( netw_exchange, &d_it, &ident, &name, &passwd );
n_log( LOG_NOTICE, "Ident %d received", netw -> link . sock );
netw_send_ident( netw, NETMSG_IDENT_REPLY_OK, netw -> link . sock, name, passwd );
n_log( LOG_NOTICE, "Answer sent", netw -> link . sock );
}
break ;
case( NETMSG_QUIT ):
n_log( LOG_INFO, "Client is asking us to quit" );
list_push( netw_to_close, netw, NULL );
break ;
default:
n_log( LOG_ERR, "Unknow message type %d", type );
break ;
}
free_nstr( &netw_exchange );
}
});
unlock( netw_pool -> rwlock );
NETWORK *netw = NULL ;
LIST_NODE *node = netw_to_close -> start ;
while( node && node -> ptr )
{
NETWORK *netw = (NETWORK *)node -> ptr ;
if( netw )
{
n_log( LOG_DEBUG, "Closing %d", netw -> link . sock );
userlist_del_user( userlist, netw -> user_id );
}
else
{
n_log( LOG_DEBUG, "Already closed: duplicated quit message" );
}
node = node -> next ;
};
list_destroy( &netw_to_close );
return TRUE ;
} /* process clients datas */
int main( int argc, char *argv[] )
{
ALLEGRO_DISPLAY *display = NULL ;
int DONE = 0, /* Flag to check if we are always running */
getoptret = 0, /* launch parameter check */
log_level = LOG_ERR, /* default log level */
ALLEGRO_BITMAP *scr_buf = NULL ;
ALLEGRO_TIMER *fps_timer = NULL ;
ALLEGRO_TIMER *logic_timer = NULL ;
ALLEGRO_TIMER *network_heartbeat_timer = NULL ;
NETWORK *server = NULL ;
NETWORK_POOL *netw_pool = NULL ;
N_USERLIST *userlist = NULL ;
char *bind_addr = NULL,
*port = NULL ;
/* processing args and set log_level */
process_args( argc, argv, &bind_addr, &port, &ip_version );
if( !port )
{
n_log( LOG_ERR, "No port given. Exiting." );
exit( -1 );
}
#ifdef __linux__
struct sigaction sa;
sa.sa_handler = netw_sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}
#endif
netw_pool = netw_new_pool( 1024 );
__n_assert( netw_pool, goto exit_server );
userlist = userlist_new( 256 );
__n_assert( userlist, goto exit_server );
n_log( LOG_INFO, "Creating listening network for %s:%s %d", _str( bind_addr ), _str( port ), ip_version );
/* create listening network */
if( netw_make_listening( &server, bind_addr, port, 128, ip_version ) == FALSE )
{
n_log( LOG_ERR, "Fatal error with network initialization" );
goto exit_server ;
}
n_log( LOG_NOTICE, "%s is starting ...", argv[ 0 ] );
/* allegro 5 + addons loading */
if (!al_init())
{
n_abort("Could not init Allegro.\n");
}
if (!al_install_audio())
{
n_abort("Unable to initialize audio addon\n");
}
if (!al_init_acodec_addon())
{
n_abort("Unable to initialize acoded addon\n");
}
if (!al_init_image_addon())
{
n_abort("Unable to initialize image addon\n");
}
if (!al_init_primitives_addon() )
{
n_abort("Unable to initialize primitives addon\n");
}
if( !al_init_font_addon() )
{
n_abort("Unable to initialize font addon\n");
}
if( !al_init_ttf_addon() )
{
n_abort("Unable to initialize ttf_font addon\n");
}
if( !al_install_keyboard() )
{
n_abort("Unable to initialize keyboard handler\n");
}
if( !al_install_mouse())
{
n_abort("Unable to initialize mouse handler\n");
}
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
event_queue = al_create_event_queue();
if(!event_queue)
{
fprintf(stderr, "failed to create event_queue!\n");
al_destroy_display(display);
return -1;
}
fps_timer = al_create_timer( 1.0/30.0 ); /* low fps on the server */
logic_timer = al_create_timer( 1.0/100.0 ); /* 100 hz == higher logic processing */
network_heartbeat_timer = al_create_timer( 1.0/10.0 ); /* frequency of the clients answers */
al_set_new_display_flags( ALLEGRO_OPENGL|ALLEGRO_WINDOWED );
display = al_create_display( WIDTH, HEIGHT );
if( !display )
{
n_abort("Unable to create display\n");
}
al_set_window_title( display, argv[ 0 ] );
al_set_new_bitmap_flags( ALLEGRO_VIDEO_BITMAP );
DONE = 0 ;
enum APP_KEYS
{
};
int key[ 7 ] = {false,false,false,false,false,false,false};
al_register_event_source(event_queue, al_get_display_event_source(display));
al_register_event_source(event_queue, al_get_keyboard_event_source());
al_register_event_source(event_queue, al_get_mouse_event_source());
al_start_timer( fps_timer );
al_start_timer( logic_timer );
al_start_timer( logic_timer );
al_register_event_source(event_queue, al_get_timer_event_source(fps_timer));
al_register_event_source(event_queue, al_get_timer_event_source(logic_timer));
al_register_event_source(event_queue, al_get_timer_event_source(logic_timer));
ALLEGRO_BITMAP *scrbuf = al_create_bitmap( WIDTH, HEIGHT );
ALLEGRO_COLOR white_color = al_map_rgba_f(1, 1, 1, 1);
ALLEGRO_FONT *font = NULL ;
font = al_load_font( "2Dumb.ttf", 18, 0 );
if (! font )
{
n_log( LOG_ERR, "Unable to load 2Dumb.ttf" );
exit( 1 );
}
al_hide_mouse_cursor(display);
int mx = 0, my = 0, mouse_b1 = 0, mouse_b2 = 0 ;
int do_draw = 0, do_logic = 0, do_network = 0 ;
al_clear_keyboard_state( NULL );
al_flush_event_queue( event_queue );
do
{
do
{
ALLEGRO_EVENT ev ;
al_wait_for_event(event_queue, &ev);
if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
{
switch(ev.keyboard.keycode)
{
case ALLEGRO_KEY_UP:
key[KEY_UP] = 1;
break;
case ALLEGRO_KEY_DOWN:
key[KEY_DOWN] = 1;
break;
case ALLEGRO_KEY_LEFT:
key[KEY_LEFT] = 1;
break;
case ALLEGRO_KEY_RIGHT:
key[KEY_RIGHT] = 1;
break;
case ALLEGRO_KEY_ESCAPE:
key[KEY_ESC] = 1 ;
break;
case ALLEGRO_KEY_SPACE:
key[KEY_SPACE] = 1 ;
break;
case ALLEGRO_KEY_LCTRL:
case ALLEGRO_KEY_RCTRL:
key[KEY_CTRL] = 1 ;
default:
break;
}
}
else if(ev.type == ALLEGRO_EVENT_KEY_UP)
{
switch(ev.keyboard.keycode)
{
case ALLEGRO_KEY_UP:
key[KEY_UP] = 0;
break;
case ALLEGRO_KEY_DOWN:
key[KEY_DOWN] = 0;
break;
case ALLEGRO_KEY_LEFT:
key[KEY_LEFT] = 0;
break;
case ALLEGRO_KEY_RIGHT:
break;
case ALLEGRO_KEY_ESCAPE:
key[KEY_ESC] = 0 ;
break;
case ALLEGRO_KEY_SPACE:
key[KEY_SPACE] = 0 ;
break;
case ALLEGRO_KEY_LCTRL:
case ALLEGRO_KEY_RCTRL:
key[KEY_CTRL] = 0 ;
default:
break;
}
}
else if( ev.type == ALLEGRO_EVENT_TIMER )
{
if( al_get_timer_event_source( fps_timer ) == ev.any.source )
{
do_draw = 1 ;
}
else if( al_get_timer_event_source( logic_timer ) == ev.any.source )
{
do_logic = 1;
}
else if( al_get_timer_event_source( network_heartbeat_timer ) == ev.any.source )
{
do_network = 1;
}
}
else if( ev.type == ALLEGRO_EVENT_MOUSE_AXES )
{
mx = ev.mouse.x;
my = ev.mouse.y;
}
else if( ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN )
{
if( ev.mouse.button == 1 )
mouse_b1 = 1 ;
if( ev.mouse.button == 2 )
mouse_b2 = 1 ;
}
else if( ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP )
{
if( ev.mouse.button == 1 )
mouse_b1 = 0 ;
if( ev.mouse.button == 2 )
mouse_b2 = 0 ;
}
else if( ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN || ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT )
{
al_clear_keyboard_state( display );
al_flush_event_queue( event_queue );
}
else
{
/* Processing inputs */
// get_keyboard( chat_line , ev );
}
}
while( !al_is_event_queue_empty( event_queue) );
if( do_logic == 1 )
{
if( key[ KEY_UP ] )
{
}
else
{
}
if( key[ KEY_DOWN ] )
{
}
else
{
}
if( key[ KEY_LEFT ] )
{
}
else
{
}
if( key[ KEY_RIGHT ] )
{
}
else
{
}
/* accept new connections */
NETWORK *netw = NULL ;
int error = 0 ;
if ( ( netw = netw_accept_from_ex( server, 0, 0, -1, &error ) ) )
{
int id = userlist_add_user( userlist, netw );
if( id >= 0 )
{
}
netw_pool_add( netw_pool, netw );
}
process_clients( netw_pool, userlist );
do_logic = 0 ;
}
if( do_network == 1 )
{
/* process queues and send datas back to clients */
do_network == 0 ;
}
if( do_draw == 1 )
{
al_acknowledge_resize( display );
int w = al_get_display_width( display );
int h = al_get_display_height( display );
al_set_target_bitmap( scrbuf );
al_clear_to_color( al_map_rgba( 0, 0, 0, 255 ) );
al_set_target_bitmap( al_get_backbuffer( display ) );
al_clear_to_color( al_map_rgba( 0, 0, 0, 255 ) );
al_draw_bitmap( scrbuf, 0, 0, 0 );
/* mouse pointer */
al_draw_line( mx - 5, my, mx + 5, my, al_map_rgb( 255, 0, 0 ), 1 );
al_draw_line( mx, my + 5, mx, my - 5, al_map_rgb( 255, 0, 0 ), 1 );
/* informations */
N_STR *textout = NULL ;
nstrprintf( textout, "Nb client: %d", netw_pool_nbclients( netw_pool) );
al_draw_text( font, white_color, 5, 5, ALLEGRO_ALIGN_LEFT, _nstr( textout ) );
free_nstr( &textout );
al_flip_display();
do_draw = 0 ;
}
}
while( !key[KEY_ESC] && !DONE );
exit_server:
return 0;
}
void usage(void)
Definition: ex_common.c:20
void process_args(int argc, char **argv)
Definition: ex_common.c:27
int main(void)
Definition: ex_exceptions.c:71
#define WIDTH
ALLEGRO_TIMER * fps_timer
int getoptret
int DONE
int log_level
ALLEGRO_BITMAP * scr_buf
#define HEIGHT
ALLEGRO_TIMER * logic_timer
ALLEGRO_DISPLAY * display
int key[7]
APP_KEYS
@ KEY_SPACE
@ KEY_CTRL
@ KEY_UP
@ KEY_LEFT
@ KEY_RIGHT
@ KEY_DOWN
@ KEY_ESC
int process_clients(NETWORK_POOL *netw_pool, N_USERLIST *userlist)
LIST * active_object
NETWORK * server
Definition: ex_network.c:17
int ip_version
Definition: ex_network.c:20
NETWORK * netw
Network for server mode, accepting incomming.
Definition: ex_network.c:18
#define __n_assert(__ptr, __ret)
macro to assert things
Definition: n_common.h:276
#define _str(__PTR)
define true
Definition: n_common.h:172
#define unlock(__rwlock_mutex)
Macro for releasing read/write lock a rwlock mutex.
Definition: n_common.h:409
void n_abort(char const *format,...)
abort program with a text
Definition: n_common.c:38
#define read_lock(__rwlock_mutex)
Macro for acquiring a read lock on a rwlock mutex.
Definition: n_common.h:377
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
Definition: n_common.h:178
#define HASH_VAL(node, type)
Cast a HASH_NODE element.
Definition: n_hash.h:191
#define HT_FOREACH(__ITEM_, __HASH_,...)
ForEach macro helper.
Definition: n_hash.h:195
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition: n_list.c:244
int list_destroy(LIST **list)
Empty and Free a list container.
Definition: n_list.c:603
LIST * new_generic_list(int max_items)
Initialiaze a generic list container to max_items pointers.
Definition: n_list.c:20
Structure of a generic LIST container.
Definition: n_list.h:45
Structure of a generic list node.
Definition: n_list.h:27
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition: n_log.h:74
#define LOG_DEBUG
debug-level messages
Definition: n_log.h:66
#define LOG_ERR
error conditions
Definition: n_log.h:58
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition: n_log.c:97
#define LOG_NOTICE
normal but significant condition
Definition: n_log.h:62
#define LOG_NULL
no log output
Definition: n_log.h:27
#define LOG_INFO
informational
Definition: n_log.h:64
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition: n_str.h:222
#define nstrprintf(__nstr_var,...)
Macro to quickly allocate and sprintf to N_STR *.
Definition: n_str.h:97
A box including a string and his lenght.
Definition: n_str.h:173
int userlist_del_user(N_USERLIST *ulist, int id)
delete an user from the list
Definition: n_user.c:134
int userlist_add_msg_to(N_USERLIST *ulist, N_STR *msg, int id)
add a N_STR *message to user list (USERLIST_ONE)
Definition: n_user.c:240
N_USERLIST * userlist_new(int max)
create a new N_USERLIST user list with 'max' users
Definition: n_user.c:16
int userlist_add_msg_to_all(N_USERLIST *ulist, N_STR *msg)
add a N_STR *message to user list (USERLIST_ALL)
Definition: n_user.c:252
int userlist_send_waiting_msgs(N_USERLIST *ulist)
send all waiting messages ofr each user of the lsit
Definition: n_user.c:362
int userlist_add_user(N_USERLIST *ulist, NETWORK *netw)
add an user to the list
Definition: n_user.c:101
USER list.
Definition: n_user.h:67
#define NETMSG_IDENT_REQUEST
Network Message is identification request: (int)type , (int)id , (N_STR *)name , (N_STR *)password.
Definition: n_network_msg.h:27
#define NETMSG_PING_REQUEST
Network Message is ping request: (int)type , (int)id_from , (int)id_to.
Definition: n_network_msg.h:37
#define NETMSG_STRING
Network Message is string: (int)type , (int)id , (N_STR *)name , (N_STR *)chan , (N_STR *)text ,...
Definition: n_network_msg.h:33
#define NETMSG_PING_REPLY
Network Message is ping reply: (int)type , (int)id_from , (int)id_to.
Definition: n_network_msg.h:39
int netw_get_ping(N_STR *msg, int *type, int *from, int *to, int *time)
Retrieves a ping travel elapsed time.
#define NETMSG_QUIT
Network asking for exit.
Definition: n_network_msg.h:45
#define NETMSG_POSITION
Network Message position: (int)type , (int)id , (int)X , (int)Y , (int)x_shift , (int)y_shift ,...
Definition: n_network_msg.h:35
int netw_msg_get_type(N_STR *msg)
Get the type of message without killing the first number. Use with netw_get_XXX.
#define NETMSG_GET_BOX
Network Message is box retrieve: int x , int y , int z.
Definition: n_network_msg.h:41
#define NETMSG_IDENT_REPLY_OK
Network Message is identification reply OK : (int)type , (int)id , (N_STR *)name ,...
Definition: n_network_msg.h:29
N_STR * netmsg_make_ping(int type, int id_from, int id_to, int time)
Make a ping message to send to a network.
int netw_get_ident(N_STR *msg, int *type, int *ident, N_STR **name, N_STR **passwd)
Retrieves identification from netwmsg.
int netw_pool_broadcast(NETWORK_POOL *netw_pool, NETWORK *from, N_STR *net_msg)
add net_msg to all network in netork pool
Definition: n_network.c:3030
N_STR * netw_get_msg(NETWORK *netw)
Get a message from aimed NETWORK.
Definition: n_network.c:2067
int netw_pool_nbclients(NETWORK_POOL *netw_pool)
return the number of networks in netw_pool
Definition: n_network.c:3061
#define NETWORK_IPV6
Flag to force IPV6
Definition: n_network.h:31
int netw_set_user_id(NETWORK *netw, int id)
associate an id and a network
Definition: n_network.c:3081
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
Definition: n_network.c:1690
int netw_start_thr_engine(NETWORK *netw)
Start the NETWORK netw Threaded Engine.
Definition: n_network.c:2151
#define NETWORK_IPV4
Flag to force IPV4
Definition: n_network.h:29
NETWORK * netw_accept_from_ex(NETWORK *from, int send_list_limit, int recv_list_limit, int non_blocking, int *retval)
make a normal 'accept' .
Definition: n_network.c:1816
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
Definition: n_network.h:27
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
Definition: n_network.c:1503
int netw_send_quit(NETWORK *netw)
Add a formatted NETMSG_QUIT message to the specified network.
Definition: n_network.c:3215
int netw_send_ping(NETWORK *netw, int type, int id_from, int id_to, int time)
Add a ping reply to the network.
Definition: n_network.c:3099
NETWORK_POOL * netw_new_pool(int nb_min_element)
return a new network pool of nb_min_element
Definition: n_network.c:2876
int netw_send_ident(NETWORK *netw, int type, int id, N_STR *name, N_STR *passwd)
Add a formatted NETWMSG_IDENT message to the specified network.
Definition: n_network.c:3121
int netw_pool_add(NETWORK_POOL *netw_pool, NETWORK *netw)
add a NETWORK *netw to a NETWORK_POOL *pool
Definition: n_network.c:2936
int netw_get_state(NETWORK *netw, int *state, int *thr_engine_status)
Get the state of a network.
Definition: n_network.c:1399
@ NETW_THR_ENGINE_STARTED
Definition: n_network.h:226
@ NETW_RUN
Definition: n_network.h:226
Structure of a NETWORK.
Definition: n_network.h:254
structure of a network pool
Definition: n_network.h:342
Common headers and low-level hugly functions & define.
Hash functions and table.
List structures and definitions.
Network Engine.
Network messages , serialization tools.
N_STR and string function declaration.
USERS handling for tiny game apps.