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