First cut of multi-player support
authorThomas Perl <thp@thpinfo.com>
Tue, 4 Aug 2009 23:27:10 +0000 (5 01:27 +0200)
committerThomas Perl <thp@thpinfo.com>
Tue, 4 Aug 2009 23:27:10 +0000 (5 01:27 +0200)
And we are proud of it :)

README.Multiplayer [new file with mode: 0644]
game.c
game.h
input.c
input.h
network.c
network.h
tennix.c
tennix.h

diff --git a/README.Multiplayer b/README.Multiplayer
new file mode 100644 (file)
index 0000000..035d9f0
--- /dev/null
@@ -0,0 +1,19 @@
+
+This is done using two computers, the first is the faster one
+and the second one is the slower one. If both computers are
+equally powerful, just chose the faster one randomly.
+
+On the faster computer, use:
+  ./tennix -n REMOTEHOST LOCALPORT REMOTEPORT -m
+
+On the slower computer, use:
+  ./tennix -n REMOTEHOST LOCALPORT REMOTEPORT
+
+So, if your faster computer is called "fast.lan" and the slower
+computer is called "slow.lan", you could use the following setup:
+
+  user@fast:~$ ./tennix -n slow.lan 1234 4321 -m
+  user@slow:~$ ./tennix -n fast.lan 4321 1234
+
+ -- thp, Wed, 05 Aug 2009 01:26:42 +0200
+
diff --git a/game.c b/game.c
index cd816ab..5b83a62 100644 (file)
--- a/game.c
+++ b/game.c
@@ -159,7 +159,7 @@ GameState* gamestate_load(const char* filename)
 }
 
 
