summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLudovic Pouzenc <ludovic@pouzenc.fr>2010-10-24 21:07:59 +0000
committerLudovic Pouzenc <ludovic@pouzenc.fr>2010-10-24 21:07:59 +0000
commitae999d7103e6d5ffa129b8f73dc4e8c8b482ddc4 (patch)
tree245e9574f814d241844d388568c62eda5b72bca0 /src
parentc4f027070dd11b463c7b2ea3ff56c97ec66a16dc (diff)
download2010-netlemmings-ae999d7103e6d5ffa129b8f73dc4e8c8b482ddc4.tar.gz
2010-netlemmings-ae999d7103e6d5ffa129b8f73dc4e8c8b482ddc4.tar.bz2
2010-netlemmings-ae999d7103e6d5ffa129b8f73dc4e8c8b482ddc4.zip
Tentative d'utilisation de cmake pour gérer la compilation, plutôt que des vieux fichiers Makefile pas portbles du tout. Bon, pour l'heure ça compile pas les dépendances nécessaires et je sais pas pourquoi
git-svn-id: file:///var/svn/2010-netlemmings/trunk@115 077b3477-7977-48bd-8428-443f22f7bfda
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