1 // This code is D 1.0
2 
3 /**
4 	Part of my old D 1.0 game helper code that used SDL. I keep it compiling on new D compilers too, but it is not meant to be used in new projects.
5 
6 	Version: 1.0
7 	License: GNU General Public License
8 */
9 module arsd.engine; //@-L-lSDL -L-lSDL_mixer -L-lSDL_ttf -L-lSDL_image -L-lGL -L-lSDL_net
10 
11 pragma(lib, "SDL");
12 pragma(lib, "SDL_mixer");
13 pragma(lib, "SDL_ttf");
14 pragma(lib, "SDL_image");
15 pragma(lib, "SDL_net");
16 pragma(lib, "GL");
17 
18 // FIXME: the difference between directions and buttons should be removed
19 
20 
21 import sdl.SDL;
22 import sdl.SDL_net;
23 
24 version(D_Version2) {
25 	import random = core.stdc.stdlib;
26 	alias random.srand srand;
27 
28 	char[] convToString(int a) {
29 		return null;
30 	}
31 	string immutableString(in char[] a) { return a.idup; }
32 } else {
33 	import random = std.random;
34 	void srand(uint a) {
35 		random.rand_seed(a, 0);
36 	}
37 	alias std..string.toString convToString;
38 
39 	char[] immutableString(in char[] a) { return a; }
40 }
41 import std.math;
42 public import arsd.screen;
43 public import arsd.audio;
44 
45 public import sdl.SDL_keysym_;
46 
47 version(D_Version2)
48 	import core.stdc.stdarg;
49 else
50 	import std.stdarg;
51 
52 version(D_Version2) {
53 	import core.stdc.stdio;
54 	void writefln(string s) { printf("%*s", s.length, s.ptr); }
55 	void writefln(string s, int i) { printf(s.ptr, i); }
56 } else
57 	import std.stdio;
58 //version(linux) pragma(lib, "kbhit.o");
59 
60 int randomNumber(int min, int max){
61 	if(min == max)
62 		return min;
63 	max++; // make it inclusive
64 
65 	uint a = random.rand();
66 	a %= (max - min);
67 	a += min;
68 	return a;
69 }
70 
71 
72 // HACK!
73 	bool waiting;
74 	uint globalTimer;
75 class Callable{
76   public:
77 	  abstract void run(int timer);
78 	  // after is the other thing that is calling this to be paused and then unpaused
79 	  final void start(Callable after = null){
80 		After = after;
81 		if(After !is null)
82 			After.paused = true;
83 		paused = false;
84 		for(int a = 0; a < objs.length; a++)
85 			if(objs[a] is null){
86 				objs[a] = this;
87 				goto done;
88 
89 		}
90 		objs.length = objs.length + 1;
91 		objs[objs.length - 1] = this;
92 
93 		done:
94 		frame();
95 	}
96 
97 	  final void queue(){
98 	  	int a;
99 		paused = true;
100 		for(a = 0; a < objs.length; a++)
101 			if(objs[a] is null){
102 				objs[a] = this;
103 				goto done;
104 
105 		}
106 		objs.length = a + 1;
107 		objs[a] = this;
108 
109 		done:
110 		if(a == 1){
111 			objs[0].paused = true;
112 			paused = false;
113 		} else {
114 			objs[a - 1].After = this;
115 		}
116 		After = objs[0];
117 
118 		frame();
119 	}
120 
121 
122 
123 
124 	final void terminate(){
125 		for(int a = 0; a < objs.length; a++)
126 			if(objs[a] !is null && objs[a] == this)
127 				objs[a] = null;
128 		if(After !is null){
129 			After.paused = false;
130 			After.frame();
131 		}
132 	}
133 
134 	bool paused;
135   private:
136 	void frame(){
137 		if(!paused){
138 			if(globalTimer > lastFrame){
139 				lastFrame = globalTimer;
140 				run(Timer);
141 				Timer++;
142 			}
143 		}
144 	}
145 	int Timer;
146 	uint lastFrame;
147 	Callable After;
148 }
149 
150 Callable[] objs;
151 // end HACK
152 
153 //	enum {up = 0, down, left, right};
154 	enum {select = 8, start = 9, square = 3, cross = 2, circle = 1, triangle = 0,
155 		R1 = 7, R2 = 5, L2 = 4, L1 = 6, L3 = 10, R3 = 11, special = 12,
156 		up = 13, down = 14, left = 15, right = 16,  // dpad and left stick
157 		up2 = 17, down2 = 18, left2 = 19, right2 = 20}; // right stick
158 	const int NUM_BUTTONS = 21;
159 class Engine{
160 	static const int NoVideo = 0;
161 	static const int Video1024x768 = 1;
162 	static const int Video640x480 = 2;
163 	static const int Video800x600 = 3;
164 	static const int Video320x200 = 4;
165 	static const int Video512x512 = 5;
166 
167 	static const int VideoFullScreen = 32;
168 
169 
170 	alias int Direction;
171 	alias int Buttons;
172 
173 	static const int MAX_NET = 8;
174 	static const int NET_PORT = 7777;
175 
176 	// For being a network server.....
177 	bool isServer;
178 	struct NetClient {
179 		TCPsocket sock;
180 		int numPlayers;
181 		int startingPlayer;
182 		int latency; // in milliseconds
183 
184 		int state; // Only valid if sock is non null.
185 				// 0: waiting on initial timestamp
186 				// 1: waiting on lag response
187 				// 2: ready for starting
188 	}
189 	NetClient[MAX_NET] clients;
190 	int numberOfClients;
191 
192 	int maxLatency;
193 
194 
195 	// For being a network client
196 	TCPsocket clientsock;
197 
198 	// All done.
199 
200 	void beginServing(){
201 		TCPsocket serversock;
202 
203 		socketset = SDLNet_AllocSocketSet(MAX_NET+1);
204 		if(socketset is null)
205 			throw new Exception("AllocSocketSet");
206 		scope(failure)
207 			SDLNet_FreeSocketSet(socketset);
208 
209 		IPaddress serverIP;
210 
211 		SDLNet_ResolveHost(&serverIP, null, NET_PORT);
212 		serversock = SDLNet_TCP_Open(&serverIP);
213 		if(serversock is null)
214 			throw new Exception("Server sock");
215 		scope(exit)
216 			SDLNet_TCP_Close(serversock);
217 
218 		if(SDLNet_AddSocket(socketset, cast(SDLNet_GenericSocket) serversock) < 0)
219 			throw new Exception("addsocket");
220 		scope(exit)
221 			SDLNet_DelSocket(socketset, cast(SDLNet_GenericSocket) serversock);
222 
223 		writefln("Waiting for players to join the game.\nPress enter when everyone has joined to start the game.");
224 
225 
226 		uint randomSeed = random.rand();
227 		srand(randomSeed);
228 		writefln("TEST: %d", randomNumber(0, 100));
229 
230 
231 		bool loopingNeeeded = false; // potential FIXME for later
232 		while(!kbhit() || loopingNeeeded){
233 			int n = SDLNet_CheckSockets(socketset, 10);
234 			if(n < 0)
235 				throw new Exception("Check sockets");
236 			if(n == 0)
237 				continue;
238 			if(SDLNet_SocketReady(serversock)){
239 				TCPsocket newsock;
240 
241 				newsock = SDLNet_TCP_Accept(serversock);
242 				if(newsock is null){
243 					continue;
244 				}
245 
246 
247 				SDLNet_AddSocket(socketset, cast(SDLNet_GenericSocket) newsock);
248 
249 				// accept the connection
250 
251 				writefln("New player:");
252 
253 				clients[numberOfClients].sock = newsock;
254 				numberOfClients++;
255 			}
256 
257 			// Check the rest of our sockets for data
258 
259 			for(int a = 0; a < numberOfClients; a++){
260 				if(SDLNet_SocketReady(clients[a].sock)){
261 					byte[16] data; // this needs to be EXACTLY the size we are actually going to get.
262 					if(SDLNet_TCP_Recv(clients[a].sock, data.ptr, 16) <= 0){
263 						// the connection was closed
264 						for(int b = a; b < numberOfClients; b++)
265 							clients[b] = clients[b+1];
266 						clients[numberOfClients] = NetClient.init;
267 						numberOfClients--;
268 						a--;
269 						continue;
270 					}
271 
272 					// And handle the data here.
273 					switch(clients[a].state){
274 					  default: assert(0);
275 					  case 0: // this is the timestamp and stuff
276 						int ts = SDLNet_Read32(data.ptr);
277 						clients[a].numPlayers = SDLNet_Read32(data.ptr+4);
278 
279 						int startingPlayer = numberOfPlayers;
280 						numberOfPlayers+= clients[a].numPlayers;
281 						clients[a].startingPlayer = startingPlayer;
282 
283 						SDLNet_Write32(ts, data.ptr);
284 						SDLNet_Write32(SDL_GetTicks(), data.ptr+4);
285 						SDLNet_Write32(randomSeed, data.ptr+8);
286 						SDLNet_Write32(startingPlayer, data.ptr+12);
287 
288 						if(SDLNet_TCP_Send(clients[a].sock, data.ptr, 16) <= 0)
289 							throw new Exception("TCP send");
290 
291 						clients[a].state++;
292 					  break;
293 					  case 1: // this is telling of the latency
294 
295 						clients[a].latency = SDLNet_Read32(data.ptr);
296 
297 						if(clients[a].latency > maxLatency)
298 							maxLatency = clients[a].latency;
299 
300 						writefln("Latency: %d", clients[a].latency);
301 
302 						clients[a].state++;
303 					  break;
304 					  case 2:
305 					  	throw new Exception("unknown data came in");
306 					}
307 				}
308 			}
309 		}
310 
311 
312 
313 		isServer = true;
314 	}
315 
316 	void connectTo(in char[] whom){
317 		socketset = SDLNet_AllocSocketSet(1);
318 		if(socketset is null)
319 			throw new Exception("AllocSocketSet");
320 		scope(failure)
321 			SDLNet_FreeSocketSet(socketset);
322 
323 		IPaddress ip;
324 
325 		if(SDLNet_ResolveHost(&ip, (whom~"\0").ptr, NET_PORT) == -1)
326 			throw new Exception("Resolve host");
327 
328 		clientsock = SDLNet_TCP_Open(&ip);
329 
330 		if(clientsock is null)
331 			throw new Exception("open socket");
332 
333 		if(SDLNet_AddSocket(socketset, cast(SDLNet_GenericSocket) clientsock) < 0)
334 			throw new Exception("addsocket");
335 		scope(failure) SDLNet_DelSocket(socketset, cast(SDLNet_GenericSocket) clientsock);
336 
337 		byte[16] data;
338 
339 		int timeStamp = SDL_GetTicks();
340 		SDLNet_Write32(timeStamp, data.ptr);
341 		SDLNet_Write32(numberOfLocalPlayers, data.ptr+4);
342 		if(SDLNet_TCP_Send(clientsock, data.ptr, 16) <= 0)
343 			throw new Exception("TCP send");
344 
345 		if(SDLNet_TCP_Recv(clientsock, data.ptr, 16) <= 0)
346 			throw new Exception("TCP recv");
347 
348 		int receivedTimeStamp = SDLNet_Read32(data.ptr);
349 		int serverTimeStamp = SDLNet_Read32(data.ptr+4);
350 		uint randomSeed = SDLNet_Read32(data.ptr+8);
351 		firstLocalPlayer = SDLNet_Read32(data.ptr+12);
352 		writefln("First local player = %d", firstLocalPlayer);
353 
354 		srand(randomSeed);
355 		writefln("TEST: %d", randomNumber(0, 100));
356 
357 		int ourLatency = SDL_GetTicks() - receivedTimeStamp;
358 
359 		SDLNet_Write32(ourLatency, data.ptr);
360 		SDLNet_Write32(serverTimeStamp, data.ptr+4);
361 
362 		if(SDLNet_TCP_Send(clientsock, data.ptr, 16) <= 0)
363 			throw new Exception("TCP send 2");
364 
365 		waiting = true;
366 	}
367 
368 	// This should be called AFTER most initialization, but BEFORE you initialize your players; you don't
369 	// know the number of players for sure until this call returns.
370 	void waitOnNetwork(){
371 		if(!net)
372 			return;
373 
374 		if(isServer){
375 
376 			// Calculate when to start, then send the signal to everyone.
377 			int desiredLag = cast(int) round(cast(float) maxLatency / msPerTick) + 2;//1;
378 			lag = desiredLag;
379 			writefln("Lag = %d", lag);
380 
381 			for(int a = 0; a < numberOfClients; a++){
382 				int delay = maxLatency - clients[a].latency;
383 
384 				byte[16] data;
385 
386 				// FIXME: We need to send all player data here!
387 
388 				SDLNet_Write32(desiredLag, data.ptr);
389 				SDLNet_Write32(delay, data.ptr + 4);
390 				SDLNet_Write32(numberOfPlayers, data.ptr + 8);
391 
392 
393 				if(SDLNet_TCP_Send(clients[a].sock, data.ptr, 16) < 16)
394 					throw new Exception("Sending failed");
395 			}
396 
397 			SDL_Delay(maxLatency); // After waiting for the signal to reach everyone, we can now begin the game!
398 			return;
399 		} else {
400 			// handle the data
401 			byte[16] data;
402 
403 			// FIXME: we need to read special per game player data here!
404 
405 			if(SDLNet_TCP_Recv(clientsock, data.ptr, 16) <= 0)
406 				throw new Exception("Server closed the connection");
407 
408 			int lagAmount   = SDLNet_Read32(data.ptr);
409 			int delayAmount = SDLNet_Read32(data.ptr + 4);
410 			numberOfPlayers = SDLNet_Read32(data.ptr+8);
411 
412 
413 			lag = lagAmount;
414 			writefln("Lag = %d", lag);
415 
416 			SDL_Delay(delayAmount);
417 			// And finally, we're done, and the game can begin.
418 			waiting = false;
419 			return;
420 		}
421 	}
422 
423 	SDLNet_SocketSet socketset;
424 
425 	int msPerTick;
426 
427 	int numberOfPlayers;
428 	int numberOfLocalPlayers;
429 	int firstLocalPlayer;
430 
431   public:
432 	int getNumberOfPlayers(){ // good for main looping and controls and such
433 		return numberOfPlayers;
434 	}
435 
436 	// returns < 0 if the player is not local
437 	int getLocalPlayerNumber(int absolutePlayerNumber){ // useful for split screening
438 		if(absolutePlayerNumber >= firstLocalPlayer && absolutePlayerNumber < firstLocalPlayer + numberOfLocalPlayers)
439 			return  absolutePlayerNumber - firstLocalPlayer;
440 
441 		return -1;
442 	}
443 
444 	int getNumberOfLocalPlayers(){ // only useful for deciding how to split the screen
445 		return numberOfLocalPlayers;
446 	}
447 
448 	int getFirstLocalPlayerNumber(){
449 		return firstLocalPlayer;
450 	}
451 
452 
453 	this(int graphics = NoVideo, bool sound = false, int timerClick = 0, int numOfLocalPlayers = 1, in char[] network = null){
454 		bool joystick = true;
455 
456 		int init = 0;
457 
458 		numberOfPlayers = numberOfLocalPlayers = numOfLocalPlayers;
459 
460 
461 
462 		if(graphics)
463 			init |= SDL_INIT_VIDEO;
464 		if(timerClick)
465 			init |= SDL_INIT_TIMER;
466 		if(sound)
467 			init |= SDL_INIT_AUDIO;
468 		if(joystick)
469 			init |= SDL_INIT_JOYSTICK;
470 
471 		msPerTick = timerClick;
472 
473 		if(SDL_Init(init) == -1 ){
474 			throw new Exception("SDL_Init");
475 		}
476 		scope(failure) SDL_Quit();
477 
478 
479 // SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"), NULL);
480 
481 
482 		if(network !is null){
483 			if(SDLNet_Init() < 0)
484 				throw new Exception("SDLNet_Init");
485 			scope(failure) SDLNet_Quit();
486 
487 
488 			if(network == "SERVER")
489 				beginServing();
490 			else
491 				connectTo(network);
492 
493 			net = true;
494 		}
495 
496 		switch(graphics & ~32){
497 			case NoVideo:
498 				screen = null;
499 			break;
500 			case Video1024x768:
501 				screen = new Screen(1024, 768, 24, true, (graphics & 32) ? true : false);
502 			break;
503 			case Video640x480:
504 				screen = new Screen(640, 480);
505 			break;
506 			case Video800x600:
507 				screen = new Screen(800, 600);
508 			break;
509 			case Video512x512:
510 				screen = new Screen(512, 512);
511 			break;
512 			case Video320x200:
513 				screen = new Screen(320, 200);
514 			break;
515 			default:
516 				throw new Exception("Invalid screen type");
517 		}
518 		scope(failure) delete screen;
519 
520 		if(timerClick)
521 			SDL_AddTimer(timerClick, &tcallback, null);
522 
523 		if(sound)
524 			audio = new Audio;
525 		else
526 			audio = new Audio(false);
527 
528 		scope(failure) delete audio;
529 
530 		if(joystick && SDL_NumJoysticks() > 0){
531 			SDL_JoystickEventState(SDL_ENABLE);
532 			for(int a = 0; a < SDL_NumJoysticks(); a++)
533 				joyStick[a] = SDL_JoystickOpen(a);
534 		}
535 		else
536 			joyStick[0] = null;
537 
538 		scope(failure){	for(int a = 0; a < 16; a++) if(joyStick[a]) SDL_JoystickClose(joyStick[a]); }
539 
540 
541 		//SDL_ShowCursor(SDL_DISABLE); // FIXME: make this a call
542 
543 //***********************************************************************
544 	// FIXME: it should load controller maps from a config file
545 
546 		// My playstation controller
547 		for(int a = 0; a < 13; a++)
548 			mapJoystickKeyToButton(a, cast(Buttons) a, 0, firstLocalPlayer);
549 
550 		leftStickXAxis[0] = 0;
551 		leftStickYAxis[0] = 1;
552 		dpadXAxis[0] = 4;
553 		dpadYAxis[0] = 5;
554 		rightStickXAxis[0] = 2;
555 		rightStickYAxis[0] = 3;
556 		leftTriggerAxis[0] = -1;
557 		rightTriggerAxis[0] = -1;
558 
559 		// 360 controllers
560 		for(int b = 1; b < 16; b++){
561 			mapJoystickKeyToButton(1, circle, b, firstLocalPlayer + b);
562 			mapJoystickKeyToButton(0, cross, b, firstLocalPlayer + b);
563 			mapJoystickKeyToButton(4, square, b, firstLocalPlayer + b);
564 			mapJoystickKeyToButton(3, triangle, b, firstLocalPlayer + b);
565 
566 			mapJoystickKeyToButton(16, select, b, firstLocalPlayer + b);
567 			mapJoystickKeyToButton(8, start, b, firstLocalPlayer + b);
568 
569 			mapJoystickKeyToButton(10, L3, b, firstLocalPlayer + b);
570 			mapJoystickKeyToButton(11, R3, b, firstLocalPlayer + b);
571 
572 			mapJoystickKeyToButton(6, L1, b, firstLocalPlayer + b);
573 			mapJoystickKeyToButton(7, R1, b, firstLocalPlayer + b);
574 
575 			mapJoystickKeyToButton(9, special, b, firstLocalPlayer + b);
576 
577 			mapJoystickKeyToButton(15, left, b, firstLocalPlayer + b);
578 			mapJoystickKeyToButton(12, up, b, firstLocalPlayer + b);
579 			mapJoystickKeyToButton(13, right, b, firstLocalPlayer + b);
580 			mapJoystickKeyToButton(14, down, b, firstLocalPlayer + b);
581 
582 			leftStickXAxis[b] = 0;
583 			dpadXAxis[b] = -1;
584 			leftStickYAxis[b] = 1;
585 			dpadYAxis[b] = -1;
586 			rightStickXAxis[b] = 4;
587 			rightStickYAxis[b] = 3;
588 			leftTriggerAxis[b] = 2;
589 			rightTriggerAxis[b] = 5;
590 		}
591 
592 
593 
594 		// Some sane keyboard defaults
595 
596 		keyboardMap['s'] = InputMap(circle, firstLocalPlayer);
597 		keyboardMap['x'] = InputMap(cross, firstLocalPlayer);
598 		keyboardMap['w'] = InputMap(triangle, firstLocalPlayer);
599 		keyboardMap['a'] = InputMap(square, firstLocalPlayer);
600 		keyboardMap[' '] = InputMap(circle, firstLocalPlayer);
601 		keyboardMap['d'] = InputMap(L1, firstLocalPlayer);
602 		keyboardMap['f'] = InputMap(R1, firstLocalPlayer);
603 		keyboardMap['e'] = InputMap(L2, firstLocalPlayer);
604 		keyboardMap['r'] = InputMap(R2, firstLocalPlayer);
605 		keyboardMap['c'] = InputMap(L3, firstLocalPlayer);
606 		keyboardMap['v'] = InputMap(R3, firstLocalPlayer);
607 		keyboardMap['['] = InputMap(start, firstLocalPlayer);
608 		keyboardMap[']'] = InputMap(select, firstLocalPlayer);
609 		keyboardMap['='] = InputMap(special, firstLocalPlayer);
610 
611 		keyboardMap[SDLK_UP] = InputMap(up, firstLocalPlayer);
612 		keyboardMap[SDLK_DOWN] = InputMap(down, firstLocalPlayer);
613 		keyboardMap[SDLK_LEFT] = InputMap(left, firstLocalPlayer);
614 		keyboardMap[SDLK_RIGHT] = InputMap(right, firstLocalPlayer);
615 
616 		keyboardMap['k'] = InputMap(up, firstLocalPlayer);
617 		keyboardMap['j'] = InputMap(down, firstLocalPlayer);
618 		keyboardMap['h'] = InputMap(left, firstLocalPlayer);
619 		keyboardMap['l'] = InputMap(right, firstLocalPlayer);
620 
621 
622 
623 	}
624 
625 	void moveMouse(Point pos){
626 		SDL_WarpMouse(cast(ushort) pos.x, cast(ushort) pos.y);
627 
628 	}
629 
630 	bool capturedInput = false;
631 
632 	void captureInput(){
633 		if(capturedInput)
634 			return;
635 		SDL_WM_GrabInput(1);
636 		capturedInput = true;
637 	}
638 
639 	void unCaptureInput(){
640 		if(!capturedInput)
641 			return;
642 		SDL_WM_GrabInput(0);
643 		capturedInput = false;
644 	}
645 
646 	~this(){
647 		unCaptureInput();
648 		if(net){
649 			SDLNet_FreeSocketSet(socketset);
650 			SDLNet_Quit();
651 		}
652 		for(int a = 0; a < 16; a++)
653 		if(joyStick[a])
654 			SDL_JoystickClose(joyStick[a]);
655 		delete audio;
656 		delete screen;
657 
658 		foreach(a; objs)
659 			if(a !is null)
660 				delete a;
661 		SDL_Quit();
662 	}
663 
664 	void run(){
665 		eventLoop();
666 	}
667 
668 	void setTitle(in char[] title){
669 		SDL_WM_SetCaption((title~"\0").ptr, null);
670 	}
671 
672 	bool buttonWasPressed(Buttons button, int which = 0){
673 		if(!buttonsChecked[button][which] && buttonsDown[button][which]){
674 			buttonsChecked[button][which] = true;
675 			return true;
676 		}
677 		return false;
678 	}
679 
680 	bool buttonIsDown(Buttons button, int which = 0){
681 		if(button < NUM_BUTTONS && button >= 0)
682 			return buttonsDown[button][which];
683 		return false;
684 	}
685 
686 	bool keyWasPressed(int button){
687 		if(button < 400 && button >= 0)
688 		if(!keysChecked[button] && keysDown[button]){
689 			keysChecked[button] = true;
690 			return true;
691 		}
692 		return false;
693 	}
694 
695 	bool keyIsDown(int button){
696 		if(button < 400 && button >= 0)
697 			return keysDown[button];
698 		assert(0);
699 	}
700 
701 	int getStickX(int stick, int which = 0){
702 		if( stick >= 0 && stick < 2)
703 			return stickX[stick][which];
704 		else
705 			return 0;
706 	}
707 
708 	int getStickY(int stick, int which = 0){
709 		if( stick >= 0 && stick < 2)
710 			return stickY[stick][which];
711 		else
712 			return 0;
713 	}
714 
715 	int getMouseX(){
716 		return mouseX;
717 	}
718 
719 	int getMouseY(){
720 		return mouseY;
721 	}
722 
723 	int getMouseChangeX(){
724 		int a = mousedx;
725 		mousedx = 0;
726 		return a;
727 	}
728 
729 	int getMouseChangeY(){
730 		int a = mousedy;
731 		mousedy = 0;
732 		return a;
733 	}
734 
735 	bool mouseHasMoved(){
736 		return (getMouseChangeY != 0 || getMouseChangeX != 0);
737 	}
738 
739 	bool mouseButtonWasPressed(int button){
740 		if(!mouseButtonsChecked[button] && mouseButtonsDown[button]){
741 			mouseButtonsChecked[button] = true;
742 			return true;
743 		}
744 		return false;
745 	}
746 
747 	bool mouseButtonIsDown(int button){
748 		if(button < 8 && button >= 0)
749 			return mouseButtonsDown[button];
750 		return false;
751 	}
752 
753 	Point mouseLocation(){
754 		return XY(mouseX, mouseY);
755 	}
756 
757 	void quit(){
758 		wantToQuit = true;
759 	}
760 
761 	bool isAltDown(){
762 		return (SDL_GetModState() & KMOD_ALT) ? true : false;
763 	}
764 	bool isControlDown(){
765 		return  (SDL_GetModState() & KMOD_CTRL) ? true : false;
766 	}
767 	bool isShiftDown(){
768 		return  (SDL_GetModState() & KMOD_SHIFT) ? true : false;
769 	}
770 
771   protected:
772 	const int BUTTONDOWN = 0;
773 	const int BUTTONUP   = 1;
774 	const int MOTION     = 2;
775 
776 	void keyEvent(int type, int keyCode, int character, int modifiers){
777 		defaultKeyEvent(type, keyCode, character, modifiers);
778 	}
779 
780 	void defaultKeyEvent(int type, int keyCode, int character, int modifiers){
781 		if(character == 'q' || keyCode == 'q')
782 			quit();
783 		if(type == BUTTONUP && keyCode == SDLK_F3){
784 			if(capturedInput)
785 				unCaptureInput();
786 			else
787 				captureInput();
788 		}
789 	}
790 
791 	void mouseEvent(int type, int x, int y, int xrel, int yrel, int button, int flags){
792 		defaultMouseEvent(type, x, y, xrel, yrel, button, flags);
793 	}
794 
795 	final void defaultMouseEvent(int type, int x, int y, int xrel, int yrel, int button, int flags){
796 
797 	}
798 
799 	void joystickEvent(int type, int whichStick, int button, int state){
800 		defaultJoystickEvent(type, whichStick, button, state);
801 	}
802 
803 	final void defaultJoystickEvent(int type, int whichStick, int button, int state){
804 
805 	}
806 
807 	bool quitEvent(){
808 		return true;
809 	}
810 
811 	void timerEvent(){
812 
813 		// Need to add network receives and the lag timer loops
814 		if(net)
815 			getNetworkControllerData();
816 
817 		// do we actually need to lag here? hmmm.....
818 
819 		globalTimer++;
820 		foreach(a; objs){//copy){
821 			if(a is null)
822 				continue;
823 			a.frame();
824 		}
825 
826 		if(lag)
827 			updateControllers();
828 
829 	}
830 
831 	public Screen screen;
832 	public Audio audio;
833 
834   private:
835 
836 	bool net;
837 
838 
839 	SDL_Joystick*[16] joyStick;
840 		struct InputMap{
841 			int button; // or direction
842 			int which; // which player
843 		}
844 	InputMap[int] keyboardMap;
845 	InputMap[int][16] joystickMap; // joystickMap[which][button] = translated val
846 
847 	int[16] leftStickXAxis;
848 	int[16] dpadXAxis;
849 	int[16] leftStickYAxis;
850 	int[16] dpadYAxis;
851 	int[16] rightStickXAxis;
852 	int[16] rightStickYAxis;
853 	int[16] leftTriggerAxis;
854 	int[16] rightTriggerAxis;
855 
856 
857 
858 
859 	bool[400] keysDown;
860 	bool[400] keysChecked;
861 
862 	bool wantToQuit;
863 
864 	bool[16][NUM_BUTTONS] buttonsDown;
865 	bool[16][NUM_BUTTONS] buttonsChecked;
866 
867 	const int LAG_QUEUE_SIZE = 10;
868 	// This lag is used for network games. It sends you old data until the lag time is up,
869 	// to try and keep all the players synchronized.
870 	int[LAG_QUEUE_SIZE][16][NUM_BUTTONS] buttonLagRemaining;
871 
872 	// This way we can queue up activities happening while the lag is waiting
873 	int[16][NUM_BUTTONS] buttonLagQueueStart;
874 	int[16][NUM_BUTTONS] buttonLagQueueEnd;
875 	int[16][NUM_BUTTONS] buttonLagQueueLength;
876 
877 	// These store what the state was before the lag began; it is what is returned while
878 	// waiting on the lag to complete
879 	bool[LAG_QUEUE_SIZE][16][NUM_BUTTONS] lagbuttonsDown;
880 
881 
882 
883 	int[16][3] stickX;
884 	int[16][3] stickY;
885 
886 	bool[8] mouseButtonsDown;
887 	bool[8] mouseButtonsChecked;
888 	const int LEFT = SDL_BUTTON_LEFT;//1;
889 	const int MIDDLE = SDL_BUTTON_MIDDLE;//2;
890 	const int RIGHT = SDL_BUTTON_RIGHT;//3;
891 	const int SCROLL_UP = 4;
892 	const int SCROLL_DOWN = 5;
893 
894 	int mouseX;
895 	int mouseY;
896 	int mousedx;
897 	int mousedy;
898 
899 	bool altDown;
900 	bool controlDown;
901 	bool shiftDown;
902 
903 	void mapJoystickKeyToButton(int a, Buttons b, int whichJoystick, int whichPlayer){
904 		if(b > NUM_BUTTONS)
905 			return;
906 		joystickMap[whichJoystick][a] = InputMap(cast(int) b, whichPlayer);
907 	}
908 
909 	/*
910 		How does this work?
911 
912 		when and local are the fancy ones.
913 
914 		Maybe when should always be globalTimer + 1. This way, you have a local wait of 1 frame
915 		and the remote ones are set to go off one frame later, which gives them time to get down the wire.
916 
917 		I think that works.
918 
919 	*/
920 
921 
922 	uint lag = 0; // should not be > 10 XXX
923 
924 	void getNetworkControllerData(){
925 		int type, when, which, button;
926 
927 		if(!net) return;
928 
929 			int n = SDLNet_CheckSockets(socketset, 0); // timeout of 1000 might be good too
930 			if(n < 0)
931 				throw new Exception("Check sockets");
932 			if(n == 0)
933 				return;
934 
935 			if(isServer){
936 			for(int a = 0; a < numberOfClients; a++){
937 				if(SDLNet_SocketReady(clients[a].sock)){
938 					byte[16] data;
939 					if(SDLNet_TCP_Recv(clients[a].sock, data.ptr, 16) <= 0){
940 						throw new Exception("someone closed");
941 					}
942 
943 					type = SDLNet_Read32(data.ptr);
944 					when = SDLNet_Read32(data.ptr+4);
945 					which = SDLNet_Read32(data.ptr+8);
946 					button = SDLNet_Read32(data.ptr+12);
947 
948 					changeButtonState(cast(Buttons) button, type == 0 ? true : false, which, when);
949 
950 					// don't forget to forward the data to everyone else
951 					for(int b = 0; b< numberOfClients; b++)
952 						if(b != a)
953 						if(SDLNet_TCP_Send(clients[b].sock, data.ptr, 16) < 16)
954 							throw new Exception("network send failure");
955 
956 				}
957 			}
958 			} else if(SDLNet_SocketReady(clientsock)) {
959 				byte[16] data;
960 				if(SDLNet_TCP_Recv(clientsock, data.ptr, 16) <= 0){
961 					throw new Exception("connection closed");
962 				}
963 				type = SDLNet_Read32(data.ptr);
964 				when = SDLNet_Read32(data.ptr+4);
965 				which = SDLNet_Read32(data.ptr+8);
966 				button = SDLNet_Read32(data.ptr+12);
967 
968 				changeButtonState(cast(Buttons) button, type == 0 ? true : false, which, when);
969 			}
970 
971 
972 	}
973 
974 	void changeButtonState(Buttons button, bool type, int which, uint when, bool sendToNet = false){
975 	if(when  <= lag)
976 		return;
977 		if(when > globalTimer){
978 			lagbuttonsDown[button][which][buttonLagQueueEnd[button][which]] = type;
979 			buttonLagRemaining[button][which][buttonLagQueueEnd[button][which]] = when - globalTimer;
980 
981 			buttonLagQueueLength[button][which]++;
982 			buttonLagQueueEnd[button][which]++;
983 			if(buttonLagQueueEnd[button][which] == LAG_QUEUE_SIZE)
984 				buttonLagQueueEnd[button][which] = 0;
985 		} else {
986 			if(when < globalTimer)
987 				throw new Exception(immutableString("Impossible control timing"));//  " ~ convToString(when) ~ " @ " ~ convToString(globalTimer)));
988 			buttonsDown[button][which] = type;
989 			buttonsChecked[button][which] = false;
990 		}
991 
992 		if(net && sendToNet){
993 			byte[16] data;
994 			SDLNet_Write32(type ? 0 : 1, data.ptr);
995 			SDLNet_Write32(when, data.ptr+4);
996 			SDLNet_Write32(which, data.ptr+8);
997 			SDLNet_Write32(button, data.ptr+12);
998 			if(isServer){
999 				for(int a = 0; a< numberOfClients; a++)
1000 					if(SDLNet_TCP_Send(clients[a].sock, data.ptr, 16) < 16)
1001 						throw new Exception("network send failure");
1002 
1003 			} else {
1004 				if(SDLNet_TCP_Send(clientsock, data.ptr, 16) < 16)
1005 					throw new Exception("network send failure");
1006 			}
1007 		}
1008 	}
1009 
1010 	void updateControllers(){
1011 		for(int a = 0; a < 16; a++){ // FIXME: should be changed to number of players
1012 			for(int b = 0; b < NUM_BUTTONS; b++)
1013 				for(int co = 0, q = buttonLagQueueStart[b][a]; co < buttonLagQueueLength[b][a]; q++, co++){
1014 				if(q == LAG_QUEUE_SIZE)
1015 					q = 0;
1016 				if(buttonLagRemaining[b][a][q]){
1017 					buttonLagRemaining[b][a][q]--;
1018 					if(!buttonLagRemaining[b][a][q]){
1019 						changeButtonState(cast(Buttons) b, lagbuttonsDown[b][a][q], a, globalTimer);
1020 						buttonLagQueueStart[b][a]++;
1021 						buttonLagQueueLength[b][a]--;
1022 						if(buttonLagQueueStart[b][a] == LAG_QUEUE_SIZE)
1023 							buttonLagQueueStart[b][a] = 0;
1024 					}
1025 				}
1026 				}
1027 		}
1028 	}
1029 
1030 	int eventLoop(){
1031 		SDL_Event event;
1032 	  while(SDL_WaitEvent(&event) >= 0 && !wantToQuit){
1033 	    switch(event.type){
1034 		case SDL_KEYUP:
1035 		case SDL_KEYDOWN:
1036 			bool type = event.key.type == SDL_KEYDOWN ? true : false;
1037 				if(event.key.keysym.sym in keyboardMap){
1038 					int button = keyboardMap[event.key.keysym.sym].button;
1039 					int which = keyboardMap[event.key.keysym.sym].which;
1040 					changeButtonState(cast(Buttons) button, type, which, globalTimer + lag, true);
1041 				}
1042 
1043 			if(event.key.keysym.sym < 400){
1044 				keysDown[event.key.keysym.sym] = (event.key.type == SDL_KEYDOWN) ? true : false;
1045 				keysChecked[event.key.keysym.sym] = false;
1046 			}
1047 
1048 
1049 			keyEvent(
1050 				event.key.type == SDL_KEYDOWN ? BUTTONDOWN : BUTTONUP,
1051 				event.key.keysym.sym,
1052 				event.key.keysym.unicode,
1053 				event.key.keysym.mod
1054 			);
1055 		break;
1056 		case SDL_JOYAXISMOTION:
1057 		// the things here are to avoid little changes around the center if the stick isn't perfect
1058 		if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) || event.jaxis.value == 0){
1059 			int stick;
1060 			if(event.jaxis.axis >= 0 && event.jaxis.axis < 6)
1061 
1062 				stick = event.jaxis.axis;
1063 			else
1064 				break;
1065 
1066 			int which = event.jaxis.which;
1067 			if(stick == leftStickXAxis[which] || stick == dpadXAxis[which]){
1068 				changeButtonState(left, event.jaxis.value < -28000, which, globalTimer + lag, true);
1069 				changeButtonState(right, event.jaxis.value > 28000, which, globalTimer + lag, true);
1070 
1071 				stickX[0][which] = event.jaxis.value;
1072 			}
1073 			if(stick == leftStickYAxis[which] || stick == dpadYAxis[which]){
1074 				changeButtonState(up, event.jaxis.value < -28000, which, globalTimer + lag, true);
1075 				changeButtonState(down, event.jaxis.value > 28000, which, globalTimer + lag, true);
1076 
1077 				stickY[0][which] = event.jaxis.value;
1078 			}
1079 			if(stick == rightStickXAxis[which]){
1080 				stickX[1][which] = event.jaxis.value;
1081 				changeButtonState(left2, event.jaxis.value < -28000, which, globalTimer + lag, true);
1082 				changeButtonState(right2, event.jaxis.value > 28000, which, globalTimer + lag, true);
1083 			}
1084 			if(stick == rightStickYAxis[which]){
1085 				stickY[1][which] = event.jaxis.value;
1086 				changeButtonState(up2, event.jaxis.value < -28000, which, globalTimer + lag, true);
1087 				changeButtonState(down2, event.jaxis.value > 28000, which, globalTimer + lag, true);
1088 			}
1089 			// x-box 360 controller stuff
1090 			if(stick == leftTriggerAxis[which]){
1091 				stickX[2][which] = event.jaxis.value;
1092 
1093 				changeButtonState(L2, event.jaxis.value > 32000, which, globalTimer + lag, true);
1094 			}
1095 			if(stick == rightTriggerAxis[which]){
1096 				stickY[2][which] = event.jaxis.value;
1097 				changeButtonState(R2, event.jaxis.value > 32000, which, globalTimer + lag, true);
1098 			}
1099 
1100 
1101 			joystickEvent(
1102 				MOTION,
1103 				event.jaxis.which,
1104 				event.jaxis.axis,
1105 				event.jaxis.value
1106 			);
1107 		}
1108 		break;
1109 		case SDL_JOYHATMOTION:
1110 		/+
1111 			joystickEvent(
1112 				HATMOTION,
1113 				event.jhat.which,
1114 				event.jhat.hat,
1115 				event.jhat.value
1116 			);
1117 		+/
1118 		break;
1119 		case SDL_JOYBUTTONDOWN:
1120 		case SDL_JOYBUTTONUP:
1121 			if(event.jbutton.button in joystickMap[event.jbutton.which]){
1122 				int which = joystickMap[event.jbutton.which][event.jbutton.button].which;
1123 				int button = joystickMap[event.jbutton.which][event.jbutton.button].button;
1124 
1125 				changeButtonState(cast(Buttons) button, event.jbutton.type == SDL_JOYBUTTONDOWN ? true : false, which, globalTimer + lag, true);
1126 			}
1127 
1128 			joystickEvent(
1129 				event.jbutton.type == SDL_JOYBUTTONDOWN ? BUTTONDOWN : BUTTONUP,
1130 				event.jbutton.which,
1131 				event.jbutton.button,
1132 				event.jbutton.state
1133 			);
1134 		break;
1135 
1136 		case SDL_MOUSEBUTTONDOWN:
1137 		case SDL_MOUSEBUTTONUP:
1138 			mouseButtonsDown[event.button.button] = event.button.type == SDL_MOUSEBUTTONDOWN ? true : false;
1139 			mouseButtonsChecked[event.button.button] = false;
1140 			mouseEvent(
1141 				event.button.type == SDL_MOUSEBUTTONDOWN ? BUTTONDOWN : BUTTONUP,
1142 				event.button.x,
1143 				event.button.y,
1144 				0,		// xrel
1145 				0,		// yrel
1146 				event.button.button,
1147 				0		//state
1148 			);
1149 		break;
1150 		case SDL_MOUSEMOTION:
1151 			mouseX = event.motion.x;
1152 			mouseY = event.motion.y;
1153 			mousedx += event.motion.xrel;
1154 			mousedy += event.motion.yrel;
1155 
1156 			mouseEvent(
1157 				MOTION,
1158 				event.motion.x,
1159 				event.motion.y,
1160 				event.motion.xrel,
1161 				event.motion.yrel,
1162 				0,
1163 				event.motion.state
1164 			);
1165 		break;
1166 
1167 		case SDL_USEREVENT:
1168 			timerEvent();
1169 		break;
1170 		case SDL_QUIT:
1171 			if(quitEvent() == true)
1172 				quit();
1173 		break;
1174 		default:
1175 	    }
1176 	  }
1177 		return 0;
1178 	}
1179 
1180 }
1181 extern(C){
1182 	Uint32 tcallback(Uint32 interval, void* param){
1183 		if(waiting)
1184 			return interval;
1185 		SDL_Event event;
1186 
1187 		event.type = SDL_USEREVENT;
1188 		event.user.code = 0;
1189 		event.user.data1 = null;
1190 		event.user.data2 = null;
1191 		SDL_PushEvent(&event);
1192 
1193 		return interval;
1194 	}
1195 }
1196 
1197 int SDLNet_SocketReady(void* sock) {
1198         SDLNet_GenericSocket s = cast(SDLNet_GenericSocket)sock;
1199         return sock != cast(TCPsocket)0 && s.ready;
1200 }
1201 
1202 
1203 
1204 Engine engine;
1205 
1206 bool buttonWasPressed(Engine.Buttons button, int which = 0){
1207 	return engine.buttonWasPressed(button, which);
1208 }
1209 
1210 bool buttonIsDown(Engine.Buttons button, int which = 0){
1211 	return engine.buttonIsDown(button, which);
1212 }
1213 /*
1214 bool directionWasPressed(Engine.Direction direction, int which = 0){
1215 	return engine.directionWasPressed(direction, which);
1216 }
1217 
1218 bool directionIsDown(Engine.Direction d, int which = 0){
1219 	return engine.directionIsDown(d, which);
1220 }
1221 */
1222 
1223 
1224 
1225 version(linux) {
1226 	version(D_Version2) {
1227 		import sys = core.sys.posix.sys.select;
1228 		version=CustomKbhit;
1229 
1230 		int kbhit()
1231 		{
1232 			sys.timeval tv;
1233 			sys.fd_set read_fd;
1234 
1235 			tv.tv_sec=0;
1236 			tv.tv_usec=0;
1237 			sys.FD_ZERO(&read_fd);
1238 			sys.FD_SET(0,&read_fd);
1239 
1240 			if(sys.select(1, &read_fd, null, null, &tv) == -1)
1241 				return 0;
1242 
1243 			if(sys.FD_ISSET(0,&read_fd))
1244 				return 1;
1245 
1246 			return 0;
1247 		}
1248 	}
1249 
1250 	// else, use kbhit.o from the C file
1251 }
1252 
1253 version(CustomKbhit) {} else
1254 	extern(C) bool kbhit();
1255 
1256 
1257