acpc_dealer 2.4.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/Brickfile +6 -0
  3. data/README.md +7 -6
  4. data/Rakefile +30 -14
  5. data/acpc_dealer.gemspec +2 -1
  6. data/bin/acpc_dealer +4 -0
  7. data/ext/hand_evaluator/extconf.rb +10 -0
  8. data/lib/acpc_dealer/version.rb +1 -1
  9. data/lib/hand_evaluator.bundle +0 -0
  10. data/spec/dealer_runner_spec.rb +76 -72
  11. metadata +19 -82
  12. data/vendor/project_acpc_server/LICENCE +0 -23
  13. data/vendor/project_acpc_server/Makefile +0 -35
  14. data/vendor/project_acpc_server/README +0 -113
  15. data/vendor/project_acpc_server/README.submission +0 -42
  16. data/vendor/project_acpc_server/acpc_play_match.pl +0 -101
  17. data/vendor/project_acpc_server/bm_run_matches.c +0 -238
  18. data/vendor/project_acpc_server/bm_server.c +0 -1604
  19. data/vendor/project_acpc_server/bm_server.config +0 -78
  20. data/vendor/project_acpc_server/bm_widget.c +0 -230
  21. data/vendor/project_acpc_server/dealer.c +0 -1293
  22. data/vendor/project_acpc_server/evalHandTables +0 -4269
  23. data/vendor/project_acpc_server/example_player.c +0 -204
  24. data/vendor/project_acpc_server/example_player.limit.2p.sh +0 -3
  25. data/vendor/project_acpc_server/example_player.limit.3p.sh +0 -3
  26. data/vendor/project_acpc_server/example_player.nolimit.2p.sh +0 -3
  27. data/vendor/project_acpc_server/example_player.nolimit.3p.sh +0 -3
  28. data/vendor/project_acpc_server/game.c +0 -1792
  29. data/vendor/project_acpc_server/game.h +0 -253
  30. data/vendor/project_acpc_server/holdem.limit.2p.reverse_blinds.game +0 -13
  31. data/vendor/project_acpc_server/holdem.limit.3p.game +0 -13
  32. data/vendor/project_acpc_server/holdem.nolimit.2p.reverse_blinds.game +0 -12
  33. data/vendor/project_acpc_server/holdem.nolimit.3p.game +0 -12
  34. data/vendor/project_acpc_server/kuhn.limit.3p.game +0 -14
  35. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player.sf1.sh +0 -3
  36. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player.sf2.sh +0 -3
  37. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player.sf3.sh +0 -3
  38. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/LICENCE +0 -23
  39. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/Makefile +0 -127
  40. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/README.md +0 -35
  41. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/CExceptionConfig.h +0 -12
  42. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/dealer_connection.c +0 -49
  43. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/dealer_connection.h +0 -22
  44. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/kuhn_3p_equilibrium_player.c +0 -483
  45. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/kuhn_3p_equilibrium_player.h +0 -108
  46. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/main.c +0 -84
  47. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/player_config.c +0 -253
  48. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/player_config.h +0 -21
  49. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/support/test_helper.c +0 -45
  50. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/support/test_helper.h +0 -27
  51. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/test_kuhn_3p_equilibrium_player.c +0 -698
  52. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/test_kuhn_3p_equilibrium_player_sub_family_1.c +0 -324
  53. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/test_kuhn_3p_equilibrium_player_sub_family_2.c +0 -262
  54. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/test_kuhn_3p_equilibrium_player_sub_family_3.c +0 -177
  55. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/docs/license.txt +0 -30
  56. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/docs/readme.txt +0 -242
  57. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/lib/CException.c +0 -43
  58. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/lib/CException.h +0 -86
  59. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/release/build.info +0 -2
  60. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/release/version.info +0 -2
  61. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/History.md +0 -27
  62. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/Makefile +0 -8
  63. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/Readme.md +0 -103
  64. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/package.json +0 -9
  65. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/src/commander.c +0 -250
  66. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/src/commander.h +0 -88
  67. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/test.c +0 -34
  68. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/colour_prompt.rb +0 -94
  69. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/colour_reporter.rb +0 -39
  70. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/generate_config.yml +0 -36
  71. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/generate_module.rb +0 -202
  72. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/generate_test_runner.rb +0 -316
  73. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/test_file_filter.rb +0 -23
  74. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/unity_test_summary.rb +0 -139
  75. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/docs/Unity Summary.txt +0 -216
  76. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/docs/license.txt +0 -31
  77. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/release/build.info +0 -2
  78. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/release/version.info +0 -2
  79. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/src/unity.c +0 -1146
  80. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/src/unity.h +0 -245
  81. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/src/unity_internals.h +0 -546
  82. data/vendor/project_acpc_server/net.c +0 -218
  83. data/vendor/project_acpc_server/net.h +0 -62
  84. data/vendor/project_acpc_server/play_match.pl +0 -99
  85. data/vendor/project_acpc_server/protocol.md +0 -239
  86. data/vendor/project_acpc_server/protocol.odt +0 -0
  87. data/vendor/project_acpc_server/protocol.pdf +0 -0
  88. data/vendor/project_acpc_server/rng.c +0 -139
  89. data/vendor/project_acpc_server/rng.h +0 -63
  90. data/vendor/project_acpc_server/validate_submission.pl +0 -546
