code duplication
[netris.git] / inet.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include "netris.h"
21
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <netdb.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <setjmp.h>
31
32 #include "inet.h"
33
34 #define HEADER_SIZE sizeof(netint2[2])
35 #define HEADER_SIZE3 sizeof(netint4[3])
36
37 static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
38 static EventGenRec netGen = {
39         NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE3
40 };
41
42
43 static sigjmp_buf close_env;
44
45 static void CatchInt(int sig)
46 {
47         siglongjmp(close_env, 1);
48 }
49
50 int InitiateConnection(char *hostStr, short port)
51 { //connect to host
52         struct sockaddr_in addr;
53         struct hostent *host;
54
55         if (sigsetjmp(close_env, 1))
56                 exit(0);
57         signal(SIGINT, CatchInt);  //handle exit (^C)
58         AtExit(CloseNet);
59         host = gethostbyname(hostStr);
60         if (!host)
61                 die("gethostbyname");
62         assert(host->h_addrtype == AF_INET);
63  again:
64         memset(&addr, 0, sizeof(addr));
65         addr.sin_family = host->h_addrtype;
66         memcpy(&addr.sin_addr, host->h_addr, host->h_length);
67         addr.sin_port = htons(port);
68         if ((netGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
69                 die("socket");
70         if (connect(netGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
71                 if (errno != ECONNREFUSED)
72                         die("connect");
73                 close(netGen.fd);
74                 sleep(1);
75                 goto again;
76         }
77         AddEventGen(&netGen);
78         return 0;
79 }
80
81 void HandShake(void)
82 { //talk to your host
83         MyEvent event;
84
85         {
86                 netint4 versiondata[2];
87                 versiondata[0] = hton4(MAJOR_VERSION);
88                 versiondata[1] = hton4(PROTOCOL_VERSION);
89                 SendPacket(0, NP_hello, sizeof(versiondata), versiondata);
90         }
91
92         do {
93                 if (WaitMyEvent(&event, EM_net) == E_net)
94                         switch (event.u.net.type) {
95                         case NP_hello:
96                         {
97                                 me = event.u.net.uid;
98                                 memcpy(&Players[me], &Players[0], sizeof(player_t));
99                                 fprintf(stderr, "Accepted (%s) as #%d (%s)\n",
100                                         event.u.net.data, me, Players[me].name);
101                                 SendPacket(0, NP_newPlayer,
102                                         sizeof(player_t) - sizeof(Players[me].host),
103                                         &Players[me]);
104                                 break;
105                         }
106                         case NP_team:
107                         { //receive your teamnumber
108                                 memcpy(&Players[event.u.net.uid].team, event.u.net.data,
109                                         event.u.net.size);
110                                 break;
111                         } //NP_team
112                         case NP_gamedata:
113                         {
114                                 static struct {
115                                         int playerflags;
116                                         int gravity;     //1
117                                         int started;     //2
118                                         int continuous;  //3
119                                         long seed;       //4
120                                         float shapes[7];
121                                         int initspeed;   //5
122                                 } data;
123
124                                 memcpy(&data, event.u.net.data, event.u.net.size);
125                                 memcpy(&Players[me].flags, &data, sizeof(data.playerflags));
126                                 memcpy(&Players[me].flags, &data, sizeof(data.playerflags));
127                                 memcpy(&Game.gravity, &data.gravity,
128                                         sizeof(data) - sizeof(data.playerflags));
129                                 break;
130                         } //NP_gamedata
131                         case NP_error:
132                         {
133                                 fprintf(stderr, "Rejected by server: %s\n", event.u.net.data);
134                                 exit(1);
135                         } //NP_error
136                         default:
137                                 break;
138                         }
139                 else
140                         fatal("Hm, the party apparantly ended prematurely.");
141         } while (event.u.net.type != NP_gamedata);
142 }
143
144
145 static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
146 { //receive
147         int result;
148         short uid, type, size;
149         netint4 *data = (netint4*)&(gen->buf);
150
151         result = MyRead(gen->fd, gen->buf + gen->bufSize,
152                 gen->bufGoal - gen->bufSize);
153         if (result < 0) {
154                 close(gen->fd);
155                 gen->fd = -1;
156                 return E_lostConn;
157         }
158         gen->bufSize += result;
159         if (gen->bufSize < gen->bufGoal)
160                 return E_none;
161         // *ugly* memcpy(data, gen->buf, sizeof(data));
162         uid = ntoh4(data[0]);
163         type = ntoh4(data[1]);
164         size = ntoh4(data[2]);
165         gen->bufGoal = size;
166         if (gen->bufSize < gen->bufGoal)
167                 return E_none;
168         gen->bufSize = 0;
169         gen->bufGoal = HEADER_SIZE3;
170         event->u.net.sender = gen->player;
171         event->u.net.uid = uid;
172         event->u.net.type = type;
173         event->u.net.size = size - HEADER_SIZE3;
174         event->u.net.data = gen->buf + HEADER_SIZE3;
175         if (type == NP_endConn) return E_lostConn;
176         return E_net;
177 }
178
179 void SendPacket(short uid, NetPacketType type, int size, void *data)
180 { //send shit to server
181         netint4 header[3];
182
183         header[0] = hton4(uid);
184         header[1] = hton4(type);
185         header[2] = hton4(size + HEADER_SIZE3);
186         if (MyWrite(netGen.fd, header, HEADER_SIZE3) != HEADER_SIZE3)
187                 die("write (header)");
188         if (size > 0 && data && MyWrite(netGen.fd, data, size) != size)
189                 die("write");
190 }
191
192 void CloseNet(void)
193 { //kick some connection's ass!
194         MyEvent event;
195
196         if (netGen.fd >= 0) {
197                 SendPacket(0, NP_endConn, 0, NULL);
198                 close(netGen.fd);
199                 netGen.fd = -1;
200         }
201         if (netGen.next)
202                 RemoveEventGen(&netGen);
203 }
204