summaryrefslogtreecommitdiff
path: root/src/netlem.c
blob: 52750af502d49435a18f244011790c8d6cb138a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
#include <signal.h>

#include "SDL/SDL.h"
#include "SDL/SDL_net.h"
#include "SDL/SDL_thread.h"

#include "data_localgame.h"

#include "netgame.h"
#include "utils.h"

#include "game.h"
//#include "events.h"
#include "loader.h"
#include "timing.h"
#include "netlem_state_machine.h"

// Application version number
#include "netlem.h"

// Thread arguments structures
struct _networkReadProc_args {
	client_t *client;
	Uint32 *tick;
	int *drift_ms;
};

struct _loadLevelProc_args { 
	serverParams_t *serverParams;
	gameIni_t *gIni;
	gameRess_t *gRess;
	Uint8 *loadProgress;
};

// Material for thread synchronization and collaboration
SDL_sem *semGameStart;
SDL_sem *semLoadLevel;

// Thread entry points
int networkReadProc(void *a);
int loadLevelProc(void *a);

// Client-specific functions
void signals(int signum);
int init(gameConfig_t *conf, gameGraphics_t *graph);
void loadGameConfig(gameConfig_t *conf);
void processLocalEvents();
int updateGraphics();


int main(int argc, char **argv) {

	int drift_ms=0, endMainLoop, result;
	Uint32 tick=0, timeBefore_ms;
	Uint8 loadProgress=0;

	client_t client;
	serverParams_t serverParams;
	gameConfig_t conf;
	gameIni_t gIni;
	gameRess_t gRess;
	gameGraphics_t graph;

	char logMsg[128];
	struct _networkReadProc_args args;
	struct _loadLevelProc_args args2;

	IPaddress ip;

	if(argc!=2) {
		fprintf(stderr, "Usage: %s <nom ou ip du serveur>\n", argv[0]);
		return 1;
	}

	// Log system initialization
	openLog(NULL);

	// Starting log message
	sprintf(logMsg, "NetLemmings version %i.%i", NetLemmings_VERSION_MAJOR, NetLemmings_VERSION_MINOR);
	logs(LOG_INFO, logMsg);

	// Server name resolution and connection
	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;
	}

	// Config file loading
	loadGameConfig(&conf);

	// Libraries initialization
	result=init(&conf, &graph);
	if(result!=0) {
		logs(LOG_ERROR,"main(), init()");
		return 3;
	}

	// Synchronization tools intialization
	semGameStart=SDL_CreateSemaphore(0);
	semLoadLevel=SDL_CreateSemaphore(0);

	// Network reader thread to process remote events and time sync
	args.client=&client;
	args.tick=&tick;
	args.drift_ms=&drift_ms;
	SDL_Thread *networkReadThread = SDL_CreateThread(networkReadProc, &args);
	if(!networkReadThread) {
		logs2(LOG_ERROR,"main(), SDL_CreateThread()", SDL_GetError());
		return 4;
	}

	// Worker thread for loading levels asynchronously (the thread begin with a SDL_Wait(semLevelLoadingStart))	
	args2.serverParams=&serverParams;
	args2.gIni=&gIni;
	args2.gRess=&gRess;
	args2.loadProgress=&loadProgress;
	SDL_Thread *loadLevelThread = SDL_CreateThread(loadLevelProc, &args2);
	if(!loadLevelThread) {
		logs2(LOG_ERROR,"main(), SDL_CreateThread()", SDL_GetError());
		return 4; //FIXME : autre num
	}

	//TODO : faire les menus et ne pas forcer ça ici
	if ( changeStateAndNotify(eMultiLoading, client.sockClient) == eNull ) {
		return 6;
	}

	// Main game loop
	endMainLoop=0;
	while(!endMainLoop) {
		timeBefore_ms = SDL_GetTicks();

		// Process local player keyboard and mouse events
		// (note: remote events are processed by network read thread)
		processLocalEvents();

		switch(getState()) {
			case eMultiLoading:
				if(loadProgress==100) {
					changeStateAndNotify(eMultiWaitLoading,client.sockClient);
				}
			break;
			case eMultiWaitLoading:
				result=SDL_SemTryWait(semGameStart);
				switch (result) {
					case -1:
						logs2(LOG_ERROR, "main(), SDL_SemTryWaitTimeout()", SDL_GetError());
						return 7;
						break;
					case 0:
//FIXME : check return value
						changeState(eMultiGame);
						break;
					default:
						break;
				}
				break;
			case eSingleGame:
			case eMultiGame:
				// Make game evolve from the current state to the next time chunk (ie. frame, or tick)
				play(tick++);
				break;
			default:
				endMainLoop=1;
			break;
		}

		// Display that new game state to the local user
		updateGraphics();

		// Delay that we have to wait for the next frame (depends on execution time and network time drift)
		waitForNextTick(timeBefore_ms, drift_ms);
	}

	// Close communcation with the server, stop threads, close logs and go out
	SDLNet_TCP_Close(client.sockClient);
	SDL_WaitThread(networkReadThread, &result);
	sprintf(logMsg, "TimeSync thread terminated with code %i", result);
	logs(LOG_DEBUG, logMsg);

	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) {
		changeState(eEnd);
		force=1;
		logs(LOG_WARN, "Trying to stop smoothly...");
	} else {
		logs(LOG_WARN, "Emergency stop");
		exit(1);
	}
}

