message window needs to show at least 2 history lines
[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 MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
38 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 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));
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) - 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 maxplayers;  //1
117                                         int started;     //2
118                                         int continuous;  //3
119                                         long seed;       //4
120                                         int initspeed;   //5
121                                 } data;
122
123                                 memcpy(&data, event.u.net.data, event.u.net.size);
124                                 memcpy(&Players[me].flags, &data, sizeof(data.playerflags));
125                                 memcpy(&Players[me].flags, &data, sizeof(data.playerflags));
126                                 memcpy(&Game, &data.maxplayers,
127                                         sizeof(data) - sizeof(data.playerflags));
128                                 break;
129                         } //NP_gamedata
130                         case NP_error:
131                         {
132                                 fprintf(stderr, "Rejected by server: %s\n", event.u.net.data);
133                                 exit(1);
134                         } //NP_error
135                         default:
136                                 break;
137                         }
138                 else
139                         fatal("Hm, the party apparantly ended prematurely.");
140         } while (event.u.net.type != NP_gamedata);
141 }
142
143
144 MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
145 { //receive
146         int result;
147         short uid, type, size;
148         netint4 *data = (netint4*)&(gen->buf);
149
150         result = MyRead(gen->fd, gen->buf + gen->bufSize,
151                 gen->bufGoal - gen->bufSize);
152         if (result < 0) {
153                 close(gen->fd);
154                 gen->fd = -1;
155                 return E_lostConn;
156         }
157         gen->bufSize += result;
158         if (gen->bufSize < gen->bufGoal)
159                 return E_none;
160         // *ugly* memcpy(data, gen->buf, sizeof(data));
161         uid = ntoh4(data[0]);
162         type = ntoh4(data[1]);
163         size = ntoh4(data[2]);
164         gen->bufGoal = size;
165         if (gen->bufSize < gen->bufGoal)
166                 return E_none;
167         gen->bufSize = 0;
168         gen->bufGoal = HEADER_SIZE3;
169         event->u.net.sender = gen->player;
170         event->u.net.uid = uid;
171         event->u.net.type = type;
172         event->u.net.size = size - HEADER_SIZE3;
173         event->u.net.data = gen->buf + HEADER_SIZE3;
174         if (type == NP_endConn) return E_lostConn;
175         return E_net;
176 }
177
178 void SendPacket(short uid, NetPacketType type, int size, void *data)
179 { //send shit to server
180         netint4 header[3];
181
182         header[0] = hton4(uid);
183         header[1] = hton4(type);
184         header[2] = hton4(size + HEADER_SIZE3);
185         if (MyWrite(netGen.fd, header, HEADER_SIZE3) != HEADER_SIZE3)
186                 die("write (header)");
187         if (size > 0 && data && MyWrite(netGen.fd, data, size) != size)
188                 die("write");
189 }
190
191 void CloseNet(void)
192 { //kick some connection's ass!
193         MyEvent event;
194
195         if (netGen.fd >= 0) {
196                 SendPacket(0, NP_endConn, 0, NULL);
197                 close(netGen.fd);
198                 netGen.fd = -1;
199         }
200         if (netGen.next)
201                 RemoveEventGen(&netGen);
202 }
203