@@ -1,1604 +0,0 @@
1
- /*
2
- Copyright (C) 2011 by the Computer Poker Research Group, University of Alberta
3
- */
4
-
5
- #include <stdlib.h>
6
- #include <stdio.h>
7
- #define __STDC_FORMAT_MACROS
8
- #include <inttypes.h>
9
- #include <assert.h>
10
- #include <string.h>
11
- #include <strings.h>
12
- #include <ctype.h>
13
- #include <unistd.h>
14
- #include <netdb.h>
15
- #include <sys/socket.h>
16
- #include <sys/types.h>
17
- #include <arpa/inet.h>
18
- #include <netinet/in.h>
19
- #include <netinet/tcp.h>
20
- #include <sys/wait.h>
21
- #include <sys/time.h>
22
- #include <time.h>
23
- #include <signal.h>
24
- #include <fcntl.h>
25
- #include "game.h"
26
- #include "net.h"
27
- #include "rng.h"
28
-
29
-
30
- #define STATUS_CLOSED 0
31
- #define STATUS_UNVALIDATED 1
32
- #define STATUS_OKAY 2
33
-
34
- #define BM_DEALER "dealer"
35
- #define BM_LOGDIR "logs"
36
- #define BM_DEALER_WAIT_SECS 5
37
- #define BM_MAX_IOWAIT_SECS 1
38
-
39
-
40
- typedef struct LLPoolEntry_struct {
41
- struct LLPoolEntry_struct *next;
42
- struct LLPoolEntry_struct *prev;
43
- char data[ 0 ];
44
- } LLPoolEntry;
45
-
46
- typedef struct {
47
- LLPoolEntry *head;
48
- LLPoolEntry *free;
49
- int dataSize;
50
- int numEntries;
51
- } LLPool;
52
-
53
- /* structure giving the specification for a local bot */
54
- typedef struct {
55
- char *name;
56
- char *command;
57
- } BotSpec;
58
-
59
- /* structure giving the specification for a user */
60
- typedef struct {
61
- char *name;
62
- char *passwd;
63
- struct timeval waitStart;
64
- } UserSpec;
65
-
66
- typedef struct {
67
- uint16_t maxMatchRuns; /* maximum number of runs for a match */
68
- uint16_t maxRunningJobs; /* maximum simultaneous jobs at a time for game
69
- 0 disables the check */
70
- uint32_t matchHands; /* number of hands in a match */
71
- Game *game;
72
- char *gameFile;
73
- LLPool *bots;
74
-
75
- int curRunningJobs;
76
- } GameConfig;
77
-
78
- typedef struct {
79
- uint16_t port;
80
- uint16_t maxRunningBots; /* maximum simultaneous bots at a time
81
- 0 disables the check */
82
- uint16_t startupTimeoutSecs; /* maximum time to wait for clients to connect
83
- 0 disables the timer */
84
- uint16_t responseTimeoutSecs; /* maximum time to wait for clients to respond
85
- with an action */
86
- uint16_t handTimeoutSecs; /* maximum time to allowed per hand of play */
87
- uint16_t avgHandTimeSecs; /* average time per hand allowed for the match */
88
-
89
- LLPool *games;
90
- LLPool *users;
91
- } Config;
92
-
93
- typedef struct {
94
- int status;
95
- UserSpec *user; /* NULL when status is STATUS_UNVALIDATED */
96
- ReadBuf *connBuf;
97
- } Connection;
98
-
99
- typedef struct {
100
- GameConfig *gameConf;
101
- UserSpec *user;
102
- int numRuns;
103
- rng_state_t rng;
104
- uint32_t rngSeed;
105
- int useRngForSeed; /* 0: use rngSeed as seed for each dealer run
106
- 1: use genrand_int32( match->rng ) */
107
- char *tag;
108
- struct timeval queueTime;
109
- struct {
110
- int isNetworkPlayer;
111
- LLPoolEntry *entry; /* connection if network player, bot otherwise */
112
- } players[ MAX_PLAYERS ];
113
- int isRunning;
114
- } Match;
115
-
116
- typedef struct {
117
- pid_t dealerPID;
118
- pid_t botPID[ MAX_PLAYERS ];
119
- LLPoolEntry *matchEntry;
120
- char *tag; /* based on tag from the match for this job */
121
- uint16_t ports[ MAX_PLAYERS ];
122
- } MatchJob;
123
-
124
- typedef struct {
125
- int listenSocket;
126
- LLPool *conns;
127
- LLPool *matches;
128
- LLPool *jobs;
129
-
130
- rng_state_t rng;
131
-
132
- char *hostname;
133
-
134
- int devnullfd;
135
- } ServerState;
136
-
137
-
138
- LLPool *newLLPool( const int dataSize )
139
- {
140
- LLPool *pool;
141
-
142
- pool = (LLPool*)malloc( sizeof( LLPool ) );
143
- assert( pool != 0 );
144
- pool->head = NULL;
145
- pool->free = NULL;
146
- pool->dataSize = dataSize;
147
- pool->numEntries = 0;
148
- return pool;
149
- }
150
-
151
- int entryInList( LLPoolEntry *list, LLPoolEntry *entry )
152
- {
153
- while( list ) {
154
-
155
- if( entry == list ) {
156
-
157
- return 1;
158
- }
159
- if( list->next ) {
160
-
161
- assert( list->next->prev == list );
162
- }
163
- list = list->next;
164
- }
165
- return 0;
166
- }
167
-
168
- /* add an object to the pool. data must have a size of pool->dataSize */
169
- LLPoolEntry *LLPoolAddItem( LLPool *pool, void *item )
170
- {
171
- LLPoolEntry *entry;
172
-
173
- if( pool->free ) {
174
-
175
- entry = pool->free;
176
- pool->free = entry->next;
177
- } else {
178
-
179
- entry = (LLPoolEntry*)malloc( sizeof( LLPoolEntry ) + pool->dataSize );
180
- assert( entry != 0 );
181
- }
182
-
183
- assert( !entryInList( pool->head, entry ) );
184
- entry->next = pool->head;
185
- entry->prev = NULL;
186
- memcpy( entry->data, item, pool->dataSize );
187
- if( pool->head ) {
188
-
189
- pool->head->prev = entry;
190
- }
191
- pool->head = entry;
192
-
193
- ++pool->numEntries;
194
-
195
- return entry;
196
- }
197
-
198
- /* remove an item from the pool, placing it in the free list.
199
- entry must have been generated by LLPoolAddItem( pool, ... )
200
- (that is, calling LLPoolRemoveEntry on an entry from another pool
201
- is potentially a very bad idea...) */
202
- void LLPoolRemoveEntry( LLPool *pool, LLPoolEntry *entry )
203
- {
204
- if( entry->prev ) {
205
-
206
- assert( entry->prev->next == entry );
207
- entry->prev->next = entry->next;
208
- } else {
209
-
210
- assert( pool->head == entry );
211
- pool->head = entry->next;
212
- }
213
- if( entry->next ) {
214
-
215
- assert( entry->next->prev == entry );
216
- entry->next->prev = entry->prev;
217
- }
218
-
219
- assert( !entryInList( pool->free, entry ) );
220
- if( pool->free ) {
221
-
222
- pool->free->prev = entry;
223
- }
224
- entry->next = pool->free;
225
- pool->free = entry;
226
-
227
- --pool->numEntries;
228
- }
229
-
230
- /* LLPool iterator start */
231
- LLPoolEntry *LLPoolFirstEntry( LLPool *pool )
232
- {
233
- return pool->head;
234
- }
235
-
236
- /* removing entries while iterating through the list is fine, as
237
- long as cur is not the entry being removed. */
238
- LLPoolEntry *LLPoolNextEntry( LLPoolEntry *cur )
239
- {
240
- if( cur ) {
241
-
242
- return cur->next;
243
- }
244
- return NULL;
245
- }
246
-
247
- void *LLPoolGetItem( LLPoolEntry *entry )
248
- {
249
- return &entry->data;
250
- }
251
-
252
-
253
- void printUsage( FILE *file )
254
- {
255
- fprintf( file, "usage: bm_server config_file\n" );
256
- }
257
-
258
- void setGameDefaults( GameConfig *gameConf )
259
- {
260
- gameConf->maxMatchRuns = 10;
261
- gameConf->maxRunningJobs = 1;
262
- gameConf->matchHands = 5000;
263
- gameConf->game = NULL;
264
- gameConf->gameFile = NULL;
265
- gameConf->bots = newLLPool( sizeof( BotSpec ) );
266
-
267
- gameConf->curRunningJobs = 0;
268
- }
269
-
270
- void setDefaults( Config *conf )
271
- {
272
- conf->port = 54000;
273
- conf->maxRunningBots = 0;
274
- conf->startupTimeoutSecs = 60;
275
- conf->responseTimeoutSecs = 600; /* Value from 2011 ACPC */
276
- conf->handTimeoutSecs = 3000 * 7; /* Not enforced for 2011 ACPC */
277
- conf->avgHandTimeSecs = 7; /* Value from 2011 ACPC */
278
- conf->games = newLLPool( sizeof( GameConfig ) );
279
- conf->users = newLLPool( sizeof( UserSpec ) );
280
- }
281
-
282
- /* returns entry for bot on success, NULL on failure */
283
- LLPoolEntry *findBot( const GameConfig *game, const char *name )
284
- {
285
- LLPoolEntry *cur;
286
-
287
- for( cur = LLPoolFirstEntry( game->bots );
288
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
289
-
290
- if( !strcmp( ( (BotSpec *)LLPoolGetItem( cur ) )->name, name ) ) {
291
-
292
- return cur;
293
- }
294
- }
295
-
296
- return NULL;
297
- }
298
-
299
- void addBot( GameConfig *gameConf, const char *spec )
300
- {
301
- BotSpec bot;
302
- char name[ READBUF_LEN ];
303
- char command[ READBUF_LEN ];
304
-
305
- /* split the line into name and command */
306
- if( sscanf( spec, " %s %s", name, command ) < 2 ) {
307
-
308
- fprintf( stderr, "BM_ERROR: could not get bot name and command from: %s",
309
- spec );
310
- exit( EXIT_FAILURE );
311
- }
312
-
313
- /* make sure there are no duplicates */
314
- if( !strcmp( name, "LOCAL" ) ) {
315
-
316
- fprintf( stderr, "BM_ERROR: LOCAL is a reserved bot name\n" );
317
- exit( EXIT_FAILURE );
318
- }
319
- if( findBot( gameConf, name ) ) {
320
-
321
- fprintf( stderr, "BM_ERROR: duplicate bot %s\n", name );
322
- exit( EXIT_FAILURE );
323
- }
324
-
325
- /* add the bot */
326
- bot.name = strdup( name );
327
- bot.command = strdup( command );
328
- LLPoolAddItem( gameConf->bots, &bot );
329
- }
330
-
331
- /* returns entry for user on success, NULL on failure */
332
- LLPoolEntry *findUser( const Config *conf, const char *name )
333
- {
334
- LLPoolEntry *cur;
335
-
336
- for( cur = LLPoolFirstEntry( conf->users );
337
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
338
-
339
- if( !strcmp( ( (UserSpec *)LLPoolGetItem( cur ) )->name, name ) ) {
340
-
341
- return cur;
342
- }
343
- }
344
-
345
- return NULL;
346
- }
347
-
348
- void addUser( Config *conf, const char *spec )
349
- {
350
- UserSpec user;
351
- char name[ READBUF_LEN ];
352
- char passwd[ READBUF_LEN ];
353
-
354
- /* split the line into name and password */
355
- if( sscanf( spec, " %s %s", name, passwd ) < 2 ) {
356
-
357
- fprintf( stderr, "BM_ERROR: could not get user name and password from: %s",
358
- spec );
359
- exit( EXIT_FAILURE );
360
- }
361
-
362
- /* make sure there are no duplicates */
363
- if( findUser( conf, name ) ) {
364
-
365
- fprintf( stderr, "BM_ERROR: duplicate user %s\n", name );
366
- exit( EXIT_FAILURE );
367
- }
368
-
369
- /* add the user */
370
- user.name = strdup( name );
371
- user.passwd = strdup( passwd );
372
- gettimeofday( &user.waitStart, NULL );
373
- LLPoolAddItem( conf->users, &user );
374
- }
375
-
376
- /* returns entry for game on success, NULL on failure */
377
- LLPoolEntry *findGame( const Config *conf, const char *name )
378
- {
379
- LLPoolEntry *cur;
380
-
381
- for( cur = LLPoolFirstEntry( conf->games );
382
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
383
-
384
- if( !strcmp( ( (GameConfig *)LLPoolGetItem( cur ) )->gameFile, name ) ) {
385
-
386
- return cur;
387
- }
388
- }
389
-
390
- return NULL;
391
- }
392
-
393
- /* validate a logon request
394
- returns user on success, or NULL on failure */
395
- UserSpec *validateLogon( const Config *conf, const char *line )
396
- {
397
- LLPoolEntry *cur;
398
- char name[ READBUF_LEN ];
399
- char passwd[ READBUF_LEN ];
400
-
401
- if( sscanf( line, " %s %s", name, passwd ) < 2 ) {
402
-
403
- return NULL;
404
- }
405
-
406
- for( cur = LLPoolFirstEntry( conf->users );
407
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
408
- UserSpec *user = (UserSpec *)LLPoolGetItem( cur );
409
-
410
- if( !strcmp( user->name, name ) ) {
411
-
412
- if( !strcmp( user->passwd, passwd ) ) {
413
-
414
- return user;
415
- }
416
- return NULL;
417
- }
418
- }
419
-
420
- return NULL;
421
- }
422
-
423
- void readConfig( const char *filename, Config *conf )
424
- {
425
- int start;
426
- FILE *file;
427
- GameConfig *gameConf;
428
- char *line, lineBuf[ READBUF_LEN ];
429
-
430
- file = fopen( filename, "r" );
431
- if( file == NULL ) {
432
-
433
- fprintf( stderr, "BM_ERROR: could not open configuration file %s\n",
434
- filename );
435
- exit( EXIT_FAILURE );
436
- }
437
-
438
- gameConf = NULL;
439
- while( fgets( lineBuf, READBUF_LEN, file ) ) {
440
-
441
- /* skip past white space at start of line */
442
- start = 0; while( isspace( lineBuf[ start ] ) ) { ++start; }
443
- line = &lineBuf[ start ];
444
-
445
- /* ignore comments or empty lines */
446
- if( line[ 0 ] == '#' || line[ 0 ] == ';'
447
- || line[ 0 ] == '\n' || line[ 0 ] == 0 ) {
448
- continue;
449
- }
450
-
451
- if( strncasecmp( line, "port", 4 ) == 0 ) {
452
-
453
- if( gameConf != NULL ) {
454
-
455
- fprintf( stderr, "BM_ERROR: server port must be defined outside of game blocks\n" );
456
- exit( EXIT_FAILURE );
457
- }
458
- if( sscanf( &line[ 4 ], "%"SCNu16, &conf->port ) < 1 ) {
459
-
460
- fprintf( stderr, "BM_ERROR: could not get port from: %s", line );
461
- exit( EXIT_FAILURE );
462
- }
463
- } else if( strncasecmp( line, "game", 4 ) == 0 ) {
464
- FILE *file;
465
- GameConfig gc;
466
- char game[ READBUF_LEN ];
467
-
468
- if( gameConf != NULL ) {
469
-
470
- fprintf( stderr, "BM_ERROR: can't define a game within another game block\n" );
471
- exit( EXIT_FAILURE );
472
- }
473
- if( sscanf( &line[ 4 ], " %s", game ) < 1 ) {
474
-
475
- fprintf( stderr, "BM_ERROR: could not get game name from: %s", line );
476
- exit( EXIT_FAILURE );
477
- }
478
- if( findGame( conf, game ) ) {
479
-
480
- fprintf( stderr, "BM_ERROR: game %s has already been used\n", game );
481
- exit( EXIT_FAILURE );
482
- }
483
-
484
- setGameDefaults( &gc );
485
- gc.gameFile = strdup( game );
486
-
487
- file = fopen( gc.gameFile, "r" );
488
- if( file == NULL ) {
489
-
490
- fprintf( stderr, "BM_ERROR: could not open game file %s\n", gc.gameFile );
491
- exit( EXIT_FAILURE );
492
- }
493
- gc.game = readGame( file );
494
- fclose( file );
495
-
496
- if( gc.game == NULL ) {
497
-
498
- fprintf( stderr, "BM_ERROR: could not read game %s", gc.gameFile );
499
- exit( EXIT_FAILURE );
500
- }
501
- gameConf
502
- = (GameConfig *)LLPoolGetItem( LLPoolAddItem( conf->games, &gc ) );
503
- } else if( strncmp( line, "}", 1 ) == 0 ) {
504
- /* finished game definition */
505
-
506
- gameConf = NULL;
507
- } else if( strncasecmp( line, "maxRunningBots", 14 ) == 0 ) {
508
-
509
- if( gameConf != NULL ) {
510
-
511
- fprintf( stderr, "BM_ERROR: maxRunningBots must be defined outside of game blocks\n" );
512
- exit( EXIT_FAILURE );
513
- }
514
- if( sscanf( &line[ 14 ], "%"SCNu16, &conf->maxRunningBots ) < 1 ) {
515
-
516
- fprintf( stderr, "BM_ERROR: could not get maximum number of bots running from: %s", line );
517
- exit( EXIT_FAILURE );
518
- }
519
- } else if( strncasecmp( line, "startupTimeoutSecs", 18 ) == 0 ) {
520
-
521
- if( gameConf != NULL ) {
522
-
523
- fprintf( stderr, "BM_ERROR: startupTimeoutSecs must be defined outside of game blocks\n" );
524
- exit( EXIT_FAILURE );
525
- }
526
- if( sscanf( &line[ 18 ], "%"SCNu16, &conf->startupTimeoutSecs ) < 1 ) {
527
-
528
- fprintf( stderr, "BM_ERROR: could not get maximum dealer startup timeout: %s", line );
529
- exit( EXIT_FAILURE );
530
- }
531
- } else if( strncasecmp( line, "responseTimeoutSecs", 19 ) == 0 ) {
532
-
533
- if( gameConf != NULL ) {
534
-
535
- fprintf( stderr, "BM_ERROR: responseTimeoutSecs must be defined outside of game blocks\n" );
536
- exit( EXIT_FAILURE );
537
- }
538
- if( sscanf( &line[ 19 ], "%"SCNu16, &conf->responseTimeoutSecs ) < 1 ) {
539
-
540
- fprintf( stderr, "BM_ERROR: could not get maximum dealer action timeout: %s", line );
541
- exit( EXIT_FAILURE );
542
- }
543
- } else if( strncasecmp( line, "handTimeoutSecs", 15 ) == 0 ) {
544
-
545
- if( gameConf != NULL ) {
546
-
547
- fprintf( stderr, "BM_ERROR: handTimeoutSecs must be defined outside of game blocks\n" );
548
- exit( EXIT_FAILURE );
549
- }
550
- if( sscanf( &line[ 15 ], "%"SCNu16, &conf->handTimeoutSecs ) < 1 ) {
551
-
552
- fprintf( stderr, "BM_ERROR: could not get maximum dealer hand timeout: %s", line );
553
- exit( EXIT_FAILURE );
554
- }
555
- } else if( strncasecmp( line, "avgHandTimeSecs", 15 ) == 0 ) {
556
-
557
- if( gameConf != NULL ) {
558
-
559
- fprintf( stderr, "BM_ERROR: avgHandTimeSecs must be defined outside of game blocks\n" );
560
- exit( EXIT_FAILURE );
561
- }
562
- if( sscanf( &line[ 15 ], "%"SCNu16, &conf->avgHandTimeSecs ) < 1 ) {
563
-
564
- fprintf( stderr, "BM_ERROR: could not get dealer average hand time: %s", line );
565
- exit( EXIT_FAILURE );
566
- }
567
- } else if( strncasecmp( line, "maxMatchRuns", 12 ) == 0 ) {
568
-
569
- if( gameConf == NULL ) {
570
-
571
- fprintf( stderr, "BM_ERROR: maxMatchRuns must be defined within a game block\n" );
572
- exit( EXIT_FAILURE );
573
- }
574
- if( sscanf( &line[ 12 ], "%"SCNu16, &gameConf->maxMatchRuns ) < 1 ) {
575
-
576
- fprintf( stderr, "BM_ERROR: could not get maximum number of runs in a match from: %s", line );
577
- exit( EXIT_FAILURE );
578
- }
579
- } else if( strncasecmp( line, "maxRunningJobs", 14 ) == 0 ) {
580
-
581
- if( gameConf == NULL ) {
582
-
583
- fprintf( stderr, "BM_ERROR: maxRunningJobs must be defined within a game block\n" );
584
- exit( EXIT_FAILURE );
585
- }
586
- if( sscanf( &line[ 14 ], "%"SCNu16, &gameConf->maxRunningJobs ) < 1 ) {
587
-
588
- fprintf( stderr, "BM_ERROR: could not get maximum number of running jobs from: %s", line );
589
- exit( EXIT_FAILURE );
590
- }
591
- } else if( strncasecmp( line, "matchHands", 10 ) == 0 ) {
592
-
593
- if( gameConf == NULL ) {
594
-
595
- fprintf( stderr, "BM_ERROR: matchHands must be defined within a game block\n" );
596
- exit( EXIT_FAILURE );
597
- }
598
- if( sscanf( &line[ 10 ], "%"SCNu32, &gameConf->matchHands ) < 1 ) {
599
-
600
- fprintf( stderr, "BM_ERROR: could not get number of hands in a match from: %s", line );
601
- exit( EXIT_FAILURE );
602
- }
603
- } else if( strncasecmp( line, "bot", 3 ) == 0 ) {
604
-
605
- if( gameConf == NULL ) {
606
-
607
- fprintf( stderr, "BM_ERROR: matchHands must be defined within a game block\n" );
608
- exit( EXIT_FAILURE );
609
- }
610
- addBot( gameConf, &line[ 3 ] );
611
- } else if( strncasecmp( line, "user", 4 ) == 0 ) {
612
-
613
- if( gameConf != NULL ) {
614
-
615
- fprintf( stderr,
616
- "BM_ERROR: users must be defined outside of game blocks\n" );
617
- exit( EXIT_FAILURE );
618
- }
619
- addUser( conf, &line[ 4 ] );
620
- } else {
621
-
622
- fprintf( stderr, "BM_ERROR: unknown configuration option %s", line );
623
- exit( EXIT_FAILURE );
624
- }
625
- }
626
-
627
- fclose( file );
628
- }
629
-
630
- void addConnection( ServerState *serv, const int sock )
631
- {
632
- Connection conn;
633
-
634
- /* add the connection */
635
- conn.status = STATUS_UNVALIDATED;
636
- conn.user = NULL;
637
- conn.connBuf = createReadBuf( sock );
638
- if( conn.connBuf == 0 ) {
639
-
640
- fprintf( stderr, "BM_ERROR: could not create read buffer for socket\n" );
641
- exit( EXIT_FAILURE );
642
- }
643
- LLPoolAddItem( serv->conns, &conn );
644
- }
645
-
646
- int matchUsesConnection( const Match *match, const LLPoolEntry *connEntry )
647
- {
648
- int p;
649
-
650
- for( p = 0; p < match->gameConf->game->numPlayers; ++p ) {
651
-
652
- if( match->players[ p ].isNetworkPlayer
653
- && match->players[ p ].entry == connEntry ) {
654
-
655
- return 1;
656
- }
657
- }
658
-
659
- return 0;
660
- }
661
-
662
- void closeConnection( ServerState *serv, LLPoolEntry *connEntry )
663
- {
664
- Connection *conn = (Connection*)LLPoolGetItem( connEntry );
665
- LLPoolEntry *cur, *next;
666
-
667
- destroyReadBuf( conn->connBuf );
668
- conn->status = STATUS_CLOSED;
669
-
670
- /* remove any pending matches which relied on the connection */
671
- for( cur = LLPoolFirstEntry( serv->matches ); cur != NULL; cur = next ) {
672
- next = LLPoolNextEntry( cur );
673
- Match *match = (Match *)LLPoolGetItem( cur );
674
-
675
- if( matchUsesConnection( match, connEntry ) ) {
676
-
677
- match->numRuns = 0;
678
- }
679
- }
680
- }
681
-
682
- void handleListenSocket( const Config *conf, ServerState *serv )
683
- {
684
- int sock;
685
- struct sockaddr_in addr;
686
- socklen_t addrLen;
687
-
688
- addrLen = sizeof( addr );
689
- sock = accept( serv->listenSocket, (struct sockaddr *)&addr, &addrLen );
690
- if( sock < 0 ) {
691
-
692
- fprintf( stderr, "WARNING: failed to accept incoming connection\n" );
693
- return;
694
- }
695
-
696
- addConnection( serv, sock );
697
- }
698
-
699
- /* -1 on failure, 0 on success */
700
- int parseMatchSpec( const Config *conf,
701
- ServerState *serv,
702
- const char *spec,
703
- LLPoolEntry *connEntry,
704
- Match *match )
705
- {
706
- uint32_t rngSeed;
707
- int pos, t, p;
708
- LLPoolEntry *entry;
709
- char tag[ READBUF_LEN ];
710
- char name[ READBUF_LEN ];
711
-
712
- pos = 0;
713
-
714
- if( sscanf( &spec[ pos ], " %s%n", name, &t ) < 1 ) {
715
-
716
- return -1;
717
- }
718
- pos += t;
719
-
720
- entry = findGame( conf, name );
721
- if( entry == NULL ) {
722
-
723
- return -1;
724
- }
725
- match->gameConf = (GameConfig *)LLPoolGetItem( entry );
726
-
727
- if( sscanf( &spec[ pos ],
728
- " %d %s %"SCNu32" %n",
729
- &match->numRuns,
730
- tag,
731
- &rngSeed,
732
- &t ) < 3 ) {
733
-
734
- return -1;
735
- }
736
- pos += t;
737
- if( match->numRuns < 0 || match->numRuns > match->gameConf->maxMatchRuns ) {
738
-
739
- return -1;
740
- }
741
-
742
- /* make sure tag has no characters in it */
743
- if( strchr( tag, '/' ) != NULL ) {
744
-
745
- return -1;
746
- }
747
-
748
- /* get bots */
749
- for( p = 0; p < match->gameConf->game->numPlayers; ++p ) {
750
-
751
- /* get the name */
752
- if( sscanf( &spec[ pos ], " %s%n", name, &t ) < 1 ) {
753
-
754
- return -1;
755
- }
756
- pos += t;
757
-
758
- /* translate the name into an index */
759
- if( !strcmp( name, "LOCAL" ) ) {
760
-
761
- match->players[ p ].isNetworkPlayer = 1;
762
- match->players[ p ].entry = connEntry;
763
- } else {
764
-
765
- match->players[ p ].isNetworkPlayer = 0;
766
- match->players[ p ].entry = findBot( match->gameConf, name );
767
- if( match->players[ p ].entry == NULL ) {
768
-
769
- return -1;
770
- }
771
- }
772
- }
773
-
774
- match->tag = strdup( tag );
775
- match->rngSeed = rngSeed;
776
- if( rngSeed ) {
777
-
778
- init_genrand( &match->rng, rngSeed );
779
- if( match->numRuns == 1 ) {
780
-
781
- match->useRngForSeed = 0;
782
- } else {
783
-
784
- match->useRngForSeed = 1;
785
- }
786
- } else {
787
-
788
- init_genrand( &match->rng, genrand_int32( &serv->rng ) );
789
- }
790
-
791
- return 0;
792
- }
793
-
794
- void writeHelpMessage( int fd )
795
- {
796
- int r;
797
-
798
- r = write( fd, "HELP - this message\n", 20 );
799
- r = write( fd, "GAMES - list available games and players\n", 41 );
800
- r = write( fd, "QSTAT - show the current queue\n", 31 );
801
- r = write( fd, "RUNMATCHES game #runs tag rngSeed player ... - submit match request\n", 68 );
802
- r = write( fd, " - Player order decides match seating\n", 39 );
803
- r = write( fd, " - \"LOCAL\" player runs the bm_widget agent (bot_command)\n", 60 );
804
- }
805
-
806
- void writeGameList( const Config *conf, int fd )
807
- {
808
- int r;
809
- LLPoolEntry *cur, *botCur;
810
- char line[ READBUF_LEN ];
811
-
812
- for( cur = LLPoolFirstEntry( conf->games );
813
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
814
- GameConfig *game = (GameConfig *)LLPoolGetItem( cur );
815
-
816
- r = snprintf( line, sizeof( line ), "\n%s\n", game->gameFile );
817
- assert( r > 0 );
818
- r = write( fd, line, r );
819
-
820
- for( botCur = LLPoolFirstEntry( game->bots );
821
- botCur != NULL; botCur = LLPoolNextEntry( botCur ) ) {
822
- BotSpec *bot = (BotSpec *)LLPoolGetItem( botCur );
823
-
824
- r = snprintf( line, sizeof( line ), " %s\n", bot->name );
825
- assert( r > 0 );
826
- r = write( fd, line, r );
827
- }
828
- }
829
- }
830
-
831
- void writeQueueStatus( const Config *conf, const ServerState *serv, int fd )
832
- {
833
- int r;
834
- LLPoolEntry *cur;
835
- char line[ READBUF_LEN * 4 ];
836
-
837
- if( serv->matches->numEntries == 0 ) {
838
- r = write( fd, "Queue empty\n", 12 );
839
- }
840
-
841
- for( cur = LLPoolFirstEntry( serv->matches );
842
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
843
- Match *match = (Match *)LLPoolGetItem( cur );
844
-
845
- r = snprintf( line,
846
- sizeof( line ),
847
- "%s %s %s * %d %s\n",
848
- match->user->name,
849
- match->tag,
850
- match->gameConf->gameFile,
851
- match->numRuns,
852
- match->isRunning ? "R" : "Q" );
853
- assert( r > 0 );
854
- r = write( fd, line, r );
855
- }
856
- }
857
-
858
- void handleConnection( Config *conf, ServerState *serv,
859
- LLPoolEntry *connEntry )
860
- {
861
- int r;
862
- Connection *conn = (Connection *)LLPoolGetItem( connEntry );
863
- char line[ READBUF_LEN ];
864
-
865
- while( ( r = getLine( conn->connBuf, READBUF_LEN, line, 0 ) ) >= 0 ) {
866
-
867
- if( r == 0 ) {
868
-
869
- closeConnection( serv, connEntry );
870
- return;
871
- }
872
-
873
- if( conn->status == STATUS_UNVALIDATED ) {
874
- UserSpec *user;
875
-
876
- user = validateLogon( conf, line );
877
- if( user == NULL ) {
878
- /* couldn't authenticate */
879
-
880
- r = write( conn->connBuf->fd, "BAD LOGON\n", 10 );
881
- fprintf( stderr, "BM_ERROR: connection failed to log in\n" );
882
- closeConnection( serv, connEntry );
883
- return;
884
- }
885
-
886
- /* send an okay message */
887
- r = write( conn->connBuf->fd, "LOGON OKAY - type help for commands\n", 36 );
888
-
889
- /* connection status is now okay */
890
- conn->user = user;
891
- conn->status = STATUS_OKAY;
892
- return;
893
- }
894
-
895
- if( !strncasecmp( line, "HELP", 4 ) ) {
896
-
897
- writeHelpMessage( conn->connBuf->fd );
898
- } else if( !strncasecmp( line, "GAMES", 5 ) ) {
899
-
900
- writeGameList( conf, conn->connBuf->fd );
901
- } else if( !strncasecmp( line, "QSTAT", 5 ) ) {
902
-
903
- writeQueueStatus( conf, serv, conn->connBuf->fd );
904
- } else if( !strncasecmp( line, "RUNMATCHES", 10 ) ) {
905
- Match match;
906
-
907
- if( parseMatchSpec( conf, serv, &line[ 10 ], connEntry, &match ) < 0 ) {
908
-
909
- fprintf( stderr, "BM_ERROR: bad RUNMATCHES command: %s", line );
910
- r = write( conn->connBuf->fd, "BAD RUNMATCHES COMMAND\n", 23 );
911
- return;
912
- }
913
- match.user = ( (Connection *)LLPoolGetItem( connEntry ) )->user;
914
- match.isRunning = 0;
915
- gettimeofday( &match.queueTime, NULL );
916
- LLPoolAddItem( serv->matches, &match );
917
- return;
918
- } else {
919
-
920
- r = write( conn->connBuf->fd, "UNKNOWN\n", 8 );
921
- return;
922
- }
923
- }
924
- }
925
-
926
- int timeIsEarlier( struct timeval *a, struct timeval *b )
927
- {
928
- if( a->tv_sec < b->tv_sec ) {
929
- return 1;
930
- } else if( a->tv_sec == b->tv_sec
931
- && a->tv_usec < b->tv_usec ) {
932
- return 1;
933
- }
934
- return 0;
935
- }
936
-
937
- /* how many bots will match start? */
938
- int botsInMatch( const Match *match )
939
- {
940
- int p, num;
941
-
942
- num = 0;
943
- for( p = 0; p < match->gameConf->game->numPlayers; ++p ) {
944
-
945
- if( !match->players[ p ].isNetworkPlayer ) {
946
-
947
- ++num;
948
- }
949
- }
950
-
951
- return num;
952
- }
953
-
954
- void startDealer( const Config *conf,
955
- const Match *match,
956
- MatchJob *job,
957
- const uint32_t rngSeed )
958
- {
959
- int stdoutPipe[ 2 ], p, arg;
960
- char handsString[ 16 ], rngString[ 16 ];
961
- char startupTimeoutString[ 16 ], responseTimeoutString[ 16 ];
962
- char handTimeoutString[ 16 ], avgHandTimeString[ 16 ];
963
- char *argv[ MAX_PLAYERS + 64 ];
964
-
965
- if( pipe( stdoutPipe ) < 0 ) {
966
-
967
- fprintf( stderr, "BM_ERROR: could not create pipe for new dealer\n" );
968
- exit( EXIT_FAILURE );
969
- }
970
-
971
- job->dealerPID = fork();
972
- if( job->dealerPID < 0 ) {
973
-
974
- fprintf( stderr, "BM_ERROR: fork() failed\n" );
975
- exit( EXIT_FAILURE );
976
- }
977
- if( !job->dealerPID ) {
978
- /* child runs the dealer command */
979
- int stderrfd;
980
- char tag[ READBUF_LEN ];
981
-
982
- snprintf( tag, sizeof( tag ), "%s/%s.stderr", BM_LOGDIR, job->tag );
983
- stderrfd = open( tag, O_WRONLY | O_APPEND | O_CREAT, 0644 );
984
- if( stderrfd < 0 ) {
985
-
986
- fprintf( stderr,
987
- "BM_ERROR: could not create error log %s\n",
988
- tag );
989
- exit( EXIT_FAILURE );
990
- }
991
- dup2( stderrfd, 2 );
992
-
993
- /* change stdout to be the write end of the pipe */
994
- close( stdoutPipe[ 0 ] );
995
- dup2( stdoutPipe[ 1 ], 1 );
996
-
997
- arg = 0;
998
-
999
- argv[ arg ] = BM_DEALER;
1000
- ++arg;
1001
-
1002
- snprintf( tag, sizeof( tag ), "%s/%s", BM_LOGDIR, job->tag );
1003
- argv[ arg ] = tag;
1004
- ++arg;
1005
-
1006
- argv[ arg ] = match->gameConf->gameFile;
1007
- ++arg;
1008
-
1009
- snprintf( handsString,
1010
- sizeof( handsString ),
1011
- "%"PRIu32,
1012
- match->gameConf->matchHands );
1013
- argv[ arg ] = handsString;
1014
- ++arg;
1015
-
1016
- snprintf( rngString, sizeof( rngString ), "%"PRIu32, rngSeed );
1017
- argv[ arg ] = rngString;
1018
- ++arg;
1019
-
1020
- for( p = 0; p < match->gameConf->game->numPlayers; ++p ) {
1021
-
1022
- if( match->players[ p ].isNetworkPlayer ) {
1023
-
1024
- argv[ arg ]
1025
- = ( (Connection *)LLPoolGetItem( match->players[ p ].entry ) )
1026
- ->user->name;
1027
- } else {
1028
-
1029
- argv[ arg ]
1030
- = ( (BotSpec *)LLPoolGetItem( match->players[ p ].entry ) )->name;
1031
- }
1032
- ++arg;
1033
- }
1034
-
1035
- if( conf->startupTimeoutSecs ) {
1036
-
1037
- argv[ arg ] = "--start_timeout";
1038
- ++arg;
1039
-
1040
- snprintf( startupTimeoutString,
1041
- sizeof( startupTimeoutString ),
1042
- "%d",
1043
- (int)conf->startupTimeoutSecs * 1000 );
1044
- argv[ arg ] = startupTimeoutString;
1045
- ++arg;
1046
- }
1047
-
1048
- /* Add maximum per action timeout argument */
1049
- argv[ arg ] = "--t_response";
1050
- ++arg;
1051
-
1052
- snprintf( responseTimeoutString,
1053
- sizeof( responseTimeoutString ),
1054
- "%d",
1055
- (int)conf->responseTimeoutSecs * 1000 );
1056
- argv[ arg ] = responseTimeoutString;
1057
- ++arg;
1058
-
1059
- /* Add maximum per hand timeout argument */
1060
- argv[ arg ] = "--t_hand";
1061
- ++arg;
1062
-
1063
- snprintf( handTimeoutString,
1064
- sizeof( handTimeoutString ),
1065
- "%d",
1066
- (int)conf->handTimeoutSecs * 1000 );
1067
- argv[ arg ] = handTimeoutString;
1068
- ++arg;
1069
-
1070
- /* Add average per hand time argument */
1071
- argv[ arg ] = "--t_per_hand";
1072
- ++arg;
1073
-
1074
- snprintf( avgHandTimeString,
1075
- sizeof( avgHandTimeString ),
1076
- "%d",
1077
- (int)conf->avgHandTimeSecs * 1000 );
1078
- argv[ arg ] = avgHandTimeString;
1079
- ++arg;
1080
-
1081
-
1082
- argv[ arg ] = "-q";
1083
- ++arg;
1084
-
1085
- /* Restore the appending behaviour so multiple matches get appended into
1086
- * the same log file */
1087
- argv[ arg ] = "-a";
1088
- ++arg;
1089
-
1090
- argv[ arg ] = NULL;
1091
-
1092
- execv( BM_DEALER, argv );
1093
-
1094
- fprintf( stderr, "BM_ERROR: could not start dealer\n" );
1095
- exit( EXIT_FAILURE );
1096
- }
1097
-
1098
- /* parent has to talk to child to get ports */
1099
- ssize_t r;
1100
- int pos, t;
1101
- fd_set readfds;
1102
- struct timeval timeout;
1103
- char portString[ READBUF_LEN ];
1104
-
1105
- close( stdoutPipe[ 1 ] );
1106
- timeout.tv_sec = BM_DEALER_WAIT_SECS;
1107
- timeout.tv_usec = 0;
1108
- FD_ZERO( &readfds );
1109
- FD_SET( stdoutPipe[ 0 ], &readfds );
1110
- if( select( stdoutPipe[ 0 ] + 1, &readfds, NULL, NULL, &timeout ) < 1 ) {
1111
-
1112
- fprintf( stderr,
1113
- "BM_ERROR: timed out waiting for port string from dealer\n" );
1114
- exit( EXIT_FAILURE );
1115
- }
1116
- r = read( stdoutPipe[ 0 ], portString, READBUF_LEN );
1117
- if( r <= 0 || portString[ r - 1 ] != '\n' ) {
1118
-
1119
- fprintf( stderr, "BM_ERROR: could not read port string from dealer\n" );
1120
- exit( EXIT_FAILURE );
1121
- }
1122
- portString[ r ] = 0;
1123
-
1124
- /* parse the port string */
1125
- pos = 0;
1126
- for( p = 0; p < match->gameConf->game->numPlayers; ++p ) {
1127
-
1128
- if( sscanf( &portString[ pos ],
1129
- " %"SCNu16"%n",
1130
- &job->ports[ p ],
1131
- &t ) < 1 ) {
1132
-
1133
- fprintf( stderr,
1134
- "BM_ERROR: could not get port for player %d from dealer\n",
1135
- p + 1 );
1136
- exit( EXIT_FAILURE );
1137
- }
1138
- pos += t;
1139
- }
1140
- }
1141
-
1142
- pid_t startBot( const ServerState *serv,
1143
- const BotSpec *bot,
1144
- const uint16_t port,
1145
- const int botPosition )
1146
- {
1147
- pid_t pid;
1148
-
1149
- pid = fork();
1150
- if( pid < 0 ) {
1151
-
1152
- fprintf( stderr, "BM_ERROR: fork() failed\n" );
1153
- exit( EXIT_FAILURE );
1154
- }
1155
- if( !pid ) {
1156
- /* child runs the bot command */
1157
- char portString[ 8 ];
1158
- char posString[ 16 ];
1159
-
1160
- snprintf( portString, sizeof( portString ), "%"PRIu16, port );
1161
- snprintf( posString, sizeof( posString ), "%d", botPosition );
1162
-
1163
- /* throw away bot output */
1164
- dup2( serv->devnullfd, 1 );
1165
- dup2( serv->devnullfd, 2 );
1166
-
1167
- execl( bot->command,
1168
- bot->command,
1169
- serv->hostname,
1170
- portString,
1171
- posString,
1172
- NULL );
1173
-
1174
- fprintf( stderr, "BM_ERROR: could not start bot %s\n", bot->command );
1175
- exit( EXIT_FAILURE );
1176
- }
1177
-
1178
- return pid;
1179
- }
1180
-
1181
- int sendStartMessage( const ServerState *serv,
1182
- const MatchJob *job,
1183
- const Connection *conn,
1184
- const uint16_t port )
1185
- {
1186
- int len;
1187
- char msg[ strlen( serv->hostname ) + 12 + READBUF_LEN ];
1188
-
1189
- len = snprintf( msg, sizeof( msg ), "# RUNNING %s\n", job->tag );
1190
- assert( len > 0 );
1191
- if( write( conn->connBuf->fd, msg, len ) < len ) {
1192
-
1193
- fprintf( stderr, "BM_ERROR: short write to connection\n" );
1194
- return -1;
1195
- }
1196
-
1197
- len = snprintf( msg,
1198
- sizeof( msg ),
1199
- "RUN %s %"PRIu16"\n",
1200
- serv->hostname, port );
1201
- assert( len > 0 );
1202
- if( write( conn->connBuf->fd, msg, len ) < len ) {
1203
-
1204
- fprintf( stderr, "BM_ERROR: short write to connection\n" );
1205
- return -1;
1206
- }
1207
-
1208
- return 0;
1209
- }
1210
-
1211
- MatchJob runMatchJob( const Config *conf,
1212
- const ServerState *serv,
1213
- LLPoolEntry *matchEntry,
1214
- const uint32_t rngSeed )
1215
- {
1216
- int p, botPosition;
1217
- MatchJob job;
1218
- Match *match = (Match *)LLPoolGetItem( matchEntry );
1219
- char tag[ READBUF_LEN ];
1220
-
1221
- job.matchEntry = matchEntry;
1222
-
1223
- /* make the tag from the match tag */
1224
- snprintf( tag, sizeof( tag ), "%s.%s", match->user->name, match->tag );
1225
- job.tag = strdup( tag );
1226
-
1227
- /* initialise all PIDs to 0 */
1228
- job.dealerPID = 0;
1229
- for( p = 0; p < match->gameConf->game->numPlayers; ++p ) {
1230
-
1231
- job.botPID[ p ] = 0;
1232
- }
1233
-
1234
- /* start the dealer */
1235
- startDealer( conf, match, &job, rngSeed );
1236
-
1237
- /* deal with all the players */
1238
- botPosition = 0;
1239
- for( p = 0; p < match->gameConf->game->numPlayers; ++p ) {
1240
-
1241
- if( match->players[ p ].isNetworkPlayer ) {
1242
- /* send message with port to network player to start up */
1243
- Connection *conn = (Connection*)LLPoolGetItem( match->players[ p ].entry );
1244
-
1245
- if( sendStartMessage( serv, &job, conn, job.ports[ p ] ) < 0 ) {
1246
- /* abort the job... */
1247
-
1248
- fprintf( stderr, "BM_ERROR: aborting job\n" );
1249
-
1250
- kill( job.dealerPID, SIGTERM );
1251
- while( p > 0 ) {
1252
- --p;
1253
-
1254
- if( job.botPID[ p ] ) {
1255
-
1256
- kill( job.botPID[ p ], SIGTERM );
1257
- }
1258
- }
1259
-
1260
- return job;
1261
- }
1262
- } else {
1263
- /* start up bot */
1264
-
1265
- job.botPID[ p ]
1266
- = startBot( serv,
1267
- (BotSpec *)LLPoolGetItem( match->players[ p ].entry ),
1268
- job.ports[ p ],
1269
- botPosition );
1270
- ++botPosition;
1271
- }
1272
- }
1273
-
1274
- return job;
1275
- }
1276
-
1277
- int startMatchJob( const Config *conf, ServerState *serv )
1278
- {
1279
- int running;
1280
- LLPoolEntry *cur, *next, *best;
1281
- Match *curMatch, *bestMatch;
1282
- MatchJob job;
1283
-
1284
- /* automatically done adding things if we've got no more matches */
1285
- if( serv->matches->numEntries == 0 ) {
1286
-
1287
- return 0;
1288
- }
1289
-
1290
- /* how many bots are currently running */
1291
- running = 0;
1292
- for( cur = LLPoolFirstEntry( serv->jobs );
1293
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
1294
- MatchJob *job = (MatchJob *)LLPoolGetItem( cur );
1295
-
1296
- running += botsInMatch( (Match*)LLPoolGetItem( job->matchEntry ) );
1297
- }
1298
-
1299
- /* pick the best match to start */
1300
- best = 0;
1301
- bestMatch = 0;
1302
- for( cur = LLPoolFirstEntry( serv->matches ); cur != NULL; cur = next ) {
1303
- next = LLPoolNextEntry( cur );
1304
- curMatch = (Match *)LLPoolGetItem( cur );
1305
-
1306
- if( curMatch->isRunning ) {
1307
-
1308
- continue;
1309
- }
1310
-
1311
- if( curMatch->numRuns <= 0 ) {
1312
- /* match is finished - clean it up */
1313
-
1314
- free( curMatch->tag );
1315
- LLPoolRemoveEntry( serv->matches, cur );
1316
- continue;
1317
- }
1318
-
1319
- if( curMatch->gameConf->maxRunningJobs
1320
- && curMatch->gameConf->curRunningJobs
1321
- >= curMatch->gameConf->maxRunningJobs ) {
1322
- /* cur refers to a match in a game which is currently too busy */
1323
-
1324
- continue;
1325
- }
1326
-
1327
- if( best == 0
1328
- || timeIsEarlier( &curMatch->user->waitStart,
1329
- &bestMatch->user->waitStart )
1330
- || ( !timeIsEarlier( &bestMatch->user->waitStart,
1331
- &curMatch->user->waitStart )
1332
- && timeIsEarlier( &curMatch->queueTime,
1333
- &bestMatch->queueTime ) ) ) {
1334
-
1335
- best = cur;
1336
- bestMatch = curMatch;
1337
- }
1338
- }
1339
-
1340
- /* return failure if we couldn't find a runnable job */
1341
- if( best == NULL ) {
1342
-
1343
- return 0;
1344
- }
1345
-
1346
- /* check if we have the space to run the bots */
1347
- if( conf->maxRunningBots
1348
- && botsInMatch( bestMatch ) + running > conf->maxRunningBots ) {
1349
-
1350
- return 0;
1351
- }
1352
-
1353
- /* create the job */
1354
- job = runMatchJob( conf,
1355
- serv,
1356
- best,
1357
- bestMatch->useRngForSeed
1358
- ? genrand_int32( &bestMatch->rng )
1359
- : bestMatch->rngSeed );
1360
- assert( job.dealerPID );
1361
- LLPoolAddItem( serv->jobs, &job );
1362
-
1363
- /* update status about running jobs */
1364
- ++( bestMatch->gameConf->curRunningJobs );
1365
- bestMatch->isRunning = 1;
1366
-
1367
- /* update the user */
1368
- gettimeofday( &bestMatch->user->waitStart, NULL );
1369
-
1370
- /* update the match */
1371
- --bestMatch->numRuns;
1372
- gettimeofday( &bestMatch->queueTime, NULL );
1373
-
1374
- return 1;
1375
- }
1376
-
1377
- void initServerState( const Config *conf, ServerState *serv )
1378
- {
1379
- struct addrinfo hints, *info;
1380
- uint16_t port;
1381
- int hnm, r;
1382
- char *hn;
1383
- char ipstr[ INET6_ADDRSTRLEN ];
1384
-
1385
- serv->conns = newLLPool( sizeof( Connection ) );
1386
- serv->matches = newLLPool( sizeof( Match ) );
1387
- serv->jobs = newLLPool( sizeof( MatchJob ) );
1388
-
1389
- /* create the socket clients will connect to */
1390
- port = conf->port;
1391
- serv->listenSocket = getListenSocket( &port );
1392
- if( serv->listenSocket < 0 ) {
1393
-
1394
- fprintf( stderr, "BM_ERROR: could not open socket for listening\n" );
1395
- exit( EXIT_FAILURE );
1396
- }
1397
- printf( "starting server on port %"PRIu16"\n", conf->port );
1398
-
1399
- init_genrand( &serv->rng, time( NULL ) );
1400
-
1401
- hnm = sysconf( _SC_HOST_NAME_MAX );
1402
- hn = (char*)malloc( hnm );
1403
- assert( hn != 0 );
1404
- if( gethostname( hn, hnm + 1 ) < 0 ) {
1405
-
1406
- fprintf( stderr, "BM_ERROR: could not get hostname\n" );
1407
- exit( EXIT_FAILURE );
1408
- }
1409
-
1410
- memset( &hints, 0, sizeof( hints ) );
1411
- hints.ai_family = AF_INET;
1412
- hints.ai_socktype = SOCK_STREAM;
1413
- if( ( r = getaddrinfo( hn, NULL, &hints, &info ) ) != 0 ) {
1414
-
1415
- fprintf( stderr,
1416
- "BM_ERROR: could not get address info for host %s\n",
1417
- hn );
1418
- exit( 1 );
1419
- }
1420
- free( hn );
1421
-
1422
- /* Get an address for the server */
1423
- void *addr;
1424
-
1425
- /* get the pointer to the address itself,
1426
- * different fields in IPv4 and IPv6: */
1427
- if ( info->ai_family == AF_INET ) {
1428
- /* IPv4 */
1429
- struct sockaddr_in *ipv4 = ( struct sockaddr_in * ) info->ai_addr;
1430
- addr = &( ipv4->sin_addr );
1431
- } else {
1432
- /* IPv6 */
1433
- struct sockaddr_in6 *ipv6 = ( struct sockaddr_in6 * ) info->ai_addr;
1434
- addr = &( ipv6->sin6_addr );
1435
- }
1436
-
1437
- /* convert the IP to a string and store it:*/
1438
- inet_ntop( info->ai_family, addr, ipstr, sizeof( ipstr ) );
1439
- serv->hostname = strdup( ipstr );
1440
-
1441
- freeaddrinfo( info ); /* free the linked list */
1442
-
1443
- serv->devnullfd = open( "/dev/null", O_WRONLY );
1444
- if( serv->devnullfd < 0 ) {
1445
-
1446
- fprintf( stderr, "BM_ERROR: could not open /dev/null\n" );
1447
- exit( EXIT_FAILURE );
1448
- }
1449
- }
1450
-
1451
- int checkIfJobFinished( MatchJob *job )
1452
- {
1453
- int status, r, p, allDone;
1454
- Match *match = (Match *)LLPoolGetItem( job->matchEntry );
1455
-
1456
- allDone = 1;
1457
-
1458
- if( job->dealerPID ) {
1459
-
1460
- r = waitpid( job->dealerPID, &status, WNOHANG );
1461
- if( r < 0 ) {
1462
-
1463
- fprintf( stderr, "BM_ERROR: could not wait on child\n" );
1464
- exit( EXIT_FAILURE );
1465
- }
1466
- if( r == job->dealerPID ) {
1467
-
1468
- job->dealerPID = 0;
1469
- } else {
1470
-
1471
- allDone = 0;
1472
- }
1473
- }
1474
-
1475
- for( p = 0; p < match->gameConf->game->numPlayers; ++p ) {
1476
- if( job->botPID[ p ] == 0 ) {
1477
- continue;
1478
- }
1479
-
1480
- r = waitpid( job->botPID[ p ], &status, WNOHANG );
1481
- if( r < 0 ) {
1482
-
1483
- fprintf( stderr, "BM_ERROR: could not wait on child\n" );
1484
- exit( EXIT_FAILURE );
1485
- }
1486
- if( r == job->botPID[ p ] ) {
1487
-
1488
- job->botPID[ p ] = 0;
1489
- } else {
1490
-
1491
- allDone = 0;
1492
- }
1493
- }
1494
-
1495
- return allDone;
1496
- }
1497
-
1498
- void finishedJob( ServerState *serv, LLPoolEntry *jobEntry )
1499
- {
1500
- MatchJob *job = (MatchJob *)LLPoolGetItem( jobEntry );
1501
- Match *match = (Match *)LLPoolGetItem( job->matchEntry );
1502
-
1503
- free( job->tag );
1504
- --( match->gameConf->curRunningJobs );
1505
- match->isRunning = 0;
1506
- LLPoolRemoveEntry( serv->jobs, jobEntry );
1507
- }
1508
-
1509
- int main( int argc, char **argv )
1510
- {
1511
- Config conf;
1512
- ServerState serv;
1513
- int maxfd;
1514
- fd_set readfds;
1515
- LLPoolEntry *cur, *next;
1516
- struct timeval tv;
1517
-
1518
- if( argc < 2 ) {
1519
-
1520
- printUsage( stderr );
1521
- exit( EXIT_FAILURE );
1522
- }
1523
-
1524
- /* Ignore SIGPIPE. It seems that SIGPIPE can be raised when the underlying
1525
- * IO fails with a SIGPIPE. Unfortunately this causes the entire benchmark
1526
- * server to crash and jobs are lost. Ignore the signal to avoid death */
1527
- /* ???: May also need to catch SIGCHLD */
1528
- signal( SIGPIPE, SIG_IGN );
1529
-
1530
- /* use the config file */
1531
- setDefaults( &conf );
1532
- readConfig( argv[ 1 ], &conf );
1533
-
1534
- /* initialise server state */
1535
- initServerState( &conf, &serv );
1536
-
1537
- /* main I/O loop */
1538
- while( 1 ) {
1539
-
1540
- /* clean up any finished jobs */
1541
- for( cur = LLPoolFirstEntry( serv.jobs ); cur != NULL; cur = next ) {
1542
- next = LLPoolNextEntry( cur );
1543
- MatchJob *job = (MatchJob *)LLPoolGetItem( cur );
1544
-
1545
- if( checkIfJobFinished( job ) ) {
1546
-
1547
- finishedJob( &serv, cur );
1548
- }
1549
- }
1550
-
1551
- /* clean up any closed connections */
1552
- for( cur = LLPoolFirstEntry( serv.conns ); cur != NULL; cur = next ) {
1553
- next = LLPoolNextEntry( cur );
1554
-
1555
- if( ( (Connection *)LLPoolGetItem( cur ) )->status == STATUS_CLOSED ) {
1556
-
1557
- LLPoolRemoveEntry( serv.conns, cur );
1558
- }
1559
- }
1560
-
1561
- /* start jobs, up to the maximum */
1562
- while( startMatchJob( &conf, &serv ) );
1563
-
1564
- /* wait for input */
1565
- FD_ZERO( &readfds );
1566
- FD_SET( serv.listenSocket, &readfds );
1567
- maxfd = serv.listenSocket;
1568
- tv.tv_sec = BM_MAX_IOWAIT_SECS;
1569
- tv.tv_usec = 0;
1570
- for( cur = LLPoolFirstEntry( serv.conns );
1571
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
1572
- Connection *conn = (Connection *)LLPoolGetItem( cur );
1573
-
1574
- FD_SET( conn->connBuf->fd, &readfds );
1575
- if( conn->connBuf->fd > maxfd ) {
1576
-
1577
- maxfd = conn->connBuf->fd;
1578
- }
1579
- }
1580
- if( select( maxfd + 1, &readfds, NULL, NULL, &tv ) < 0 ) {
1581
-
1582
- fprintf( stderr, "BM_ERROR: select failed\n" );
1583
- exit( -1 );
1584
- }
1585
-
1586
- /* process anything that's happened */
1587
- if( FD_ISSET( serv.listenSocket, &readfds ) ) {
1588
-
1589
- handleListenSocket( &conf, &serv );
1590
- }
1591
- for( cur = LLPoolFirstEntry( serv.conns );
1592
- cur != NULL; cur = LLPoolNextEntry( cur ) ) {
1593
- Connection *conn = (Connection *)LLPoolGetItem( cur );
1594
-
1595
- if( FD_ISSET( conn->connBuf->fd, &readfds ) ) {
1596
-
1597
- handleConnection( &conf, &serv, cur );
1598
- }
1599
- }
1600
- }
1601
-
1602
- close( serv.listenSocket );
1603
- return EXIT_SUCCESS;
1604
- }