summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt26
-rw-r--r--src/events.c149
-rw-r--r--src/events.h25
-rw-r--r--src/game.c153
-rw-r--r--src/game.h85
-rw-r--r--src/netlem.c197
-rw-r--r--src/netlem.h8
-rw-r--r--src/netlem.h.in8
-rw-r--r--src/netlem_ds.c501
-rw-r--r--src/netlem_ds.h8
-rw-r--r--src/test/test.c28
-rw-r--r--src/utils.c41
-rw-r--r--src/utils.h15
13 files changed, 1244 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..02bf57c
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required (VERSION 2.6)
+project (NetLemmings)
+
+# The version number.
+set (NetLemmings_VERSION_MAJOR 0)
+set (NetLemmings_VERSION_MINOR 1)
+
+# Check if all necessary libs are here
+find_package(SDL REQUIRED)
+find_package(SDL_net REQUIRED)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file (
+ "${PROJECT_SOURCE_DIR}/netlem.h.in"
+ "${PROJECT_BINARY_DIR}/netlem.h"
+)
+
+# add the binary tree to the search path for include files
+# so that we will find NetLemmingsConfig.h
+include_directories("${PROJECT_BINARY_DIR}")
+
+add_executable(netlem netlem.c)
+add_definitions(-Wall -Wextra -pedantic -Werror -std=c99)
+target_link_libraries(netlem SDL SDL_net)
+
diff --git a/src/events.c b/src/events.c
new file mode 100644
index 0000000..95be9e9
--- /dev/null
+++ b/src/events.c
@@ -0,0 +1,149 @@
+#include <stdlib.h> // calloc
+#include <string.h> //memcpy
+
+#include "events.h"
+
+inline int eventSerializedSize() {
+ //FIXME : A rendre portable
+ return sizeof(event_t);
+}
+
+void eventSerialize(const event_t *e, char *buf) {
+ //FIXME : A rendre portable
+ memcpy(buf,e,sizeof(event_t));
+}
+
+int eventUnserialize(event_t *e, char *buf) {
+ //FIXME : A rendre portable et vérifier les champs
+ memcpy(e,buf,sizeof(event_t));
+ return 0;
+}
+
+void eventListInit(eventList_t *elist) {
+ elist->first=NULL;
+ elist->last=NULL;
+ elist->lock= SDL_CreateMutex();
+}
+
+void eventListFree(eventList_t *elist) {
+ event_t *next;
+ event_t *curr = elist->first;
+ while(curr!=NULL) {
+ next = curr->next;
+ free(curr);
+ curr=next;
+ }
+}
+int eventListLock(eventList_t *elist) {
+ return SDL_mutexP(elist->lock);
+}
+
+int eventListUnlock(eventList_t *elist) {
+ return SDL_mutexV(elist->lock);
+}
+
+void eventListAdd(eventList_t *elist, event_t *event) {
+ if (elist->first==NULL) {
+ elist->first=elist->last=event;
+ event->next=event->prev=NULL;
+ } else {
+ elist->last->next = event;
+ event->prev = elist->last;
+ event->next = NULL;
+ elist->last = event;
+ }
+}
+
+event_t * eventListPop(eventList_t *elist) {
+ event_t *res = elist->first;
+
+ if (res!=NULL) {
+ elist->first=res->next;
+ }
+
+ return res;
+}
+
+int eventListItemCount(eventList_t *elist) {
+ int i=0;
+ event_t *cur = elist->first;
+ while(cur!=NULL) { ++i, cur=cur->next; }
+ return i;
+}
+
+// Private methods below (not present in .h file)
+void _eventListCopyItemValues(event_t *dst, const event_t *src) {
+ dst->playerId = src->playerId;
+ dst->eventTick = src->eventTick;
+ dst->serverTick = src->serverTick;
+ dst->type = src->type;
+ dst->lemId = src->lemId;
+ dst->newRole = src->newRole;
+}
+
+
+void _eventList_sort(event_t *left, event_t *right) {
+
+ event_t *start, *curr, copy;
+
+ // If the left and right pointers are the same, then return
+ if (left == right) return;
+
+ // Set the Start and the Current item pointers
+ start = left;
+ curr = start->next;
+
+ // Loop until we get to the right
+ while (1)
+ {
+ // If the start item is less then the right
+ if (
+ (start->eventTick < curr->eventTick) ||
+ (
+ (start->eventTick == curr->eventTick) &&
+ (start->playerId < curr->playerId)
+ )
+ )
+ {
+ // Swap the items TODO : change references
+ _eventListCopyItemValues(&copy, curr);
+ _eventListCopyItemValues(curr, start);
+ _eventListCopyItemValues(start, &copy);
+ }
+
+ // Check if we have reached the right end
+ if (curr == right) break;
+
+ // Move to the next item in the list
+ curr = curr->next;
+ }
+
+ // Swap the First and Current items
+ _eventListCopyItemValues(&copy, left);
+ _eventListCopyItemValues(left, curr);
+ _eventListCopyItemValues(curr, &copy);
+
+ // Save this Current item
+ const event_t *oldCurr = curr;
+
+ // Check if we need to sort the left hand size of the Current point
+ curr = curr->prev;
+ if (curr != NULL)
+ {
+ if ((left->prev != curr) && (curr->next != left))
+ _eventList_sort(left, curr);
+ }
+
+ // Check if we need to sort the right hand size of the Current point
+ curr = oldCurr->next;
+ if (curr != NULL)
+ {
+ if ((curr->prev != right) && (right->next != curr))
+ _eventList_sort(curr, right);
+ }
+}
+
+void eventList_sort(eventList_t *elist) {
+ _eventList_sort(elist->first,elist->last);
+}
+
diff --git a/src/events.h b/src/events.h
new file mode 100644
index 0000000..7f9ca50
--- /dev/null
+++ b/src/events.h
@@ -0,0 +1,25 @@
+#ifndef EVENTS_H
+#define EVENTS_H
+
+#include "SDL/SDL.h"
+#include "game.h"
+#include "utils.h"
+
+int eventSerializedSize();
+void eventSerialize(const event_t *e, char *buf);
+int eventUnserialize(event_t *e, char *buf);
+
+void eventListInit(eventList_t *elist);
+void eventListFree(eventList_t *elist);
+
+int eventListLock(eventList_t *elist);
+int eventListUnlock(eventList_t *elist);
+
+void eventListAdd(eventList_t *elist, event_t *event);
+event_t * eventListPop(eventList_t *elist);
+
+int eventListItemCount(eventList_t *elist);
+
+void eventListSort(eventList_t *elist);
+
+#endif //EVENTS_H
diff --git a/src/game.c b/src/game.c
new file mode 100644
index 0000000..8015b89
--- /dev/null
+++ b/src/game.c
@@ -0,0 +1,153 @@
+#include "game.h"
+#include "utils.h"
+#include "events.h"
+
+#include <stdlib.h> // calloc
+#include <string.h> // memset
+
+inline tick_t getGameCurrentTick(Uint32 startTime_ms) {
+/* char buf[128];
+ sprintf(buf, "SDL_GetTicks()==%i, startTime_ms==%i, TICK_DURATION_MS==%i", SDL_GetTicks(), startTime_ms, TICK_DURATION_MS);
+ logs(LOG_DEBUG, buf);*/
+ tick_t t = SDL_GetTicks()-startTime_ms; //FIXME Débodements possibles ?!?
+ return t/TICK_DURATION_MS;
+}
+
+int initNetGgame(netGame_t *ng, id_t lemCount) {
+ memset(ng,0,sizeof(netGame_t));
+
+ eventListInit(&(ng->elist));
+
+ ng->lemCount=lemCount;
+
+ ng->local.lems = calloc(lemCount,sizeof(stateLem_t));
+ if (ng->local.lems==NULL) { logp(LOG_ERROR, "initNetGame(), calloc()"); return 3; }
+
+ ng->net.lems = calloc(lemCount,sizeof(stateLem_t));
+ if (ng->net.lems==NULL) { logp(LOG_ERROR, "initNetGame(), calloc()"); return 4; }
+
+ return 0;
+}
+
+void freeNetGame(netGame_t *ng) {
+ eventListFree(&(ng->elist));
+ free(ng->local.lems);
+ free(ng->net.lems);
+}
+
+// Serialize en envoi une trame d'évènement sur le réseau
+int sendEvent(TCPsocket sockDest, const event_t *e) {
+ int msgLen, result;
+ char *buf;
+
+ msgLen=eventSerializedSize();
+ buf=malloc(msgLen);
+ if ( buf==NULL ) {
+ logp(LOG_ERROR, "sendEvent(), malloc()");
+ return 1;
+ }
+ eventSerialize(e, buf);
+
+ result=SDLNet_TCP_Send(sockDest,buf,msgLen);
+ free(buf);
+ if(result<msgLen) {
+ logs2(LOG_WARN, "sendEvent(), SDLNet_TCP_Send() error", SDLNet_GetError());
+ return 2;
+ }
+
+ return 0;
+}
+
+// Attends de recevoir une trame d'évènement du réseau pour un client donné et l'envoi dans la file à traiter
+int receiveEvent(client_t *c, event_t *e) {
+ int result, bufLen;
+ char *buf;
+
+ bufLen=eventSerializedSize();
+ buf=malloc(bufLen);
+ if (buf==NULL) {
+ logp(LOG_ERROR, "receiveEvent(), malloc()");
+ return 1;
+ }
+
+ // Réception de l'évènement
+ result=SDLNet_TCP_Recv(c->sockClient,buf,bufLen); // Appel bloquant
+ if(result<=0) {
+ logs(LOG_ERROR, "receiveEvent(), SDLNet_TCP_Recv() error");
+ free(buf);
+ return 2;
+ }
+
+ // Désérialisation de l'évènement
+ result=eventUnserialize(e,buf);
+ if ( result!=0 ) {
+ logs(LOG_WARN, "receiveEvent(), eventUnserialize() error");
+ free(buf);
+ return 4;
+ }
+
+ free(buf);
+ return 0;
+}
+
+int init() {
+ int result;
+ /*TODO : Avoir un struct de paramètre de config (taille écran) et un struct avec tous les éléments SDL manipulés dans le jeu
+
+ //Initialisation des sous-systèmes de SDL
+ result = SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO );
+ if ( result != 0 ) {
+ logs2(LOG_ERROR, "init(), SDL_Init()", SDL_GetError());
+ return 1;
+ }
+ atexit(SDL_Quit);
+
+ //Mise en place de l'écran
+ screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 24, SDL_HWSURFACE | SDL_DOUBLEBUF );
+ if( screen == NULL ) {
+ logs2(LOG_ERROR, "init(), SDL_SetVideoMode()", SDL_GetError());
+ return 2;
+ }
+
+ //Titre de la fenêtre SDL
+ SDL_WM_SetCaption(WIN_CAPTION, NULL);
+
+ //Désactiver le pointeur de la souris
+ SDL_ShowCursor(0);
+
+ // Allocation et initialisation des différents calques d'affichage et de collision
+ pTerrain = SDL_CreateRGBSurface(SDL_HWSURFACE, LEVEL_WIDTH, LEVEL_HEIGHT, SCREEN_BPP,0,0,0,0);
+ if( pTerrain == NULL ) {
+ logs2(LOG_ERROR, "init(), SDL_CreateRGBSurface()", SDL_GetError());
+ return 3;
+ }
+
+ pSpr_Lem = SDL_CreateRGBSurface(SDL_HWSURFACE, LEVEL_WIDTH, LEVEL_HEIGHT,SCREEN_BPP,0,0,0,0);
+ if( pSpr_Lem == NULL ) {
+ logs2(LOG_ERROR, "init(), SDL_CreateRGBSurface()", SDL_GetError());
+ return 3;
+ }
+
+ pStencil = SDL_CreateRGBSurface(SDL_SWSURFACE, LEVEL_WIDTH, LEVEL_HEIGHT, SCREEN_BPP,0,0,0,0);
+ if( pStencil == NULL ) {
+ logs2(LOG_ERROR, "init(), SDL_CreateRGBSurface()", SDL_GetError());
+ return 3;
+ }
+
+ pStencilFixe = SDL_CreateRGBSurface(SDL_SWSURFACE, LEVEL_WIDTH, LEVEL_HEIGHT, SCREEN_BPP,0,0,0,0);
+ if( pStencil == NULL ) {
+ logs2(LOG_ERROR, "init(), SDL_CreateRGBSurface()", SDL_GetError());
+ return 3;
+ }
+*/
+ //Si tout s'est bien passé
+ return 0;
+}
+
+void play(int tick) {
+ if (tick%100==0) { printf("tick==%i\n",tick); }
+
+ //TODO : boucle de jeu principale ici (maj état de jeu)
+ SDL_Delay(rand()%8);
+}
+
diff --git a/src/game.h b/src/game.h
new file mode 100644
index 0000000..a8b68c4
--- /dev/null
+++ b/src/game.h
@@ -0,0 +1,85 @@
+#ifndef GAME_H
+#define GAME_H
+
+#define DEFAULT_LISTEN_PORT 9999
+#define TICK_DURATION_MS 30
+
+#include "SDL/SDL_net.h"
+
+typedef int tick_t;
+typedef unsigned int id_t;
+typedef struct {
+ unsigned int x,y;
+} pos_t;
+
+enum eventType_t { eReady, eTimeSync, eLemAction };
+
+typedef struct {
+ unsigned int dead : 1;
+ unsigned int side : 1;
+ unsigned int flotter : 1;
+ unsigned int climber : 1;
+ unsigned int remainCount: 4;
+ unsigned int currRole: 4;
+ unsigned int animState: 4;
+ pos_t pos;
+} stateLem_t;
+
+enum gameState { gameNew, gameRunning, gameEnded };
+
+typedef struct {
+ enum gameState state;
+ int startTime_ms;
+ int clientCount;
+ struct _client_t **clients;
+} game_t;
+
+typedef struct _event_t {
+ id_t playerId;
+ tick_t eventTick;
+ tick_t serverTick;
+
+ enum eventType_t type;
+
+ id_t lemId;
+ unsigned int newRole: 4;
+
+ struct _event_t *prev, *next;
+} event_t;
+
+typedef struct {
+ event_t *first, *last;
+ SDL_mutex *lock;
+} eventList_t;
+
+enum clientState { clientNew, clientReady };
+
+typedef struct _client_t {
+ int numClient;
+ enum clientState state;
+ TCPsocket sockClient;
+ eventList_t events;
+ SDL_sem *semEventAvailable;
+ SDL_sem *semGameStart;
+ int lastEventSendTime_ms;
+ game_t *game;
+ stateLem_t *lems;
+} client_t;
+
+typedef struct {
+ id_t lemCount;
+ eventList_t elist;
+ client_t local, net; //TODO : reprendre un peu, bcp de champs dupliqué pour rien
+} netGame_t;
+
+
+tick_t getGameCurrentTick(Uint32 startTime_ms);
+int initNetGame(netGame_t *ng, id_t lemCount);
+void freeNetGame(netGame_t *ng);
+int sendEvent(TCPsocket sockDest, const event_t *e);
+int receiveEvent(client_t *c, event_t *e);
+
+int init();
+void play(int tick);
+
+#endif //GAME_H
diff --git a/src/netlem.c b/src/netlem.c
new file mode 100644
index 0000000..e9a4304
--- /dev/null
+++ b/src/netlem.c
@@ -0,0 +1,197 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "SDL/SDL.h"
+#include "SDL/SDL_net.h"
+#include "SDL/SDL_thread.h"
+
+#include "netlem.h"
+#include "game.h"
+#include "events.h"
+#include "utils.h"
+
+int tick=0;
+SDL_sem *semGameStart;
+
+struct _processNetworkEvents_args { int *end; int *drift_ms; client_t *client; };
+
+void processLocalEvents();
+int processNetworkEvents(void *a);
+int updateGraphics();
+
+int main(int argc, char **argv) {
+ const event_t evReady = {0,0,0,eReady,0,0,NULL,NULL};
+ int end=0, drift_ms=0, timeBefore_ms, delay_ms, result, status;
+ client_t client;
+ struct _processNetworkEvents_args args;
+ char logMsg[128];
+
+ // connect to localhost at port 9999 using TCP (client)
+ IPaddress ip;
+
+ if(argc!=2) {
+ fprintf(stderr, "Usage: %s <nom ou ip du serveur>\n", argv[0]);
+ return 1;
+ }
+
+ openLog(NULL);
+
+ sprintf(logMsg, "NetLemmings version %i.%i", NetLemmings_VERSION_MAJOR, NetLemmings_VERSION_MINOR);
+ logs(LOG_INFO, logMsg);
+
+ if(SDLNet_ResolveHost(&ip,argv[1],9999)==-1) {
+ logs2(LOG_ERROR, "main(), SDLNet_ResolveHost()", SDLNet_GetError());
+ return 2;
+ }
+
+ client.sockClient=SDLNet_TCP_Open(&ip);
+ if(!client.sockClient) {
+ logs2(LOG_ERROR,"main(), SDLNet_TCP_Open()", SDLNet_GetError());
+ return 3;
+ }
+
+ result=init();
+ if(result!=0) {
+ logs(LOG_ERROR,"main(), mySDLInit()");
+ return 3;
+ }
+
+ semGameStart=SDL_CreateSemaphore(0);
+
+ args.end=&end;
+ args.drift_ms=&drift_ms;
+ args.client=&client;
+ SDL_Thread *timeSyncThread = SDL_CreateThread(processNetworkEvents, &args);
+ if(!timeSyncThread) {
+ logs2(LOG_ERROR,"main(), SDL_CreateThread()", SDL_GetError());
+ return 4;
+ }
+
+ //Signale au serveur que le client est pret
+ result=sendEvent(client.sockClient, &evReady);
+ if (result!=0) return 6;
+
+ logs(LOG_INFO, "Waiting game start");
+ result=SDL_SemWait(semGameStart);
+ if (result!=0) {
+ logs2(LOG_ERROR, "main(), SDL_SemWait()", SDL_GetError());
+ return 7;
+ }
+ logs(LOG_INFO, "Game started !");
+
+ while(!end) {
+ timeBefore_ms = SDL_GetTicks();
+
+ processLocalEvents();
+ play(tick++);
+ updateGraphics();
+
+ delay_ms=TICK_DURATION_MS-(SDL_GetTicks()-timeBefore_ms)+drift_ms;
+ if (delay_ms>0) SDL_Delay(delay_ms); //TODO Si le client rame trop, faut décrocher la partie
+ }
+
+ SDLNet_TCP_Close(client.sockClient);
+ SDL_WaitThread(timeSyncThread, &status);
+ sprintf(logMsg, "TimeSync thread terminated with code %i", status);
+ logs(LOG_DEBUG, logMsg);
+
+ closeLog();
+ return 0;
+}
+
+int processNetworkEvents(void *a) {
+ int result;
+ event_t e;
+ char logMsg[128];
+
+ struct _processNetworkEvents_args *args = (struct _processNetworkEvents_args *)a;
+
+ while(! *(args->end) ) {
+ // logs(LOG_DEBUG, "Waiting event");
+ result=receiveEvent(args->client,&e);
+ if (result != 0) {
+ logs(LOG_WARN, "processNetworkEvents(), receiveEvents() error");
+ *(args->end)=1;
+ continue; //TODO : je doute que ça skipe vriament la syncrho du temps :s
+ }
+ // logs(LOG_DEBUG, "Got event");
+
+ *(args->drift_ms)=( tick - e.serverTick );
+ sprintf(logMsg, "serverTick==%i, tick==%i, drift_ms==%i\n", e.serverTick, tick, *(args->drift_ms));
+ logs(LOG_DEBUG, logMsg);
+
+
+ result=SDL_SemPost(semGameStart);
+ if (result!=0) {
+ logs2(LOG_ERROR, "main(), SDL_SemPost()", SDL_GetError());
+ return 1;
+ }
+
+
+ switch(e.type) {
+ case eLemAction:
+ //TODO
+ break;
+ case eTimeSync: // Rien à faire dans ce cas, la synchro du temps se fait quelque soti le tyep d'évènement
+ break;
+ default:
+ logs(LOG_ERROR, "serveClient(), Unknown event type");
+ }
+ }
+ // logs(LOG_DEBUG, "processNetworkEvents() : end");
+
+ return 0;
+}
+
+void processLocalEvents() {
+ /*TODO : intégration des variables manipulées dans des structs qui vont bien
+while( SDL_PollEvent( &event ) ) {
+ switch (event.type) {
+ case SDL_KEYBOARDEVENT:
+ switch(event.key.keysym.sym){
+ case SDLK_HOME : decalFps = 0; break;
+ case SDLK_END : decalFps = FPS-11; break;
+ case SDLK_PAGEUP : if(decalFps>0){decalFps -=1;}break;
+ case SDLK_PAGEDOWN : if(decalFps<(FPS-1)){decalFps +=1;}break;
+ case SDLK_w : paint_stencil = (paint_stencil==0)? 1 : 0 ; break;
+ case SDLK_ESCAPE : quit = 1; break;
+ default:break;
+ }
+ break;
+ case SDL_MOUSEMOTION:
+ mouseX = event.motion.x;
+ mouseY = event.motion.y;
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ err=mouse_action(&gInit, mouseX, mouseY,camera.x,camera.y );
+ if(err!=0){return err;} //FIXME : WTF ?
+ break;
+ case SDL_QUIT:
+ end=1;
+ break;
+ }
+ }
+
+ if(mouseY <= LEVEL_HEIGHT){
+ if (mouseX > (SCREEN_WIDTH - BOUND_SENSIBILITE)){
+ if (camera.x < (LEVEL_WIDTH - SCREEN_WIDTH ) )
+ {camera.x += CAM_VITESSE;}
+ }
+ if (mouseX < BOUND_SENSIBILITE){
+ if (camera.x >= CAM_VITESSE )
+ {camera.x -= CAM_VITESSE;}
+ }
+ */
+}
+
+int updateGraphics() {
+ //TODO : modifier les calques
+
+
+ //Mise à jour de l'écran
+
+ /*if( SDL_Flip(screen) == -1 ) {
+ return 4;
+ }*/
+
+ return 0;
+}
diff --git a/src/netlem.h b/src/netlem.h
new file mode 100644
index 0000000..f0566c5
--- /dev/null
+++ b/src/netlem.h
@@ -0,0 +1,8 @@
+#ifndef NETLEM_H
+#define NETLEM_H
+
+// the configured options and settings for NetLemmings
+#define NetLemmings_VERSION_MAJOR 0
+#define NetLemmings_VERSION_MINOR 1
+
+#endif /*NETLEM_H*/
diff --git a/src/netlem.h.in b/src/netlem.h.in
new file mode 100644
index 0000000..3da7f07
--- /dev/null
+++ b/src/netlem.h.in
@@ -0,0 +1,8 @@
+#ifndef NETLEM_H
+#define NETLEM_H
+
+// the configured options and settings for NetLemmings
+#define NetLemmings_VERSION_MAJOR @NetLemmings_VERSION_MAJOR@
+#define NetLemmings_VERSION_MINOR @NetLemmings_VERSION_MINOR@
+
+#endif /*NETLEM_H*/
diff --git a/src/netlem_ds.c b/src/netlem_ds.c
new file mode 100644
index 0000000..507b4fb
--- /dev/null
+++ b/src/netlem_ds.c
@@ -0,0 +1,501 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include "SDL/SDL.h"
+#include "SDL/SDL_net.h"
+#include "SDL/SDL_thread.h"
+
+#include "game.h"
+#include "netlem_ds.h"
+#include "events.h"
+#include "utils.h"
+
+//struct _timerArgs_t { int *numClients; client_t *clients[]; }; // Comprendre pq ça chie ça
+struct _timerArgs_t { int *numClients; client_t **clients; };
+
+game_t evilGlobalGameVariable = { gameNew, 0, 0, NULL };
+int end=0;
+
+void signals(int signum);
+Uint32 timeSyncInsertNullEventsIfNeeded(Uint32 interval, void *args);
+TCPsocket initServerSocket(int port, SDLNet_SocketSet *sockSetServer);
+int acceptClient(TCPsocket sockServer, int *numThreads, client_t *clients[], SDL_Thread *threads[] );
+int serveClient(void *args);
+int sendEvents(void *args);
+int checkIfAllClientAre(enum clientState state, game_t *game);
+int startGame(game_t *game);
+
+int main(int argc, char *argv[]) {
+
+ int numThreads=0, result;
+ struct _timerArgs_t timerArgs;
+
+ char logMsg[128];
+ SDL_Thread *threads[MAX_THREADS];
+ client_t *clients[MAX_THREADS];
+
+ TCPsocket sockServer;
+ SDLNet_SocketSet sockSetServer;
+ SDL_TimerID timerTimeSync;
+
+ //TODO : dégager ce bidule temporaire
+ evilGlobalGameVariable.clients=clients;
+
+ openLog(NULL);
+
+ logs(LOG_INFO, "Server initialization in progress...");
+
+ result = SDL_Init(SDL_INIT_TIMER);
+ if ( result != 0 ) {
+ logs2(LOG_ERROR, "main(), SDL_Init()", SDL_GetError());
+ return 1;
+ }
+ atexit(SDL_Quit);
+ signal(2,signals);
+
+
+ // Ecoute sur port principal pour recevoir les clients
+ sockServer=initServerSocket(DEFAULT_LISTEN_PORT, &sockSetServer);
+ if (sockServer == NULL ) {
+ return 2;
+ }
+
+ // Mise en place du timer pour executer périodiquement la fonction server-side pour la synchro du temps
+ timerArgs.numClients=&numThreads;
+ timerArgs.clients=clients;
+ timerTimeSync = SDL_AddTimer(1000, timeSyncInsertNullEventsIfNeeded, &timerArgs);
+ if ( timerTimeSync == NULL ) {
+ logs2(LOG_ERROR, "main(), SDL_AddTimer()", SDL_GetError());
+ return 3;
+ }
+
+ logs(LOG_INFO, "Server ready to accept clients");
+ // Acceptation des clients qui tentent de se connecter
+ while(!end) {
+ SDLNet_CheckSockets(sockSetServer, -1); // Appel bloquant jusqu'à activité
+ acceptClient(sockServer, &numThreads, clients, threads);
+ }
+
+ logs(LOG_INFO, "Server is shuting down...");
+
+ // Libération de toutes les ressources
+ SDLNet_TCP_Close(sockServer);
+ SDLNet_FreeSocketSet(sockSetServer);
+ SDL_RemoveTimer(timerTimeSync);
+
+ // Attente de terminaison des autres threads
+ logs(LOG_DEBUG, "Waiting all remaining threads\n");
+ while(numThreads>=0) {
+ int status;
+ SDL_WaitThread(threads[numThreads-1], &status);
+ --numThreads;
+ sprintf(logMsg, "Thread %i terminated with code %i", numThreads,status);
+ logs(LOG_DEBUG, logMsg);
+ }
+
+
+ logs(LOG_INFO, "Server down");
+
+ closeLog();
+
+ return 0;
+}
+
+void signals(int signum) {
+ static int force=0;
+ char buf[128];
+ sprintf(buf, "Caught signal %i", signum);
+ logs(LOG_WARN, buf);
+
+ if(!force) {
+ end=1;
+ force=1;
+ logs(LOG_WARN, "Trying to stop smoothly...");
+ } else {
+ logs(LOG_WARN, "Emergency stop");
+ exit(1);
+ }
+}
+
+Uint32 timeSyncInsertNullEventsIfNeeded(Uint32 interval, void *args) {
+ const event_t evTimeSyncModel = {0,0,0,eTimeSync, 0,0, NULL,NULL};
+ static int inProgress=0;
+
+ game_t *game=NULL;
+ client_t *cli=NULL;
+ event_t *evTimeSync=NULL;
+ int i;
+
+// char buf[128];
+
+ struct _timerArgs_t *a=(struct _timerArgs_t *)args;
+
+// logs(LOG_DEBUG, "timeSyncInsertNullEventsIfNeeded() : begin");
+
+ if (inProgress != 0) {
+ logs(LOG_WARN, "timeSyncInsertNullEventsIfNeeded() takes too long !");
+ return 200;
+ }
+ inProgress=1;
+
+// sprintf(buf, "*(a->numClients)==%i", *(a->numClients));
+// logs2(LOG_DEBUG, "timeSyncInsertNullEventsIfNeeded()", buf);
+
+ //TODO : be _globally_ thread-safe for all the loop
+ for(i=0 ; i < *(a->numClients) ; ++i) {
+ cli=a->clients[i];
+ game=cli->game;
+
+// sprintf(buf, "game->state==%i, SDL_GetTicks()==%i, cli->lastEventSendTime_ms==%i", game->state, SDL_GetTicks(), cli->lastEventSendTime_ms);
+// logs2(LOG_DEBUG, "timeSyncInsertNullEventsIfNeeded()", buf);
+
+ if ( game->state == gameRunning &&
+ ( (SDL_GetTicks() - cli->lastEventSendTime_ms) > (0.9 * MAX_EVENT_INTERVAL_MS) )
+ ) {
+ evTimeSync=malloc(sizeof(event_t));
+ if (evTimeSync==NULL) {
+ logp(LOG_ERROR, "timeSyncInsertNullEventsIfNeeded(), malloc()");
+ return 0;
+ }
+ memcpy(evTimeSync,&evTimeSyncModel,sizeof(event_t));
+ eventListAdd((&cli->events), evTimeSync);
+ SDL_SemPost(cli->semEventAvailable); // FIXME Code retour
+ }
+ }
+
+// logs(LOG_DEBUG, "timeSyncInsertNullEventsIfNeeded() : end");
+
+ inProgress=0;
+
+ return 1000; // TODO : optimiser la régularité avec la variable interval
+}
+
+TCPsocket initServerSocket(int port, SDLNet_SocketSet *sockSetServer) {
+ int result;
+ TCPsocket sockServer;
+ IPaddress ip;
+
+ // Crée un socket TCP en écoute sur le port passé en paramètre bindé en 0.0.0.0
+ if(SDLNet_ResolveHost(&ip,NULL,port)==-1) {
+ logs2(LOG_ERROR, "initServerSocket(), SDLNet_ResolveHost()", SDLNet_GetError());
+ return NULL;
+ }
+
+ sockServer=SDLNet_TCP_Open(&ip);
+ if(!sockServer) {
+ logs2(LOG_ERROR, "initServerSocket(), SDLNet_TCP_Open()", SDLNet_GetError());
+ return NULL;
+ }
+
+ // Crée un socketSet qui permet essentiellement d'utiliser SDLNet_Check_Sockets()
+ if (sockSetServer != NULL) {
+ *sockSetServer = SDLNet_AllocSocketSet(1);
+ if ( *sockSetServer < 0 ) {
+ logs2(LOG_ERROR, "initServerSocket(), SDLNet_SocketSet()", SDLNet_GetError());
+ return NULL;
+ }
+
+ result = SDLNet_TCP_AddSocket(*sockSetServer,sockServer);
+ if ( result < 0 ) {
+ logs2(LOG_ERROR, "initServerSocket(), SDLNet_TCP_AddSocket()", SDLNet_GetError());
+ return NULL;
+ }
+ }
+
+ return sockServer;
+}
+
+int acceptClient(TCPsocket sockServer, int *numThreads, client_t *clients[], SDL_Thread *threads[] ) {
+ //TODO : faire une vraie gestion d'un pool de threads et de clients
+ TCPsocket sockClient;
+ SDL_Thread *threadClient=NULL;
+ client_t *cli=NULL;
+
+ sockClient=SDLNet_TCP_Accept(sockServer); // Appel non bloquant (contrairement à accept() en unix)
+ if(!sockClient) {
+ logs2(LOG_WARN, "acceptClient(), SDLNet_TCP_Accept()", SDLNet_GetError());
+ return 1;
+ }
+
+ if(*numThreads >= MAX_THREADS) {
+ logs2(LOG_WARN, "acceptClient()", "Too many threads");
+ SDLNet_TCP_Close(sockClient);
+ return 2;
+ }
+
+ //Préparation de la structure de données pour stocker l'état du nouveau client
+ cli=malloc(sizeof(client_t));
+ if( cli==NULL ) {
+ logp(LOG_ERROR, "acceptClient()), malloc()");
+ SDLNet_TCP_Close(sockClient);
+ return 3;
+ }
+
+ cli->numClient=*numThreads;
+ cli->state=clientNew;
+ cli->sockClient = sockClient;
+ eventListInit(&cli->events);
+ cli->semEventAvailable = SDL_CreateSemaphore(0); //FIXME : test retour
+ cli->semGameStart = SDL_CreateSemaphore(0);
+ cli->lastEventSendTime_ms=0;
+ cli->game=NULL;
+ cli->lems=NULL;
+
+ clients[(*numThreads)]=cli;
+ //TODO : Dégager ce truc temporaire
+ evilGlobalGameVariable.clientCount=(*numThreads)+1;
+
+ //Préparation du thread correspondant à ce nouveau client (modèle 1 client => 1 thread)
+ threadClient = SDL_CreateThread(serveClient, (void *)cli);
+ if(!threadClient) {
+ logs2(LOG_ERROR, "acceptClient(), SDL_CreateThread()", SDL_GetError());
+ SDLNet_TCP_Close(sockClient);
+ SDL_DestroySemaphore(cli->semEventAvailable);
+ SDL_DestroySemaphore(cli->semGameStart);
+ eventListFree(&(cli->events));
+ free(cli);
+ return 5;
+ }
+
+ //Inscrption dans le pool de clients et de threads
+ threads[(*numThreads)]=threadClient;
+ ++(*numThreads);
+
+
+ return 0;
+}
+
+// Fonction exécutée par le thread associé à un client
+int serveClient(void *args) {
+ int i, result;
+ char logMsg[128];
+ const char *hostTmp;
+ char host[128];
+ IPaddress *ipRemote=NULL;
+ SDL_Thread *threadSendEvents=NULL;
+ event_t e;
+
+ client_t *otherClient=NULL;
+ client_t *c=(client_t *) args;
+
+
+ // Récupération d'infos à propos du client (au sens réseau) : nom d'hôte et port distant
+ ipRemote=SDLNet_TCP_GetPeerAddress(c->sockClient);
+ if(!ipRemote) {
+ logs2(LOG_ERROR, "serveClient(), SDLNet_TCP_GetPeerAddress", SDLNet_GetError());
+ SDLNet_TCP_Close(c->sockClient);
+ free(args);
+ return 1;
+ }
+
+ hostTmp=SDLNet_ResolveIP(ipRemote);
+ if(!hostTmp) {
+ logs2(LOG_INFO, "serveClient(), SDLNet_ResolveIP", SDLNet_GetError());
+ } else {
+ strcpy(host, hostTmp); //FIXME possible buffer overflow
+ // Add a default "(Unknown)" value)
+ }
+
+ //TODO : faire ici toute la partie de l'automate côté serveur pour la phrase d'initialisation de la partie
+ c->game=&evilGlobalGameVariable;
+
+ // On attends le signal du client qu'il envoi lorsqu'il a chargé les ressources nécessaires a la partie
+ sprintf(logMsg, "Waiting client %i (%s)", c->numClient, host);
+ logs(LOG_INFO, logMsg);
+
+ //FIXME : utiliser reveiveEvent
+ result=SDLNet_TCP_Recv(c->sockClient,&e,sizeof(e));
+ if(result<=0) {
+ logs2(LOG_ERROR, "serveClient(), SDLNet_TCP_Recv()", SDLNet_GetError());
+ SDLNet_TCP_Close(c->sockClient);
+ free(args);
+ return 3;
+ }
+ if ( e.type != eReady) {
+ logs(LOG_WARN, "serveClient(), first event received is not eReady");
+ SDLNet_TCP_Close(c->sockClient);
+ free(args);
+ return 4;
+ }
+
+ // Création d'un nouveau thread par client pour l'envoi asynchrone des évènements
+ threadSendEvents = SDL_CreateThread(sendEvents, args);
+ if(!threadSendEvents) {
+ logs2(LOG_ERROR, "serveClient(), SDL_CreateThread()", SDL_GetError());
+ SDLNet_TCP_Close(c->sockClient);
+ free(args);
+ return 5;
+ }
+
+ sprintf(logMsg, "Client %i (%s) ready", c->numClient, host);
+ logs(LOG_INFO, logMsg);
+
+ // Si tous les (autres) clients de la partie sont prêts, on initie le début de la partie
+ c->state=clientReady;
+ if ( c->game->clientCount == 2 && checkIfAllClientAre(clientReady, c->game) == 0) {
+ result=startGame(c->game);
+ if (result!=0) {
+ logs(LOG_ERROR, "serveClient(), startGame() error");
+ return 5; //FIXME : 5bis !
+ }
+ }
+
+ // Ce thread se bloque jusqu'au démarrage de la partie (débloquage immédiat si on est rentré dans le if précédent)
+ logs(LOG_INFO, "Waiting for game start");
+ result=SDL_SemWait(c->semGameStart);
+ if (result!=0) {
+ logs2(LOG_ERROR, "serveClient(), SDL_SemWait()", SDL_GetError());
+ SDLNet_TCP_Close(c->sockClient);
+ free(args);
+ return 6;
+ }
+ logs(LOG_INFO, "Game Started !");
+
+ // Boucle principale de jeu pour chaque client du point de vue du serveur
+ while(c->game->state != gameEnded ) {
+ result=receiveEvent(c, &e);
+ if(result!=0) {
+ c->game->state = gameEnded;
+ continue;
+ }
+
+ switch(e.type) {
+ case eLemAction:
+ for( i=0 ; i < (c->game->clientCount); ++i ) {
+ otherClient=c->game->clients[i];
+ eventListAdd(&(otherClient->events), &e);
+ SDL_SemPost(otherClient->semEventAvailable); //FIXME code de retour
+ }
+ break;
+ default:
+ logs(LOG_ERROR, "serveClient(), Unknown event type");
+ }
+ }
+
+ // La partie est terminée, provoque et attends la terminaison du threadSendEvents
+ result=SDL_SemPost(c->semEventAvailable);
+ if(result!=0) {
+ logs(LOG_ERROR, "serveClient(), SDL_SemPost() error");
+ SDLNet_TCP_Close(c->sockClient);
+ free(args);
+ return 7;
+ }
+ SDL_WaitThread(threadSendEvents, &result);
+
+ logs2(LOG_INFO, "serveClient() ended for", (char *)host);
+
+ SDLNet_TCP_Close(c->sockClient);
+ free(args);
+
+ return 0;
+}
+
+// Fonction principale du thread qui envoi sur le réseau les event_t qui sont en file pour chaque client
+int sendEvents(void *args) {
+ char *buf=NULL/*, bufDbg[128]*/;
+ int i, result, eLen, msgLen;
+ client_t *c=(client_t *) args;
+ event_t *e;
+
+ // S'autodétruit si la partie est terminée
+ // Note : peut être bloqué sur SemWait, donc faire un V après avoir changé le game state
+ while(c->game->state != gameEnded ) {
+ //Attends qu'un ou des events soient disponibles
+ result=SDL_SemWait(c->semEventAvailable);
+ if(result!=0) {
+ logs(LOG_ERROR, "sendEvents(), SDL_SemWait() error");
+ return 1;
+ }
+
+// logs(LOG_DEBUG, "Event available !");
+
+ // Verrouile, enumère et sérialise dans buf tous les évènements de la file
+ eventListLock(&(c->events)); //FIXME : check return code
+ eLen=eventSerializedSize();
+ msgLen=eLen * eventListItemCount((&c->events));
+ buf=malloc(msgLen);
+ if ( buf==NULL ) {
+ logp(LOG_ERROR, "sendEvents(), malloc()");
+ return 2;
+ }
+
+ i=0;
+ while ((e=eventListPop(&(c->events))) != NULL) {
+ //Sending timestamp for time synchronization
+ e->serverTick=getGameCurrentTick(c->game->startTime_ms);
+ //Serialize messages and put them contiguous in buf
+ eventSerialize(e, buf+eLen*i++);
+ free(e);
+ }
+ eventListUnlock(&(c->events)); //FIXME : check return code
+
+ // Envoi ces évènements sérialisés sur le réseau
+ if (msgLen>0) {
+ result=SDLNet_TCP_Send(c->sockClient,buf,msgLen);
+ if(result<msgLen) {
+ logs2(LOG_WARN, "sendEvents(), SDLNet_TCP_Send() error", SDLNet_GetError());
+ return 3;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// Retourne 0 si tous les clients d'une partie donnée sont dan sun état donné (1 sinon)
+inline int checkIfAllClientAre(enum clientState state, game_t *game) {
+ int i;
+ for(i=0 ; i < (game->clientCount) ; ++i ) {
+ if(game->clients[i]->state != state) { return 1; }
+ }
+ return 0;
+}
+
+int startGame(game_t *game) {
+ int i, result;
+ client_t *cli;
+
+ char buf[128];
+
+ // Date de début dans le futur pour laisser du temps pour converger
+ // à l'algorithme de synchronisation du temps
+ game->startTime_ms=SDL_GetTicks()+TIME_FOR_TIMESYNC_MS;
+
+ sprintf(buf, "game->startTime_ms==%i", game->startTime_ms);
+ logs(LOG_DEBUG, buf);
+
+ game->state = gameRunning;
+
+ // On débloque tous les threads des clients qui sont en attente
+ for(i=0 ; i < (game->clientCount) ; ++i) {
+ cli=game->clients[i];
+ result=SDL_SemPost(cli->semGameStart);
+ if (result!=0) {
+ logs2(LOG_ERROR, "startGame(), SDL_SemPost()", SDL_GetError());
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+Machine a états complète à implémenter
+on lance le serveur, il se met en écoute et lance un thread / timer qui compte le temps (globalServerTick)
+un client demande une partie réseau, se connecte au serveur, donne un username
+le serveur le met dans un pool de client en attente et lui envoi la liste des clients en attente
+un second client demande une partie réseau, idem
+la liste des clients en attente est renvoyée au premier client (enfait à tous les clients)
+le client1 choisi le client2 en coequipier et envoi cette info au serveur
+le serveur transmet au client2 qui affiche la proposition
+le client2 accepte et renvoi cette acceptation au serveur (s'il refuse rien n'est transmis)
+le serveur voit que les 2 clients sont ok pour jouer ensemble. (si le client1 ne s'est pas barré ou a commencé ailleurs). Le serveur envoi un paquet à chacun : une demande de paramètres de jeu au premier (car il a initié) et une attente pour l'autre.
+Le client1 modifier des paramètres de jeu, les transmet au serveur qui les forwarde au client2 pour acceptation.
+Le client2 accepte les paramètres et envoi un paquet pour le ire au serveur.
+Le serveur envoi un paquet à chacun pour dire de charger la partie.
+Quand un client est (presque) prêt, il envoi un premier event NULL.
+Quand le serveur a un event null de chaque joueur, il lance la partie en ajoutant la partie dans la liste des parties actives, en positionnant la variable tickGameStart à la valeur ( globalServerTick + TIME_FOR_TIMESYNC_MS ).
+Les envois d'évèments commencent donc (un timer réveille une procédure qui checke la lastSentEventTick, si trop vieux, envoi d'un event NULL pour la synchro du temps)
+Le reste de la logique est déclanhée sur la réception d'évènement des clients (remplissage de liste d'évènement, tri et renvoi à tous...)
+*/
+
diff --git a/src/netlem_ds.h b/src/netlem_ds.h
new file mode 100644
index 0000000..cd28d03
--- /dev/null
+++ b/src/netlem_ds.h
@@ -0,0 +1,8 @@
+#ifndef NETLEM_DS_H
+#define NETLEM_DS_H
+
+#define MAX_THREADS 2
+#define TIME_FOR_TIMESYNC_MS 2000
+#define MAX_EVENT_INTERVAL_MS 1000
+
+#endif
diff --git a/src/test/test.c b/src/test/test.c
new file mode 100644
index 0000000..4f157b1
--- /dev/null
+++ b/src/test/test.c
@@ -0,0 +1,28 @@
+
+#include <stdio.h>
+#include "SDL/SDL.h"
+
+
+Uint32 timeSyncInsertNullEventsIfNeeded(Uint32 interval, void *args) {
+ static i=0;
+ printf("%i\n",i++);
+ return 2000;
+}
+
+int main(void) {
+
+ SDL_TimerID timerTimeSync;
+
+
+ SDL_Init(SDL_INIT_TIMER);
+ printf("before addtimer\n");
+ timerTimeSync = SDL_AddTimer(2000, timeSyncInsertNullEventsIfNeeded, NULL);
+ printf("after addtimer\n");
+
+ if ( timerTimeSync==NULL ) {
+ printf("bug addtimer\n");
+ }
+
+ SDL_Delay(100000);
+}
+
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..e2282fe
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,41 @@
+#include "utils.h"
+
+#include <stdlib.h> // exit()
+#include <stdio.h> // FILE *
+#include <string.h> // strerror_r()
+#include <errno.h>
+
+static FILE *fdLog=NULL;
+
+void openLog(char path[]) {
+ if(path==NULL) {
+ fdLog=stderr;
+ } else {
+ //TODO : à implementer
+ fprintf(stderr,"openLog : not yet implemented\n");
+ exit(128);
+ }
+}
+
+void closeLog() {
+ if(fdLog!=NULL && fdLog!=stderr) {
+ fclose(fdLog);
+ }
+}
+
+//TODO : ajouter une notion de niveau de masquage de messages
+void logs(int level, char msg[]) {
+ fprintf(fdLog,"%i - %s\n", level, msg);
+}
+
+void logs2(int level, char context[], char msg[]) {
+ fprintf(fdLog,"%i - %s : %s\n", level, context, msg);
+}
+
+void logp(int level, char msg[]) {
+ char errbuf[128]; //TODO : Pas cool la taille fixe ici
+
+ strerror_r(errno, errbuf, 128);
+ fprintf(fdLog,"%i - %s : %s\n", level, msg, errbuf);
+}
+
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..021816d
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,15 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#define LOG_DEBUG 0
+#define LOG_INFO 1
+#define LOG_WARN 2
+#define LOG_ERROR 3
+
+void openLog(char path[]);
+void closeLog();
+void logs(int level, char msg[]);
+void logs2(int level, char context[], char msg[]);
+void logp(int level, char msg[]);
+
+#endif //UTILS_H