4
** Copyright (c) 1993-2001 by Hans-Ulrich Kiel & Joerg Czeranski
5
** All rights reserved.
7
** Redistribution and use in source and binary forms, with or without
8
** modification, are permitted provided that the following conditions are
11
** 1. Redistributions of source code must retain the above copyright
12
** notice, this list of conditions and the following disclaimer.
13
** 2. Redistributions in binary form must reproduce the above copyright
14
** notice, this list of conditions and the following disclaimer in the
15
** documentation and/or other materials provided with the distribution.
16
** 3. The name of the authors may not be used to endorse or promote
17
** products derived from this software without specific prior written
19
** 4. The name ``iMaze'' may not be used for products derived from this
20
** software unless a prefix or a suffix is added to the name.
22
** THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
** DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
26
** INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30
** STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
31
** IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
** POSSIBILITY OF SUCH DAMAGE.
38
** Das Hauptprogramm des Servers
39
** inklusive des Startens der anderen Prozesse
53
#include "labyrinth.h"
54
#include "server_netz.h"
56
#include "init_spieler.h"
58
static char sccsid[] = "@(#)server.c 3.19 12/04/01";
61
static int zeittakt_parsen(char *str);
64
static char *labfile_name = NULL;
66
struct arg_option argv_opts[] =
68
{ Arg_Include, NULL, net_opts },
69
{ Arg_Include, NULL, feature_opts },
70
{ Arg_Callback, "c", (void *)zeittakt_parsen,
71
"set cycle time (15-150 ms, default 60)", "time" },
72
{ Arg_End, NULL, &labfile_name, NULL, "lab-file" }
76
/* Identifikationstext, mit dem jede Labyrinthdatei beginnt: */
77
static char MAGIC[] = "iMazeLab1\n";
79
u_int feldlaenge, feldbreite; /* Feldgroesse */
80
block **spielfeld; /* Daten des Spielfeldes */
82
static void *session_deskriptor; /* Deskriptor zur Kommunikation mit der
83
einzigen Session, die gestartet wird */
84
static char *session_status; /* Infostring ueber die einzige Session */
86
static int zeittakt = 60; /* in Millisekunden */
95
static int zeittakt_parsen(char *str)
100
takt = strtol(str, &end, 10);
101
if (*end != 0 || takt < 15 || takt > 150)
102
return 1; /* parse/value error */
111
** wird aufgerufen, wenn der Server vom User beendet wird;
112
** schickt dem Session-Prozess die Nachricht; der Server-Prozess wird
113
** erst beendet, wenn der Session-Prozess geantwortet hat
116
** signum: Dummy-Parameter
118
static void ende_handler(int signum)
120
char daten; /* Puffer fuer die Nachricht */
122
/* Nachricht NT_SPIEL_BEENDEN an Session-Prozess senden */
123
daten = NT_SPIEL_BEENDEN;
124
nachricht_senden(session_deskriptor, NULL, sizeof daten, &daten);
128
static void spieler_an_session(void *verbindung, char *progname, char *spruch,
131
char *daten; /* Puffer zum Uebertragen des Spruchs
132
an den Sessions-Prozess */
133
int progname_laenge, spruch_laenge, daten_laenge;
135
if (progname == NULL || progname[0] == 0)
138
progname_laenge = strlen(progname) + 1; /* Null mitkopieren */
140
/* Laenge begrenzen */
141
if (progname_laenge > 255)
142
progname_laenge = 255;
144
/* Kameras habens keinen Spruch, da sie nicht toeten koennen */
145
if (spruch == NULL || spruch[0] == 0 || kamera)
148
spruch_laenge = strlen(spruch) + 1; /* Null mitkopieren */
150
/* Speicher fuer den Typ, die Laenge des Programmnamen, den Namen
151
selbst und den Spruch belegen */
152
daten_laenge = 2 + progname_laenge + spruch_laenge;
153
speicher_belegen((void **)&daten, daten_laenge);
155
/* die Nachricht beginnt mit dem Typ und der Laenge des Namen */
156
daten[0] = kamera ? NT_NEUE_KAMERA : NT_NEUER_SPIELER;
157
daten[1] = progname_laenge;
159
/* dann der Programmname */
161
memcpy(daten + 2, progname, progname_laenge);
163
/* und zuletzt der Spruch */
165
memcpy(daten + 2 + progname_laenge, spruch, spruch_laenge);
167
/* dem Session-Prozess den Verbindungsdeskriptor per Nachricht
169
nachricht_senden(session_deskriptor, verbindung, daten_laenge, daten);
171
/* allen Speicher wieder freigeben */
172
speicher_freigeben((void **)&daten);
173
if (progname != NULL)
174
speicher_freigeben((void **)&progname);
176
speicher_freigeben((void **)&spruch);
178
/* der Verbindungsdeskriptor wird vom Server nicht mehr
179
benoetigt, aufraeumen; einen evtl. gestarteten
180
Initialisierungsprozess implizit beenden */
181
verbindung_freigeben(verbindung);
187
** loest die Initialisierung von neuen Clients aus und kommuniziert
188
** mit dem Session-Prozess
191
** diese Prozedur ist eine Endlosschleife und wird nur durch Fehler
192
** oder Signale per exit verlassen
194
static void server(void)
196
/* Status initialisieren */
197
speicher_belegen((void **)&session_status, 1);
198
session_status[0] = 0;
200
/* Endlosschleife, s.o. */
203
/* auf Nachrichten von den anderen Prozessen und neue
204
Verbindungen von Clients warten */
205
if (nachricht_oder_verbindung_erwarten(1,
206
&session_deskriptor) == NULL)
207
/* eine Verbindung steht evtl. bereit */
209
void *verbindung; /* Deskriptor fuer die Verbindung zum
211
char *progname; /* Name des Client-Programms */
212
char *spruch; /* Spruch des neuen Spielers */
213
int kamera; /* Spieler will nur zusehen */
216
/* versuchen, die Verbindung entgegenzunehmen */
217
if ((verbindung = verbindung_annehmen()) == NULL)
218
/* keine Verbindung bekommen oder ein
219
Initialisierungsprozess wurde abgespalten, um die
220
Verbindung anzunehmen */
223
/* die Verbindung wurde angenommen; evtl. ist dies ein
224
abgespaltener Initialisierungsprozess */
226
/* dem Client die Initialisierungsdaten senden
227
und die Verbindung vom Prolog auf das Spiel schalten */
228
init_result = init_spieler(verbindung, feldbreite,
229
feldlaenge, spielfeld, session_status,
230
&progname, &spruch, &kamera);
231
if (init_result != Init_Success ||
232
verbindung_auf_spiel(session_deskriptor,
235
/* Speicher fuer Programmname und Spruch
236
bei Fehler wieder freigeben */
237
if (progname != NULL)
238
speicher_freigeben((void **)&progname);
241
speicher_freigeben((void **)&spruch);
243
/* bei einem Fehler die Verbindung abbrechen und weiter
244
warten; einen evtl. gestarteten Initialisierungsprozess
246
verbindung_abbrechen(verbindung, init_result !=
247
Init_Aborted_By_Client /* fehler? */);
251
/* Spieler an Session uebergeben; einen evtl.
252
gestarteten Initialisierungsprozess implizit
254
spieler_an_session(verbindung, progname,
258
/* eine Nachricht von einer Session liegt evtl. an */
260
void *daten; /* Datenteil der Nachricht */
261
int laenge; /* Laenge des Datenteils */
262
void *verbindung; /* Verbindungsdeskriptor, auf den die
263
Nachricht sich bezieht */
264
char typ; /* Typ der Nachricht; siehe server.h */
266
/* Nachricht (Typ und Deskriptor) entgegennehmen */
267
if (nachricht_empfangen(session_deskriptor, &verbindung,
269
/* keine Nachricht bekommen; weiter warten */
272
/* Session-Status erneuern? */
273
if (laenge > 0 && ((char *)daten)[0] == NT_STATUS)
275
speicher_freigeben((void **)&session_status);
277
/* Typ entfernen, Null anhaengen */
279
memmove(daten, (char *)daten + 1,
282
((char *)daten)[laenge - 1] = 0;
284
/* und abspeichern */
285
session_status = daten;
289
/* Datenteil besteht nicht genau aus dem Typ? */
292
/* dann Speicher wieder freigeben */
294
speicher_freigeben(&daten);
296
/* und Nachricht ignorieren */
300
typ = *(char *)daten; /* Typ der Nachricht */
302
/* Speicher wieder freigeben */
303
speicher_freigeben(&daten);
305
/* wenn der Session-Prozess sich beendet hat, den
306
Server-Prozess auch beenden */
307
if (typ == NT_SPIEL_ENDE)
310
/* alle weiteren Nachrichten ignorieren und einen
311
evtl. mitgesandten Deskriptor wieder freigeben */
312
if (verbindung != NULL)
313
verbindung_freigeben(verbindung);
320
** aussenwand_korrigieren
321
** korrigiert eine Aussenwand des Spielfeldes, falls diese unsichtbar,
322
** begehbar oder nicht schusssicher ist
325
** wand: Zeiger auf eine Wand im Spielfeld
328
** 0, falls nichts veraendert wurde, 1 sonst
331
** *wand wird noetigenfalls korrigiert
333
static int aussenwand_korrigieren(struct wand *wand)
335
int veraendert; /* Flag, ob etwas veraendert wurde */
339
/* die Wand muss schusssicher sein */
340
if (!wand->schusssicher)
341
wand->schusssicher = 1,
344
/* die Wand muss unbegehbar sein */
345
if (!wand->unbegehbar)
346
wand->unbegehbar = 1,
349
/* die Wand muss die Farbe einer Wand haben */
350
if (wand->farbe < 1 || wand->farbe > 7)
354
/* zurueckmelden, ob etwas veraendert wurde */
361
** laedt das Labyrinth aus einer Datei
364
** dateiname: der Dateiname
367
** spielfeld, feldbreite und feldlaenge werden initialisiert
369
static void ladelab(char *dateiname)
371
FILE *datei; /* Deskriptor fuer die Labyrinthdatei */
372
char magic[sizeof MAGIC - 1]; /* Identifikationstext in der Datei */
373
u_char daten[4]; /* Puffer zum Lesen aus der Datei */
374
int x, y, i; /* Indizes fuer das Spielfeld */
375
int warnung; /* Labyrinth enthaelt
376
unzulaessige Daten */
378
/* falls der Dateiname "-" ist, von stdin lesen, sonst Datei oeffnen */
379
if (strcmp(dateiname, "-"))
381
/* Datei zum Lesen oeffnen */
382
datei = fopen(dateiname, "rb");
384
/* bei Fehler, diesen anzeigen und Programm abbrechen */
387
static char *meldung[] = { "iMaze - Initialization Error", "",
388
"Can't read labyrinth file", NULL, NULL };
391
/* Fehlermeldung zusammenstellen und ausgeben */
392
meldung[3] = dateiname;
393
uebler_fehler(meldung, NULL);
400
/* Identifikationstext aus der Datei lesen */
401
if (fread(magic, 1, sizeof magic, datei) != sizeof magic)
402
/* bei Fehler "Initialization Error" anzeigen */
405
/* Falls der Identifikationstext nicht der erwartete ist,
406
Fehler anzeigen und Programm abbrechen */
407
if (memcmp(MAGIC, magic, sizeof magic))
409
static char *meldung[] = { "iMaze - Initialization Error", "",
410
"Invalid labyrinth file format", NULL, NULL };
413
/* Fehlermeldung zusammenstellen und ausgeben */
414
meldung[3] = dateiname;
415
uebler_fehler(meldung, NULL);
418
/* Feldgroesse aus der Datei lesen */
419
if (fread(daten, 1, 2, datei) != 2)
420
/* bei Fehler "Initialization Error" anzeigen */
422
feldbreite = daten[0];
423
feldlaenge = daten[1];
425
/* Feldgroesse ueberpruefen */
426
if (feldbreite < 1 || feldbreite > MAXFELDBREITE ||
427
feldlaenge < 1 || feldlaenge > MAXFELDBREITE)
428
/* bei ungueltier Groesse "Initialization Error" anzeigen */
431
/* Speicher fuer das Spielfeld belegen */
432
speicher_belegen((void **)&spielfeld, feldbreite * sizeof(block *));
434
for (x = 0; x < feldbreite; x++)
435
speicher_belegen((void **)&spielfeld[x],
436
feldlaenge * sizeof(block));
438
/* bei unzulaessigem Spielfeldinhalt Warnung ausgeben,
442
/* Spielfeld einlesen */
443
for (y = 0; y < feldlaenge; y++)
444
for (x = 0; x < feldbreite; x++)
446
/* einen Block (4 Waende) aus der Datei lesen */
447
if (fread(daten, 1, 4, datei) != 4)
448
/* bei Fehler "Initialization Error" anzeigen */
451
/* alle 4 Waende in Spielfeld kopieren */
452
for (i = 0; i < 4; i++)
454
struct wand *wand; /* Zeiger in das Spielfeld */
456
wand = &spielfeld[x][y][i];
458
/* Farbe und Flags "schusssicher" und "unbegehbar" aus der
460
wand->farbe = daten[i] & 15;
461
wand->schusssicher = daten[i] >> 6 & 1;
462
wand->unbegehbar = daten[i] >> 7 & 1;
464
/* Daten unzulaessig? */
466
wand->unbegehbar && wand->farbe >= 8 ||
467
!wand->unbegehbar && wand->farbe >= 1 &&
469
wand->schusssicher ^ (wand->farbe != 0) ||
470
(daten[i] >> 4 & 3) != 0;
474
/* falls am Rand keine Waende sind, korrigieren */
475
for (x = 0; x < feldbreite; x++)
477
warnung |= aussenwand_korrigieren(&spielfeld[x][0][NORD]);
478
warnung |= aussenwand_korrigieren(&spielfeld[x][feldlaenge - 1][SUED]);
480
for (y = 0; y < feldlaenge; y++)
482
warnung |= aussenwand_korrigieren(&spielfeld[0][y][WEST]);
483
warnung |= aussenwand_korrigieren(&spielfeld[feldbreite - 1][y][OST]);
486
/* Datei schliessen, wenn es nicht stdin war */
490
/* evtl. Warnung ausgeben */
493
fprintf(stderr, "imazesrv: Invalid labyrinth\n");
494
fprintf(stderr, "imazesrv: %s\n", dateiname);
495
fprintf(stderr, "imazesrv:\n");
496
fprintf(stderr, "imazesrv: Who cares?\n");
501
/* bis hier lokaler Teil */
502
/***********************************************/
503
/* ab hier globaler Teil */
508
** zeigt eine Fehlermeldung an und beendet das Programm
511
** meldung: Feld von Strings, die die Zeilen des auszugebenden Textes
513
** knopf: Text der auf einem Bestaetigungs-Knopf stehen sollte;
517
** Ende des Programms
519
void uebler_fehler(char **meldung, char *knopf)
521
int i; /* Zaehlvariable fuer Ausgeben der Meldung */
523
/* Ausgabe der Meldung auf standard-error */
524
for (i = 0; meldung[i] != NULL; i++)
525
fprintf(stderr, "imazesrv: %s\n", meldung[i]);
527
/* Programm abbrechen */
534
** die Hauptroutine, der Server und eine Session werden gestartet
537
** argc: Anzahl der Argumente inklusive Programmname
538
** argv: Argumentliste
540
int main(int argc, char **argv)
542
/* diese Signale immer ignorieren */
543
signal(SIGHUP, SIG_IGN);
544
signal(SIGALRM, SIG_IGN);
545
signal(SIGPIPE, SIG_IGN);
547
/* fuer diese Signale gibt es fuer einen Prozess einen Handler,
548
erstmal ignorieren */
549
signal(SIGINT, SIG_IGN);
550
signal(SIGTERM, SIG_IGN);
552
process_args(&argc, argv);
554
/* Labyrinth laden */
555
ladelab(labfile_name);
557
/* Netzwerkroutinen initialisieren */
560
/* eine Session starten */
561
if ((session_deskriptor = session_starten()) != NULL)
563
/* dies ist der Server-Prozess */
565
/* fuer diese Signale einen Handler installieren, der ein
566
sauberes Programmende erlaubt */
567
handle_signal(SIGINT, ende_handler);
568
handle_signal(SIGTERM, ende_handler);
573
/* dies ist der Session-Prozess */