int init(gameConfig_t *conf, gameGraphics_t *graph) {
	int result;


	// SDL subsystems initialization
	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);
	signal(2,signals);

	// Screen setup
	graph->screen = SDL_SetVideoMode(conf->screen.w, conf->screen.h, SCREEN_BPP, SDL_HWSURFACE | SDL_DOUBLEBUF );
	if( graph->screen == NULL ) {
		logs2(LOG_ERROR, "init(), SDL_SetVideoMode()", SDL_GetError());
		return 2;
	}

	// SDL main window caption
	SDL_WM_SetCaption(WIN_CAPTION, NULL);

	// We dont want to see the standard mouse cursor in our main window
	SDL_ShowCursor(0);

	// Memory allocation and initialization for all display layers
	graph->terrain = SDL_CreateRGBSurface(SDL_HWSURFACE, LEVEL_WIDTH, LEVEL_HEIGHT, SCREEN_BPP,0,0,0,0);
	if( graph->terrain == NULL ) {
		logs2(LOG_ERROR, "init(), SDL_CreateRGBSurface()", SDL_GetError());
		return 3;
	}

	graph->stencil = SDL_CreateRGBSurface(SDL_SWSURFACE, LEVEL_WIDTH, LEVEL_HEIGHT, SCREEN_BPP,0,0,0,0);
	if( graph->stencil == NULL ) {
		logs2(LOG_ERROR, "init(), SDL_CreateRGBSurface()", SDL_GetError());
		return 3;
	}

	return 0;
}

void loadGameConfig(gameConfig_t *conf) {
	conf->screen.w=640;
	conf->screen.h=480;
	conf->screen.x=0;
	conf->screen.y=0;
	//TODO : charger vraiment le fichier de conf ^^
}

int networkReadProc(void *a) {
	int result;
	event_t e;
	char logMsg[128];
	
	struct _networkReadProc_args *args = (struct _networkReadProc_args *)a;

	while( getState() != eEnd ) {
	//	logs(LOG_DEBUG, "Waiting event");
		result=receiveEvent(args->client,&e);
		if (result != 0) {
			logs(LOG_WARN, "networkReadProc(), receiveEvents() error");
			changeState(eEnd);
			continue; //TODO : je doute que ça skipe vriament la syncrho du temps :s
		}
	//	logs(LOG_DEBUG, "Got event");

		*(args->drift_ms)=( *(args->tick) - e.serverTick );
		sprintf(logMsg, "serverTick==%i, tick==%i, drift_ms==%i\n", e.serverTick, *(args->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, "networkReadProc() : end");

	return 0;
}

int startLoadLevel() {
	int result;

	logs(LOG_INFO, "Start game loading");
	result=SDL_SemPost(semLoadLevel);

	return result;
}

int loadLevelProc(void *a) {
	int result,i;
	//char logMsg[128];
	
	struct _loadLevelProc_args *args = (struct _loadLevelProc_args *)a;
	
	logs(LOG_WARN, "loadLevelProc(), beginnng");
	while( getState() != eEnd ) {
		result=SDL_SemWait(semLoadLevel);
		if (result!=0) {
			logs2(LOG_ERROR, "main(), SDL_SemTryWaitTimeout()", SDL_GetError());

		}

		logs(LOG_WARN, "loadLevelProc(), start load level");





		*(args->loadProgress)=100;
		logs(LOG_WARN, "loadLevelProc(), end load level");
		
	}
	logs(LOG_WARN, "loadLevelProc(), end thread");

	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;
}