-void gameloop(GameState *s) {
+void gameloop(GameState *s, TennixNet* c) {
     Uint32 ot = SDL_GetTicks();
     Uint32 nt;
     Uint32 dt = GAME_TICKS;
@@ -167,6 +167,7 @@ void gameloop(GameState *s) {
     Uint32 accumulator = 0;
     bool quit = false;
     int p;
+    int i=0;
     RenderState r = {
         SOUND_EVENT_NONE,
         REFEREE_COUNT,
@@ -178,6 +179,8 @@ void gameloop(GameState *s) {
         NULL,
     };
 
+    assert(c != NULL);
+
     /* Catch-up with existing sound events */
     r.sound_events = s->sound_events;
 
@@ -212,7 +215,15 @@ void gameloop(GameState *s) {
 
         while( accumulator >= dt) {
             step(s);
-            quit = handle_input(s);
+
+            network_receive(c);
+            network_get_gamestate(c, s);
+
+            if ((i++) % 10 == 0) {
+                network_send_state(c, s);
+            }
+
+            quit = handle_input(s, c);
             accumulator -= dt;
         }
 
@@ -241,9 +252,6 @@ void gameloop(GameState *s) {
 void step(GameState* s) {
     bool ground_event = false;
     int p;
-    fprintf(stderr, "ball(%f, %f, %f, %f, %f, %f)\n",
-            s->ball.x, s->ball.y, s->ball.z,
-            s->ball.move_x, s->ball.move_y, s->ball.move_z);
 
     s->ball.z += s->ball.move_z;
     if (!s->ball.inhibit_gravity) {
@@ -396,8 +404,9 @@ void step(GameState* s) {
     }
 }
 
-bool handle_input(GameState* s) {
+bool handle_input(GameState* s, TennixNet* c) {
     static NetworkGameState tmp;
+    static NetworkInputData net_input;
     Uint8* keys = NULL;
     int p;
 
@@ -410,10 +419,18 @@ bool handle_input(GameState* s) {
         net_unserialize_gamestate(&tmp, s);
     }
 
+    network_get_input(c, &net_input);
     if (s->winner == WINNER_NONE) {
         for (p=1; p<=MAXPLAYERS; p++) {
             if( PLAYER(s, p).type == PLAYER_TYPE_HUMAN) {
+                if (PLAYER(s, p).input->type == INPUT_TYPE_NETWORK) {
+                    memcpy(&(PLAYER(s, p).input->net), &net_input,
+                            sizeof(NetworkInputData));
+                }
                 input_human(s, p);
+                if (PLAYER(s, p).input->type != INPUT_TYPE_NETWORK) {
+                    network_send_input(c, &(PLAYER(s, p).input->net));
+                }
             } else {
                 input_ai(s, p);
             }
@@ -678,6 +695,9 @@ void input_human(GameState* s, int player) {
         }
         PLAYER(s, player).y += move_y;
         PLAYER(s, player).accelerate *= PLAYER_ACCEL_INCREASE;
+        if (PLAYER(s, player).accelerate > 190) {
+            PLAYER(s, player).accelerate = 190;
+        }
     } else {
         PLAYER(s, player).accelerate = PLAYER_ACCEL_DEFAULT;
     }
diff --git a/game.h b/game.h
index b76a36d..249d6e1 100644 (file)
--- a/game.h
+++ b/game.h
@@ -30,6 +30,7 @@
 #include "graphics.h"
 #include "input.h"
 
+
 #define SETS_TO_WIN 3
 
 #define MAXPLAYERS 2
 #  define BALL_STATES 4
 #endif
 
-typedef unsigned char bool;
-enum {
-    false,
-    true
-};
-
 typedef unsigned char referee_t;
 enum {
     REFEREE_NORMAL,
@@ -279,9 +274,9 @@ int gamestate_save(GameState* s, const char* filename);
 GameState* gamestate_load(const char* filename);
 
 /* Game module functions */
-void gameloop(GameState*);
+void gameloop(GameState*, TennixNet*);
 void step(GameState*);
-bool handle_input(GameState*);
+bool handle_input(GameState*, TennixNet*);
 void render(const GameState*, RenderState*);
 float get_move_y( GameState*, unsigned char);
 void input_human(GameState*, int);
diff --git a/input.c b/input.c
index 73e3069..9e6ce96 100644 (file)
--- a/input.c
+++ b/input.c
@@ -100,6 +100,12 @@ void init_input()
 #endif
     devices_count++;
 
+    /* network peer */
+    devices[devices_count].type = INPUT_TYPE_NETWORK;
+    devices[devices_count].icon = GR_INPUT_AI; /* FIXME - network icon! */
+    strcpy(devices[devices_count].name, "Network player");
+    devices_count++;
+
     /* joysticks */
     n = SDL_NumJoysticks();
     for (x=0; x<n && devices_count<MAX_INPUT_DEVICES; x++) {
@@ -144,6 +150,24 @@ void uninit_input()
     devices_count = 0;
 }
 
+Uint8
+input_pack_axis(float v)
+{
+    static const float min = -1.2;
+    static const float max = 1.2;
+    assert(v >= min && v < max);
+    return (Uint8)((1U<<7) * (v-min) / (max-min));
+}
+
+float
+input_unpack_axis(Uint8 v)
+{
+    static const float min = -1.2;
+    static const float max = 1.2;
+    assert(v < (1U<<7));
+    return v * (max-min) / (1U<<7) + min;
+}
+
 
 InputDevice* find_input_devices(unsigned int* count)
 {
@@ -188,21 +212,23 @@ const char* input_device_get_name(InputDevice* d)
 float input_device_get_axis(InputDevice* d, unsigned const char axis) {
     Uint8 *keystate;
     Uint8 mb;
+    Uint8 net_value;
+    float result = 0.0;
 
     SDL_PumpEvents();
 
     if (d->type == INPUT_TYPE_KEYBOARD) {
         keystate = SDL_GetKeyState(NULL);
         if (axis == INPUT_AXIS_X) {
-            return 1.0*keystate[d->right_key] + -1.0*keystate[d->left_key];
+            result =  1.0*keystate[d->right_key] + -1.0*keystate[d->left_key];
         } else {
-            return 1.0*keystate[d->down_key] + -1.0*keystate[d->up_key];
+            result = 1.0*keystate[d->down_key] + -1.0*keystate[d->up_key];
         }
     } else if (d->type == INPUT_TYPE_JOYSTICK) {
         if (axis == INPUT_AXIS_X) {
-            return JOYSTICK_PERCENTIZE(SDL_JoystickGetAxis(d->joystick, d->x_axis*2));
+            result = JOYSTICK_PERCENTIZE(SDL_JoystickGetAxis(d->joystick, d->x_axis*2));
         } else {
-            return JOYSTICK_PERCENTIZE(SDL_JoystickGetAxis(d->joystick, 1+d->y_axis*2));
+            result = JOYSTICK_PERCENTIZE(SDL_JoystickGetAxis(d->joystick, 1+d->y_axis*2));
         }
     } else if (d->type == INPUT_TYPE_MOUSE) {
         mb = SDL_GetMouseState(&d->mx, &d->my);
@@ -218,41 +244,66 @@ float input_device_get_axis(InputDevice* d, unsigned const char axis) {
         } else {
             if (fabsf(d->my - d->player_y) > PLAYER_MOVE_Y) {
                 if (d->my > d->player_y)  {
-                    return 1.0;
+                    result = 1.0;
                 } else if (d->my < d->player_y) {
-                    return -1.0;
+                    result = -1.0;
                 }
             }
         }
 #ifdef TENNIX_PYTHON
     } else if (d->type == INPUT_TYPE_AI_PYTHON) {
-        return tennixpy_bot_get_axis(d->py_bot, axis);
+        result = tennixpy_bot_get_axis(d->py_bot, axis);
 #endif
+    } else if (d->type == INPUT_TYPE_NETWORK) {
+        if (axis == INPUT_AXIS_X) {
+            result = input_unpack_axis(d->net.x);
+        } else if (axis == INPUT_AXIS_Y) {
+            result = input_unpack_axis(d->net.y);
+        }
     } else {
-        /* unimplemented */
+        assert(0/*unimplemented*/);
     }
-    return 0.0;
+
+    net_value = input_pack_axis(result);
+    if (axis == INPUT_AXIS_X) {
+        d->net.x = net_value;
+    } else if (axis == INPUT_AXIS_Y) {
+        d->net.y = net_value;
+    }
+
+    return result;
 }
 
 char input_device_get_key(InputDevice* d, unsigned const char key) {
     Uint8 mb;
+    char result = 0;
     SDL_PumpEvents();
 
     if (d->type == INPUT_TYPE_KEYBOARD) {
-        return SDL_GetKeyState(NULL)[d->input_keys[key]];
+        result = SDL_GetKeyState(NULL)[d->input_keys[key]];
     } else if (d->type == INPUT_TYPE_JOYSTICK) {
-        return SDL_JoystickGetButton(d->joystick, d->input_keys[key]);
+        result = SDL_JoystickGetButton(d->joystick, d->input_keys[key]);
     } else if (d->type == INPUT_TYPE_MOUSE) {
         mb = SDL_GetMouseState(NULL, NULL);
-        return (mb & d->input_keys[key]) != 0;
+        result = (mb & d->input_keys[key]) != 0;
 #ifdef TENNIX_PYTHON
     } else if (d->type == INPUT_TYPE_AI_PYTHON) {
-        return tennixpy_bot_get_key(d->py_bot, key);
+        result = tennixpy_bot_get_key(d->py_bot, key);
 #endif
+    } else if (d->type == INPUT_TYPE_NETWORK) {
+        result = (d->net.keys & (1<<key));
     } else {
-        /* unimplemented */
+        assert(0/*unimplemented*/);
     }
-    return 0;
+
+    /* Update the input device's NetworkInputData struct */
+    if(result) {
+        d->net.keys |= (1<<key);
+    } else {
+        d->net.keys &= ~(1<<key);
+    }
+
+    return result;
 }
 
 
diff --git a/input.h b/input.h
index 772f2e4..98eb471 100644 (file)
--- a/input.h
+++ b/input.h
@@ -43,6 +43,7 @@ enum {
     INPUT_TYPE_KEYBOARD,
     INPUT_TYPE_JOYSTICK,
     INPUT_TYPE_MOUSE,
+    INPUT_TYPE_NETWORK,
 #ifdef TENNIX_PYTHON
     INPUT_TYPE_AI_PYTHON,
 #endif
@@ -61,6 +62,18 @@ enum {
     INPUT_KEY_COUNT
 };
 
+enum {
+    NET_KEY_HIT     = 1 << INPUT_KEY_HIT,
+    NET_KEY_TOPSPIN = 1 << INPUT_KEY_TOPSPIN,
+    NET_KEY_SMASH   = 1 << INPUT_KEY_SMASH
+};
+
+typedef struct {
+    unsigned char x;
+    unsigned char y;
+    unsigned char keys; /* bitfield (0 = key pressed, 1 = not pressed) */
+} NetworkInputData;
+
 #define INPUT_DEVICE_NAME_MAX 100
 
 typedef struct {
@@ -87,6 +100,9 @@ typedef struct {
     int player_x;
     int player_y;
 
+    /* Network-specific items */
+    NetworkInputData net;
+
 #ifdef TENNIX_PYTHON
     /* Python-specific items */
     PyObject* py_bot_class;
index 0b5f271..84df73a 100644 (file)
--- a/network.c
+++ b/network.c
@@ -43,12 +43,128 @@ unpack_float(Uint32 v, float min, float max)
     return v * (max-min) / (1U<<31) + min;
 }
 
+void
+init_network()
+{
+    SDLNet_Init();
+}
+
+void
+uninit_network()
+{
+    SDLNet_Quit();
+}
+
+TennixNet*
+network_connect(const char* host, Uint16 local_port, Uint16 remote_port)
+{
+    TennixNet* connection = (TennixNet*)malloc(sizeof(TennixNet));
+    assert(connection != NULL);
+
+    fprintf(stderr, "ports: %d and %d\n", local_port, remote_port);
+
+    assert(SDLNet_ResolveHost(&(connection->peer), (const char*)host, 0) == 0);
+    connection->base_port_local = local_port;
+    connection->base_port_remote = remote_port;
+    connection->input_packet = SDLNet_AllocPacket(sizeof(NetworkInputData));
+    connection->state_packet = SDLNet_AllocPacket(sizeof(NetworkGameState));
+    connection->input_available = false;
+    connection->state_available = false;
+    connection->send_input_socket = SDLNet_UDP_Open(0);
+    connection->send_state_socket = SDLNet_UDP_Open(0);
+    connection->recv_input_socket = SDLNet_UDP_Open(connection->base_port_local);
+    connection->recv_state_socket = SDLNet_UDP_Open(connection->base_port_local+1);
+    connection->master = false;
+
+    return connection;
+}
+
+void
+network_disconnect(TennixNet* connection)
+{
+    if (connection != NULL)
+    {
+        SDLNet_UDP_Close(connection->send_input_socket);
+        SDLNet_UDP_Close(connection->send_state_socket);
+        SDLNet_UDP_Close(connection->recv_input_socket);
+        SDLNet_UDP_Close(connection->recv_state_socket);
+        SDLNet_FreePacket(connection->input_packet);
+        SDLNet_FreePacket(connection->state_packet);
+        free(connection);
+    }
+}
+
+void
+network_set_master(TennixNet* connection, bool master)
+{
+    connection->master = master;
+}
+
+void
+network_send_input(TennixNet* connection, NetworkInputData* src)
+{
+    memcpy(connection->input_packet->data, src, sizeof(NetworkInputData));
+    connection->input_packet->address.host = connection->peer.host;
+    SDLNet_Write16(connection->base_port_remote, &(connection->input_packet->address.port));
+    connection->input_packet->channel = -1;
+    connection->input_packet->len = sizeof(NetworkInputData);
+    SDLNet_UDP_Send(connection->send_input_socket, -1, connection->input_packet);
+}
+
+void
+network_send_state(TennixNet* connection, GameState* src)
+{
+    if (connection->master) {
+        net_serialize_gamestate(src, (NetworkGameState*)(connection->state_packet->data));
+        connection->state_packet->address.host = connection->peer.host;
+        SDLNet_Write16(connection->base_port_remote+1, &(connection->state_packet->address.port));
+        connection->state_packet->channel = -1;
+        connection->state_packet->len = sizeof(NetworkGameState);
+        assert(SDLNet_UDP_Send(connection->send_state_socket, -1, connection->state_packet)!=0);
+    }
+}
+
+void
+network_receive(TennixNet* connection)
+{
+    connection->input_packet->len = sizeof(NetworkInputData);
+    while (SDLNet_UDP_Recv(connection->recv_input_socket, connection->input_packet)) {
+        connection->input_available = true;
+    }
+
+    if (!(connection->master)) {
+        connection->state_packet->len = sizeof(NetworkGameState);
+        while (SDLNet_UDP_Recv(connection->recv_state_socket, connection->state_packet)) {
+            connection->state_available = true;
+        }
+    }
+}
+
+void
+network_get_input(TennixNet* connection, NetworkInputData* dest)
+{
+    if (connection->input_available) {
+        memcpy(dest, connection->input_packet->data, sizeof(NetworkInputData));
+        connection->input_available = false;
+    }
+}
+
+void
+network_get_gamestate(TennixNet* connection, GameState* dest)
+{
+    if (connection->state_available) {
+        net_unserialize_gamestate((NetworkGameState*)
+                (connection->state_packet->data), dest);
+        connection->state_available = false;
+    }
+}
+
 
 void
 net_serialize_ball(const Ball* src, NetworkBall* dest)
 {
-    SDLNet_Write32(pack_float(src->x, -WIDTH, WIDTH), &(dest->x));
-    SDLNet_Write32(pack_float(src->y, -HEIGHT, HEIGHT), &(dest->y));
+    SDLNet_Write32(pack_float(src->x, -WIDTH, WIDTH*2), &(dest->x));
+    SDLNet_Write32(pack_float(src->y, -HEIGHT, HEIGHT*2), &(dest->y));
     SDLNet_Write32(pack_float(src->z, -50, 50), &(dest->z));
     SDLNet_Write32(pack_float(src->move_x, -50, 50), &(dest->move_x));
     SDLNet_Write32(pack_float(src->move_y, -50, 50), &(dest->move_y));
@@ -61,8 +177,8 @@ net_serialize_ball(const Ball* src, NetworkBall* dest)
 void
 net_unserialize_ball(const NetworkBall* src, Ball* dest)
 {
-    dest->x = unpack_float(SDLNet_Read32(&(src->x)), -WIDTH, WIDTH);
-    dest->y = unpack_float(SDLNet_Read32(&(src->y)), -HEIGHT, HEIGHT);
+    dest->x = unpack_float(SDLNet_Read32(&(src->x)), -WIDTH, WIDTH*2);
+    dest->y = unpack_float(SDLNet_Read32(&(src->y)), -HEIGHT, HEIGHT*2);
     dest->z = unpack_float(SDLNet_Read32(&(src->z)), -50, 50);
     dest->move_x = unpack_float(SDLNet_Read32(&(src->move_x)), -50, 50);
     dest->move_y = unpack_float(SDLNet_Read32(&(src->move_y)), -50, 50);
@@ -75,29 +191,29 @@ net_unserialize_ball(const NetworkBall* src, Ball* dest)
 void
 net_serialize_player(const Player* src, NetworkPlayer* dest)
 {
-    SDLNet_Write32(pack_float(src->x, 0, WIDTH), &(dest->x));
-    SDLNet_Write32(pack_float(src->y, 0, HEIGHT), &(dest->y));
+    SDLNet_Write32(pack_float(src->x, 0, WIDTH*1.2), &(dest->x));
+    SDLNet_Write32(pack_float(src->y, 0, HEIGHT*1.2), &(dest->y));
     SDLNet_Write32(pack_float(src->power, 0, 110), &(dest->power));
     dest->use_power = src->use_power;
     dest->score = src->score;
     dest->desire = src->desire;
     dest->game = src->game;
     memcpy(dest->sets, src->sets, sizeof(unsigned char)*(SETS_TO_WIN*2));
-    SDLNet_Write32(pack_float(src->accelerate, 0, 1), &(dest->accelerate));
+    SDLNet_Write32(pack_float(src->accelerate, 0, 200), &(dest->accelerate));
 }
 
 void
 net_unserialize_player(const NetworkPlayer* src, Player* dest)
 {
-    dest->x = unpack_float(SDLNet_Read32(&(src->x)), 0, WIDTH);
-    dest->y = unpack_float(SDLNet_Read32(&(src->y)), 0, HEIGHT);
+    dest->x = unpack_float(SDLNet_Read32(&(src->x)), 0, WIDTH*1.2);
+    dest->y = unpack_float(SDLNet_Read32(&(src->y)), 0, HEIGHT*1.2);
     dest->power = unpack_float(SDLNet_Read32(&(src->power)), 0, 110);
     dest->use_power = src->use_power;
     dest->score = src->score;
     dest->desire = src->desire;
     dest->game = src->game;
     memcpy(dest->sets, src->sets, sizeof(unsigned char)*(SETS_TO_WIN*2));
-    dest->accelerate = unpack_float(SDLNet_Read32(&(src->accelerate)), 0, 1);
+    dest->accelerate = unpack_float(SDLNet_Read32(&(src->accelerate)), 0, 200);
 }
 
 void
index b5e0e9a..298187c 100644 (file)
--- a/network.h
+++ b/network.h
 #define __NETWORK_H
 
 #include "game.h"
+#include "input.h"
+
+#include <SDL/SDL_net.h>
+
+#define TENNIXNET_STATE_PORT 1448
+#define TENNIXNET_INPUT_PORT 1449
 
 typedef struct {
     Uint32 x;
@@ -45,9 +51,9 @@ typedef struct {
     bool use_power;
     unsigned char score;
     unsigned char desire;
-    char game; /* score for the current game */
-    unsigned char sets[SETS_TO_WIN*2]; /* score for each set */
-    Uint32 accelerate; /* a value [0..1] how fast the user accelerates */
+    char game;
+    unsigned char sets[SETS_TO_WIN*2];
+    Uint32 accelerate;
 } NetworkPlayer;
 
 typedef struct {
@@ -66,6 +72,36 @@ typedef struct {
 } NetworkGameState;
 
 void
+init_network();
+
+void
+uninit_network();
+
+TennixNet*
+network_connect(const char* host, Uint16 local_port, Uint16 remote_port);
+
+void
+network_disconnect(TennixNet* connection);
+
+void
+network_set_master(TennixNet* connection, bool master);
+
+void
+network_send_input(TennixNet* connection, NetworkInputData* src);
+
+void
+network_send_state(TennixNet* connection, GameState* src);
+
+void
+network_receive(TennixNet* connection);
+
+void
+network_get_input(TennixNet* connection, NetworkInputData* dest);
+
+void
+network_get_gamestate(TennixNet* connection, GameState* dest);
+
+void
 net_serialize_ball(const Ball* src, NetworkBall* dest);
 
 void
index 10cf5a5..5b6d44b 100644 (file)
--- a/tennix.c
+++ b/tennix.c
@@ -36,6 +36,7 @@
 #include "graphics.h"
 #include "sound.h"
 #include "input.h"
+#include "network.h"
 #include "util.h"
 #include "animation.h"
 
@@ -105,6 +106,11 @@ int main( int argc, char** argv) {
     Animation *intro;
     AnimationState *intro_playback;
     float wiggle;
+    TennixNet* connection = NULL;
+    char* net_host = NULL;
+    int net_port_local;
+    int net_port_remote;
+    bool net_master = false;
 
     MenuButton btn_back = {
         NULL, /* not needed for image-based button */
@@ -194,6 +200,20 @@ int main( int argc, char** argv) {
         else if (OPTION_SET("--benchmark", "-b")) {
             benchmark = true;
         }
+        else if (OPTION_SET("--network", "-n")) {
+            net_host = OPTION_VALUE;
+            assert(OPTION_VALUE != NULL);
+            OPTION_VALUE_PROCESSED;
+            net_port_local = atoi(OPTION_VALUE);
+            assert(OPTION_VALUE != NULL);
+            OPTION_VALUE_PROCESSED;
+            net_port_remote = atoi(OPTION_VALUE);
+            assert(OPTION_VALUE != NULL);
+            OPTION_VALUE_PROCESSED;
+        }
+        else if (OPTION_SET("--master", "-m")) {
+            net_master = true;
+        }
         else {
             fprintf(stderr, "Ignoring unknown option: %s\n", argv[i]);
         }
@@ -230,6 +250,12 @@ int main( int argc, char** argv) {
     init_graphics();
     init_sound();
     init_input();
+    init_network();
+
+    if (net_host != NULL) {
+        connection = network_connect(net_host, net_port_local, net_port_remote);
+        network_set_master(connection, net_master);
+    }
 
     menu_button_init(&btn_back);
     menu_button_init(&btn_start);
@@ -268,7 +294,7 @@ int main( int argc, char** argv) {
         PLAYER(g, 1).type = PLAYER_TYPE_AI;
         PLAYER(g, 2).type = PLAYER_TYPE_AI;
         g->location = &(locations[0]);
-        gameloop(g);
+        gameloop(g, connection);
         free(g);
         exit(0);
     }
@@ -416,7 +442,7 @@ int main( int argc, char** argv) {
                     exit(EXIT_FAILURE);
                 }
                 start_fade();
-                gameloop(current_game);
+                gameloop(current_game, connection);
                 SDL_Delay(150);
                 while(SDL_PollEvent(&e));
 #ifdef ENABLE_FPS_LIMIT
@@ -757,6 +783,7 @@ int main( int argc, char** argv) {
 
     uninit_graphics();
     uninit_input();
+    uninit_network();
 
     SDL_Quit();
     return 0;
index b85eea7..5902f5c 100644 (file)
--- a/tennix.h
+++ b/tennix.h
 #include <sys/param.h>
 
 #include <SDL/SDL.h>
+#include <SDL/SDL_net.h>
+
+typedef unsigned char bool;
+enum {
+    false,
+    true
+};
+
+typedef struct {
+    UDPsocket send_input_socket;
+    UDPsocket recv_input_socket;
+    UDPsocket send_state_socket;
+    UDPsocket recv_state_socket;
+
+    UDPpacket* input_packet;
+    UDPpacket* state_packet;
+
+    IPaddress peer;
+
+    bool input_available;
+    bool state_available;
+
+    Uint16 base_port_local;
+    Uint16 base_port_remote;
+
+    bool master;
+} TennixNet;
 
 #ifdef DELUXE_EDITION
 #  define HAVE_VOICE_FILES